new v1beta1 apis (#184)

Signed-off-by: liheng.zms <liheng.zms@alibaba-inc.com>
This commit is contained in:
berg 2023-12-04 14:56:47 +08:00 committed by GitHub
parent 07b7f20f6a
commit 9dcf3659d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 1461 additions and 603 deletions

458
api/v1alpha1/conversion.go Normal file
View File

@ -0,0 +1,458 @@
/*
Copyright 2023 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
"fmt"
"github.com/openkruise/rollouts/api/v1beta1"
"k8s.io/apimachinery/pkg/util/intstr"
utilpointer "k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/conversion"
)
func (src *Rollout) ConvertTo(dst conversion.Hub) error {
switch t := dst.(type) {
case *v1beta1.Rollout:
obj := dst.(*v1beta1.Rollout)
obj.ObjectMeta = src.ObjectMeta
obj.Spec = v1beta1.RolloutSpec{}
srcSpec := src.Spec
obj.Spec.WorkloadRef = v1beta1.ObjectRef{
APIVersion: srcSpec.ObjectRef.WorkloadRef.APIVersion,
Kind: srcSpec.ObjectRef.WorkloadRef.Kind,
Name: srcSpec.ObjectRef.WorkloadRef.Name,
}
obj.Spec.Disabled = srcSpec.Disabled
obj.Spec.Strategy = v1beta1.RolloutStrategy{
Paused: srcSpec.Strategy.Paused,
Canary: &v1beta1.CanaryStrategy{
FailureThreshold: srcSpec.Strategy.Canary.FailureThreshold,
},
}
for _, step := range srcSpec.Strategy.Canary.Steps {
o := v1beta1.CanaryStep{
TrafficRoutingStrategy: ConversionToV1beta1TrafficRoutingStrategy(step.TrafficRoutingStrategy),
Replicas: step.Replicas,
Pause: v1beta1.RolloutPause{Duration: step.Pause.Duration},
}
if step.Replicas == nil && step.Weight != nil {
o.Replicas = &intstr.IntOrString{
Type: intstr.String,
StrVal: fmt.Sprintf("%d", *step.Weight) + "%",
}
}
obj.Spec.Strategy.Canary.Steps = append(obj.Spec.Strategy.Canary.Steps, o)
}
for _, ref := range srcSpec.Strategy.Canary.TrafficRoutings {
o := ConversionToV1beta1TrafficRoutingRef(ref)
obj.Spec.Strategy.Canary.TrafficRoutings = append(obj.Spec.Strategy.Canary.TrafficRoutings, o)
}
if srcSpec.Strategy.Canary.PatchPodTemplateMetadata != nil {
obj.Spec.Strategy.Canary.PatchPodTemplateMetadata = &v1beta1.PatchPodTemplateMetadata{
Annotations: map[string]string{},
Labels: map[string]string{},
}
for k, v := range srcSpec.Strategy.Canary.PatchPodTemplateMetadata.Annotations {
obj.Spec.Strategy.Canary.PatchPodTemplateMetadata.Annotations[k] = v
}
for k, v := range srcSpec.Strategy.Canary.PatchPodTemplateMetadata.Labels {
obj.Spec.Strategy.Canary.PatchPodTemplateMetadata.Labels[k] = v
}
}
if src.Annotations[RolloutStyleAnnotation] != string(PartitionRollingStyle) {
obj.Spec.Strategy.Canary.EnableExtraWorkloadForCanary = true
}
if src.Annotations[TrafficRoutingAnnotation] != "" {
obj.Spec.Strategy.Canary.TrafficRoutingRef = src.Annotations[TrafficRoutingAnnotation]
}
// status
obj.Status = v1beta1.RolloutStatus{
ObservedGeneration: src.Status.ObservedGeneration,
Phase: v1beta1.RolloutPhase(src.Status.Phase),
Message: src.Status.Message,
}
for _, cond := range src.Status.Conditions {
o := v1beta1.RolloutCondition{
Type: v1beta1.RolloutConditionType(cond.Type),
Status: cond.Status,
LastUpdateTime: cond.LastUpdateTime,
LastTransitionTime: cond.LastTransitionTime,
Reason: cond.Reason,
Message: cond.Message,
}
obj.Status.Conditions = append(obj.Status.Conditions, o)
}
if src.Status.CanaryStatus == nil {
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,
}
return nil
default:
return fmt.Errorf("unsupported type %v", t)
}
}
func ConversionToV1beta1TrafficRoutingRef(src TrafficRoutingRef) (dst v1beta1.TrafficRoutingRef) {
dst.Service = src.Service
dst.GracePeriodSeconds = src.GracePeriodSeconds
if src.Ingress != nil {
dst.Ingress = &v1beta1.IngressTrafficRouting{
ClassType: src.Ingress.ClassType,
Name: src.Ingress.Name,
}
}
if src.Gateway != nil {
dst.Gateway = &v1beta1.GatewayTrafficRouting{
HTTPRouteName: src.Gateway.HTTPRouteName,
}
}
for _, ref := range src.CustomNetworkRefs {
obj := v1beta1.ObjectRef{
APIVersion: ref.APIVersion,
Kind: ref.Kind,
Name: ref.Name,
}
dst.CustomNetworkRefs = append(dst.CustomNetworkRefs, obj)
}
return dst
}
func ConversionToV1beta1TrafficRoutingStrategy(src TrafficRoutingStrategy) (dst v1beta1.TrafficRoutingStrategy) {
if src.Weight != nil {
dst.Traffic = utilpointer.StringPtr(fmt.Sprintf("%d", *src.Weight) + "%")
}
dst.RequestHeaderModifier = src.RequestHeaderModifier
for _, match := range src.Matches {
obj := v1beta1.HttpRouteMatch{
Headers: match.Headers,
}
dst.Matches = append(dst.Matches, obj)
}
return dst
}
func (dst *Rollout) ConvertFrom(src conversion.Hub) error {
switch t := src.(type) {
case *v1beta1.Rollout:
srcV1beta1 := src.(*v1beta1.Rollout)
dst.ObjectMeta = srcV1beta1.ObjectMeta
// spec
dst.Spec = RolloutSpec{
ObjectRef: ObjectRef{
WorkloadRef: &WorkloadRef{
APIVersion: srcV1beta1.Spec.WorkloadRef.APIVersion,
Kind: srcV1beta1.Spec.WorkloadRef.Kind,
Name: srcV1beta1.Spec.WorkloadRef.Name,
},
},
Strategy: RolloutStrategy{
Paused: srcV1beta1.Spec.Strategy.Paused,
Canary: &CanaryStrategy{
FailureThreshold: srcV1beta1.Spec.Strategy.Canary.FailureThreshold,
},
},
Disabled: srcV1beta1.Spec.Disabled,
}
for _, step := range srcV1beta1.Spec.Strategy.Canary.Steps {
obj := CanaryStep{
TrafficRoutingStrategy: ConversionToV1alpha1TrafficRoutingStrategy(step.TrafficRoutingStrategy),
Replicas: step.Replicas,
Pause: RolloutPause{Duration: step.Pause.Duration},
}
dst.Spec.Strategy.Canary.Steps = append(dst.Spec.Strategy.Canary.Steps, obj)
}
for _, ref := range srcV1beta1.Spec.Strategy.Canary.TrafficRoutings {
obj := ConversionToV1alpha1TrafficRoutingRef(ref)
dst.Spec.Strategy.Canary.TrafficRoutings = append(dst.Spec.Strategy.Canary.TrafficRoutings, obj)
}
if srcV1beta1.Spec.Strategy.Canary.PatchPodTemplateMetadata != nil {
dst.Spec.Strategy.Canary.PatchPodTemplateMetadata = &PatchPodTemplateMetadata{
Annotations: map[string]string{},
Labels: map[string]string{},
}
for k, v := range srcV1beta1.Spec.Strategy.Canary.PatchPodTemplateMetadata.Annotations {
dst.Spec.Strategy.Canary.PatchPodTemplateMetadata.Annotations[k] = v
}
for k, v := range srcV1beta1.Spec.Strategy.Canary.PatchPodTemplateMetadata.Labels {
dst.Spec.Strategy.Canary.PatchPodTemplateMetadata.Labels[k] = v
}
}
if dst.Annotations == nil {
dst.Annotations = map[string]string{}
}
if srcV1beta1.Spec.Strategy.Canary.EnableExtraWorkloadForCanary {
dst.Annotations[RolloutStyleAnnotation] = string(CanaryRollingStyle)
} else {
dst.Annotations[RolloutStyleAnnotation] = string(PartitionRollingStyle)
}
if srcV1beta1.Spec.Strategy.Canary.TrafficRoutingRef != "" {
dst.Annotations[TrafficRoutingAnnotation] = srcV1beta1.Spec.Strategy.Canary.TrafficRoutingRef
}
// status
dst.Status = RolloutStatus{
ObservedGeneration: srcV1beta1.Status.ObservedGeneration,
Phase: RolloutPhase(srcV1beta1.Status.Phase),
Message: srcV1beta1.Status.Message,
}
for _, cond := range srcV1beta1.Status.Conditions {
obj := RolloutCondition{
Type: RolloutConditionType(cond.Type),
Status: cond.Status,
LastUpdateTime: cond.LastUpdateTime,
LastTransitionTime: cond.LastTransitionTime,
Reason: cond.Reason,
Message: cond.Message,
}
dst.Status.Conditions = append(dst.Status.Conditions, obj)
}
if srcV1beta1.Status.CanaryStatus == nil {
return nil
}
dst.Status.CanaryStatus = &CanaryStatus{
ObservedWorkloadGeneration: srcV1beta1.Status.CanaryStatus.ObservedWorkloadGeneration,
ObservedRolloutID: srcV1beta1.Status.CanaryStatus.ObservedRolloutID,
RolloutHash: srcV1beta1.Status.CanaryStatus.RolloutHash,
StableRevision: srcV1beta1.Status.CanaryStatus.StableRevision,
CanaryRevision: srcV1beta1.Status.CanaryStatus.CanaryRevision,
PodTemplateHash: srcV1beta1.Status.CanaryStatus.PodTemplateHash,
CanaryReplicas: srcV1beta1.Status.CanaryStatus.CanaryReplicas,
CanaryReadyReplicas: srcV1beta1.Status.CanaryStatus.CanaryReadyReplicas,
CurrentStepIndex: srcV1beta1.Status.CanaryStatus.CurrentStepIndex,
CurrentStepState: CanaryStepState(srcV1beta1.Status.CanaryStatus.CurrentStepState),
Message: srcV1beta1.Status.CanaryStatus.Message,
LastUpdateTime: srcV1beta1.Status.CanaryStatus.LastUpdateTime,
}
return nil
default:
return fmt.Errorf("unsupported type %v", t)
}
}
func ConversionToV1alpha1TrafficRoutingStrategy(src v1beta1.TrafficRoutingStrategy) (dst TrafficRoutingStrategy) {
if src.Traffic != nil {
is := intstr.FromString(*src.Traffic)
weight, _ := intstr.GetScaledValueFromIntOrPercent(&is, 100, true)
dst.Weight = utilpointer.Int32(int32(weight))
}
dst.RequestHeaderModifier = src.RequestHeaderModifier
for _, match := range src.Matches {
obj := HttpRouteMatch{
Headers: match.Headers,
}
dst.Matches = append(dst.Matches, obj)
}
return dst
}
func ConversionToV1alpha1TrafficRoutingRef(src v1beta1.TrafficRoutingRef) (dst TrafficRoutingRef) {
dst.Service = src.Service
dst.GracePeriodSeconds = src.GracePeriodSeconds
if src.Ingress != nil {
dst.Ingress = &IngressTrafficRouting{
ClassType: src.Ingress.ClassType,
Name: src.Ingress.Name,
}
}
if src.Gateway != nil {
dst.Gateway = &GatewayTrafficRouting{
HTTPRouteName: src.Gateway.HTTPRouteName,
}
}
for _, ref := range src.CustomNetworkRefs {
obj := CustomNetworkRef{
APIVersion: ref.APIVersion,
Kind: ref.Kind,
Name: ref.Name,
}
dst.CustomNetworkRefs = append(dst.CustomNetworkRefs, obj)
}
return dst
}
func (src *BatchRelease) ConvertTo(dst conversion.Hub) error {
switch t := dst.(type) {
case *v1beta1.BatchRelease:
obj := dst.(*v1beta1.BatchRelease)
obj.ObjectMeta = src.ObjectMeta
obj.Spec = v1beta1.BatchReleaseSpec{}
srcSpec := src.Spec
obj.Spec.WorkloadRef = v1beta1.ObjectRef{
APIVersion: srcSpec.TargetRef.WorkloadRef.APIVersion,
Kind: srcSpec.TargetRef.WorkloadRef.Kind,
Name: srcSpec.TargetRef.WorkloadRef.Name,
}
obj.Spec.ReleasePlan = v1beta1.ReleasePlan{
BatchPartition: srcSpec.ReleasePlan.BatchPartition,
RolloutID: srcSpec.ReleasePlan.RolloutID,
FailureThreshold: srcSpec.ReleasePlan.FailureThreshold,
FinalizingPolicy: v1beta1.FinalizingPolicyType(srcSpec.ReleasePlan.FinalizingPolicy),
}
for _, batch := range srcSpec.ReleasePlan.Batches {
o := v1beta1.ReleaseBatch{
CanaryReplicas: batch.CanaryReplicas,
}
obj.Spec.ReleasePlan.Batches = append(obj.Spec.ReleasePlan.Batches, o)
}
if srcSpec.ReleasePlan.PatchPodTemplateMetadata != nil {
obj.Spec.ReleasePlan.PatchPodTemplateMetadata = &v1beta1.PatchPodTemplateMetadata{
Annotations: map[string]string{},
Labels: map[string]string{},
}
for k, v := range srcSpec.ReleasePlan.PatchPodTemplateMetadata.Annotations {
obj.Spec.ReleasePlan.PatchPodTemplateMetadata.Annotations[k] = v
}
for k, v := range srcSpec.ReleasePlan.PatchPodTemplateMetadata.Labels {
obj.Spec.ReleasePlan.PatchPodTemplateMetadata.Labels[k] = v
}
}
if src.Annotations[RolloutStyleAnnotation] != string(PartitionRollingStyle) {
obj.Spec.ReleasePlan.EnableExtraWorkloadForCanary = true
}
// status
obj.Status = v1beta1.BatchReleaseStatus{
StableRevision: src.Status.StableRevision,
UpdateRevision: src.Status.UpdateRevision,
ObservedGeneration: src.Status.ObservedGeneration,
ObservedRolloutID: src.Status.ObservedRolloutID,
ObservedWorkloadReplicas: src.Status.ObservedWorkloadReplicas,
ObservedReleasePlanHash: src.Status.ObservedReleasePlanHash,
CollisionCount: src.Status.CollisionCount,
Phase: v1beta1.RolloutPhase(src.Status.Phase),
}
for _, cond := range src.Status.Conditions {
o := v1beta1.RolloutCondition{
Type: v1beta1.RolloutConditionType(cond.Type),
Status: cond.Status,
LastUpdateTime: cond.LastUpdateTime,
LastTransitionTime: cond.LastTransitionTime,
Reason: cond.Reason,
Message: cond.Message,
}
obj.Status.Conditions = append(obj.Status.Conditions, o)
}
obj.Status.CanaryStatus = v1beta1.BatchReleaseCanaryStatus{
CurrentBatchState: v1beta1.BatchReleaseBatchStateType(src.Status.CanaryStatus.CurrentBatchState),
CurrentBatch: src.Status.CanaryStatus.CurrentBatch,
BatchReadyTime: src.Status.CanaryStatus.BatchReadyTime,
UpdatedReplicas: src.Status.CanaryStatus.UpdatedReplicas,
UpdatedReadyReplicas: src.Status.CanaryStatus.UpdatedReadyReplicas,
NoNeedUpdateReplicas: src.Status.CanaryStatus.NoNeedUpdateReplicas,
}
return nil
default:
return fmt.Errorf("unsupported type %v", t)
}
}
func (dst *BatchRelease) ConvertFrom(src conversion.Hub) error {
switch t := src.(type) {
case *v1beta1.BatchRelease:
srcV1beta1 := src.(*v1beta1.BatchRelease)
dst.ObjectMeta = srcV1beta1.ObjectMeta
dst.Spec = BatchReleaseSpec{}
srcSpec := srcV1beta1.Spec
dst.Spec.TargetRef.WorkloadRef = &WorkloadRef{
APIVersion: srcSpec.WorkloadRef.APIVersion,
Kind: srcSpec.WorkloadRef.Kind,
Name: srcSpec.WorkloadRef.Name,
}
dst.Spec.ReleasePlan = ReleasePlan{
BatchPartition: srcSpec.ReleasePlan.BatchPartition,
RolloutID: srcSpec.ReleasePlan.RolloutID,
FailureThreshold: srcSpec.ReleasePlan.FailureThreshold,
FinalizingPolicy: FinalizingPolicyType(srcSpec.ReleasePlan.FinalizingPolicy),
}
for _, batch := range srcSpec.ReleasePlan.Batches {
obj := ReleaseBatch{
CanaryReplicas: batch.CanaryReplicas,
}
dst.Spec.ReleasePlan.Batches = append(dst.Spec.ReleasePlan.Batches, obj)
}
if srcSpec.ReleasePlan.PatchPodTemplateMetadata != nil {
dst.Spec.ReleasePlan.PatchPodTemplateMetadata = &PatchPodTemplateMetadata{
Annotations: map[string]string{},
Labels: map[string]string{},
}
for k, v := range srcSpec.ReleasePlan.PatchPodTemplateMetadata.Annotations {
dst.Spec.ReleasePlan.PatchPodTemplateMetadata.Annotations[k] = v
}
for k, v := range srcSpec.ReleasePlan.PatchPodTemplateMetadata.Labels {
dst.Spec.ReleasePlan.PatchPodTemplateMetadata.Labels[k] = v
}
}
if dst.Annotations == nil {
dst.Annotations = map[string]string{}
}
if srcV1beta1.Spec.ReleasePlan.EnableExtraWorkloadForCanary {
dst.Annotations[RolloutStyleAnnotation] = string(CanaryRollingStyle)
} else {
dst.Annotations[RolloutStyleAnnotation] = string(PartitionRollingStyle)
}
// status
dst.Status = BatchReleaseStatus{
StableRevision: srcV1beta1.Status.StableRevision,
UpdateRevision: srcV1beta1.Status.UpdateRevision,
ObservedGeneration: srcV1beta1.Status.ObservedGeneration,
ObservedRolloutID: srcV1beta1.Status.ObservedRolloutID,
ObservedWorkloadReplicas: srcV1beta1.Status.ObservedWorkloadReplicas,
ObservedReleasePlanHash: srcV1beta1.Status.ObservedReleasePlanHash,
CollisionCount: srcV1beta1.Status.CollisionCount,
Phase: RolloutPhase(srcV1beta1.Status.Phase),
}
for _, cond := range srcV1beta1.Status.Conditions {
obj := RolloutCondition{
Type: RolloutConditionType(cond.Type),
Status: cond.Status,
LastUpdateTime: cond.LastUpdateTime,
LastTransitionTime: cond.LastTransitionTime,
Reason: cond.Reason,
Message: cond.Message,
}
dst.Status.Conditions = append(dst.Status.Conditions, obj)
}
dst.Status.CanaryStatus = BatchReleaseCanaryStatus{
CurrentBatchState: BatchReleaseBatchStateType(srcV1beta1.Status.CanaryStatus.CurrentBatchState),
CurrentBatch: srcV1beta1.Status.CanaryStatus.CurrentBatch,
BatchReadyTime: srcV1beta1.Status.CanaryStatus.BatchReadyTime,
UpdatedReplicas: srcV1beta1.Status.CanaryStatus.UpdatedReplicas,
UpdatedReadyReplicas: srcV1beta1.Status.CanaryStatus.UpdatedReadyReplicas,
NoNeedUpdateReplicas: srcV1beta1.Status.CanaryStatus.NoNeedUpdateReplicas,
}
return nil
default:
return fmt.Errorf("unsupported type %v", t)
}
}

View File

@ -1,3 +1,19 @@
/*
Copyright 2023 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1 package v1alpha1
import ( import (

View File

@ -54,6 +54,10 @@ type ReleasePlan struct {
// only support for canary deployment // only support for canary deployment
// +optional // +optional
PatchPodTemplateMetadata *PatchPodTemplateMetadata `json:"patchPodTemplateMetadata,omitempty"` 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
EnableExtraWorkloadForCanary bool `json:"enableExtraWorkloadForCanary"`
} }
type FinalizingPolicyType string type FinalizingPolicyType string

View File

@ -41,8 +41,9 @@ type BatchRelease struct {
// BatchReleaseSpec defines how to describe an update between different compRevision // BatchReleaseSpec defines how to describe an update between different compRevision
type BatchReleaseSpec struct { type BatchReleaseSpec struct {
// TargetRef contains the GVK and name of the workload that we need to upgrade to. // WorkloadRef contains enough information to let you identify a workload for Rollout
TargetRef ObjectRef `json:"targetReference"` // Batch release of the bypass
WorkloadRef ObjectRef `json:"workloadRef,omitempty"`
// ReleasePlan is the details on how to rollout the resources // ReleasePlan is the details on how to rollout the resources
ReleasePlan ReleasePlan `json:"releasePlan"` ReleasePlan ReleasePlan `json:"releasePlan"`
} }

View File

@ -17,7 +17,6 @@ limitations under the License.
package v1beta1 package v1beta1
import ( import (
"github.com/openkruise/rollouts/api/v1alpha1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
@ -42,51 +41,25 @@ const (
// RollbackInBatchAnnotation is set to rollout annotations. // RollbackInBatchAnnotation is set to rollout annotations.
// RollbackInBatchAnnotation allow use disable quick rollback, and will roll back in batch style. // RollbackInBatchAnnotation allow use disable quick rollback, and will roll back in batch style.
RollbackInBatchAnnotation = "rollouts.kruise.io/rollback-in-batch" RollbackInBatchAnnotation = "rollouts.kruise.io/rollback-in-batch"
// RolloutStyleAnnotation define the rolling behavior for Deployment.
// must be "partition" or "canary":
// * "partition" means rolling in batches just like CloneSet, and will NOT create any extra Workload;
// * "canary" means rolling in canary way, and will create a canary Workload.
// Currently, only Deployment support both "partition" and "canary" rolling styles.
// For other workload types, they only support "partition" styles.
// Defaults to "canary" to Deployment.
// Defaults to "partition" to the others.
RolloutStyleAnnotation = "rollouts.kruise.io/rolling-style"
// TrafficRoutingAnnotation is the TrafficRouting Name, and it is the Rollout's TrafficRouting.
// The Rollout release will trigger the TrafficRouting release. For example:
// A microservice consists of three applications, and the invocation relationship is as follows: a -> b -> c,
// and application(a, b, c)'s gateway is trafficRouting. Any application(a, b or b) release will trigger TrafficRouting release.
TrafficRoutingAnnotation = "rollouts.kruise.io/trafficrouting"
) )
// RolloutSpec defines the desired state of Rollout // RolloutSpec defines the desired state of Rollout
type RolloutSpec struct { type RolloutSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file // Important: Run "make" to regenerate code after modifying this file
// ObjectRef indicates workload // WorkloadRef contains enough information to let you identify a workload for Rollout
ObjectRef ObjectRef `json:"objectRef"` // Batch release of the bypass
WorkloadRef ObjectRef `json:"workloadRef"`
// rollout strategy // rollout strategy
Strategy RolloutStrategy `json:"strategy"` Strategy RolloutStrategy `json:"strategy"`
// DeprecatedRolloutID is the deprecated field.
// It is recommended that configure RolloutId in workload.annotations[rollouts.kruise.io/rollout-id].
// RolloutID should be changed before each workload revision publication.
// It is to distinguish consecutive multiple workload publications and rollout progress.
DeprecatedRolloutID string `json:"rolloutID,omitempty"`
// if a rollout disabled, then the rollout would not watch changes of workload // if a rollout disabled, then the rollout would not watch changes of workload
//+kubebuilder:validation:Optional //+kubebuilder:validation:Optional
//+kubebuilder:default=false //+kubebuilder:default=false
Disabled bool `json:"disabled"` Disabled bool `json:"disabled"`
} }
// ObjectRef holds a references to the Kubernetes object
type ObjectRef struct { type ObjectRef struct {
// WorkloadRef contains enough information to let you identify a workload for Rollout
// Batch release of the bypass
WorkloadRef *WorkloadRef `json:"workloadRef,omitempty"`
}
// WorkloadRef holds a references to the Kubernetes object
type WorkloadRef struct {
// API Version of the referent // API Version of the referent
APIVersion string `json:"apiVersion"` APIVersion string `json:"apiVersion"`
// Kind of the referent // Kind of the referent
@ -111,7 +84,7 @@ type CanaryStrategy struct {
Steps []CanaryStep `json:"steps,omitempty"` Steps []CanaryStep `json:"steps,omitempty"`
// TrafficRoutings hosts all the supported service meshes supported to enable more fine-grained traffic routing // TrafficRoutings hosts all the supported service meshes supported to enable more fine-grained traffic routing
// and current only support one TrafficRouting // and current only support one TrafficRouting
TrafficRoutings []v1alpha1.TrafficRoutingRef `json:"trafficRoutings,omitempty"` TrafficRoutings []TrafficRoutingRef `json:"trafficRoutings,omitempty"`
// FailureThreshold indicates how many failed pods can be tolerated in all upgraded pods. // FailureThreshold indicates how many failed pods can be tolerated in all upgraded pods.
// Only when FailureThreshold are satisfied, Rollout can enter ready state. // Only when FailureThreshold are satisfied, Rollout can enter ready state.
// If FailureThreshold is nil, Rollout will use the MaxUnavailable of workload as its // If FailureThreshold is nil, Rollout will use the MaxUnavailable of workload as its
@ -122,6 +95,12 @@ type CanaryStrategy struct {
// only support for canary deployment // only support for canary deployment
// +optional // +optional
PatchPodTemplateMetadata *PatchPodTemplateMetadata `json:"patchPodTemplateMetadata,omitempty"` 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
EnableExtraWorkloadForCanary bool `json:"enableExtraWorkloadForCanary,omitempty"`
// TrafficRoutingRef is TrafficRouting's Name
TrafficRoutingRef string `json:"trafficRoutingRef,omitempty"`
} }
type PatchPodTemplateMetadata struct { type PatchPodTemplateMetadata struct {
@ -133,7 +112,7 @@ type PatchPodTemplateMetadata struct {
// CanaryStep defines a step of a canary workload. // CanaryStep defines a step of a canary workload.
type CanaryStep struct { type CanaryStep struct {
v1alpha1.TrafficRoutingStrategy `json:",inline"` TrafficRoutingStrategy `json:",inline"`
// Replicas is the number of expected canary pods in this batch // 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. // it can be an absolute number (ex: 5) or a percentage of total pods.
Replicas *intstr.IntOrString `json:"replicas,omitempty"` Replicas *intstr.IntOrString `json:"replicas,omitempty"`
@ -142,6 +121,35 @@ type CanaryStep struct {
Pause RolloutPause `json:"pause,omitempty"` Pause RolloutPause `json:"pause,omitempty"`
} }
type TrafficRoutingStrategy struct {
// Traffic indicate how many percentage of traffic the canary pods should receive
// +optional
Traffic *string `json:"traffic,omitempty"`
// Set overwrites the request with the given header (name, value)
// before the action.
//
// Input:
// GET /foo HTTP/1.1
// my-header: foo
//
// requestHeaderModifier:
// set:
// - name: "my-header"
// value: "bar"
//
// Output:
// GET /foo HTTP/1.1
// my-header: bar
//
// +optional
RequestHeaderModifier *gatewayv1alpha2.HTTPRequestHeaderFilter `json:"requestHeaderModifier,omitempty"`
// 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.
Matches []HttpRouteMatch `json:"matches,omitempty"`
}
type HttpRouteMatch struct { type HttpRouteMatch struct {
// Headers specifies HTTP request header matchers. Multiple match values are // Headers specifies HTTP request header matchers. Multiple match values are
// ANDed together, meaning, a request must match all the specified headers // ANDed together, meaning, a request must match all the specified headers

View File

@ -0,0 +1,50 @@
/*
Copyright 2023 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1beta1
// TrafficRoutingRef hosts all the different configuration for supported service meshes to enable more fine-grained traffic routing
type TrafficRoutingRef struct {
// Service holds the name of a service which selects pods with stable version and don't select any pods with canary version.
Service string `json:"service"`
// Optional duration in seconds the traffic provider(e.g. nginx ingress controller) consumes the service, ingress configuration changes gracefully.
GracePeriodSeconds int32 `json:"gracePeriodSeconds,omitempty"`
// Ingress holds Ingress specific configuration to route traffic, e.g. Nginx, Alb.
Ingress *IngressTrafficRouting `json:"ingress,omitempty"`
// Gateway holds Gateway specific configuration to route traffic
// Gateway configuration only supports >= v0.4.0 (v1alpha2).
Gateway *GatewayTrafficRouting `json:"gateway,omitempty"`
// CustomNetworkRefs hold a list of custom providers to route traffic
CustomNetworkRefs []ObjectRef `json:"customNetworkRefs,omitempty"`
}
// IngressTrafficRouting configuration for ingress controller to control traffic routing
type IngressTrafficRouting struct {
// ClassType refers to the type of `Ingress`.
// current support nginx, aliyun-alb. default is nginx.
// +optional
ClassType string `json:"classType,omitempty"`
// Name refers to the name of an `Ingress` resource in the same namespace as the `Rollout`
Name string `json:"name"`
}
// GatewayTrafficRouting configuration for gateway api
type GatewayTrafficRouting struct {
// HTTPRouteName refers to the name of an `HTTPRoute` resource in the same namespace as the `Rollout`
HTTPRouteName *string `json:"httpRouteName,omitempty"`
// TCPRouteName *string `json:"tcpRouteName,omitempty"`
// UDPRouteName *string `json:"udpRouteName,omitempty"`
}

View File

@ -22,7 +22,6 @@ limitations under the License.
package v1beta1 package v1beta1
import ( import (
"github.com/openkruise/rollouts/api/v1alpha1"
"k8s.io/api/apps/v1" "k8s.io/api/apps/v1"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
@ -115,7 +114,7 @@ func (in *BatchReleaseList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BatchReleaseSpec) DeepCopyInto(out *BatchReleaseSpec) { func (in *BatchReleaseSpec) DeepCopyInto(out *BatchReleaseSpec) {
*out = *in *out = *in
in.TargetRef.DeepCopyInto(&out.TargetRef) out.WorkloadRef = in.WorkloadRef
in.ReleasePlan.DeepCopyInto(&out.ReleasePlan) in.ReleasePlan.DeepCopyInto(&out.ReleasePlan)
} }
@ -210,7 +209,7 @@ func (in *CanaryStrategy) DeepCopyInto(out *CanaryStrategy) {
} }
if in.TrafficRoutings != nil { if in.TrafficRoutings != nil {
in, out := &in.TrafficRoutings, &out.TrafficRoutings in, out := &in.TrafficRoutings, &out.TrafficRoutings
*out = make([]v1alpha1.TrafficRoutingRef, len(*in)) *out = make([]TrafficRoutingRef, len(*in))
for i := range *in { for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i]) (*in)[i].DeepCopyInto(&(*out)[i])
} }
@ -273,6 +272,26 @@ func (in *DeploymentStrategy) DeepCopy() *DeploymentStrategy {
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GatewayTrafficRouting) DeepCopyInto(out *GatewayTrafficRouting) {
*out = *in
if in.HTTPRouteName != nil {
in, out := &in.HTTPRouteName, &out.HTTPRouteName
*out = new(string)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayTrafficRouting.
func (in *GatewayTrafficRouting) DeepCopy() *GatewayTrafficRouting {
if in == nil {
return nil
}
out := new(GatewayTrafficRouting)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HttpRouteMatch) DeepCopyInto(out *HttpRouteMatch) { func (in *HttpRouteMatch) DeepCopyInto(out *HttpRouteMatch) {
*out = *in *out = *in
@ -295,14 +314,24 @@ func (in *HttpRouteMatch) DeepCopy() *HttpRouteMatch {
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IngressTrafficRouting) DeepCopyInto(out *IngressTrafficRouting) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressTrafficRouting.
func (in *IngressTrafficRouting) DeepCopy() *IngressTrafficRouting {
if in == nil {
return nil
}
out := new(IngressTrafficRouting)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ObjectRef) DeepCopyInto(out *ObjectRef) { func (in *ObjectRef) DeepCopyInto(out *ObjectRef) {
*out = *in *out = *in
if in.WorkloadRef != nil {
in, out := &in.WorkloadRef, &out.WorkloadRef
*out = new(WorkloadRef)
**out = **in
}
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectRef. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectRef.
@ -494,7 +523,7 @@ func (in *RolloutPause) DeepCopy() *RolloutPause {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RolloutSpec) DeepCopyInto(out *RolloutSpec) { func (in *RolloutSpec) DeepCopyInto(out *RolloutSpec) {
*out = *in *out = *in
in.ObjectRef.DeepCopyInto(&out.ObjectRef) out.WorkloadRef = in.WorkloadRef
in.Strategy.DeepCopyInto(&out.Strategy) in.Strategy.DeepCopyInto(&out.Strategy)
} }
@ -556,16 +585,63 @@ func (in *RolloutStrategy) DeepCopy() *RolloutStrategy {
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkloadRef) DeepCopyInto(out *WorkloadRef) { func (in *TrafficRoutingRef) DeepCopyInto(out *TrafficRoutingRef) {
*out = *in *out = *in
if in.Ingress != nil {
in, out := &in.Ingress, &out.Ingress
*out = new(IngressTrafficRouting)
**out = **in
}
if in.Gateway != nil {
in, out := &in.Gateway, &out.Gateway
*out = new(GatewayTrafficRouting)
(*in).DeepCopyInto(*out)
}
if in.CustomNetworkRefs != nil {
in, out := &in.CustomNetworkRefs, &out.CustomNetworkRefs
*out = make([]ObjectRef, len(*in))
copy(*out, *in)
}
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkloadRef. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficRoutingRef.
func (in *WorkloadRef) DeepCopy() *WorkloadRef { func (in *TrafficRoutingRef) DeepCopy() *TrafficRoutingRef {
if in == nil { if in == nil {
return nil return nil
} }
out := new(WorkloadRef) out := new(TrafficRoutingRef)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TrafficRoutingStrategy) DeepCopyInto(out *TrafficRoutingStrategy) {
*out = *in
if in.Traffic != nil {
in, out := &in.Traffic, &out.Traffic
*out = new(string)
**out = **in
}
if in.RequestHeaderModifier != nil {
in, out := &in.RequestHeaderModifier, &out.RequestHeaderModifier
*out = new(v1alpha2.HTTPRequestHeaderFilter)
(*in).DeepCopyInto(*out)
}
if in.Matches != nil {
in, out := &in.Matches, &out.Matches
*out = make([]HttpRouteMatch, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficRoutingStrategy.
func (in *TrafficRoutingStrategy) DeepCopy() *TrafficRoutingStrategy {
if in == nil {
return nil
}
out := new(TrafficRoutingStrategy)
in.DeepCopyInto(out) in.DeepCopyInto(out)
return out return out
} }

View File

@ -345,6 +345,13 @@ spec:
- canaryReplicas - canaryReplicas
type: object type: object
type: array 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'
type: boolean
failureThreshold: failureThreshold:
anyOf: anyOf:
- type: integer - type: integer
@ -378,33 +385,29 @@ spec:
rolloutID: rolloutID:
description: RolloutID indicates an id for each rollout progress description: RolloutID indicates an id for each rollout progress
type: string type: string
required:
- enableExtraWorkloadForCanary
type: object type: object
targetReference: workloadRef:
description: TargetRef contains the GVK and name of the workload that description: WorkloadRef contains enough information to let you identify
we need to upgrade to. a workload for Rollout Batch release of the bypass
properties: properties:
workloadRef: apiVersion:
description: WorkloadRef contains enough information to let you description: API Version of the referent
identify a workload for Rollout Batch release of the bypass type: string
properties: kind:
apiVersion: description: Kind of the referent
description: API Version of the referent type: string
type: string name:
kind: description: Name of the referent
description: Kind of the referent type: string
type: string required:
name: - apiVersion
description: Name of the referent - kind
type: string - name
required:
- apiVersion
- kind
- name
type: object
type: object type: object
required: required:
- releasePlan - releasePlan
- targetReference
type: object type: object
status: status:
description: BatchReleaseStatus defines the observed state of a release description: BatchReleaseStatus defines the observed state of a release

View File

@ -567,37 +567,6 @@ spec:
description: if a rollout disabled, then the rollout would not watch description: if a rollout disabled, then the rollout would not watch
changes of workload changes of workload
type: boolean type: boolean
objectRef:
description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
Important: Run "make" to regenerate code after modifying this file
ObjectRef indicates workload'
properties:
workloadRef:
description: WorkloadRef contains enough information to let you
identify a workload for Rollout Batch release of the bypass
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: object
rolloutID:
description: DeprecatedRolloutID is the deprecated field. It is recommended
that configure RolloutId in workload.annotations[rollouts.kruise.io/rollout-id].
RolloutID should be changed before each workload revision publication.
It is to distinguish consecutive multiple workload publications
and rollout progress.
type: string
strategy: strategy:
description: rollout strategy description: rollout strategy
properties: properties:
@ -605,6 +574,13 @@ spec:
description: CanaryStrategy defines parameters for a Replica Based description: CanaryStrategy defines parameters for a Replica Based
Canary Canary
properties: properties:
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'
type: boolean
failureThreshold: failureThreshold:
anyOf: anyOf:
- type: integer - type: integer
@ -830,13 +806,15 @@ spec:
- name - name
x-kubernetes-list-type: map x-kubernetes-list-type: map
type: object type: object
weight: traffic:
description: Weight indicate how many percentage of description: Traffic indicate how many percentage of
traffic the canary pods should receive traffic the canary pods should receive
format: int32 type: string
type: integer
type: object type: object
type: array type: array
trafficRoutingRef:
description: TrafficRoutingRef is TrafficRouting's Name
type: string
trafficRoutings: trafficRoutings:
description: TrafficRoutings hosts all the supported service description: TrafficRoutings hosts all the supported service
meshes supported to enable more fine-grained traffic routing meshes supported to enable more fine-grained traffic routing
@ -850,12 +828,17 @@ spec:
description: CustomNetworkRefs hold a list of custom description: CustomNetworkRefs hold a list of custom
providers to route traffic providers to route traffic
items: items:
description: ObjectRef holds a references to the Kubernetes
object
properties: properties:
apiVersion: apiVersion:
description: API Version of the referent
type: string type: string
kind: kind:
description: Kind of the referent
type: string type: string
name: name:
description: Name of the referent
type: string type: string
required: required:
- apiVersion - apiVersion
@ -911,9 +894,29 @@ spec:
value is false value is false
type: boolean type: boolean
type: object type: object
workloadRef:
description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
Important: Run "make" to regenerate code after modifying this file
WorkloadRef contains enough information to let you identify a workload
for Rollout Batch release of the bypass'
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
required: required:
- objectRef
- strategy - strategy
- workloadRef
type: object type: object
status: status:
description: RolloutStatus defines the observed state of Rollout description: RolloutStatus defines the observed state of Rollout

View File

@ -11,8 +11,8 @@ resources:
patchesStrategicMerge: patchesStrategicMerge:
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
# patches here are for enabling the conversion webhook for each CRD # patches here are for enabling the conversion webhook for each CRD
#- patches/webhook_in_rollouts.yaml - patches/webhook_in_rollouts.yaml
#- patches/webhook_in_batchreleases.yaml - patches/webhook_in_batchreleases.yaml
#+kubebuilder:scaffold:crdkustomizewebhookpatch #+kubebuilder:scaffold:crdkustomizewebhookpatch
# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. # [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix.

View File

@ -13,4 +13,4 @@ spec:
name: webhook-service name: webhook-service
path: /convert path: /convert
conversionReviewVersions: conversionReviewVersions:
- v1 - v1beta1

View File

@ -13,4 +13,4 @@ spec:
name: webhook-service name: webhook-service
path: /convert path: /convert
conversionReviewVersions: conversionReviewVersions:
- v1 - v1beta1

View File

@ -0,0 +1,224 @@
---
title: v1beta1-apis-proposal
authors:
- "@zmberg"
creation-date: 2023-11-07
---
## Motivation
The Kruise Rollout project has been stable for a year, recently we plan to upgrade the apis from v1alpha1 to v1beta1 and optimize some of the fields in response to past questions and community feedback,
this proposal will organize the v1beta1 apis and discuss it with the community.
## Proposal
To make it easier to understand, I'm going to introduce the v1beta1 field from 6 scenario.
### Canary Release
```
apiVersion: rollouts.kruise.io/v1beta1
kind: Rollout
metadata:
name: rollouts-demo
spec:
workloadRef:
apiVersion: apps/v1
kind: Deployment
name: workload-demo
strategy:
canary:
# If true, then it will create new deployment for canary, such as: workload-demo-canary.
# When user verifies that the canary version is OK, we will remove the canary deployment and release the deployment workload-demo in full.
# Current only support k8s native deployment
enableExtraWorkloadForCanary: true
steps:
- trafficWeight: 20%
desiredReplicas: 2
trafficRoutings:
- service: service-demo
ingress:
classType: nginx
name: ingress-demo
```
### A/B Testing Release
```
apiVersion: rollouts.kruise.io/v1beta1
kind: Rollout
metadata:
name: rollouts-demo
spec:
workloadRef:
apiVersion: apps/v1
kind: Deployment
name: workload-demo
strategy:
canary:
enableExtraWorkloadForCanary: true
steps:
- desiredReplicas: 2
trafficMatches:
- headers:
- name: user-agent
type: Exact
value: pc
trafficRoutings:
- service: service-demo
ingress:
classType: nginx
name: ingress-demo
```
### Only Batch Release
```
apiVersion: rollouts.kruise.io/v1beta1
kind: Rollout
metadata:
name: rollouts-demo
spec:
workloadRef:
apiVersion: apps/v1
kind: Deployment
name: workload-demo
strategy:
canary:
steps:
- desiredReplicas: 1
- desiredReplicas: 10%
# After desiredReplicas Pods are ready, sleep 60 and continue to release later batches.
# If you don't configure it, manual confirmation is required by default.
pause: {duration: 60}
- desiredReplicas: 30%
pause: {duration: 60}
- desiredReplicas: 60%
pause: {duration: 60}
- desiredReplicas: 100%
pause: {duration: 60}
```
### Batch Release + Traffic Weight
```
apiVersion: rollouts.kruise.io/v1beta1
kind: Rollout
metadata:
name: rollouts-demo
spec:
workloadRef:
apiVersion: apps/v1
kind: Deployment
name: workload-demo
strategy:
canary:
steps:
- trafficWeight: 5%
desiredReplicas: 2
- desiredReplicas: 30%
- desiredReplicas: 60%
- desiredReplicas: 100%
trafficRoutings:
- service: service-demo
ingress:
classType: nginx
name: ingress-demo
```
### Batch Release + Traffic A/B Testing
```
apiVersion: rollouts.kruise.io/v1beta1
kind: Rollout
metadata:
name: rollouts-demo
spec:
workloadRef:
apiVersion: apps/v1
kind: Deployment
name: workload-demo
strategy:
canary:
steps:
- trafficMatches:
- headers:
- name: user-agent
type: Exact
value: pc
desiredReplicas: 2
- desiredReplicas: 30%
- desiredReplicas: 60%
- desiredReplicas: 100%
trafficRoutings:
- service: service-demo
ingress:
classType: nginx
name: ingress-demo
```
### End-to-End progressive delivery for microservice application
```
apiVersion: rollouts.kruise.io/v1alpha1
kind: TrafficRouting
metadata:
name: mse-traffic
spec:
objectRef:
- service: spring-cloud-a
ingress:
classType: mse
name: spring-cloud-a
strategy:
matches:
- headers:
- type: Exact
name: User-Agent
value: xiaoming
# http request via ingress, and add header[x-mse-tag]=gray
# for mse or istio routing the gray traffic to gray application
requestHeaderModifier:
set:
- name: x-mse-tag
value: gray
---
apiVersion: rollouts.kruise.io/v1alpha1
kind: Rollout
metadata:
name: rollout-a
spec:
workloadRef:
apiVersion: apps/v1
kind: Deployment
name: spring-cloud-a
strategy:
canary:
enableExtraWorkloadForCanary: true
# Type TrafficRouting's name
trafficRoutingRef: mse-traffic
steps:
- desiredReplicas: 1
# patch pod template metadata to canary workload
# current only support deployment, and when enableExtraWorkloadForCanary=true
patchPodTemplateMetadata:
labels:
alicloud.service.tag: gray
opensergo.io/canary-gray: gray
---
apiVersion: rollouts.kruise.io/v1alpha1
kind: Rollout
metadata:
name: rollout-a
spec:
workloadRef:
apiVersion: apps/v1
kind: Deployment
name: spring-cloud-a
strategy:
canary:
enableExtraWorkloadForCanary: true
# Type TrafficRouting's name
trafficRoutingRef: mse-traffic
steps:
- desiredReplicas: 1
# patch pod template metadata to canary workload
patchPodTemplateMetadata:
labels:
alicloud.service.tag: gray
opensergo.io/canary-gray: gray
```

View File

@ -14,6 +14,7 @@ import (
lua "github.com/yuin/gopher-lua" lua "github.com/yuin/gopher-lua"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
utilpointer "k8s.io/utils/pointer" utilpointer "k8s.io/utils/pointer"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
@ -82,8 +83,12 @@ func objectToTable(path string) error {
if rollout != nil { if rollout != nil {
steps := rollout.Spec.Strategy.Canary.Steps steps := rollout.Spec.Strategy.Canary.Steps
for i, step := range steps { for i, step := range steps {
weight := step.TrafficRoutingStrategy.Weight var weight *int32
if step.TrafficRoutingStrategy.Weight == nil { if step.TrafficRoutingStrategy.Traffic != nil {
is := intstr.FromString(*step.TrafficRoutingStrategy.Traffic)
weightInt, _ := intstr.GetScaledValueFromIntOrPercent(&is, 100, true)
weight = utilpointer.Int32(int32(weightInt))
} else {
weight = utilpointer.Int32(-1) weight = utilpointer.Int32(-1)
} }
var canaryService string var canaryService string
@ -111,13 +116,19 @@ func objectToTable(path string) error {
var canaryService string var canaryService string
stableService := trafficRouting.Spec.ObjectRef[0].Service stableService := trafficRouting.Spec.ObjectRef[0].Service
canaryService = stableService canaryService = stableService
matches := make([]v1beta1.HttpRouteMatch, 0)
for _, match := range trafficRouting.Spec.Strategy.Matches {
obj := v1beta1.HttpRouteMatch{}
obj.Headers = match.Headers
matches = append(matches, obj)
}
data := &custom.LuaData{ data := &custom.LuaData{
Data: custom.Data{ Data: custom.Data{
Labels: testCase.Original.GetLabels(), Labels: testCase.Original.GetLabels(),
Annotations: testCase.Original.GetAnnotations(), Annotations: testCase.Original.GetAnnotations(),
Spec: testCase.Original.Object["spec"], Spec: testCase.Original.Object["spec"],
}, },
Matches: trafficRouting.Spec.Strategy.Matches, Matches: matches,
CanaryWeight: *weight, CanaryWeight: *weight,
StableWeight: 100 - *weight, StableWeight: 100 - *weight,
CanaryService: canaryService, CanaryService: canaryService,

View File

@ -1,17 +1,13 @@
rollout: rollout:
apiVersion: rollouts.kruise.io/v1alpha1 apiVersion: rollouts.kruise.io/v1beta1
kind: Rollout kind: Rollout
metadata: metadata:
name: rollouts-demo name: rollouts-demo
annotations:
rollouts.kruise.io/rolling-style: canary
spec: spec:
disabled: false workloadRef:
objectRef: apiVersion: apps/v1
workloadRef: kind: Deployment
apiVersion: apps/v1 name: deploy-demo
kind: Deployment
name: deploy-demo
strategy: strategy:
canary: canary:
steps: steps:
@ -32,7 +28,7 @@ rollout:
- type: RegularExpression - type: RegularExpression
name: name name: name
value: ".*demo" value: ".*demo"
- weight: 50 - traffic: "50%"
trafficRoutings: trafficRoutings:
- service: svc-demo - service: svc-demo
customNetworkRefs: customNetworkRefs:

View File

@ -114,7 +114,7 @@ function GenerateRoutes(spec, stableService, canaryService, stableWeight, canary
end end
end end
if (obj.matches) if (obj.matches and next(obj.matches) ~= nil)
then then
GenerateRoutesWithMatches(spec, obj.matches, obj.stableService, obj.canaryService) GenerateRoutesWithMatches(spec, obj.matches, obj.stableService, obj.canaryService)
else else

View File

@ -167,10 +167,10 @@ func (r *BatchReleaseReconciler) Reconcile(ctx context.Context, req ctrl.Request
klog.Infof("Begin to reconcile BatchRelease(%v/%v), release-phase: %v", release.Namespace, release.Name, release.Status.Phase) klog.Infof("Begin to reconcile BatchRelease(%v/%v), release-phase: %v", release.Namespace, release.Name, release.Status.Phase)
// If workload watcher does not exist, then add the watcher dynamically // If workload watcher does not exist, then add the watcher dynamically
workloadRef := release.Spec.TargetRef.WorkloadRef workloadRef := release.Spec.WorkloadRef
workloadGVK := util.GetGVKFrom(workloadRef) workloadGVK := util.GetGVKFrom(&workloadRef)
_, exists := watchedWorkload.Load(workloadGVK.String()) _, exists := watchedWorkload.Load(workloadGVK.String())
if workloadRef != nil && !exists { if !exists {
succeeded, err := util.AddWatcherDynamically(runtimeController, workloadHandler, workloadGVK) succeeded, err := util.AddWatcherDynamically(runtimeController, workloadHandler, workloadGVK)
if err != nil { if err != nil {
return ctrl.Result{}, err return ctrl.Result{}, err

View File

@ -61,15 +61,14 @@ var (
UID: types.UID("87076677"), UID: types.UID("87076677"),
}, },
Spec: v1beta1.BatchReleaseSpec{ Spec: v1beta1.BatchReleaseSpec{
TargetRef: v1beta1.ObjectRef{ WorkloadRef: v1beta1.ObjectRef{
WorkloadRef: &v1beta1.WorkloadRef{ APIVersion: "apps/v1",
APIVersion: "apps/v1", Kind: "Deployment",
Kind: "Deployment", Name: "sample",
Name: "sample",
},
}, },
ReleasePlan: v1beta1.ReleasePlan{ ReleasePlan: v1beta1.ReleasePlan{
BatchPartition: pointer.Int32(0), EnableExtraWorkloadForCanary: true,
BatchPartition: pointer.Int32(0),
Batches: []v1beta1.ReleaseBatch{ Batches: []v1beta1.ReleaseBatch{
{ {
CanaryReplicas: intstr.FromString("10%"), CanaryReplicas: intstr.FromString("10%"),
@ -141,12 +140,10 @@ var (
UID: types.UID("87076677"), UID: types.UID("87076677"),
}, },
Spec: v1beta1.BatchReleaseSpec{ Spec: v1beta1.BatchReleaseSpec{
TargetRef: v1beta1.ObjectRef{ WorkloadRef: v1beta1.ObjectRef{
WorkloadRef: &v1beta1.WorkloadRef{ APIVersion: "apps.kruise.io/v1alpha1",
APIVersion: "apps.kruise.io/v1alpha1", Kind: "CloneSet",
Kind: "CloneSet", Name: "sample",
Name: "sample",
},
}, },
ReleasePlan: v1beta1.ReleasePlan{ ReleasePlan: v1beta1.ReleasePlan{
BatchPartition: pointer.Int32Ptr(0), BatchPartition: pointer.Int32Ptr(0),

View File

@ -240,14 +240,14 @@ func getBatchRelease(c client.Reader, workloadNamespaceName types.NamespacedName
for i := range brList.Items { for i := range brList.Items {
br := &brList.Items[i] br := &brList.Items[i]
targetRef := br.Spec.TargetRef targetRef := br.Spec.WorkloadRef
targetGV, err := schema.ParseGroupVersion(targetRef.WorkloadRef.APIVersion) targetGV, err := schema.ParseGroupVersion(targetRef.APIVersion)
if err != nil { if err != nil {
klog.Errorf("Failed to parse targetRef's group version: %s for BatchRelease(%v)", targetRef.WorkloadRef.APIVersion, client.ObjectKeyFromObject(br)) klog.Errorf("Failed to parse targetRef's group version: %s for BatchRelease(%v)", targetRef.APIVersion, client.ObjectKeyFromObject(br))
continue continue
} }
if targetRef.WorkloadRef.Kind == gvk.Kind && targetGV.Group == gvk.Group && targetRef.WorkloadRef.Name == workloadNamespaceName.Name { if targetRef.Kind == gvk.Kind && targetGV.Group == gvk.Group && targetRef.Name == workloadNamespaceName.Name {
nsn = client.ObjectKeyFromObject(br) nsn = client.ObjectKeyFromObject(br)
} }
} }

View File

@ -19,11 +19,9 @@ package batchrelease
import ( import (
"fmt" "fmt"
"reflect" "reflect"
"strings"
"time" "time"
appsv1alpha1 "github.com/openkruise/kruise-api/apps/v1alpha1" appsv1alpha1 "github.com/openkruise/kruise-api/apps/v1alpha1"
"github.com/openkruise/rollouts/api/v1alpha1"
"github.com/openkruise/rollouts/api/v1beta1" "github.com/openkruise/rollouts/api/v1beta1"
"github.com/openkruise/rollouts/pkg/controller/batchrelease/control" "github.com/openkruise/rollouts/pkg/controller/batchrelease/control"
"github.com/openkruise/rollouts/pkg/controller/batchrelease/control/canarystyle" "github.com/openkruise/rollouts/pkg/controller/batchrelease/control/canarystyle"
@ -187,11 +185,7 @@ func (r *Executor) progressBatches(release *v1beta1.BatchRelease, newStatus *v1b
// GetWorkloadController pick the right workload controller to work on the workload // GetWorkloadController pick the right workload controller to work on the workload
func (r *Executor) getReleaseController(release *v1beta1.BatchRelease, newStatus *v1beta1.BatchReleaseStatus) (control.Interface, error) { func (r *Executor) getReleaseController(release *v1beta1.BatchRelease, newStatus *v1beta1.BatchReleaseStatus) (control.Interface, error) {
targetRef := release.Spec.TargetRef.WorkloadRef targetRef := release.Spec.WorkloadRef
if targetRef == nil {
return nil, nil
}
gvk := schema.FromAPIVersionAndKind(targetRef.APIVersion, targetRef.Kind) gvk := schema.FromAPIVersionAndKind(targetRef.APIVersion, targetRef.Kind)
if !util.IsSupportedWorkload(gvk) { if !util.IsSupportedWorkload(gvk) {
message := fmt.Sprintf("the workload type '%v' is not supported", gvk) message := fmt.Sprintf("the workload type '%v' is not supported", gvk)
@ -217,7 +211,7 @@ func (r *Executor) getReleaseController(release *v1beta1.BatchRelease, newStatus
case apps.SchemeGroupVersion.String(): case apps.SchemeGroupVersion.String():
if targetRef.Kind == reflect.TypeOf(apps.Deployment{}).Name() { if targetRef.Kind == reflect.TypeOf(apps.Deployment{}).Name() {
if strings.EqualFold(release.Annotations[v1beta1.RolloutStyleAnnotation], string(v1alpha1.PartitionRollingStyle)) { if !release.Spec.ReleasePlan.EnableExtraWorkloadForCanary {
klog.InfoS("Using Deployment partition-style release controller for this batch release", "workload name", targetKey.Name, "namespace", targetKey.Namespace) 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 return partitionstyle.NewControlPlane(partitiondeployment.NewController, r.client, r.recorder, release, newStatus, targetKey, gvk), nil
} else { } else {

View File

@ -131,12 +131,10 @@ var (
}, },
}, },
}, },
TargetRef: v1beta1.ObjectRef{ WorkloadRef: v1beta1.ObjectRef{
WorkloadRef: &v1beta1.WorkloadRef{ APIVersion: deploymentDemo.APIVersion,
APIVersion: deploymentDemo.APIVersion, Kind: deploymentDemo.Kind,
Kind: deploymentDemo.Kind, Name: deploymentDemo.Name,
Name: deploymentDemo.Name,
},
}, },
}, },
Status: v1beta1.BatchReleaseStatus{ Status: v1beta1.BatchReleaseStatus{

View File

@ -129,12 +129,10 @@ var (
}, },
}, },
}, },
TargetRef: v1beta1.ObjectRef{ WorkloadRef: v1beta1.ObjectRef{
WorkloadRef: &v1beta1.WorkloadRef{ APIVersion: cloneDemo.APIVersion,
APIVersion: cloneDemo.APIVersion, Kind: cloneDemo.Kind,
Kind: cloneDemo.Kind, Name: cloneDemo.Name,
Name: cloneDemo.Name,
},
}, },
}, },
Status: v1beta1.BatchReleaseStatus{ Status: v1beta1.BatchReleaseStatus{

View File

@ -117,12 +117,10 @@ var (
}, },
}, },
}, },
TargetRef: v1beta1.ObjectRef{ WorkloadRef: v1beta1.ObjectRef{
WorkloadRef: &v1beta1.WorkloadRef{ APIVersion: daemonDemo.APIVersion,
APIVersion: daemonDemo.APIVersion, Kind: daemonDemo.Kind,
Kind: daemonDemo.Kind, Name: daemonDemo.Name,
Name: daemonDemo.Name,
},
}, },
}, },
Status: v1beta1.BatchReleaseStatus{ Status: v1beta1.BatchReleaseStatus{

View File

@ -131,12 +131,10 @@ var (
}, },
}, },
}, },
TargetRef: v1beta1.ObjectRef{ WorkloadRef: v1beta1.ObjectRef{
WorkloadRef: &v1beta1.WorkloadRef{ APIVersion: deploymentDemo.APIVersion,
APIVersion: deploymentDemo.APIVersion, Kind: deploymentDemo.Kind,
Kind: deploymentDemo.Kind, Name: deploymentDemo.Name,
Name: deploymentDemo.Name,
},
}, },
}, },
Status: v1beta1.BatchReleaseStatus{ Status: v1beta1.BatchReleaseStatus{

View File

@ -136,12 +136,10 @@ var (
}, },
}, },
}, },
TargetRef: v1beta1.ObjectRef{ WorkloadRef: v1beta1.ObjectRef{
WorkloadRef: &v1beta1.WorkloadRef{ APIVersion: stsDemo.APIVersion,
APIVersion: stsDemo.APIVersion, Kind: stsDemo.Kind,
Kind: stsDemo.Kind, Name: stsDemo.Name,
Name: stsDemo.Name,
},
}, },
}, },
Status: v1beta1.BatchReleaseStatus{ Status: v1beta1.BatchReleaseStatus{

View File

@ -159,8 +159,8 @@ func TestSyncDeployment(t *testing.T) {
rs.Status.Replicas = replicas rs.Status.Replicas = replicas
if strings.HasPrefix(name, "scale") { if strings.HasPrefix(name, "scale") {
rs.Annotations = map[string]string{ rs.Annotations = map[string]string{
util.DesiredReplicasAnnotation: strconv.Itoa(-1), util.ReplicasAnnotation: strconv.Itoa(-1),
util.MaxReplicasAnnotation: strconv.Itoa(int(test.dAvailable + test.maxSurge.IntVal)), util.MaxReplicasAnnotation: strconv.Itoa(int(test.dAvailable + test.maxSurge.IntVal)),
} }
} }
rs.Spec.Template.Spec.Containers[0].Image = fmt.Sprintf("old-version-%d", index) rs.Spec.Template.Spec.Containers[0].Image = fmt.Sprintf("old-version-%d", index)
@ -179,8 +179,8 @@ func TestSyncDeployment(t *testing.T) {
newRS.Spec.Replicas = pointer.Int32(test.newRSReplicas) newRS.Spec.Replicas = pointer.Int32(test.newRSReplicas)
if strings.HasPrefix(name, "scale") { if strings.HasPrefix(name, "scale") {
newRS.Annotations = map[string]string{ newRS.Annotations = map[string]string{
util.DesiredReplicasAnnotation: strconv.Itoa(-1), util.ReplicasAnnotation: strconv.Itoa(-1),
util.MaxReplicasAnnotation: strconv.Itoa(int(test.dAvailable + test.maxSurge.IntVal)), util.MaxReplicasAnnotation: strconv.Itoa(int(test.dAvailable + test.maxSurge.IntVal)),
} }
} }
newRS.Status.Replicas = test.newRSReplicas newRS.Status.Replicas = test.newRSReplicas

View File

@ -270,9 +270,9 @@ func ScaleDownLimitForOld(oldRSs []*apps.ReplicaSet, newRS *apps.ReplicaSet, dep
klog.V(4).InfoS("Calculate scale down limit for ", klog.V(4).InfoS("Calculate scale down limit for ",
"Deployment", klog.KObj(deployment), "Deployment", klog.KObj(deployment),
// About the new replica set // About the new replica set
"Replicas(New)", *(newRS.Spec.Replicas), "DesiredReplicas(New)", newRSDesiredCount, "Replicas(New)", *(newRS.Spec.Replicas), "Replicas(New)", newRSDesiredCount,
// About the old replica sets // About the old replica sets
"ReplicaS(Old)", oldPodsCount, "DesiredReplicas(Old)", oldRSDesiredCount, "ScaleDownLimit(Old)", scaleDownOldLimit, "ReplicaS(Old)", oldPodsCount, "Replicas(Old)", oldRSDesiredCount, "ScaleDownLimit(Old)", scaleDownOldLimit,
// About the deployment // About the deployment
"Replicas(Deployment)", *(deployment.Spec.Replicas), "Partition(Deployment)", newRSUpdateLimit) "Replicas(Deployment)", *(deployment.Spec.Replicas), "Partition(Deployment)", newRSUpdateLimit)

View File

@ -525,7 +525,7 @@ func (dc *DeploymentController) isScalingEvent(ctx context.Context, d *apps.Depl
} }
allRSs := append(oldRSs, newRS) allRSs := append(oldRSs, newRS)
for _, rs := range deploymentutil.FilterActiveReplicaSets(allRSs) { for _, rs := range deploymentutil.FilterActiveReplicaSets(allRSs) {
desired, ok := deploymentutil.GetDesiredReplicasAnnotation(rs) desired, ok := deploymentutil.GetReplicasAnnotation(rs)
if !ok { if !ok {
continue continue
} }

View File

@ -44,10 +44,10 @@ const (
RevisionAnnotation = "deployment.kubernetes.io/revision" RevisionAnnotation = "deployment.kubernetes.io/revision"
// RevisionHistoryAnnotation maintains the history of all old revisions that a replica set has served for a deployment. // RevisionHistoryAnnotation maintains the history of all old revisions that a replica set has served for a deployment.
RevisionHistoryAnnotation = "deployment.kubernetes.io/revision-history" RevisionHistoryAnnotation = "deployment.kubernetes.io/revision-history"
// DesiredReplicasAnnotation is the desired replicas for a deployment recorded as an annotation // ReplicasAnnotation is the desired replicas for a deployment recorded as an annotation
// in its replica sets. Helps in separating scaling events from the rollout process and for // in its replica sets. Helps in separating scaling events from the rollout process and for
// determining if the new replica set for a deployment is really saturated. // determining if the new replica set for a deployment is really saturated.
DesiredReplicasAnnotation = "deployment.kubernetes.io/desired-replicas" ReplicasAnnotation = "deployment.kubernetes.io/desired-replicas"
// MaxReplicasAnnotation is the maximum replicas a deployment can have at a given point, which // MaxReplicasAnnotation is the maximum replicas a deployment can have at a given point, which
// is deployment.spec.replicas + maxSurge. Used by the underlying replica sets to estimate their // is deployment.spec.replicas + maxSurge. Used by the underlying replica sets to estimate their
// proportions in case the deployment has surge replicas. // proportions in case the deployment has surge replicas.
@ -267,7 +267,7 @@ var annotationsToSkip = map[string]bool{
v1.LastAppliedConfigAnnotation: true, v1.LastAppliedConfigAnnotation: true,
RevisionAnnotation: true, RevisionAnnotation: true,
RevisionHistoryAnnotation: true, RevisionHistoryAnnotation: true,
DesiredReplicasAnnotation: true, ReplicasAnnotation: true,
MaxReplicasAnnotation: true, MaxReplicasAnnotation: true,
apps.DeprecatedRollbackTo: true, apps.DeprecatedRollbackTo: true,
} }
@ -325,9 +325,9 @@ func FindActiveOrLatest(newRS *apps.ReplicaSet, oldRSs []*apps.ReplicaSet) *apps
} }
} }
// GetDesiredReplicasAnnotation returns the number of desired replicas // GetReplicasAnnotation returns the number of desired replicas
func GetDesiredReplicasAnnotation(rs *apps.ReplicaSet) (int32, bool) { func GetReplicasAnnotation(rs *apps.ReplicaSet) (int32, bool) {
return getIntFromAnnotation(rs, DesiredReplicasAnnotation) return getIntFromAnnotation(rs, ReplicasAnnotation)
} }
func getMaxReplicasAnnotation(rs *apps.ReplicaSet) (int32, bool) { func getMaxReplicasAnnotation(rs *apps.ReplicaSet) (int32, bool) {
@ -354,8 +354,8 @@ func SetReplicasAnnotations(rs *apps.ReplicaSet, desiredReplicas, maxReplicas in
rs.Annotations = make(map[string]string) rs.Annotations = make(map[string]string)
} }
desiredString := fmt.Sprintf("%d", desiredReplicas) desiredString := fmt.Sprintf("%d", desiredReplicas)
if hasString := rs.Annotations[DesiredReplicasAnnotation]; hasString != desiredString { if hasString := rs.Annotations[ReplicasAnnotation]; hasString != desiredString {
rs.Annotations[DesiredReplicasAnnotation] = desiredString rs.Annotations[ReplicasAnnotation] = desiredString
updated = true updated = true
} }
maxString := fmt.Sprintf("%d", maxReplicas) maxString := fmt.Sprintf("%d", maxReplicas)
@ -372,7 +372,7 @@ func ReplicasAnnotationsNeedUpdate(rs *apps.ReplicaSet, desiredReplicas, maxRepl
return true return true
} }
desiredString := fmt.Sprintf("%d", desiredReplicas) desiredString := fmt.Sprintf("%d", desiredReplicas)
if hasString := rs.Annotations[DesiredReplicasAnnotation]; hasString != desiredString { if hasString := rs.Annotations[ReplicasAnnotation]; hasString != desiredString {
return true return true
} }
maxString := fmt.Sprintf("%d", maxReplicas) maxString := fmt.Sprintf("%d", maxReplicas)
@ -742,7 +742,7 @@ func IsSaturated(deployment *apps.Deployment, rs *apps.ReplicaSet) bool {
if rs == nil { if rs == nil {
return false return false
} }
desiredString := rs.Annotations[DesiredReplicasAnnotation] desiredString := rs.Annotations[ReplicasAnnotation]
desired, err := strconv.Atoi(desiredString) desired, err := strconv.Atoi(desiredString)
if err != nil { if err != nil {
return false return false

View File

@ -1104,15 +1104,15 @@ func TestAnnotationUtils(t *testing.T) {
if !updated { if !updated {
t.Errorf("SetReplicasAnnotations() failed") t.Errorf("SetReplicasAnnotations() failed")
} }
value, ok := tRS.Annotations[DesiredReplicasAnnotation] value, ok := tRS.Annotations[ReplicasAnnotation]
if !ok { if !ok {
t.Errorf("SetReplicasAnnotations did not set DesiredReplicasAnnotation") t.Errorf("SetReplicasAnnotations did not set ReplicasAnnotation")
} }
if value != "10" { if value != "10" {
t.Errorf("SetReplicasAnnotations did not set DesiredReplicasAnnotation correctly value=%s", value) t.Errorf("SetReplicasAnnotations did not set ReplicasAnnotation correctly value=%s", value)
} }
if value, ok = tRS.Annotations[MaxReplicasAnnotation]; !ok { if value, ok = tRS.Annotations[MaxReplicasAnnotation]; !ok {
t.Errorf("SetReplicasAnnotations did not set DesiredReplicasAnnotation") t.Errorf("SetReplicasAnnotations did not set ReplicasAnnotation")
} }
if value != "11" { if value != "11" {
t.Errorf("SetReplicasAnnotations did not set MaxReplicasAnnotation correctly value=%s", value) t.Errorf("SetReplicasAnnotations did not set MaxReplicasAnnotation correctly value=%s", value)
@ -1120,7 +1120,7 @@ func TestAnnotationUtils(t *testing.T) {
}) })
//Test Case 3: Check if annotations reflect deployments state //Test Case 3: Check if annotations reflect deployments state
tRS.Annotations[DesiredReplicasAnnotation] = "1" tRS.Annotations[ReplicasAnnotation] = "1"
tRS.Status.AvailableReplicas = 1 tRS.Status.AvailableReplicas = 1
tRS.Spec.Replicas = new(int32) tRS.Spec.Replicas = new(int32)
*tRS.Spec.Replicas = 1 *tRS.Spec.Replicas = 1
@ -1160,7 +1160,7 @@ func TestReplicasAnnotationsNeedUpdate(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "hello", Name: "hello",
Namespace: "test", Namespace: "test",
Annotations: map[string]string{DesiredReplicasAnnotation: "8", MaxReplicasAnnotation: maxReplicas}, Annotations: map[string]string{ReplicasAnnotation: "8", MaxReplicasAnnotation: maxReplicas},
}, },
Spec: apps.ReplicaSetSpec{ Spec: apps.ReplicaSetSpec{
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
@ -1174,7 +1174,7 @@ func TestReplicasAnnotationsNeedUpdate(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "hello", Name: "hello",
Namespace: "test", Namespace: "test",
Annotations: map[string]string{DesiredReplicasAnnotation: desiredReplicas, MaxReplicasAnnotation: "16"}, Annotations: map[string]string{ReplicasAnnotation: desiredReplicas, MaxReplicasAnnotation: "16"},
}, },
Spec: apps.ReplicaSetSpec{ Spec: apps.ReplicaSetSpec{
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
@ -1188,7 +1188,7 @@ func TestReplicasAnnotationsNeedUpdate(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "hello", Name: "hello",
Namespace: "test", Namespace: "test",
Annotations: map[string]string{DesiredReplicasAnnotation: desiredReplicas, MaxReplicasAnnotation: maxReplicas}, Annotations: map[string]string{ReplicasAnnotation: desiredReplicas, MaxReplicasAnnotation: maxReplicas},
}, },
Spec: apps.ReplicaSetSpec{ Spec: apps.ReplicaSetSpec{
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},

View File

@ -20,7 +20,6 @@ import (
"context" "context"
"fmt" "fmt"
"reflect" "reflect"
"strconv"
"time" "time"
"github.com/openkruise/rollouts/api/v1alpha1" "github.com/openkruise/rollouts/api/v1alpha1"
@ -32,7 +31,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/tools/record" "k8s.io/client-go/tools/record"
"k8s.io/client-go/util/retry" "k8s.io/client-go/util/retry"
"k8s.io/klog/v2" "k8s.io/klog/v2"
@ -70,7 +68,7 @@ func (m *canaryReleaseManager) runCanary(c *RolloutContext) error {
// When the first batch is trafficRouting rolling and the next steps are rolling release, // When the first batch is trafficRouting rolling and the next steps are rolling release,
// We need to clean up the canary-related resources first and then rollout the rest of the batch. // We need to clean up the canary-related resources first and then rollout the rest of the batch.
currentStep := c.Rollout.Spec.Strategy.Canary.Steps[canaryStatus.CurrentStepIndex-1] currentStep := c.Rollout.Spec.Strategy.Canary.Steps[canaryStatus.CurrentStepIndex-1]
if currentStep.Weight == nil && len(currentStep.Matches) == 0 { if currentStep.Traffic == nil && len(currentStep.Matches) == 0 {
tr := newTrafficRoutingContext(c) tr := newTrafficRoutingContext(c)
done, err := m.trafficRoutingManager.FinalisingTrafficRouting(tr, false) done, err := m.trafficRoutingManager.FinalisingTrafficRouting(tr, false)
c.NewStatus.CanaryStatus.LastUpdateTime = tr.LastUpdateTime c.NewStatus.CanaryStatus.LastUpdateTime = tr.LastUpdateTime
@ -201,8 +199,7 @@ func (m *canaryReleaseManager) doCanaryPaused(c *RolloutContext) (bool, error) {
steps := len(c.Rollout.Spec.Strategy.Canary.Steps) steps := len(c.Rollout.Spec.Strategy.Canary.Steps)
// If it is the last step, and 100% of pods, then return true // If it is the last step, and 100% of pods, then return true
if int32(steps) == canaryStatus.CurrentStepIndex { if int32(steps) == canaryStatus.CurrentStepIndex {
if currentStep.Weight != nil && *currentStep.Weight == 100 || if currentStep.Replicas != nil && currentStep.Replicas.StrVal == "100%" {
currentStep.Replicas != nil && currentStep.Replicas.StrVal == "100%" {
return true, nil return true, nil
} }
} }
@ -276,7 +273,7 @@ func (m *canaryReleaseManager) removeRolloutProgressingAnnotation(c *RolloutCont
if _, ok := c.Workload.Annotations[util.InRolloutProgressingAnnotation]; !ok { if _, ok := c.Workload.Annotations[util.InRolloutProgressingAnnotation]; !ok {
return nil return nil
} }
workloadRef := c.Rollout.Spec.ObjectRef.WorkloadRef workloadRef := c.Rollout.Spec.WorkloadRef
workloadGVK := schema.FromAPIVersionAndKind(workloadRef.APIVersion, workloadRef.Kind) workloadGVK := schema.FromAPIVersionAndKind(workloadRef.APIVersion, workloadRef.Kind)
obj := util.GetEmptyWorkloadObject(workloadGVK) obj := util.GetEmptyWorkloadObject(workloadGVK)
obj.SetNamespace(c.Workload.Namespace) obj.SetNamespace(c.Workload.Namespace)
@ -313,7 +310,6 @@ func (m *canaryReleaseManager) runBatchRelease(rollout *v1beta1.Rollout, rollout
klog.Infof("rollout(%s/%s) do batchRelease batch(%d) success", rollout.Namespace, rollout.Name, batch+1) klog.Infof("rollout(%s/%s) do batchRelease batch(%d) success", rollout.Namespace, rollout.Name, batch+1)
return true, br, nil return true, br, nil
} }
// update batchRelease to the latest version // update batchRelease to the latest version
if err = retry.RetryOnConflict(retry.DefaultBackoff, func() error { if err = retry.RetryOnConflict(retry.DefaultBackoff, func() error {
if err = m.Get(context.TODO(), client.ObjectKey{Namespace: newBr.Namespace, Name: newBr.Name}, br); err != nil { if err = m.Get(context.TODO(), client.ObjectKey{Namespace: newBr.Namespace, Name: newBr.Name}, br); err != nil {
@ -341,11 +337,7 @@ func (m *canaryReleaseManager) fetchBatchRelease(ns, name string) (*v1beta1.Batc
func createBatchRelease(rollout *v1beta1.Rollout, rolloutID string, batch int32, isRollback bool) *v1beta1.BatchRelease { func createBatchRelease(rollout *v1beta1.Rollout, rolloutID string, batch int32, isRollback bool) *v1beta1.BatchRelease {
var batches []v1beta1.ReleaseBatch var batches []v1beta1.ReleaseBatch
for _, step := range rollout.Spec.Strategy.Canary.Steps { for _, step := range rollout.Spec.Strategy.Canary.Steps {
if step.Replicas == nil { batches = append(batches, v1beta1.ReleaseBatch{CanaryReplicas: *step.Replicas})
batches = append(batches, v1beta1.ReleaseBatch{CanaryReplicas: intstr.FromString(strconv.Itoa(int(*step.Weight)) + "%")})
} else {
batches = append(batches, v1beta1.ReleaseBatch{CanaryReplicas: *step.Replicas})
}
} }
br := &v1beta1.BatchRelease{ br := &v1beta1.BatchRelease{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
@ -354,19 +346,18 @@ func createBatchRelease(rollout *v1beta1.Rollout, rolloutID string, batch int32,
OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(rollout, rolloutControllerKind)}, OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(rollout, rolloutControllerKind)},
}, },
Spec: v1beta1.BatchReleaseSpec{ Spec: v1beta1.BatchReleaseSpec{
TargetRef: v1beta1.ObjectRef{ WorkloadRef: v1beta1.ObjectRef{
WorkloadRef: &v1beta1.WorkloadRef{ APIVersion: rollout.Spec.WorkloadRef.APIVersion,
APIVersion: rollout.Spec.ObjectRef.WorkloadRef.APIVersion, Kind: rollout.Spec.WorkloadRef.Kind,
Kind: rollout.Spec.ObjectRef.WorkloadRef.Kind, Name: rollout.Spec.WorkloadRef.Name,
Name: rollout.Spec.ObjectRef.WorkloadRef.Name,
},
}, },
ReleasePlan: v1beta1.ReleasePlan{ ReleasePlan: v1beta1.ReleasePlan{
Batches: batches, Batches: batches,
RolloutID: rolloutID, RolloutID: rolloutID,
BatchPartition: utilpointer.Int32Ptr(batch), BatchPartition: utilpointer.Int32Ptr(batch),
FailureThreshold: rollout.Spec.Strategy.Canary.FailureThreshold, FailureThreshold: rollout.Spec.Strategy.Canary.FailureThreshold,
PatchPodTemplateMetadata: rollout.Spec.Strategy.Canary.PatchPodTemplateMetadata, PatchPodTemplateMetadata: rollout.Spec.Strategy.Canary.PatchPodTemplateMetadata,
EnableExtraWorkloadForCanary: rollout.Spec.Strategy.Canary.EnableExtraWorkloadForCanary,
}, },
}, },
} }
@ -374,9 +365,6 @@ func createBatchRelease(rollout *v1beta1.Rollout, rolloutID string, batch int32,
if isRollback { if isRollback {
annotations[v1alpha1.RollbackInBatchAnnotation] = rollout.Annotations[v1alpha1.RollbackInBatchAnnotation] annotations[v1alpha1.RollbackInBatchAnnotation] = rollout.Annotations[v1alpha1.RollbackInBatchAnnotation]
} }
if style, ok := rollout.Annotations[v1beta1.RolloutStyleAnnotation]; ok {
annotations[v1beta1.RolloutStyleAnnotation] = style
}
if len(annotations) > 0 { if len(annotations) > 0 {
br.Annotations = annotations br.Annotations = annotations
} }

View File

@ -57,6 +57,7 @@ func TestRunCanary(t *testing.T) {
}, },
getRollout: func() (*v1beta1.Rollout, *v1beta1.BatchRelease) { getRollout: func() (*v1beta1.Rollout, *v1beta1.BatchRelease) {
obj := rolloutDemo.DeepCopy() obj := rolloutDemo.DeepCopy()
obj.Spec.Strategy.Canary.EnableExtraWorkloadForCanary = true
obj.Status.CanaryStatus.ObservedWorkloadGeneration = 2 obj.Status.CanaryStatus.ObservedWorkloadGeneration = 2
obj.Status.CanaryStatus.RolloutHash = "f55bvd874d5f2fzvw46bv966x4bwbdv4wx6bd9f7b46ww788954b8z8w29b7wxfd" obj.Status.CanaryStatus.RolloutHash = "f55bvd874d5f2fzvw46bv966x4bwbdv4wx6bd9f7b46ww788954b8z8w29b7wxfd"
obj.Status.CanaryStatus.StableRevision = "pod-template-hash-v1" obj.Status.CanaryStatus.StableRevision = "pod-template-hash-v1"
@ -98,6 +99,7 @@ func TestRunCanary(t *testing.T) {
}, },
} }
br.Spec.ReleasePlan.BatchPartition = utilpointer.Int32(0) br.Spec.ReleasePlan.BatchPartition = utilpointer.Int32(0)
br.Spec.ReleasePlan.EnableExtraWorkloadForCanary = true
return br return br
}, },
}, },
@ -130,6 +132,7 @@ func TestRunCanary(t *testing.T) {
}, },
getRollout: func() (*v1beta1.Rollout, *v1beta1.BatchRelease) { getRollout: func() (*v1beta1.Rollout, *v1beta1.BatchRelease) {
obj := rolloutDemo.DeepCopy() obj := rolloutDemo.DeepCopy()
obj.Spec.Strategy.Canary.EnableExtraWorkloadForCanary = true
obj.Status.CanaryStatus.ObservedWorkloadGeneration = 2 obj.Status.CanaryStatus.ObservedWorkloadGeneration = 2
obj.Status.CanaryStatus.RolloutHash = "f55bvd874d5f2fzvw46bv966x4bwbdv4wx6bd9f7b46ww788954b8z8w29b7wxfd" obj.Status.CanaryStatus.RolloutHash = "f55bvd874d5f2fzvw46bv966x4bwbdv4wx6bd9f7b46ww788954b8z8w29b7wxfd"
obj.Status.CanaryStatus.StableRevision = "pod-template-hash-v1" obj.Status.CanaryStatus.StableRevision = "pod-template-hash-v1"
@ -155,9 +158,10 @@ func TestRunCanary(t *testing.T) {
}, },
} }
br.Spec.ReleasePlan.BatchPartition = utilpointer.Int32(0) br.Spec.ReleasePlan.BatchPartition = utilpointer.Int32(0)
br.Spec.ReleasePlan.EnableExtraWorkloadForCanary = true
br.Status = v1beta1.BatchReleaseStatus{ br.Status = v1beta1.BatchReleaseStatus{
ObservedGeneration: 1, ObservedGeneration: 1,
ObservedReleasePlanHash: "6d6a40791161e88ec0483688e951b589a4cbd0bf351974827706b79f99378fd5", ObservedReleasePlanHash: "d444a1007776da957d7d8549e3375c96179621b85670ad1e2bb0fc5fea16446a",
CanaryStatus: v1beta1.BatchReleaseCanaryStatus{ CanaryStatus: v1beta1.BatchReleaseCanaryStatus{
CurrentBatchState: v1beta1.ReadyBatchState, CurrentBatchState: v1beta1.ReadyBatchState,
CurrentBatch: 0, CurrentBatch: 0,
@ -200,6 +204,7 @@ func TestRunCanary(t *testing.T) {
}, },
} }
br.Spec.ReleasePlan.BatchPartition = utilpointer.Int32(0) br.Spec.ReleasePlan.BatchPartition = utilpointer.Int32(0)
br.Spec.ReleasePlan.EnableExtraWorkloadForCanary = true
return br return br
}, },
}, },

View File

@ -111,10 +111,10 @@ func (r *RolloutReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
klog.Infof("Begin to reconcile Rollout %v", klog.KObj(rollout)) klog.Infof("Begin to reconcile Rollout %v", klog.KObj(rollout))
// If workload watcher does not exist, then add the watcher dynamically // If workload watcher does not exist, then add the watcher dynamically
workloadRef := rollout.Spec.ObjectRef.WorkloadRef workloadRef := rollout.Spec.WorkloadRef
workloadGVK := util.GetGVKFrom(workloadRef) workloadGVK := util.GetGVKFrom(&workloadRef)
_, exists := watchedWorkload.Load(workloadGVK.String()) _, exists := watchedWorkload.Load(workloadGVK.String())
if workloadRef != nil && !exists { if !exists {
succeeded, err := util.AddWatcherDynamically(runtimeController, workloadHandler, workloadGVK) succeeded, err := util.AddWatcherDynamically(runtimeController, workloadHandler, workloadGVK)
if err != nil { if err != nil {
return ctrl.Result{}, err return ctrl.Result{}, err

View File

@ -48,45 +48,44 @@ var (
}, },
}, },
Spec: v1beta1.RolloutSpec{ Spec: v1beta1.RolloutSpec{
ObjectRef: v1beta1.ObjectRef{ WorkloadRef: v1beta1.ObjectRef{
WorkloadRef: &v1beta1.WorkloadRef{ APIVersion: "apps/v1",
APIVersion: "apps/v1", Kind: "Deployment",
Kind: "Deployment", Name: "echoserver",
Name: "echoserver",
},
}, },
Strategy: v1beta1.RolloutStrategy{ Strategy: v1beta1.RolloutStrategy{
Canary: &v1beta1.CanaryStrategy{ Canary: &v1beta1.CanaryStrategy{
EnableExtraWorkloadForCanary: true,
Steps: []v1beta1.CanaryStep{ Steps: []v1beta1.CanaryStep{
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(5), Traffic: utilpointer.String("5%"),
}, },
Replicas: &intstr.IntOrString{IntVal: 1}, Replicas: &intstr.IntOrString{IntVal: 1},
}, },
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(20), Traffic: utilpointer.String("20%"),
}, },
Replicas: &intstr.IntOrString{IntVal: 2}, Replicas: &intstr.IntOrString{IntVal: 2},
}, },
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(60), Traffic: utilpointer.String("60%"),
}, },
Replicas: &intstr.IntOrString{IntVal: 6}, Replicas: &intstr.IntOrString{IntVal: 6},
}, },
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(100), Traffic: utilpointer.String("100%"),
}, },
Replicas: &intstr.IntOrString{IntVal: 10}, Replicas: &intstr.IntOrString{IntVal: 10},
}, },
}, },
TrafficRoutings: []v1alpha1.TrafficRoutingRef{ TrafficRoutings: []v1beta1.TrafficRoutingRef{
{ {
Service: "echoserver", Service: "echoserver",
Ingress: &v1alpha1.IngressTrafficRouting{ Ingress: &v1beta1.IngressTrafficRouting{
Name: "echoserver", Name: "echoserver",
}, },
}, },
@ -207,12 +206,10 @@ var (
Generation: 1, Generation: 1,
}, },
Spec: v1beta1.BatchReleaseSpec{ Spec: v1beta1.BatchReleaseSpec{
TargetRef: v1beta1.ObjectRef{ WorkloadRef: v1beta1.ObjectRef{
WorkloadRef: &v1beta1.WorkloadRef{ APIVersion: "apps/v1",
APIVersion: "apps/v1", Kind: "Deployment",
Kind: "Deployment", Name: "echoserver",
Name: "echoserver",
},
}, },
}, },
Status: v1beta1.BatchReleaseStatus{}, Status: v1beta1.BatchReleaseStatus{},

View File

@ -85,7 +85,7 @@ func (w *enqueueRequestForWorkload) getRolloutForWorkload(key types.NamespacedNa
} }
for _, rollout := range rList.Items { for _, rollout := range rList.Items {
targetRef := rollout.Spec.ObjectRef.WorkloadRef targetRef := rollout.Spec.WorkloadRef
targetGV, err := schema.ParseGroupVersion(targetRef.APIVersion) targetGV, err := schema.ParseGroupVersion(targetRef.APIVersion)
if err != nil { if err != nil {
klog.Errorf("failed to parse rollout(%s/%s) targetRef's group version: %s", rollout.Namespace, rollout.Name, targetRef.APIVersion) klog.Errorf("failed to parse rollout(%s/%s) targetRef's group version: %s", rollout.Namespace, rollout.Name, targetRef.APIVersion)

View File

@ -19,7 +19,6 @@ package rollout
import ( import (
"context" "context"
"fmt" "fmt"
"strconv"
"time" "time"
"github.com/openkruise/rollouts/api/v1alpha1" "github.com/openkruise/rollouts/api/v1alpha1"
@ -391,12 +390,7 @@ func (r *RolloutReconciler) recalculateCanaryStep(c *RolloutContext) (int32, err
for i := range c.Rollout.Spec.Strategy.Canary.Steps { for i := range c.Rollout.Spec.Strategy.Canary.Steps {
step := c.Rollout.Spec.Strategy.Canary.Steps[i] step := c.Rollout.Spec.Strategy.Canary.Steps[i]
var desiredReplicas int var desiredReplicas int
if step.Replicas != nil { desiredReplicas, _ = intstr.GetScaledValueFromIntOrPercent(step.Replicas, int(c.Workload.Replicas), true)
desiredReplicas, _ = intstr.GetScaledValueFromIntOrPercent(step.Replicas, int(c.Workload.Replicas), true)
} else {
replicas := intstr.FromString(strconv.Itoa(int(*step.Weight)) + "%")
desiredReplicas, _ = intstr.GetScaledValueFromIntOrPercent(&replicas, int(c.Workload.Replicas), true)
}
stepIndex = int32(i + 1) stepIndex = int32(i + 1)
if currentReplicas <= desiredReplicas { if currentReplicas <= desiredReplicas {
break break

View File

@ -660,19 +660,13 @@ func TestReCalculateCanaryStepIndex(t *testing.T) {
obj := rolloutDemo.DeepCopy() obj := rolloutDemo.DeepCopy()
obj.Spec.Strategy.Canary.Steps = []v1beta1.CanaryStep{ obj.Spec.Strategy.Canary.Steps = []v1beta1.CanaryStep{
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "20%"},
Weight: utilpointer.Int32(20),
},
}, },
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "50%"},
Weight: utilpointer.Int32(50),
},
}, },
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "100%"},
Weight: utilpointer.Int32(100),
},
}, },
} }
return obj return obj
@ -705,19 +699,13 @@ func TestReCalculateCanaryStepIndex(t *testing.T) {
obj := rolloutDemo.DeepCopy() obj := rolloutDemo.DeepCopy()
obj.Spec.Strategy.Canary.Steps = []v1beta1.CanaryStep{ obj.Spec.Strategy.Canary.Steps = []v1beta1.CanaryStep{
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "20%"},
Weight: utilpointer.Int32(20),
},
}, },
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "40%"},
Weight: utilpointer.Int32(40),
},
}, },
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "100%"},
Weight: utilpointer.Int32(100),
},
}, },
} }
return obj return obj
@ -750,19 +738,13 @@ func TestReCalculateCanaryStepIndex(t *testing.T) {
obj := rolloutDemo.DeepCopy() obj := rolloutDemo.DeepCopy()
obj.Spec.Strategy.Canary.Steps = []v1beta1.CanaryStep{ obj.Spec.Strategy.Canary.Steps = []v1beta1.CanaryStep{
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "40%"},
Weight: utilpointer.Int32(40),
},
}, },
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "60%"},
Weight: utilpointer.Int32(60),
},
}, },
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "100%"},
Weight: utilpointer.Int32(100),
},
}, },
} }
return obj return obj
@ -795,19 +777,13 @@ func TestReCalculateCanaryStepIndex(t *testing.T) {
obj := rolloutDemo.DeepCopy() obj := rolloutDemo.DeepCopy()
obj.Spec.Strategy.Canary.Steps = []v1beta1.CanaryStep{ obj.Spec.Strategy.Canary.Steps = []v1beta1.CanaryStep{
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "10%"},
Weight: utilpointer.Int32(10),
},
}, },
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "30%"},
Weight: utilpointer.Int32(30),
},
}, },
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "100%"},
Weight: utilpointer.Int32(100),
},
}, },
} }
return obj return obj
@ -840,8 +816,8 @@ func TestReCalculateCanaryStepIndex(t *testing.T) {
obj := rolloutDemo.DeepCopy() obj := rolloutDemo.DeepCopy()
obj.Spec.Strategy.Canary.Steps = []v1beta1.CanaryStep{ obj.Spec.Strategy.Canary.Steps = []v1beta1.CanaryStep{
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(2), Traffic: utilpointer.String("2%"),
}, },
Replicas: &intstr.IntOrString{ Replicas: &intstr.IntOrString{
Type: intstr.String, Type: intstr.String,
@ -849,8 +825,8 @@ func TestReCalculateCanaryStepIndex(t *testing.T) {
}, },
}, },
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(3), Traffic: utilpointer.String("3%"),
}, },
Replicas: &intstr.IntOrString{ Replicas: &intstr.IntOrString{
Type: intstr.String, Type: intstr.String,

View File

@ -20,7 +20,6 @@ import (
"context" "context"
"testing" "testing"
"github.com/openkruise/rollouts/api/v1alpha1"
"github.com/openkruise/rollouts/api/v1beta1" "github.com/openkruise/rollouts/api/v1beta1"
"github.com/openkruise/rollouts/pkg/trafficrouting" "github.com/openkruise/rollouts/pkg/trafficrouting"
"github.com/openkruise/rollouts/pkg/util" "github.com/openkruise/rollouts/pkg/util"
@ -43,7 +42,7 @@ func TestCalculateRolloutHash(t *testing.T) {
return obj return obj
}, },
expectHash: func() string { expectHash: func() string {
return "626fd556c5d5v2d9b4f7c2xvbc9dxddxzd48xvb9w9wfcdvdz6v959fbzd84b57x" return "75746v7d5z9x59v5c7dff4wd9cv9cc28czf6c2z664w7zbb7vw2bzv76v99z6bd9"
}, },
}, },
{ {
@ -56,7 +55,7 @@ func TestCalculateRolloutHash(t *testing.T) {
return obj return obj
}, },
expectHash: func() string { expectHash: func() string {
return "626fd556c5d5v2d9b4f7c2xvbc9dxddxzd48xvb9w9wfcdvdz6v959fbzd84b57x" return "75746v7d5z9x59v5c7dff4wd9cv9cc28czf6c2z664w7zbb7vw2bzv76v99z6bd9"
}, },
}, },
{ {
@ -65,20 +64,20 @@ func TestCalculateRolloutHash(t *testing.T) {
obj := rolloutDemo.DeepCopy() obj := rolloutDemo.DeepCopy()
obj.Spec.Strategy.Canary.Steps = []v1beta1.CanaryStep{ obj.Spec.Strategy.Canary.Steps = []v1beta1.CanaryStep{
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(50), Traffic: utilpointer.String("50%"),
}, },
}, },
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(100), Traffic: utilpointer.String("100%"),
}, },
}, },
} }
return obj return obj
}, },
expectHash: func() string { expectHash: func() string {
return "8c449wxc46x8dd764x4v4wzvc7454f48478vd9db27fv8v9dw5cwbcb6b42b75dc" return "db9c2x47d282c84z6684d598bzwf9b4x6ffb45fc456xdfv97945v2vb79w72c7z"
}, },
}, },
} }

View File

@ -215,12 +215,15 @@ func (r *TrafficRoutingReconciler) SetupWithManager(mgr ctrl.Manager) error {
} }
func newTrafficRoutingContext(tr *v1alpha1.TrafficRouting) *trafficrouting.TrafficRoutingContext { func newTrafficRoutingContext(tr *v1alpha1.TrafficRouting) *trafficrouting.TrafficRoutingContext {
return &trafficrouting.TrafficRoutingContext{ c := &trafficrouting.TrafficRoutingContext{
Key: fmt.Sprintf("TrafficRouting(%s/%s)", tr.Namespace, tr.Name), Key: fmt.Sprintf("TrafficRouting(%s/%s)", tr.Namespace, tr.Name),
Namespace: tr.Namespace, Namespace: tr.Namespace,
ObjectRef: tr.Spec.ObjectRef, Strategy: v1alpha1.ConversionToV1beta1TrafficRoutingStrategy(tr.Spec.Strategy),
Strategy: tr.Spec.Strategy,
OwnerRef: *metav1.NewControllerRef(tr, trControllerKind), OwnerRef: *metav1.NewControllerRef(tr, trControllerKind),
OnlyTrafficRouting: true, OnlyTrafficRouting: true,
} }
for _, ref := range tr.Spec.ObjectRef {
c.ObjectRef = append(c.ObjectRef, v1alpha1.ConversionToV1beta1TrafficRoutingRef(ref))
}
return c
} }

View File

@ -21,7 +21,7 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/openkruise/rollouts/api/v1alpha1" "github.com/openkruise/rollouts/api/v1beta1"
"github.com/openkruise/rollouts/pkg/trafficrouting/network" "github.com/openkruise/rollouts/pkg/trafficrouting/network"
custom "github.com/openkruise/rollouts/pkg/trafficrouting/network/customNetworkProvider" custom "github.com/openkruise/rollouts/pkg/trafficrouting/network/customNetworkProvider"
"github.com/openkruise/rollouts/pkg/trafficrouting/network/gateway" "github.com/openkruise/rollouts/pkg/trafficrouting/network/gateway"
@ -44,8 +44,8 @@ type TrafficRoutingContext struct {
// only for log info // only for log info
Key string Key string
Namespace string Namespace string
ObjectRef []v1alpha1.TrafficRoutingRef ObjectRef []v1beta1.TrafficRoutingRef
Strategy v1alpha1.TrafficRoutingStrategy Strategy v1beta1.TrafficRoutingStrategy
// OnlyTrafficRouting // OnlyTrafficRouting
OnlyTrafficRouting bool OnlyTrafficRouting bool
OwnerRef metav1.OwnerReference OwnerRef metav1.OwnerReference
@ -100,7 +100,7 @@ func (m *Manager) DoTrafficRouting(c *TrafficRoutingContext) (bool, error) {
if trafficRouting.GracePeriodSeconds <= 0 { if trafficRouting.GracePeriodSeconds <= 0 {
trafficRouting.GracePeriodSeconds = defaultGracePeriodSeconds trafficRouting.GracePeriodSeconds = defaultGracePeriodSeconds
} }
if c.Strategy.Weight == nil && len(c.Strategy.Matches) == 0 { if c.Strategy.Traffic == nil && len(c.Strategy.Matches) == 0 {
return true, nil return true, nil
} }
@ -228,7 +228,7 @@ func (m *Manager) FinalisingTrafficRouting(c *TrafficRoutingContext, onlyRestore
} }
// First route 100% traffic to stable service // First route 100% traffic to stable service
c.Strategy.Weight = utilpointer.Int32(0) c.Strategy.Traffic = utilpointer.StringPtr("0%")
verify, err = trController.EnsureRoutes(context.TODO(), &c.Strategy) verify, err = trController.EnsureRoutes(context.TODO(), &c.Strategy)
if err != nil { if err != nil {
return false, err return false, err

View File

@ -113,45 +113,43 @@ var (
}, },
}, },
Spec: v1beta1.RolloutSpec{ Spec: v1beta1.RolloutSpec{
ObjectRef: v1beta1.ObjectRef{ WorkloadRef: v1beta1.ObjectRef{
WorkloadRef: &v1beta1.WorkloadRef{ APIVersion: "apps/v1",
APIVersion: "apps/v1", Kind: "Deployment",
Kind: "Deployment", Name: "echoserver",
Name: "echoserver",
},
}, },
Strategy: v1beta1.RolloutStrategy{ Strategy: v1beta1.RolloutStrategy{
Canary: &v1beta1.CanaryStrategy{ Canary: &v1beta1.CanaryStrategy{
Steps: []v1beta1.CanaryStep{ Steps: []v1beta1.CanaryStep{
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(5), Traffic: utilpointer.String("5%"),
}, },
Replicas: &intstr.IntOrString{IntVal: 1}, Replicas: &intstr.IntOrString{IntVal: 1},
}, },
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(20), Traffic: utilpointer.String("20%"),
}, },
Replicas: &intstr.IntOrString{IntVal: 2}, Replicas: &intstr.IntOrString{IntVal: 2},
}, },
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(60), Traffic: utilpointer.String("60%"),
}, },
Replicas: &intstr.IntOrString{IntVal: 6}, Replicas: &intstr.IntOrString{IntVal: 6},
}, },
{ {
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(100), Traffic: utilpointer.String("100%"),
}, },
Replicas: &intstr.IntOrString{IntVal: 10}, Replicas: &intstr.IntOrString{IntVal: 10},
}, },
}, },
TrafficRoutings: []v1alpha1.TrafficRoutingRef{ TrafficRoutings: []v1beta1.TrafficRoutingRef{
{ {
Service: "echoserver", Service: "echoserver",
Ingress: &v1alpha1.IngressTrafficRouting{ Ingress: &v1beta1.IngressTrafficRouting{
Name: "echoserver", Name: "echoserver",
}, },
}, },

View File

@ -23,20 +23,20 @@ import (
"reflect" "reflect"
"strings" "strings"
"k8s.io/apimachinery/pkg/api/errors" "github.com/openkruise/rollouts/api/v1beta1"
"k8s.io/klog/v2"
rolloutv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
"github.com/openkruise/rollouts/pkg/trafficrouting/network" "github.com/openkruise/rollouts/pkg/trafficrouting/network"
"github.com/openkruise/rollouts/pkg/util" "github.com/openkruise/rollouts/pkg/util"
"github.com/openkruise/rollouts/pkg/util/configuration" "github.com/openkruise/rollouts/pkg/util/configuration"
"github.com/openkruise/rollouts/pkg/util/luamanager" "github.com/openkruise/rollouts/pkg/util/luamanager"
lua "github.com/yuin/gopher-lua" lua "github.com/yuin/gopher-lua"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/klog/v2"
utilpointer "k8s.io/utils/pointer" utilpointer "k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
) )
@ -50,7 +50,7 @@ type LuaData struct {
Data Data Data Data
CanaryWeight int32 CanaryWeight int32
StableWeight int32 StableWeight int32
Matches []rolloutv1alpha1.HttpRouteMatch Matches []v1beta1.HttpRouteMatch
CanaryService string CanaryService string
StableService string StableService string
} }
@ -72,7 +72,7 @@ type Config struct {
CanaryService string CanaryService string
StableService string StableService string
// network providers need to be created // network providers need to be created
TrafficConf []rolloutv1alpha1.CustomNetworkRef TrafficConf []v1beta1.ObjectRef
OwnerRef metav1.OwnerReference OwnerRef metav1.OwnerReference
} }
@ -107,11 +107,11 @@ func (r *customController) Initialize(ctx context.Context) error {
} }
// when ensuring routes, first execute lua for all custom providers, then update // when ensuring routes, first execute lua for all custom providers, then update
func (r *customController) EnsureRoutes(ctx context.Context, strategy *rolloutv1alpha1.TrafficRoutingStrategy) (bool, error) { func (r *customController) EnsureRoutes(ctx context.Context, strategy *v1beta1.TrafficRoutingStrategy) (bool, error) {
done := true done := true
// *strategy.Weight == 0 indicates traffic routing is doing finalising and tries to route whole traffic to stable service // *strategy.Weight == 0 indicates traffic routing is doing finalising and tries to route whole traffic to stable service
// then directly do finalising // then directly do finalising
if strategy.Weight != nil && *strategy.Weight == 0 { if strategy.Traffic != nil && *strategy.Traffic == "0%" {
return true, nil return true, nil
} }
var err error var err error
@ -254,8 +254,13 @@ func (r *customController) restoreObject(obj *unstructured.Unstructured) error {
return nil return nil
} }
func (r *customController) executeLuaForCanary(spec Data, strategy *rolloutv1alpha1.TrafficRoutingStrategy, luaScript string) (Data, error) { func (r *customController) executeLuaForCanary(spec Data, strategy *v1beta1.TrafficRoutingStrategy, luaScript string) (Data, error) {
weight := strategy.Weight var weight *int32
if strategy.Traffic != nil {
is := intstr.FromString(*strategy.Traffic)
weightInt, _ := intstr.GetScaledValueFromIntOrPercent(&is, 100, true)
weight = utilpointer.Int32(int32(weightInt))
}
matches := strategy.Matches matches := strategy.Matches
if weight == nil { if weight == nil {
// the lua script does not have a pointer type, // the lua script does not have a pointer type,
@ -296,7 +301,7 @@ func (r *customController) executeLuaForCanary(spec Data, strategy *rolloutv1alp
return Data{}, fmt.Errorf("expect table output from Lua script, not %s", returnValue.Type().String()) return Data{}, fmt.Errorf("expect table output from Lua script, not %s", returnValue.Type().String())
} }
func (r *customController) getLuaScript(ctx context.Context, ref rolloutv1alpha1.CustomNetworkRef) (string, error) { func (r *customController) getLuaScript(ctx context.Context, ref v1beta1.ObjectRef) (string, error) {
// get local lua script // get local lua script
// luaScript.Provider: CRDGroupt/Kind // luaScript.Provider: CRDGroupt/Kind
group := strings.Split(ref.APIVersion, "/")[0] group := strings.Split(ref.APIVersion, "/")[0]

View File

@ -28,7 +28,7 @@ import (
rolloutapi "github.com/openkruise/rollouts/api" rolloutapi "github.com/openkruise/rollouts/api"
rolloutsv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1" rolloutsv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
rolloutsv1beta1 "github.com/openkruise/rollouts/api/v1beta1" "github.com/openkruise/rollouts/api/v1beta1"
"github.com/openkruise/rollouts/pkg/util" "github.com/openkruise/rollouts/pkg/util"
"github.com/openkruise/rollouts/pkg/util/configuration" "github.com/openkruise/rollouts/pkg/util/configuration"
"github.com/openkruise/rollouts/pkg/util/luamanager" "github.com/openkruise/rollouts/pkg/util/luamanager"
@ -38,6 +38,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
clientgoscheme "k8s.io/client-go/kubernetes/scheme" clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/klog/v2" "k8s.io/klog/v2"
utilpointer "k8s.io/utils/pointer" utilpointer "k8s.io/utils/pointer"
@ -141,7 +142,7 @@ func TestInitialize(t *testing.T) {
return Config{ return Config{
StableService: "echoserver", StableService: "echoserver",
CanaryService: "echoserver-canary", CanaryService: "echoserver-canary",
TrafficConf: []rolloutsv1alpha1.CustomNetworkRef{ TrafficConf: []v1beta1.ObjectRef{
{ {
APIVersion: "networking.istio.io/v1alpha3", APIVersion: "networking.istio.io/v1alpha3",
Kind: "VirtualService", Kind: "VirtualService",
@ -174,7 +175,7 @@ func TestInitialize(t *testing.T) {
return Config{ return Config{
StableService: "echoserver", StableService: "echoserver",
CanaryService: "echoserver-canary", CanaryService: "echoserver-canary",
TrafficConf: []rolloutsv1alpha1.CustomNetworkRef{ TrafficConf: []v1beta1.ObjectRef{
{ {
APIVersion: "networking.test.io/v1alpha3", APIVersion: "networking.test.io/v1alpha3",
Kind: "VirtualService", Kind: "VirtualService",
@ -237,7 +238,7 @@ func TestEnsureRoutes(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
getLua func() map[string]string getLua func() map[string]string
getRoutes func() *rolloutsv1alpha1.TrafficRoutingStrategy getRoutes func() *v1beta1.TrafficRoutingStrategy
getUnstructureds func() []*unstructured.Unstructured getUnstructureds func() []*unstructured.Unstructured
getConfig func() Config getConfig func() Config
expectState func() (bool, bool) expectState func() (bool, bool)
@ -245,9 +246,9 @@ func TestEnsureRoutes(t *testing.T) {
}{ }{
{ {
name: "test1, do traffic routing for VirtualService and DestinationRule", name: "test1, do traffic routing for VirtualService and DestinationRule",
getRoutes: func() *rolloutsv1alpha1.TrafficRoutingStrategy { getRoutes: func() *v1beta1.TrafficRoutingStrategy {
return &rolloutsv1alpha1.TrafficRoutingStrategy{ return &v1beta1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(5), Traffic: utilpointer.String("5%"),
} }
}, },
getUnstructureds: func() []*unstructured.Unstructured { getUnstructureds: func() []*unstructured.Unstructured {
@ -268,7 +269,7 @@ func TestEnsureRoutes(t *testing.T) {
Key: "rollout-demo", Key: "rollout-demo",
StableService: "echoserver", StableService: "echoserver",
CanaryService: "echoserver-canary", CanaryService: "echoserver-canary",
TrafficConf: []rolloutsv1alpha1.CustomNetworkRef{ TrafficConf: []v1beta1.ObjectRef{
{ {
APIVersion: "networking.istio.io/v1alpha3", APIVersion: "networking.istio.io/v1alpha3",
Kind: "VirtualService", Kind: "VirtualService",
@ -317,9 +318,9 @@ func TestEnsureRoutes(t *testing.T) {
}, },
{ {
name: "test2, do traffic routing but failed to execute lua", name: "test2, do traffic routing but failed to execute lua",
getRoutes: func() *rolloutsv1alpha1.TrafficRoutingStrategy { getRoutes: func() *v1beta1.TrafficRoutingStrategy {
return &rolloutsv1alpha1.TrafficRoutingStrategy{ return &v1beta1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(5), Traffic: utilpointer.String("5%"),
} }
}, },
getUnstructureds: func() []*unstructured.Unstructured { getUnstructureds: func() []*unstructured.Unstructured {
@ -340,7 +341,7 @@ func TestEnsureRoutes(t *testing.T) {
Key: "rollout-demo", Key: "rollout-demo",
StableService: "echoserver", StableService: "echoserver",
CanaryService: "echoserver-canary", CanaryService: "echoserver-canary",
TrafficConf: []rolloutsv1alpha1.CustomNetworkRef{ TrafficConf: []v1beta1.ObjectRef{
{ {
APIVersion: "networking.istio.io/v1alpha3", APIVersion: "networking.istio.io/v1alpha3",
Kind: "VirtualService", Kind: "VirtualService",
@ -446,7 +447,7 @@ func TestFinalise(t *testing.T) {
return Config{ return Config{
StableService: "echoserver", StableService: "echoserver",
CanaryService: "echoserver-canary", CanaryService: "echoserver-canary",
TrafficConf: []rolloutsv1alpha1.CustomNetworkRef{ TrafficConf: []v1beta1.ObjectRef{
{ {
APIVersion: "networking.istio.io/v1alpha3", APIVersion: "networking.istio.io/v1alpha3",
Kind: "VirtualService", Kind: "VirtualService",
@ -482,7 +483,7 @@ func TestFinalise(t *testing.T) {
} }
type TestCase struct { type TestCase struct {
Rollout *rolloutsv1beta1.Rollout `json:"rollout,omitempty"` Rollout *v1beta1.Rollout `json:"rollout,omitempty"`
TrafficRouting *rolloutsv1alpha1.TrafficRouting `json:"trafficRouting,omitempty"` TrafficRouting *rolloutsv1alpha1.TrafficRouting `json:"trafficRouting,omitempty"`
Original *unstructured.Unstructured `json:"original,omitempty"` Original *unstructured.Unstructured `json:"original,omitempty"`
Expected []*unstructured.Unstructured `json:"expected,omitempty"` Expected []*unstructured.Unstructured `json:"expected,omitempty"`
@ -522,8 +523,12 @@ func TestLuaScript(t *testing.T) {
if rollout != nil { if rollout != nil {
steps := rollout.Spec.Strategy.Canary.Steps steps := rollout.Spec.Strategy.Canary.Steps
for i, step := range steps { for i, step := range steps {
weight := step.TrafficRoutingStrategy.Weight var weight *int32
if weight == nil { if step.TrafficRoutingStrategy.Traffic != nil {
is := intstr.FromString(*step.TrafficRoutingStrategy.Traffic)
weightInt, _ := intstr.GetScaledValueFromIntOrPercent(&is, 100, true)
weight = utilpointer.Int32(int32(weightInt))
} else {
weight = utilpointer.Int32(-1) weight = utilpointer.Int32(-1)
} }
var canaryService string var canaryService string
@ -563,13 +568,19 @@ func TestLuaScript(t *testing.T) {
var canaryService string var canaryService string
stableService := trafficRouting.Spec.ObjectRef[0].Service stableService := trafficRouting.Spec.ObjectRef[0].Service
canaryService = stableService canaryService = stableService
matches := make([]v1beta1.HttpRouteMatch, 0)
for _, match := range trafficRouting.Spec.Strategy.Matches {
obj := v1beta1.HttpRouteMatch{}
obj.Headers = match.Headers
matches = append(matches, obj)
}
data := &LuaData{ data := &LuaData{
Data: Data{ Data: Data{
Labels: testCase.Original.GetLabels(), Labels: testCase.Original.GetLabels(),
Annotations: testCase.Original.GetAnnotations(), Annotations: testCase.Original.GetAnnotations(),
Spec: testCase.Original.Object["spec"], Spec: testCase.Original.Object["spec"],
}, },
Matches: trafficRouting.Spec.Strategy.Matches, Matches: matches,
CanaryWeight: *weight, CanaryWeight: *weight,
StableWeight: 100 - *weight, StableWeight: 100 - *weight,
CanaryService: canaryService, CanaryService: canaryService,
@ -615,6 +626,7 @@ func getLuaTestCase(t *testing.T, path string) (*TestCase, error) {
t.Errorf("failed to read file %s", path) t.Errorf("failed to read file %s", path)
return nil, err return nil, err
} }
fmt.Println(string(yamlFile))
luaTestCase := &TestCase{} luaTestCase := &TestCase{}
err = yaml.Unmarshal(yamlFile, luaTestCase) err = yaml.Unmarshal(yamlFile, luaTestCase)
if err != nil { if err != nil {

View File

@ -17,10 +17,11 @@ import (
"context" "context"
"reflect" "reflect"
rolloutv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1" "github.com/openkruise/rollouts/api/v1beta1"
"github.com/openkruise/rollouts/pkg/trafficrouting/network" "github.com/openkruise/rollouts/pkg/trafficrouting/network"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/util/retry" "k8s.io/client-go/util/retry"
"k8s.io/klog/v2" "k8s.io/klog/v2"
utilpointer "k8s.io/utils/pointer" utilpointer "k8s.io/utils/pointer"
@ -34,7 +35,7 @@ type Config struct {
Namespace string Namespace string
CanaryService string CanaryService string
StableService string StableService string
TrafficConf *rolloutv1alpha1.GatewayTrafficRouting TrafficConf *v1beta1.GatewayTrafficRouting
} }
type gatewayController struct { type gatewayController struct {
@ -56,8 +57,13 @@ func (r *gatewayController) Initialize(ctx context.Context) error {
return r.Get(ctx, types.NamespacedName{Namespace: r.conf.Namespace, Name: *r.conf.TrafficConf.HTTPRouteName}, route) return r.Get(ctx, types.NamespacedName{Namespace: r.conf.Namespace, Name: *r.conf.TrafficConf.HTTPRouteName}, route)
} }
func (r *gatewayController) EnsureRoutes(ctx context.Context, strategy *rolloutv1alpha1.TrafficRoutingStrategy) (bool, error) { func (r *gatewayController) EnsureRoutes(ctx context.Context, strategy *v1beta1.TrafficRoutingStrategy) (bool, error) {
weight := strategy.Weight var weight *int32
if strategy.Traffic != nil {
is := intstr.FromString(*strategy.Traffic)
weightInt, _ := intstr.GetScaledValueFromIntOrPercent(&is, 100, true)
weight = utilpointer.Int32(int32(weightInt))
}
matches := strategy.Matches matches := strategy.Matches
// headerModifier := strategy.RequestHeaderModifier // headerModifier := strategy.RequestHeaderModifier
var httpRoute gatewayv1alpha2.HTTPRoute var httpRoute gatewayv1alpha2.HTTPRoute
@ -118,7 +124,7 @@ func (r *gatewayController) Finalise(ctx context.Context) error {
return nil return nil
} }
func (r *gatewayController) buildDesiredHTTPRoute(rules []gatewayv1alpha2.HTTPRouteRule, weight *int32, matches []rolloutv1alpha1.HttpRouteMatch, func (r *gatewayController) buildDesiredHTTPRoute(rules []gatewayv1alpha2.HTTPRouteRule, weight *int32, matches []v1beta1.HttpRouteMatch,
rh *gatewayv1alpha2.HTTPRequestHeaderFilter) []gatewayv1alpha2.HTTPRouteRule { rh *gatewayv1alpha2.HTTPRequestHeaderFilter) []gatewayv1alpha2.HTTPRouteRule {
var desired []gatewayv1alpha2.HTTPRouteRule var desired []gatewayv1alpha2.HTTPRouteRule
// Only when finalize method parameter weight=-1, // Only when finalize method parameter weight=-1,
@ -146,7 +152,7 @@ func (r *gatewayController) buildDesiredHTTPRoute(rules []gatewayv1alpha2.HTTPRo
return r.buildCanaryWeightHttpRoutes(rules, weight) return r.buildCanaryWeightHttpRoutes(rules, weight)
} }
func (r *gatewayController) buildCanaryHeaderHttpRoutes(rules []gatewayv1alpha2.HTTPRouteRule, matchs []rolloutv1alpha1.HttpRouteMatch) []gatewayv1alpha2.HTTPRouteRule { func (r *gatewayController) buildCanaryHeaderHttpRoutes(rules []gatewayv1alpha2.HTTPRouteRule, matchs []v1beta1.HttpRouteMatch) []gatewayv1alpha2.HTTPRouteRule {
var desired []gatewayv1alpha2.HTTPRouteRule var desired []gatewayv1alpha2.HTTPRouteRule
var canarys []gatewayv1alpha2.HTTPRouteRule var canarys []gatewayv1alpha2.HTTPRouteRule
for i := range rules { for i := range rules {

View File

@ -17,7 +17,7 @@ import (
"reflect" "reflect"
"testing" "testing"
rolloutsv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1" "github.com/openkruise/rollouts/api/v1beta1"
"github.com/openkruise/rollouts/pkg/util" "github.com/openkruise/rollouts/pkg/util"
utilpointer "k8s.io/utils/pointer" utilpointer "k8s.io/utils/pointer"
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
@ -130,7 +130,7 @@ func TestBuildDesiredHTTPRoute(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
getRouteRules func() []gatewayv1alpha2.HTTPRouteRule getRouteRules func() []gatewayv1alpha2.HTTPRouteRule
getRoutes func() (*int32, []rolloutsv1alpha1.HttpRouteMatch) getRoutes func() (*int32, []v1beta1.HttpRouteMatch)
desiredRules func() []gatewayv1alpha2.HTTPRouteRule desiredRules func() []gatewayv1alpha2.HTTPRouteRule
}{ }{
{ {
@ -139,9 +139,9 @@ func TestBuildDesiredHTTPRoute(t *testing.T) {
rules := routeDemo.DeepCopy().Spec.Rules rules := routeDemo.DeepCopy().Spec.Rules
return rules return rules
}, },
getRoutes: func() (*int32, []rolloutsv1alpha1.HttpRouteMatch) { getRoutes: func() (*int32, []v1beta1.HttpRouteMatch) {
iType := gatewayv1alpha2.HeaderMatchRegularExpression iType := gatewayv1alpha2.HeaderMatchRegularExpression
return nil, []rolloutsv1alpha1.HttpRouteMatch{ return nil, []v1beta1.HttpRouteMatch{
// header // header
{ {
Headers: []gatewayv1alpha2.HTTPHeaderMatch{ Headers: []gatewayv1alpha2.HTTPHeaderMatch{
@ -360,7 +360,7 @@ func TestBuildDesiredHTTPRoute(t *testing.T) {
} }
return rules return rules
}, },
getRoutes: func() (*int32, []rolloutsv1alpha1.HttpRouteMatch) { getRoutes: func() (*int32, []v1beta1.HttpRouteMatch) {
return utilpointer.Int32(20), nil return utilpointer.Int32(20), nil
}, },
desiredRules: func() []gatewayv1alpha2.HTTPRouteRule { desiredRules: func() []gatewayv1alpha2.HTTPRouteRule {
@ -494,7 +494,7 @@ func TestBuildDesiredHTTPRoute(t *testing.T) {
}) })
return rules return rules
}, },
getRoutes: func() (*int32, []rolloutsv1alpha1.HttpRouteMatch) { getRoutes: func() (*int32, []v1beta1.HttpRouteMatch) {
return utilpointer.Int32(-1), nil return utilpointer.Int32(-1), nil
}, },
desiredRules: func() []gatewayv1alpha2.HTTPRouteRule { desiredRules: func() []gatewayv1alpha2.HTTPRouteRule {

View File

@ -23,7 +23,7 @@ import (
"reflect" "reflect"
jsonpatch "github.com/evanphx/json-patch" jsonpatch "github.com/evanphx/json-patch"
rolloutv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1" "github.com/openkruise/rollouts/api/v1beta1"
"github.com/openkruise/rollouts/pkg/trafficrouting/network" "github.com/openkruise/rollouts/pkg/trafficrouting/network"
"github.com/openkruise/rollouts/pkg/util" "github.com/openkruise/rollouts/pkg/util"
"github.com/openkruise/rollouts/pkg/util/configuration" "github.com/openkruise/rollouts/pkg/util/configuration"
@ -35,6 +35,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/klog/v2" "k8s.io/klog/v2"
utilpointer "k8s.io/utils/pointer" utilpointer "k8s.io/utils/pointer"
@ -56,7 +57,7 @@ type Config struct {
Namespace string Namespace string
CanaryService string CanaryService string
StableService string StableService string
TrafficConf *rolloutv1alpha1.IngressTrafficRouting TrafficConf *v1beta1.IngressTrafficRouting
OwnerRef metav1.OwnerReference OwnerRef metav1.OwnerReference
} }
@ -82,8 +83,13 @@ func (r *ingressController) Initialize(ctx context.Context) error {
return r.Get(ctx, types.NamespacedName{Namespace: r.conf.Namespace, Name: r.conf.TrafficConf.Name}, ingress) return r.Get(ctx, types.NamespacedName{Namespace: r.conf.Namespace, Name: r.conf.TrafficConf.Name}, ingress)
} }
func (r *ingressController) EnsureRoutes(ctx context.Context, strategy *rolloutv1alpha1.TrafficRoutingStrategy) (bool, error) { func (r *ingressController) EnsureRoutes(ctx context.Context, strategy *v1beta1.TrafficRoutingStrategy) (bool, error) {
weight := strategy.Weight var weight *int32
if strategy.Traffic != nil {
is := intstr.FromString(*strategy.Traffic)
weightInt, _ := intstr.GetScaledValueFromIntOrPercent(&is, 100, true)
weight = utilpointer.Int32(int32(weightInt))
}
matches := strategy.Matches matches := strategy.Matches
headerModifier := strategy.RequestHeaderModifier headerModifier := strategy.RequestHeaderModifier
@ -217,7 +223,7 @@ func defaultCanaryIngressName(name string) string {
return fmt.Sprintf("%s-canary", name) return fmt.Sprintf("%s-canary", name)
} }
func (r *ingressController) executeLuaForCanary(annotations map[string]string, weight *int32, matches []rolloutv1alpha1.HttpRouteMatch, func (r *ingressController) executeLuaForCanary(annotations map[string]string, weight *int32, matches []v1beta1.HttpRouteMatch,
headerModifier *gatewayv1alpha2.HTTPRequestHeaderFilter) (map[string]string, error) { headerModifier *gatewayv1alpha2.HTTPRequestHeaderFilter) (map[string]string, error) {
if weight == nil { if weight == nil {
@ -228,7 +234,7 @@ func (r *ingressController) executeLuaForCanary(annotations map[string]string, w
type LuaData struct { type LuaData struct {
Annotations map[string]string Annotations map[string]string
Weight string Weight string
Matches []rolloutv1alpha1.HttpRouteMatch Matches []v1beta1.HttpRouteMatch
CanaryService string CanaryService string
RequestHeaderModifier *gatewayv1alpha2.HTTPRequestHeaderFilter RequestHeaderModifier *gatewayv1alpha2.HTTPRequestHeaderFilter
} }

View File

@ -20,8 +20,7 @@ import (
"testing" "testing"
rolloutsapi "github.com/openkruise/rollouts/api" rolloutsapi "github.com/openkruise/rollouts/api"
rolloutsv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1" "github.com/openkruise/rollouts/api/v1beta1"
rolloutsv1beta1 "github.com/openkruise/rollouts/api/v1beta1"
"github.com/openkruise/rollouts/pkg/util" "github.com/openkruise/rollouts/pkg/util"
"github.com/openkruise/rollouts/pkg/util/configuration" "github.com/openkruise/rollouts/pkg/util/configuration"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
@ -279,7 +278,7 @@ func TestInitialize(t *testing.T) {
Key: "rollout-demo", Key: "rollout-demo",
StableService: "echoserver", StableService: "echoserver",
CanaryService: "echoserver-canary", CanaryService: "echoserver-canary",
TrafficConf: &rolloutsv1alpha1.IngressTrafficRouting{ TrafficConf: &v1beta1.IngressTrafficRouting{
Name: "echoserver", Name: "echoserver",
}, },
} }
@ -309,7 +308,7 @@ func TestEnsureRoutes(t *testing.T) {
name string name string
getConfigmap func() *corev1.ConfigMap getConfigmap func() *corev1.ConfigMap
getIngress func() []*netv1.Ingress getIngress func() []*netv1.Ingress
getRoutes func() *rolloutsv1beta1.CanaryStep getRoutes func() *v1beta1.CanaryStep
expectIngress func() *netv1.Ingress expectIngress func() *netv1.Ingress
ingressType string ingressType string
}{ }{
@ -329,11 +328,11 @@ func TestEnsureRoutes(t *testing.T) {
canary.Spec.Rules[1].HTTP.Paths[0].Backend.Service.Name = "echoserver-canary" canary.Spec.Rules[1].HTTP.Paths[0].Backend.Service.Name = "echoserver-canary"
return []*netv1.Ingress{demoIngress.DeepCopy(), canary} return []*netv1.Ingress{demoIngress.DeepCopy(), canary}
}, },
getRoutes: func() *rolloutsv1beta1.CanaryStep { getRoutes: func() *v1beta1.CanaryStep {
return &rolloutsv1beta1.CanaryStep{ return &v1beta1.CanaryStep{
TrafficRoutingStrategy: rolloutsv1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
Weight: nil, Traffic: nil,
Matches: []rolloutsv1alpha1.HttpRouteMatch{ Matches: []v1beta1.HttpRouteMatch{
// header // header
{ {
Headers: []gatewayv1alpha2.HTTPHeaderMatch{ Headers: []gatewayv1alpha2.HTTPHeaderMatch{
@ -400,10 +399,10 @@ func TestEnsureRoutes(t *testing.T) {
canary.Spec.Rules[1].HTTP.Paths[0].Backend.Service.Name = "echoserver-canary" canary.Spec.Rules[1].HTTP.Paths[0].Backend.Service.Name = "echoserver-canary"
return []*netv1.Ingress{demoIngress.DeepCopy(), canary} return []*netv1.Ingress{demoIngress.DeepCopy(), canary}
}, },
getRoutes: func() *rolloutsv1beta1.CanaryStep { getRoutes: func() *v1beta1.CanaryStep {
return &rolloutsv1beta1.CanaryStep{ return &v1beta1.CanaryStep{
TrafficRoutingStrategy: rolloutsv1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(40), Traffic: utilpointer.String("40%"),
}, },
} }
}, },
@ -435,11 +434,11 @@ func TestEnsureRoutes(t *testing.T) {
canary.Spec.Rules[1].HTTP.Paths[0].Backend.Service.Name = "echoserver-canary" canary.Spec.Rules[1].HTTP.Paths[0].Backend.Service.Name = "echoserver-canary"
return []*netv1.Ingress{demoIngress.DeepCopy(), canary} return []*netv1.Ingress{demoIngress.DeepCopy(), canary}
}, },
getRoutes: func() *rolloutsv1beta1.CanaryStep { getRoutes: func() *v1beta1.CanaryStep {
iType := gatewayv1alpha2.HeaderMatchRegularExpression iType := gatewayv1alpha2.HeaderMatchRegularExpression
return &rolloutsv1beta1.CanaryStep{ return &v1beta1.CanaryStep{
TrafficRoutingStrategy: rolloutsv1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
Matches: []rolloutsv1alpha1.HttpRouteMatch{ Matches: []v1beta1.HttpRouteMatch{
// header // header
{ {
Headers: []gatewayv1alpha2.HTTPHeaderMatch{ Headers: []gatewayv1alpha2.HTTPHeaderMatch{
@ -483,10 +482,10 @@ func TestEnsureRoutes(t *testing.T) {
canary.Spec.Rules[1].HTTP.Paths[0].Backend.Service.Name = "echoserver-canary" canary.Spec.Rules[1].HTTP.Paths[0].Backend.Service.Name = "echoserver-canary"
return []*netv1.Ingress{demoIngress.DeepCopy(), canary} return []*netv1.Ingress{demoIngress.DeepCopy(), canary}
}, },
getRoutes: func() *rolloutsv1beta1.CanaryStep { getRoutes: func() *v1beta1.CanaryStep {
return &rolloutsv1beta1.CanaryStep{ return &v1beta1.CanaryStep{
TrafficRoutingStrategy: rolloutsv1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
Matches: []rolloutsv1alpha1.HttpRouteMatch{ Matches: []v1beta1.HttpRouteMatch{
// header // header
{ {
Headers: []gatewayv1alpha2.HTTPHeaderMatch{ Headers: []gatewayv1alpha2.HTTPHeaderMatch{
@ -526,7 +525,7 @@ func TestEnsureRoutes(t *testing.T) {
Key: "rollout-demo", Key: "rollout-demo",
StableService: "echoserver", StableService: "echoserver",
CanaryService: "echoserver-canary", CanaryService: "echoserver-canary",
TrafficConf: &rolloutsv1alpha1.IngressTrafficRouting{ TrafficConf: &v1beta1.IngressTrafficRouting{
Name: "echoserver", Name: "echoserver",
}, },
} }
@ -596,7 +595,7 @@ func TestFinalise(t *testing.T) {
Key: "rollout-demo", Key: "rollout-demo",
StableService: "echoserver", StableService: "echoserver",
CanaryService: "echoserver-canary", CanaryService: "echoserver-canary",
TrafficConf: &rolloutsv1alpha1.IngressTrafficRouting{ TrafficConf: &v1beta1.IngressTrafficRouting{
Name: "echoserver", Name: "echoserver",
}, },
} }

View File

@ -19,7 +19,7 @@ package network
import ( import (
"context" "context"
rolloutv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1" "github.com/openkruise/rollouts/api/v1beta1"
) )
// NetworkProvider common function across all TrafficRouting implementation // NetworkProvider common function across all TrafficRouting implementation
@ -33,7 +33,7 @@ type NetworkProvider interface {
// 1. check if canary has been set desired weight. // 1. check if canary has been set desired weight.
// 2. If not, set canary desired weight // 2. If not, set canary desired weight
// When the first set weight is returned false, mainly to give the provider some time to process, only when again ensure, will return true // When the first set weight is returned false, mainly to give the provider some time to process, only when again ensure, will return true
EnsureRoutes(ctx context.Context, strategy *rolloutv1alpha1.TrafficRoutingStrategy) (bool, error) EnsureRoutes(ctx context.Context, strategy *v1beta1.TrafficRoutingStrategy) (bool, error)
// Finalise will do some cleanup work after the canary rollout complete, such as delete canary ingress. // Finalise will do some cleanup work after the canary rollout complete, such as delete canary ingress.
// Finalise is called with a 3-second delay after completing the canary. // Finalise is called with a 3-second delay after completing the canary.
Finalise(ctx context.Context) error Finalise(ctx context.Context) error

View File

@ -66,7 +66,7 @@ type Workload struct {
// ControllerFinderFunc is a function type that maps a pod to a list of // ControllerFinderFunc is a function type that maps a pod to a list of
// controllers and their scale. // controllers and their scale.
type ControllerFinderFunc func(namespace string, ref *rolloutv1beta1.WorkloadRef) (*Workload, error) type ControllerFinderFunc func(namespace string, ref *rolloutv1beta1.ObjectRef) (*Workload, error)
type ControllerFinder struct { type ControllerFinder struct {
client.Client client.Client
@ -86,29 +86,17 @@ func NewControllerFinder(c client.Client) *ControllerFinder {
// +kubebuilder:rbac:groups=apps,resources=replicasets/status,verbs=get;update;patch // +kubebuilder:rbac:groups=apps,resources=replicasets/status,verbs=get;update;patch
func (r *ControllerFinder) GetWorkloadForRef(rollout *rolloutv1beta1.Rollout) (*Workload, error) { func (r *ControllerFinder) GetWorkloadForRef(rollout *rolloutv1beta1.Rollout) (*Workload, error) {
workloadRef := rollout.Spec.ObjectRef.WorkloadRef workloadRef := rollout.Spec.WorkloadRef
if workloadRef == nil { if rollout.Spec.Strategy.Canary.EnableExtraWorkloadForCanary {
return nil, nil
}
switch strings.ToLower(rollout.Annotations[rolloutv1beta1.RolloutStyleAnnotation]) {
case strings.ToLower(string(rolloutv1alpha1.PartitionRollingStyle)):
for _, finder := range r.partitionStyleFinders() {
workload, err := finder(rollout.Namespace, workloadRef)
if workload != nil || err != nil {
return workload, err
}
}
case strings.ToLower(string(rolloutv1alpha1.CanaryRollingStyle)):
for _, finder := range r.canaryStyleFinders() {
workload, err := finder(rollout.Namespace, workloadRef)
if workload != nil || err != nil {
return workload, err
}
}
default:
for _, finder := range append(r.canaryStyleFinders(), r.partitionStyleFinders()...) { for _, finder := range append(r.canaryStyleFinders(), r.partitionStyleFinders()...) {
workload, err := finder(rollout.Namespace, workloadRef) workload, err := finder(rollout.Namespace, &workloadRef)
if workload != nil || err != nil {
return workload, err
}
}
} else {
for _, finder := range r.partitionStyleFinders() {
workload, err := finder(rollout.Namespace, &workloadRef)
if workload != nil || err != nil { if workload != nil || err != nil {
return workload, err return workload, err
} }
@ -138,7 +126,7 @@ var (
) )
// getKruiseCloneSet returns the kruise cloneSet referenced by the provided controllerRef. // getKruiseCloneSet returns the kruise cloneSet referenced by the provided controllerRef.
func (r *ControllerFinder) getKruiseCloneSet(namespace string, ref *rolloutv1beta1.WorkloadRef) (*Workload, error) { func (r *ControllerFinder) getKruiseCloneSet(namespace string, ref *rolloutv1beta1.ObjectRef) (*Workload, error) {
// This error is irreversible, so there is no need to return error // This error is irreversible, so there is no need to return error
ok, _ := verifyGroupKind(ref, ControllerKruiseKindCS.Kind, []string{ControllerKruiseKindCS.Group}) ok, _ := verifyGroupKind(ref, ControllerKruiseKindCS.Kind, []string{ControllerKruiseKindCS.Group})
if !ok { if !ok {
@ -180,7 +168,7 @@ func (r *ControllerFinder) getKruiseCloneSet(namespace string, ref *rolloutv1bet
return workload, nil return workload, nil
} }
func (r *ControllerFinder) getKruiseDaemonSet(namespace string, ref *rolloutv1beta1.WorkloadRef) (*Workload, error) { func (r *ControllerFinder) getKruiseDaemonSet(namespace string, ref *rolloutv1beta1.ObjectRef) (*Workload, error) {
// This error is irreversible, so there is no need to return error // This error is irreversible, so there is no need to return error
ok, _ := verifyGroupKind(ref, ControllerKruiseKindDS.Kind, []string{ControllerKruiseKindDS.Group}) ok, _ := verifyGroupKind(ref, ControllerKruiseKindDS.Kind, []string{ControllerKruiseKindDS.Group})
if !ok { if !ok {
@ -224,7 +212,7 @@ func (r *ControllerFinder) getKruiseDaemonSet(namespace string, ref *rolloutv1be
} }
// getPartitionStyleDeployment returns the Advanced Deployment referenced by the provided controllerRef. // getPartitionStyleDeployment returns the Advanced Deployment referenced by the provided controllerRef.
func (r *ControllerFinder) getAdvancedDeployment(namespace string, ref *rolloutv1beta1.WorkloadRef) (*Workload, error) { func (r *ControllerFinder) getAdvancedDeployment(namespace string, ref *rolloutv1beta1.ObjectRef) (*Workload, error) {
// This error is irreversible, so there is no need to return error // This error is irreversible, so there is no need to return error
ok, _ := verifyGroupKind(ref, ControllerKindDep.Kind, []string{ControllerKindDep.Group}) ok, _ := verifyGroupKind(ref, ControllerKindDep.Kind, []string{ControllerKindDep.Group})
if !ok { if !ok {
@ -278,7 +266,7 @@ func (r *ControllerFinder) getAdvancedDeployment(namespace string, ref *rolloutv
} }
// getDeployment returns the k8s native deployment referenced by the provided controllerRef. // getDeployment returns the k8s native deployment referenced by the provided controllerRef.
func (r *ControllerFinder) getDeployment(namespace string, ref *rolloutv1beta1.WorkloadRef) (*Workload, error) { func (r *ControllerFinder) getDeployment(namespace string, ref *rolloutv1beta1.ObjectRef) (*Workload, error) {
// This error is irreversible, so there is no need to return error // This error is irreversible, so there is no need to return error
ok, _ := verifyGroupKind(ref, ControllerKindDep.Kind, []string{ControllerKindDep.Group}) ok, _ := verifyGroupKind(ref, ControllerKindDep.Kind, []string{ControllerKindDep.Group})
if !ok { if !ok {
@ -335,7 +323,7 @@ func (r *ControllerFinder) getDeployment(namespace string, ref *rolloutv1beta1.W
return workload, err return workload, err
} }
func (r *ControllerFinder) getStatefulSetLikeWorkload(namespace string, ref *rolloutv1beta1.WorkloadRef) (*Workload, error) { func (r *ControllerFinder) getStatefulSetLikeWorkload(namespace string, ref *rolloutv1beta1.ObjectRef) (*Workload, error) {
if ref == nil { if ref == nil {
return nil, nil return nil, nil
} }
@ -446,7 +434,7 @@ func (r *ControllerFinder) getDeploymentStableRs(obj *apps.Deployment) (*apps.Re
return rss[0], nil return rss[0], nil
} }
func verifyGroupKind(ref *rolloutv1beta1.WorkloadRef, expectedKind string, expectedGroups []string) (bool, error) { func verifyGroupKind(ref *rolloutv1beta1.ObjectRef, expectedKind string, expectedGroups []string) (bool, error) {
gv, err := schema.ParseGroupVersion(ref.APIVersion) gv, err := schema.ParseGroupVersion(ref.APIVersion)
if err != nil { if err != nil {
return false, err return false, err

View File

@ -50,7 +50,7 @@ func IsRollbackInBatchPolicy(rollout *rolloutv1beta1.Rollout, labels map[string]
if len(rollout.Spec.Strategy.Canary.TrafficRoutings) > 0 { if len(rollout.Spec.Strategy.Canary.TrafficRoutings) > 0 {
return false return false
} }
workloadRef := rollout.Spec.ObjectRef.WorkloadRef workloadRef := rollout.Spec.WorkloadRef
//currently, only CloneSet, StatefulSet support this policy //currently, only CloneSet, StatefulSet support this policy
if workloadRef.Kind == ControllerKindSts.Kind || if workloadRef.Kind == ControllerKindSts.Kind ||
workloadRef.Kind == ControllerKruiseKindCS.Kind || workloadRef.Kind == ControllerKruiseKindCS.Kind ||
@ -131,7 +131,7 @@ func DiscoverGVK(gvk schema.GroupVersionKind) bool {
return true return true
} }
func GetGVKFrom(workloadRef *rolloutv1beta1.WorkloadRef) schema.GroupVersionKind { func GetGVKFrom(workloadRef *rolloutv1beta1.ObjectRef) schema.GroupVersionKind {
if workloadRef == nil { if workloadRef == nil {
return schema.GroupVersionKind{} return schema.GroupVersionKind{}
} }

View File

@ -21,7 +21,6 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"reflect" "reflect"
"strings"
appsv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1" appsv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
appsv1beta1 "github.com/openkruise/rollouts/api/v1beta1" appsv1beta1 "github.com/openkruise/rollouts/api/v1beta1"
@ -131,15 +130,15 @@ func (h *RolloutCreateUpdateHandler) validateRolloutUpdate(oldObj, newObj *appsv
switch latestObject.Status.Phase { switch latestObject.Status.Phase {
// The workloadRef and TrafficRouting are not allowed to be modified in the Progressing, Terminating state // The workloadRef and TrafficRouting are not allowed to be modified in the Progressing, Terminating state
case appsv1beta1.RolloutPhaseProgressing, appsv1beta1.RolloutPhaseTerminating: case appsv1beta1.RolloutPhaseProgressing, appsv1beta1.RolloutPhaseTerminating:
if !reflect.DeepEqual(oldObj.Spec.ObjectRef, newObj.Spec.ObjectRef) { if !reflect.DeepEqual(oldObj.Spec.WorkloadRef, newObj.Spec.WorkloadRef) {
return field.ErrorList{field.Forbidden(field.NewPath("Spec.ObjectRef"), "Rollout 'ObjectRef' field is immutable")} return field.ErrorList{field.Forbidden(field.NewPath("Spec.ObjectRef"), "Rollout 'ObjectRef' field is immutable")}
} }
// canary strategy // canary strategy
if !reflect.DeepEqual(oldObj.Spec.Strategy.Canary.TrafficRoutings, newObj.Spec.Strategy.Canary.TrafficRoutings) { 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")} return field.ErrorList{field.Forbidden(field.NewPath("Spec.Strategy.Canary.TrafficRoutings"), "Rollout 'Strategy.Canary.TrafficRoutings' field is immutable")}
} }
if !strings.EqualFold(oldObj.Annotations[appsv1beta1.RolloutStyleAnnotation], newObj.Annotations[appsv1beta1.RolloutStyleAnnotation]) { if oldObj.Spec.Strategy.Canary.EnableExtraWorkloadForCanary != newObj.Spec.Strategy.Canary.EnableExtraWorkloadForCanary {
return field.ErrorList{field.Forbidden(field.NewPath("Metadata.Annotation"), "Rollout 'Rolling-Style' annotation is immutable")} return field.ErrorList{field.Forbidden(field.NewPath("Spec.Strategy.Canary"), "Rollout enableExtraWorkloadForCanary is immutable")}
} }
} }
@ -171,7 +170,7 @@ func (h *RolloutCreateUpdateHandler) validateRolloutConflict(rollout *appsv1beta
} }
for i := range rolloutList.Items { for i := range rolloutList.Items {
r := &rolloutList.Items[i] r := &rolloutList.Items[i]
if r.Name == rollout.Name || !IsSameWorkloadRefGVKName(r.Spec.ObjectRef.WorkloadRef, rollout.Spec.ObjectRef.WorkloadRef) { if r.Name == rollout.Name || !IsSameWorkloadRefGVKName(&r.Spec.WorkloadRef, &rollout.Spec.WorkloadRef) {
continue continue
} }
return field.ErrorList{field.Invalid(path, rollout.Name, return field.ErrorList{field.Invalid(path, rollout.Name,
@ -181,39 +180,32 @@ func (h *RolloutCreateUpdateHandler) validateRolloutConflict(rollout *appsv1beta
} }
func validateRolloutSpec(rollout *appsv1beta1.Rollout, fldPath *field.Path) field.ErrorList { func validateRolloutSpec(rollout *appsv1beta1.Rollout, fldPath *field.Path) field.ErrorList {
errList := validateRolloutSpecObjectRef(&rollout.Spec.ObjectRef, fldPath.Child("ObjectRef")) errList := validateRolloutSpecObjectRef(&rollout.Spec.WorkloadRef, fldPath.Child("ObjectRef"))
errList = append(errList, validateRolloutRollingStyle(rollout, field.NewPath("RollingStyle"))...) errList = append(errList, validateRolloutRollingStyle(rollout, field.NewPath("RollingStyle"))...)
errList = append(errList, validateRolloutSpecStrategy(&rollout.Spec.Strategy, fldPath.Child("Strategy"))...) errList = append(errList, validateRolloutSpecStrategy(&rollout.Spec.Strategy, fldPath.Child("Strategy"))...)
return errList return errList
} }
func validateRolloutRollingStyle(rollout *appsv1beta1.Rollout, fldPath *field.Path) field.ErrorList { func validateRolloutRollingStyle(rollout *appsv1beta1.Rollout, fldPath *field.Path) field.ErrorList {
switch strings.ToLower(rollout.Annotations[appsv1beta1.RolloutStyleAnnotation]) { workloadRef := rollout.Spec.WorkloadRef
case "", strings.ToLower(string(appsv1alpha1.CanaryRollingStyle)), strings.ToLower(string(appsv1alpha1.PartitionRollingStyle)): if workloadRef.Kind == util.ControllerKindDep.Kind {
default:
return field.ErrorList{field.Invalid(fldPath, rollout.Annotations[appsv1beta1.RolloutStyleAnnotation],
"Rolling style must be 'Canary', 'Partition' or empty")}
}
workloadRef := rollout.Spec.ObjectRef.WorkloadRef
if workloadRef == nil || workloadRef.Kind == util.ControllerKindDep.Kind {
return nil // Deployment support all rolling styles, no need to validate. return nil // Deployment support all rolling styles, no need to validate.
} }
if strings.EqualFold(rollout.Annotations[appsv1beta1.RolloutStyleAnnotation], string(appsv1alpha1.CanaryRollingStyle)) { if rollout.Spec.Strategy.Canary.EnableExtraWorkloadForCanary {
return field.ErrorList{field.Invalid(fldPath, rollout.Annotations[appsv1beta1.RolloutStyleAnnotation], return field.ErrorList{field.Invalid(fldPath, rollout.Spec.Strategy.Canary.EnableExtraWorkloadForCanary,
"Only Deployment support canary rolling style")} "Only Deployment can set enableExtraWorkloadForCanary=true")}
} }
return nil return nil
} }
func validateRolloutSpecObjectRef(objectRef *appsv1beta1.ObjectRef, fldPath *field.Path) field.ErrorList { func validateRolloutSpecObjectRef(workloadRef *appsv1beta1.ObjectRef, fldPath *field.Path) field.ErrorList {
if objectRef.WorkloadRef == nil { if workloadRef == nil {
return field.ErrorList{field.Invalid(fldPath.Child("WorkloadRef"), objectRef.WorkloadRef, "WorkloadRef is required")} return field.ErrorList{field.Invalid(fldPath.Child("WorkloadRef"), workloadRef, "WorkloadRef is required")}
} }
gvk := schema.FromAPIVersionAndKind(objectRef.WorkloadRef.APIVersion, objectRef.WorkloadRef.Kind) gvk := schema.FromAPIVersionAndKind(workloadRef.APIVersion, workloadRef.Kind)
if !util.IsSupportedWorkload(gvk) { if !util.IsSupportedWorkload(gvk) {
return field.ErrorList{field.Invalid(fldPath.Child("WorkloadRef"), objectRef.WorkloadRef, "WorkloadRef kind is not supported")} return field.ErrorList{field.Invalid(fldPath.Child("WorkloadRef"), workloadRef, "WorkloadRef kind is not supported")}
} }
return nil return nil
} }
@ -237,7 +229,7 @@ func validateRolloutSpecCanaryStrategy(canary *appsv1beta1.CanaryStrategy, fldPa
return errList return errList
} }
func validateRolloutSpecCanaryTraffic(traffic appsv1alpha1.TrafficRoutingRef, fldPath *field.Path) field.ErrorList { func validateRolloutSpecCanaryTraffic(traffic appsv1beta1.TrafficRoutingRef, fldPath *field.Path) field.ErrorList {
errList := field.ErrorList{} errList := field.ErrorList{}
if len(traffic.Service) == 0 { if len(traffic.Service) == 0 {
errList = append(errList, field.Invalid(fldPath.Child("Service"), traffic.Service, "TrafficRouting.Service cannot be empty")) errList = append(errList, field.Invalid(fldPath.Child("Service"), traffic.Service, "TrafficRouting.Service cannot be empty"))
@ -269,40 +261,38 @@ func validateRolloutSpecCanarySteps(steps []appsv1beta1.CanaryStep, fldPath *fie
for i := range steps { for i := range steps {
s := &steps[i] s := &steps[i]
if s.Weight == nil && s.Replicas == nil { if s.Replicas == nil {
return field.ErrorList{field.Invalid(fldPath.Index(i).Child("steps"), steps, `weight and replicas cannot be empty at the same time`)} return field.ErrorList{field.Invalid(fldPath.Index(i).Child("steps"), steps, `replicas cannot be empty`)}
} }
if s.Replicas != nil { canaryReplicas, err := intstr.GetScaledValueFromIntOrPercent(s.Replicas, 100, true)
canaryReplicas, err := intstr.GetScaledValueFromIntOrPercent(s.Replicas, 100, true) if err != nil ||
if err != nil || canaryReplicas <= 0 ||
canaryReplicas <= 0 || (canaryReplicas > 100 && s.Replicas.Type == intstr.String) {
(canaryReplicas > 100 && s.Replicas.Type == intstr.String) { return field.ErrorList{field.Invalid(fldPath.Index(i).Child("Replicas"),
return field.ErrorList{field.Invalid(fldPath.Index(i).Child("Replicas"), s.Replicas, `replicas must be positive number, or a percentage with "0%" < canaryReplicas <= "100%"`)}
s.Replicas, `replicas must be positive number, or a percentage with "0%" < canaryReplicas <= "100%"`)} }
} if !isTraffic {
continue
}
if s.Traffic == nil {
return field.ErrorList{field.Invalid(fldPath.Index(i).Child("steps"), steps, `traffic cannot be empty`)}
}
is := intstr.FromString(*s.Traffic)
weight, err := intstr.GetScaledValueFromIntOrPercent(&is, 100, true)
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%"`)}
} }
} }
for i := 1; i < stepCount; i++ { for i := 1; i < stepCount; i++ {
prev := &steps[i-1] prev := &steps[i-1]
curr := &steps[i] curr := &steps[i]
if isTraffic && curr.Weight != nil && prev.Weight != nil && *curr.Weight < *prev.Weight {
return field.ErrorList{field.Invalid(fldPath.Child("Weight"), steps, `Steps.Weight must be a non decreasing sequence`)}
}
// if they are comparable, then compare them // if they are comparable, then compare them
if IsPercentageCanaryReplicasType(prev.Replicas) != IsPercentageCanaryReplicasType(curr.Replicas) { if IsPercentageCanaryReplicasType(prev.Replicas) != IsPercentageCanaryReplicasType(curr.Replicas) {
continue continue
} }
prevCanaryReplicas, _ := intstr.GetScaledValueFromIntOrPercent(prev.Replicas, 100, true) prevCanaryReplicas, _ := intstr.GetScaledValueFromIntOrPercent(prev.Replicas, 100, true)
currCanaryReplicas, _ := intstr.GetScaledValueFromIntOrPercent(curr.Replicas, 100, true) currCanaryReplicas, _ := intstr.GetScaledValueFromIntOrPercent(curr.Replicas, 100, true)
if prev.Replicas == nil {
prevCanaryReplicas = int(*prev.Weight)
}
if curr.Replicas == nil {
currCanaryReplicas = int(*curr.Weight)
}
if currCanaryReplicas < prevCanaryReplicas { if currCanaryReplicas < prevCanaryReplicas {
return field.ErrorList{field.Invalid(fldPath.Child("CanaryReplicas"), steps, `Steps.CanaryReplicas must be a non decreasing sequence`)} return field.ErrorList{field.Invalid(fldPath.Child("CanaryReplicas"), steps, `Steps.CanaryReplicas must be a non decreasing sequence`)}
} }
@ -315,7 +305,7 @@ func IsPercentageCanaryReplicasType(replicas *intstr.IntOrString) bool {
return replicas == nil || replicas.Type == intstr.String return replicas == nil || replicas.Type == intstr.String
} }
func IsSameWorkloadRefGVKName(a, b *appsv1beta1.WorkloadRef) bool { func IsSameWorkloadRefGVKName(a, b *appsv1beta1.ObjectRef) bool {
if a == nil || b == nil { if a == nil || b == nil {
return false return false
} }

View File

@ -22,7 +22,6 @@ import (
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
rolloutapi "github.com/openkruise/rollouts/api" rolloutapi "github.com/openkruise/rollouts/api"
appsv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
appsv1beta1 "github.com/openkruise/rollouts/api/v1beta1" appsv1beta1 "github.com/openkruise/rollouts/api/v1beta1"
apps "k8s.io/api/apps/v1" apps "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -47,56 +46,46 @@ var (
Annotations: map[string]string{}, Annotations: map[string]string{},
}, },
Spec: appsv1beta1.RolloutSpec{ Spec: appsv1beta1.RolloutSpec{
ObjectRef: appsv1beta1.ObjectRef{ WorkloadRef: appsv1beta1.ObjectRef{
WorkloadRef: &appsv1beta1.WorkloadRef{ APIVersion: apps.SchemeGroupVersion.String(),
APIVersion: apps.SchemeGroupVersion.String(), Kind: "Deployment",
Kind: "Deployment", Name: "deployment-demo",
Name: "deployment-demo",
},
}, },
Strategy: appsv1beta1.RolloutStrategy{ Strategy: appsv1beta1.RolloutStrategy{
Canary: &appsv1beta1.CanaryStrategy{ Canary: &appsv1beta1.CanaryStrategy{
Steps: []appsv1beta1.CanaryStep{ Steps: []appsv1beta1.CanaryStep{
{ {
TrafficRoutingStrategy: appsv1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: appsv1beta1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(10), Traffic: utilpointer.String("10%"),
}, },
Replicas: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)}, Replicas: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)},
Pause: appsv1beta1.RolloutPause{}, Pause: appsv1beta1.RolloutPause{},
}, },
{ {
TrafficRoutingStrategy: appsv1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: appsv1beta1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(10), Traffic: utilpointer.String("10%"),
}, },
Replicas: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(3)}, Replicas: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(3)},
Pause: appsv1beta1.RolloutPause{Duration: utilpointer.Int32(1 * 24 * 60 * 60)}, Pause: appsv1beta1.RolloutPause{Duration: utilpointer.Int32(1 * 24 * 60 * 60)},
}, },
{ {
TrafficRoutingStrategy: appsv1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: appsv1beta1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(30), Traffic: utilpointer.String("30%"),
}, },
Pause: appsv1beta1.RolloutPause{Duration: utilpointer.Int32(7 * 24 * 60 * 60)}, Replicas: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(10)},
Pause: appsv1beta1.RolloutPause{Duration: utilpointer.Int32(7 * 24 * 60 * 60)},
}, },
{ {
TrafficRoutingStrategy: appsv1alpha1.TrafficRoutingStrategy{ TrafficRoutingStrategy: appsv1beta1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(100), Traffic: utilpointer.String("100%"),
},
},
{
TrafficRoutingStrategy: appsv1alpha1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(101),
},
},
{
TrafficRoutingStrategy: appsv1alpha1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(200),
}, },
Replicas: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(20)},
}, },
}, },
TrafficRoutings: []appsv1alpha1.TrafficRoutingRef{ TrafficRoutings: []appsv1beta1.TrafficRoutingRef{
{ {
Service: "service-demo", Service: "service-demo",
Ingress: &appsv1alpha1.IngressTrafficRouting{ Ingress: &appsv1beta1.IngressTrafficRouting{
ClassType: "nginx", ClassType: "nginx",
Name: "ingress-nginx-demo", Name: "ingress-nginx-demo",
}, },
@ -129,21 +118,13 @@ func TestRolloutValidateCreate(t *testing.T) {
Name: "Normal case", Name: "Normal case",
Succeed: true, Succeed: true,
GetObject: func() []client.Object { GetObject: func() []client.Object {
return []client.Object{rollout.DeepCopy()} obj := rollout.DeepCopy()
return []client.Object{obj}
}, },
}, },
/*********************************************************** /***********************************************************
The following cases may lead to controller panic The following cases may lead to controller panic
**********************************************************/ **********************************************************/
{
Name: "WorkloadRef is nil",
Succeed: false,
GetObject: func() []client.Object {
object := rollout.DeepCopy()
object.Spec.ObjectRef.WorkloadRef = nil
return []client.Object{object}
},
},
{ {
Name: "Canary is nil", Name: "Canary is nil",
Succeed: false, Succeed: false,
@ -188,29 +169,21 @@ func TestRolloutValidateCreate(t *testing.T) {
Succeed: true, Succeed: true,
GetObject: func() []client.Object { GetObject: func() []client.Object {
object := rollout.DeepCopy() object := rollout.DeepCopy()
object.Spec.ObjectRef.WorkloadRef = &appsv1beta1.WorkloadRef{ object.Spec.WorkloadRef = appsv1beta1.ObjectRef{
APIVersion: "apps/v1", APIVersion: "apps/v1",
Kind: "StatefulSet", Kind: "StatefulSet",
Name: "whatever", Name: "whatever",
} }
return []client.Object{object} return []client.Object{object}
}, },
}, },
{ {
Name: "Steps.Weight is a decreasing sequence", Name: "Steps.Traffic is a decreasing sequence",
Succeed: false, Succeed: false,
GetObject: func() []client.Object { GetObject: func() []client.Object {
object := rollout.DeepCopy() object := rollout.DeepCopy()
object.Spec.Strategy.Canary.Steps[2].Weight = utilpointer.Int32Ptr(5) object.Spec.Strategy.Canary.Steps[2].Traffic = utilpointer.String("%5")
return []client.Object{object}
},
},
{
Name: "Steps.Replicas is a decreasing sequence",
Succeed: false,
GetObject: func() []client.Object {
object := rollout.DeepCopy()
object.Spec.Strategy.Canary.Steps[1].Replicas = &intstr.IntOrString{Type: intstr.String, StrVal: "50%"}
return []client.Object{object} return []client.Object{object}
}, },
}, },
@ -242,20 +215,20 @@ func TestRolloutValidateCreate(t *testing.T) {
}, },
}, },
{ {
Name: "Steps.Weight is illegal value, 0", Name: "Steps.Traffic is illegal value, 0",
Succeed: false, Succeed: false,
GetObject: func() []client.Object { GetObject: func() []client.Object {
object := rollout.DeepCopy() object := rollout.DeepCopy()
object.Spec.Strategy.Canary.Steps[1].Weight = utilpointer.Int32Ptr(0) object.Spec.Strategy.Canary.Steps[1].Traffic = utilpointer.String("0%")
return []client.Object{object} return []client.Object{object}
}, },
}, },
{ {
Name: "Steps.Weight is illegal value, 101", Name: "Steps.Traffic is illegal value, 101",
Succeed: false, Succeed: false,
GetObject: func() []client.Object { GetObject: func() []client.Object {
object := rollout.DeepCopy() object := rollout.DeepCopy()
object.Spec.Strategy.Canary.Steps[1].Weight = utilpointer.Int32Ptr(101) object.Spec.Strategy.Canary.Steps[1].Traffic = utilpointer.String("101%")
return []client.Object{object} return []client.Object{object}
}, },
}, },
@ -264,9 +237,7 @@ func TestRolloutValidateCreate(t *testing.T) {
Succeed: true, Succeed: true,
GetObject: func() []client.Object { GetObject: func() []client.Object {
object := rollout.DeepCopy() object := rollout.DeepCopy()
object.Annotations = map[string]string{ object.Spec.Strategy.Canary.EnableExtraWorkloadForCanary = true
appsv1beta1.RolloutStyleAnnotation: "Canary",
}
return []client.Object{object} return []client.Object{object}
}, },
}, },
@ -275,20 +246,6 @@ func TestRolloutValidateCreate(t *testing.T) {
Succeed: true, Succeed: true,
GetObject: func() []client.Object { GetObject: func() []client.Object {
object := rollout.DeepCopy() object := rollout.DeepCopy()
object.Annotations = map[string]string{
appsv1beta1.RolloutStyleAnnotation: "Partition",
}
return []client.Object{object}
},
},
{
Name: "Wrong rolling style",
Succeed: false,
GetObject: func() []client.Object {
object := rollout.DeepCopy()
object.Annotations = map[string]string{
appsv1beta1.RolloutStyleAnnotation: "Other",
}
return []client.Object{object} return []client.Object{object}
}, },
}, },
@ -297,21 +254,19 @@ func TestRolloutValidateCreate(t *testing.T) {
Succeed: false, Succeed: false,
GetObject: func() []client.Object { GetObject: func() []client.Object {
object := rollout.DeepCopy() object := rollout.DeepCopy()
object.Annotations = map[string]string{ object.Spec.Strategy.Canary.EnableExtraWorkloadForCanary = true
appsv1beta1.RolloutStyleAnnotation: "Canary", object.Spec.WorkloadRef.APIVersion = "apps.kruise.io/v1alpha1"
} object.Spec.WorkloadRef.Kind = "CloneSet"
object.Spec.ObjectRef.WorkloadRef.APIVersion = "apps.kruise.io/v1alpha1"
object.Spec.ObjectRef.WorkloadRef.Kind = "CloneSet"
return []client.Object{object} return []client.Object{object}
}, },
}, },
//{ //{
// Name: "The last Steps.Weight is not 100", // Name: "The last Steps.Traffic is not 100",
// Succeed: false, // Succeed: false,
// GetObject: func() []client.Object { // GetObject: func() []client.Object {
// object := rollout.DeepCopy() // object := rollout.DeepCopy()
// n := len(object.Spec.Strategy.Canary.Steps) // n := len(object.Spec.Strategy.Canary.Steps)
// object.Spec.Strategy.Canary.Steps[n-1].Weight = 80 // object.Spec.Strategy.Canary.Steps[n-1].Traffic = 80
// return []client.Object{object} // return []client.Object{object}
// }, // },
//}, //},
@ -326,15 +281,15 @@ func TestRolloutValidateCreate(t *testing.T) {
object1 := rollout.DeepCopy() object1 := rollout.DeepCopy()
object1.Name = "object-1" object1.Name = "object-1"
object1.Spec.ObjectRef.WorkloadRef.Name = "another" object1.Spec.WorkloadRef.Name = "another"
object2 := rollout.DeepCopy() object2 := rollout.DeepCopy()
object2.Name = "object-2" object2.Name = "object-2"
object2.Spec.ObjectRef.WorkloadRef.APIVersion = "another" object2.Spec.WorkloadRef.APIVersion = "another"
object3 := rollout.DeepCopy() object3 := rollout.DeepCopy()
object3.Name = "object-3" object3.Name = "object-3"
object3.Spec.ObjectRef.WorkloadRef.Kind = "another" object3.Spec.WorkloadRef.Kind = "another"
return []client.Object{ return []client.Object{
object, object1, object2, object3, object, object1, object2, object3,
@ -349,15 +304,15 @@ func TestRolloutValidateCreate(t *testing.T) {
object1 := rollout.DeepCopy() object1 := rollout.DeepCopy()
object1.Name = "object-1" object1.Name = "object-1"
object1.Spec.ObjectRef.WorkloadRef.Name = "another" object1.Spec.WorkloadRef.Name = "another"
object2 := rollout.DeepCopy() object2 := rollout.DeepCopy()
object2.Name = "object-2" object2.Name = "object-2"
object2.Spec.ObjectRef.WorkloadRef.APIVersion = "another" object2.Spec.WorkloadRef.APIVersion = "another"
object3 := rollout.DeepCopy() object3 := rollout.DeepCopy()
object3.Name = "object-3" object3.Name = "object-3"
object3.Spec.ObjectRef.WorkloadRef.Kind = "another" object3.Spec.WorkloadRef.Kind = "another"
object4 := rollout.DeepCopy() object4 := rollout.DeepCopy()
object4.Name = "object-4" object4.Name = "object-4"
@ -400,7 +355,7 @@ func TestRolloutValidateUpdate(t *testing.T) {
}, },
GetNewObject: func() client.Object { GetNewObject: func() client.Object {
object := rollout.DeepCopy() object := rollout.DeepCopy()
object.Spec.Strategy.Canary.Steps[0].Weight = utilpointer.Int32Ptr(5) object.Spec.Strategy.Canary.Steps[0].Traffic = utilpointer.String("5%")
return object return object
}, },
}, },
@ -430,7 +385,7 @@ func TestRolloutValidateUpdate(t *testing.T) {
GetNewObject: func() client.Object { GetNewObject: func() client.Object {
object := rollout.DeepCopy() object := rollout.DeepCopy()
object.Status.Phase = appsv1beta1.RolloutPhaseProgressing object.Status.Phase = appsv1beta1.RolloutPhaseProgressing
object.Spec.Strategy.Canary.Steps[0].Weight = utilpointer.Int32Ptr(5) object.Spec.Strategy.Canary.Steps[0].Traffic = utilpointer.String("5%")
return object return object
}, },
}, },
@ -439,14 +394,13 @@ func TestRolloutValidateUpdate(t *testing.T) {
Succeed: false, Succeed: false,
GetOldObject: func() client.Object { GetOldObject: func() client.Object {
object := rollout.DeepCopy() object := rollout.DeepCopy()
object.Annotations[appsv1beta1.RolloutStyleAnnotation] = "Partition"
object.Status.Phase = appsv1beta1.RolloutPhaseProgressing object.Status.Phase = appsv1beta1.RolloutPhaseProgressing
return object return object
}, },
GetNewObject: func() client.Object { GetNewObject: func() client.Object {
object := rollout.DeepCopy() object := rollout.DeepCopy()
object.Status.Phase = appsv1beta1.RolloutPhaseProgressing object.Status.Phase = appsv1beta1.RolloutPhaseProgressing
object.Annotations[appsv1beta1.RolloutStyleAnnotation] = "Canary" object.Spec.Strategy.Canary.EnableExtraWorkloadForCanary = true
return object return object
}, },
}, },
@ -476,7 +430,7 @@ func TestRolloutValidateUpdate(t *testing.T) {
GetNewObject: func() client.Object { GetNewObject: func() client.Object {
object := rollout.DeepCopy() object := rollout.DeepCopy()
object.Status.Phase = appsv1beta1.RolloutPhaseInitial object.Status.Phase = appsv1beta1.RolloutPhaseInitial
object.Spec.Strategy.Canary.Steps[0].Weight = utilpointer.Int32Ptr(5) object.Spec.Strategy.Canary.Steps[0].Traffic = utilpointer.String("5%")
return object return object
}, },
}, },
@ -491,7 +445,7 @@ func TestRolloutValidateUpdate(t *testing.T) {
GetNewObject: func() client.Object { GetNewObject: func() client.Object {
object := rollout.DeepCopy() object := rollout.DeepCopy()
object.Status.Phase = appsv1beta1.RolloutPhaseHealthy object.Status.Phase = appsv1beta1.RolloutPhaseHealthy
object.Spec.Strategy.Canary.Steps[0].Weight = utilpointer.Int32Ptr(5) object.Spec.Strategy.Canary.Steps[0].Traffic = utilpointer.String("5%")
return object return object
}, },
}, },

