diff --git a/artifacts/flagger/crd.yaml b/artifacts/flagger/crd.yaml index 6d3b918e..d2fb67dd 100644 --- a/artifacts/flagger/crd.yaml +++ b/artifacts/flagger/crd.yaml @@ -967,6 +967,7 @@ spec: - Initialized - Waiting - Progressing + - WaitingPromotion - Promoting - Finalising - Succeeded diff --git a/charts/flagger/crds/crd.yaml b/charts/flagger/crds/crd.yaml index 6d3b918e..d2fb67dd 100644 --- a/charts/flagger/crds/crd.yaml +++ b/charts/flagger/crds/crd.yaml @@ -967,6 +967,7 @@ spec: - Initialized - Waiting - Progressing + - WaitingPromotion - Promoting - Finalising - Succeeded diff --git a/docs/gitbook/usage/how-it-works.md b/docs/gitbook/usage/how-it-works.md index 9e34283f..a6fcab3e 100644 --- a/docs/gitbook/usage/how-it-works.md +++ b/docs/gitbook/usage/how-it-works.md @@ -244,7 +244,7 @@ status: ``` The `Promoted` status condition can have one of the following reasons: -Initialized, Waiting, Progressing, Promoting, Finalising, Succeeded or Failed. +Initialized, Waiting, Progressing, WaitingPromotion, Promoting, Finalising, Succeeded or Failed. A failed canary will have the promoted status set to `false`, the reason to `failed` and the last applied spec will be different to the last promoted one. diff --git a/kustomize/base/flagger/crd.yaml b/kustomize/base/flagger/crd.yaml index 41c7ff87..8db3506f 100644 --- a/kustomize/base/flagger/crd.yaml +++ b/kustomize/base/flagger/crd.yaml @@ -966,6 +966,7 @@ spec: - Initialized - Waiting - Progressing + - WaitingPromotion - Promoting - Finalising - Succeeded diff --git a/pkg/apis/flagger/v1beta1/status.go b/pkg/apis/flagger/v1beta1/status.go index 17300312..fcfbd4b5 100644 --- a/pkg/apis/flagger/v1beta1/status.go +++ b/pkg/apis/flagger/v1beta1/status.go @@ -47,6 +47,8 @@ const ( CanaryPhaseWaiting CanaryPhase = "Waiting" // CanaryPhaseProgressing means the canary analysis is underway CanaryPhaseProgressing CanaryPhase = "Progressing" + // CanaryWaitingPromotion means the canary promotion is paused (waiting for confirmation to proceed) + CanaryWaitingPromotion CanaryPhase = "WaitingPromotion" // CanaryPhasePromoting means the canary analysis is finished and the primary spec has been updated CanaryPhasePromoting CanaryPhase = "Promoting" // CanaryPhaseFinalising means the canary promotion is finished and traffic has been routed back to primary diff --git a/pkg/canary/status.go b/pkg/canary/status.go index fbd7020a..caa132cf 100644 --- a/pkg/canary/status.go +++ b/pkg/canary/status.go @@ -207,6 +207,9 @@ func MakeStatusConditions(cd *flaggerv1.Canary, case flaggerv1.CanaryPhaseWaiting: status = corev1.ConditionUnknown message = "Waiting for approval." + case flaggerv1.CanaryWaitingPromotion: + status = corev1.ConditionUnknown + message = "Waiting for approval." case flaggerv1.CanaryPhaseProgressing: status = corev1.ConditionUnknown message = "New revision detected, progressing canary analysis." diff --git a/pkg/controller/scheduler.go b/pkg/controller/scheduler.go index 6537983a..684dbdca 100644 --- a/pkg/controller/scheduler.go +++ b/pkg/controller/scheduler.go @@ -310,7 +310,8 @@ func (c *Controller) advanceCanary(name string, namespace string) { // check if we should rollback if cd.Status.Phase == flaggerv1.CanaryPhaseProgressing || - cd.Status.Phase == flaggerv1.CanaryPhaseWaiting { + cd.Status.Phase == flaggerv1.CanaryPhaseWaiting || + cd.Status.Phase == flaggerv1.CanaryWaitingPromotion { if ok := c.runRollbackHooks(cd, cd.Status.Phase); ok { c.recordEventWarningf(cd, "Rolling back %s.%s manual webhook invoked", cd.Name, cd.Namespace) c.alert(cd, "Rolling back manual webhook invoked", false, flaggerv1.SeverityWarn) @@ -534,7 +535,7 @@ func (c *Controller) runCanary(canary *flaggerv1.Canary, canaryController canary // promote canary - max weight reached if canaryWeight >= maxWeight { // check promotion gate - if promote := c.runConfirmPromotionHooks(canary); !promote { + if promote := c.runConfirmPromotionHooks(canary, canaryController); !promote { return } @@ -576,7 +577,7 @@ func (c *Controller) runAB(canary *flaggerv1.Canary, canaryController canary.Con } // check promotion gate - if promote := c.runConfirmPromotionHooks(canary); !promote { + if promote := c.runConfirmPromotionHooks(canary, canaryController); !promote { return } @@ -622,7 +623,7 @@ func (c *Controller) runBlueGreen(canary *flaggerv1.Canary, canaryController can } // check promotion gate - if promote := c.runConfirmPromotionHooks(canary); !promote { + if promote := c.runConfirmPromotionHooks(canary, canaryController); !promote { return } @@ -751,6 +752,7 @@ func (c *Controller) shouldAdvance(canary *flaggerv1.Canary, canaryController ca canary.Status.Phase == flaggerv1.CanaryPhaseInitializing || canary.Status.Phase == flaggerv1.CanaryPhaseProgressing || canary.Status.Phase == flaggerv1.CanaryPhaseWaiting || + canary.Status.Phase == flaggerv1.CanaryWaitingPromotion || canary.Status.Phase == flaggerv1.CanaryPhasePromoting || canary.Status.Phase == flaggerv1.CanaryPhaseFinalising { return true, nil @@ -776,6 +778,7 @@ func (c *Controller) shouldAdvance(canary *flaggerv1.Canary, canaryController ca func (c *Controller) checkCanaryStatus(canary *flaggerv1.Canary, canaryController canary.Controller, shouldAdvance bool) bool { c.recorder.SetStatus(canary, canary.Status.Phase) if canary.Status.Phase == flaggerv1.CanaryPhaseProgressing || + canary.Status.Phase == flaggerv1.CanaryWaitingPromotion || canary.Status.Phase == flaggerv1.CanaryPhasePromoting || canary.Status.Phase == flaggerv1.CanaryPhaseFinalising { return true @@ -822,7 +825,8 @@ func (c *Controller) checkCanaryStatus(canary *flaggerv1.Canary, canaryControlle } func (c *Controller) hasCanaryRevisionChanged(canary *flaggerv1.Canary, canaryController canary.Controller) bool { - if canary.Status.Phase == flaggerv1.CanaryPhaseProgressing { + if canary.Status.Phase == flaggerv1.CanaryPhaseProgressing || + canary.Status.Phase == flaggerv1.CanaryWaitingPromotion { if diff, _ := canaryController.HasTargetChanged(canary); diff { return true } diff --git a/pkg/controller/scheduler_hooks.go b/pkg/controller/scheduler_hooks.go index cb551630..b17783ac 100644 --- a/pkg/controller/scheduler_hooks.go +++ b/pkg/controller/scheduler_hooks.go @@ -68,14 +68,19 @@ func (c *Controller) runConfirmRolloutHooks(canary *flaggerv1.Canary, canaryCont return true } -func (c *Controller) runConfirmPromotionHooks(canary *flaggerv1.Canary) bool { +func (c *Controller) runConfirmPromotionHooks(canary *flaggerv1.Canary, canaryController canary.Controller) bool { for _, webhook := range canary.GetAnalysis().Webhooks { if webhook.Type == flaggerv1.ConfirmPromotionHook { err := CallWebhook(canary.Name, canary.Namespace, flaggerv1.CanaryPhaseProgressing, webhook) if err != nil { - c.recordEventWarningf(canary, "Halt %s.%s advancement waiting for promotion approval %s", - canary.Name, canary.Namespace, webhook.Name) - c.alert(canary, "Canary promotion is waiting for approval.", false, flaggerv1.SeverityWarn) + if canary.Status.Phase != flaggerv1.CanaryWaitingPromotion { + if err := canaryController.SetStatusPhase(canary, flaggerv1.CanaryWaitingPromotion); err != nil { + c.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).Errorf("%v", err) + } + c.recordEventWarningf(canary, "Halt %s.%s advancement waiting for promotion approval %s", + canary.Name, canary.Namespace, webhook.Name) + c.alert(canary, "Canary promotion is waiting for approval.", false, flaggerv1.SeverityWarn) + } return false } else { c.recordEventInfof(canary, "Confirm-promotion check %s passed", webhook.Name)