From b4dd8628b155945e67c3a6f09fae1a9e85e3984a Mon Sep 17 00:00:00 2001 From: Kaushik Surya Date: Tue, 10 Jun 2025 14:44:43 -0400 Subject: [PATCH] Add support for UpdateStrategy when collector mode is Deployment --- apis/v1alpha1/amazoncloudwatchagent_types.go | 5 + apis/v1alpha1/collector_webhook.go | 5 + apis/v1alpha1/collector_webhook_test.go | 16 +++ apis/v1alpha1/zz_generated.deepcopy.go | 1 + apis/v1alpha2/amazoncloudwatchagent_types.go | 11 ++ apis/v1alpha2/zz_generated.deepcopy.go | 2 + ...aws.amazon.com_amazoncloudwatchagents.yaml | 53 +++++++++ docs/api.md | 106 ++++++++++++++++++ internal/manifests/collector/deployment.go | 1 + .../manifests/collector/deployment_test.go | 32 ++++++ 10 files changed, 232 insertions(+) diff --git a/apis/v1alpha1/amazoncloudwatchagent_types.go b/apis/v1alpha1/amazoncloudwatchagent_types.go index 7e3cd06bc..a9b7179e4 100644 --- a/apis/v1alpha1/amazoncloudwatchagent_types.go +++ b/apis/v1alpha1/amazoncloudwatchagent_types.go @@ -280,6 +280,11 @@ type AmazonCloudWatchAgentSpec struct { // This is only applicable to Daemonset mode. // +optional UpdateStrategy appsv1.DaemonSetUpdateStrategy `json:"updateStrategy,omitempty"` + // UpdateStrategy represents the strategy the operator will take replacing existing Deployment pods with new pods + // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/deployment-v1/#DeploymentSpec + // This is only applicable to Deployment mode. + // +optional + DeploymentUpdateStrategy appsv1.DeploymentStrategy `json:"deploymentUpdateStrategy,omitempty"` } // AmazonCloudWatchAgentTargetAllocator defines the configurations for the Prometheus target allocator. diff --git a/apis/v1alpha1/collector_webhook.go b/apis/v1alpha1/collector_webhook.go index b893ff8df..7343f430e 100644 --- a/apis/v1alpha1/collector_webhook.go +++ b/apis/v1alpha1/collector_webhook.go @@ -295,6 +295,11 @@ func (c CollectorWebhook) validate(r *AmazonCloudWatchAgent) (admission.Warnings return warnings, fmt.Errorf("the OpenTelemetry Collector mode is set to %s, which does not support the attribute 'updateStrategy'", r.Spec.Mode) } + // validate updateStrategy for Deployment + if r.Spec.Mode != ModeDeployment && len(r.Spec.DeploymentUpdateStrategy.Type) > 0 { + return warnings, fmt.Errorf("the OpenTelemetry Collector mode is set to %s, which does not support the attribute 'deploymentUpdateStrategy'", r.Spec.Mode) + } + return warnings, nil } diff --git a/apis/v1alpha1/collector_webhook_test.go b/apis/v1alpha1/collector_webhook_test.go index 9f1a7fde0..6bf6628f1 100644 --- a/apis/v1alpha1/collector_webhook_test.go +++ b/apis/v1alpha1/collector_webhook_test.go @@ -759,6 +759,22 @@ func TestOTELColValidatingWebhook(t *testing.T) { }, expectedErr: "the OpenTelemetry Collector mode is set to deployment, which does not support the attribute 'updateStrategy'", }, + { + name: "invalid updateStrategy for Statefulset mode", + otelcol: AmazonCloudWatchAgent{ + Spec: AmazonCloudWatchAgentSpec{ + Mode: ModeStatefulSet, + DeploymentUpdateStrategy: appsv1.DeploymentStrategy{ + Type: "RollingUpdate", + RollingUpdate: &appsv1.RollingUpdateDeployment{ + MaxSurge: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)}, + MaxUnavailable: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)}, + }, + }, + }, + }, + expectedErr: "the OpenTelemetry Collector mode is set to statefulset, which does not support the attribute 'deploymentUpdateStrategy'", + }, } for _, test := range tests { diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index 4d7355472..a4d7befdc 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -234,6 +234,7 @@ func (in *AmazonCloudWatchAgentSpec) DeepCopyInto(out *AmazonCloudWatchAgentSpec copy(*out, *in) } in.UpdateStrategy.DeepCopyInto(&out.UpdateStrategy) + in.DeploymentUpdateStrategy.DeepCopyInto(&out.DeploymentUpdateStrategy) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AmazonCloudWatchAgentSpec. diff --git a/apis/v1alpha2/amazoncloudwatchagent_types.go b/apis/v1alpha2/amazoncloudwatchagent_types.go index 267fa0cf8..b377a289d 100644 --- a/apis/v1alpha2/amazoncloudwatchagent_types.go +++ b/apis/v1alpha2/amazoncloudwatchagent_types.go @@ -6,6 +6,7 @@ package v1alpha2 import ( + appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -200,6 +201,16 @@ type AmazonCloudWatchAgentSpec struct { // object, which shall be mounted into the Collector Pods. // Each ConfigMap will be added to the Collector's Deployments as a volume named `configmap-`. ConfigMaps []v1alpha1.ConfigMapsSpec `json:"configmaps,omitempty"` + // UpdateStrategy represents the strategy the operator will take replacing existing DaemonSet pods with new pods + // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/daemon-set-v1/#DaemonSetSpec + // This is only applicable to Daemonset mode. + // +optional + UpdateStrategy appsv1.DaemonSetUpdateStrategy `json:"updateStrategy,omitempty"` + // UpdateStrategy represents the strategy the operator will take replacing existing Deployment pods with new pods + // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/deployment-v1/#DeploymentSpec + // This is only applicable to Deployment mode. + // +optional + DeploymentUpdateStrategy appsv1.DeploymentStrategy `json:"deploymentUpdateStrategy,omitempty"` } // AmazonCloudWatchAgentStatus defines the observed state of AmazonCloudWatchAgent. diff --git a/apis/v1alpha2/zz_generated.deepcopy.go b/apis/v1alpha2/zz_generated.deepcopy.go index 06959cc6b..3df978654 100644 --- a/apis/v1alpha2/zz_generated.deepcopy.go +++ b/apis/v1alpha2/zz_generated.deepcopy.go @@ -221,6 +221,8 @@ func (in *AmazonCloudWatchAgentSpec) DeepCopyInto(out *AmazonCloudWatchAgentSpec *out = make([]v1alpha1.ConfigMapsSpec, len(*in)) copy(*out, *in) } + in.UpdateStrategy.DeepCopyInto(&out.UpdateStrategy) + in.DeploymentUpdateStrategy.DeepCopyInto(&out.DeploymentUpdateStrategy) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AmazonCloudWatchAgentSpec. diff --git a/config/crd/bases/cloudwatch.aws.amazon.com_amazoncloudwatchagents.yaml b/config/crd/bases/cloudwatch.aws.amazon.com_amazoncloudwatchagents.yaml index bf6c09215..996fb4315 100644 --- a/config/crd/bases/cloudwatch.aws.amazon.com_amazoncloudwatchagents.yaml +++ b/config/crd/bases/cloudwatch.aws.amazon.com_amazoncloudwatchagents.yaml @@ -2595,6 +2595,59 @@ spec: - name type: object type: array + deploymentUpdateStrategy: + description: |- + UpdateStrategy represents the strategy the operator will take replacing existing Deployment pods with new pods + https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/deployment-v1/#DeploymentSpec + This is only applicable to Deployment mode. + properties: + rollingUpdate: + description: |- + Rolling update config params. Present only if DeploymentStrategyType = + RollingUpdate. + --- + TODO: Update this to follow our convention for oneOf, whatever we decide it + to be. + properties: + maxSurge: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of pods that can be scheduled above the desired number of + pods. + Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). + This can not be 0 if MaxUnavailable is 0. + Absolute number is calculated from percentage by rounding up. + Defaults to 25%. + Example: when this is set to 30%, the new ReplicaSet can be scaled up immediately when + the rolling update starts, such that the total number of old and new pods do not exceed + 130% of desired pods. Once old pods have been killed, + new ReplicaSet can be scaled up further, ensuring that total number of pods running + at any time during the update is at most 130% of desired pods. + x-kubernetes-int-or-string: true + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of pods that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). + Absolute number is calculated from percentage by rounding down. + This can not be 0 if MaxSurge is 0. + Defaults to 25%. + Example: when this is set to 30%, the old ReplicaSet can be scaled down to 70% of desired pods + immediately when the rolling update starts. Once new pods are ready, old ReplicaSet + can be scaled down further, followed by scaling up the new ReplicaSet, ensuring + that the total number of pods available at all times during the update is at + least 70% of desired pods. + x-kubernetes-int-or-string: true + type: object + type: + description: Type of deployment. Can be "Recreate" or "RollingUpdate". + Default is RollingUpdate. + type: string + type: object env: description: |- ENV vars to set on the OpenTelemetry Collector's Pods. These can then in certain cases be diff --git a/docs/api.md b/docs/api.md index 885b2413f..f9263039e 100644 --- a/docs/api.md +++ b/docs/api.md @@ -147,6 +147,15 @@ object, which shall be mounted into the Collector Pods. Each ConfigMap will be added to the Collector's Deployments as a volume named `configmap-`.
false + + deploymentUpdateStrategy + object + + UpdateStrategy represents the strategy the operator will take replacing existing Deployment pods with new pods +https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/deployment-v1/#DeploymentSpec +This is only applicable to Deployment mode.
+ + false env []object @@ -5483,6 +5492,103 @@ metric across all relevant pods (as a quantity)
+### AmazonCloudWatchAgent.spec.deploymentUpdateStrategy +[↩ Parent](#amazoncloudwatchagentspec) + + + +UpdateStrategy represents the strategy the operator will take replacing existing Deployment pods with new pods +https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/deployment-v1/#DeploymentSpec +This is only applicable to Deployment mode. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
rollingUpdateobject + Rolling update config params. Present only if DeploymentStrategyType = +RollingUpdate. +--- +TODO: Update this to follow our convention for oneOf, whatever we decide it +to be.
+
false
typestring + Type of deployment. Can be "Recreate" or "RollingUpdate". Default is RollingUpdate.
+
false
+ + +### AmazonCloudWatchAgent.spec.deploymentUpdateStrategy.rollingUpdate +[↩ Parent](#amazoncloudwatchagentspecdeploymentupdatestrategy) + + + +Rolling update config params. Present only if DeploymentStrategyType = +RollingUpdate. +--- +TODO: Update this to follow our convention for oneOf, whatever we decide it +to be. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
maxSurgeint or string + The maximum number of pods that can be scheduled above the desired number of +pods. +Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). +This can not be 0 if MaxUnavailable is 0. +Absolute number is calculated from percentage by rounding up. +Defaults to 25%. +Example: when this is set to 30%, the new ReplicaSet can be scaled up immediately when +the rolling update starts, such that the total number of old and new pods do not exceed +130% of desired pods. Once old pods have been killed, +new ReplicaSet can be scaled up further, ensuring that total number of pods running +at any time during the update is at most 130% of desired pods.
+
false
maxUnavailableint or string + The maximum number of pods that can be unavailable during the update. +Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). +Absolute number is calculated from percentage by rounding down. +This can not be 0 if MaxSurge is 0. +Defaults to 25%. +Example: when this is set to 30%, the old ReplicaSet can be scaled down to 70% of desired pods +immediately when the rolling update starts. Once new pods are ready, old ReplicaSet +can be scaled down further, followed by scaling up the new ReplicaSet, ensuring +that the total number of pods available at all times during the update is at +least 70% of desired pods.
+
false
+ + ### AmazonCloudWatchAgent.spec.env[index] [↩ Parent](#amazoncloudwatchagentspec) diff --git a/internal/manifests/collector/deployment.go b/internal/manifests/collector/deployment.go index ebb800a2d..3b6bee76f 100644 --- a/internal/manifests/collector/deployment.go +++ b/internal/manifests/collector/deployment.go @@ -33,6 +33,7 @@ func Deployment(params manifests.Params) *appsv1.Deployment { Selector: &metav1.LabelSelector{ MatchLabels: manifestutils.SelectorLabels(params.OtelCol.ObjectMeta, ComponentAmazonCloudWatchAgent), }, + Strategy: params.OtelCol.Spec.DeploymentUpdateStrategy, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: labels, diff --git a/internal/manifests/collector/deployment_test.go b/internal/manifests/collector/deployment_test.go index 7550dad75..00e7118e2 100644 --- a/internal/manifests/collector/deployment_test.go +++ b/internal/manifests/collector/deployment_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" "github.com/aws/amazon-cloudwatch-agent-operator/apis/v1alpha1" "github.com/aws/amazon-cloudwatch-agent-operator/internal/config" @@ -185,6 +186,37 @@ func TestDeploymenttPodSecurityContext(t *testing.T) { assert.Equal(t, &runasGroup, d.Spec.Template.Spec.SecurityContext.RunAsGroup) } +func TestDeploymentUpdateStrategy(t *testing.T) { + otelcol := v1alpha1.AmazonCloudWatchAgent{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + Spec: v1alpha1.AmazonCloudWatchAgentSpec{ + DeploymentUpdateStrategy: appsv1.DeploymentStrategy{ + Type: "RollingUpdate", + RollingUpdate: &appsv1.RollingUpdateDeployment{ + MaxSurge: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)}, + MaxUnavailable: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)}, + }, + }, + }, + } + + cfg := config.New() + + params := manifests.Params{ + Config: cfg, + OtelCol: otelcol, + Log: logger, + } + + d := Deployment(params) + + assert.Equal(t, "RollingUpdate", string(d.Spec.Strategy.Type)) + assert.Equal(t, 1, d.Spec.Strategy.RollingUpdate.MaxSurge.IntValue()) + assert.Equal(t, 1, d.Spec.Strategy.RollingUpdate.MaxUnavailable.IntValue()) +} + func TestDeploymentHostNetwork(t *testing.T) { // Test default otelcol1 := v1alpha1.AmazonCloudWatchAgent{