Skip to content

Commit

Permalink
Auto detect kubernetes cluster (#1322)
Browse files Browse the repository at this point in the history
Closes #1321

---------

Co-authored-by: Amir Blum <amirgiraffe@gmail.com>
  • Loading branch information
edeNFed and blumamir authored Jul 4, 2024
1 parent 26f1714 commit b2f1969
Show file tree
Hide file tree
Showing 12 changed files with 301 additions and 4 deletions.
16 changes: 15 additions & 1 deletion cli/cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"os"
"time"

"github.com/odigos-io/odigos/cli/pkg/autodetect"

"github.com/odigos-io/odigos/cli/pkg/labels"

apierrors "k8s.io/apimachinery/pkg/api/errors"
Expand Down Expand Up @@ -85,8 +87,20 @@ This command will install k8s components that will auto-instrument your applicat

fmt.Printf("Installing Odigos version %s in namespace %s ...\n", versionFlag, ns)

kc := cmd.Flag("kubeconfig").Value.String()
kubeKind, kubeVersion := autodetect.KubernetesClusterProduct(ctx, kc, client)
if kubeKind != autodetect.KindUnknown {
autodetect.CurrentKubernetesVersion = autodetect.KubernetesVersion{
Kind: kubeKind,
Version: kubeVersion,
}
fmt.Printf("Detected Kubernetes: %s version %s\n", kubeKind, kubeVersion)
} else {
fmt.Println("Unknown Kubernetes cluster detected, proceeding with installation")
}

// namespace is created on "install" and is not managed by resource manager
createKubeResourceWithLogging(ctx, fmt.Sprintf("Creating namespace %s", ns),
createKubeResourceWithLogging(ctx, fmt.Sprintf("> Creating namespace %s", ns),
client, cmd, ns, createNamespace)

resourceManagers := resources.CreateResourceManagers(client, ns, odigosTier, &odigosProToken, &config)
Expand Down
2 changes: 1 addition & 1 deletion cli/cmd/resources/applyresources.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

func ApplyResourceManagers(ctx context.Context, client *kube.Client, resourceManagers []resourcemanager.ResourceManager, prefixForLogging string) error {
for _, rm := range resourceManagers {
l := log.Print(fmt.Sprintf("%s %s", prefixForLogging, rm.Name()))
l := log.Print(fmt.Sprintf("> %s %s", prefixForLogging, rm.Name()))
err := rm.InstallFromScratch(ctx)
if err != nil {
l.Error(err)
Expand Down
40 changes: 38 additions & 2 deletions cli/cmd/resources/odiglet.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ package resources
import (
"context"

"k8s.io/apimachinery/pkg/api/resource"

"github.com/odigos-io/odigos/cli/pkg/autodetect"

odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1"
"github.com/odigos-io/odigos/cli/cmd/resources/odigospro"
"github.com/odigos-io/odigos/cli/cmd/resources/resourcemanager"
Expand Down Expand Up @@ -362,6 +366,33 @@ func NewSCClusterRoleBinding(ns string) *rbacv1.ClusterRoleBinding {
}
}

func NewResourceQuota(ns string) *corev1.ResourceQuota {
return &corev1.ResourceQuota{
TypeMeta: metav1.TypeMeta{
Kind: "ResourceQuota",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "odigos-quota",
Namespace: ns,
},
Spec: corev1.ResourceQuotaSpec{
Hard: corev1.ResourceList{
"pods": resource.MustParse("10k"),
},
ScopeSelector: &corev1.ScopeSelector{
MatchExpressions: []corev1.ScopedResourceSelectorRequirement{
{
ScopeName: corev1.ResourceQuotaScopePriorityClass,
Operator: corev1.ScopeSelectorOpIn,
Values: []string{"system-node-critical"},
},
},
},
},
}
}

func NewOdigletDaemonSet(ns string, version string, imagePrefix string, imageName string, odigosTier common.OdigosTier, openshiftEnabled bool, goAutoIncludeCodeAttributes bool) *appsv1.DaemonSet {

dynamicEnv := []corev1.EnvVar{}
Expand All @@ -380,7 +411,7 @@ func NewOdigletDaemonSet(ns string, version string, imagePrefix string, imageNam

odigosSeLinuxHostVolumes := []corev1.Volume{}
odigosSeLinuxHostVolumeMounts := []corev1.VolumeMount{}
if openshiftEnabled {
if openshiftEnabled || autodetect.CurrentKubernetesVersion.Kind == autodetect.KindOpenShift {
odigosSeLinuxHostVolumes = append(odigosSeLinuxHostVolumes, selinuxHostVolumes()...)
odigosSeLinuxHostVolumeMounts = append(odigosSeLinuxHostVolumeMounts, selinuxHostVolumeMounts()...)
}
Expand Down Expand Up @@ -631,11 +662,16 @@ func (a *odigletResourceManager) InstallFromScratch(ctx context.Context) error {
}

// if openshift is enabled, we need to create additional SCC cluster role binding first
if a.config.OpenshiftEnabled {
if a.config.OpenshiftEnabled || autodetect.CurrentKubernetesVersion.Kind == autodetect.KindOpenShift {
resources = append(resources, NewSCCRoleBinding(a.ns))
resources = append(resources, NewSCClusterRoleBinding(a.ns))
}

// if gke, create resource quota
if autodetect.CurrentKubernetesVersion.Kind == autodetect.KindGKE {
resources = append(resources, NewResourceQuota(a.ns))
}

// before creating the daemonset, we need to create the service account, cluster role and cluster role binding
resources = append(resources,
NewOdigletDaemonSet(a.ns, a.config.OdigosVersion, a.config.ImagePrefix, odigletImage, a.odigosTier, a.config.OpenshiftEnabled, a.config.GoAutoIncludeCodeAttributes))
Expand Down
33 changes: 33 additions & 0 deletions cli/pkg/autodetect/aks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package autodetect

import (
"context"
"strings"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type aksDetector struct{}

func (a aksDetector) Detect(ctx context.Context, args DetectionArguments) (Kind, error) {
if strings.HasSuffix(args.ServerEndpoint, "azmk8s.io:443") {
return KindAKS, nil
}

// Look for nodes that have an AKS specific label
listOpts := metav1.ListOptions{
LabelSelector: "kubernetes.azure.com/cluster",
// Only need one
Limit: 1,
}

nodes, err := args.KubeClient.CoreV1().Nodes().List(ctx, listOpts)
if err != nil {
return KindUnknown, err
}
if len(nodes.Items) > 0 {
return KindAKS, nil
}

return KindUnknown, nil
}
24 changes: 24 additions & 0 deletions cli/pkg/autodetect/eks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package autodetect

import (
"context"
"strings"
)

type eksDetector struct{}

func (e eksDetector) Detect(ctx context.Context, args DetectionArguments) (Kind, error) {
if strings.Contains(args.ServerVersion, "-eks-") {
return KindEKS, nil
}

if strings.HasSuffix(args.ClusterName, ".eksctl.io") {
return KindEKS, nil
}

if strings.HasSuffix(args.ServerEndpoint, "eks.amazonaws.com") {
return KindEKS, nil
}

return KindUnknown, nil
}
20 changes: 20 additions & 0 deletions cli/pkg/autodetect/gke.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package autodetect

import (
"context"
"strings"
)

type gkeDetector struct{}

func (g gkeDetector) Detect(ctx context.Context, args DetectionArguments) (Kind, error) {
if strings.Contains(args.ServerVersion, "-gke.") {
return KindGKE, nil
}

if strings.HasPrefix(args.ClusterName, "gke_") {
return KindGKE, nil
}

return KindUnknown, nil
}
16 changes: 16 additions & 0 deletions cli/pkg/autodetect/k3s.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package autodetect

import (
"context"
"strings"
)

type k3sDetector struct{}

func (k k3sDetector) Detect(ctx context.Context, args DetectionArguments) (Kind, error) {
if strings.Contains(args.ServerVersion, "+k3s") {
return KindK3s, nil
}

return KindUnknown, nil
}
70 changes: 70 additions & 0 deletions cli/pkg/autodetect/kind.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package autodetect

import (
"context"
"fmt"

"github.com/odigos-io/odigos/cli/pkg/kube"
k8sutils "github.com/odigos-io/odigos/k8sutils/pkg/client"
)

type Kind string

var availableDetectors = []Detector{&kindDetector{}, &eksDetector{}, &gkeDetector{}, &minikubeDetector{}, &k3sDetector{}, &openshiftDetector{}, &aksDetector{}}

type KubernetesVersion struct {
Kind Kind
Version string
}

var CurrentKubernetesVersion KubernetesVersion

const (
KindUnknown Kind = "Unknown"
KindMinikube Kind = "Minikube"
KindKind Kind = "KinD"
KindEKS Kind = "EKS"
KindGKE Kind = "GKE"
KindAKS Kind = "AKS"
KindK3s Kind = "k3s"
KindOpenShift Kind = "Openshift"
)

// DetectionArguments are the arguments passed to the Detect function
type DetectionArguments struct {
k8sutils.ClusterDetails
ServerVersion string
KubeClient *kube.Client
}

type Detector interface {
Detect(ctx context.Context, args DetectionArguments) (Kind, error)
}

func KubernetesClusterProduct(ctx context.Context, kc string, client *kube.Client) (Kind, string) {
details := k8sutils.GetCurrentClusterDetails(kc)
serverVersion, err := client.Discovery().ServerVersion()
kubeVersion := fmt.Sprintf("%s.%s", serverVersion.Major, serverVersion.Minor)
gitServerVersion := ""
if err == nil {
gitServerVersion = serverVersion.GitVersion
}

args := DetectionArguments{
ClusterDetails: details,
ServerVersion: gitServerVersion,
KubeClient: client,
}

for _, detector := range availableDetectors {
kind, err := detector.Detect(ctx, args)
if err != nil {
continue
}
if kind != KindUnknown {
return kind, kubeVersion
}
}

return KindUnknown, kubeVersion
}
16 changes: 16 additions & 0 deletions cli/pkg/autodetect/kindkind.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package autodetect

import (
"context"
"strings"
)

type kindDetector struct{}

func (k kindDetector) Detect(ctx context.Context, args DetectionArguments) (Kind, error) {
if strings.HasPrefix(args.ClusterName, "kind-") || strings.HasPrefix(args.CurrentContext, "kind-") {
return KindKind, nil
}

return KindUnknown, nil
}
17 changes: 17 additions & 0 deletions cli/pkg/autodetect/minikube.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package autodetect

import "context"

type minikubeDetector struct{}

func (m minikubeDetector) Detect(ctx context.Context, args DetectionArguments) (Kind, error) {
if args.ClusterName == "minikube" {
return KindMinikube, nil
}

if args.CurrentContext == "minikube" {
return KindMinikube, nil
}

return KindUnknown, nil
}
21 changes: 21 additions & 0 deletions cli/pkg/autodetect/openshift.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package autodetect

import "context"

type openshiftDetector struct{}

func (o openshiftDetector) Detect(ctx context.Context, args DetectionArguments) (Kind, error) {
apiList, err := args.KubeClient.Discovery().ServerGroups()
if err != nil {
return KindUnknown, err
}

apiGroups := apiList.Groups
for i := 0; i < len(apiGroups); i++ {
if apiGroups[i].Name == "route.openshift.io" {
return KindOpenShift, nil
}
}

return KindUnknown, nil
}
30 changes: 30 additions & 0 deletions k8sutils/pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,33 @@ func GetClientConfig(kc string) (*rest.Config, error) {
func IsRunningInKubernetes() bool {
return os.Getenv("KUBERNETES_SERVICE_HOST") != ""
}

type ClusterDetails struct {
CurrentContext string
ClusterName string
ServerEndpoint string
}

func GetCurrentClusterDetails(kc string) ClusterDetails {
config, err := clientcmd.LoadFromFile(kc)
if err != nil {
return ClusterDetails{}
}

ctx := config.CurrentContext
cluster := ""
if val, ok := config.Contexts[ctx]; ok {
cluster = val.Cluster
}

server := ""
if val, ok := config.Clusters[cluster]; ok {
server = val.Server
}

return ClusterDetails{
CurrentContext: ctx,
ClusterName: cluster,
ServerEndpoint: server,
}
}

0 comments on commit b2f1969

Please sign in to comment.