Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto detect kubernetes cluster #1322

Merged
merged 6 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 NewRResourceQuota(ns string) *corev1.ResourceQuota {
edeNFed marked this conversation as resolved.
Show resolved Hide resolved
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, NewRResourceQuota(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,
}
}
Loading