From 4a8ed5cb9c388d26a0067b96bb17a5eed4326333 Mon Sep 17 00:00:00 2001 From: Eden Federman Date: Sat, 26 Oct 2024 12:00:26 +0300 Subject: [PATCH] Inject env vars via webhook --- .../instrumentationdevice/manager.go | 4 +- .../instrumentationdevice/pods_webhook.go | 80 +++++++++++-------- k8sutils/pkg/workload/ownerreference.go | 1 - k8sutils/pkg/workload/runtimeobjects.go | 34 ++++++++ 4 files changed, 82 insertions(+), 37 deletions(-) diff --git a/instrumentor/controllers/instrumentationdevice/manager.go b/instrumentor/controllers/instrumentationdevice/manager.go index 8b37b0015..2c14e0792 100644 --- a/instrumentor/controllers/instrumentationdevice/manager.go +++ b/instrumentor/controllers/instrumentationdevice/manager.go @@ -168,7 +168,9 @@ func SetupWithManager(mgr ctrl.Manager) error { err = builder. WebhookManagedBy(mgr). For(&corev1.Pod{}). - WithDefaulter(&PodsWebhook{}). + WithDefaulter(&PodsWebhook{ + Client: mgr.GetClient(), + }). Complete() if err != nil { return err diff --git a/instrumentor/controllers/instrumentationdevice/pods_webhook.go b/instrumentor/controllers/instrumentationdevice/pods_webhook.go index cbf4f231c..186abd914 100644 --- a/instrumentor/controllers/instrumentationdevice/pods_webhook.go +++ b/instrumentor/controllers/instrumentationdevice/pods_webhook.go @@ -5,6 +5,11 @@ import ( "fmt" "strings" + "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/odigos-io/odigos/k8sutils/pkg/workload" + "sigs.k8s.io/controller-runtime/pkg/client" + common "github.com/odigos-io/odigos/common" "sigs.k8s.io/controller-runtime/pkg/webhook" @@ -18,11 +23,14 @@ const ( EnvVarPodName = "ODIGOS_POD_NAME" ) -type PodsWebhook struct{} +type PodsWebhook struct { + Client client.Client +} var _ webhook.CustomDefaulter = &PodsWebhook{} func (p *PodsWebhook) Default(ctx context.Context, obj runtime.Object) error { + logger := log.FromContext(ctx) pod, ok := obj.(*corev1.Pod) if !ok { return fmt.Errorf("expected a Pod but got a %T", obj) @@ -32,31 +40,27 @@ func (p *PodsWebhook) Default(ctx context.Context, obj runtime.Object) error { pod.Annotations = map[string]string{} } + instApp, err := workload.GetRuntimeDetailsForPod(ctx, p.Client, pod) + if err != nil { + logger.Error(err, "Failed to get runtime details for pod") + return err + } + // Inject ODIGOS environment variables into all containers - injectOdigosEnvVars(pod) + p.injectOdigosEnvVars(pod) return nil } -func injectOdigosEnvVars(pod *corev1.Pod) { - namespace := pod.Namespace - - // Common environment variables that do not change across containers - commonEnvVars := []corev1.EnvVar{ - { - Name: EnvVarNamespace, - Value: namespace, - }, - { - Name: EnvVarPodName, - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.name", - }, - }, - }, +func (p *PodsWebhook) envVarsToMap(envVars []corev1.EnvVar) map[string]corev1.EnvVar { + envMap := make(map[string]corev1.EnvVar) + for i := range envVars { + envMap[envVars[i].Name] = envVars[i] } + return envMap +} +func (p *PodsWebhook) injectOdigosEnvVars(pod *corev1.Pod) { for i := range pod.Spec.Containers { container := &pod.Spec.Containers[i] @@ -65,30 +69,36 @@ func injectOdigosEnvVars(pod *corev1.Pod) { continue } - // Check if the environment variables are already present, if so skip inject them again. - if envVarsExist(container.Env, commonEnvVars) { - continue + envsMap := p.envVarsToMap(container.Env) + envsMap[EnvVarNamespace] = corev1.EnvVar{ + Name: EnvVarNamespace, + Value: pod.Namespace, } - container.Env = append(container.Env, append(commonEnvVars, corev1.EnvVar{ + envsMap[EnvVarPodName] = corev1.EnvVar{ + Name: EnvVarPodName, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + } + + envsMap[EnvVarContainerName] = corev1.EnvVar{ Name: EnvVarContainerName, Value: container.Name, - })...) + } + + p.persistEnvVars(envsMap, container) } } -func envVarsExist(containerEnv []corev1.EnvVar, commonEnvVars []corev1.EnvVar) bool { - envMap := make(map[string]struct{}) - for _, envVar := range containerEnv { - envMap[envVar.Name] = struct{}{} // Inserting empty struct as value +func (p *PodsWebhook) persistEnvVars(envsMap map[string]corev1.EnvVar, container *corev1.Container) { + envs := make([]corev1.EnvVar, 0, len(envsMap)) + for _, env := range envsMap { + envs = append(envs, env) } - - for _, commonEnvVar := range commonEnvVars { - if _, exists := envMap[commonEnvVar.Name]; exists { // Checking if key exists - return true - } - } - return false + container.Env = envs } // Helper function to check if a container's resource limits have a key starting with the specified namespace diff --git a/k8sutils/pkg/workload/ownerreference.go b/k8sutils/pkg/workload/ownerreference.go index fbb061ee9..98bc50f25 100644 --- a/k8sutils/pkg/workload/ownerreference.go +++ b/k8sutils/pkg/workload/ownerreference.go @@ -10,7 +10,6 @@ import ( // GetWorkloadFromOwnerReference retrieves both the workload name and workload kind // from the provided owner reference. func GetWorkloadFromOwnerReference(ownerReference metav1.OwnerReference) (workloadName string, workloadKind WorkloadKind, err error) { - return GetWorkloadNameAndKind(ownerReference.Name, ownerReference.Kind) } diff --git a/k8sutils/pkg/workload/runtimeobjects.go b/k8sutils/pkg/workload/runtimeobjects.go index fa2ec5692..054d785ac 100644 --- a/k8sutils/pkg/workload/runtimeobjects.go +++ b/k8sutils/pkg/workload/runtimeobjects.go @@ -1,8 +1,14 @@ package workload import ( + "context" "errors" "strings" + + odigosv1 "github.com/odigos-io/odigos/api/odigos/v1alpha1" + + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" ) // this file contains utils related to odigos workload runtime object names. @@ -34,3 +40,31 @@ func ExtractWorkloadInfoFromRuntimeObjectName(runtimeObjectName string) (workloa return } + +func GetRuntimeDetailsForPod(ctx context.Context, kubeClient client.Client, pod *corev1.Pod) (*odigosv1.InstrumentedApplication, error) { + var workloadName string + var workloadKind WorkloadKind + for _, owner := range pod.OwnerReferences { + wn, wk, err := GetWorkloadFromOwnerReference(owner) + if IgnoreErrorKindNotSupported(err) != nil { + return nil, err + } + + workloadName = wn + workloadKind = wk + break + } + + instrumentedApplicationName := CalculateWorkloadRuntimeObjectName(workloadName, workloadKind) + + var runtimeDetails odigosv1.InstrumentedApplication + err := kubeClient.Get(ctx, client.ObjectKey{ + Namespace: pod.Namespace, + Name: instrumentedApplicationName, + }, &runtimeDetails) + if err != nil { + return nil, err + } + + return &runtimeDetails, nil +}