View File

@ -107,15 +107,6 @@ func validateV1alpha1RolloutRollingStyle(rollout *appsv1alpha1.Rollout, fldPath
return field.ErrorList{field.Invalid(fldPath, rollout.Annotations[appsv1alpha1.RolloutStyleAnnotation], return field.ErrorList{field.Invalid(fldPath, rollout.Annotations[appsv1alpha1.RolloutStyleAnnotation],
"Rolling style must be 'Canary', 'Partition' or empty")} "Rolling style must be 'Canary', 'Partition' or empty")}
} }
workloadRef := rollout.Spec.ObjectRef.WorkloadRef
if workloadRef == nil || workloadRef.Kind == util.ControllerKindDep.Kind {
return nil // Deployment support all rolling styles, no need to validate.
}
if strings.EqualFold(rollout.Annotations[appsv1alpha1.RolloutStyleAnnotation], string(appsv1alpha1.CanaryRollingStyle)) {
return field.ErrorList{field.Invalid(fldPath, rollout.Annotations[appsv1alpha1.RolloutStyleAnnotation],
"Only Deployment support canary rolling style")}
}
return nil return nil
} }
@ -145,11 +136,35 @@ func validateV1alpha1RolloutSpecCanaryStrategy(canary *appsv1alpha1.CanaryStrate
errList = append(errList, field.Invalid(fldPath, canary.TrafficRoutings, "Rollout currently only support single TrafficRouting.")) errList = append(errList, field.Invalid(fldPath, canary.TrafficRoutings, "Rollout currently only support single TrafficRouting."))
} }
for _, traffic := range canary.TrafficRoutings { for _, traffic := range canary.TrafficRoutings {
errList = append(errList, validateRolloutSpecCanaryTraffic(traffic, fldPath.Child("TrafficRouting"))...) errList = append(errList, validateV1alpha1RolloutSpecCanaryTraffic(traffic, fldPath.Child("TrafficRouting"))...)
} }
return errList return errList
} }
func validateV1alpha1RolloutSpecCanaryTraffic(traffic appsv1alpha1.TrafficRoutingRef, fldPath *field.Path) field.ErrorList {
errList := field.ErrorList{}
if len(traffic.Service) == 0 {
errList = append(errList, field.Invalid(fldPath.Child("Service"), traffic.Service, "TrafficRouting.Service cannot be empty"))
}
if traffic.Gateway == nil && traffic.Ingress == nil && traffic.CustomNetworkRefs == nil {
errList = append(errList, field.Invalid(fldPath.Child("TrafficRoutings"), traffic.Ingress, "TrafficRoutings are not set"))
}
if traffic.Ingress != nil {
if traffic.Ingress.Name == "" {
errList = append(errList, field.Invalid(fldPath.Child("Ingress"), traffic.Ingress, "TrafficRouting.Ingress.Ingress cannot be empty"))
}
}
if traffic.Gateway != nil {
if traffic.Gateway.HTTPRouteName == nil || *traffic.Gateway.HTTPRouteName == "" {
errList = append(errList, field.Invalid(fldPath.Child("Gateway"), traffic.Gateway, "TrafficRouting.Gateway must set the name of HTTPRoute or HTTPsRoute"))
}
}
return errList
}
func validateV1alpha1RolloutSpecCanarySteps(steps []appsv1alpha1.CanaryStep, fldPath *field.Path, isTraffic bool) field.ErrorList { func validateV1alpha1RolloutSpecCanarySteps(steps []appsv1alpha1.CanaryStep, fldPath *field.Path, isTraffic bool) field.ErrorList {
stepCount := len(steps) stepCount := len(steps)
if stepCount == 0 { if stepCount == 0 {

View File

@ -29,6 +29,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission" "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"sigs.k8s.io/controller-runtime/pkg/webhook/conversion"
) )
type GateFunc func() (enabled bool) type GateFunc func() (enabled bool)
@ -90,11 +91,12 @@ func SetupWithManager(mgr manager.Manager) error {
server.Register(path, &webhook.Admission{Handler: handler}) server.Register(path, &webhook.Admission{Handler: handler})
klog.V(3).Infof("Registered webhook handler %s", path) klog.V(3).Infof("Registered webhook handler %s", path)
} }
err := initialize(context.TODO(), mgr.GetConfig()) err := initialize(context.TODO(), mgr.GetConfig())
if err != nil { if err != nil {
return err return err
} }
// register conversion webhook
server.Register("/convert", &conversion.Webhook{})
klog.Infof("webhook init done") klog.Infof("webhook init done")
return nil return nil
} }

