Skip to content

Commit

Permalink
rollouts: update progressive strategy to pause after wave (kptdev#3721)
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristopherFry authored and droot committed Jan 24, 2023
1 parent 66014b9 commit ddcd904
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 18 deletions.
14 changes: 12 additions & 2 deletions rollouts/api/v1alpha1/rollout_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,19 @@ type StrategyRollingUpdate struct {

// StrategyProgressive defines the progressive rollout strategy to use.
type StrategyProgressive struct {
// Reference of ProgressiveRolloutStrategy to use.
Name string `json:"name"`
// Name of the ProgressiveRolloutStrategy to use.
Name string `json:"name"`

// Namespace of the ProgressiveRolloutStrategy to use.
Namespace string `json:"namespace"`

// PauseAfterWave represents the highest wave the strategy will deploy.
PauseAfterWave PauseAfterWave `json:"pauseAfterWave,omitempty"`
}

type PauseAfterWave struct {
// WaveName represents name of the wave defined in the ProgressiveRolloutStrategy.
WaveName string `json:"waveName"`
}

type RolloutStrategy struct {
Expand Down
16 changes: 16 additions & 0 deletions rollouts/api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 14 additions & 1 deletion rollouts/config/crd/bases/gitops.kpt.dev_rollouts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,23 @@ spec:
strategy to use.
properties:
name:
description: Reference of ProgressiveRolloutStrategy to use.
description: Name of the ProgressiveRolloutStrategy to use.
type: string
namespace:
description: Namespace of the ProgressiveRolloutStrategy to
use.
type: string
pauseAfterWave:
description: PauseAfterWave represents the highest wave the
strategy will deploy.
properties:
waveName:
description: WaveName represents name of the wave defined
in the ProgressiveRolloutStrategy.
type: string
required:
- waveName
type: object
required:
- name
- namespace
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ spec:
progressive:
name: cluster-addons-default-rollout
namespace: default
pauseAfterWave:
waveName: dev clusters
48 changes: 33 additions & 15 deletions rollouts/controllers/rollout_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,43 +192,56 @@ func (r *RolloutReconciler) validateProgressiveRolloutStrategy(ctx context.Conte
return err
}

clusterWaveMap := make(map[string]int)
clusterWaveMap := make(map[string]string)
for _, cluster := range allClusters.Items {
clusterWaveMap[cluster.Name] = -1
clusterWaveMap[cluster.Name] = ""
}

for waveIdx, wave := range strategy.Spec.Waves {
pauseAfterWaveName := ""
pauseWaveNameFound := false

if rollout.Spec.Strategy.Progressive != nil {
pauseAfterWaveName = rollout.Spec.Strategy.Progressive.PauseAfterWave.WaveName
}

for _, wave := range strategy.Spec.Waves {
waveClusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector, wave.Targets.Selector)
if err != nil {
return err
}

if len(waveClusters.Items) == 0 {
return fmt.Errorf("wave %d does not target any clusters", waveIdx)
return fmt.Errorf("wave %q does not target any clusters", wave.Name)
}

for _, cluster := range waveClusters.Items {
currentClusterWave, found := clusterWaveMap[cluster.Name]
if !found {
// this should never happen
return fmt.Errorf("wave %d references cluster %s not selected by the rollout", waveIdx, cluster.Name)
return fmt.Errorf("wave %q references cluster %s not selected by the rollout", wave.Name, cluster.Name)
}

if currentClusterWave > -1 {
return fmt.Errorf("a cluster cannot be selected by more than one wave - cluster %s is selected by waves %d and %d", cluster.Name, currentClusterWave, waveIdx)
if currentClusterWave != "" {
return fmt.Errorf("a cluster cannot be selected by more than one wave - cluster %s is selected by waves %q and %q", cluster.Name, currentClusterWave, wave.Name)
}

clusterWaveMap[cluster.Name] = waveIdx
clusterWaveMap[cluster.Name] = wave.Name
}

pauseWaveNameFound = pauseWaveNameFound || pauseAfterWaveName == wave.Name
}

for _, cluster := range allClusters.Items {
wave, _ := clusterWaveMap[cluster.Name]
if wave == -1 {
if wave == "" {
return fmt.Errorf("waves should cover all clusters selected by the rollout - cluster %s is not covered by any waves", cluster.Name)
}
}

if pauseAfterWaveName != "" && !pauseWaveNameFound {
return fmt.Errorf("%q pause wave not found in progressive rollout strategy", pauseAfterWaveName)
}

return nil
}

Expand Down Expand Up @@ -257,7 +270,12 @@ func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitop

allClusterStatuses := []gitopsv1alpha1.ClusterStatus{}

waveInProgress := false
pauseFutureWaves := false
pauseAfterWaveName := ""

if rollout.Spec.Strategy.Progressive != nil {
pauseAfterWaveName = rollout.Spec.Strategy.Progressive.PauseAfterWave.WaveName
}

for _, wave := range strategy.Spec.Waves {
waveClusters, err := r.store.ListClusters(ctx, rollout.Spec.Targets.Selector, wave.Targets.Selector)
Expand All @@ -282,13 +300,13 @@ func (r *RolloutReconciler) reconcileRollout(ctx context.Context, rollout *gitop
return err
}

thisWaveInProgress, clusterStatuses, err := r.rolloutTargets(ctx, rollout, &wave, targets, waveInProgress)
thisWaveInProgress, clusterStatuses, err := r.rolloutTargets(ctx, rollout, &wave, targets, pauseFutureWaves)
if err != nil {
return err
}

if thisWaveInProgress {
waveInProgress = true
if thisWaveInProgress || wave.Name == pauseAfterWaveName {
pauseFutureWaves = true
}

allClusterStatuses = append(allClusterStatuses, clusterStatuses...)
Expand Down Expand Up @@ -382,14 +400,14 @@ func (r *RolloutReconciler) computeTargets(ctx context.Context,
return targets, nil
}

func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv1alpha1.Rollout, wave *gitopsv1alpha1.Wave, targets *Targets, previousWaveAlreadyInProgress bool) (bool, []gitopsv1alpha1.ClusterStatus, error) {
func (r *RolloutReconciler) rolloutTargets(ctx context.Context, rollout *gitopsv1alpha1.Rollout, wave *gitopsv1alpha1.Wave, targets *Targets, pauseWave bool) (bool, []gitopsv1alpha1.ClusterStatus, error) {
clusterStatuses := []gitopsv1alpha1.ClusterStatus{}

concurrentUpdates := 0
maxConcurrent := int(wave.MaxConcurrent)
waiting := "Waiting"

if previousWaveAlreadyInProgress {
if pauseWave {
maxConcurrent = 0
waiting = "Waiting (Upcoming Wave)"
}
Expand Down

0 comments on commit ddcd904

Please sign in to comment.