diff --git a/Gopkg.lock b/Gopkg.lock index 8970f66bea..3dbd3389a2 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -604,6 +604,7 @@ "pkg/api/meta", "pkg/api/resource", "pkg/api/validation", + "pkg/apis/config", "pkg/apis/meta/internalversion", "pkg/apis/meta/v1", "pkg/apis/meta/v1/unstructured", @@ -820,6 +821,8 @@ "testing", "third_party/forked/golang/template", "tools/auth", + "tools/bootstrap/token/api", + "tools/bootstrap/token/util", "tools/cache", "tools/clientcmd", "tools/clientcmd/api", @@ -905,6 +908,9 @@ digest = "1:6061aa42761235df375f20fa4a1aa6d1845cba3687575f3adb2ef3f3bc540af5" name = "k8s.io/kubernetes" packages = [ + "cmd/kubeadm/app/apis/kubeadm", + "cmd/kubeadm/app/constants", + "cmd/kubeadm/app/util", "pkg/api/legacyscheme", "pkg/api/service", "pkg/api/v1/pod", @@ -925,8 +931,12 @@ "pkg/features", "pkg/fieldpath", "pkg/kubelet/apis", + "pkg/kubelet/apis/config", "pkg/kubelet/types", "pkg/master/ports", + "pkg/proxy/apis/config", + "pkg/registry/core/service/allocator", + "pkg/registry/core/service/ipallocator", "pkg/scheduler/algorithm", "pkg/scheduler/algorithm/priorities/util", "pkg/scheduler/api", @@ -941,6 +951,8 @@ "pkg/util/node", "pkg/util/parsers", "pkg/util/taints", + "pkg/util/version", + "pkg/version", ] pruneopts = "" revision = "17c77c7898218073f14c8d573582e8d2313dc740" @@ -950,7 +962,10 @@ branch = "master" digest = "1:bea542e853f98bfcc80ecbe8fe0f32bc52c97664102aacdd7dca676354ef2faa" name = "k8s.io/utils" - packages = ["pointer"] + packages = [ + "exec", + "pointer", + ] pruneopts = "" revision = "0d26856f57b32ec3398579285e5c8a2bfe8c5243" @@ -1001,11 +1016,13 @@ "k8s.io/client-go/discovery/fake", "k8s.io/client-go/informers", "k8s.io/client-go/informers/apps/v1", + "k8s.io/client-go/informers/core/v1", "k8s.io/client-go/kubernetes", "k8s.io/client-go/kubernetes/fake", "k8s.io/client-go/kubernetes/scheme", "k8s.io/client-go/kubernetes/typed/core/v1", "k8s.io/client-go/listers/apps/v1", + "k8s.io/client-go/listers/core/v1", "k8s.io/client-go/plugin/pkg/client/auth/gcp", "k8s.io/client-go/plugin/pkg/client/auth/oidc", "k8s.io/client-go/rest", @@ -1018,6 +1035,7 @@ "k8s.io/code-generator/cmd/client-gen", "k8s.io/kube-openapi/cmd/openapi-gen", "k8s.io/kube-openapi/pkg/common", + "k8s.io/kubernetes/cmd/kubeadm/app/util", "k8s.io/kubernetes/pkg/controller", "k8s.io/kubernetes/pkg/util/hash", "k8s.io/kubernetes/pkg/util/labels", diff --git a/controller/bluegreen.go b/controller/bluegreen.go index a68ee7a58f..3fb686422d 100644 --- a/controller/bluegreen.go +++ b/controller/bluegreen.go @@ -81,11 +81,13 @@ func (c *Controller) rolloutBlueGreen(r *v1alpha1.Rollout, rsList []*appsv1.Repl return c.syncRolloutStatusBlueGreen(oldRSs, newRS, previewSvc, activeSvc, r, false) } - logCtx.Info("Reconciling pause") - pauseBeforeSwitchActive := c.reconcileBlueGreenPause(activeSvc, previewSvc, r) - if pauseBeforeSwitchActive { - logCtx.Info("Not finished reconciling pause") - return c.syncRolloutStatusBlueGreen(oldRSs, newRS, previewSvc, activeSvc, r, true) + if !defaults.GetAutoPromotionEnabledOrDefault(r) { + logCtx.Info("Reconciling pause") + pauseBeforeSwitchActive := c.reconcileBlueGreenPause(activeSvc, previewSvc, r, newRS) + if pauseBeforeSwitchActive { + logCtx.Info("Not finished reconciling pause") + return c.syncRolloutStatusBlueGreen(oldRSs, newRS, previewSvc, activeSvc, r, true) + } } logCtx.Infof("Reconciling active service '%s'", activeSvc.Name) @@ -120,20 +122,17 @@ func (c *Controller) scaleDownPreviousActiveRS(rollout *v1alpha1.Rollout) bool { return now.After(pauseEnd.Time) } -func (c *Controller) reconcileBlueGreenPause(activeSvc, previewSvc *corev1.Service, rollout *v1alpha1.Rollout) bool { - if rollout.Spec.Strategy.BlueGreenStrategy.PreviewService == "" { +func (c *Controller) reconcileBlueGreenPause(activeSvc, previewSvc *corev1.Service, rollout *v1alpha1.Rollout, newRS *appsv1.ReplicaSet) bool { + newRSPodHash := newRS.Labels[v1alpha1.DefaultRolloutUniqueLabelKey] + + if _, ok := activeSvc.Spec.Selector[v1alpha1.DefaultRolloutUniqueLabelKey]; !ok { return false } - - // If the rollout is not paused and the preview service is pointing at the currentPodHash, the rollout should enter a paused state - currentPodHash := controller.ComputeHash(&rollout.Spec.Template, rollout.Status.CollisionCount) - if !rollout.Spec.Paused && rollout.Status.PauseStartTime == nil && !rollout.Status.BlueGreen.ScaleUpPreviewCheckPoint && previewSvc.Spec.Selector[v1alpha1.DefaultRolloutUniqueLabelKey] == currentPodHash { + // If the rollout is not paused and the active service is not point at the newRS, we should pause the rollout. + if !rollout.Spec.Paused && rollout.Status.PauseStartTime == nil && !rollout.Status.BlueGreen.ScaleUpPreviewCheckPoint && activeSvc.Spec.Selector[v1alpha1.DefaultRolloutUniqueLabelKey] != newRSPodHash { return true } - if _, ok := activeSvc.Spec.Selector[v1alpha1.DefaultRolloutUniqueLabelKey]; !ok { - return false - } pauseStartTime := rollout.Status.PauseStartTime autoPromoteActiveServiceDelaySeconds := rollout.Spec.Strategy.BlueGreenStrategy.AutoPromotionSeconds if autoPromoteActiveServiceDelaySeconds != nil && pauseStartTime != nil { @@ -205,19 +204,24 @@ func (c *Controller) syncRolloutStatusBlueGreen(oldRSs []*appsv1.ReplicaSet, new } pauseStartTime, paused := calculatePauseStatus(r, addPause) - newStatus.BlueGreen.ScaleUpPreviewCheckPoint = r.Status.BlueGreen.ScaleUpPreviewCheckPoint - if paused && r.Spec.Strategy.BlueGreenStrategy.PreviewReplicaCount != nil { - newStatus.BlueGreen.ScaleUpPreviewCheckPoint = true - } else if reconcileBlueGreenTemplateChange(r) { - newStatus.BlueGreen.ScaleUpPreviewCheckPoint = false - } else if newRS != nil && activeRS != nil && activeRS.Name == newRS.Name { - newStatus.BlueGreen.ScaleUpPreviewCheckPoint = false - } newStatus.PauseStartTime = pauseStartTime + newStatus.BlueGreen.ScaleUpPreviewCheckPoint = calculateScaleUpPreviewCheckPoint(r, newRS, activeRS) newStatus = c.calculateRolloutConditions(r, newStatus, allRSs, newRS) return c.persistRolloutStatus(r, &newStatus, &paused) } +func calculateScaleUpPreviewCheckPoint(r *v1alpha1.Rollout, newRS *appsv1.ReplicaSet, activeRS *appsv1.ReplicaSet) bool { + newRSAvailableCount := replicasetutil.GetAvailableReplicaCountForReplicaSets([]*appsv1.ReplicaSet{newRS}) + if r.Spec.Strategy.BlueGreenStrategy.PreviewReplicaCount != nil && newRSAvailableCount == *r.Spec.Strategy.BlueGreenStrategy.PreviewReplicaCount { + return true + } else if reconcileBlueGreenTemplateChange(r) { + return false + } else if newRS != nil && activeRS != nil && activeRS.Name == newRS.Name { + return false + } + return r.Status.BlueGreen.ScaleUpPreviewCheckPoint +} + // Should run only on scaling events and not during the normal rollout process. func (c *Controller) scaleBlueGreen(rollout *v1alpha1.Rollout, newRS *appsv1.ReplicaSet, oldRSs []*appsv1.ReplicaSet, previewSvc *corev1.Service, activeSvc *corev1.Service) error { rolloutReplicas := defaults.GetRolloutReplicasOrDefault(rollout) diff --git a/controller/bluegreen_test.go b/controller/bluegreen_test.go index ce2a322f56..cf838aa370 100644 --- a/controller/bluegreen_test.go +++ b/controller/bluegreen_test.go @@ -132,6 +132,7 @@ func TestBlueGreenHandlePause(t *testing.T) { f := newFixture(t) r1 := newBlueGreenRollout("foo", 1, nil, "active", "preview") + r1.Spec.Strategy.BlueGreenStrategy.AutoPromotionEnabled = pointer.BoolPtr(false) r2 := bumpVersion(r1) rs1 := newReplicaSetWithStatus(r1, "foo-895c6c4f9", 1, 1) rs2 := newReplicaSetWithStatus(r2, "foo-5f79b78d7f", 1, 1) @@ -173,6 +174,7 @@ func TestBlueGreenHandlePause(t *testing.T) { f := newFixture(t) r1 := newBlueGreenRollout("foo", 1, nil, "active", "preview") + r1.Spec.Strategy.BlueGreenStrategy.AutoPromotionEnabled = pointer.BoolPtr(false) r2 := bumpVersion(r1) rs1 := newReplicaSetWithStatus(r1, "foo-895c6c4f9", 1, 1) @@ -212,6 +214,7 @@ func TestBlueGreenHandlePause(t *testing.T) { f := newFixture(t) r1 := newBlueGreenRollout("foo", 1, nil, "active", "preview") + r1.Spec.Strategy.BlueGreenStrategy.AutoPromotionEnabled = pointer.BoolPtr(false) r2 := bumpVersion(r1) rs1 := newReplicaSetWithStatus(r1, "foo-895c6c4f9", 1, 1) @@ -245,6 +248,7 @@ func TestBlueGreenHandlePause(t *testing.T) { f := newFixture(t) r1 := newBlueGreenRollout("foo", 1, nil, "active", "preview") + r1.Spec.Strategy.BlueGreenStrategy.AutoPromotionEnabled = pointer.BoolPtr(false) r2 := bumpVersion(r1) r2.Spec.Strategy.BlueGreenStrategy.AutoPromotionSeconds = pointer.Int32Ptr(10) @@ -279,6 +283,7 @@ func TestBlueGreenHandlePause(t *testing.T) { f := newFixture(t) r1 := newBlueGreenRollout("foo", 1, nil, "active", "preview") + r1.Spec.Strategy.BlueGreenStrategy.AutoPromotionEnabled = pointer.BoolPtr(false) r2 := bumpVersion(r1) r2.Spec.Strategy.BlueGreenStrategy.AutoPromotionSeconds = pointer.Int32Ptr(10) @@ -322,7 +327,7 @@ func TestBlueGreenHandlePause(t *testing.T) { assert.Equal(t, expectedPatch, rolloutPatch) }) - t.Run("SkipWhenNoPreviewSpecified", func(t *testing.T) { + t.Run("NoPauseWhenAutoPromotionEnabledIsNotSet", func(t *testing.T) { f := newFixture(t) r1 := newBlueGreenRollout("foo", 1, nil, "active", "") @@ -372,10 +377,53 @@ func TestBlueGreenHandlePause(t *testing.T) { assert.Equal(t, expectedPatch, rolloutPatch) }) + t.Run("PauseWhenAutoPromotionEnabledIsFalse", func(t *testing.T) { + f := newFixture(t) + + r1 := newBlueGreenRollout("foo", 1, nil, "active", "") + r1.Spec.Strategy.BlueGreenStrategy.AutoPromotionEnabled = pointer.BoolPtr(false) + r2 := bumpVersion(r1) + + rs1 := newReplicaSetWithStatus(r1, "foo-895c6c4f9", 1, 1) + rs2 := newReplicaSetWithStatus(r2, "foo-5f79b78d7f", 1, 1) + rs1PodHash := rs1.Labels[v1alpha1.DefaultRolloutUniqueLabelKey] + + r2 = updateBlueGreenRolloutStatus(r2, "", rs1PodHash, 2, 1, 1, false, true) + + progressingCondition, _ := newProgressingCondition(conditions.NewReplicaSetReason, rs2) + conditions.SetRolloutCondition(&r2.Status, progressingCondition) + activeSelector := map[string]string{v1alpha1.DefaultRolloutUniqueLabelKey: rs1PodHash} + activeSvc := newService("active", 80, activeSelector) + + f.objects = append(f.objects, r2) + f.kubeobjects = append(f.kubeobjects, activeSvc, rs1, rs2) + f.rolloutLister = append(f.rolloutLister, r2) + f.replicaSetLister = append(f.replicaSetLister, rs1, rs2) + + f.expectGetServiceAction(activeSvc) + + now := metav1.Now().UTC().Format(time.RFC3339) + expectedPatchWithoutSubs := `{ + "spec": { + "paused": true + }, + "status": { + "pauseStartTime": "%s" + } + }` + expectedPatch := calculatePatch(r2, fmt.Sprintf(expectedPatchWithoutSubs, now)) + patchIndex := f.expectPatchRolloutActionWithPatch(r2, expectedPatch) + f.run(getKey(r2, t)) + + rolloutPatch := f.getPatchedRollout(patchIndex) + assert.Equal(t, expectedPatch, rolloutPatch) + }) + t.Run("SkipPreviewWhenActiveHasNoSelector", func(t *testing.T) { f := newFixture(t) r1 := newBlueGreenRollout("foo", 1, nil, "active", "preview") + r1.Spec.Strategy.BlueGreenStrategy.AutoPromotionEnabled = pointer.BoolPtr(false) rs1 := newReplicaSetWithStatus(r1, "foo-895c6c4f9", 1, 1) rs1PodHash := rs1.Labels[v1alpha1.DefaultRolloutUniqueLabelKey] @@ -419,6 +467,7 @@ func TestBlueGreenHandlePause(t *testing.T) { f := newFixture(t) r1 := newBlueGreenRollout("foo", 1, nil, "active", "preview") + r1.Spec.Strategy.BlueGreenStrategy.AutoPromotionEnabled = pointer.BoolPtr(false) r2 := bumpVersion(r1) rs1 := newReplicaSetWithStatus(r1, "foo-895c6c4f9", 1, 1) @@ -621,6 +670,7 @@ func TestBlueGreenRolloutStatusHPAStatusFieldsActiveSelectorSet(t *testing.T) { f := newFixture(t) r := newBlueGreenRollout("foo", 1, nil, "active", "preview") + r.Spec.Strategy.BlueGreenStrategy.AutoPromotionEnabled = pointer.BoolPtr(false) r2 := bumpVersion(r) rs1 := newReplicaSetWithStatus(r, "foo-867bc46cdc", 1, 1) diff --git a/controller/pause.go b/controller/pause.go index 830caa43a5..9c4424fbe4 100644 --- a/controller/pause.go +++ b/controller/pause.go @@ -6,6 +6,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + "github.com/argoproj/argo-rollouts/utils/defaults" logutil "github.com/argoproj/argo-rollouts/utils/log" ) @@ -50,6 +51,9 @@ func calculatePauseStatus(rollout *v1alpha1.Rollout, addPause bool) (*metav1.Tim if !paused { pauseStartTime = nil } + if rollout.Spec.Strategy.BlueGreenStrategy != nil && defaults.GetAutoPromotionEnabledOrDefault(rollout) { + return nil, false + } if addPause { if pauseStartTime == nil { diff --git a/manifests/crds/rollout-crd.yaml b/manifests/crds/rollout-crd.yaml index 94877d686b..b108f3fe91 100644 --- a/manifests/crds/rollout-crd.yaml +++ b/manifests/crds/rollout-crd.yaml @@ -424,6 +424,12 @@ spec: description: Name of the service that the rollout modifies as the active service. type: string + autoPromotionEnabled: + description: AutoPromotionEnabled indicates if the rollout should + automatically promote the new ReplicaSet to the active service + or enter a paused state. If not specified, the default value + is true. + type: boolean autoPromotionSeconds: description: AutoPromotionSeconds automatically promotes the current ReplicaSet to active after the specified pause delay diff --git a/manifests/install.yaml b/manifests/install.yaml index 098ba0e841..7ce4c02a7c 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -425,6 +425,12 @@ spec: description: Name of the service that the rollout modifies as the active service. type: string + autoPromotionEnabled: + description: AutoPromotionEnabled indicates if the rollout should + automatically promote the new ReplicaSet to the active service + or enter a paused state. If not specified, the default value + is true. + type: boolean autoPromotionSeconds: description: AutoPromotionSeconds automatically promotes the current ReplicaSet to active after the specified pause delay @@ -4754,6 +4760,136 @@ spec: - template - strategy type: object + status: + properties: + HPAReplicas: + description: HPAReplicas the number of non-terminated replicas that + are receiving active traffic + format: int32 + type: integer + availableReplicas: + description: Total number of available pods (ready for at least minReadySeconds) + targeted by this rollout. + format: int32 + type: integer + blueGreen: + description: BlueGreen describes the state of the bluegreen rollout + properties: + activeSelector: + description: ActiveSelector indicates which replicas set the active + service is serving traffic to + type: string + previewSelector: + description: PreviewSelector indicates which replicas set the preview + service is serving traffic to + type: string + previousActiveSelector: + description: PreviousActiveSelector indicates the last selector + that the active service used. This is used to know which replicaset + to avoid scaling down for the scale down delay + type: string + scaleDownDelayStartTime: + description: ScaleDownDelayStartTime indicates the start of the + scaleDownDelay + format: date-time + type: string + scaleUpPreviewCheckPoint: + description: ScaleUpPreviewCheckPoint indicates that the Replicaset + receiving traffic from the preview service is ready to be scaled + up after the rollout is unpaused + type: boolean + type: object + canary: + description: Canary describes the state of the canary rollout + properties: + stableRS: + description: StableRS indicates the last replicaset that walked + through all the canary steps or was the only replicaset + type: string + type: object + collisionCount: + description: Count of hash collisions for the Rollout. The Rollout controller + uses this field as a collision avoidance mechanism when it needs to + create the name for the newest ReplicaSet. + format: int32 + type: integer + conditions: + description: Conditions a list of conditions a rollout can have. + items: + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + format: date-time + type: string + lastUpdateTime: + description: The last time this condition was updated. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of deployment condition. + type: string + required: + - type + - status + - lastUpdateTime + - lastTransitionTime + - reason + - message + type: object + type: array + currentPodHash: + description: CurrentPodHash the hash of the current pod template + type: string + currentStepHash: + description: CurrentStepHash the hash of the current list of steps for + the current strategy. This is used to detect when the list of current + steps change + type: string + currentStepIndex: + description: CurrentStepIndex defines the current step of the rollout + is on. If the current step index is null, the controller will execute + the rollout. + format: int32 + type: integer + observedGeneration: + description: The generation observed by the rollout controller by taking + a hash of the spec. + type: string + pauseStartTime: + description: PauseStartTime this field is set when the rollout is in + a pause step and indicates the time the wait started at + format: date-time + type: string + readyReplicas: + description: Total number of ready pods targeted by this rollout. + format: int32 + type: integer + replicas: + description: Total number of non-terminated pods targeted by this rollout + (their labels match the selector). + format: int32 + type: integer + selector: + description: Selector that identifies the pods that are receiving active + traffic + type: string + updatedReplicas: + description: Total number of non-terminated pods targeted by this rollout + that have the desired template spec. + format: int32 + type: integer + type: object required: - spec type: object diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index ca20e67fc1..8d8271bf8e 100644 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -425,6 +425,12 @@ spec: description: Name of the service that the rollout modifies as the active service. type: string + autoPromotionEnabled: + description: AutoPromotionEnabled indicates if the rollout should + automatically promote the new ReplicaSet to the active service + or enter a paused state. If not specified, the default value + is true. + type: boolean autoPromotionSeconds: description: AutoPromotionSeconds automatically promotes the current ReplicaSet to active after the specified pause delay @@ -4754,6 +4760,136 @@ spec: - template - strategy type: object + status: + properties: + HPAReplicas: + description: HPAReplicas the number of non-terminated replicas that + are receiving active traffic + format: int32 + type: integer + availableReplicas: + description: Total number of available pods (ready for at least minReadySeconds) + targeted by this rollout. + format: int32 + type: integer + blueGreen: + description: BlueGreen describes the state of the bluegreen rollout + properties: + activeSelector: + description: ActiveSelector indicates which replicas set the active + service is serving traffic to + type: string + previewSelector: + description: PreviewSelector indicates which replicas set the preview + service is serving traffic to + type: string + previousActiveSelector: + description: PreviousActiveSelector indicates the last selector + that the active service used. This is used to know which replicaset + to avoid scaling down for the scale down delay + type: string + scaleDownDelayStartTime: + description: ScaleDownDelayStartTime indicates the start of the + scaleDownDelay + format: date-time + type: string + scaleUpPreviewCheckPoint: + description: ScaleUpPreviewCheckPoint indicates that the Replicaset + receiving traffic from the preview service is ready to be scaled + up after the rollout is unpaused + type: boolean + type: object + canary: + description: Canary describes the state of the canary rollout + properties: + stableRS: + description: StableRS indicates the last replicaset that walked + through all the canary steps or was the only replicaset + type: string + type: object + collisionCount: + description: Count of hash collisions for the Rollout. The Rollout controller + uses this field as a collision avoidance mechanism when it needs to + create the name for the newest ReplicaSet. + format: int32 + type: integer + conditions: + description: Conditions a list of conditions a rollout can have. + items: + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + format: date-time + type: string + lastUpdateTime: + description: The last time this condition was updated. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of deployment condition. + type: string + required: + - type + - status + - lastUpdateTime + - lastTransitionTime + - reason + - message + type: object + type: array + currentPodHash: + description: CurrentPodHash the hash of the current pod template + type: string + currentStepHash: + description: CurrentStepHash the hash of the current list of steps for + the current strategy. This is used to detect when the list of current + steps change + type: string + currentStepIndex: + description: CurrentStepIndex defines the current step of the rollout + is on. If the current step index is null, the controller will execute + the rollout. + format: int32 + type: integer + observedGeneration: + description: The generation observed by the rollout controller by taking + a hash of the spec. + type: string + pauseStartTime: + description: PauseStartTime this field is set when the rollout is in + a pause step and indicates the time the wait started at + format: date-time + type: string + readyReplicas: + description: Total number of ready pods targeted by this rollout. + format: int32 + type: integer + replicas: + description: Total number of non-terminated pods targeted by this rollout + (their labels match the selector). + format: int32 + type: integer + selector: + description: Selector that identifies the pods that are receiving active + traffic + type: string + updatedReplicas: + description: Total number of non-terminated pods targeted by this rollout + that have the desired template spec. + format: int32 + type: integer + type: object required: - spec type: object diff --git a/pkg/apis/rollouts/v1alpha1/openapi_generated.go b/pkg/apis/rollouts/v1alpha1/openapi_generated.go index bc6ebf09b8..4bfcc76f14 100644 --- a/pkg/apis/rollouts/v1alpha1/openapi_generated.go +++ b/pkg/apis/rollouts/v1alpha1/openapi_generated.go @@ -119,6 +119,13 @@ func schema_pkg_apis_rollouts_v1alpha1_BlueGreenStrategy(ref common.ReferenceCal Format: "int32", }, }, + "autoPromotionEnabled": { + SchemaProps: spec.SchemaProps{ + Description: "AutoPromotionEnabled indicates if the rollout should automatically promote the new ReplicaSet to the active service or enter a paused state. If not specified, the default value is true.", + Type: []string{"boolean"}, + Format: "", + }, + }, "autoPromotionSeconds": { SchemaProps: spec.SchemaProps{ Description: "AutoPromotionSeconds automatically promotes the current ReplicaSet to active after the specified pause delay in seconds after the ReplicaSet becomes ready. If omitted, the Rollout enters and remains in a paused state until manually resumed by resetting spec.Paused to false.", diff --git a/pkg/apis/rollouts/v1alpha1/types.go b/pkg/apis/rollouts/v1alpha1/types.go index f188dc0b3f..5a163db254 100644 --- a/pkg/apis/rollouts/v1alpha1/types.go +++ b/pkg/apis/rollouts/v1alpha1/types.go @@ -80,6 +80,10 @@ type BlueGreenStrategy struct { // resumed the new replicaset will be full scaled up before the switch occurs // +optional PreviewReplicaCount *int32 `json:"previewReplicaCount,omitempty"` + // AutoPromotionEnabled indicates if the rollout should automatically promote the new ReplicaSet + // to the active service or enter a paused state. If not specified, the default value is true. + // +optional + AutoPromotionEnabled *bool `json:"autoPromotionEnabled,omitempty"` // AutoPromotionSeconds automatically promotes the current ReplicaSet to active after the // specified pause delay in seconds after the ReplicaSet becomes ready. // If omitted, the Rollout enters and remains in a paused state until manually resumed by diff --git a/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go index 26d1c8d870..1346a8b495 100644 --- a/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go @@ -54,6 +54,11 @@ func (in *BlueGreenStrategy) DeepCopyInto(out *BlueGreenStrategy) { *out = new(int32) **out = **in } + if in.AutoPromotionEnabled != nil { + in, out := &in.AutoPromotionEnabled, &out.AutoPromotionEnabled + *out = new(bool) + **out = **in + } if in.AutoPromotionSeconds != nil { in, out := &in.AutoPromotionSeconds, &out.AutoPromotionSeconds *out = new(int32) diff --git a/utils/defaults/defaults.go b/utils/defaults/defaults.go index c842b52c12..e2f500d4db 100644 --- a/utils/defaults/defaults.go +++ b/utils/defaults/defaults.go @@ -19,6 +19,8 @@ const ( DefaultProgressDeadlineSeconds = int32(600) // DefaultScaleDownDelaySeconds default seconds before scaling down old replicaset after switching services DefaultScaleDownDelaySeconds = int32(30) + // DefaultAutoPromotionEnabled default value for auto promoting a blueGreen strategy + DefaultAutoPromotionEnabled = true ) // GetRolloutReplicasOrDefault returns the specified number of replicas in a rollout or the default number @@ -80,3 +82,13 @@ func GetScaleDownDelaySecondsOrDefault(rollout *v1alpha1.Rollout) int32 { return *rollout.Spec.Strategy.BlueGreenStrategy.ScaleDownDelaySeconds } + +func GetAutoPromotionEnabledOrDefault(rollout *v1alpha1.Rollout) bool { + if rollout.Spec.Strategy.BlueGreenStrategy == nil { + return DefaultAutoPromotionEnabled + } + if rollout.Spec.Strategy.BlueGreenStrategy.AutoPromotionEnabled == nil { + return DefaultAutoPromotionEnabled + } + return *rollout.Spec.Strategy.BlueGreenStrategy.AutoPromotionEnabled +} \ No newline at end of file diff --git a/utils/defaults/defaults_test.go b/utils/defaults/defaults_test.go index 8ecc31b8ac..bf0f0eac21 100644 --- a/utils/defaults/defaults_test.go +++ b/utils/defaults/defaults_test.go @@ -133,3 +133,28 @@ func TestGetScaleDownDelaySecondsOrDefault(t *testing.T) { } assert.Equal(t, DefaultScaleDownDelaySeconds, GetScaleDownDelaySecondsOrDefault(rolloutNoScaleDownDelaySeconds)) } + +func TestGetAutoPromotionEnabledOrDefault(t *testing.T) { + autoPromote := false + rolloutNonDefaultValue := &v1alpha1.Rollout{ + Spec: v1alpha1.RolloutSpec{ + Strategy: v1alpha1.RolloutStrategy{ + BlueGreenStrategy: &v1alpha1.BlueGreenStrategy{ + AutoPromotionEnabled: &autoPromote, + }, + }, + }, + } + + assert.Equal(t, autoPromote, GetAutoPromotionEnabledOrDefault(rolloutNonDefaultValue)) + rolloutNoStrategyDefaultValue := &v1alpha1.Rollout{} + assert.Equal(t, DefaultAutoPromotionEnabled, GetAutoPromotionEnabledOrDefault(rolloutNoStrategyDefaultValue)) + rolloutNoAutoPromotionEnabled := &v1alpha1.Rollout{ + Spec: v1alpha1.RolloutSpec{ + Strategy: v1alpha1.RolloutStrategy{ + BlueGreenStrategy: &v1alpha1.BlueGreenStrategy{}, + }, + }, + } + assert.Equal(t, DefaultAutoPromotionEnabled, GetAutoPromotionEnabledOrDefault(rolloutNoAutoPromotionEnabled)) +}