diff --git a/docs/TektonConfig.md b/docs/TektonConfig.md
index cb19ad4dd8..f36009dd63 100644
--- a/docs/TektonConfig.md
+++ b/docs/TektonConfig.md
@@ -769,6 +769,12 @@ There is a field called `options` available in all the components.
> **NOTE:** There is a possibility to have two different values for a field.
> An example: with a pre-defined field you can set value and the same field may be defined under `options` as well. In that case value from `options` will be final.
+> **NOTE:** The embedded objects are not merged as a whole. The operator copies a
+> known set of fields onto the manifest, and the per-kind lists below are
+> exhaustive - a field outside them is ignored silently, with no error or warning.
+> If a field you set under `options` has no effect, it is most likely not
+> supported yet - please open an issue.
+
A sample `options` field,
```yaml
@@ -822,6 +828,11 @@ options:
custom-annotation: "foo"
spec:
replicas: 2
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 0
+ maxUnavailable: 1
template:
spec:
containers:
@@ -838,6 +849,10 @@ options:
custom-annotation: foo
spec:
replicas: 3
+ updateStrategy:
+ type: RollingUpdate
+ rollingUpdate:
+ partition: 1
template:
spec:
containers:
@@ -895,6 +910,7 @@ The following fields are supported in `deployment`
- `annotations` - supports add and update
- `spec`
- `replicas` - updates deployment replicas count
+ - `strategy` - replaces the existing deployment strategy with this, if `type` is not empty
- `template`
- `metadata`
- `labels` - supports add and update
@@ -929,6 +945,7 @@ The following fields are supported in `StatefulSet`
- `annotations` - supports add and update
- `spec`
- `replicas` - updates statefulSets replicas count
+ - `updateStrategy` - replaces the existing statefulSet update strategy with this, if `type` is not empty
- `serviceName` - updates service name
- `podManagementPolicy` - updates pod management policy
- `volumeClaimTemplates` - updates volume claim templates
diff --git a/pkg/reconciler/common/testdata/test-additional-options-base-strategy-deployment.yaml b/pkg/reconciler/common/testdata/test-additional-options-base-strategy-deployment.yaml
new file mode 100644
index 0000000000..ef9062b7f2
--- /dev/null
+++ b/pkg/reconciler/common/testdata/test-additional-options-base-strategy-deployment.yaml
@@ -0,0 +1,24 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: tekton-pipelines-controller
+ namespace: tekton-pipelines
+spec:
+ replicas: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 25%
+ maxUnavailable: 25%
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: controller
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: controller
+ spec:
+ containers:
+ - name: tekton-pipelines-controller
+ image: gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/controller:v0.50.1
diff --git a/pkg/reconciler/common/testdata/test-additional-options-base-strategy-recreate-deployment.yaml b/pkg/reconciler/common/testdata/test-additional-options-base-strategy-recreate-deployment.yaml
new file mode 100644
index 0000000000..f01d752811
--- /dev/null
+++ b/pkg/reconciler/common/testdata/test-additional-options-base-strategy-recreate-deployment.yaml
@@ -0,0 +1,21 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: tekton-pipelines-controller
+ namespace: tekton-pipelines
+spec:
+ replicas: 1
+ strategy:
+ type: Recreate
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: controller
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: controller
+ spec:
+ containers:
+ - name: tekton-pipelines-controller
+ image: gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/controller:v0.50.1
diff --git a/pkg/reconciler/common/testdata/test-additional-options-base-updatestrategy-statefulset.yaml b/pkg/reconciler/common/testdata/test-additional-options-base-updatestrategy-statefulset.yaml
new file mode 100644
index 0000000000..335be24ecf
--- /dev/null
+++ b/pkg/reconciler/common/testdata/test-additional-options-base-updatestrategy-statefulset.yaml
@@ -0,0 +1,23 @@
+---
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: web
+spec:
+ serviceName: nginx
+ replicas: 2
+ updateStrategy:
+ type: RollingUpdate
+ rollingUpdate:
+ partition: 0
+ selector:
+ matchLabels:
+ app: nginx
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ containers:
+ - name: nginx
+ image: registry.k8s.io/nginx-slim:0.8
diff --git a/pkg/reconciler/common/testdata/test-additional-options-test-strategy-recreate-deployment.yaml b/pkg/reconciler/common/testdata/test-additional-options-test-strategy-recreate-deployment.yaml
new file mode 100644
index 0000000000..e692e3f156
--- /dev/null
+++ b/pkg/reconciler/common/testdata/test-additional-options-test-strategy-recreate-deployment.yaml
@@ -0,0 +1,23 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: tekton-pipelines-controller
+ namespace: tekton-pipelines
+status: {}
+spec:
+ replicas: 1
+ strategy:
+ type: Recreate
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: controller
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: controller
+ spec:
+ containers:
+ - name: tekton-pipelines-controller
+ image: gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/controller:v0.50.1
+ resources: {}
diff --git a/pkg/reconciler/common/testdata/test-additional-options-test-strategy-recreate-to-rollingupdate-deployment.yaml b/pkg/reconciler/common/testdata/test-additional-options-test-strategy-recreate-to-rollingupdate-deployment.yaml
new file mode 100644
index 0000000000..4f5c803473
--- /dev/null
+++ b/pkg/reconciler/common/testdata/test-additional-options-test-strategy-recreate-to-rollingupdate-deployment.yaml
@@ -0,0 +1,26 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: tekton-pipelines-controller
+ namespace: tekton-pipelines
+status: {}
+spec:
+ replicas: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 0
+ maxUnavailable: 1
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: controller
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: controller
+ spec:
+ containers:
+ - name: tekton-pipelines-controller
+ image: gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/controller:v0.50.1
+ resources: {}
diff --git a/pkg/reconciler/common/testdata/test-additional-options-test-strategy-recreate-with-rollingupdate-deployment.yaml b/pkg/reconciler/common/testdata/test-additional-options-test-strategy-recreate-with-rollingupdate-deployment.yaml
new file mode 100644
index 0000000000..94d6e4c242
--- /dev/null
+++ b/pkg/reconciler/common/testdata/test-additional-options-test-strategy-recreate-with-rollingupdate-deployment.yaml
@@ -0,0 +1,29 @@
+---
+# The options asked for Recreate while also supplying a rollingUpdate block.
+# The transformer passes both through untouched rather than silently dropping
+# one of them; the API server is the component that rejects the combination.
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: tekton-pipelines-controller
+ namespace: tekton-pipelines
+status: {}
+spec:
+ replicas: 1
+ strategy:
+ type: Recreate
+ rollingUpdate:
+ maxSurge: 0
+ maxUnavailable: 1
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: controller
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: controller
+ spec:
+ containers:
+ - name: tekton-pipelines-controller
+ image: gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/controller:v0.50.1
+ resources: {}
diff --git a/pkg/reconciler/common/testdata/test-additional-options-test-strategy-rollingupdate-deployment.yaml b/pkg/reconciler/common/testdata/test-additional-options-test-strategy-rollingupdate-deployment.yaml
new file mode 100644
index 0000000000..4f5c803473
--- /dev/null
+++ b/pkg/reconciler/common/testdata/test-additional-options-test-strategy-rollingupdate-deployment.yaml
@@ -0,0 +1,26 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: tekton-pipelines-controller
+ namespace: tekton-pipelines
+status: {}
+spec:
+ replicas: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 0
+ maxUnavailable: 1
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: controller
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: controller
+ spec:
+ containers:
+ - name: tekton-pipelines-controller
+ image: gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/controller:v0.50.1
+ resources: {}
diff --git a/pkg/reconciler/common/testdata/test-additional-options-test-strategy-unset-deployment.yaml b/pkg/reconciler/common/testdata/test-additional-options-test-strategy-unset-deployment.yaml
new file mode 100644
index 0000000000..561900efc1
--- /dev/null
+++ b/pkg/reconciler/common/testdata/test-additional-options-test-strategy-unset-deployment.yaml
@@ -0,0 +1,26 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: tekton-pipelines-controller
+ namespace: tekton-pipelines
+status: {}
+spec:
+ replicas: 2
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 25%
+ maxUnavailable: 25%
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: controller
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: controller
+ spec:
+ containers:
+ - name: tekton-pipelines-controller
+ image: gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/controller:v0.50.1
+ resources: {}
diff --git a/pkg/reconciler/common/testdata/test-additional-options-test-updatestrategy-ondelete-statefulset.yaml b/pkg/reconciler/common/testdata/test-additional-options-test-updatestrategy-ondelete-statefulset.yaml
new file mode 100644
index 0000000000..847fbf55ac
--- /dev/null
+++ b/pkg/reconciler/common/testdata/test-additional-options-test-updatestrategy-ondelete-statefulset.yaml
@@ -0,0 +1,25 @@
+---
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: web
+spec:
+ serviceName: nginx
+ replicas: 2
+ updateStrategy:
+ type: OnDelete
+ selector:
+ matchLabels:
+ app: nginx
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ containers:
+ - name: nginx
+ image: registry.k8s.io/nginx-slim:0.8
+ resources: {}
+status:
+ availableReplicas: 0
+ replicas: 0
diff --git a/pkg/reconciler/common/testdata/test-additional-options-test-updatestrategy-partition-statefulset.yaml b/pkg/reconciler/common/testdata/test-additional-options-test-updatestrategy-partition-statefulset.yaml
new file mode 100644
index 0000000000..b42d4f7b68
--- /dev/null
+++ b/pkg/reconciler/common/testdata/test-additional-options-test-updatestrategy-partition-statefulset.yaml
@@ -0,0 +1,27 @@
+---
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: web
+spec:
+ serviceName: nginx
+ replicas: 2
+ updateStrategy:
+ type: RollingUpdate
+ rollingUpdate:
+ partition: 2
+ selector:
+ matchLabels:
+ app: nginx
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ containers:
+ - name: nginx
+ image: registry.k8s.io/nginx-slim:0.8
+ resources: {}
+status:
+ availableReplicas: 0
+ replicas: 0
diff --git a/pkg/reconciler/common/testdata/test-additional-options-test-updatestrategy-unset-statefulset.yaml b/pkg/reconciler/common/testdata/test-additional-options-test-updatestrategy-unset-statefulset.yaml
new file mode 100644
index 0000000000..78036f9995
--- /dev/null
+++ b/pkg/reconciler/common/testdata/test-additional-options-test-updatestrategy-unset-statefulset.yaml
@@ -0,0 +1,27 @@
+---
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: web
+spec:
+ serviceName: nginx
+ replicas: 3
+ updateStrategy:
+ type: RollingUpdate
+ rollingUpdate:
+ partition: 0
+ selector:
+ matchLabels:
+ app: nginx
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ containers:
+ - name: nginx
+ image: registry.k8s.io/nginx-slim:0.8
+ resources: {}
+status:
+ availableReplicas: 0
+ replicas: 0
diff --git a/pkg/reconciler/common/transformer_additional_options.go b/pkg/reconciler/common/transformer_additional_options.go
index 9369518911..3a8468f652 100644
--- a/pkg/reconciler/common/transformer_additional_options.go
+++ b/pkg/reconciler/common/transformer_additional_options.go
@@ -287,6 +287,15 @@ func (ot *OptionsTransformer) updateDeployments(u *unstructured.Unstructured) er
targetDeployment.Spec.Replicas = ptr.Int32(*deploymentOptions.Spec.Replicas)
}
+ // update deployment strategy
+ // The whole struct is replaced instead of merged field by field: "rollingUpdate"
+ // may not be set when the type is "Recreate", so merging would leave the
+ // rollingUpdate block from the base manifest behind and the API server would
+ // reject the resulting deployment.
+ if deploymentOptions.Spec.Strategy.Type != "" {
+ targetDeployment.Spec.Strategy = deploymentOptions.Spec.Strategy
+ }
+
// update affinity
if deploymentOptions.Spec.Template.Spec.Affinity != nil {
targetDeployment.Spec.Template.Spec.Affinity = deploymentOptions.Spec.Template.Spec.Affinity
@@ -589,6 +598,15 @@ func (ot *OptionsTransformer) updateStatefulSets(u *unstructured.Unstructured) e
targetStatefulSet.Spec.Replicas = ptr.Int32(*statefulSetOptions.Spec.Replicas)
}
+ // update statefulSet update strategy
+ // The whole struct is replaced instead of merged field by field: "rollingUpdate"
+ // may not be set when the type is "OnDelete", so merging would leave the
+ // rollingUpdate block from the base manifest behind and the API server would
+ // reject the resulting statefulSet.
+ if statefulSetOptions.Spec.UpdateStrategy.Type != "" {
+ targetStatefulSet.Spec.UpdateStrategy = statefulSetOptions.Spec.UpdateStrategy
+ }
+
// update affinity
if statefulSetOptions.Spec.Template.Spec.Affinity != nil {
targetStatefulSet.Spec.Template.Spec.Affinity = statefulSetOptions.Spec.Template.Spec.Affinity
diff --git a/pkg/reconciler/common/transformer_additional_options_test.go b/pkg/reconciler/common/transformer_additional_options_test.go
index 2d448af484..4c5600b830 100644
--- a/pkg/reconciler/common/transformer_additional_options_test.go
+++ b/pkg/reconciler/common/transformer_additional_options_test.go
@@ -31,6 +31,7 @@ import (
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/util/intstr"
"knative.dev/pkg/ptr"
)
@@ -41,6 +42,8 @@ func TestExecuteAdditionalOptionsTransformer(t *testing.T) {
ignorePolicy := admissionregistrationv1.Ignore
failPolicy := admissionregistrationv1.Fail
sideEffectUnknown := admissionregistrationv1.SideEffectClassUnknown
+ maxSurgeZero := intstr.FromInt32(0)
+ maxUnavailableOne := intstr.FromInt32(1)
// verify the changes applied on the manifest
@@ -575,6 +578,188 @@ func TestExecuteAdditionalOptionsTransformer(t *testing.T) {
inputFilename: "./testdata/test-additional-options-base-webhook.yaml",
expectedResultFilename: "./testdata/test-additional-options-test-webhook.yaml",
},
+ {
+ // switching to Recreate must drop the rollingUpdate block coming from
+ // the base manifest, otherwise the API server rejects the deployment
+ name: "test-strategy-recreate-for-deployments",
+ additionalOptions: v1alpha1.AdditionalOptions{
+ Disabled: ptr.Bool(false),
+ Deployments: map[string]appsv1.Deployment{
+ "tekton-pipelines-controller": {
+ Spec: appsv1.DeploymentSpec{
+ Strategy: appsv1.DeploymentStrategy{
+ Type: appsv1.RecreateDeploymentStrategyType,
+ },
+ },
+ },
+ },
+ },
+ inputFilename: "./testdata/test-additional-options-base-strategy-deployment.yaml",
+ expectedResultFilename: "./testdata/test-additional-options-test-strategy-recreate-deployment.yaml",
+ },
+ {
+ name: "test-strategy-rollingupdate-tuning-for-deployments",
+ additionalOptions: v1alpha1.AdditionalOptions{
+ Disabled: ptr.Bool(false),
+ Deployments: map[string]appsv1.Deployment{
+ "tekton-pipelines-controller": {
+ Spec: appsv1.DeploymentSpec{
+ Strategy: appsv1.DeploymentStrategy{
+ Type: appsv1.RollingUpdateDeploymentStrategyType,
+ RollingUpdate: &appsv1.RollingUpdateDeployment{
+ MaxSurge: &maxSurgeZero,
+ MaxUnavailable: &maxUnavailableOne,
+ },
+ },
+ },
+ },
+ },
+ },
+ inputFilename: "./testdata/test-additional-options-base-strategy-deployment.yaml",
+ expectedResultFilename: "./testdata/test-additional-options-test-strategy-rollingupdate-deployment.yaml",
+ },
+ {
+ // a deployment present in options but without a strategy must keep
+ // the strategy defined in the base manifest untouched
+ name: "test-strategy-not-set-for-deployments",
+ additionalOptions: v1alpha1.AdditionalOptions{
+ Disabled: ptr.Bool(false),
+ Deployments: map[string]appsv1.Deployment{
+ "tekton-pipelines-controller": {
+ Spec: appsv1.DeploymentSpec{
+ Replicas: ptr.Int32(2),
+ },
+ },
+ },
+ },
+ inputFilename: "./testdata/test-additional-options-base-strategy-deployment.yaml",
+ expectedResultFilename: "./testdata/test-additional-options-test-strategy-unset-deployment.yaml",
+ },
+ {
+ // switching back from Recreate must bring the rollingUpdate block in,
+ // proving the replacement works in both directions
+ name: "test-strategy-recreate-to-rollingupdate-for-deployments",
+ additionalOptions: v1alpha1.AdditionalOptions{
+ Disabled: ptr.Bool(false),
+ Deployments: map[string]appsv1.Deployment{
+ "tekton-pipelines-controller": {
+ Spec: appsv1.DeploymentSpec{
+ Strategy: appsv1.DeploymentStrategy{
+ Type: appsv1.RollingUpdateDeploymentStrategyType,
+ RollingUpdate: &appsv1.RollingUpdateDeployment{
+ MaxSurge: &maxSurgeZero,
+ MaxUnavailable: &maxUnavailableOne,
+ },
+ },
+ },
+ },
+ },
+ },
+ inputFilename: "./testdata/test-additional-options-base-strategy-recreate-deployment.yaml",
+ expectedResultFilename: "./testdata/test-additional-options-test-strategy-recreate-to-rollingupdate-deployment.yaml",
+ },
+ {
+ // a rollingUpdate without a strategy type is deliberately ignored:
+ // the type drives the decision and an empty type means "keep the base"
+ name: "test-strategy-rollingupdate-without-type-is-ignored-for-deployments",
+ additionalOptions: v1alpha1.AdditionalOptions{
+ Disabled: ptr.Bool(false),
+ Deployments: map[string]appsv1.Deployment{
+ "tekton-pipelines-controller": {
+ Spec: appsv1.DeploymentSpec{
+ Replicas: ptr.Int32(2),
+ Strategy: appsv1.DeploymentStrategy{
+ RollingUpdate: &appsv1.RollingUpdateDeployment{
+ MaxSurge: &maxSurgeZero,
+ MaxUnavailable: &maxUnavailableOne,
+ },
+ },
+ },
+ },
+ },
+ },
+ inputFilename: "./testdata/test-additional-options-base-strategy-deployment.yaml",
+ expectedResultFilename: "./testdata/test-additional-options-test-strategy-unset-deployment.yaml",
+ },
+ {
+ // a self-contradictory strategy from options is passed through as given
+ // rather than partially dropped; rejecting it is the API server's job
+ name: "test-strategy-recreate-with-rollingupdate-is-passed-through-for-deployments",
+ additionalOptions: v1alpha1.AdditionalOptions{
+ Disabled: ptr.Bool(false),
+ Deployments: map[string]appsv1.Deployment{
+ "tekton-pipelines-controller": {
+ Spec: appsv1.DeploymentSpec{
+ Strategy: appsv1.DeploymentStrategy{
+ Type: appsv1.RecreateDeploymentStrategyType,
+ RollingUpdate: &appsv1.RollingUpdateDeployment{
+ MaxSurge: &maxSurgeZero,
+ MaxUnavailable: &maxUnavailableOne,
+ },
+ },
+ },
+ },
+ },
+ },
+ inputFilename: "./testdata/test-additional-options-base-strategy-deployment.yaml",
+ expectedResultFilename: "./testdata/test-additional-options-test-strategy-recreate-with-rollingupdate-deployment.yaml",
+ },
+ {
+ name: "test-updatestrategy-partition-tuning-for-statefulsets",
+ additionalOptions: v1alpha1.AdditionalOptions{
+ Disabled: ptr.Bool(false),
+ StatefulSets: map[string]appsv1.StatefulSet{
+ "web": {
+ Spec: appsv1.StatefulSetSpec{
+ UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
+ Type: appsv1.RollingUpdateStatefulSetStrategyType,
+ RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
+ Partition: ptr.Int32(2),
+ },
+ },
+ },
+ },
+ },
+ },
+ inputFilename: "./testdata/test-additional-options-base-updatestrategy-statefulset.yaml",
+ expectedResultFilename: "./testdata/test-additional-options-test-updatestrategy-partition-statefulset.yaml",
+ },
+ {
+ // a statefulSet present in options but without an update strategy must
+ // keep the strategy defined in the base manifest untouched
+ name: "test-updatestrategy-not-set-for-statefulsets",
+ additionalOptions: v1alpha1.AdditionalOptions{
+ Disabled: ptr.Bool(false),
+ StatefulSets: map[string]appsv1.StatefulSet{
+ "web": {
+ Spec: appsv1.StatefulSetSpec{
+ Replicas: ptr.Int32(3),
+ },
+ },
+ },
+ },
+ inputFilename: "./testdata/test-additional-options-base-updatestrategy-statefulset.yaml",
+ expectedResultFilename: "./testdata/test-additional-options-test-updatestrategy-unset-statefulset.yaml",
+ },
+ {
+ // switching to OnDelete must drop the rollingUpdate block coming from
+ // the base manifest, otherwise the API server rejects the statefulSet
+ name: "test-updatestrategy-ondelete-for-statefulsets",
+ additionalOptions: v1alpha1.AdditionalOptions{
+ Disabled: ptr.Bool(false),
+ StatefulSets: map[string]appsv1.StatefulSet{
+ "web": {
+ Spec: appsv1.StatefulSetSpec{
+ UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
+ Type: appsv1.OnDeleteStatefulSetStrategyType,
+ },
+ },
+ },
+ },
+ },
+ inputFilename: "./testdata/test-additional-options-base-updatestrategy-statefulset.yaml",
+ expectedResultFilename: "./testdata/test-additional-options-test-updatestrategy-ondelete-statefulset.yaml",
+ },
{
name: "test-runtimeclassname-for-deployments",
additionalOptions: v1alpha1.AdditionalOptions{
@@ -861,3 +1046,65 @@ func TestExecuteAdditionalOptionsTransformer(t *testing.T) {
})
}
}
+
+// The pod-template hash label drives pod recreation: it is recomputed from the
+// deployment spec, but updateDeploymentHashValue() deliberately zeroes the
+// strategy before hashing. Changing only the strategy must therefore leave the
+// hash untouched, so switching the rollout strategy does not restart the pods.
+// The table test above strips this label before comparing, so it is asserted here.
+func TestDeploymentStrategyDoesNotAffectSpecHash(t *testing.T) {
+ ctx := context.TODO()
+ targetNamespace := "tekton-pipelines"
+ inputFile := "./testdata/test-additional-options-base-strategy-deployment.yaml"
+
+ hashOf := func(t *testing.T, options v1alpha1.AdditionalOptions) string {
+ t.Helper()
+ manifest, err := Fetch(inputFile)
+ require.NoError(t, err)
+ require.NoError(t, ExecuteAdditionalOptionsTransformer(ctx, &manifest, targetNamespace, options))
+
+ for _, resource := range manifest.Resources() {
+ if resource.GetKind() != "Deployment" {
+ continue
+ }
+ labels, found, err := unstructured.NestedStringMap(resource.Object, "spec", "template", "metadata", "labels")
+ require.NoError(t, err)
+ require.True(t, found, "pod template labels not found")
+ hash, found := labels[v1alpha1.DeploymentSpecHashValueLabelKey]
+ require.True(t, found, "spec hash label not found")
+ return hash
+ }
+ t.Fatal("no deployment found in manifest")
+ return ""
+ }
+
+ baseline := hashOf(t, v1alpha1.AdditionalOptions{Disabled: ptr.Bool(false)})
+
+ strategyOnly := hashOf(t, v1alpha1.AdditionalOptions{
+ Disabled: ptr.Bool(false),
+ Deployments: map[string]appsv1.Deployment{
+ "tekton-pipelines-controller": {
+ Spec: appsv1.DeploymentSpec{
+ Strategy: appsv1.DeploymentStrategy{
+ Type: appsv1.RecreateDeploymentStrategyType,
+ },
+ },
+ },
+ },
+ })
+ require.Equal(t, baseline, strategyOnly, "changing the strategy must not change the spec hash")
+
+ // a change outside the strategy must still be reflected in the hash,
+ // otherwise the assertion above would hold vacuously
+ replicasChanged := hashOf(t, v1alpha1.AdditionalOptions{
+ Disabled: ptr.Bool(false),
+ Deployments: map[string]appsv1.Deployment{
+ "tekton-pipelines-controller": {
+ Spec: appsv1.DeploymentSpec{
+ Replicas: ptr.Int32(5),
+ },
+ },
+ },
+ })
+ require.NotEqual(t, baseline, replicasChanged, "changing the replicas must change the spec hash")
+}