Skip to content

Commit fe4bb1b

Browse files
authored
fix: rollout paused longer than progressDeadlineSeconds would briefly degrade (argoproj#1268)
* Do not exit status cmd for ProgressDeadlineExceeded error Signed-off-by: khhirani <[email protected]> * Do not return status if RO is paused in sync.go Signed-off-by: khhirani <[email protected]>
1 parent fa865d3 commit fe4bb1b

File tree

8 files changed

+219
-25
lines changed

8 files changed

+219
-25
lines changed

pkg/kubectl-argo-rollouts/cmd/status/status.go

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ func NewCmdStatus(o *options.ArgoRolloutsOptions) *cobra.Command {
6565
controller.RegisterCallback(func(roInfo *rollout.RolloutInfo) {
6666
rolloutUpdates <- roInfo
6767
})
68-
go statusOptions.WatchStatus(ctx.Done(), cancel, statusOptions.Timeout, rolloutUpdates)
69-
controller.Run(ctx)
68+
go controller.Run(ctx)
69+
statusOptions.WatchStatus(ctx.Done(), rolloutUpdates)
7070

7171
finalRi, err := controller.GetRolloutInfo()
7272
if err != nil {
@@ -88,14 +88,14 @@ func NewCmdStatus(o *options.ArgoRolloutsOptions) *cobra.Command {
8888
return cmd
8989
}
9090

91-
func (o *StatusOptions) WatchStatus(stopCh <-chan struct{}, cancelFunc context.CancelFunc, timeoutDuration time.Duration, rolloutUpdates chan *rollout.RolloutInfo) {
91+
func (o *StatusOptions) WatchStatus(stopCh <-chan struct{}, rolloutUpdates chan *rollout.RolloutInfo) string {
9292
timeout := make(chan bool)
9393
var roInfo *rollout.RolloutInfo
9494
var preventFlicker time.Time
9595

96-
if timeoutDuration != 0 {
96+
if o.Timeout != 0 {
9797
go func() {
98-
time.Sleep(timeoutDuration)
98+
time.Sleep(o.Timeout)
9999
timeout <- true
100100
}()
101101
}
@@ -105,18 +105,16 @@ func (o *StatusOptions) WatchStatus(stopCh <-chan struct{}, cancelFunc context.C
105105
case roInfo = <-rolloutUpdates:
106106
if roInfo != nil && roInfo.Status == "Healthy" || roInfo.Status == "Degraded" {
107107
fmt.Fprintln(o.Out, roInfo.Status)
108-
cancelFunc()
109-
return
108+
return roInfo.Status
110109
}
111110
if roInfo != nil && time.Now().After(preventFlicker.Add(200*time.Millisecond)) {
112111
fmt.Fprintf(o.Out, "%s - %s\n", roInfo.Status, roInfo.Message)
113112
preventFlicker = time.Now()
114113
}
115114
case <-stopCh:
116-
return
115+
return ""
117116
case <-timeout:
118-
cancelFunc()
119-
return
117+
return ""
120118
}
121119
}
122120
}

rollout/analysis_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,6 +1475,9 @@ func TestDoNotCreateBackgroundAnalysisRunAfterInconclusiveRun(t *testing.T) {
14751475
pausedCondition, _ := newPausedCondition(true)
14761476
conditions.SetRolloutCondition(&r2.Status, pausedCondition)
14771477

1478+
availableCondition, _ := newAvailableCondition(true)
1479+
conditions.SetRolloutCondition(&r2.Status, availableCondition)
1480+
14781481
f.rolloutLister = append(f.rolloutLister, r2)
14791482
f.analysisTemplateLister = append(f.analysisTemplateLister, at)
14801483
f.objects = append(f.objects, r2, at)
@@ -1844,7 +1847,7 @@ func TestRolloutPrePromotionAnalysisSwitchServiceAfterSuccess(t *testing.T) {
18441847
f.expectPatchReplicaSetAction(rs1)
18451848
patchIndex := f.expectPatchRolloutActionWithPatch(r2, OnlyObservedGenerationPatch)
18461849
f.run(getKey(r2, t))
1847-
patch := f.getPatchedRollout(patchIndex)
1850+
patch := f.getPatchedRolloutWithoutConditions(patchIndex)
18481851
expectedPatch := fmt.Sprintf(`{
18491852
"status": {
18501853
"blueGreen": {
@@ -1912,7 +1915,7 @@ func TestRolloutPrePromotionAnalysisHonorAutoPromotionSeconds(t *testing.T) {
19121915
f.expectPatchReplicaSetAction(rs1)
19131916
patchIndex := f.expectPatchRolloutActionWithPatch(r2, OnlyObservedGenerationPatch)
19141917
f.run(getKey(r2, t))
1915-
patch := f.getPatchedRollout(patchIndex)
1918+
patch := f.getPatchedRolloutWithoutConditions(patchIndex)
19161919
expectedPatch := fmt.Sprintf(`{
19171920
"status": {
19181921
"blueGreen": {
@@ -1965,6 +1968,9 @@ func TestRolloutPrePromotionAnalysisDoNothingOnInconclusiveAnalysis(t *testing.T
19651968
pausedCondition, _ := newPausedCondition(true)
19661969
conditions.SetRolloutCondition(&r2.Status, pausedCondition)
19671970

1971+
availableCondition, _ := newAvailableCondition(true)
1972+
conditions.SetRolloutCondition(&r2.Status, availableCondition)
1973+
19681974
activeSelector := map[string]string{v1alpha1.DefaultRolloutUniqueLabelKey: rs1PodHash}
19691975
activeSvc := newService("active", 80, activeSelector, r2)
19701976

rollout/bluegreen_test.go

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ func TestBlueGreenHandlePause(t *testing.T) {
440440
patchRolloutIndex := f.expectPatchRolloutActionWithPatch(r2, expectedPatch)
441441
f.run(getKey(r2, t))
442442

443-
rolloutPatch := f.getPatchedRollout(patchRolloutIndex)
443+
rolloutPatch := f.getPatchedRolloutWithoutConditions(patchRolloutIndex)
444444
assert.Equal(t, expectedPatch, rolloutPatch)
445445
})
446446

@@ -798,7 +798,7 @@ func TestBlueGreenRolloutStatusHPAStatusFieldsActiveSelectorSet(t *testing.T) {
798798
patchIndex := f.expectPatchRolloutActionWithPatch(r2, expectedPatch)
799799
f.run(getKey(r2, t))
800800

801-
rolloutPatch := f.getPatchedRollout(patchIndex)
801+
rolloutPatch := f.getPatchedRolloutWithoutConditions(patchIndex)
802802
assert.Equal(t, expectedPatch, rolloutPatch)
803803
}
804804

@@ -1060,6 +1060,8 @@ func TestBlueGreenRolloutCompletedFalse(t *testing.T) {
10601060
conditions.SetRolloutCondition(&r2.Status, progressingCondition)
10611061
pausedCondition, _ := newPausedCondition(true)
10621062
conditions.SetRolloutCondition(&r2.Status, pausedCondition)
1063+
availableCondition, _ := newAvailableCondition(true)
1064+
conditions.SetRolloutCondition(&r2.Status, availableCondition)
10631065

10641066
f.kubeobjects = append(f.kubeobjects, rs2)
10651067
f.replicaSetLister = append(f.replicaSetLister, rs2)
@@ -1084,7 +1086,7 @@ func TestBlueGreenRolloutCompletedFalse(t *testing.T) {
10841086
err := json.Unmarshal([]byte(patch), &rolloutPatch)
10851087
assert.NoError(t, err)
10861088

1087-
index := len(rolloutPatch.Status.Conditions) - 1
1089+
index := len(rolloutPatch.Status.Conditions) - 2
10881090
assert.Equal(t, v1alpha1.RolloutCompleted, rolloutPatch.Status.Conditions[index].Type)
10891091
assert.Equal(t, corev1.ConditionFalse, rolloutPatch.Status.Conditions[index].Status)
10901092
}
@@ -1322,3 +1324,74 @@ func TestBlueGreenAbort(t *testing.T) {
13221324
patch := f.getPatchedRollout(patchIndex)
13231325
assert.Equal(t, calculatePatch(r2, expectedPatch), patch)
13241326
}
1327+
1328+
func TestBlueGreenHandlePauseAutoPromoteWithConditions(t *testing.T) {
1329+
f := newFixture(t)
1330+
defer f.Close()
1331+
1332+
r1 := newBlueGreenRollout("foo", 1, nil, "active", "preview")
1333+
r2 := bumpVersion(r1)
1334+
r2.Spec.Strategy.BlueGreen.AutoPromotionSeconds = 10
1335+
1336+
rs1 := newReplicaSetWithStatus(r1, 1, 1)
1337+
rs2 := newReplicaSetWithStatus(r2, 1, 1)
1338+
rs1PodHash := rs1.Labels[v1alpha1.DefaultRolloutUniqueLabelKey]
1339+
rs2PodHash := rs2.Labels[v1alpha1.DefaultRolloutUniqueLabelKey]
1340+
1341+
r2 = updateBlueGreenRolloutStatus(r2, rs2PodHash, rs1PodHash, rs1PodHash, 1, 1, 2, 1, true, true)
1342+
now := metav1.Now()
1343+
before := metav1.NewTime(now.Add(-1 * time.Minute))
1344+
r2.Status.PauseConditions[0].StartTime = before
1345+
r2.Status.ControllerPause = true
1346+
progressingCondition, _ := newProgressingCondition(conditions.RolloutPausedReason, rs2, "")
1347+
conditions.SetRolloutCondition(&r2.Status, progressingCondition)
1348+
1349+
pausedCondition, _ := newPausedCondition(true)
1350+
conditions.SetRolloutCondition(&r2.Status, pausedCondition)
1351+
r2.Status.Phase, r2.Status.Message = rolloututil.CalculateRolloutPhase(r2.Spec, r2.Status)
1352+
1353+
availableCondition, _ := newAvailableCondition(true)
1354+
conditions.SetRolloutCondition(&r2.Status, availableCondition)
1355+
r2.Status.Phase, r2.Status.Message = rolloututil.CalculateRolloutPhase(r2.Spec, r2.Status)
1356+
1357+
activeSelector := map[string]string{v1alpha1.DefaultRolloutUniqueLabelKey: rs1PodHash}
1358+
activeSvc := newService("active", 80, activeSelector, r2)
1359+
previewSelector := map[string]string{v1alpha1.DefaultRolloutUniqueLabelKey: rs2PodHash}
1360+
previewSvc := newService("preview", 80, previewSelector, r2)
1361+
1362+
f.objects = append(f.objects, r2)
1363+
f.kubeobjects = append(f.kubeobjects, activeSvc, previewSvc, rs1, rs2)
1364+
f.rolloutLister = append(f.rolloutLister, r2)
1365+
f.replicaSetLister = append(f.replicaSetLister, rs1, rs2)
1366+
f.serviceLister = append(f.serviceLister, activeSvc, previewSvc)
1367+
1368+
expectedPatchWithoutSubs := `{
1369+
"status": {
1370+
"blueGreen": {
1371+
"activeSelector": "%s"
1372+
},
1373+
"conditions": [%s, %s, %s],
1374+
"stableRS": "%s",
1375+
"pauseConditions": null,
1376+
"controllerPause": null,
1377+
"selector": "foo=bar,rollouts-pod-template-hash=%s",
1378+
"phase": "Healthy",
1379+
"message": null
1380+
}
1381+
}`
1382+
availableCondBytes, err := json.Marshal(r2.Status.Conditions[0])
1383+
assert.Nil(t, err)
1384+
updatedProgressingCond, _ := newProgressingCondition(conditions.ReplicaSetUpdatedReason, rs2, fmt.Sprintf("ReplicaSet \"%s\" is progressing.", rs2.Name))
1385+
progressingCondBytes, err := json.Marshal(updatedProgressingCond)
1386+
assert.Nil(t, err)
1387+
pausedCondBytes, err := json.Marshal(r2.Status.Conditions[2])
1388+
assert.Nil(t, err)
1389+
expectedPatch := calculatePatch(r2, fmt.Sprintf(expectedPatchWithoutSubs, rs2PodHash, string(availableCondBytes), string(pausedCondBytes), string(progressingCondBytes), rs2PodHash, rs2PodHash))
1390+
f.expectPatchServiceAction(activeSvc, rs2PodHash)
1391+
f.expectPatchReplicaSetAction(rs1)
1392+
patchRolloutIndex := f.expectPatchRolloutActionWithPatch(r2, expectedPatch)
1393+
f.run(getKey(r2, t))
1394+
1395+
rolloutPatch := f.getPatchedRollout(patchRolloutIndex)
1396+
assert.Equal(t, expectedPatch, rolloutPatch)
1397+
}

rollout/canary_test.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,9 @@ func TestCanaryRolloutUpdatePauseConditionWhilePaused(t *testing.T) {
230230
pausedCondition, _ := newPausedCondition(true)
231231
conditions.SetRolloutCondition(&r2.Status, pausedCondition)
232232

233+
availableCondition, _ := newAvailableCondition(true)
234+
conditions.SetRolloutCondition(&r2.Status, availableCondition)
235+
233236
rs1 := newReplicaSetWithStatus(r1, 10, 10)
234237
rs2 := newReplicaSetWithStatus(r2, 0, 0)
235238

@@ -896,6 +899,9 @@ func TestSyncRolloutWaitAddToQueue(t *testing.T) {
896899
pausedCondition, _ := newPausedCondition(true)
897900
conditions.SetRolloutCondition(&r2.Status, pausedCondition)
898901

902+
availableCondition, _ := newAvailableCondition(true)
903+
conditions.SetRolloutCondition(&r2.Status, availableCondition)
904+
899905
r2.Status.ObservedGeneration = strconv.Itoa(int(r2.Generation))
900906
f.rolloutLister = append(f.rolloutLister, r2)
901907
f.objects = append(f.objects, r2)
@@ -941,6 +947,9 @@ func TestSyncRolloutIgnoreWaitOutsideOfReconciliationPeriod(t *testing.T) {
941947
pausedCondition, _ := newPausedCondition(true)
942948
conditions.SetRolloutCondition(&r2.Status, pausedCondition)
943949

950+
availableCondition, _ := newAvailableCondition(true)
951+
conditions.SetRolloutCondition(&r2.Status, availableCondition)
952+
944953
f.rolloutLister = append(f.rolloutLister, r2)
945954
f.objects = append(f.objects, r2)
946955

@@ -997,7 +1006,7 @@ func TestSyncRolloutWaitIncrementStepIndex(t *testing.T) {
9971006
patchIndex := f.expectPatchRolloutAction(r2)
9981007
f.run(getKey(r2, t))
9991008

1000-
patch := f.getPatchedRollout(patchIndex)
1009+
patch := f.getPatchedRolloutWithoutConditions(patchIndex)
10011010
expectedPatch := `{
10021011
"status":{
10031012
"controllerPause": null,
@@ -1048,7 +1057,7 @@ func TestCanaryRolloutStatusHPAStatusFields(t *testing.T) {
10481057
index := f.expectPatchRolloutActionWithPatch(r2, expectedPatchWithSub)
10491058
f.run(getKey(r2, t))
10501059

1051-
patch := f.getPatchedRollout(index)
1060+
patch := f.getPatchedRolloutWithoutConditions(index)
10521061
assert.Equal(t, calculatePatch(r2, expectedPatchWithSub), patch)
10531062
}
10541063

@@ -1285,7 +1294,7 @@ func TestCanaryRolloutScaleWhilePaused(t *testing.T) {
12851294
updatedRS := f.getUpdatedReplicaSet(updatedIndex)
12861295
assert.Equal(t, int32(8), *updatedRS.Spec.Replicas)
12871296

1288-
patch := f.getPatchedRollout(patchIndex)
1297+
patch := f.getPatchedRolloutWithoutConditions(patchIndex)
12891298
expectedPatch := calculatePatch(r2, OnlyObservedGenerationPatch)
12901299
assert.Equal(t, expectedPatch, patch)
12911300
}
@@ -1381,7 +1390,7 @@ func TestNoResumeAfterPauseDurationIfUserPaused(t *testing.T) {
13811390
_ = f.expectPatchRolloutAction(r2) // this just sets a conditions. ignore for now
13821391
patchIndex := f.expectPatchRolloutAction(r2)
13831392
f.run(getKey(r2, t))
1384-
patch := f.getPatchedRollout(patchIndex)
1393+
patch := f.getPatchedRolloutWithoutConditions(patchIndex)
13851394
expectedPatch := `{
13861395
"status": {
13871396
"message": "manually paused"
@@ -1419,6 +1428,9 @@ func TestHandleNilNewRSOnScaleAndImageChange(t *testing.T) {
14191428
pausedCondition, _ := newPausedCondition(true)
14201429
conditions.SetRolloutCondition(&r2.Status, pausedCondition)
14211430

1431+
availableCondition, _ := newAvailableCondition(true)
1432+
conditions.SetRolloutCondition(&r2.Status, availableCondition)
1433+
14221434
f.kubeobjects = append(f.kubeobjects, rs1)
14231435
f.replicaSetLister = append(f.replicaSetLister, rs1)
14241436
f.rolloutLister = append(f.rolloutLister, r2)

rollout/controller_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,25 @@ func (f *fixture) getPatchedRollout(index int) string {
965965
return string(patchAction.GetPatch())
966966
}
967967

968+
func (f *fixture) getPatchedRolloutWithoutConditions(index int) string {
969+
action := filterInformerActions(f.client.Actions())[index]
970+
patchAction, ok := action.(core.PatchAction)
971+
if !ok {
972+
f.t.Fatalf("Expected Patch action, not %s", action.GetVerb())
973+
}
974+
ro := make(map[string]interface{})
975+
err := json.Unmarshal(patchAction.GetPatch(), &ro)
976+
if err != nil {
977+
f.t.Fatalf("Unable to unmarshal Patch")
978+
}
979+
unstructured.RemoveNestedField(ro, "status", "conditions")
980+
roBytes, err := json.Marshal(ro)
981+
if err != nil {
982+
f.t.Fatalf("Unable to marshal Patch")
983+
}
984+
return string(roBytes)
985+
}
986+
968987
func (f *fixture) getPatchedRolloutAsObject(index int) *v1alpha1.Rollout {
969988
action := filterInformerActions(f.client.Actions())[index]
970989
patchAction, ok := action.(core.PatchAction)

rollout/sync.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -571,11 +571,14 @@ func (c *rolloutContext) patchCondition(r *v1alpha1.Rollout, newStatus *v1alpha1
571571
return nil
572572
}
573573

574-
// isIndefiniteStep returns whether or not the rollout is at an Experiment or Analysis step which should
574+
// isIndefiniteStep returns whether or not the rollout is at an Experiment or Analysis or Pause step which should
575575
// not affect the progressDeadlineSeconds
576576
func isIndefiniteStep(r *v1alpha1.Rollout) bool {
577577
currentStep, _ := replicasetutil.GetCurrentCanaryStep(r)
578-
return currentStep != nil && (currentStep.Experiment != nil || currentStep.Analysis != nil)
578+
if currentStep != nil && (currentStep.Experiment != nil || currentStep.Analysis != nil || currentStep.Pause != nil) {
579+
return true
580+
}
581+
return false
579582
}
580583

581584
func (c *rolloutContext) calculateRolloutConditions(newStatus v1alpha1.RolloutStatus) v1alpha1.RolloutStatus {
@@ -605,10 +608,6 @@ func (c *rolloutContext) calculateRolloutConditions(newStatus v1alpha1.RolloutSt
605608
}
606609
}
607610

608-
if isPaused {
609-
return newStatus
610-
}
611-
612611
// If there is only one replica set that is active then that means we are not running
613612
// a new rollout and this is a resync where we don't need to estimate any progress.
614613
// In such a case, we should simply not estimate any progress for this rollout.

test/e2e/functional_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,3 +1237,40 @@ func (s *FunctionalSuite) TestControllerMetrics() {
12371237
assert.NoError(s.T(), err)
12381238
assert.Equal(s.T(), http.StatusOK, resp.StatusCode)
12391239
}
1240+
1241+
func (s *FunctionalSuite) TestRolloutPauseDurationGreaterThanProgressDeadlineSeconds() {
1242+
(s.Given().
1243+
HealthyRollout(`
1244+
apiVersion: argoproj.io/v1alpha1
1245+
kind: Rollout
1246+
metadata:
1247+
name: rollout-canary
1248+
spec:
1249+
replicas: 3
1250+
progressDeadlineSeconds: 5
1251+
selector:
1252+
matchLabels:
1253+
app: rollout-canary
1254+
template:
1255+
metadata:
1256+
labels:
1257+
app: rollout-canary
1258+
spec:
1259+
containers:
1260+
- name: rollouts-demo
1261+
image: nginx:1.19-alpine
1262+
ports:
1263+
- containerPort: 80
1264+
strategy:
1265+
canary:
1266+
steps:
1267+
- setWeight: 32
1268+
- pause: {duration: 30s}
1269+
- setWeight: 67
1270+
`).
1271+
When().
1272+
UpdateSpec().
1273+
WatchRolloutStatus("Healthy").
1274+
Then().
1275+
ExpectRolloutStatus("Healthy"))
1276+
}

0 commit comments

Comments
 (0)