From 876fed4a403414e61ed76bfa89e44671aaf792cc Mon Sep 17 00:00:00 2001 From: Dominic Sciascia Date: Mon, 15 Sep 2025 18:04:18 +0000 Subject: [PATCH 1/5] Flag to automatically roll operator deployment --- .../templates/operator-deployment.yaml | 3 +++ charts/amazon-cloudwatch-observability/values.yaml | 2 ++ 2 files changed, 5 insertions(+) diff --git a/charts/amazon-cloudwatch-observability/templates/operator-deployment.yaml b/charts/amazon-cloudwatch-observability/templates/operator-deployment.yaml index e3bc23d..5ac9dec 100644 --- a/charts/amazon-cloudwatch-observability/templates/operator-deployment.yaml +++ b/charts/amazon-cloudwatch-observability/templates/operator-deployment.yaml @@ -18,6 +18,9 @@ spec: {{- if .Values.manager.podAnnotations }} {{- include "amazon-cloudwatch-observability.podAnnotations" . | nindent 8 }} {{- end }} + {{- if .Values.manager.rolling }} + rollme: {{ randAlphaNum 5 | quote }} + {{- end }} labels: app.kubernetes.io/name: {{ template "amazon-cloudwatch-observability.name" . }} control-plane: controller-manager diff --git a/charts/amazon-cloudwatch-observability/values.yaml b/charts/amazon-cloudwatch-observability/values.yaml index 9d02a84..7e838dd 100644 --- a/charts/amazon-cloudwatch-observability/values.yaml +++ b/charts/amazon-cloudwatch-observability/values.yaml @@ -1197,6 +1197,8 @@ manager: affinity: {} nodeSelector: kubernetes.io/os: linux + # Enable automatic rolling by forcing a deployment spec change + rolling: false ## Admission webhooks make sure only requests with correctly formatted rules will get into the Operator. admissionWebhooks: create: true From 5b0845d1ce039e0251b28f008bc7182e06a86137 Mon Sep 17 00:00:00 2001 From: Dominic Sciascia Date: Wed, 17 Sep 2025 18:36:36 +0000 Subject: [PATCH 2/5] Add recreate flag for auto-generated certs --- .../templates/_helpers.tpl | 30 +++++++++++++++++++ .../admission-webhooks/operator-webhook.yaml | 26 ++++++++-------- .../values.yaml | 1 + 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/charts/amazon-cloudwatch-observability/templates/_helpers.tpl b/charts/amazon-cloudwatch-observability/templates/_helpers.tpl index 91574ee..4cc484c 100644 --- a/charts/amazon-cloudwatch-observability/templates/_helpers.tpl +++ b/charts/amazon-cloudwatch-observability/templates/_helpers.tpl @@ -342,3 +342,33 @@ Define the default service name {{- define "amazon-cloudwatch-observability.webhookServiceName" -}} {{- default (printf "%s-webhook-service" (include "amazon-cloudwatch-observability.name" .)) .Values.manager.service.name }} {{- end -}} + +{{/* +Returns auto-generated certificate and CA for admission webhooks. +*/}} +{{- define "amazon-cloudwatch-observability.webhookCert -}} +{{- $tlsCrt := "" }} +{{- $tlsKey := "" }} +{{- $caCrt := "" }} +{{- if .Values.admissionWebhooks.autoGenerateCert.enabled }} +{{- $existingCert := ( lookup "v1" "Secrets" .Release.Namespace (template "amazon-cloudwatch-observability.certificateSecretName" .) ) }} +{{- if and (not .Values.admissionWebhooks.autoGenerateCert.recreate) $existingCert }} +{{- $tlsCrt = index $existingCert "data" "tls.crt" }} +{{- $tlsKey = index $existingCert "data" "tls.key" }} +{{- $caCrt = index $existingCert "data" "ca.crt" }} +{{- if not $caCrt }} +{{- $existingWebhook := ( lookup "admissionregistration.k8s.io/v1" "MutatingWebhookConfiguration" "" (printf "%s-mutating-webhook-configuration" (include "amazon-cloudwatch-observability.name" .)) ) }} +{{- $caCrt = (first $existingWebhook.webhooks).clientConfig.caBundle }} +{{- end }} +{{- else }} +{{- $altNames := list ( printf "%s-webhook-service.%s" (include "amazon-cloudwatch-observability.name" .) .Release.Namespace ) ( printf "%s-webhook-service.%s.svc" (include "amazon-cloudwatch-observability.name" .) .Release.Namespace ) ( printf "%s-webhook-service.%s.svc.cluster.local" (include "amazon-cloudwatch-observability.name" .) .Release.Namespace ) -}} +{{- $ca := genCA ( printf "%s-ca" (include "amazon-cloudwatch-observability.name" .) ) ( .Values.admissionWebhooks.autoGenerateCert.expiryDays | int ) -}} +{{- $cert := genSignedCert (include "amazon-cloudwatch-observability.name" .) nil $altNames ( .Values.admissionWebhooks.autoGenerateCert.expiryDays | int ) $ca -}} +{{- $tlsCrt = b64enc $cert.Cert }} +{{- $tlsKey = b64enc $cert.Key }} +{{- $caCrt = b64enc $ca.Cert }} +{{- end }} +{{- $result := dict "Cert" $tlsCrt "Key" $tlsKey "Ca" $caCrt }} +{{- $result | toYaml }} +{{- end }} +{{- end }} diff --git a/charts/amazon-cloudwatch-observability/templates/admission-webhooks/operator-webhook.yaml b/charts/amazon-cloudwatch-observability/templates/admission-webhooks/operator-webhook.yaml index a091366..c69097e 100644 --- a/charts/amazon-cloudwatch-observability/templates/admission-webhooks/operator-webhook.yaml +++ b/charts/amazon-cloudwatch-observability/templates/admission-webhooks/operator-webhook.yaml @@ -1,7 +1,5 @@ {{- if and (.Values.admissionWebhooks.create) (.Values.admissionWebhooks.autoGenerateCert.enabled) (not .Values.admissionWebhooks.certManager.enabled) }} -{{- $altNames := list ( printf "%s-webhook-service.%s" (include "amazon-cloudwatch-observability.name" .) .Release.Namespace ) ( printf "%s-webhook-service.%s.svc" (include "amazon-cloudwatch-observability.name" .) .Release.Namespace ) ( printf "%s-webhook-service.%s.svc.cluster.local" (include "amazon-cloudwatch-observability.name" .) .Release.Namespace ) -}} -{{- $ca := genCA ( printf "%s-ca" (include "amazon-cloudwatch-observability.name" .) ) ( .Values.admissionWebhooks.autoGenerateCert.expiryDays | int ) -}} -{{- $cert := genSignedCert (include "amazon-cloudwatch-observability.name" .) nil $altNames ( .Values.admissionWebhooks.autoGenerateCert.expiryDays | int ) $ca -}} +{{- $cert := fromYaml (include "amazon-cloudwatch-observability.webhookCert") }} apiVersion: v1 kind: Secret type: kubernetes.io/tls @@ -11,8 +9,8 @@ metadata: name: {{ template "amazon-cloudwatch-observability.certificateSecretName" . }} namespace: {{ .Release.Namespace }} data: - tls.crt: {{ $cert.Cert | b64enc }} - tls.key: {{ $cert.Key | b64enc }} + tls.crt: {{ $cert.Cert }} + tls.key: {{ $cert.Key }} --- apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration @@ -28,7 +26,7 @@ webhooks: name: {{ template "amazon-cloudwatch-observability.webhookServiceName" . }} namespace: {{ .Release.Namespace }} path: /mutate-cloudwatch-aws-amazon-com-v1alpha1-instrumentation - caBundle: {{ $ca.Cert | b64enc }} + caBundle: {{ $cert.Ca }} failurePolicy: {{ .Values.admissionWebhooks.failurePolicy }} name: minstrumentation.kb.io {{- if .Values.admissionWebhooks.namespaceSelector }} @@ -58,7 +56,7 @@ webhooks: name: {{ template "amazon-cloudwatch-observability.webhookServiceName" . }} namespace: {{ .Release.Namespace }} path: /mutate-cloudwatch-aws-amazon-com-v1alpha1-amazoncloudwatchagent - caBundle: {{ $ca.Cert | b64enc }} + caBundle: {{ $cert.Ca }} failurePolicy: {{ .Values.admissionWebhooks.failurePolicy }} name: mamazoncloudwatchagent.kb.io {{- if .Values.admissionWebhooks.namespaceSelector }} @@ -88,7 +86,7 @@ webhooks: name: {{ template "amazon-cloudwatch-observability.webhookServiceName" . }} namespace: {{ .Release.Namespace }} path: /mutate-v1-pod - caBundle: {{ $ca.Cert | b64enc }} + caBundle: {{ $cert.Ca }} failurePolicy: {{ .Values.admissionWebhooks.pods.failurePolicy }} name: mpod.kb.io {{- if .Values.admissionWebhooks.namespaceSelector }} @@ -118,7 +116,7 @@ webhooks: name: {{ template "amazon-cloudwatch-observability.webhookServiceName" . }} namespace: {{ .Release.Namespace }} path: /mutate-v1-namespace - caBundle: {{ $ca.Cert | b64enc }} + caBundle: {{ $cert.Ca }} failurePolicy: {{ .Values.admissionWebhooks.pods.failurePolicy }} name: mnamespace.kb.io {{- if .Values.admissionWebhooks.namespaceSelector }} @@ -148,7 +146,7 @@ webhooks: name: {{ template "amazon-cloudwatch-observability.webhookServiceName" . }} namespace: {{ .Release.Namespace }} path: /mutate-v1-workload - caBundle: {{ $ca.Cert | b64enc }} + caBundle: {{ $cert.Ca }} failurePolicy: {{ .Values.admissionWebhooks.pods.failurePolicy }} name: mworkload.kb.io {{- if .Values.admissionWebhooks.namespaceSelector }} @@ -188,7 +186,7 @@ webhooks: name: {{ template "amazon-cloudwatch-observability.webhookServiceName" . }} namespace: {{ .Release.Namespace }} path: /validate-cloudwatch-aws-amazon-com-v1alpha1-instrumentation - caBundle: {{ $ca.Cert | b64enc }} + caBundle: {{ $cert.Ca }} failurePolicy: {{ .Values.admissionWebhooks.failurePolicy }} name: vinstrumentationcreateupdate.kb.io {{- if .Values.admissionWebhooks.namespaceSelector }} @@ -218,7 +216,7 @@ webhooks: name: {{ template "amazon-cloudwatch-observability.webhookServiceName" . }} namespace: {{ .Release.Namespace }} path: /validate-cloudwatch-aws-amazon-com-v1alpha1-instrumentation - caBundle: {{ $ca.Cert | b64enc }} + caBundle: {{ $cert.Ca }} failurePolicy: Ignore name: vinstrumentationdelete.kb.io {{- if .Values.admissionWebhooks.namespaceSelector }} @@ -247,7 +245,7 @@ webhooks: name: {{ template "amazon-cloudwatch-observability.webhookServiceName" . }} namespace: {{ .Release.Namespace }} path: /validate-cloudwatch-aws-amazon-com-v1alpha1-amazoncloudwatchagent - caBundle: {{ $ca.Cert | b64enc }} + caBundle: {{ $cert.Ca }} failurePolicy: {{ .Values.admissionWebhooks.failurePolicy }} name: vamazoncloudwatchagentcreateupdate.kb.io {{- if .Values.admissionWebhooks.namespaceSelector }} @@ -277,7 +275,7 @@ webhooks: name: {{ template "amazon-cloudwatch-observability.webhookServiceName" . }} namespace: {{ .Release.Namespace }} path: /validate-cloudwatch-aws-amazon-com-v1alpha1-amazoncloudwatchagent - caBundle: {{ $ca.Cert | b64enc }} + caBundle: {{ $cert.Ca }} failurePolicy: Ignore name: vamazoncloudwatchagentdelete.kb.io {{- if .Values.admissionWebhooks.namespaceSelector }} diff --git a/charts/amazon-cloudwatch-observability/values.yaml b/charts/amazon-cloudwatch-observability/values.yaml index 7e838dd..4dbc942 100644 --- a/charts/amazon-cloudwatch-observability/values.yaml +++ b/charts/amazon-cloudwatch-observability/values.yaml @@ -1223,6 +1223,7 @@ admissionWebhooks: autoGenerateCert: enabled: true expiryDays: 3650 # 10 years + recreate: true ## TLS Certificate Option 2: Use certManager to generate self-signed certificate. ## certManager must be enabled. If enabled, it takes precedence over option 1. certManager: From 63e7aa3d16024bd7c0dbb1bf087895a25327bcf6 Mon Sep 17 00:00:00 2001 From: Dominic Sciascia Date: Mon, 22 Sep 2025 20:08:49 +0000 Subject: [PATCH 3/5] Add tests for rolling and cert recreate --- ...dwatch-observability-integration-test.yaml | 5 +- .../certificate-recreate-disabled/main.tf | 31 ++++++++++ .../certificate-recreate-disabled/values.yaml | 6 ++ .../certificate-recreate-enabled/main.tf | 30 ++++++++++ .../certificate-recreate-enabled/values.yaml | 6 ++ .../deployment-rolling-disabled/main.tf | 28 +++++++++ .../deployment-rolling-disabled/values.yaml | 5 ++ .../deployment-rolling-enabled/main.tf | 33 +++++++++++ .../deployment-rolling-enabled/values.yaml | 6 ++ .../certificate_recreate_disabled_test.go | 59 +++++++++++++++++++ .../certificate_recreate_enabled_test.go | 39 ++++++++++++ .../deployment_rolling_disabled_test.go | 32 ++++++++++ .../deployment_rolling_enabled_test.go | 54 +++++++++++++++++ 13 files changed, 332 insertions(+), 2 deletions(-) create mode 100644 integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/certificate-recreate-disabled/main.tf create mode 100644 integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/certificate-recreate-disabled/values.yaml create mode 100644 integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/certificate-recreate-enabled/main.tf create mode 100644 integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/certificate-recreate-enabled/values.yaml create mode 100644 integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/deployment-rolling-disabled/main.tf create mode 100644 integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/deployment-rolling-disabled/values.yaml create mode 100644 integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/deployment-rolling-enabled/main.tf create mode 100644 integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/deployment-rolling-enabled/values.yaml create mode 100644 integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/certificate_recreate_disabled_test.go create mode 100644 integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/certificate_recreate_enabled_test.go create mode 100644 integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/deployment_rolling_disabled_test.go create mode 100644 integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/deployment_rolling_enabled_test.go diff --git a/.github/workflows/amazon-cloudwatch-observability-integration-test.yaml b/.github/workflows/amazon-cloudwatch-observability-integration-test.yaml index 1dee48f..2345612 100644 --- a/.github/workflows/amazon-cloudwatch-observability-integration-test.yaml +++ b/.github/workflows/amazon-cloudwatch-observability-integration-test.yaml @@ -39,7 +39,8 @@ jobs: timeout-minutes: 30 strategy: matrix: - scenario: [default, appsignals-disabled, appsignals-disabled-multi-agents, appsignals-enabled-multi-agents] + # TODO: add webhooks-disabled + scenario: [default, appsignals-disabled, appsignals-disabled-multi-agents, appsignals-enabled-multi-agents, webhooks-partially-enabled, webhooks-configured, deployment-rolling-enabled, deployment-rolling-disabled, certificate-recreate-enabled, certificate-recreate-disabled] steps: - uses: actions/checkout@v3 @@ -247,4 +248,4 @@ jobs: retry_wait_seconds: 5 command: | cd integration-tests/amazon-cloudwatch-observability/terraform/eks/windows - terraform destroy --auto-approve \ No newline at end of file + terraform destroy --auto-approve diff --git a/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/certificate-recreate-disabled/main.tf b/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/certificate-recreate-disabled/main.tf new file mode 100644 index 0000000..cc9cbf0 --- /dev/null +++ b/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/certificate-recreate-disabled/main.tf @@ -0,0 +1,31 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +module "base" { + source = "../.." + helm_dir = var.helm_dir + helm_values_file = "${path.module}/values.yaml" +} + +variable "helm_dir" { + type = string + default = "../../../../../../charts/amazon-cloudwatch-observability" +} + +resource "null_resource" "validator" { + depends_on = [module.base.helm_release] + + provisioner "local-exec" { + command = <<-EOT + go test ${var.test_dir} -v -run=TestCertificateRecreateDisabled_Save + helm upgrade --wait --create-namespace --namespace amazon-cloudwatch amazon-cloudwatch-observability ${var.helm_dir} -f ${path.module}/values.yaml + go test ${var.test_dir} -v -run=TestCertificateRecreateDisabled_Compare + EOT + } +} + +variable "test_dir" { + type = string + default = "../../../../validations/minikube/scenarios" +} + diff --git a/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/certificate-recreate-disabled/values.yaml b/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/certificate-recreate-disabled/values.yaml new file mode 100644 index 0000000..f6c137b --- /dev/null +++ b/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/certificate-recreate-disabled/values.yaml @@ -0,0 +1,6 @@ +region: us-west-2 +clusterName: minikube + +admissionWebhooks: + autoGenerateCert: + recreate: false diff --git a/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/certificate-recreate-enabled/main.tf b/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/certificate-recreate-enabled/main.tf new file mode 100644 index 0000000..fd6906c --- /dev/null +++ b/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/certificate-recreate-enabled/main.tf @@ -0,0 +1,30 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +module "base" { + source = "../.." + helm_dir = var.helm_dir + helm_values_file = "${path.module}/values.yaml" +} + +variable "helm_dir" { + type = string + default = "../../../../../../charts/amazon-cloudwatch-observability" +} + +resource "null_resource" "validator" { + depends_on = [module.base.helm_release] + + provisioner "local-exec" { + command = <<-EOT + go test ${var.test_dir} -v -run=TestCertificateRecreateEnabled_Save + helm upgrade --wait --create-namespace --namespace amazon-cloudwatch amazon-cloudwatch-observability ${var.helm_dir} -f ${path.module}/values.yaml + go test ${var.test_dir} -v -run=TestCertificateRecreateEnabled_Compare + EOT + } +} + +variable "test_dir" { + type = string + default = "../../../../validations/minikube/scenarios" +} diff --git a/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/certificate-recreate-enabled/values.yaml b/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/certificate-recreate-enabled/values.yaml new file mode 100644 index 0000000..b053ca6 --- /dev/null +++ b/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/certificate-recreate-enabled/values.yaml @@ -0,0 +1,6 @@ +region: us-west-2 +clusterName: minikube + +admissionWebhooks: + autoGenerateCert: + recreate: true diff --git a/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/deployment-rolling-disabled/main.tf b/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/deployment-rolling-disabled/main.tf new file mode 100644 index 0000000..875f7ac --- /dev/null +++ b/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/deployment-rolling-disabled/main.tf @@ -0,0 +1,28 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +module "base" { + source = "../.." + helm_dir = var.helm_dir + helm_values_file = "${path.module}/values.yaml" +} + +variable "helm_dir" { + type = string + default = "../../../../../../charts/amazon-cloudwatch-observability" +} + +resource "null_resource" "validator" { + depends_on = [module.base.helm_release] + + provisioner "local-exec" { + command = "go test ${var.test_dir} -v -run=TestDeploymentRollingDisabled" + } +} + +variable "test_dir" { + type = string + default = "../../../../validations/minikube/scenarios" +} + + diff --git a/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/deployment-rolling-disabled/values.yaml b/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/deployment-rolling-disabled/values.yaml new file mode 100644 index 0000000..cb285a8 --- /dev/null +++ b/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/deployment-rolling-disabled/values.yaml @@ -0,0 +1,5 @@ +region: us-west-2 +clusterName: minikube + +manager: + rolling: false diff --git a/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/deployment-rolling-enabled/main.tf b/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/deployment-rolling-enabled/main.tf new file mode 100644 index 0000000..a73af09 --- /dev/null +++ b/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/deployment-rolling-enabled/main.tf @@ -0,0 +1,33 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +module "base" { + source = "../.." + helm_dir = var.helm_dir + helm_values_file = "${path.module}/values.yaml" +} + +variable "helm_dir" { + type = string + default = "../../../../../../charts/amazon-cloudwatch-observability" +} + +resource "null_resource" "validator" { + depends_on = [module.base.helm_release] + + provisioner "local-exec" { + command = <<-EOT + go test ${var.test_dir} -v -run=TestDeploymentRollingEnabled_Save + helm upgrade --wait --create-namespace --namespace amazon-cloudwatch amazon-cloudwatch-observability ${var.helm_dir} -f ${path.module}/values.yaml + go test ${var.test_dir} -v -run=TestDeploymentRollingEnabled_Compare + EOT + } +} + +variable "test_dir" { + type = string + default = "../../../../validations/minikube/scenarios" +} + + + diff --git a/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/deployment-rolling-enabled/values.yaml b/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/deployment-rolling-enabled/values.yaml new file mode 100644 index 0000000..4e1150f --- /dev/null +++ b/integration-tests/amazon-cloudwatch-observability/terraform/minikube/scenarios/deployment-rolling-enabled/values.yaml @@ -0,0 +1,6 @@ +region: us-west-2 +clusterName: minikube + +manager: + rolling: true + diff --git a/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/certificate_recreate_disabled_test.go b/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/certificate_recreate_disabled_test.go new file mode 100644 index 0000000..b026afd --- /dev/null +++ b/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/certificate_recreate_disabled_test.go @@ -0,0 +1,59 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package scenarios + +import ( + "bytes" + "os" + "testing" + + "github.com/aws-observability/helm-charts/integration-tests/amazon-cloudwatch-observability/util" + "github.com/aws-observability/helm-charts/integration-tests/amazon-cloudwatch-observability/validations/minikube" + "github.com/stretchr/testify/assert" +) + +const certificateRecreateDisabledPath = "/tmp/test-certificate-recreate-disabled.bin" + +func TestCertificateRecreateDisabled_Save(t *testing.T) { + k8sClient, err := util.NewK8sClient() + assert.NoError(t, err) + + data := retrieveCABundle(t, *k8sClient) + assert.NotNil(t, data) + + err = os.WriteFile(certificateRecreateDisabledPath, data, 0644) + assert.NoError(t, err) +} + +func TestCertificateRecreateDisabled_Compare(t *testing.T) { + k8sClient, err := util.NewK8sClient() + assert.NoError(t, err) + + actualData := retrieveCABundle(t, *k8sClient) + assert.NotNil(t, actualData) + + savedData, err := os.ReadFile(certificateRecreateDisabledPath) + assert.NoError(t, err) + + assert.True(t, bytes.Equal(actualData, savedData)) +} + +func retrieveCABundle(t *testing.T, k8sClient util.K8sClient) []byte { + whs, err := k8sClient.ListMutatingWebhookConfigurations() + assert.NoError(t, err) + assert.NotEmpty(t, whs.Items) + + for _, item := range whs.Items { + if item.ObjectMeta.Name != minikube.WebhookName { + continue + } + assert.NotEmpty(t, item.Webhooks) + assert.GreaterOrEqual(t, len(item.Webhooks), 1) + + // Grab the first CA bundle + data := item.Webhooks[0].ClientConfig.CABundle + return data + } + return nil +} diff --git a/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/certificate_recreate_enabled_test.go b/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/certificate_recreate_enabled_test.go new file mode 100644 index 0000000..2efa086 --- /dev/null +++ b/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/certificate_recreate_enabled_test.go @@ -0,0 +1,39 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package scenarios + +import ( + "bytes" + "os" + "testing" + + "github.com/aws-observability/helm-charts/integration-tests/amazon-cloudwatch-observability/util" + "github.com/stretchr/testify/assert" +) + +const certificateRecreateEnabledPath = "/tmp/test-certificate-recreate-enabled.bin" + +func TestCertificateRecreateEnabled_Save(t *testing.T) { + k8sClient, err := util.NewK8sClient() + assert.NoError(t, err) + + data := retrieveCABundle(t, *k8sClient) + assert.NotNil(t, data) + + err = os.WriteFile(certificateRecreateEnabledPath, data, 0644) + assert.NoError(t, err) +} + +func TestCertificateRecreateEnabled_Compare(t *testing.T) { + k8sClient, err := util.NewK8sClient() + assert.NoError(t, err) + + actualData := retrieveCABundle(t, *k8sClient) + assert.NotNil(t, actualData) + + savedData, err := os.ReadFile(certificateRecreateEnabledPath) + assert.NoError(t, err) + + assert.False(t, bytes.Equal(actualData, savedData)) +} diff --git a/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/deployment_rolling_disabled_test.go b/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/deployment_rolling_disabled_test.go new file mode 100644 index 0000000..57f2877 --- /dev/null +++ b/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/deployment_rolling_disabled_test.go @@ -0,0 +1,32 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package scenarios + +import ( + "testing" + + "github.com/aws-observability/helm-charts/integration-tests/amazon-cloudwatch-observability/util" + "github.com/stretchr/testify/assert" +) + +func TestDeploymentRollingDisabled(t *testing.T) { + k8sClient, err := util.NewK8sClient() + assert.NoError(t, err) + + ds, err := k8sClient.ListDeployments("amazon-cloudwatch") + assert.NoError(t, err) + + found := false + for _, d := range ds.Items { + if d.GetName() != "amazon-cloudwatch-observability-controller-manager" { + continue + } else { + found = true + } + + _, exists := d.Spec.Template.Annotations["rollme"] + assert.False(t, exists) + } + assert.True(t, found) +} diff --git a/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/deployment_rolling_enabled_test.go b/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/deployment_rolling_enabled_test.go new file mode 100644 index 0000000..5c0611c --- /dev/null +++ b/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/deployment_rolling_enabled_test.go @@ -0,0 +1,54 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package scenarios + +import ( + "bytes" + "os" + "testing" + + "github.com/aws-observability/helm-charts/integration-tests/amazon-cloudwatch-observability/util" + "github.com/stretchr/testify/assert" +) + +const deploymentRollingEnabledPath = "/tmp/test-deployment-rolling-enabled.bin" + +func TestDeploymentRollingEnabled_Save(t *testing.T) { + k8sClient, err := util.NewK8sClient() + assert.NoError(t, err) + + data := retrieveRollMe(t, *k8sClient) + assert.NotNil(t, data) + + err = os.WriteFile(deploymentRollingEnabledPath, data, 0644) + assert.NoError(t, err) +} + +func TestDeploymentRollingEnabled_Compare(t *testing.T) { + k8sClient, err := util.NewK8sClient() + assert.NoError(t, err) + + actualData := retrieveRollMe(t, *k8sClient) + assert.NotNil(t, actualData) + + savedData, err := os.ReadFile(deploymentRollingEnabledPath) + assert.NoError(t, err) + + assert.False(t, bytes.Equal(actualData, savedData)) +} + +func retrieveRollMe(t *testing.T, k8sClient util.K8sClient) []byte { + ds, err := k8sClient.ListDeployments("amazon-cloudwatch") + assert.NoError(t, err) + + for _, d := range ds.Items { + if d.GetName() != "amazon-cloudwatch-observability-controller-manager" { + continue + } + + value := d.Spec.Template.Annotations["rollme"] + return []byte(value) + } + return nil +} From b9660654b95965a3571c944986e2f0d0ecd432c7 Mon Sep 17 00:00:00 2001 From: Dominic Sciascia Date: Thu, 25 Sep 2025 22:28:59 -0400 Subject: [PATCH 4/5] Fix after rebase --- .../templates/_helpers.tpl | 55 ++++++++++++++++++- .../admission-webhooks/operator-webhook.yaml | 18 +++--- 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/charts/amazon-cloudwatch-observability/templates/_helpers.tpl b/charts/amazon-cloudwatch-observability/templates/_helpers.tpl index 4cc484c..c73ab0f 100644 --- a/charts/amazon-cloudwatch-observability/templates/_helpers.tpl +++ b/charts/amazon-cloudwatch-observability/templates/_helpers.tpl @@ -343,15 +343,66 @@ Define the default service name {{- default (printf "%s-webhook-service" (include "amazon-cloudwatch-observability.name" .)) .Values.manager.service.name }} {{- end -}} +{{/* +Check if a specific admission webhook is enabled +*/}} +{{- define "amazon-cloudwatch-observability.isWebhookEnabled" -}} +{{- $ctx := index . 0 -}} +{{- $webhook := index . 1 -}} +{{- $webhookConfig := index $ctx.Values.admissionWebhooks $webhook -}} +{{- if hasKey $webhookConfig "create" -}} +{{- if $webhookConfig.create }}true{{- end -}} +{{- else -}} +{{- if $ctx.Values.admissionWebhooks.create }}true{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Check if any admission webhook is enabled +*/}} +{{- define "amazon-cloudwatch-observability.webhookEnabled" -}} +{{- $webhooks := list "agents" "instrumentations" "pods" "workloads" "namespaces" -}} +{{- range $webhook := $webhooks -}} +{{- if include "amazon-cloudwatch-observability.isWebhookEnabled" (list $ $webhook) -}} +true +{{- break -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Get namespaceSelector value for admission webhooks +*/}} +{{- define "amazon-cloudwatch-observability.namespaceSelector" -}} +{{- $ctx := index . 0 -}} +{{- $webhook := index . 1 -}} +{{- $webhookConfig := index $ctx.Values.admissionWebhooks $webhook -}} +{{- if and (hasKey $webhookConfig "namespaceSelector") (ne $webhookConfig.namespaceSelector nil) -}} +{{- $selector := $webhookConfig.namespaceSelector -}} +{{- if $selector -}} +{{- toYaml $selector | nindent 4 -}} +{{- else -}} +{} +{{- end -}} +{{- else -}} +{{- $selector := $ctx.Values.admissionWebhooks.namespaceSelector -}} +{{- if $selector -}} +{{- toYaml $selector | nindent 4 -}} +{{- else -}} +{} +{{- end -}} +{{- end -}} +{{- end -}} + {{/* Returns auto-generated certificate and CA for admission webhooks. */}} -{{- define "amazon-cloudwatch-observability.webhookCert -}} +{{- define "amazon-cloudwatch-observability.webhookCert" -}} {{- $tlsCrt := "" }} {{- $tlsKey := "" }} {{- $caCrt := "" }} {{- if .Values.admissionWebhooks.autoGenerateCert.enabled }} -{{- $existingCert := ( lookup "v1" "Secrets" .Release.Namespace (template "amazon-cloudwatch-observability.certificateSecretName" .) ) }} +{{- $existingCert := ( lookup "v1" "Secret" .Release.Namespace (include "amazon-cloudwatch-observability.certificateSecretName" .) ) }} {{- if and (not .Values.admissionWebhooks.autoGenerateCert.recreate) $existingCert }} {{- $tlsCrt = index $existingCert "data" "tls.crt" }} {{- $tlsKey = index $existingCert "data" "tls.key" }} diff --git a/charts/amazon-cloudwatch-observability/templates/admission-webhooks/operator-webhook.yaml b/charts/amazon-cloudwatch-observability/templates/admission-webhooks/operator-webhook.yaml index b34e49d..d34897a 100644 --- a/charts/amazon-cloudwatch-observability/templates/admission-webhooks/operator-webhook.yaml +++ b/charts/amazon-cloudwatch-observability/templates/admission-webhooks/operator-webhook.yaml @@ -1,5 +1,5 @@ -{{- if and (.Values.admissionWebhooks.create) (.Values.admissionWebhooks.autoGenerateCert.enabled) (not .Values.admissionWebhooks.certManager.enabled) }} -{{- $cert := fromYaml (include "amazon-cloudwatch-observability.webhookCert") }} +{{- if and (.Values.admissionWebhooks.autoGenerateCert.enabled) (not .Values.admissionWebhooks.certManager.enabled) (include "amazon-cloudwatch-observability.webhookEnabled" .) }} +{{- $cert := fromYaml (include "amazon-cloudwatch-observability.webhookCert" .) }} apiVersion: v1 kind: Secret type: kubernetes.io/tls @@ -27,7 +27,7 @@ webhooks: name: {{ template "amazon-cloudwatch-observability.webhookServiceName" . }} namespace: {{ .Release.Namespace }} path: /mutate-cloudwatch-aws-amazon-com-v1alpha1-instrumentation - caBundle: {{ $ca.Cert | b64enc }} + caBundle: {{ $cert.Ca }} failurePolicy: {{ .Values.admissionWebhooks.instrumentations.failurePolicy | default .Values.admissionWebhooks.failurePolicy }} name: minstrumentation.kb.io namespaceSelector: {{ include "amazon-cloudwatch-observability.namespaceSelector" (list . "instrumentations") }} @@ -56,7 +56,7 @@ webhooks: name: {{ template "amazon-cloudwatch-observability.webhookServiceName" . }} namespace: {{ .Release.Namespace }} path: /mutate-cloudwatch-aws-amazon-com-v1alpha1-amazoncloudwatchagent - caBundle: {{ $ca.Cert | b64enc }} + caBundle: {{ $cert.Ca }} failurePolicy: {{ .Values.admissionWebhooks.agents.failurePolicy | default .Values.admissionWebhooks.failurePolicy }} name: mamazoncloudwatchagent.kb.io namespaceSelector: {{ include "amazon-cloudwatch-observability.namespaceSelector" (list . "agents") }} @@ -85,7 +85,7 @@ webhooks: name: {{ template "amazon-cloudwatch-observability.webhookServiceName" . }} namespace: {{ .Release.Namespace }} path: /mutate-v1-pod - caBundle: {{ $ca.Cert | b64enc }} + caBundle: {{ $cert.Ca }} failurePolicy: {{ .Values.admissionWebhooks.pods.failurePolicy | default .Values.admissionWebhooks.failurePolicy }} name: mpod.kb.io namespaceSelector: {{ include "amazon-cloudwatch-observability.namespaceSelector" (list . "pods") }} @@ -114,7 +114,7 @@ webhooks: name: {{ template "amazon-cloudwatch-observability.webhookServiceName" . }} namespace: {{ .Release.Namespace }} path: /mutate-v1-namespace - caBundle: {{ $ca.Cert | b64enc }} + caBundle: {{ $cert.Ca }} failurePolicy: {{ .Values.admissionWebhooks.namespaces.failurePolicy | default .Values.admissionWebhooks.pods.failurePolicy | default .Values.admissionWebhooks.failurePolicy }} name: mnamespace.kb.io namespaceSelector: {{ include "amazon-cloudwatch-observability.namespaceSelector" (list . "namespaces") }} @@ -143,7 +143,7 @@ webhooks: name: {{ template "amazon-cloudwatch-observability.webhookServiceName" . }} namespace: {{ .Release.Namespace }} path: /mutate-v1-workload - caBundle: {{ $ca.Cert | b64enc }} + caBundle: {{ $cert.Ca }} failurePolicy: {{ .Values.admissionWebhooks.workloads.failurePolicy | default .Values.admissionWebhooks.pods.failurePolicy | default .Values.admissionWebhooks.failurePolicy }} name: mworkload.kb.io namespaceSelector: {{ include "amazon-cloudwatch-observability.namespaceSelector" (list . "workloads") }} @@ -182,7 +182,7 @@ webhooks: name: {{ template "amazon-cloudwatch-observability.webhookServiceName" . }} namespace: {{ .Release.Namespace }} path: /validate-cloudwatch-aws-amazon-com-v1alpha1-instrumentation - caBundle: {{ $ca.Cert | b64enc }} + caBundle: {{ $cert.Ca }} failurePolicy: {{ .Values.admissionWebhooks.instrumentations.failurePolicy | default .Values.admissionWebhooks.failurePolicy }} name: vinstrumentationcreateupdate.kb.io namespaceSelector: {{ include "amazon-cloudwatch-observability.namespaceSelector" (list . "instrumentations") }} @@ -239,7 +239,7 @@ webhooks: name: {{ template "amazon-cloudwatch-observability.webhookServiceName" . }} namespace: {{ .Release.Namespace }} path: /validate-cloudwatch-aws-amazon-com-v1alpha1-amazoncloudwatchagent - caBundle: {{ $ca.Cert | b64enc }} + caBundle: {{ $cert.Ca }} failurePolicy: {{ .Values.admissionWebhooks.agents.failurePolicy | default .Values.admissionWebhooks.failurePolicy }} name: vamazoncloudwatchagentcreateupdate.kb.io namespaceSelector: {{ include "amazon-cloudwatch-observability.namespaceSelector" (list . "agents") }} From 2051841d38a60839e7b59251d71581bea606ff9b Mon Sep 17 00:00:00 2001 From: Dominic Sciascia Date: Fri, 17 Oct 2025 19:09:43 +0000 Subject: [PATCH 5/5] Use common consts --- .../validations/minikube/common.go | 4 ++-- .../minikube/scenarios/deployment_rolling_disabled_test.go | 5 +++-- .../minikube/scenarios/deployment_rolling_enabled_test.go | 5 +++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/integration-tests/amazon-cloudwatch-observability/validations/minikube/common.go b/integration-tests/amazon-cloudwatch-observability/validations/minikube/common.go index 20eaf1b..70f3b9a 100644 --- a/integration-tests/amazon-cloudwatch-observability/validations/minikube/common.go +++ b/integration-tests/amazon-cloudwatch-observability/validations/minikube/common.go @@ -15,7 +15,7 @@ import ( const ( Namespace = "amazon-cloudwatch" - operatorName = "amazon-cloudwatch-observability-controller-manager" + OperatorName = "amazon-cloudwatch-observability-controller-manager" WebhookName = "amazon-cloudwatch-observability-mutating-webhook-configuration" WebhookPathMutateInstrumentation = "/mutate-cloudwatch-aws-amazon-com-v1alpha1-instrumentation" @@ -37,7 +37,7 @@ func ValidateOperatorAutoMonitorConfig(t *testing.T, expectedConfig map[string]i // Find the operator deployment by name var deployment *appsV1.Deployment for i := range deployments.Items { - if deployments.Items[i].Name == operatorName { + if deployments.Items[i].Name == OperatorName { deployment = &deployments.Items[i] break } diff --git a/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/deployment_rolling_disabled_test.go b/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/deployment_rolling_disabled_test.go index 57f2877..a107d68 100644 --- a/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/deployment_rolling_disabled_test.go +++ b/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/deployment_rolling_disabled_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/aws-observability/helm-charts/integration-tests/amazon-cloudwatch-observability/util" + "github.com/aws-observability/helm-charts/integration-tests/amazon-cloudwatch-observability/validations/minikube" "github.com/stretchr/testify/assert" ) @@ -14,12 +15,12 @@ func TestDeploymentRollingDisabled(t *testing.T) { k8sClient, err := util.NewK8sClient() assert.NoError(t, err) - ds, err := k8sClient.ListDeployments("amazon-cloudwatch") + ds, err := k8sClient.ListDeployments(minikube.Namespace) assert.NoError(t, err) found := false for _, d := range ds.Items { - if d.GetName() != "amazon-cloudwatch-observability-controller-manager" { + if d.GetName() != minikube.OperatorName { continue } else { found = true diff --git a/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/deployment_rolling_enabled_test.go b/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/deployment_rolling_enabled_test.go index 5c0611c..a78d5b1 100644 --- a/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/deployment_rolling_enabled_test.go +++ b/integration-tests/amazon-cloudwatch-observability/validations/minikube/scenarios/deployment_rolling_enabled_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/aws-observability/helm-charts/integration-tests/amazon-cloudwatch-observability/util" + "github.com/aws-observability/helm-charts/integration-tests/amazon-cloudwatch-observability/validations/minikube" "github.com/stretchr/testify/assert" ) @@ -39,11 +40,11 @@ func TestDeploymentRollingEnabled_Compare(t *testing.T) { } func retrieveRollMe(t *testing.T, k8sClient util.K8sClient) []byte { - ds, err := k8sClient.ListDeployments("amazon-cloudwatch") + ds, err := k8sClient.ListDeployments(minikube.Namespace) assert.NoError(t, err) for _, d := range ds.Items { - if d.GetName() != "amazon-cloudwatch-observability-controller-manager" { + if d.GetName() != minikube.OperatorName { continue }