update api for future bluegreen (#214)
add status conversion nextStepIndex default value from 0 to -1 restore the enableExtra field in BR Signed-off-by: yunbo <yunbo10124scut@gmail.com> Co-authored-by: yunbo <yunbo10124scut@gmail.com>
This commit is contained in:
		
							parent
							
								
									62794dc883
								
							
						
					
					
						commit
						1e8af4a4c1
					
				| 
						 | 
				
			
			@ -54,6 +54,16 @@ type ReleasePlan struct {
 | 
			
		|||
	// only support for canary deployment
 | 
			
		||||
	// +optional
 | 
			
		||||
	PatchPodTemplateMetadata *PatchPodTemplateMetadata `json:"patchPodTemplateMetadata,omitempty"`
 | 
			
		||||
	// RollingStyle can be "Canary", "Partiton" or "BlueGreen"
 | 
			
		||||
	RollingStyle RollingStyleType `json:"rollingStyle,omitempty"`
 | 
			
		||||
	// EnableExtraWorkloadForCanary indicates whether to create extra workload for canary
 | 
			
		||||
	// True corresponds to RollingStyle "Canary".
 | 
			
		||||
	// False corresponds to RollingStyle "Partiton".
 | 
			
		||||
	// Ignored in BlueGreen-style.
 | 
			
		||||
	// This field is about to deprecate, use RollingStyle instead.
 | 
			
		||||
	// If both of them are set, controller will only consider this
 | 
			
		||||
	// filed when RollingStyle is empty
 | 
			
		||||
	EnableExtraWorkloadForCanary bool `json:"enableExtraWorkloadForCanary"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FinalizingPolicyType string
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -104,18 +104,22 @@ func (src *Rollout) ConvertTo(dst conversion.Hub) error {
 | 
			
		|||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		obj.Status.CanaryStatus = &v1beta1.CanaryStatus{
 | 
			
		||||
			ObservedWorkloadGeneration: src.Status.CanaryStatus.ObservedWorkloadGeneration,
 | 
			
		||||
			ObservedRolloutID:          src.Status.CanaryStatus.ObservedRolloutID,
 | 
			
		||||
			RolloutHash:                src.Status.CanaryStatus.RolloutHash,
 | 
			
		||||
			StableRevision:             src.Status.CanaryStatus.StableRevision,
 | 
			
		||||
			CanaryRevision:             src.Status.CanaryStatus.CanaryRevision,
 | 
			
		||||
			PodTemplateHash:            src.Status.CanaryStatus.PodTemplateHash,
 | 
			
		||||
			CanaryReplicas:             src.Status.CanaryStatus.CanaryReplicas,
 | 
			
		||||
			CanaryReadyReplicas:        src.Status.CanaryStatus.CanaryReadyReplicas,
 | 
			
		||||
			CurrentStepIndex:           src.Status.CanaryStatus.CurrentStepIndex,
 | 
			
		||||
			CurrentStepState:           v1beta1.CanaryStepState(src.Status.CanaryStatus.CurrentStepState),
 | 
			
		||||
			Message:                    src.Status.CanaryStatus.Message,
 | 
			
		||||
			LastUpdateTime:             src.Status.CanaryStatus.LastUpdateTime,
 | 
			
		||||
			CommonStatus: v1beta1.CommonStatus{
 | 
			
		||||
				ObservedWorkloadGeneration: src.Status.CanaryStatus.ObservedWorkloadGeneration,
 | 
			
		||||
				ObservedRolloutID:          src.Status.CanaryStatus.ObservedRolloutID,
 | 
			
		||||
				RolloutHash:                src.Status.CanaryStatus.RolloutHash,
 | 
			
		||||
				StableRevision:             src.Status.CanaryStatus.StableRevision,
 | 
			
		||||
				PodTemplateHash:            src.Status.CanaryStatus.PodTemplateHash,
 | 
			
		||||
				CurrentStepIndex:           src.Status.CanaryStatus.CurrentStepIndex,
 | 
			
		||||
				CurrentStepState:           v1beta1.CanaryStepState(src.Status.CanaryStatus.CurrentStepState),
 | 
			
		||||
				Message:                    src.Status.CanaryStatus.Message,
 | 
			
		||||
				LastUpdateTime:             src.Status.CanaryStatus.LastUpdateTime,
 | 
			
		||||
				FinalisingStep:             v1beta1.FinalisingStepType(src.Status.CanaryStatus.FinalisingStep),
 | 
			
		||||
				NextStepIndex:              src.Status.CanaryStatus.NextStepIndex,
 | 
			
		||||
			},
 | 
			
		||||
			CanaryRevision:      src.Status.CanaryStatus.CanaryRevision,
 | 
			
		||||
			CanaryReplicas:      src.Status.CanaryStatus.CanaryReplicas,
 | 
			
		||||
			CanaryReadyReplicas: src.Status.CanaryStatus.CanaryReadyReplicas,
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	default:
 | 
			
		||||
| 
						 | 
				
			
			@ -167,7 +171,9 @@ func (dst *Rollout) ConvertFrom(src conversion.Hub) error {
 | 
			
		|||
	case *v1beta1.Rollout:
 | 
			
		||||
		srcV1beta1 := src.(*v1beta1.Rollout)
 | 
			
		||||
		dst.ObjectMeta = srcV1beta1.ObjectMeta
 | 
			
		||||
 | 
			
		||||
		if !srcV1beta1.Spec.Strategy.IsCanaryStragegy() {
 | 
			
		||||
			return fmt.Errorf("v1beta1 Rollout with %s strategy cannot be converted to v1alpha1", srcV1beta1.Spec.Strategy.GetRollingStyle())
 | 
			
		||||
		}
 | 
			
		||||
		// spec
 | 
			
		||||
		dst.Spec = RolloutSpec{
 | 
			
		||||
			ObjectRef: ObjectRef{
 | 
			
		||||
| 
						 | 
				
			
			@ -254,6 +260,8 @@ func (dst *Rollout) ConvertFrom(src conversion.Hub) error {
 | 
			
		|||
			CurrentStepState:           CanaryStepState(srcV1beta1.Status.CanaryStatus.CurrentStepState),
 | 
			
		||||
			Message:                    srcV1beta1.Status.CanaryStatus.Message,
 | 
			
		||||
			LastUpdateTime:             srcV1beta1.Status.CanaryStatus.LastUpdateTime,
 | 
			
		||||
			FinalisingStep:             FinalizeStateType(srcV1beta1.Status.CanaryStatus.FinalisingStep),
 | 
			
		||||
			NextStepIndex:              srcV1beta1.Status.CanaryStatus.NextStepIndex,
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	default:
 | 
			
		||||
| 
						 | 
				
			
			@ -338,9 +346,18 @@ func (src *BatchRelease) ConvertTo(dst conversion.Hub) error {
 | 
			
		|||
				obj.Spec.ReleasePlan.PatchPodTemplateMetadata.Labels[k] = v
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !strings.EqualFold(src.Annotations[RolloutStyleAnnotation], string(PartitionRollingStyle)) {
 | 
			
		||||
			obj.Spec.ReleasePlan.EnableExtraWorkloadForCanary = true
 | 
			
		||||
 | 
			
		||||
		if strings.EqualFold(src.Annotations[RolloutStyleAnnotation], string(PartitionRollingStyle)) {
 | 
			
		||||
			obj.Spec.ReleasePlan.RollingStyle = v1beta1.PartitionRollingStyle
 | 
			
		||||
		}
 | 
			
		||||
		if strings.EqualFold(src.Annotations[RolloutStyleAnnotation], string(CanaryRollingStyle)) {
 | 
			
		||||
			obj.Spec.ReleasePlan.RollingStyle = v1beta1.CanaryRollingStyle
 | 
			
		||||
		}
 | 
			
		||||
		if strings.EqualFold(src.Annotations[RolloutStyleAnnotation], string(BlueGreenRollingStyle)) {
 | 
			
		||||
			obj.Spec.ReleasePlan.RollingStyle = v1beta1.BlueGreenRollingStyle
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		obj.Spec.ReleasePlan.EnableExtraWorkloadForCanary = srcSpec.ReleasePlan.EnableExtraWorkloadForCanary
 | 
			
		||||
 | 
			
		||||
		// status
 | 
			
		||||
		obj.Status = v1beta1.BatchReleaseStatus{
 | 
			
		||||
| 
						 | 
				
			
			@ -417,11 +434,9 @@ func (dst *BatchRelease) ConvertFrom(src conversion.Hub) error {
 | 
			
		|||
		if dst.Annotations == nil {
 | 
			
		||||
			dst.Annotations = map[string]string{}
 | 
			
		||||
		}
 | 
			
		||||
		if srcV1beta1.Spec.ReleasePlan.EnableExtraWorkloadForCanary {
 | 
			
		||||
			dst.Annotations[RolloutStyleAnnotation] = strings.ToLower(string(CanaryRollingStyle))
 | 
			
		||||
		} else {
 | 
			
		||||
			dst.Annotations[RolloutStyleAnnotation] = strings.ToLower(string(PartitionRollingStyle))
 | 
			
		||||
		}
 | 
			
		||||
		dst.Annotations[RolloutStyleAnnotation] = strings.ToLower(string(srcV1beta1.Spec.ReleasePlan.RollingStyle))
 | 
			
		||||
		dst.Spec.ReleasePlan.RollingStyle = RollingStyleType(srcV1beta1.Spec.ReleasePlan.RollingStyle)
 | 
			
		||||
		dst.Spec.ReleasePlan.EnableExtraWorkloadForCanary = srcV1beta1.Spec.ReleasePlan.EnableExtraWorkloadForCanary
 | 
			
		||||
 | 
			
		||||
		// status
 | 
			
		||||
		dst.Status = BatchReleaseStatus{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,6 +59,8 @@ const (
 | 
			
		|||
	PartitionRollingStyle RollingStyleType = "Partition"
 | 
			
		||||
	// CanaryRollingStyle means rolling in canary way, and will create a canary Deployment.
 | 
			
		||||
	CanaryRollingStyle RollingStyleType = "Canary"
 | 
			
		||||
	// BlueGreenRollingStyle means rolling in blue-green way, and will NOT create a canary Deployment.
 | 
			
		||||
	BlueGreenRollingStyle RollingStyleType = "BlueGreen"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DeploymentExtraStatus is extra status field for Advanced Deployment
 | 
			
		||||
| 
						 | 
				
			
			@ -74,7 +76,7 @@ type DeploymentExtraStatus struct {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func SetDefaultDeploymentStrategy(strategy *DeploymentStrategy) {
 | 
			
		||||
	if strategy.RollingStyle == CanaryRollingStyle {
 | 
			
		||||
	if strategy.RollingStyle != PartitionRollingStyle {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if strategy.RollingUpdate == nil {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -242,16 +242,28 @@ type CanaryStatus struct {
 | 
			
		|||
	CanaryReplicas int32 `json:"canaryReplicas"`
 | 
			
		||||
	// CanaryReadyReplicas the numbers of ready canary revision pods
 | 
			
		||||
	CanaryReadyReplicas int32 `json:"canaryReadyReplicas"`
 | 
			
		||||
	// CurrentStepIndex defines the current step of the rollout is on. If the current step index is null, the
 | 
			
		||||
	// controller will execute the rollout.
 | 
			
		||||
	// NextStepIndex defines the next step of the rollout is on.
 | 
			
		||||
	// In normal case, NextStepIndex is equal to CurrentStepIndex + 1
 | 
			
		||||
	// If the current step is the last step, NextStepIndex is equal to -1
 | 
			
		||||
	// Before the release, NextStepIndex is also equal to -1
 | 
			
		||||
	// 0 is not used and won't appear in any case
 | 
			
		||||
	// It is allowed to patch NextStepIndex by design,
 | 
			
		||||
	// e.g. if CurrentStepIndex is 2, user can patch NextStepIndex to 3 (if exists) to
 | 
			
		||||
	// achieve batch jump, or patch NextStepIndex to 1 to implement a re-execution of step 1
 | 
			
		||||
	// Patching it with a non-positive value is meaningless, which will be corrected
 | 
			
		||||
	// in the next reconciliation
 | 
			
		||||
	// achieve batch jump, or patch NextStepIndex to 1 to implement a re-execution of step 1
 | 
			
		||||
	NextStepIndex int32 `json:"nextStepIndex"`
 | 
			
		||||
	// +optional
 | 
			
		||||
	CurrentStepIndex int32           `json:"currentStepIndex"`
 | 
			
		||||
	CurrentStepState CanaryStepState `json:"currentStepState"`
 | 
			
		||||
	Message          string          `json:"message,omitempty"`
 | 
			
		||||
	LastUpdateTime   *metav1.Time    `json:"lastUpdateTime,omitempty"`
 | 
			
		||||
	CurrentStepIndex int32             `json:"currentStepIndex"`
 | 
			
		||||
	CurrentStepState CanaryStepState   `json:"currentStepState"`
 | 
			
		||||
	Message          string            `json:"message,omitempty"`
 | 
			
		||||
	LastUpdateTime   *metav1.Time      `json:"lastUpdateTime,omitempty"`
 | 
			
		||||
	FinalisingStep   FinalizeStateType `json:"finalisingStep"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type CanaryStepState string
 | 
			
		||||
type FinalizeStateType string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	CanaryStepStateUpgrade         CanaryStepState = "StepUpgrade"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,9 +54,15 @@ type ReleasePlan struct {
 | 
			
		|||
	// only support for canary deployment
 | 
			
		||||
	// +optional
 | 
			
		||||
	PatchPodTemplateMetadata *PatchPodTemplateMetadata `json:"patchPodTemplateMetadata,omitempty"`
 | 
			
		||||
	// If true, then it will create new deployment for canary, such as: workload-demo-canary.
 | 
			
		||||
	// When user verifies that the canary version is ready, we will remove the canary deployment and release the deployment workload-demo in full.
 | 
			
		||||
	// Current only support k8s native deployment
 | 
			
		||||
	// RollingStyle can be "Canary", "Partiton" or "BlueGreen"
 | 
			
		||||
	RollingStyle RollingStyleType `json:"rollingStyle,omitempty"`
 | 
			
		||||
	// EnableExtraWorkloadForCanary indicates whether to create extra workload for canary
 | 
			
		||||
	// True corresponds to RollingStyle "Canary".
 | 
			
		||||
	// False corresponds to RollingStyle "Partiton".
 | 
			
		||||
	// Ignored in BlueGreen-style.
 | 
			
		||||
	// This field is about to deprecate, use RollingStyle instead.
 | 
			
		||||
	// If both of them are set, controller will only consider this
 | 
			
		||||
	// filed when RollingStyle is empty
 | 
			
		||||
	EnableExtraWorkloadForCanary bool `json:"enableExtraWorkloadForCanary"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,16 @@ const (
 | 
			
		|||
	// AdvancedDeploymentControlLabel is label for deployment,
 | 
			
		||||
	// which labels whether the deployment is controlled by advanced-deployment-controller.
 | 
			
		||||
	AdvancedDeploymentControlLabel = "rollouts.kruise.io/controlled-by-advanced-deployment-controller"
 | 
			
		||||
 | 
			
		||||
	// OriginalDeploymentStrategyAnnotation is annotation for workload in BlueGreen Release,
 | 
			
		||||
	// it will store the original setting of the workload, which will be used to restore the workload
 | 
			
		||||
	OriginalDeploymentStrategyAnnotation = "rollouts.kruise.io/original-deployment-strategy"
 | 
			
		||||
 | 
			
		||||
	// MaxProgressSeconds is the value we set for ProgressDeadlineSeconds
 | 
			
		||||
	// MaxReadySeconds is the value we set for MinReadySeconds, which is one less than ProgressDeadlineSeconds
 | 
			
		||||
	// MaxInt32: 2147483647, ≈ 68 years
 | 
			
		||||
	MaxProgressSeconds = 1<<31 - 1
 | 
			
		||||
	MaxReadySeconds    = MaxProgressSeconds - 1
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DeploymentStrategy is strategy field for Advanced Deployment
 | 
			
		||||
| 
						 | 
				
			
			@ -52,6 +62,31 @@ type DeploymentStrategy struct {
 | 
			
		|||
	Partition intstr.IntOrString `json:"partition,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OriginalDeploymentStrategy stores part of the fileds of a workload,
 | 
			
		||||
// so that it can be restored when finalizing.
 | 
			
		||||
// It is only used for BlueGreen Release
 | 
			
		||||
// Similar to DeploymentStrategy, it is an annotation used in workload
 | 
			
		||||
// However, unlike DeploymentStrategy, it is only used to store and restore the user's strategy
 | 
			
		||||
type OriginalDeploymentStrategy struct {
 | 
			
		||||
	// The deployment strategy to use to replace existing pods with new ones.
 | 
			
		||||
	// +optional
 | 
			
		||||
	// +patchStrategy=retainKeys
 | 
			
		||||
	Strategy *apps.DeploymentStrategy `json:"strategy,omitempty" patchStrategy:"retainKeys" protobuf:"bytes,4,opt,name=strategy"`
 | 
			
		||||
 | 
			
		||||
	// Minimum number of seconds for which a newly created pod should be ready
 | 
			
		||||
	// without any of its container crashing, for it to be considered available.
 | 
			
		||||
	// Defaults to 0 (pod will be considered available as soon as it is ready)
 | 
			
		||||
	// +optional
 | 
			
		||||
	MinReadySeconds int32 `json:"minReadySeconds,omitempty" protobuf:"varint,5,opt,name=minReadySeconds"`
 | 
			
		||||
 | 
			
		||||
	// The maximum time in seconds for a deployment to make progress before it
 | 
			
		||||
	// is considered to be failed. The deployment controller will continue to
 | 
			
		||||
	// process failed deployments and a condition with a ProgressDeadlineExceeded
 | 
			
		||||
	// reason will be surfaced in the deployment status. Note that progress will
 | 
			
		||||
	// not be estimated during the time a deployment is paused. Defaults to 600s.
 | 
			
		||||
	ProgressDeadlineSeconds *int32 `json:"progressDeadlineSeconds,omitempty" protobuf:"varint,9,opt,name=progressDeadlineSeconds"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RollingStyleType string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
| 
						 | 
				
			
			@ -59,6 +94,8 @@ const (
 | 
			
		|||
	PartitionRollingStyle RollingStyleType = "Partition"
 | 
			
		||||
	// CanaryRollingStyle means rolling in canary way, and will create a canary Deployment.
 | 
			
		||||
	CanaryRollingStyle RollingStyleType = "Canary"
 | 
			
		||||
	// BlueGreenRollingStyle means rolling in blue-green way, and will NOT create a extra Deployment.
 | 
			
		||||
	BlueGreenRollingStyle RollingStyleType = "BlueGreen"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DeploymentExtraStatus is extra status field for Advanced Deployment
 | 
			
		||||
| 
						 | 
				
			
			@ -74,7 +111,7 @@ type DeploymentExtraStatus struct {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func SetDefaultDeploymentStrategy(strategy *DeploymentStrategy) {
 | 
			
		||||
	if strategy.RollingStyle == CanaryRollingStyle {
 | 
			
		||||
	if strategy.RollingStyle != PartitionRollingStyle {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if strategy.RollingUpdate == nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -101,3 +138,44 @@ func SetDefaultDeploymentStrategy(strategy *DeploymentStrategy) {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SetDefaultSetting(setting *OriginalDeploymentStrategy) {
 | 
			
		||||
	if setting.ProgressDeadlineSeconds == nil {
 | 
			
		||||
		setting.ProgressDeadlineSeconds = new(int32)
 | 
			
		||||
		*setting.ProgressDeadlineSeconds = 600
 | 
			
		||||
	}
 | 
			
		||||
	if setting.Strategy == nil {
 | 
			
		||||
		setting.Strategy = &apps.DeploymentStrategy{}
 | 
			
		||||
	}
 | 
			
		||||
	if setting.Strategy.Type == "" {
 | 
			
		||||
		setting.Strategy.Type = apps.RollingUpdateDeploymentStrategyType
 | 
			
		||||
	}
 | 
			
		||||
	if setting.Strategy.Type == apps.RecreateDeploymentStrategyType {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	strategy := setting.Strategy
 | 
			
		||||
	if strategy.RollingUpdate == nil {
 | 
			
		||||
		strategy.RollingUpdate = &apps.RollingUpdateDeployment{}
 | 
			
		||||
	}
 | 
			
		||||
	if strategy.RollingUpdate.MaxUnavailable == nil {
 | 
			
		||||
		// Set MaxUnavailable as 25% by default
 | 
			
		||||
		maxUnavailable := intstr.FromString("25%")
 | 
			
		||||
		strategy.RollingUpdate.MaxUnavailable = &maxUnavailable
 | 
			
		||||
	}
 | 
			
		||||
	if strategy.RollingUpdate.MaxSurge == nil {
 | 
			
		||||
		// Set MaxSurge as 25% by default
 | 
			
		||||
		maxSurge := intstr.FromString("25%")
 | 
			
		||||
		strategy.RollingUpdate.MaxUnavailable = &maxSurge
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Cannot allow maxSurge==0 && MaxUnavailable==0, otherwise, no pod can be updated when rolling update.
 | 
			
		||||
	maxSurge, _ := intstr.GetScaledValueFromIntOrPercent(strategy.RollingUpdate.MaxSurge, 100, true)
 | 
			
		||||
	maxUnavailable, _ := intstr.GetScaledValueFromIntOrPercent(strategy.RollingUpdate.MaxUnavailable, 100, true)
 | 
			
		||||
	if maxSurge == 0 && maxUnavailable == 0 {
 | 
			
		||||
		strategy.RollingUpdate = &apps.RollingUpdateDeployment{
 | 
			
		||||
			MaxSurge:       &intstr.IntOrString{Type: intstr.Int, IntVal: 0},
 | 
			
		||||
			MaxUnavailable: &intstr.IntOrString{Type: intstr.Int, IntVal: 1},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -75,6 +75,92 @@ type RolloutStrategy struct {
 | 
			
		|||
	Paused bool `json:"paused,omitempty"`
 | 
			
		||||
	// +optional
 | 
			
		||||
	Canary *CanaryStrategy `json:"canary,omitempty"`
 | 
			
		||||
	// +optional
 | 
			
		||||
	BlueGreen *BlueGreenStrategy `json:"blueGreen,omitempty" protobuf:"bytes,1,opt,name=blueGreen"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get the rolling style based on the strategy
 | 
			
		||||
func (r *RolloutStrategy) GetRollingStyle() RollingStyleType {
 | 
			
		||||
	if r.BlueGreen != nil {
 | 
			
		||||
		return BlueGreenRollingStyle
 | 
			
		||||
	}
 | 
			
		||||
	//NOTE - even EnableExtraWorkloadForCanary is true, as long as it is not Deployment,
 | 
			
		||||
	//we won't do canary release. BatchRelease will treat it as Partiton release
 | 
			
		||||
	if r.Canary.EnableExtraWorkloadForCanary {
 | 
			
		||||
		return CanaryRollingStyle
 | 
			
		||||
	}
 | 
			
		||||
	return PartitionRollingStyle
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// r.GetRollingStyle() == BlueGreenRollingStyle
 | 
			
		||||
func (r *RolloutStrategy) IsBlueGreenRelease() bool {
 | 
			
		||||
	return r.GetRollingStyle() == BlueGreenRollingStyle
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// r.GetRollingStyle() == CanaryRollingStyle || r.GetRollingStyle() == PartitionRollingStyle
 | 
			
		||||
func (r *RolloutStrategy) IsCanaryStragegy() bool {
 | 
			
		||||
	return r.GetRollingStyle() == CanaryRollingStyle || r.GetRollingStyle() == PartitionRollingStyle
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get the steps based on the rolling style
 | 
			
		||||
func (r *RolloutStrategy) GetSteps() []CanaryStep {
 | 
			
		||||
	switch r.GetRollingStyle() {
 | 
			
		||||
	case BlueGreenRollingStyle:
 | 
			
		||||
		return r.BlueGreen.Steps
 | 
			
		||||
	case CanaryRollingStyle, PartitionRollingStyle:
 | 
			
		||||
		return r.Canary.Steps
 | 
			
		||||
	default:
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get the traffic routing based on the rolling style
 | 
			
		||||
func (r *RolloutStrategy) GetTrafficRouting() []TrafficRoutingRef {
 | 
			
		||||
	switch r.GetRollingStyle() {
 | 
			
		||||
	case BlueGreenRollingStyle:
 | 
			
		||||
		return r.BlueGreen.TrafficRoutings
 | 
			
		||||
	case CanaryRollingStyle, PartitionRollingStyle:
 | 
			
		||||
		return r.Canary.TrafficRoutings
 | 
			
		||||
	default:
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Check if there are traffic routings
 | 
			
		||||
func (r *RolloutStrategy) HasTrafficRoutings() bool {
 | 
			
		||||
	return len(r.GetTrafficRouting()) > 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Check the value of DisableGenerateCanaryService
 | 
			
		||||
func (r *RolloutStrategy) DisableGenerateCanaryService() bool {
 | 
			
		||||
	switch r.GetRollingStyle() {
 | 
			
		||||
	case BlueGreenRollingStyle:
 | 
			
		||||
		return r.BlueGreen.DisableGenerateCanaryService
 | 
			
		||||
	case CanaryRollingStyle, PartitionRollingStyle:
 | 
			
		||||
		return r.Canary.DisableGenerateCanaryService
 | 
			
		||||
	default:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BlueGreenStrategy defines parameters for Blue Green Release
 | 
			
		||||
type BlueGreenStrategy struct {
 | 
			
		||||
	// Steps define the order of phases to execute release in batches(20%, 40%, 60%, 80%, 100%)
 | 
			
		||||
	// +optional
 | 
			
		||||
	Steps []CanaryStep `json:"steps,omitempty"`
 | 
			
		||||
	// TrafficRoutings support ingress, gateway api and custom network resource(e.g. istio, apisix) to enable more fine-grained traffic routing
 | 
			
		||||
	// and current only support one TrafficRouting
 | 
			
		||||
	TrafficRoutings []TrafficRoutingRef `json:"trafficRoutings,omitempty"`
 | 
			
		||||
	// FailureThreshold indicates how many failed pods can be tolerated in all upgraded pods.
 | 
			
		||||
	// Only when FailureThreshold are satisfied, Rollout can enter ready state.
 | 
			
		||||
	// If FailureThreshold is nil, Rollout will use the MaxUnavailable of workload as its
 | 
			
		||||
	// FailureThreshold.
 | 
			
		||||
	// Defaults to nil.
 | 
			
		||||
	FailureThreshold *intstr.IntOrString `json:"failureThreshold,omitempty"`
 | 
			
		||||
	// TrafficRoutingRef is TrafficRouting's Name
 | 
			
		||||
	TrafficRoutingRef string `json:"trafficRoutingRef,omitempty"`
 | 
			
		||||
	// canary service will not be generated if DisableGenerateCanaryService is true
 | 
			
		||||
	DisableGenerateCanaryService bool `json:"disableGenerateCanaryService,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CanaryStrategy defines parameters for a Replica Based Canary
 | 
			
		||||
| 
						 | 
				
			
			@ -206,6 +292,9 @@ type RolloutStatus struct {
 | 
			
		|||
	// Canary describes the state of the canary rollout
 | 
			
		||||
	// +optional
 | 
			
		||||
	CanaryStatus *CanaryStatus `json:"canaryStatus,omitempty"`
 | 
			
		||||
	// BlueGreen describes the state of the blueGreen rollout
 | 
			
		||||
	// +optional
 | 
			
		||||
	BlueGreenStatus *BlueGreenStatus `json:"blueGreenStatus,omitempty"`
 | 
			
		||||
	// Conditions a list of conditions a rollout can have.
 | 
			
		||||
	// +optional
 | 
			
		||||
	Conditions []RolloutCondition `json:"conditions,omitempty"`
 | 
			
		||||
| 
						 | 
				
			
			@ -259,10 +348,26 @@ const (
 | 
			
		|||
	// Terminating Reason
 | 
			
		||||
	TerminatingReasonInTerminating = "InTerminating"
 | 
			
		||||
	TerminatingReasonCompleted     = "Completed"
 | 
			
		||||
 | 
			
		||||
	// Finalise Reason
 | 
			
		||||
	// Finalise when the last batch is released and all pods will update to new version
 | 
			
		||||
	FinaliseReasonSuccess = "Success"
 | 
			
		||||
	// Finalise when rollback detected
 | 
			
		||||
	FinaliseReasonRollback = "Rollback"
 | 
			
		||||
	// Finalise when Continuous Release detected
 | 
			
		||||
	FinaliseReasonContinuous = "Continuous"
 | 
			
		||||
	// Finalise when Rollout is disabling
 | 
			
		||||
	FinaliseReasonDisalbed = "RolloutDisabled"
 | 
			
		||||
	// Finalise when Rollout is deleting
 | 
			
		||||
	FinaliseReasonDelete = "RolloutDeleting"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CanaryStatus status fields that only pertain to the canary rollout
 | 
			
		||||
type CanaryStatus struct {
 | 
			
		||||
// fields in CommonStatus are shared between canary status and bluegreen status
 | 
			
		||||
// if a field is accessed in strategy-agnostic way, e.g. accessed from rollout_progressing.go, or rollout_status.go
 | 
			
		||||
// then it can be put into CommonStatus
 | 
			
		||||
// if a field is only accessed in strategy-specific way, e.g. accessed from rollout_canary.go or rollout_bluegreen.go
 | 
			
		||||
// then it should stay behind with CanaryStatus or BlueGreenStatus
 | 
			
		||||
type CommonStatus struct {
 | 
			
		||||
	// observedWorkloadGeneration is the most recent generation observed for this Rollout ref workload generation.
 | 
			
		||||
	ObservedWorkloadGeneration int64 `json:"observedWorkloadGeneration,omitempty"`
 | 
			
		||||
	// ObservedRolloutID will record the newest spec.RolloutID if status.canaryRevision equals to workload.updateRevision
 | 
			
		||||
| 
						 | 
				
			
			@ -271,27 +376,126 @@ type CanaryStatus struct {
 | 
			
		|||
	RolloutHash string `json:"rolloutHash,omitempty"`
 | 
			
		||||
	// StableRevision indicates the revision of stable pods
 | 
			
		||||
	StableRevision string `json:"stableRevision,omitempty"`
 | 
			
		||||
	// pod template hash is used as service selector label
 | 
			
		||||
	PodTemplateHash string `json:"podTemplateHash"`
 | 
			
		||||
	// CurrentStepIndex defines the current step of the rollout is on.
 | 
			
		||||
	// +optional
 | 
			
		||||
	CurrentStepIndex int32 `json:"currentStepIndex"`
 | 
			
		||||
	// NextStepIndex defines the next step of the rollout is on.
 | 
			
		||||
	// In normal case, NextStepIndex is equal to CurrentStepIndex + 1
 | 
			
		||||
	// If the current step is the last step, NextStepIndex is equal to -1
 | 
			
		||||
	// Before the release, NextStepIndex is also equal to -1
 | 
			
		||||
	// 0 is not used and won't appear in any case
 | 
			
		||||
	// It is allowed to patch NextStepIndex by design,
 | 
			
		||||
	// e.g. if CurrentStepIndex is 2, user can patch NextStepIndex to 3 (if exists) to
 | 
			
		||||
	// achieve batch jump, or patch NextStepIndex to 1 to implement a re-execution of step 1
 | 
			
		||||
	// Patching it with a non-positive value is useless and meaningless, which will be corrected
 | 
			
		||||
	// in the next reconciliation
 | 
			
		||||
	NextStepIndex int32 `json:"nextStepIndex"`
 | 
			
		||||
	// FinalisingStep the step of finalising
 | 
			
		||||
	FinalisingStep   FinalisingStepType `json:"finalisingStep"`
 | 
			
		||||
	CurrentStepState CanaryStepState    `json:"currentStepState"`
 | 
			
		||||
	Message          string             `json:"message,omitempty"`
 | 
			
		||||
	LastUpdateTime   *metav1.Time       `json:"lastUpdateTime,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CanaryStatus status fields that only pertain to the canary rollout
 | 
			
		||||
type CanaryStatus struct {
 | 
			
		||||
	// must be inline
 | 
			
		||||
	CommonStatus `json:",inline"`
 | 
			
		||||
	// CanaryRevision is calculated by rollout based on podTemplateHash, and the internal logic flow uses
 | 
			
		||||
	// It may be different from rs podTemplateHash in different k8s versions, so it cannot be used as service selector label
 | 
			
		||||
	CanaryRevision string `json:"canaryRevision"`
 | 
			
		||||
	// pod template hash is used as service selector label
 | 
			
		||||
	PodTemplateHash string `json:"podTemplateHash"`
 | 
			
		||||
	// CanaryReplicas the numbers of canary revision pods
 | 
			
		||||
	CanaryReplicas int32 `json:"canaryReplicas"`
 | 
			
		||||
	// CanaryReadyReplicas the numbers of ready canary revision pods
 | 
			
		||||
	CanaryReadyReplicas int32 `json:"canaryReadyReplicas"`
 | 
			
		||||
	// CurrentStepIndex defines the current step of the rollout is on. If the current step index is null, the
 | 
			
		||||
	// controller will execute the rollout.
 | 
			
		||||
	// +optional
 | 
			
		||||
	CurrentStepIndex int32           `json:"currentStepIndex"`
 | 
			
		||||
	CurrentStepState CanaryStepState `json:"currentStepState"`
 | 
			
		||||
	Message          string          `json:"message,omitempty"`
 | 
			
		||||
	LastUpdateTime   *metav1.Time    `json:"lastUpdateTime,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BlueGreenStatus status fields that only pertain to the blueGreen rollout
 | 
			
		||||
type BlueGreenStatus struct {
 | 
			
		||||
	CommonStatus `json:",inline"`
 | 
			
		||||
	// CanaryRevision is calculated by rollout based on podTemplateHash, and the internal logic flow uses
 | 
			
		||||
	// It may be different from rs podTemplateHash in different k8s versions, so it cannot be used as service selector label
 | 
			
		||||
	UpdatedRevision string `json:"updatedRevision"`
 | 
			
		||||
	// UpdatedReplicas the numbers of updated pods
 | 
			
		||||
	UpdatedReplicas int32 `json:"updatedReplicas"`
 | 
			
		||||
	// UpdatedReadyReplicas the numbers of updated ready pods
 | 
			
		||||
	UpdatedReadyReplicas int32 `json:"updatedReadyReplicas"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSubStatus returns the ethier canary or bluegreen status
 | 
			
		||||
func (r *RolloutStatus) GetSubStatus() *CommonStatus {
 | 
			
		||||
	if r.CanaryStatus != nil {
 | 
			
		||||
		return &(r.CanaryStatus.CommonStatus)
 | 
			
		||||
	}
 | 
			
		||||
	return &(r.BlueGreenStatus.CommonStatus)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *RolloutStatus) IsSubStatusEmpty() bool {
 | 
			
		||||
	return r.CanaryStatus == nil && r.BlueGreenStatus == nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *RolloutStatus) Clear() {
 | 
			
		||||
	r.CanaryStatus = nil
 | 
			
		||||
	r.BlueGreenStatus = nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//TODO - the following functions seem awkward, is there better way for our case?
 | 
			
		||||
 | 
			
		||||
func (r *RolloutStatus) GetCanaryRevision() string {
 | 
			
		||||
	if r.CanaryStatus != nil {
 | 
			
		||||
		return r.CanaryStatus.CanaryRevision
 | 
			
		||||
	}
 | 
			
		||||
	return r.BlueGreenStatus.UpdatedRevision
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *RolloutStatus) SetCanaryRevision(revision string) {
 | 
			
		||||
	if r.CanaryStatus != nil {
 | 
			
		||||
		r.CanaryStatus.CanaryRevision = revision
 | 
			
		||||
	}
 | 
			
		||||
	if r.BlueGreenStatus != nil {
 | 
			
		||||
		r.BlueGreenStatus.UpdatedRevision = revision
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *RolloutStatus) GetCanaryReplicas() int32 {
 | 
			
		||||
	if r.CanaryStatus != nil {
 | 
			
		||||
		return r.CanaryStatus.CanaryReplicas
 | 
			
		||||
	}
 | 
			
		||||
	return r.BlueGreenStatus.UpdatedReplicas
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *RolloutStatus) SetCanaryReplicas(replicas int32) {
 | 
			
		||||
	if r.CanaryStatus != nil {
 | 
			
		||||
		r.CanaryStatus.CanaryReplicas = replicas
 | 
			
		||||
	}
 | 
			
		||||
	if r.BlueGreenStatus != nil {
 | 
			
		||||
		r.BlueGreenStatus.UpdatedReplicas = replicas
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *RolloutStatus) GetCanaryReadyReplicas() int32 {
 | 
			
		||||
	if r.CanaryStatus != nil {
 | 
			
		||||
		return r.CanaryStatus.CanaryReadyReplicas
 | 
			
		||||
	}
 | 
			
		||||
	return r.BlueGreenStatus.UpdatedReadyReplicas
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *RolloutStatus) SetCanaryReadyReplicas(replicas int32) {
 | 
			
		||||
	if r.CanaryStatus != nil {
 | 
			
		||||
		r.CanaryStatus.CanaryReadyReplicas = replicas
 | 
			
		||||
	}
 | 
			
		||||
	if r.BlueGreenStatus != nil {
 | 
			
		||||
		r.BlueGreenStatus.UpdatedReadyReplicas = replicas
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type CanaryStepState string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// the first step, handle some special cases before step upgrade, to prevent traffic loss
 | 
			
		||||
	CanaryStepStateInit            CanaryStepState = "BeforeStepUpgrade"
 | 
			
		||||
	CanaryStepStateUpgrade         CanaryStepState = "StepUpgrade"
 | 
			
		||||
	CanaryStepStateTrafficRouting  CanaryStepState = "StepTrafficRouting"
 | 
			
		||||
	CanaryStepStateMetricsAnalysis CanaryStepState = "StepMetricsAnalysis"
 | 
			
		||||
| 
						 | 
				
			
			@ -318,6 +522,34 @@ const (
 | 
			
		|||
	RolloutPhaseDisabling RolloutPhase = "Disabling"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type FinalisingStepType string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// some work that should be done before pod scaling down.
 | 
			
		||||
	// For BlueGreenStrategy:
 | 
			
		||||
	// we rout all traffic to stable or new version based on FinaliseReason
 | 
			
		||||
	// For CanaryStrategy:
 | 
			
		||||
	// we remove the selector of stable service
 | 
			
		||||
	FinalisingStepTypePreparing FinalisingStepType = "Preparing"
 | 
			
		||||
	// Patch Batch Release to scale down (exception: the canary Deployment will be
 | 
			
		||||
	// scaled down in FinalisingStepTypeDeleteBR step)
 | 
			
		||||
	// For Both BlueGreenStrategy and CanaryStrategy:
 | 
			
		||||
	// set workload.pause=false, set workload.partition=0
 | 
			
		||||
	FinalisingStepTypeBatchRelease FinalisingStepType = "PatchBatchRelease"
 | 
			
		||||
	//TODO - Currently, the next three steps are in the same function, FinalisingTrafficRouting
 | 
			
		||||
	// we should try to separate the FinalisingStepTypeGateway and FinalisingStepTypeCanaryService
 | 
			
		||||
	// with graceful time to prevent some potential issues
 | 
			
		||||
 | 
			
		||||
	// Restore the stable Service (i.e. remove corresponding selector)
 | 
			
		||||
	FinalisingStepTypeStableService FinalisingStepType = "RestoreStableService"
 | 
			
		||||
	// Restore the GatewayAPI/Ingress/Istio
 | 
			
		||||
	FinalisingStepTypeGateway FinalisingStepType = "RestoreGateway"
 | 
			
		||||
	// Delete Canary Service
 | 
			
		||||
	FinalisingStepTypeCanaryService FinalisingStepType = "DeleteCanayService"
 | 
			
		||||
	// Delete Batch Release
 | 
			
		||||
	FinalisingStepTypeDeleteBR FinalisingStepType = "DeleteBatchRelease"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// +genclient
 | 
			
		||||
//+kubebuilder:object:root=true
 | 
			
		||||
//+kubebuilder:subresource:status
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -156,13 +156,60 @@ func (in *BatchReleaseStatus) DeepCopy() *BatchReleaseStatus {
 | 
			
		|||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
			
		||||
func (in *BlueGreenStatus) DeepCopyInto(out *BlueGreenStatus) {
 | 
			
		||||
	*out = *in
 | 
			
		||||
	in.CommonStatus.DeepCopyInto(&out.CommonStatus)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BlueGreenStatus.
 | 
			
		||||
func (in *BlueGreenStatus) DeepCopy() *BlueGreenStatus {
 | 
			
		||||
	if in == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	out := new(BlueGreenStatus)
 | 
			
		||||
	in.DeepCopyInto(out)
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
			
		||||
func (in *BlueGreenStrategy) DeepCopyInto(out *BlueGreenStrategy) {
 | 
			
		||||
	*out = *in
 | 
			
		||||
	if in.Steps != nil {
 | 
			
		||||
		in, out := &in.Steps, &out.Steps
 | 
			
		||||
		*out = make([]CanaryStep, len(*in))
 | 
			
		||||
		for i := range *in {
 | 
			
		||||
			(*in)[i].DeepCopyInto(&(*out)[i])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if in.TrafficRoutings != nil {
 | 
			
		||||
		in, out := &in.TrafficRoutings, &out.TrafficRoutings
 | 
			
		||||
		*out = make([]TrafficRoutingRef, len(*in))
 | 
			
		||||
		for i := range *in {
 | 
			
		||||
			(*in)[i].DeepCopyInto(&(*out)[i])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if in.FailureThreshold != nil {
 | 
			
		||||
		in, out := &in.FailureThreshold, &out.FailureThreshold
 | 
			
		||||
		*out = new(intstr.IntOrString)
 | 
			
		||||
		**out = **in
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BlueGreenStrategy.
 | 
			
		||||
func (in *BlueGreenStrategy) DeepCopy() *BlueGreenStrategy {
 | 
			
		||||
	if in == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	out := new(BlueGreenStrategy)
 | 
			
		||||
	in.DeepCopyInto(out)
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
			
		||||
func (in *CanaryStatus) DeepCopyInto(out *CanaryStatus) {
 | 
			
		||||
	*out = *in
 | 
			
		||||
	if in.LastUpdateTime != nil {
 | 
			
		||||
		in, out := &in.LastUpdateTime, &out.LastUpdateTime
 | 
			
		||||
		*out = (*in).DeepCopy()
 | 
			
		||||
	}
 | 
			
		||||
	in.CommonStatus.DeepCopyInto(&out.CommonStatus)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CanaryStatus.
 | 
			
		||||
| 
						 | 
				
			
			@ -236,6 +283,25 @@ func (in *CanaryStrategy) DeepCopy() *CanaryStrategy {
 | 
			
		|||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
			
		||||
func (in *CommonStatus) DeepCopyInto(out *CommonStatus) {
 | 
			
		||||
	*out = *in
 | 
			
		||||
	if in.LastUpdateTime != nil {
 | 
			
		||||
		in, out := &in.LastUpdateTime, &out.LastUpdateTime
 | 
			
		||||
		*out = (*in).DeepCopy()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonStatus.
 | 
			
		||||
func (in *CommonStatus) DeepCopy() *CommonStatus {
 | 
			
		||||
	if in == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	out := new(CommonStatus)
 | 
			
		||||
	in.DeepCopyInto(out)
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
			
		||||
func (in *DeploymentExtraStatus) DeepCopyInto(out *DeploymentExtraStatus) {
 | 
			
		||||
	*out = *in
 | 
			
		||||
| 
						 | 
				
			
			@ -356,6 +422,31 @@ func (in *ObjectRef) DeepCopy() *ObjectRef {
 | 
			
		|||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
			
		||||
func (in *OriginalDeploymentStrategy) DeepCopyInto(out *OriginalDeploymentStrategy) {
 | 
			
		||||
	*out = *in
 | 
			
		||||
	if in.Strategy != nil {
 | 
			
		||||
		in, out := &in.Strategy, &out.Strategy
 | 
			
		||||
		*out = new(v1.DeploymentStrategy)
 | 
			
		||||
		(*in).DeepCopyInto(*out)
 | 
			
		||||
	}
 | 
			
		||||
	if in.ProgressDeadlineSeconds != nil {
 | 
			
		||||
		in, out := &in.ProgressDeadlineSeconds, &out.ProgressDeadlineSeconds
 | 
			
		||||
		*out = new(int32)
 | 
			
		||||
		**out = **in
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OriginalDeploymentStrategy.
 | 
			
		||||
func (in *OriginalDeploymentStrategy) DeepCopy() *OriginalDeploymentStrategy {
 | 
			
		||||
	if in == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	out := new(OriginalDeploymentStrategy)
 | 
			
		||||
	in.DeepCopyInto(out)
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
			
		||||
func (in *PatchPodTemplateMetadata) DeepCopyInto(out *PatchPodTemplateMetadata) {
 | 
			
		||||
	*out = *in
 | 
			
		||||
| 
						 | 
				
			
			@ -557,6 +648,11 @@ func (in *RolloutStatus) DeepCopyInto(out *RolloutStatus) {
 | 
			
		|||
		*out = new(CanaryStatus)
 | 
			
		||||
		(*in).DeepCopyInto(*out)
 | 
			
		||||
	}
 | 
			
		||||
	if in.BlueGreenStatus != nil {
 | 
			
		||||
		in, out := &in.BlueGreenStatus, &out.BlueGreenStatus
 | 
			
		||||
		*out = new(BlueGreenStatus)
 | 
			
		||||
		(*in).DeepCopyInto(*out)
 | 
			
		||||
	}
 | 
			
		||||
	if in.Conditions != nil {
 | 
			
		||||
		in, out := &in.Conditions, &out.Conditions
 | 
			
		||||
		*out = make([]RolloutCondition, len(*in))
 | 
			
		||||
| 
						 | 
				
			
			@ -584,6 +680,11 @@ func (in *RolloutStrategy) DeepCopyInto(out *RolloutStrategy) {
 | 
			
		|||
		*out = new(CanaryStrategy)
 | 
			
		||||
		(*in).DeepCopyInto(*out)
 | 
			
		||||
	}
 | 
			
		||||
	if in.BlueGreen != nil {
 | 
			
		||||
		in, out := &in.BlueGreen, &out.BlueGreen
 | 
			
		||||
		*out = new(BlueGreenStrategy)
 | 
			
		||||
		(*in).DeepCopyInto(*out)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RolloutStrategy.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -88,6 +88,14 @@ spec:
 | 
			
		|||
                      - canaryReplicas
 | 
			
		||||
                      type: object
 | 
			
		||||
                    type: array
 | 
			
		||||
                  enableExtraWorkloadForCanary:
 | 
			
		||||
                    description: EnableExtraWorkloadForCanary indicates whether to
 | 
			
		||||
                      create extra workload for canary True corresponds to RollingStyle
 | 
			
		||||
                      "Canary". False corresponds to RollingStyle "Partiton". Ignored
 | 
			
		||||
                      in BlueGreen-style. This field is about to deprecate, use RollingStyle
 | 
			
		||||
                      instead. If both of them are set, controller will only consider
 | 
			
		||||
                      this filed when RollingStyle is empty
 | 
			
		||||
                    type: boolean
 | 
			
		||||
                  failureThreshold:
 | 
			
		||||
                    anyOf:
 | 
			
		||||
                    - type: integer
 | 
			
		||||
| 
						 | 
				
			
			@ -118,9 +126,14 @@ spec:
 | 
			
		|||
                        description: labels
 | 
			
		||||
                        type: object
 | 
			
		||||
                    type: object
 | 
			
		||||
                  rollingStyle:
 | 
			
		||||
                    description: RollingStyle can be "Canary", "Partiton" or "BlueGreen"
 | 
			
		||||
                    type: string
 | 
			
		||||
                  rolloutID:
 | 
			
		||||
                    description: RolloutID indicates an id for each rollout progress
 | 
			
		||||
                    type: string
 | 
			
		||||
                required:
 | 
			
		||||
                - enableExtraWorkloadForCanary
 | 
			
		||||
                type: object
 | 
			
		||||
              targetReference:
 | 
			
		||||
                description: TargetRef contains the GVK and name of the workload that
 | 
			
		||||
| 
						 | 
				
			
			@ -346,11 +359,12 @@ spec:
 | 
			
		|||
                      type: object
 | 
			
		||||
                    type: array
 | 
			
		||||
                  enableExtraWorkloadForCanary:
 | 
			
		||||
                    description: 'If true, then it will create new deployment for
 | 
			
		||||
                      canary, such as: workload-demo-canary. When user verifies that
 | 
			
		||||
                      the canary version is ready, we will remove the canary deployment
 | 
			
		||||
                      and release the deployment workload-demo in full. Current only
 | 
			
		||||
                      support k8s native deployment'
 | 
			
		||||
                    description: EnableExtraWorkloadForCanary indicates whether to
 | 
			
		||||
                      create extra workload for canary True corresponds to RollingStyle
 | 
			
		||||
                      "Canary". False corresponds to RollingStyle "Partiton". Ignored
 | 
			
		||||
                      in BlueGreen-style. This field is about to deprecate, use RollingStyle
 | 
			
		||||
                      instead. If both of them are set, controller will only consider
 | 
			
		||||
                      this filed when RollingStyle is empty
 | 
			
		||||
                    type: boolean
 | 
			
		||||
                  failureThreshold:
 | 
			
		||||
                    anyOf:
 | 
			
		||||
| 
						 | 
				
			
			@ -382,6 +396,9 @@ spec:
 | 
			
		|||
                        description: labels
 | 
			
		||||
                        type: object
 | 
			
		||||
                    type: object
 | 
			
		||||
                  rollingStyle:
 | 
			
		||||
                    description: RollingStyle can be "Canary", "Partiton" or "BlueGreen"
 | 
			
		||||
                    type: string
 | 
			
		||||
                  rolloutID:
 | 
			
		||||
                    description: RolloutID indicates an id for each rollout progress
 | 
			
		||||
                    type: string
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -435,18 +435,32 @@ spec:
 | 
			
		|||
                      so it cannot be used as service selector label
 | 
			
		||||
                    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
 | 
			
		||||
                  currentStepState:
 | 
			
		||||
                    type: string
 | 
			
		||||
                  finalisingStep:
 | 
			
		||||
                    type: string
 | 
			
		||||
                  lastUpdateTime:
 | 
			
		||||
                    format: date-time
 | 
			
		||||
                    type: string
 | 
			
		||||
                  message:
 | 
			
		||||
                    type: string
 | 
			
		||||
                  nextStepIndex:
 | 
			
		||||
                    description: NextStepIndex defines the next step of the rollout
 | 
			
		||||
                      is on. In normal case, NextStepIndex is equal to CurrentStepIndex
 | 
			
		||||
                      + 1 If the current step is the last step, NextStepIndex is equal
 | 
			
		||||
                      to -1 Before the release, NextStepIndex is also equal to -1
 | 
			
		||||
                      0 is not used and won't appear in any case It is allowed to
 | 
			
		||||
                      patch NextStepIndex by design, e.g. if CurrentStepIndex is 2,
 | 
			
		||||
                      user can patch NextStepIndex to 3 (if exists) to achieve batch
 | 
			
		||||
                      jump, or patch NextStepIndex to 1 to implement a re-execution
 | 
			
		||||
                      of step 1 Patching it with a non-positive value is meaningless,
 | 
			
		||||
                      which will be corrected in the next reconciliation achieve batch
 | 
			
		||||
                      jump, or patch NextStepIndex to 1 to implement a re-execution
 | 
			
		||||
                      of step 1
 | 
			
		||||
                    format: int32
 | 
			
		||||
                    type: integer
 | 
			
		||||
                  observedRolloutID:
 | 
			
		||||
                    description: ObservedRolloutID will record the newest spec.RolloutID
 | 
			
		||||
                      if status.canaryRevision equals to workload.updateRevision
 | 
			
		||||
| 
						 | 
				
			
			@ -470,6 +484,8 @@ spec:
 | 
			
		|||
                - canaryReplicas
 | 
			
		||||
                - canaryRevision
 | 
			
		||||
                - currentStepState
 | 
			
		||||
                - finalisingStep
 | 
			
		||||
                - nextStepIndex
 | 
			
		||||
                - podTemplateHash
 | 
			
		||||
                type: object
 | 
			
		||||
              conditions:
 | 
			
		||||
| 
						 | 
				
			
			@ -574,6 +590,308 @@ spec:
 | 
			
		|||
              strategy:
 | 
			
		||||
                description: rollout strategy
 | 
			
		||||
                properties:
 | 
			
		||||
                  blueGreen:
 | 
			
		||||
                    description: BlueGreenStrategy defines parameters for Blue Green
 | 
			
		||||
                      Release
 | 
			
		||||
                    properties:
 | 
			
		||||
                      disableGenerateCanaryService:
 | 
			
		||||
                        description: canary service will not be generated if DisableGenerateCanaryService
 | 
			
		||||
                          is true
 | 
			
		||||
                        type: boolean
 | 
			
		||||
                      failureThreshold:
 | 
			
		||||
                        anyOf:
 | 
			
		||||
                        - type: integer
 | 
			
		||||
                        - type: string
 | 
			
		||||
                        description: FailureThreshold indicates how many failed pods
 | 
			
		||||
                          can be tolerated in all upgraded pods. Only when FailureThreshold
 | 
			
		||||
                          are satisfied, Rollout can enter ready state. If FailureThreshold
 | 
			
		||||
                          is nil, Rollout will use the MaxUnavailable of workload
 | 
			
		||||
                          as its FailureThreshold. Defaults to nil.
 | 
			
		||||
                        x-kubernetes-int-or-string: true
 | 
			
		||||
                      steps:
 | 
			
		||||
                        description: Steps define the order of phases to execute release
 | 
			
		||||
                          in batches(20%, 40%, 60%, 80%, 100%)
 | 
			
		||||
                        items:
 | 
			
		||||
                          description: CanaryStep defines a step of a canary workload.
 | 
			
		||||
                          properties:
 | 
			
		||||
                            matches:
 | 
			
		||||
                              description: Matches define conditions used for matching
 | 
			
		||||
                                the incoming HTTP requests to canary service. Each
 | 
			
		||||
                                match is independent, i.e. this rule will be matched
 | 
			
		||||
                                if **any** one of the matches is satisfied. If Gateway
 | 
			
		||||
                                API, current only support one match. And cannot support
 | 
			
		||||
                                both weight and matches, if both are configured, then
 | 
			
		||||
                                matches takes precedence.
 | 
			
		||||
                              items:
 | 
			
		||||
                                properties:
 | 
			
		||||
                                  headers:
 | 
			
		||||
                                    description: Headers specifies HTTP request header
 | 
			
		||||
                                      matchers. Multiple match values are ANDed together,
 | 
			
		||||
                                      meaning, a request must match all the specified
 | 
			
		||||
                                      headers to select the route.
 | 
			
		||||
                                    items:
 | 
			
		||||
                                      description: HTTPHeaderMatch describes how to
 | 
			
		||||
                                        select a HTTP route by matching HTTP request
 | 
			
		||||
                                        headers.
 | 
			
		||||
                                      properties:
 | 
			
		||||
                                        name:
 | 
			
		||||
                                          description: "Name is the name of the HTTP
 | 
			
		||||
                                            Header to be matched. Name matching MUST
 | 
			
		||||
                                            be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2).
 | 
			
		||||
                                            \n If multiple entries specify equivalent
 | 
			
		||||
                                            header names, only the first entry with
 | 
			
		||||
                                            an equivalent name MUST be considered
 | 
			
		||||
                                            for a match. Subsequent entries with an
 | 
			
		||||
                                            equivalent header name MUST be ignored.
 | 
			
		||||
                                            Due to the case-insensitivity of header
 | 
			
		||||
                                            names, \"foo\" and \"Foo\" are considered
 | 
			
		||||
                                            equivalent. \n When a header is repeated
 | 
			
		||||
                                            in an HTTP request, it is implementation-specific
 | 
			
		||||
                                            behavior as to how this is represented.
 | 
			
		||||
                                            Generally, proxies should follow the guidance
 | 
			
		||||
                                            from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2
 | 
			
		||||
                                            regarding processing a repeated header,
 | 
			
		||||
                                            with special handling for \"Set-Cookie\"."
 | 
			
		||||
                                          maxLength: 256
 | 
			
		||||
                                          minLength: 1
 | 
			
		||||
                                          pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$
 | 
			
		||||
                                          type: string
 | 
			
		||||
                                        type:
 | 
			
		||||
                                          default: Exact
 | 
			
		||||
                                          description: "Type specifies how to match
 | 
			
		||||
                                            against the value of the header. \n Support:
 | 
			
		||||
                                            Core (Exact) \n Support: Custom (RegularExpression)
 | 
			
		||||
                                            \n Since RegularExpression HeaderMatchType
 | 
			
		||||
                                            has custom conformance, implementations
 | 
			
		||||
                                            can support POSIX, PCRE or any other dialects
 | 
			
		||||
                                            of regular expressions. Please read the
 | 
			
		||||
                                            implementation's documentation to determine
 | 
			
		||||
                                            the supported dialect."
 | 
			
		||||
                                          enum:
 | 
			
		||||
                                          - Exact
 | 
			
		||||
                                          - RegularExpression
 | 
			
		||||
                                          type: string
 | 
			
		||||
                                        value:
 | 
			
		||||
                                          description: Value is the value of HTTP
 | 
			
		||||
                                            Header to be matched.
 | 
			
		||||
                                          maxLength: 4096
 | 
			
		||||
                                          minLength: 1
 | 
			
		||||
                                          type: string
 | 
			
		||||
                                      required:
 | 
			
		||||
                                      - name
 | 
			
		||||
                                      - value
 | 
			
		||||
                                      type: object
 | 
			
		||||
                                    maxItems: 16
 | 
			
		||||
                                    type: array
 | 
			
		||||
                                type: object
 | 
			
		||||
                              type: array
 | 
			
		||||
                            pause:
 | 
			
		||||
                              description: Pause defines a pause stage for a rollout,
 | 
			
		||||
                                manual or auto
 | 
			
		||||
                              properties:
 | 
			
		||||
                                duration:
 | 
			
		||||
                                  description: Duration the amount of time to wait
 | 
			
		||||
                                    before moving to the next step.
 | 
			
		||||
                                  format: int32
 | 
			
		||||
                                  type: integer
 | 
			
		||||
                              type: object
 | 
			
		||||
                            replicas:
 | 
			
		||||
                              anyOf:
 | 
			
		||||
                              - type: integer
 | 
			
		||||
                              - type: string
 | 
			
		||||
                              description: 'Replicas is the number of expected canary
 | 
			
		||||
                                pods in this batch it can be an absolute number (ex:
 | 
			
		||||
                                5) or a percentage of total pods.'
 | 
			
		||||
                              x-kubernetes-int-or-string: true
 | 
			
		||||
                            requestHeaderModifier:
 | 
			
		||||
                              description: "Set overwrites the request with the given
 | 
			
		||||
                                header (name, value) before the action. \n Input:
 | 
			
		||||
                                \  GET /foo HTTP/1.1   my-header: foo \n requestHeaderModifier:
 | 
			
		||||
                                \  set:   - name: \"my-header\"     value: \"bar\"
 | 
			
		||||
                                \n Output:   GET /foo HTTP/1.1   my-header: bar"
 | 
			
		||||
                              properties:
 | 
			
		||||
                                add:
 | 
			
		||||
                                  description: "Add adds the given header(s) (name,
 | 
			
		||||
                                    value) to the request before the action. It appends
 | 
			
		||||
                                    to any existing values associated with the header
 | 
			
		||||
                                    name. \n Input:   GET /foo HTTP/1.1   my-header:
 | 
			
		||||
                                    foo \n Config:   add:   - name: \"my-header\"
 | 
			
		||||
                                    \    value: \"bar\" \n Output:   GET /foo HTTP/1.1
 | 
			
		||||
                                    \  my-header: foo   my-header: bar"
 | 
			
		||||
                                  items:
 | 
			
		||||
                                    description: HTTPHeader represents an HTTP Header
 | 
			
		||||
                                      name and value as defined by RFC 7230.
 | 
			
		||||
                                    properties:
 | 
			
		||||
                                      name:
 | 
			
		||||
                                        description: "Name is the name of the HTTP
 | 
			
		||||
                                          Header to be matched. Name matching MUST
 | 
			
		||||
                                          be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2).
 | 
			
		||||
                                          \n If multiple entries specify equivalent
 | 
			
		||||
                                          header names, the first entry with an equivalent
 | 
			
		||||
                                          name MUST be considered for a match. Subsequent
 | 
			
		||||
                                          entries with an equivalent header name MUST
 | 
			
		||||
                                          be ignored. Due to the case-insensitivity
 | 
			
		||||
                                          of header names, \"foo\" and \"Foo\" are
 | 
			
		||||
                                          considered equivalent."
 | 
			
		||||
                                        maxLength: 256
 | 
			
		||||
                                        minLength: 1
 | 
			
		||||
                                        pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$
 | 
			
		||||
                                        type: string
 | 
			
		||||
                                      value:
 | 
			
		||||
                                        description: Value is the value of HTTP Header
 | 
			
		||||
                                          to be matched.
 | 
			
		||||
                                        maxLength: 4096
 | 
			
		||||
                                        minLength: 1
 | 
			
		||||
                                        type: string
 | 
			
		||||
                                    required:
 | 
			
		||||
                                    - name
 | 
			
		||||
                                    - value
 | 
			
		||||
                                    type: object
 | 
			
		||||
                                  maxItems: 16
 | 
			
		||||
                                  type: array
 | 
			
		||||
                                  x-kubernetes-list-map-keys:
 | 
			
		||||
                                  - name
 | 
			
		||||
                                  x-kubernetes-list-type: map
 | 
			
		||||
                                remove:
 | 
			
		||||
                                  description: "Remove the given header(s) from the
 | 
			
		||||
                                    HTTP request before the action. The value of Remove
 | 
			
		||||
                                    is a list of HTTP header names. Note that the
 | 
			
		||||
                                    header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2).
 | 
			
		||||
                                    \n Input:   GET /foo HTTP/1.1   my-header1: foo
 | 
			
		||||
                                    \  my-header2: bar   my-header3: baz \n Config:
 | 
			
		||||
                                    \  remove: [\"my-header1\", \"my-header3\"] \n
 | 
			
		||||
                                    Output:   GET /foo HTTP/1.1   my-header2: bar"
 | 
			
		||||
                                  items:
 | 
			
		||||
                                    type: string
 | 
			
		||||
                                  maxItems: 16
 | 
			
		||||
                                  type: array
 | 
			
		||||
                                set:
 | 
			
		||||
                                  description: "Set overwrites the request with the
 | 
			
		||||
                                    given header (name, value) before the action.
 | 
			
		||||
                                    \n Input:   GET /foo HTTP/1.1   my-header: foo
 | 
			
		||||
                                    \n Config:   set:   - name: \"my-header\"     value:
 | 
			
		||||
                                    \"bar\" \n Output:   GET /foo HTTP/1.1   my-header:
 | 
			
		||||
                                    bar"
 | 
			
		||||
                                  items:
 | 
			
		||||
                                    description: HTTPHeader represents an HTTP Header
 | 
			
		||||
                                      name and value as defined by RFC 7230.
 | 
			
		||||
                                    properties:
 | 
			
		||||
                                      name:
 | 
			
		||||
                                        description: "Name is the name of the HTTP
 | 
			
		||||
                                          Header to be matched. Name matching MUST
 | 
			
		||||
                                          be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2).
 | 
			
		||||
                                          \n If multiple entries specify equivalent
 | 
			
		||||
                                          header names, the first entry with an equivalent
 | 
			
		||||
                                          name MUST be considered for a match. Subsequent
 | 
			
		||||
                                          entries with an equivalent header name MUST
 | 
			
		||||
                                          be ignored. Due to the case-insensitivity
 | 
			
		||||
                                          of header names, \"foo\" and \"Foo\" are
 | 
			
		||||
                                          considered equivalent."
 | 
			
		||||
                                        maxLength: 256
 | 
			
		||||
                                        minLength: 1
 | 
			
		||||
                                        pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$
 | 
			
		||||
                                        type: string
 | 
			
		||||
                                      value:
 | 
			
		||||
                                        description: Value is the value of HTTP Header
 | 
			
		||||
                                          to be matched.
 | 
			
		||||
                                        maxLength: 4096
 | 
			
		||||
                                        minLength: 1
 | 
			
		||||
                                        type: string
 | 
			
		||||
                                    required:
 | 
			
		||||
                                    - name
 | 
			
		||||
                                    - value
 | 
			
		||||
                                    type: object
 | 
			
		||||
                                  maxItems: 16
 | 
			
		||||
                                  type: array
 | 
			
		||||
                                  x-kubernetes-list-map-keys:
 | 
			
		||||
                                  - name
 | 
			
		||||
                                  x-kubernetes-list-type: map
 | 
			
		||||
                              type: object
 | 
			
		||||
                            traffic:
 | 
			
		||||
                              description: Traffic indicate how many percentage of
 | 
			
		||||
                                traffic the canary pods should receive Value is of
 | 
			
		||||
                                string type and is a percentage, e.g. 5%.
 | 
			
		||||
                              type: string
 | 
			
		||||
                          type: object
 | 
			
		||||
                        type: array
 | 
			
		||||
                      trafficRoutingRef:
 | 
			
		||||
                        description: TrafficRoutingRef is TrafficRouting's Name
 | 
			
		||||
                        type: string
 | 
			
		||||
                      trafficRoutings:
 | 
			
		||||
                        description: TrafficRoutings support ingress, gateway api
 | 
			
		||||
                          and custom network resource(e.g. istio, apisix) to enable
 | 
			
		||||
                          more fine-grained traffic routing and current only support
 | 
			
		||||
                          one TrafficRouting
 | 
			
		||||
                        items:
 | 
			
		||||
                          description: TrafficRoutingRef hosts all the different configuration
 | 
			
		||||
                            for supported service meshes to enable more fine-grained
 | 
			
		||||
                            traffic routing
 | 
			
		||||
                          properties:
 | 
			
		||||
                            customNetworkRefs:
 | 
			
		||||
                              description: CustomNetworkRefs hold a list of custom
 | 
			
		||||
                                providers to route traffic
 | 
			
		||||
                              items:
 | 
			
		||||
                                description: ObjectRef holds a references to the Kubernetes
 | 
			
		||||
                                  object
 | 
			
		||||
                                properties:
 | 
			
		||||
                                  apiVersion:
 | 
			
		||||
                                    description: API Version of the referent
 | 
			
		||||
                                    type: string
 | 
			
		||||
                                  kind:
 | 
			
		||||
                                    description: Kind of the referent
 | 
			
		||||
                                    type: string
 | 
			
		||||
                                  name:
 | 
			
		||||
                                    description: Name of the referent
 | 
			
		||||
                                    type: string
 | 
			
		||||
                                required:
 | 
			
		||||
                                - apiVersion
 | 
			
		||||
                                - kind
 | 
			
		||||
                                - name
 | 
			
		||||
                                type: object
 | 
			
		||||
                              type: array
 | 
			
		||||
                            gateway:
 | 
			
		||||
                              description: Gateway holds Gateway specific configuration
 | 
			
		||||
                                to route traffic Gateway configuration only supports
 | 
			
		||||
                                >= v0.4.0 (v1alpha2).
 | 
			
		||||
                              properties:
 | 
			
		||||
                                httpRouteName:
 | 
			
		||||
                                  description: HTTPRouteName refers to the name of
 | 
			
		||||
                                    an `HTTPRoute` resource in the same namespace
 | 
			
		||||
                                    as the `Rollout`
 | 
			
		||||
                                  type: string
 | 
			
		||||
                              type: object
 | 
			
		||||
                            gracePeriodSeconds:
 | 
			
		||||
                              description: Optional duration in seconds the traffic
 | 
			
		||||
                                provider(e.g. nginx ingress controller) consumes the
 | 
			
		||||
                                service, ingress configuration changes gracefully.
 | 
			
		||||
                              format: int32
 | 
			
		||||
                              type: integer
 | 
			
		||||
                            ingress:
 | 
			
		||||
                              description: Ingress holds Ingress specific configuration
 | 
			
		||||
                                to route traffic, e.g. Nginx, Alb.
 | 
			
		||||
                              properties:
 | 
			
		||||
                                classType:
 | 
			
		||||
                                  description: ClassType refers to the type of `Ingress`.
 | 
			
		||||
                                    current support nginx, aliyun-alb. default is
 | 
			
		||||
                                    nginx.
 | 
			
		||||
                                  type: string
 | 
			
		||||
                                name:
 | 
			
		||||
                                  description: Name refers to the name of an `Ingress`
 | 
			
		||||
                                    resource in the same namespace as the `Rollout`
 | 
			
		||||
                                  type: string
 | 
			
		||||
                              required:
 | 
			
		||||
                              - name
 | 
			
		||||
                              type: object
 | 
			
		||||
                            service:
 | 
			
		||||
                              description: Service holds the name of a service which
 | 
			
		||||
                                selects pods with stable version and don't select
 | 
			
		||||
                                any pods with canary version.
 | 
			
		||||
                              type: string
 | 
			
		||||
                          required:
 | 
			
		||||
                          - service
 | 
			
		||||
                          type: object
 | 
			
		||||
                        type: array
 | 
			
		||||
                    type: object
 | 
			
		||||
                  canary:
 | 
			
		||||
                    description: CanaryStrategy defines parameters for a Replica Based
 | 
			
		||||
                      Canary
 | 
			
		||||
| 
						 | 
				
			
			@ -1013,6 +1331,79 @@ spec:
 | 
			
		|||
          status:
 | 
			
		||||
            description: RolloutStatus defines the observed state of Rollout
 | 
			
		||||
            properties:
 | 
			
		||||
              blueGreenStatus:
 | 
			
		||||
                description: BlueGreen describes the state of the blueGreen rollout
 | 
			
		||||
                properties:
 | 
			
		||||
                  currentStepIndex:
 | 
			
		||||
                    description: CurrentStepIndex defines the current step of the
 | 
			
		||||
                      rollout is on.
 | 
			
		||||
                    format: int32
 | 
			
		||||
                    type: integer
 | 
			
		||||
                  currentStepState:
 | 
			
		||||
                    type: string
 | 
			
		||||
                  finalisingStep:
 | 
			
		||||
                    description: FinalisingStep the step of finalising
 | 
			
		||||
                    type: string
 | 
			
		||||
                  lastUpdateTime:
 | 
			
		||||
                    format: date-time
 | 
			
		||||
                    type: string
 | 
			
		||||
                  message:
 | 
			
		||||
                    type: string
 | 
			
		||||
                  nextStepIndex:
 | 
			
		||||
                    description: NextStepIndex defines the next step of the rollout
 | 
			
		||||
                      is on. In normal case, NextStepIndex is equal to CurrentStepIndex
 | 
			
		||||
                      + 1 If the current step is the last step, NextStepIndex is equal
 | 
			
		||||
                      to -1 Before the release, NextStepIndex is also equal to -1
 | 
			
		||||
                      0 is not used and won't appear in any case It is allowed to
 | 
			
		||||
                      patch NextStepIndex by design, e.g. if CurrentStepIndex is 2,
 | 
			
		||||
                      user can patch NextStepIndex to 3 (if exists) to achieve batch
 | 
			
		||||
                      jump, or patch NextStepIndex to 1 to implement a re-execution
 | 
			
		||||
                      of step 1 Patching it with a non-positive value is useless and
 | 
			
		||||
                      meaningless, which will be corrected in the next reconciliation
 | 
			
		||||
                    format: int32
 | 
			
		||||
                    type: integer
 | 
			
		||||
                  observedRolloutID:
 | 
			
		||||
                    description: ObservedRolloutID will record the newest spec.RolloutID
 | 
			
		||||
                      if status.canaryRevision equals to workload.updateRevision
 | 
			
		||||
                    type: string
 | 
			
		||||
                  observedWorkloadGeneration:
 | 
			
		||||
                    description: observedWorkloadGeneration is the most recent generation
 | 
			
		||||
                      observed for this Rollout ref workload generation.
 | 
			
		||||
                    format: int64
 | 
			
		||||
                    type: integer
 | 
			
		||||
                  podTemplateHash:
 | 
			
		||||
                    description: pod template hash is used as service selector label
 | 
			
		||||
                    type: string
 | 
			
		||||
                  rolloutHash:
 | 
			
		||||
                    description: RolloutHash from rollout.spec object
 | 
			
		||||
                    type: string
 | 
			
		||||
                  stableRevision:
 | 
			
		||||
                    description: StableRevision indicates the revision of stable pods
 | 
			
		||||
                    type: string
 | 
			
		||||
                  updatedReadyReplicas:
 | 
			
		||||
                    description: UpdatedReadyReplicas the numbers of updated ready
 | 
			
		||||
                      pods
 | 
			
		||||
                    format: int32
 | 
			
		||||
                    type: integer
 | 
			
		||||
                  updatedReplicas:
 | 
			
		||||
                    description: UpdatedReplicas the numbers of updated pods
 | 
			
		||||
                    format: int32
 | 
			
		||||
                    type: integer
 | 
			
		||||
                  updatedRevision:
 | 
			
		||||
                    description: CanaryRevision is calculated by rollout based on
 | 
			
		||||
                      podTemplateHash, and the internal logic flow uses It may be
 | 
			
		||||
                      different from rs podTemplateHash in different k8s versions,
 | 
			
		||||
                      so it cannot be used as service selector label
 | 
			
		||||
                    type: string
 | 
			
		||||
                required:
 | 
			
		||||
                - currentStepState
 | 
			
		||||
                - finalisingStep
 | 
			
		||||
                - nextStepIndex
 | 
			
		||||
                - podTemplateHash
 | 
			
		||||
                - updatedReadyReplicas
 | 
			
		||||
                - updatedReplicas
 | 
			
		||||
                - updatedRevision
 | 
			
		||||
                type: object
 | 
			
		||||
              canaryStatus:
 | 
			
		||||
                description: Canary describes the state of the canary rollout
 | 
			
		||||
                properties:
 | 
			
		||||
| 
						 | 
				
			
			@ -1033,17 +1424,32 @@ spec:
 | 
			
		|||
                    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.
 | 
			
		||||
                      rollout is on.
 | 
			
		||||
                    format: int32
 | 
			
		||||
                    type: integer
 | 
			
		||||
                  currentStepState:
 | 
			
		||||
                    type: string
 | 
			
		||||
                  finalisingStep:
 | 
			
		||||
                    description: FinalisingStep the step of finalising
 | 
			
		||||
                    type: string
 | 
			
		||||
                  lastUpdateTime:
 | 
			
		||||
                    format: date-time
 | 
			
		||||
                    type: string
 | 
			
		||||
                  message:
 | 
			
		||||
                    type: string
 | 
			
		||||
                  nextStepIndex:
 | 
			
		||||
                    description: NextStepIndex defines the next step of the rollout
 | 
			
		||||
                      is on. In normal case, NextStepIndex is equal to CurrentStepIndex
 | 
			
		||||
                      + 1 If the current step is the last step, NextStepIndex is equal
 | 
			
		||||
                      to -1 Before the release, NextStepIndex is also equal to -1
 | 
			
		||||
                      0 is not used and won't appear in any case It is allowed to
 | 
			
		||||
                      patch NextStepIndex by design, e.g. if CurrentStepIndex is 2,
 | 
			
		||||
                      user can patch NextStepIndex to 3 (if exists) to achieve batch
 | 
			
		||||
                      jump, or patch NextStepIndex to 1 to implement a re-execution
 | 
			
		||||
                      of step 1 Patching it with a non-positive value is useless and
 | 
			
		||||
                      meaningless, which will be corrected in the next reconciliation
 | 
			
		||||
                    format: int32
 | 
			
		||||
                    type: integer
 | 
			
		||||
                  observedRolloutID:
 | 
			
		||||
                    description: ObservedRolloutID will record the newest spec.RolloutID
 | 
			
		||||
                      if status.canaryRevision equals to workload.updateRevision
 | 
			
		||||
| 
						 | 
				
			
			@ -1067,6 +1473,8 @@ spec:
 | 
			
		|||
                - canaryReplicas
 | 
			
		||||
                - canaryRevision
 | 
			
		||||
                - currentStepState
 | 
			
		||||
                - finalisingStep
 | 
			
		||||
                - nextStepIndex
 | 
			
		||||
                - podTemplateHash
 | 
			
		||||
                type: object
 | 
			
		||||
              conditions:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -67,8 +67,8 @@ var (
 | 
			
		|||
				Name:       "sample",
 | 
			
		||||
			},
 | 
			
		||||
			ReleasePlan: v1beta1.ReleasePlan{
 | 
			
		||||
				EnableExtraWorkloadForCanary: true,
 | 
			
		||||
				BatchPartition:               pointer.Int32(0),
 | 
			
		||||
				RollingStyle:   v1beta1.CanaryRollingStyle,
 | 
			
		||||
				BatchPartition: pointer.Int32(0),
 | 
			
		||||
				Batches: []v1beta1.ReleaseBatch{
 | 
			
		||||
					{
 | 
			
		||||
						CanaryReplicas: intstr.FromString("10%"),
 | 
			
		||||
| 
						 | 
				
			
			@ -147,6 +147,7 @@ var (
 | 
			
		|||
			},
 | 
			
		||||
			ReleasePlan: v1beta1.ReleasePlan{
 | 
			
		||||
				BatchPartition: pointer.Int32Ptr(0),
 | 
			
		||||
				RollingStyle:   v1beta1.PartitionRollingStyle,
 | 
			
		||||
				Batches: []v1beta1.ReleaseBatch{
 | 
			
		||||
					{
 | 
			
		||||
						CanaryReplicas: intstr.FromString("10%"),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -197,28 +197,43 @@ func (r *Executor) getReleaseController(release *v1beta1.BatchRelease, newStatus
 | 
			
		|||
		Namespace: release.Namespace,
 | 
			
		||||
		Name:      targetRef.Name,
 | 
			
		||||
	}
 | 
			
		||||
	rollingStyle := release.Spec.ReleasePlan.RollingStyle
 | 
			
		||||
	if len(rollingStyle) == 0 && release.Spec.ReleasePlan.EnableExtraWorkloadForCanary {
 | 
			
		||||
		rollingStyle = v1beta1.CanaryRollingStyle
 | 
			
		||||
	}
 | 
			
		||||
	klog.Infof("BatchRelease(%v) using %s-style release controller for this batch release", klog.KObj(release), rollingStyle)
 | 
			
		||||
	switch rollingStyle {
 | 
			
		||||
	case v1beta1.BlueGreenRollingStyle:
 | 
			
		||||
		// if targetRef.APIVersion == appsv1alpha1.GroupVersion.String() && targetRef.Kind == reflect.TypeOf(appsv1alpha1.CloneSet{}).Name() {
 | 
			
		||||
		// 	klog.InfoS("Using CloneSet bluegreen-style release controller for this batch release", "workload name", targetKey.Name, "namespace", targetKey.Namespace)
 | 
			
		||||
		// 	return partitionstyle.NewControlPlane(cloneset.NewController, r.client, r.recorder, release, newStatus, targetKey, gvk), nil
 | 
			
		||||
		// }
 | 
			
		||||
		// if targetRef.APIVersion == apps.SchemeGroupVersion.String() && targetRef.Kind == reflect.TypeOf(apps.Deployment{}).Name() {
 | 
			
		||||
		// 	klog.InfoS("Using Deployment bluegreen-style release controller for this batch release", "workload name", targetKey.Name, "namespace", targetKey.Namespace)
 | 
			
		||||
		// 	return bluegreenstyle.NewControlPlane(deployment.NewController, r.client, r.recorder, release, newStatus, targetKey, gvk), nil
 | 
			
		||||
		// }
 | 
			
		||||
 | 
			
		||||
	switch targetRef.APIVersion {
 | 
			
		||||
	case appsv1alpha1.GroupVersion.String():
 | 
			
		||||
		if targetRef.Kind == reflect.TypeOf(appsv1alpha1.CloneSet{}).Name() {
 | 
			
		||||
	case v1beta1.CanaryRollingStyle:
 | 
			
		||||
		if targetRef.APIVersion == apps.SchemeGroupVersion.String() && targetRef.Kind == reflect.TypeOf(apps.Deployment{}).Name() {
 | 
			
		||||
			klog.InfoS("Using Deployment canary-style release controller for this batch release", "workload name", targetKey.Name, "namespace", targetKey.Namespace)
 | 
			
		||||
			return canarystyle.NewControlPlane(canarydeployment.NewController, r.client, r.recorder, release, newStatus, targetKey), nil
 | 
			
		||||
		}
 | 
			
		||||
		fallthrough
 | 
			
		||||
 | 
			
		||||
	case v1beta1.PartitionRollingStyle, "":
 | 
			
		||||
		if targetRef.APIVersion == appsv1alpha1.GroupVersion.String() && targetRef.Kind == reflect.TypeOf(appsv1alpha1.CloneSet{}).Name() {
 | 
			
		||||
			klog.InfoS("Using CloneSet partition-style release controller for this batch release", "workload name", targetKey.Name, "namespace", targetKey.Namespace)
 | 
			
		||||
			return partitionstyle.NewControlPlane(cloneset.NewController, r.client, r.recorder, release, newStatus, targetKey, gvk), nil
 | 
			
		||||
		}
 | 
			
		||||
		if targetRef.Kind == reflect.TypeOf(appsv1alpha1.DaemonSet{}).Name() {
 | 
			
		||||
		if targetRef.APIVersion == appsv1alpha1.GroupVersion.String() && targetRef.Kind == reflect.TypeOf(appsv1alpha1.DaemonSet{}).Name() {
 | 
			
		||||
			klog.InfoS("Using DaemonSet partition-style release controller for this batch release", "workload name", targetKey.Name, "namespace", targetKey.Namespace)
 | 
			
		||||
			return partitionstyle.NewControlPlane(daemonset.NewController, r.client, r.recorder, release, newStatus, targetKey, gvk), nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case apps.SchemeGroupVersion.String():
 | 
			
		||||
		if targetRef.Kind == reflect.TypeOf(apps.Deployment{}).Name() {
 | 
			
		||||
			if !release.Spec.ReleasePlan.EnableExtraWorkloadForCanary {
 | 
			
		||||
				klog.InfoS("Using Deployment partition-style release controller for this batch release", "workload name", targetKey.Name, "namespace", targetKey.Namespace)
 | 
			
		||||
				return partitionstyle.NewControlPlane(partitiondeployment.NewController, r.client, r.recorder, release, newStatus, targetKey, gvk), nil
 | 
			
		||||
			} else {
 | 
			
		||||
				klog.InfoS("Using Deployment canary-style release controller for this batch release", "workload name", targetKey.Name, "namespace", targetKey.Namespace)
 | 
			
		||||
				return canarystyle.NewControlPlane(canarydeployment.NewController, r.client, r.recorder, release, newStatus, targetKey), nil
 | 
			
		||||
			}
 | 
			
		||||
		if targetRef.APIVersion == apps.SchemeGroupVersion.String() && targetRef.Kind == reflect.TypeOf(apps.Deployment{}).Name() {
 | 
			
		||||
			klog.InfoS("Using Deployment partition-style release controller for this batch release", "workload name", targetKey.Name, "namespace", targetKey.Namespace)
 | 
			
		||||
			return partitionstyle.NewControlPlane(partitiondeployment.NewController, r.client, r.recorder, release, newStatus, targetKey, gvk), nil
 | 
			
		||||
		}
 | 
			
		||||
		klog.Info("Partition, but use StatefulSet-Like partition-style release controller for this batch release")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// try to use StatefulSet-like rollout controller by default
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -364,6 +364,7 @@ func createBatchRelease(rollout *v1beta1.Rollout, rolloutID string, batch int32,
 | 
			
		|||
				BatchPartition:               utilpointer.Int32Ptr(batch),
 | 
			
		||||
				FailureThreshold:             rollout.Spec.Strategy.Canary.FailureThreshold,
 | 
			
		||||
				PatchPodTemplateMetadata:     rollout.Spec.Strategy.Canary.PatchPodTemplateMetadata,
 | 
			
		||||
				RollingStyle:                 rollout.Spec.Strategy.GetRollingStyle(),
 | 
			
		||||
				EnableExtraWorkloadForCanary: rollout.Spec.Strategy.Canary.EnableExtraWorkloadForCanary,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -100,6 +100,7 @@ func TestRunCanary(t *testing.T) {
 | 
			
		|||
				}
 | 
			
		||||
				br.Spec.ReleasePlan.BatchPartition = utilpointer.Int32(0)
 | 
			
		||||
				br.Spec.ReleasePlan.EnableExtraWorkloadForCanary = true
 | 
			
		||||
				br.Spec.ReleasePlan.RollingStyle = v1beta1.CanaryRollingStyle
 | 
			
		||||
				return br
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			@ -159,9 +160,12 @@ func TestRunCanary(t *testing.T) {
 | 
			
		|||
				}
 | 
			
		||||
				br.Spec.ReleasePlan.BatchPartition = utilpointer.Int32(0)
 | 
			
		||||
				br.Spec.ReleasePlan.EnableExtraWorkloadForCanary = true
 | 
			
		||||
				br.Spec.ReleasePlan.RollingStyle = v1beta1.CanaryRollingStyle
 | 
			
		||||
				br.Status = v1beta1.BatchReleaseStatus{
 | 
			
		||||
					ObservedGeneration:      1,
 | 
			
		||||
					ObservedReleasePlanHash: "d444a1007776da957d7d8549e3375c96179621b85670ad1e2bb0fc5fea16446a",
 | 
			
		||||
					ObservedGeneration: 1,
 | 
			
		||||
					// since we use RollingStyle over EnableExtraWorkloadForCanary now, former hardcoded hash
 | 
			
		||||
					// should be re-calculated
 | 
			
		||||
					ObservedReleasePlanHash: util.HashReleasePlanBatches(&br.Spec.ReleasePlan),
 | 
			
		||||
					CanaryStatus: v1beta1.BatchReleaseCanaryStatus{
 | 
			
		||||
						CurrentBatchState:    v1beta1.ReadyBatchState,
 | 
			
		||||
						CurrentBatch:         0,
 | 
			
		||||
| 
						 | 
				
			
			@ -205,6 +209,7 @@ func TestRunCanary(t *testing.T) {
 | 
			
		|||
				}
 | 
			
		||||
				br.Spec.ReleasePlan.BatchPartition = utilpointer.Int32(0)
 | 
			
		||||
				br.Spec.ReleasePlan.EnableExtraWorkloadForCanary = true
 | 
			
		||||
				br.Spec.ReleasePlan.RollingStyle = v1beta1.CanaryRollingStyle
 | 
			
		||||
				return br
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,17 +66,31 @@ func (r *RolloutReconciler) reconcileRolloutProgressing(rollout *v1beta1.Rollout
 | 
			
		|||
	switch cond.Reason {
 | 
			
		||||
	case v1alpha1.ProgressingReasonInitializing:
 | 
			
		||||
		klog.Infof("rollout(%s/%s) is Progressing, and in reason(%s)", rollout.Namespace, rollout.Name, cond.Reason)
 | 
			
		||||
		// new canaryStatus
 | 
			
		||||
		newStatus.CanaryStatus = &v1beta1.CanaryStatus{
 | 
			
		||||
		// clear and create
 | 
			
		||||
		newStatus.Clear()
 | 
			
		||||
		commonStatus := v1beta1.CommonStatus{
 | 
			
		||||
			ObservedWorkloadGeneration: rolloutContext.Workload.Generation,
 | 
			
		||||
			RolloutHash:                rolloutContext.Rollout.Annotations[util.RolloutHashAnnotation],
 | 
			
		||||
			ObservedRolloutID:          getRolloutID(rolloutContext.Workload),
 | 
			
		||||
			StableRevision:             rolloutContext.Workload.StableRevision,
 | 
			
		||||
			CanaryRevision:             rolloutContext.Workload.CanaryRevision,
 | 
			
		||||
			CurrentStepIndex:           1,
 | 
			
		||||
			CurrentStepState:           v1beta1.CanaryStepStateUpgrade,
 | 
			
		||||
			NextStepIndex:              util.NextBatchIndex(rollout, 1),
 | 
			
		||||
			CurrentStepState:           v1beta1.CanaryStepStateInit,
 | 
			
		||||
			LastUpdateTime:             &metav1.Time{Time: time.Now()},
 | 
			
		||||
		}
 | 
			
		||||
		if rollout.Spec.Strategy.IsBlueGreenRelease() {
 | 
			
		||||
			newStatus.BlueGreenStatus = &v1beta1.BlueGreenStatus{
 | 
			
		||||
				CommonStatus:    commonStatus,
 | 
			
		||||
				UpdatedRevision: rolloutContext.Workload.CanaryRevision,
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			commonStatus.CurrentStepState = v1beta1.CanaryStepStateUpgrade
 | 
			
		||||
			newStatus.CanaryStatus = &v1beta1.CanaryStatus{
 | 
			
		||||
				CommonStatus:   commonStatus,
 | 
			
		||||
				CanaryRevision: rolloutContext.Workload.CanaryRevision,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		done, err := r.doProgressingInitializing(rolloutContext)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			klog.Errorf("rollout(%s/%s) doProgressingInitializing error(%s)", rollout.Namespace, rollout.Name, err.Error())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -67,6 +67,7 @@ func TestReconcileRolloutProgressing(t *testing.T) {
 | 
			
		|||
				s.CanaryStatus.StableRevision = "pod-template-hash-v1"
 | 
			
		||||
				s.CanaryStatus.CanaryRevision = "6f8cc56547"
 | 
			
		||||
				s.CanaryStatus.CurrentStepIndex = 1
 | 
			
		||||
				s.CanaryStatus.NextStepIndex = 2
 | 
			
		||||
				s.CanaryStatus.CurrentStepState = v1beta1.CanaryStepStateUpgrade
 | 
			
		||||
				return s
 | 
			
		||||
			},
 | 
			
		||||
| 
						 | 
				
			
			@ -99,6 +100,7 @@ func TestReconcileRolloutProgressing(t *testing.T) {
 | 
			
		|||
				s.CanaryStatus.StableRevision = "pod-template-hash-v1"
 | 
			
		||||
				s.CanaryStatus.CanaryRevision = "6f8cc56547"
 | 
			
		||||
				s.CanaryStatus.CurrentStepIndex = 1
 | 
			
		||||
				s.CanaryStatus.NextStepIndex = 2
 | 
			
		||||
				s.CanaryStatus.CurrentStepState = v1beta1.CanaryStepStateUpgrade
 | 
			
		||||
				cond := util.GetRolloutCondition(*s, v1beta1.RolloutConditionProgressing)
 | 
			
		||||
				cond.Reason = v1alpha1.ProgressingReasonInRolling
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -123,17 +123,28 @@ func (r *RolloutReconciler) calculateRolloutStatus(rollout *v1beta1.Rollout) (re
 | 
			
		|||
			// But at the first deployment of Rollout/Workload, CanaryStatus isn't set due to no rollout progression,
 | 
			
		||||
			// and PaaS platform cannot judge whether the deployment is completed base on the code above. So we have
 | 
			
		||||
			// to update the status just like the rollout was completed.
 | 
			
		||||
 | 
			
		||||
			newStatus.CanaryStatus = &v1beta1.CanaryStatus{
 | 
			
		||||
			commonStatus := v1beta1.CommonStatus{
 | 
			
		||||
				ObservedRolloutID:          getRolloutID(workload),
 | 
			
		||||
				ObservedWorkloadGeneration: workload.Generation,
 | 
			
		||||
				PodTemplateHash:            workload.PodTemplateHash,
 | 
			
		||||
				CanaryRevision:             workload.CanaryRevision,
 | 
			
		||||
				StableRevision:             workload.StableRevision,
 | 
			
		||||
				CurrentStepIndex:           int32(len(rollout.Spec.Strategy.Canary.Steps)),
 | 
			
		||||
				CurrentStepIndex:           int32(len(rollout.Spec.Strategy.GetSteps())),
 | 
			
		||||
				NextStepIndex:              util.NextBatchIndex(rollout, int32(len(rollout.Spec.Strategy.GetSteps()))),
 | 
			
		||||
				CurrentStepState:           v1beta1.CanaryStepStateCompleted,
 | 
			
		||||
				RolloutHash:                rollout.Annotations[util.RolloutHashAnnotation],
 | 
			
		||||
			}
 | 
			
		||||
			if rollout.Spec.Strategy.IsBlueGreenRelease() {
 | 
			
		||||
				newStatus.BlueGreenStatus = &v1beta1.BlueGreenStatus{
 | 
			
		||||
					CommonStatus:    commonStatus,
 | 
			
		||||
					UpdatedRevision: workload.CanaryRevision,
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				newStatus.CanaryStatus = &v1beta1.CanaryStatus{
 | 
			
		||||
					CommonStatus:   commonStatus,
 | 
			
		||||
					CanaryRevision: workload.CanaryRevision,
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			newStatus.Message = "workload deployment is completed"
 | 
			
		||||
		}
 | 
			
		||||
	case v1beta1.RolloutPhaseDisabled:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -163,15 +163,17 @@ var (
 | 
			
		|||
		Status: v1beta1.RolloutStatus{
 | 
			
		||||
			Phase: v1beta1.RolloutPhaseProgressing,
 | 
			
		||||
			CanaryStatus: &v1beta1.CanaryStatus{
 | 
			
		||||
				ObservedWorkloadGeneration: 1,
 | 
			
		||||
				RolloutHash:                "rollout-hash-v1",
 | 
			
		||||
				ObservedRolloutID:          "rollout-id-1",
 | 
			
		||||
				StableRevision:             "podtemplatehash-v1",
 | 
			
		||||
				CanaryRevision:             "revision-v2",
 | 
			
		||||
				CurrentStepIndex:           1,
 | 
			
		||||
				CurrentStepState:           v1beta1.CanaryStepStateTrafficRouting,
 | 
			
		||||
				PodTemplateHash:            "podtemplatehash-v2",
 | 
			
		||||
				LastUpdateTime:             &metav1.Time{Time: time.Now()},
 | 
			
		||||
				CommonStatus: v1beta1.CommonStatus{
 | 
			
		||||
					ObservedWorkloadGeneration: 1,
 | 
			
		||||
					RolloutHash:                "rollout-hash-v1",
 | 
			
		||||
					ObservedRolloutID:          "rollout-id-1",
 | 
			
		||||
					StableRevision:             "podtemplatehash-v1",
 | 
			
		||||
					CurrentStepIndex:           1,
 | 
			
		||||
					CurrentStepState:           v1beta1.CanaryStepStateTrafficRouting,
 | 
			
		||||
					PodTemplateHash:            "podtemplatehash-v2",
 | 
			
		||||
					LastUpdateTime:             &metav1.Time{Time: time.Now()},
 | 
			
		||||
				},
 | 
			
		||||
				CanaryRevision: "revision-v2",
 | 
			
		||||
			},
 | 
			
		||||
			Conditions: []v1beta1.RolloutCondition{
 | 
			
		||||
				{
 | 
			
		||||
| 
						 | 
				
			
			@ -249,15 +251,17 @@ var (
 | 
			
		|||
		Status: v1beta1.RolloutStatus{
 | 
			
		||||
			Phase: v1beta1.RolloutPhaseProgressing,
 | 
			
		||||
			CanaryStatus: &v1beta1.CanaryStatus{
 | 
			
		||||
				ObservedWorkloadGeneration: 1,
 | 
			
		||||
				RolloutHash:                "rollout-hash-v1",
 | 
			
		||||
				ObservedRolloutID:          "rollout-id-1",
 | 
			
		||||
				StableRevision:             "podtemplatehash-v1",
 | 
			
		||||
				CanaryRevision:             "revision-v2",
 | 
			
		||||
				CurrentStepIndex:           1,
 | 
			
		||||
				CurrentStepState:           v1beta1.CanaryStepStateTrafficRouting,
 | 
			
		||||
				PodTemplateHash:            "podtemplatehash-v2",
 | 
			
		||||
				LastUpdateTime:             &metav1.Time{Time: time.Now()},
 | 
			
		||||
				CommonStatus: v1beta1.CommonStatus{
 | 
			
		||||
					ObservedWorkloadGeneration: 1,
 | 
			
		||||
					RolloutHash:                "rollout-hash-v1",
 | 
			
		||||
					ObservedRolloutID:          "rollout-id-1",
 | 
			
		||||
					StableRevision:             "podtemplatehash-v1",
 | 
			
		||||
					CurrentStepIndex:           1,
 | 
			
		||||
					CurrentStepState:           v1beta1.CanaryStepStateTrafficRouting,
 | 
			
		||||
					PodTemplateHash:            "podtemplatehash-v2",
 | 
			
		||||
					LastUpdateTime:             &metav1.Time{Time: time.Now()},
 | 
			
		||||
				},
 | 
			
		||||
				CanaryRevision: "revision-v2",
 | 
			
		||||
			},
 | 
			
		||||
			Conditions: []v1beta1.RolloutCondition{
 | 
			
		||||
				{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -164,3 +164,15 @@ func DumpJSON(o interface{}) string {
 | 
			
		|||
func EncodeHash(data string) string {
 | 
			
		||||
	return fmt.Sprintf("%x", sha256.Sum256([]byte(data)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// calculate the next batch index
 | 
			
		||||
func NextBatchIndex(rollout *rolloutv1beta1.Rollout, CurrentStepIndex int32) int32 {
 | 
			
		||||
	if rollout == nil {
 | 
			
		||||
		return -1
 | 
			
		||||
	}
 | 
			
		||||
	allSteps := int32(len(rollout.Spec.Strategy.GetSteps()))
 | 
			
		||||
	if CurrentStepIndex >= allSteps {
 | 
			
		||||
		return -1
 | 
			
		||||
	}
 | 
			
		||||
	return CurrentStepIndex + 1
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -133,12 +133,11 @@ func (h *RolloutCreateUpdateHandler) validateRolloutUpdate(oldObj, newObj *appsv
 | 
			
		|||
		if !reflect.DeepEqual(oldObj.Spec.WorkloadRef, newObj.Spec.WorkloadRef) {
 | 
			
		||||
			return field.ErrorList{field.Forbidden(field.NewPath("Spec.ObjectRef"), "Rollout 'ObjectRef' field is immutable")}
 | 
			
		||||
		}
 | 
			
		||||
		// canary strategy
 | 
			
		||||
		if !reflect.DeepEqual(oldObj.Spec.Strategy.Canary.TrafficRoutings, newObj.Spec.Strategy.Canary.TrafficRoutings) {
 | 
			
		||||
			return field.ErrorList{field.Forbidden(field.NewPath("Spec.Strategy.Canary.TrafficRoutings"), "Rollout 'Strategy.Canary.TrafficRoutings' field is immutable")}
 | 
			
		||||
		if !reflect.DeepEqual(oldObj.Spec.Strategy.GetTrafficRouting(), newObj.Spec.Strategy.GetTrafficRouting()) {
 | 
			
		||||
			return field.ErrorList{field.Forbidden(field.NewPath("Spec.Strategy.Canary|BlueGreen.TrafficRoutings"), "Rollout 'Strategy.Canary|BlueGreen.TrafficRoutings' field is immutable")}
 | 
			
		||||
		}
 | 
			
		||||
		if oldObj.Spec.Strategy.Canary.EnableExtraWorkloadForCanary != newObj.Spec.Strategy.Canary.EnableExtraWorkloadForCanary {
 | 
			
		||||
			return field.ErrorList{field.Forbidden(field.NewPath("Spec.Strategy.Canary"), "Rollout enableExtraWorkloadForCanary is immutable")}
 | 
			
		||||
		if oldObj.Spec.Strategy.GetRollingStyle() != newObj.Spec.Strategy.GetRollingStyle() {
 | 
			
		||||
			return field.ErrorList{field.Forbidden(field.NewPath("Spec.Strategy.Canary|BlueGreen"), "Rollout style and enableExtraWorkloadForCanary are immutable")}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -198,15 +197,32 @@ func validateRolloutSpecObjectRef(workloadRef *appsv1beta1.ObjectRef, fldPath *f
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func validateRolloutSpecStrategy(strategy *appsv1beta1.RolloutStrategy, fldPath *field.Path) field.ErrorList {
 | 
			
		||||
	if strategy.Canary == nil && strategy.BlueGreen == nil {
 | 
			
		||||
		return field.ErrorList{field.Invalid(fldPath, nil, "Canary and BlueGreen cannot both be empty")}
 | 
			
		||||
	}
 | 
			
		||||
	if strategy.Canary != nil && strategy.BlueGreen != nil {
 | 
			
		||||
		return field.ErrorList{field.Invalid(fldPath, nil, "Canary and BlueGreen cannot both be set")}
 | 
			
		||||
	}
 | 
			
		||||
	if strategy.BlueGreen != nil {
 | 
			
		||||
		return validateRolloutSpecBlueGreenStrategy(strategy.BlueGreen, fldPath.Child("BlueGreen"))
 | 
			
		||||
	}
 | 
			
		||||
	return validateRolloutSpecCanaryStrategy(strategy.Canary, fldPath.Child("Canary"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateRolloutSpecCanaryStrategy(canary *appsv1beta1.CanaryStrategy, fldPath *field.Path) field.ErrorList {
 | 
			
		||||
	if canary == nil {
 | 
			
		||||
		return field.ErrorList{field.Invalid(fldPath, nil, "Canary cannot be empty")}
 | 
			
		||||
	}
 | 
			
		||||
type TrafficRule string
 | 
			
		||||
 | 
			
		||||
	errList := validateRolloutSpecCanarySteps(canary.Steps, fldPath.Child("Steps"), len(canary.TrafficRoutings) > 0)
 | 
			
		||||
const (
 | 
			
		||||
	TrafficRuleCanary    TrafficRule = "Canary"
 | 
			
		||||
	TrafficRuleBlueGreen TrafficRule = "BlueGreen"
 | 
			
		||||
	NoTraffic            TrafficRule = "NoTraffic"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func validateRolloutSpecCanaryStrategy(canary *appsv1beta1.CanaryStrategy, fldPath *field.Path) field.ErrorList {
 | 
			
		||||
	trafficRule := NoTraffic
 | 
			
		||||
	if len(canary.TrafficRoutings) > 0 {
 | 
			
		||||
		trafficRule = TrafficRuleCanary
 | 
			
		||||
	}
 | 
			
		||||
	errList := validateRolloutSpecCanarySteps(canary.Steps, fldPath.Child("Steps"), trafficRule)
 | 
			
		||||
	if len(canary.TrafficRoutings) > 1 {
 | 
			
		||||
		errList = append(errList, field.Invalid(fldPath, canary.TrafficRoutings, "Rollout currently only support single TrafficRouting."))
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -216,6 +232,21 @@ func validateRolloutSpecCanaryStrategy(canary *appsv1beta1.CanaryStrategy, fldPa
 | 
			
		|||
	return errList
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateRolloutSpecBlueGreenStrategy(blueGreen *appsv1beta1.BlueGreenStrategy, fldPath *field.Path) field.ErrorList {
 | 
			
		||||
	trafficRule := NoTraffic
 | 
			
		||||
	if len(blueGreen.TrafficRoutings) > 0 {
 | 
			
		||||
		trafficRule = TrafficRuleBlueGreen
 | 
			
		||||
	}
 | 
			
		||||
	errList := validateRolloutSpecCanarySteps(blueGreen.Steps, fldPath.Child("Steps"), trafficRule)
 | 
			
		||||
	if len(blueGreen.TrafficRoutings) > 1 {
 | 
			
		||||
		errList = append(errList, field.Invalid(fldPath, blueGreen.TrafficRoutings, "Rollout currently only support single TrafficRouting."))
 | 
			
		||||
	}
 | 
			
		||||
	for _, traffic := range blueGreen.TrafficRoutings {
 | 
			
		||||
		errList = append(errList, validateRolloutSpecCanaryTraffic(traffic, fldPath.Child("TrafficRouting"))...)
 | 
			
		||||
	}
 | 
			
		||||
	return errList
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateRolloutSpecCanaryTraffic(traffic appsv1beta1.TrafficRoutingRef, fldPath *field.Path) field.ErrorList {
 | 
			
		||||
	errList := field.ErrorList{}
 | 
			
		||||
	if len(traffic.Service) == 0 {
 | 
			
		||||
| 
						 | 
				
			
			@ -240,7 +271,7 @@ func validateRolloutSpecCanaryTraffic(traffic appsv1beta1.TrafficRoutingRef, fld
 | 
			
		|||
	return errList
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateRolloutSpecCanarySteps(steps []appsv1beta1.CanaryStep, fldPath *field.Path, isTraffic bool) field.ErrorList {
 | 
			
		||||
func validateRolloutSpecCanarySteps(steps []appsv1beta1.CanaryStep, fldPath *field.Path, trafficRule TrafficRule) field.ErrorList {
 | 
			
		||||
	stepCount := len(steps)
 | 
			
		||||
	if stepCount == 0 {
 | 
			
		||||
		return field.ErrorList{field.Invalid(fldPath, steps, "The number of Canary.Steps cannot be empty")}
 | 
			
		||||
| 
						 | 
				
			
			@ -258,14 +289,21 @@ func validateRolloutSpecCanarySteps(steps []appsv1beta1.CanaryStep, fldPath *fie
 | 
			
		|||
			return field.ErrorList{field.Invalid(fldPath.Index(i).Child("Replicas"),
 | 
			
		||||
				s.Replicas, `replicas must be positive number, or a percentage with "0%" < canaryReplicas <= "100%"`)}
 | 
			
		||||
		}
 | 
			
		||||
		if !isTraffic {
 | 
			
		||||
		if trafficRule == NoTraffic || s.Traffic == nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if s.Traffic != nil {
 | 
			
		||||
			is := intstr.FromString(*s.Traffic)
 | 
			
		||||
			weight, err := intstr.GetScaledValueFromIntOrPercent(&is, 100, true)
 | 
			
		||||
		is := intstr.FromString(*s.Traffic)
 | 
			
		||||
		weight, err := intstr.GetScaledValueFromIntOrPercent(&is, 100, true)
 | 
			
		||||
		switch trafficRule {
 | 
			
		||||
		case TrafficRuleBlueGreen:
 | 
			
		||||
			// traffic "0%" is allowed in blueGreen strategy
 | 
			
		||||
			if err != nil || weight < 0 || weight > 100 {
 | 
			
		||||
				return field.ErrorList{field.Invalid(fldPath.Index(i).Child("steps"), steps, `traffic must be percentage with "0%" <= traffic <= "100%" in blueGreen strategy`)}
 | 
			
		||||
			}
 | 
			
		||||
		default:
 | 
			
		||||
			// traffic "0%" is not allowed in canary strategy
 | 
			
		||||
			if err != nil || weight <= 0 || weight > 100 {
 | 
			
		||||
				return field.ErrorList{field.Invalid(fldPath.Index(i).Child("steps"), steps, `traffic must be percentage with "0%" < traffic <= "100%"`)}
 | 
			
		||||
				return field.ErrorList{field.Invalid(fldPath.Index(i).Child("steps"), steps, `traffic must be percentage with "0%" < traffic <= "100%" in canary strategy`)}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -96,7 +96,9 @@ var (
 | 
			
		|||
		},
 | 
			
		||||
		Status: appsv1beta1.RolloutStatus{
 | 
			
		||||
			CanaryStatus: &appsv1beta1.CanaryStatus{
 | 
			
		||||
				CurrentStepState: appsv1beta1.CanaryStepStateCompleted,
 | 
			
		||||
				CommonStatus: appsv1beta1.CommonStatus{
 | 
			
		||||
					CurrentStepState: appsv1beta1.CanaryStepStateCompleted,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,16 @@ import (
 | 
			
		|||
 | 
			
		||||
var _ = SIGDescribe("Advanced Deployment", func() {
 | 
			
		||||
	var namespace string
 | 
			
		||||
 | 
			
		||||
	DumpAllResources := func() {
 | 
			
		||||
		deploy := &apps.DeploymentList{}
 | 
			
		||||
		k8sClient.List(context.TODO(), deploy, client.InNamespace(namespace))
 | 
			
		||||
		fmt.Println(util.DumpJSON(deploy))
 | 
			
		||||
		rs := &apps.ReplicaSetList{}
 | 
			
		||||
		k8sClient.List(context.TODO(), rs, client.InNamespace(namespace))
 | 
			
		||||
		fmt.Println(util.DumpJSON(rs))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defaultRetry := wait.Backoff{
 | 
			
		||||
		Steps:    10,
 | 
			
		||||
		Duration: 10 * time.Millisecond,
 | 
			
		||||
| 
						 | 
				
			
			@ -132,7 +142,12 @@ var _ = SIGDescribe("Advanced Deployment", func() {
 | 
			
		|||
 | 
			
		||||
	CheckReplicas := func(deployment *apps.Deployment, replicas, available, updated int32) {
 | 
			
		||||
		var clone *apps.Deployment
 | 
			
		||||
		start := time.Now()
 | 
			
		||||
		Eventually(func() bool {
 | 
			
		||||
			if start.Add(time.Minute * 2).Before(time.Now()) {
 | 
			
		||||
				DumpAllResources()
 | 
			
		||||
				Expect(true).Should(BeFalse())
 | 
			
		||||
			}
 | 
			
		||||
			clone = &apps.Deployment{}
 | 
			
		||||
			err := GetObject(deployment.Namespace, deployment.Name, clone)
 | 
			
		||||
			Expect(err).NotTo(HaveOccurred())
 | 
			
		||||
| 
						 | 
				
			
			@ -239,6 +254,7 @@ var _ = SIGDescribe("Advanced Deployment", func() {
 | 
			
		|||
			deployment.Namespace = namespace
 | 
			
		||||
			Expect(ReadYamlToObject("./test_data/deployment/deployment.yaml", deployment)).ToNot(HaveOccurred())
 | 
			
		||||
			CreateObject(deployment)
 | 
			
		||||
			CheckReplicas(deployment, 5, 5, 5)
 | 
			
		||||
			UpdateDeployment(deployment, "version2")
 | 
			
		||||
			UpdatePartitionWithCheck(deployment, intstr.FromInt(0))
 | 
			
		||||
			UpdatePartitionWithCheck(deployment, intstr.FromInt(1))
 | 
			
		||||
| 
						 | 
				
			
			@ -255,6 +271,7 @@ var _ = SIGDescribe("Advanced Deployment", func() {
 | 
			
		|||
			Expect(ReadYamlToObject("./test_data/deployment/deployment.yaml", deployment)).ToNot(HaveOccurred())
 | 
			
		||||
			deployment.Spec.Replicas = pointer.Int32(10)
 | 
			
		||||
			CreateObject(deployment)
 | 
			
		||||
			CheckReplicas(deployment, 10, 10, 10)
 | 
			
		||||
			UpdateDeployment(deployment, "version2")
 | 
			
		||||
			UpdatePartitionWithCheck(deployment, intstr.FromString("0%"))
 | 
			
		||||
			UpdatePartitionWithCheck(deployment, intstr.FromString("40%"))
 | 
			
		||||
| 
						 | 
				
			
			@ -287,6 +304,7 @@ var _ = SIGDescribe("Advanced Deployment", func() {
 | 
			
		|||
				`{"rollingStyle":"Partition","rollingUpdate":{"maxUnavailable":1,"maxSurge":0}}`
 | 
			
		||||
			deployment.Spec.MinReadySeconds = 10
 | 
			
		||||
			CreateObject(deployment)
 | 
			
		||||
			CheckReplicas(deployment, 5, 5, 5)
 | 
			
		||||
			UpdateDeployment(deployment, "version2")
 | 
			
		||||
			UpdatePartitionWithCheck(deployment, intstr.FromInt(0))
 | 
			
		||||
			UpdatePartitionWithoutCheck(deployment, intstr.FromInt(3))
 | 
			
		||||
| 
						 | 
				
			
			@ -303,6 +321,7 @@ var _ = SIGDescribe("Advanced Deployment", func() {
 | 
			
		|||
			deployment.Namespace = namespace
 | 
			
		||||
			Expect(ReadYamlToObject("./test_data/deployment/deployment.yaml", deployment)).ToNot(HaveOccurred())
 | 
			
		||||
			CreateObject(deployment)
 | 
			
		||||
			CheckReplicas(deployment, 5, 5, 5)
 | 
			
		||||
			UpdateDeployment(deployment, "version2")
 | 
			
		||||
			UpdatePartitionWithCheck(deployment, intstr.FromInt(0))
 | 
			
		||||
			UpdatePartitionWithCheck(deployment, intstr.FromInt(2))
 | 
			
		||||
| 
						 | 
				
			
			@ -317,6 +336,7 @@ var _ = SIGDescribe("Advanced Deployment", func() {
 | 
			
		|||
			deployment.Namespace = namespace
 | 
			
		||||
			Expect(ReadYamlToObject("./test_data/deployment/deployment.yaml", deployment)).ToNot(HaveOccurred())
 | 
			
		||||
			CreateObject(deployment)
 | 
			
		||||
			CheckReplicas(deployment, 5, 5, 5)
 | 
			
		||||
			UpdateDeployment(deployment, "version2")
 | 
			
		||||
			UpdatePartitionWithCheck(deployment, intstr.FromInt(0))
 | 
			
		||||
			UpdatePartitionWithCheck(deployment, intstr.FromInt(2))
 | 
			
		||||
| 
						 | 
				
			
			@ -335,6 +355,7 @@ var _ = SIGDescribe("Advanced Deployment", func() {
 | 
			
		|||
			Expect(ReadYamlToObject("./test_data/deployment/deployment.yaml", deployment)).ToNot(HaveOccurred())
 | 
			
		||||
			deployment.Annotations["rollouts.kruise.io/deployment-strategy"] = `{"rollingUpdate":{"maxUnavailable":0,"maxSurge":1}}`
 | 
			
		||||
			CreateObject(deployment)
 | 
			
		||||
			CheckReplicas(deployment, 5, 5, 5)
 | 
			
		||||
			UpdateDeployment(deployment, "version2", "busybox:not-exists")
 | 
			
		||||
			UpdatePartitionWithoutCheck(deployment, intstr.FromInt(1))
 | 
			
		||||
			CheckReplicas(deployment, 6, 5, 1)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -215,18 +215,25 @@ var _ = SIGDescribe("Rollout", func() {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	ResumeRolloutCanary := func(name string) {
 | 
			
		||||
		clone := &v1alpha1.Rollout{}
 | 
			
		||||
		Expect(GetObject(name, clone)).NotTo(HaveOccurred())
 | 
			
		||||
		currentIndex := clone.Status.CanaryStatus.CurrentStepIndex
 | 
			
		||||
		Eventually(func() bool {
 | 
			
		||||
			clone := &v1alpha1.Rollout{}
 | 
			
		||||
			Expect(GetObject(name, clone)).NotTo(HaveOccurred())
 | 
			
		||||
			if clone.Status.CanaryStatus.CurrentStepState != v1alpha1.CanaryStepStatePaused {
 | 
			
		||||
			if clone.Status.CanaryStatus.CurrentStepIndex == currentIndex && clone.Status.CanaryStatus.CurrentStepState == v1alpha1.CanaryStepStatePaused {
 | 
			
		||||
				klog.Info("patch to stepReady")
 | 
			
		||||
				body := fmt.Sprintf(`{"status":{"canaryStatus":{"currentStepState":"%s"}}}`, v1alpha1.CanaryStepStateReady)
 | 
			
		||||
				Expect(k8sClient.Status().Patch(context.TODO(), clone, client.RawPatch(types.MergePatchType, []byte(body)))).NotTo(HaveOccurred())
 | 
			
		||||
				return false
 | 
			
		||||
			} else {
 | 
			
		||||
				fmt.Println("resume rollout success, and CurrentStepState", util.DumpJSON(clone.Status))
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			body := fmt.Sprintf(`{"status":{"canaryStatus":{"currentStepState":"%s"}}}`, v1alpha1.CanaryStepStateReady)
 | 
			
		||||
			Expect(k8sClient.Status().Patch(context.TODO(), clone, client.RawPatch(types.MergePatchType, []byte(body)))).NotTo(HaveOccurred())
 | 
			
		||||
			return false
 | 
			
		||||
		}, 10*time.Second, time.Second).Should(BeTrue())
 | 
			
		||||
			// interval was critical before:
 | 
			
		||||
			// too small: StepReady could be overidden by StepPaused
 | 
			
		||||
			// too big: StepReady could progress to StepPaused of next Step
 | 
			
		||||
		}, 10*time.Second, 2*time.Second).Should(BeTrue())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ResumeRolloutCanaryV1beta1 := func(name string) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue