@@ -28,6 +28,7 @@ import (
2828 kruiseclientsets "github.com/openkruise/kruise-api/client/clientset/versioned"
2929 internalapps "github.com/openkruise/kruise-tools/pkg/internal/apps"
3030
31+ utils "github.com/openkruise/kruise-tools/pkg/utils"
3132 appsv1 "k8s.io/api/apps/v1"
3233 corev1 "k8s.io/api/core/v1"
3334 apiequality "k8s.io/apimachinery/pkg/api/equality"
@@ -143,9 +144,6 @@ func (r *DeploymentRollbacker) Rollback(obj runtime.Object, updatedAnnotations m
143144 if dryRunStrategy == cmdutil .DryRunClient {
144145 return printTemplate (& rsForRevision .Spec .Template )
145146 }
146- if deployment .Spec .Paused {
147- return "" , fmt .Errorf ("you cannot rollback a paused deployment; resume it first with 'kubectl rollout resume deployment/%s' and try again" , name )
148- }
149147
150148 // Skip if the revision already matches current Deployment
151149 if equalIgnoreHash (& rsForRevision .Spec .Template , & deployment .Spec .Template ) {
@@ -157,13 +155,18 @@ func (r *DeploymentRollbacker) Rollback(obj runtime.Object, updatedAnnotations m
157155
158156 // compute deployment annotations
159157 annotations := map [string ]string {}
160- for k := range annotationsToSkip {
161- if v , ok := deployment .Annotations [k ]; ok {
158+
159+ // In the same vein as annotationsToSkip, which records annotations to exclude,
160+ // IsKruiseRolloutsAnnotation checks whether an annotation is generated by kruise-rollout.
161+ // Annotations identified as generated by kruise-rollout
162+ // will be skipped when copying from ReplicaSet annotations to Deployment annotations.
163+ for k , v := range deployment .Annotations {
164+ if annotationsToSkip [k ] || utils .IsKruiseRolloutsAnnotation (& k ) {
162165 annotations [k ] = v
163166 }
164167 }
165168 for k , v := range rsForRevision .Annotations {
166- if ! annotationsToSkip [k ] {
169+ if ! annotationsToSkip [k ] && ! utils . IsKruiseRolloutsAnnotation ( & k ) {
167170 annotations [k ] = v
168171 }
169172 }
@@ -270,6 +273,50 @@ func deploymentRevision(deployment *appsv1.Deployment, c kubernetes.Interface, t
270273 return nil , revisionNotFoundErr (toRevision )
271274 }
272275
276+ // even a deployment is paused, rollout history can update if the current rollout progress is
277+ // actually a rollback, for example:
278+
279+ // Step1. Initially, there's only one revision for the deployment:
280+ // REVISION CHANGE-CAUSE
281+ // 1 <none>
282+ // before the first canary release is completed, the rollout history won't update since the original deployment
283+ // is paused. If someone decides to rollback during this release (before it is completed),
284+ // we should return the latestReplicaSet instead of the previousReplicaSet.
285+
286+ // Step2. After a canary release completed, the rollout history will be like:
287+ // REVISION CHANGE-CAUSE
288+ // 1 <none>
289+ // 2 <none>
290+
291+ // Step3. Someone decides to rollback after this release using rollout undo.
292+ // It is ok, and it will be seen as a new canary release.
293+ // However, the rollout history will update though the original deployment is paused, because
294+ // the "new" canary revision can be found from the rollout history, which will be like:
295+ // REVISION CHANGE-CAUSE
296+ // 2 <none>
297+ // 3 <none>
298+
299+ // Step4. Someone decides to rollback during this rollback progress (before it is completed),
300+ // that means rolling back to the revision referred in the Step2.
301+ // We should return the previousReplicaSet instead of the latestReplicaSet.
302+ if utils .InCanaryProgress (deployment ) {
303+ if latestReplicaSet == nil {
304+ return nil , fmt .Errorf ("no rollout history found for deployment %q" , deployment .Name )
305+ }
306+ // only one revison found, then return it, equalIgnoreHash will be called later in Rollback function
307+ if previousReplicaSet == nil {
308+ return latestReplicaSet , nil
309+ }
310+ // possibly attempting to rollback during a rollback progress, so we return previousReplicaSet,
311+ // since the rollout history is updated
312+ if equalIgnoreHash (& latestReplicaSet .Spec .Template , & deployment .Spec .Template ) {
313+ return previousReplicaSet , nil
314+ }
315+ // attempting to rollback during a normal publication,
316+ // so we return latestReplicaSet since the rollout history hasn't updated
317+ return latestReplicaSet , nil
318+ }
319+
273320 if previousReplicaSet == nil {
274321 return nil , fmt .Errorf ("no rollout history found for deployment %q" , deployment .Name )
275322 }
0 commit comments