From 50ff86b9c3cd1bd2b39f129c6c1e758cb419359d Mon Sep 17 00:00:00 2001 From: Bastian Krol Date: Wed, 29 May 2024 14:16:43 +0200 Subject: [PATCH] fix: ignore next webhook admission request after reverting --- internal/controller/dash0_controller.go | 15 +- internal/controller/dash0_controller_test.go | 24 +- internal/util/constants.go | 13 - internal/util/labels.go | 27 +++ internal/webhook/dash0_webhook.go | 51 +++- internal/webhook/dash0_webhook_test.go | 59 +++++ internal/workloads/workload_modifier.go | 15 +- test/util/resources.go | 241 +++++++++---------- test/util/verification.go | 8 + 9 files changed, 287 insertions(+), 166 deletions(-) delete mode 100644 internal/util/constants.go create mode 100644 internal/util/labels.go diff --git a/internal/controller/dash0_controller.go b/internal/controller/dash0_controller.go index 4a0d0b99..48382266 100644 --- a/internal/controller/dash0_controller.go +++ b/internal/controller/dash0_controller.go @@ -28,6 +28,8 @@ import ( ) const ( + FinalizerId = "operator.dash0.com/finalizer" + workkloadTypeLabel = "workload type" workloadNamespaceLabel = "workload namespace" workloadNameLabel = "workload name" @@ -570,7 +572,7 @@ func (r *Dash0Reconciler) checkImminentDeletionAndHandleFinalizers( return isMarkedForDeletion, err } } else { - if controllerutil.ContainsFinalizer(dash0CustomResource, util.FinalizerId) { + if controllerutil.ContainsFinalizer(dash0CustomResource, FinalizerId) { err := r.runCleanupActions(ctx, dash0CustomResource, logger) if err != nil { // error has already been logged in runCleanupActions @@ -601,7 +603,7 @@ func (r *Dash0Reconciler) runCleanupActions( return err } - controllerutil.RemoveFinalizer(dash0CustomResource, util.FinalizerId) + controllerutil.RemoveFinalizer(dash0CustomResource, FinalizerId) if err = r.Update(ctx, dash0CustomResource); err != nil { logger.Error(err, "Failed to remove the finalizer from the Dash0 custom resource, requeuing reconcile request.") return err @@ -613,7 +615,7 @@ func (r *Dash0Reconciler) addFinalizerIfNecessary( ctx context.Context, dash0CustomResource *operatorv1alpha1.Dash0, ) error { - finalizerHasBeenAdded := controllerutil.AddFinalizer(dash0CustomResource, util.FinalizerId) + finalizerHasBeenAdded := controllerutil.AddFinalizer(dash0CustomResource, FinalizerId) if finalizerHasBeenAdded { return r.Update(ctx, dash0CustomResource) } @@ -794,6 +796,9 @@ func (r *Dash0Reconciler) handleJobOnUninstrumentation(ctx context.Context, job // There was an attempt to instrument this job (probably by the controller), which has not been successful. // We only need remove the labels from that instrumentation attempt to clean up. newWorkloadModifier(r.Versions, &logger).RemoveLabelsFromImmutableJob(&job) + + // Apparently for jobs we do not need to set the "dash0.webhook.ignore.once" label, since changing there + // labels does not trigger a new admission request. return r.Client.Update(ctx, &job) } }, &logger) @@ -905,6 +910,10 @@ func (r *Dash0Reconciler) revertWorkloadInstrumentation( } hasBeenModified = workload.revert(&logger) if hasBeenModified { + // Changing the workload spec sometimes triggers a new admission request, which would re-instrument the + // workload via the webhook immediately. To prevent this, we add a label that the webhook can check to + // prevent instrumentation. + util.AddWebhookIgnoreOnceLabel(objectMeta) return r.Client.Update(ctx, workload.asClientObject()) } else { return nil diff --git a/internal/controller/dash0_controller_test.go b/internal/controller/dash0_controller_test.go index 5845ac2c..6b818bbf 100644 --- a/internal/controller/dash0_controller_test.go +++ b/internal/controller/dash0_controller_test.go @@ -233,7 +233,9 @@ var _ = Describe("Dash0 Controller", func() { triggerReconcileRequest(ctx, reconciler, "Trigger a reconcile request to revert the instrumented workload") VerifySuccessfulUninstrumentationEvent(ctx, clientset, namespace, name, "controller") - VerifyUnmodifiedCronJob(GetCronJob(ctx, k8sClient, namespace, name)) + cronJob = GetCronJob(ctx, k8sClient, namespace, name) + VerifyUnmodifiedCronJob(cronJob) + VerifyWebhookIgnoreOnceLabelIsPresent(&cronJob.ObjectMeta) }) It("should revert an instrumented daemon set", func() { @@ -255,7 +257,9 @@ var _ = Describe("Dash0 Controller", func() { triggerReconcileRequest(ctx, reconciler, "Trigger a reconcile request to revert the instrumented workload") VerifySuccessfulUninstrumentationEvent(ctx, clientset, namespace, name, "controller") - VerifyUnmodifiedDaemonSet(GetDaemonSet(ctx, k8sClient, namespace, name)) + daemonSet = GetDaemonSet(ctx, k8sClient, namespace, name) + VerifyUnmodifiedDaemonSet(daemonSet) + VerifyWebhookIgnoreOnceLabelIsPresent(&daemonSet.ObjectMeta) }) It("should revert an instrumented deployment", func() { @@ -277,7 +281,9 @@ var _ = Describe("Dash0 Controller", func() { triggerReconcileRequest(ctx, reconciler, "Trigger a reconcile request to revert the instrumented workload") VerifySuccessfulUninstrumentationEvent(ctx, clientset, namespace, name, "controller") - VerifyUnmodifiedDeployment(GetDeployment(ctx, k8sClient, namespace, name)) + deployment = GetDeployment(ctx, k8sClient, namespace, name) + VerifyUnmodifiedDeployment(deployment) + VerifyWebhookIgnoreOnceLabelIsPresent(&deployment.ObjectMeta) }) It("should record a failure event when attempting to revert an existing instrumenting job (which has been instrumented by the webhook)", func() { @@ -349,10 +355,12 @@ var _ = Describe("Dash0 Controller", func() { triggerReconcileRequest(ctx, reconciler, "Trigger a reconcile request to revert the instrumented workload") VerifySuccessfulUninstrumentationEvent(ctx, clientset, namespace, name, "controller") - VerifyUnmodifiedReplicaSet(GetReplicaSet(ctx, k8sClient, namespace, name)) + replicaSet = GetReplicaSet(ctx, k8sClient, namespace, name) + VerifyUnmodifiedReplicaSet(replicaSet) + VerifyWebhookIgnoreOnceLabelIsPresent(&replicaSet.ObjectMeta) }) - It("should not leave existing uninstrumented replica sets owned by deployments alone", func() { + It("should leave existing uninstrumented replica sets owned by deployments alone", func() { // We trigger one reconcile request before creating any workload and before deleting the Dash0 custom // resource, just to get the `isFirstReconcile` logic out of the way and to add the finalizer. // Alternatively, we could just add the finalizer here directly, but this approach is closer to what usually @@ -393,7 +401,9 @@ var _ = Describe("Dash0 Controller", func() { triggerReconcileRequest(ctx, reconciler, "Trigger a reconcile request to revert the instrumented workload") VerifySuccessfulUninstrumentationEvent(ctx, clientset, namespace, name, "controller") - VerifyUnmodifiedStatefulSet(GetStatefulSet(ctx, k8sClient, namespace, name)) + statefulSet = GetStatefulSet(ctx, k8sClient, namespace, name) + VerifyUnmodifiedStatefulSet(statefulSet) + VerifyWebhookIgnoreOnceLabelIsPresent(&statefulSet.ObjectMeta) }) }) }) @@ -460,7 +470,7 @@ func verifyDash0ResourceStatus(ctx context.Context, expectedStatus metav1.Condit } func removeFinalizer(ctx context.Context, dash0CustomResource *operatorv1alpha1.Dash0) { - finalizerHasBeenRemoved := controllerutil.RemoveFinalizer(dash0CustomResource, util.FinalizerId) + finalizerHasBeenRemoved := controllerutil.RemoveFinalizer(dash0CustomResource, FinalizerId) if finalizerHasBeenRemoved { Expect(k8sClient.Update(ctx, dash0CustomResource)).To(Succeed()) } diff --git a/internal/util/constants.go b/internal/util/constants.go deleted file mode 100644 index 6930bb04..00000000 --- a/internal/util/constants.go +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. -// SPDX-License-Identifier: Apache-2.0 - -package util - -const ( - InstrumentedLabelKey = "dash0.instrumented" - OperatorVersionLabelKey = "dash0.operator.version" - InitContainerImageVersionLabelKey = "dash0.initcontainer.image.version" - InstrumentedByLabelKey = "dash0.instrumented.by" - - FinalizerId = "operator.dash0.com/finalizer" -) diff --git a/internal/util/labels.go b/internal/util/labels.go new file mode 100644 index 00000000..0ca6b30c --- /dev/null +++ b/internal/util/labels.go @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc. +// SPDX-License-Identifier: Apache-2.0 + +package util + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + InstrumentedLabelKey = "dash0.instrumented" + OperatorVersionLabelKey = "dash0.operator.version" + InitContainerImageVersionLabelKey = "dash0.initcontainer.image.version" + InstrumentedByLabelKey = "dash0.instrumented.by" + WebhookIgnoreOnceLabelKey = "dash0.webhook.ignore.once" +) + +func AddWebhookIgnoreOnceLabel(meta *metav1.ObjectMeta) { + AddLabel(meta, WebhookIgnoreOnceLabelKey, "true") +} + +func AddLabel(meta *metav1.ObjectMeta, key string, value string) { + if meta.Labels == nil { + meta.Labels = make(map[string]string, 1) + } + meta.Labels[key] = value +} diff --git a/internal/webhook/dash0_webhook.go b/internal/webhook/dash0_webhook.go index a5e2604b..dfaf308d 100644 --- a/internal/webhook/dash0_webhook.go +++ b/internal/webhook/dash0_webhook.go @@ -12,6 +12,7 @@ import ( "github.com/go-logr/logr" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/tools/record" @@ -109,8 +110,11 @@ func (h *Handler) handleCronJob( if failed { return responseIfFailed } + if isIgnored(&cronJob.ObjectMeta) { + return h.postProcess(request, cronJob, false, true, logger) + } hasBeenModified := h.newWorkloadModifier(logger).ModifyCronJob(cronJob) - return h.postProcess(request, cronJob, hasBeenModified, logger) + return h.postProcess(request, cronJob, hasBeenModified, false, logger) } func (h *Handler) handleDaemonSet( @@ -123,8 +127,11 @@ func (h *Handler) handleDaemonSet( if failed { return responseIfFailed } + if isIgnored(&daemonSet.ObjectMeta) { + return h.postProcess(request, daemonSet, false, true, logger) + } hasBeenModified := h.newWorkloadModifier(logger).ModifyDaemonSet(daemonSet) - return h.postProcess(request, daemonSet, hasBeenModified, logger) + return h.postProcess(request, daemonSet, hasBeenModified, false, logger) } func (h *Handler) handleDeployment( @@ -137,8 +144,11 @@ func (h *Handler) handleDeployment( if failed { return responseIfFailed } + if isIgnored(&deployment.ObjectMeta) { + return h.postProcess(request, deployment, false, true, logger) + } hasBeenModified := h.newWorkloadModifier(logger).ModifyDeployment(deployment) - return h.postProcess(request, deployment, hasBeenModified, logger) + return h.postProcess(request, deployment, hasBeenModified, false, logger) } func (h *Handler) handleJob( @@ -151,8 +161,11 @@ func (h *Handler) handleJob( if failed { return responseIfFailed } + if isIgnored(&job.ObjectMeta) { + return h.postProcess(request, job, false, true, logger) + } hasBeenModified := h.newWorkloadModifier(logger).ModifyJob(job) - return h.postProcess(request, job, hasBeenModified, logger) + return h.postProcess(request, job, hasBeenModified, false, logger) } func (h *Handler) handleReplicaSet( @@ -165,8 +178,11 @@ func (h *Handler) handleReplicaSet( if failed { return responseIfFailed } + if isIgnored(&replicaSet.ObjectMeta) { + return h.postProcess(request, replicaSet, false, true, logger) + } hasBeenModified := h.newWorkloadModifier(logger).ModifyReplicaSet(replicaSet) - return h.postProcess(request, replicaSet, hasBeenModified, logger) + return h.postProcess(request, replicaSet, hasBeenModified, false, logger) } func (h *Handler) handleStatefulSet( @@ -179,8 +195,11 @@ func (h *Handler) handleStatefulSet( if failed { return responseIfFailed } + if isIgnored(&statefulSet.ObjectMeta) { + return h.postProcess(request, statefulSet, false, true, logger) + } hasBeenModified := h.newWorkloadModifier(logger).ModifyStatefulSet(statefulSet) - return h.postProcess(request, statefulSet, hasBeenModified, logger) + return h.postProcess(request, statefulSet, hasBeenModified, false, logger) } func (h *Handler) preProcess( @@ -196,13 +215,25 @@ func (h *Handler) preProcess( return admission.Response{}, false } +func isIgnored(meta *metav1.ObjectMeta) bool { + if meta.Labels == nil { + return false + } + if value, ok := meta.Labels[util.WebhookIgnoreOnceLabelKey]; ok && value == "true" { + delete(meta.Labels, util.WebhookIgnoreOnceLabelKey) + return true + } + return false +} + func (h *Handler) postProcess( request admission.Request, resource runtime.Object, hasBeenModified bool, + ignored bool, logger *logr.Logger, ) admission.Response { - if !hasBeenModified { + if !ignored && !hasBeenModified { logger.Info("Dash0 instrumentation already present, no modification by webhook is necessary.") util.QueueNoInstrumentationNecessaryEvent(h.Recorder, resource, "webhook") return admission.Allowed("no changes") @@ -214,6 +245,12 @@ func (h *Handler) postProcess( return admission.Allowed(fmt.Errorf("error when marshalling modfied resource to JSON: %w", err).Error()) } + if ignored { + logger.Info(fmt.Sprintf("Ignoring this admission request due to the presence of %s.", util.WebhookIgnoreOnceLabelKey)) + // deliberately not queueing an event for this case + return admission.PatchResponseFromRaw(request.Object.Raw, marshalled) + } + logger.Info("The webhook has added Dash0 instrumentation to the workload.") util.QueueSuccessfulInstrumentationEvent(h.Recorder, resource, "webhook") return admission.PatchResponseFromRaw(request.Object.Raw, marshalled) diff --git a/internal/webhook/dash0_webhook_test.go b/internal/webhook/dash0_webhook_test.go index f1615114..7708c883 100644 --- a/internal/webhook/dash0_webhook_test.go +++ b/internal/webhook/dash0_webhook_test.go @@ -6,6 +6,8 @@ package webhook import ( "fmt" + "github.com/dash0hq/dash0-operator/internal/util" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -139,4 +141,61 @@ var _ = Describe("Dash0 Webhook", func() { VerifyModifiedStatefulSet(statefulSet, BasicInstrumentedPodSpecExpectations) }) }) + + Context("when seeing the ignore once label", func() { + It("should not instrument a cron job that has the label, but remove the label", func() { + workload := BasicCronJob(TestNamespaceName, DeploymentName) + AddLabel(&workload.ObjectMeta, util.WebhookIgnoreOnceLabelKey, "true") + CreateWorkload(ctx, k8sClient, workload) + workload = GetCronJob(ctx, k8sClient, TestNamespaceName, DeploymentName) + VerifyUnmodifiedCronJob(workload) + VerifyWebhookIgnoreOnceLabelIsAbesent(&workload.ObjectMeta) + }) + + It("should not instrument a daemonset that has the label, but remove the label", func() { + workload := BasicDaemonSet(TestNamespaceName, DeploymentName) + AddLabel(&workload.ObjectMeta, util.WebhookIgnoreOnceLabelKey, "true") + CreateWorkload(ctx, k8sClient, workload) + workload = GetDaemonSet(ctx, k8sClient, TestNamespaceName, DeploymentName) + VerifyUnmodifiedDaemonSet(workload) + VerifyWebhookIgnoreOnceLabelIsAbesent(&workload.ObjectMeta) + }) + + It("should not instrument a deployment that has the label, but remove the label", func() { + workload := BasicDeployment(TestNamespaceName, DeploymentName) + AddLabel(&workload.ObjectMeta, util.WebhookIgnoreOnceLabelKey, "true") + CreateWorkload(ctx, k8sClient, workload) + workload = GetDeployment(ctx, k8sClient, TestNamespaceName, DeploymentName) + VerifyUnmodifiedDeployment(workload) + VerifyWebhookIgnoreOnceLabelIsAbesent(&workload.ObjectMeta) + }) + + It("should not instrument a job that has the label, but remove the label", func() { + workload := BasicJob(TestNamespaceName, DeploymentName) + AddLabel(&workload.ObjectMeta, util.WebhookIgnoreOnceLabelKey, "true") + CreateWorkload(ctx, k8sClient, workload) + workload = GetJob(ctx, k8sClient, TestNamespaceName, DeploymentName) + VerifyUnmodifiedJob(workload) + VerifyWebhookIgnoreOnceLabelIsAbesent(&workload.ObjectMeta) + }) + + It("should not instrument an orphan replica set that has the label, but remove the label", func() { + workload := BasicReplicaSet(TestNamespaceName, DeploymentName) + AddLabel(&workload.ObjectMeta, util.WebhookIgnoreOnceLabelKey, "true") + CreateWorkload(ctx, k8sClient, workload) + workload = GetReplicaSet(ctx, k8sClient, TestNamespaceName, DeploymentName) + VerifyUnmodifiedReplicaSet(workload) + VerifyWebhookIgnoreOnceLabelIsAbesent(&workload.ObjectMeta) + }) + + It("should not instrument a stateful set that has the label, but remove the label", func() { + workload := BasicStatefulSet(TestNamespaceName, DeploymentName) + AddLabel(&workload.ObjectMeta, util.WebhookIgnoreOnceLabelKey, "true") + CreateWorkload(ctx, k8sClient, workload) + workload = GetStatefulSet(ctx, k8sClient, TestNamespaceName, DeploymentName) + VerifyUnmodifiedStatefulSet(workload) + VerifyWebhookIgnoreOnceLabelIsAbesent(&workload.ObjectMeta) + }) + }) + }) diff --git a/internal/workloads/workload_modifier.go b/internal/workloads/workload_modifier.go index cfff6f13..c9dfa2c7 100644 --- a/internal/workloads/workload_modifier.go +++ b/internal/workloads/workload_modifier.go @@ -288,17 +288,10 @@ func (m *ResourceModifier) addInstrumentationLabels( meta *metav1.ObjectMeta, hasBeenInstrumented bool, ) { - m.addLabel(meta, util.InstrumentedLabelKey, strconv.FormatBool(hasBeenInstrumented)) - m.addLabel(meta, util.OperatorVersionLabelKey, m.instrumentationMetadata.OperatorVersion) - m.addLabel(meta, util.InitContainerImageVersionLabelKey, m.instrumentationMetadata.InitContainerImageVersion) - m.addLabel(meta, util.InstrumentedByLabelKey, m.instrumentationMetadata.InstrumentedBy) -} - -func (m *ResourceModifier) addLabel(meta *metav1.ObjectMeta, key string, value string) { - if meta.Labels == nil { - meta.Labels = make(map[string]string, 1) - } - meta.Labels[key] = value + util.AddLabel(meta, util.InstrumentedLabelKey, strconv.FormatBool(hasBeenInstrumented)) + util.AddLabel(meta, util.OperatorVersionLabelKey, m.instrumentationMetadata.OperatorVersion) + util.AddLabel(meta, util.InitContainerImageVersionLabelKey, m.instrumentationMetadata.InitContainerImageVersion) + util.AddLabel(meta, util.InstrumentedByLabelKey, m.instrumentationMetadata.InstrumentedBy) } func (m *ResourceModifier) RevertCronJob(cronJob *batchv1.CronJob) bool { diff --git a/test/util/resources.go b/test/util/resources.go index 9445d953..f7e7be8f 100644 --- a/test/util/resources.go +++ b/test/util/resources.go @@ -110,14 +110,14 @@ func EnsureTestNamespaceExists( } func BasicCronJob(namespace string, name string) *batchv1.CronJob { - resource := &batchv1.CronJob{} - resource.Namespace = namespace - resource.Name = name - resource.Spec = batchv1.CronJobSpec{} - resource.Spec.Schedule = "*/1 * * * *" - resource.Spec.JobTemplate.Spec.Template = basicPodSpecTemplate() - resource.Spec.JobTemplate.Spec.Template.Spec.RestartPolicy = corev1.RestartPolicyNever - return resource + workload := &batchv1.CronJob{} + workload.Namespace = namespace + workload.Name = name + workload.Spec = batchv1.CronJobSpec{} + workload.Spec.Schedule = "*/1 * * * *" + workload.Spec.JobTemplate.Spec.Template = basicPodSpecTemplate() + workload.Spec.JobTemplate.Spec.Template.Spec.RestartPolicy = corev1.RestartPolicyNever + return workload } func CreateBasicCronJob( @@ -126,13 +126,13 @@ func CreateBasicCronJob( namespace string, name string, ) *batchv1.CronJob { - return createResource(ctx, k8sClient, BasicCronJob(namespace, name)).(*batchv1.CronJob) + return CreateWorkload(ctx, k8sClient, BasicCronJob(namespace, name)).(*batchv1.CronJob) } func InstrumentedCronJob(namespace string, name string) *batchv1.CronJob { - resource := BasicCronJob(namespace, name) - simulateInstrumentedResource(&resource.Spec.JobTemplate.Spec.Template, &resource.ObjectMeta, namespace) - return resource + workload := BasicCronJob(namespace, name) + simulateInstrumentedResource(&workload.Spec.JobTemplate.Spec.Template, &workload.ObjectMeta, namespace) + return workload } func CreateInstrumentedCronJob( @@ -141,17 +141,17 @@ func CreateInstrumentedCronJob( namespace string, name string, ) *batchv1.CronJob { - return createResource(ctx, k8sClient, InstrumentedCronJob(namespace, name)).(*batchv1.CronJob) + return CreateWorkload(ctx, k8sClient, InstrumentedCronJob(namespace, name)).(*batchv1.CronJob) } func BasicDaemonSet(namespace string, name string) *appsv1.DaemonSet { - resource := &appsv1.DaemonSet{} - resource.Namespace = namespace - resource.Name = name - resource.Spec = appsv1.DaemonSetSpec{} - resource.Spec.Template = basicPodSpecTemplate() - resource.Spec.Selector = createSelector() - return resource + workload := &appsv1.DaemonSet{} + workload.Namespace = namespace + workload.Name = name + workload.Spec = appsv1.DaemonSetSpec{} + workload.Spec.Template = basicPodSpecTemplate() + workload.Spec.Selector = createSelector() + return workload } func CreateBasicDaemonSet( @@ -160,13 +160,13 @@ func CreateBasicDaemonSet( namespace string, name string, ) *appsv1.DaemonSet { - return createResource(ctx, k8sClient, BasicDaemonSet(namespace, name)).(*appsv1.DaemonSet) + return CreateWorkload(ctx, k8sClient, BasicDaemonSet(namespace, name)).(*appsv1.DaemonSet) } func InstrumentedDaemonSet(namespace string, name string) *appsv1.DaemonSet { - resource := BasicDaemonSet(namespace, name) - simulateInstrumentedResource(&resource.Spec.Template, &resource.ObjectMeta, namespace) - return resource + workload := BasicDaemonSet(namespace, name) + simulateInstrumentedResource(&workload.Spec.Template, &workload.ObjectMeta, namespace) + return workload } func CreateInstrumentedDaemonSet( @@ -175,17 +175,17 @@ func CreateInstrumentedDaemonSet( namespace string, name string, ) *appsv1.DaemonSet { - return createResource(ctx, k8sClient, InstrumentedDaemonSet(namespace, name)).(*appsv1.DaemonSet) + return CreateWorkload(ctx, k8sClient, InstrumentedDaemonSet(namespace, name)).(*appsv1.DaemonSet) } func BasicDeployment(namespace string, name string) *appsv1.Deployment { - resource := &appsv1.Deployment{} - resource.Namespace = namespace - resource.Name = name - resource.Spec = appsv1.DeploymentSpec{} - resource.Spec.Template = basicPodSpecTemplate() - resource.Spec.Selector = createSelector() - return resource + workload := &appsv1.Deployment{} + workload.Namespace = namespace + workload.Name = name + workload.Spec = appsv1.DeploymentSpec{} + workload.Spec.Template = basicPodSpecTemplate() + workload.Spec.Selector = createSelector() + return workload } func CreateBasicDeployment( @@ -194,13 +194,13 @@ func CreateBasicDeployment( namespace string, name string, ) *appsv1.Deployment { - return createResource(ctx, k8sClient, BasicDeployment(namespace, name)).(*appsv1.Deployment) + return CreateWorkload(ctx, k8sClient, BasicDeployment(namespace, name)).(*appsv1.Deployment) } func InstrumentedDeployment(namespace string, name string) *appsv1.Deployment { - resource := BasicDeployment(namespace, name) - simulateInstrumentedResource(&resource.Spec.Template, &resource.ObjectMeta, namespace) - return resource + workload := BasicDeployment(namespace, name) + simulateInstrumentedResource(&workload.Spec.Template, &workload.ObjectMeta, namespace) + return workload } func CreateInstrumentedDeployment( @@ -209,23 +209,23 @@ func CreateInstrumentedDeployment( namespace string, name string, ) *appsv1.Deployment { - return createResource(ctx, k8sClient, InstrumentedDeployment(namespace, name)).(*appsv1.Deployment) + return CreateWorkload(ctx, k8sClient, InstrumentedDeployment(namespace, name)).(*appsv1.Deployment) } func DeploymentWithInstrumentedFalseLabel(namespace string, name string) *appsv1.Deployment { - resource := BasicDeployment(namespace, name) - addInstrumentationLabels(&resource.ObjectMeta, false) - return resource + workload := BasicDeployment(namespace, name) + addInstrumentationLabels(&workload.ObjectMeta, false) + return workload } func BasicJob(namespace string, name string) *batchv1.Job { - resource := &batchv1.Job{} - resource.Namespace = namespace - resource.Name = name - resource.Spec = batchv1.JobSpec{} - resource.Spec.Template = basicPodSpecTemplate() - resource.Spec.Template.Spec.RestartPolicy = corev1.RestartPolicyNever - return resource + workload := &batchv1.Job{} + workload.Namespace = namespace + workload.Name = name + workload.Spec = batchv1.JobSpec{} + workload.Spec.Template = basicPodSpecTemplate() + workload.Spec.Template.Spec.RestartPolicy = corev1.RestartPolicyNever + return workload } func CreateBasicJob( @@ -234,13 +234,13 @@ func CreateBasicJob( namespace string, name string, ) *batchv1.Job { - return createResource(ctx, k8sClient, BasicJob(namespace, name)).(*batchv1.Job) + return CreateWorkload(ctx, k8sClient, BasicJob(namespace, name)).(*batchv1.Job) } func InstrumentedJob(namespace string, name string) *batchv1.Job { - resource := BasicJob(namespace, name) - simulateInstrumentedResource(&resource.Spec.Template, &resource.ObjectMeta, namespace) - return resource + workload := BasicJob(namespace, name) + simulateInstrumentedResource(&workload.Spec.Template, &workload.ObjectMeta, namespace) + return workload } func CreateInstrumentedJob( @@ -249,13 +249,13 @@ func CreateInstrumentedJob( namespace string, name string, ) *batchv1.Job { - return createResource(ctx, k8sClient, InstrumentedJob(namespace, name)).(*batchv1.Job) + return CreateWorkload(ctx, k8sClient, InstrumentedJob(namespace, name)).(*batchv1.Job) } func JobWithInstrumentationLabels(namespace string, name string) *batchv1.Job { - resource := BasicJob(namespace, name) - addInstrumentationLabels(&resource.ObjectMeta, false) - return resource + workload := BasicJob(namespace, name) + addInstrumentationLabels(&workload.ObjectMeta, false) + return workload } func CreateJobWithInstrumentationLabels( @@ -264,17 +264,17 @@ func CreateJobWithInstrumentationLabels( namespace string, name string, ) *batchv1.Job { - return createResource(ctx, k8sClient, JobWithInstrumentationLabels(namespace, name)).(*batchv1.Job) + return CreateWorkload(ctx, k8sClient, JobWithInstrumentationLabels(namespace, name)).(*batchv1.Job) } func BasicReplicaSet(namespace string, name string) *appsv1.ReplicaSet { - resource := &appsv1.ReplicaSet{} - resource.Namespace = namespace - resource.Name = name - resource.Spec = appsv1.ReplicaSetSpec{} - resource.Spec.Template = basicPodSpecTemplate() - resource.Spec.Selector = createSelector() - return resource + workload := &appsv1.ReplicaSet{} + workload.Namespace = namespace + workload.Name = name + workload.Spec = appsv1.ReplicaSetSpec{} + workload.Spec.Template = basicPodSpecTemplate() + workload.Spec.Selector = createSelector() + return workload } func CreateBasicReplicaSet( @@ -283,13 +283,13 @@ func CreateBasicReplicaSet( namespace string, name string, ) *appsv1.ReplicaSet { - return createResource(ctx, k8sClient, BasicReplicaSet(namespace, name)).(*appsv1.ReplicaSet) + return CreateWorkload(ctx, k8sClient, BasicReplicaSet(namespace, name)).(*appsv1.ReplicaSet) } func InstrumentedReplicaSet(namespace string, name string) *appsv1.ReplicaSet { - resource := BasicReplicaSet(namespace, name) - simulateInstrumentedResource(&resource.Spec.Template, &resource.ObjectMeta, namespace) - return resource + workload := BasicReplicaSet(namespace, name) + simulateInstrumentedResource(&workload.Spec.Template, &workload.ObjectMeta, namespace) + return workload } func CreateInstrumentedReplicaSet( @@ -298,12 +298,12 @@ func CreateInstrumentedReplicaSet( namespace string, name string, ) *appsv1.ReplicaSet { - return createResource(ctx, k8sClient, InstrumentedReplicaSet(namespace, name)).(*appsv1.ReplicaSet) + return CreateWorkload(ctx, k8sClient, InstrumentedReplicaSet(namespace, name)).(*appsv1.ReplicaSet) } func ReplicaSetOwnedByDeployment(namespace string, name string) *appsv1.ReplicaSet { - resource := BasicReplicaSet(namespace, name) - resource.ObjectMeta = metav1.ObjectMeta{ + workload := BasicReplicaSet(namespace, name) + workload.ObjectMeta = metav1.ObjectMeta{ Namespace: namespace, Name: name, OwnerReferences: []metav1.OwnerReference{{ @@ -313,7 +313,7 @@ func ReplicaSetOwnedByDeployment(namespace string, name string) *appsv1.ReplicaS UID: "1234", }}, } - return resource + return workload } func CreateReplicaSetOwnedByDeployment( @@ -322,32 +322,23 @@ func CreateReplicaSetOwnedByDeployment( namespace string, name string, ) *appsv1.ReplicaSet { - return createResource(ctx, k8sClient, ReplicaSetOwnedByDeployment(namespace, name)).(*appsv1.ReplicaSet) + return CreateWorkload(ctx, k8sClient, ReplicaSetOwnedByDeployment(namespace, name)).(*appsv1.ReplicaSet) } func InstrumentedReplicaSetOwnedByDeployment(namespace string, name string) *appsv1.ReplicaSet { - resource := ReplicaSetOwnedByDeployment(namespace, name) - simulateInstrumentedResource(&resource.Spec.Template, &resource.ObjectMeta, namespace) - return resource -} - -func CreateInstrumentedReplicaSetOwnedByDeployment( - ctx context.Context, - k8sClient client.Client, - namespace string, - name string, -) *appsv1.ReplicaSet { - return createResource(ctx, k8sClient, InstrumentedReplicaSetOwnedByDeployment(namespace, name)).(*appsv1.ReplicaSet) + workload := ReplicaSetOwnedByDeployment(namespace, name) + simulateInstrumentedResource(&workload.Spec.Template, &workload.ObjectMeta, namespace) + return workload } func BasicStatefulSet(namespace string, name string) *appsv1.StatefulSet { - resource := &appsv1.StatefulSet{} - resource.Namespace = namespace - resource.Name = name - resource.Spec = appsv1.StatefulSetSpec{} - resource.Spec.Template = basicPodSpecTemplate() - resource.Spec.Selector = createSelector() - return resource + workload := &appsv1.StatefulSet{} + workload.Namespace = namespace + workload.Name = name + workload.Spec = appsv1.StatefulSetSpec{} + workload.Spec.Template = basicPodSpecTemplate() + workload.Spec.Selector = createSelector() + return workload } func CreateBasicStatefulSet( @@ -356,13 +347,13 @@ func CreateBasicStatefulSet( namespace string, name string, ) *appsv1.StatefulSet { - return createResource(ctx, k8sClient, BasicStatefulSet(namespace, name)).(*appsv1.StatefulSet) + return CreateWorkload(ctx, k8sClient, BasicStatefulSet(namespace, name)).(*appsv1.StatefulSet) } func InstrumentedStatefulSet(namespace string, name string) *appsv1.StatefulSet { - resource := BasicStatefulSet(namespace, name) - simulateInstrumentedResource(&resource.Spec.Template, &resource.ObjectMeta, namespace) - return resource + workload := BasicStatefulSet(namespace, name) + simulateInstrumentedResource(&workload.Spec.Template, &workload.ObjectMeta, namespace) + return workload } func CreateInstrumentedStatefulSet( @@ -371,7 +362,7 @@ func CreateInstrumentedStatefulSet( namespace string, name string, ) *appsv1.StatefulSet { - return createResource(ctx, k8sClient, InstrumentedStatefulSet(namespace, name)).(*appsv1.StatefulSet) + return CreateWorkload(ctx, k8sClient, InstrumentedStatefulSet(namespace, name)).(*appsv1.StatefulSet) } func basicPodSpecTemplate() corev1.PodTemplateSpec { @@ -390,14 +381,14 @@ func createSelector() *metav1.LabelSelector { return selector } -func createResource(ctx context.Context, k8sClient client.Client, resource client.Object) client.Object { - Expect(k8sClient.Create(ctx, resource)).Should(Succeed()) - return resource +func CreateWorkload(ctx context.Context, k8sClient client.Client, workload client.Object) client.Object { + Expect(k8sClient.Create(ctx, workload)).Should(Succeed()) + return workload } func DeploymentWithMoreBellsAndWhistles(namespace string, name string) *appsv1.Deployment { - resource := BasicDeployment(namespace, name) - podSpec := &resource.Spec.Template.Spec + workload := BasicDeployment(namespace, name) + podSpec := &workload.Spec.Template.Spec podSpec.Volumes = []corev1.Volume{ { Name: "test-volume-0", @@ -471,7 +462,7 @@ func DeploymentWithMoreBellsAndWhistles(namespace string, name string) *appsv1.D }, } - return resource + return workload } func DeploymentWithExistingDash0Artifacts(namespace string, name string) *appsv1.Deployment { @@ -746,13 +737,13 @@ func GetCronJob( namespace string, name string, ) *batchv1.CronJob { - resource := &batchv1.CronJob{} + workload := &batchv1.CronJob{} namespacedName := types.NamespacedName{ Namespace: namespace, Name: name, } - ExpectWithOffset(1, k8sClient.Get(ctx, namespacedName, resource)).Should(Succeed()) - return resource + ExpectWithOffset(1, k8sClient.Get(ctx, namespacedName, workload)).Should(Succeed()) + return workload } func GetDaemonSet( @@ -761,13 +752,13 @@ func GetDaemonSet( namespace string, name string, ) *appsv1.DaemonSet { - resource := &appsv1.DaemonSet{} + workload := &appsv1.DaemonSet{} namespacedName := types.NamespacedName{ Namespace: namespace, Name: name, } - ExpectWithOffset(1, k8sClient.Get(ctx, namespacedName, resource)).Should(Succeed()) - return resource + ExpectWithOffset(1, k8sClient.Get(ctx, namespacedName, workload)).Should(Succeed()) + return workload } func GetDeployment( @@ -776,13 +767,13 @@ func GetDeployment( namespace string, name string, ) *appsv1.Deployment { - resource := &appsv1.Deployment{} + workload := &appsv1.Deployment{} namespacedName := types.NamespacedName{ Namespace: namespace, Name: name, } - ExpectWithOffset(1, k8sClient.Get(ctx, namespacedName, resource)).Should(Succeed()) - return resource + ExpectWithOffset(1, k8sClient.Get(ctx, namespacedName, workload)).Should(Succeed()) + return workload } func GetJob( @@ -791,13 +782,13 @@ func GetJob( namespace string, name string, ) *batchv1.Job { - resource := &batchv1.Job{} + workload := &batchv1.Job{} namespacedName := types.NamespacedName{ Namespace: namespace, Name: name, } - ExpectWithOffset(1, k8sClient.Get(ctx, namespacedName, resource)).Should(Succeed()) - return resource + ExpectWithOffset(1, k8sClient.Get(ctx, namespacedName, workload)).Should(Succeed()) + return workload } func GetReplicaSet( @@ -806,13 +797,13 @@ func GetReplicaSet( namespace string, name string, ) *appsv1.ReplicaSet { - resource := &appsv1.ReplicaSet{} + workload := &appsv1.ReplicaSet{} namespacedName := types.NamespacedName{ Namespace: namespace, Name: name, } - ExpectWithOffset(1, k8sClient.Get(ctx, namespacedName, resource)).Should(Succeed()) - return resource + ExpectWithOffset(1, k8sClient.Get(ctx, namespacedName, workload)).Should(Succeed()) + return workload } func GetStatefulSet( @@ -821,23 +812,23 @@ func GetStatefulSet( namespace string, name string, ) *appsv1.StatefulSet { - resource := &appsv1.StatefulSet{} + workload := &appsv1.StatefulSet{} namespacedName := types.NamespacedName{ Namespace: namespace, Name: name, } - ExpectWithOffset(1, k8sClient.Get(ctx, namespacedName, resource)).Should(Succeed()) - return resource + ExpectWithOffset(1, k8sClient.Get(ctx, namespacedName, workload)).Should(Succeed()) + return workload } func addInstrumentationLabels(meta *metav1.ObjectMeta, instrumented bool) { - addLabel(meta, util.InstrumentedLabelKey, strconv.FormatBool(instrumented)) - addLabel(meta, util.OperatorVersionLabelKey, "1.2.3") - addLabel(meta, util.InitContainerImageVersionLabelKey, "4.5.6") - addLabel(meta, util.InstrumentedByLabelKey, "someone") + AddLabel(meta, util.InstrumentedLabelKey, strconv.FormatBool(instrumented)) + AddLabel(meta, util.OperatorVersionLabelKey, "1.2.3") + AddLabel(meta, util.InitContainerImageVersionLabelKey, "4.5.6") + AddLabel(meta, util.InstrumentedByLabelKey, "someone") } -func addLabel(meta *metav1.ObjectMeta, key string, value string) { +func AddLabel(meta *metav1.ObjectMeta, key string, value string) { if meta.Labels == nil { meta.Labels = make(map[string]string, 1) } diff --git a/test/util/verification.go b/test/util/verification.go index ee5706b3..ac22681f 100644 --- a/test/util/verification.go +++ b/test/util/verification.go @@ -236,6 +236,14 @@ func verifyNoDash0Labels(meta metav1.ObjectMeta) { Expect(meta.Labels["dash0.instrumented.by"]).To(Equal("")) } +func VerifyWebhookIgnoreOnceLabelIsPresent(objectMeta *metav1.ObjectMeta) bool { + return Expect(objectMeta.Labels["dash0.webhook.ignore.once"]).To(Equal("true")) +} + +func VerifyWebhookIgnoreOnceLabelIsAbesent(objectMeta *metav1.ObjectMeta) bool { + return Expect(objectMeta.Labels["dash0.webhook.ignore.once"]).To(Equal("")) +} + func VerifyNoEvents( ctx context.Context, clientset *kubernetes.Clientset,