View File

@ -24,6 +24,7 @@ import (
webhookutil "github.com/openkruise/rollouts/pkg/webhook/util" webhookutil "github.com/openkruise/rollouts/pkg/webhook/util"
"github.com/openkruise/rollouts/pkg/webhook/util/configuration" "github.com/openkruise/rollouts/pkg/webhook/util/configuration"
"github.com/openkruise/rollouts/pkg/webhook/util/crd"
"github.com/openkruise/rollouts/pkg/webhook/util/generator" "github.com/openkruise/rollouts/pkg/webhook/util/generator"
"github.com/openkruise/rollouts/pkg/webhook/util/writer" "github.com/openkruise/rollouts/pkg/webhook/util/writer"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1" admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
@ -266,6 +267,10 @@ func (c *Controller) sync() error {
return fmt.Errorf("failed to ensure configuration: %v", err) return fmt.Errorf("failed to ensure configuration: %v", err)
} }
if err := crd.Ensure(c.crdClient, c.crdLister, certs.CACert); err != nil {
return fmt.Errorf("failed to ensure crd: %v", err)
}
onceInit.Do(func() { onceInit.Do(func() {
close(uninit) close(uninit)
}) })

View File

@ -0,0 +1,87 @@
/*
Copyright 2023 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package crd
import (
"context"
"fmt"
"reflect"
rolloutapi "github.com/openkruise/rollouts/api"
webhookutil "github.com/openkruise/rollouts/pkg/webhook/util"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
apiextensionslisters "k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
)
var (
scheme = runtime.NewScheme()
)
func init() {
utilruntime.Must(rolloutapi.AddToScheme(scheme))
}
func Ensure(client apiextensionsclientset.Interface, lister apiextensionslisters.CustomResourceDefinitionLister, caBundle []byte) error {
crdList, err := lister.List(labels.Everything())
if err != nil {
return fmt.Errorf("failed to list crds: %v", err)
}
webhookConfig := apiextensionsv1.WebhookClientConfig{
CABundle: caBundle,
}
path := "/convert"
if host := webhookutil.GetHost(); len(host) > 0 {
url := fmt.Sprintf("https://%s:%d%s", host, webhookutil.GetPort(), path)
webhookConfig.URL = &url
} else {
var port int32 = 443
webhookConfig.Service = &apiextensionsv1.ServiceReference{
Namespace: webhookutil.GetNamespace(),
Name: webhookutil.GetServiceName(),
Port: &port,
Path: &path,
}
}
for _, crd := range crdList {
if len(crd.Spec.Versions) == 0 || crd.Spec.Conversion == nil || crd.Spec.Conversion.Strategy != apiextensionsv1.WebhookConverter {
continue
}
if !scheme.Recognizes(schema.GroupVersionKind{Group: crd.Spec.Group, Version: crd.Spec.Versions[0].Name, Kind: crd.Spec.Names.Kind}) {
continue
}
if crd.Spec.Conversion.Webhook == nil || !reflect.DeepEqual(crd.Spec.Conversion.Webhook.ClientConfig, webhookConfig) {
newCRD := crd.DeepCopy()
newCRD.Spec.Conversion.Webhook = &apiextensionsv1.WebhookConversion{
ClientConfig: webhookConfig.DeepCopy(),
ConversionReviewVersions: []string{"v1", "v1beta1"},
}
if _, err := client.ApiextensionsV1().CustomResourceDefinitions().Update(context.TODO(), newCRD, metav1.UpdateOptions{}); err != nil {
return fmt.Errorf("failed to update CRD %s: %v", newCRD.Name, err)
}
}
}
return nil
}

View File

@ -422,14 +422,14 @@ func (h *WorkloadHandler) fetchMatchedRollout(obj client.Object) (*appsv1beta1.R
} }
for i := range rolloutList.Items { for i := range rolloutList.Items {
rollout := &rolloutList.Items[i] rollout := &rolloutList.Items[i]
if !rollout.DeletionTimestamp.IsZero() || rollout.Spec.ObjectRef.WorkloadRef == nil { if !rollout.DeletionTimestamp.IsZero() {
continue continue
} }
if rollout.Status.Phase == appsv1beta1.RolloutPhaseDisabled { if rollout.Status.Phase == appsv1beta1.RolloutPhaseDisabled {
klog.Infof("Disabled rollout(%s/%s) fetched when fetching matched rollout", rollout.Namespace, rollout.Name) klog.Infof("Disabled rollout(%s/%s) fetched when fetching matched rollout", rollout.Namespace, rollout.Name)
continue continue
} }
ref := rollout.Spec.ObjectRef.WorkloadRef ref := rollout.Spec.WorkloadRef
gv, err := schema.ParseGroupVersion(ref.APIVersion) gv, err := schema.ParseGroupVersion(ref.APIVersion)
if err != nil { if err != nil {
klog.Warningf("ParseGroupVersion rollout(%s/%s) ref failed: %s", rollout.Namespace, rollout.Name, err.Error()) klog.Warningf("ParseGroupVersion rollout(%s/%s) ref failed: %s", rollout.Namespace, rollout.Name, err.Error())

View File

@ -274,12 +274,10 @@ var (
Labels: map[string]string{}, Labels: map[string]string{},
}, },
Spec: appsv1beta1.RolloutSpec{ Spec: appsv1beta1.RolloutSpec{
ObjectRef: appsv1beta1.ObjectRef{ WorkloadRef: appsv1beta1.ObjectRef{
WorkloadRef: &appsv1beta1.WorkloadRef{ APIVersion: "apps/v1",
APIVersion: "apps/v1", Kind: "Deployment",
Kind: "Deployment", Name: "echoserver",
Name: "echoserver",
},
}, },
Strategy: appsv1beta1.RolloutStrategy{ Strategy: appsv1beta1.RolloutStrategy{
Canary: &appsv1beta1.CanaryStrategy{}, Canary: &appsv1beta1.CanaryStrategy{},
@ -345,7 +343,7 @@ func TestHandlerDeployment(t *testing.T) {
}, },
getRollout: func() *appsv1beta1.Rollout { getRollout: func() *appsv1beta1.Rollout {
obj := rolloutDemo.DeepCopy() obj := rolloutDemo.DeepCopy()
obj.Spec.ObjectRef.WorkloadRef = &appsv1beta1.WorkloadRef{ obj.Spec.WorkloadRef = appsv1beta1.ObjectRef{
APIVersion: "apps/v1", APIVersion: "apps/v1",
Kind: "Deployment", Kind: "Deployment",
Name: "other", Name: "other",
@ -377,10 +375,10 @@ func TestHandlerDeployment(t *testing.T) {
getRollout: func() *appsv1beta1.Rollout { getRollout: func() *appsv1beta1.Rollout {
demo := rolloutDemo.DeepCopy() demo := rolloutDemo.DeepCopy()
demo.Spec.Strategy.Canary = &appsv1beta1.CanaryStrategy{ demo.Spec.Strategy.Canary = &appsv1beta1.CanaryStrategy{
TrafficRoutings: []appsv1alpha1.TrafficRoutingRef{ TrafficRoutings: []appsv1beta1.TrafficRoutingRef{
{ {
Service: "echoserver", Service: "echoserver",
Ingress: &appsv1alpha1.IngressTrafficRouting{ Ingress: &appsv1beta1.IngressTrafficRouting{
Name: "echoserver", Name: "echoserver",
}, },
}, },
@ -582,7 +580,7 @@ func TestHandlerCloneSet(t *testing.T) {
}, },
getRollout: func() *appsv1beta1.Rollout { getRollout: func() *appsv1beta1.Rollout {
obj := rolloutDemo.DeepCopy() obj := rolloutDemo.DeepCopy()
obj.Spec.ObjectRef.WorkloadRef = &appsv1beta1.WorkloadRef{ obj.Spec.WorkloadRef = appsv1beta1.ObjectRef{
APIVersion: "apps.kruise.io/v1alpha1", APIVersion: "apps.kruise.io/v1alpha1",
Kind: "CloneSet", Kind: "CloneSet",
Name: "echoserver", Name: "echoserver",
@ -646,7 +644,7 @@ func TestHandlerDaemonSet(t *testing.T) {
}, },
getRollout: func() *appsv1beta1.Rollout { getRollout: func() *appsv1beta1.Rollout {
obj := rolloutDemo.DeepCopy() obj := rolloutDemo.DeepCopy()
obj.Spec.ObjectRef.WorkloadRef = &appsv1beta1.WorkloadRef{ obj.Spec.WorkloadRef = appsv1beta1.ObjectRef{
APIVersion: "apps.kruise.io/v1alpha1", APIVersion: "apps.kruise.io/v1alpha1",
Kind: "DaemonSet", Kind: "DaemonSet",
Name: "echoserver", Name: "echoserver",
@ -710,7 +708,7 @@ func TestHandleStatefulSet(t *testing.T) {
}, },
getRollout: func() *appsv1beta1.Rollout { getRollout: func() *appsv1beta1.Rollout {
obj := rolloutDemo.DeepCopy() obj := rolloutDemo.DeepCopy()
obj.Spec.ObjectRef.WorkloadRef = &appsv1beta1.WorkloadRef{ obj.Spec.WorkloadRef = appsv1beta1.ObjectRef{
APIVersion: "apps.kruise.io/v1beta1", APIVersion: "apps.kruise.io/v1beta1",
Kind: "StatefulSet", Kind: "StatefulSet",
Name: "echoserver", Name: "echoserver",