new v1beta1 apis (#184)
Signed-off-by: liheng.zms <liheng.zms@alibaba-inc.com>
This commit is contained in:
parent
07b7f20f6a
commit
9dcf3659d2
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
import (
|
||||
|
|
|
@ -54,6 +54,10 @@ type ReleasePlan struct {
|
|||
// only support for canary deployment
|
||||
// +optional
|
||||
PatchPodTemplateMetadata *PatchPodTemplateMetadata `json:"patchPodTemplateMetadata,omitempty"`
|
||||
// If true, then it will create new deployment for canary, such as: workload-demo-canary.
|
||||
// When user verifies that the canary version is ready, we will remove the canary deployment and release the deployment workload-demo in full.
|
||||
// Current only support k8s native deployment
|
||||
EnableExtraWorkloadForCanary bool `json:"enableExtraWorkloadForCanary"`
|
||||
}
|
||||
|
||||
type FinalizingPolicyType string
|
||||
|
|
|
@ -41,8 +41,9 @@ type BatchRelease struct {
|
|||
|
||||
// BatchReleaseSpec defines how to describe an update between different compRevision
|
||||
type BatchReleaseSpec struct {
|
||||
// TargetRef contains the GVK and name of the workload that we need to upgrade to.
|
||||
TargetRef ObjectRef `json:"targetReference"`
|
||||
// WorkloadRef contains enough information to let you identify a workload for Rollout
|
||||
// Batch release of the bypass
|
||||
WorkloadRef ObjectRef `json:"workloadRef,omitempty"`
|
||||
// ReleasePlan is the details on how to rollout the resources
|
||||
ReleasePlan ReleasePlan `json:"releasePlan"`
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
package v1beta1
|
||||
|
||||
import (
|
||||
"github.com/openkruise/rollouts/api/v1alpha1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
|
@ -42,51 +41,25 @@ const (
|
|||
// RollbackInBatchAnnotation is set to rollout annotations.
|
||||
// RollbackInBatchAnnotation allow use disable quick rollback, and will roll back in batch style.
|
||||
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
|
||||
type RolloutSpec struct {
|
||||
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||
// Important: Run "make" to regenerate code after modifying this file
|
||||
// ObjectRef indicates workload
|
||||
ObjectRef ObjectRef `json:"objectRef"`
|
||||
// WorkloadRef contains enough information to let you identify a workload for Rollout
|
||||
// Batch release of the bypass
|
||||
WorkloadRef ObjectRef `json:"workloadRef"`
|
||||
// rollout 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
|
||||
//+kubebuilder:validation:Optional
|
||||
//+kubebuilder:default=false
|
||||
Disabled bool `json:"disabled"`
|
||||
}
|
||||
|
||||
// ObjectRef holds a references to the Kubernetes object
|
||||
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
|
||||
APIVersion string `json:"apiVersion"`
|
||||
// Kind of the referent
|
||||
|
@ -111,7 +84,7 @@ type CanaryStrategy struct {
|
|||
Steps []CanaryStep `json:"steps,omitempty"`
|
||||
// TrafficRoutings hosts all the supported service meshes supported to enable more fine-grained traffic routing
|
||||
// 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.
|
||||
// Only when FailureThreshold are satisfied, Rollout can enter ready state.
|
||||
// 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
|
||||
// +optional
|
||||
PatchPodTemplateMetadata *PatchPodTemplateMetadata `json:"patchPodTemplateMetadata,omitempty"`
|
||||
// If true, then it will create new deployment for canary, such as: workload-demo-canary.
|
||||
// When user verifies that the canary version is ready, we will remove the canary deployment and release the deployment workload-demo in full.
|
||||
// Current only support k8s native deployment
|
||||
EnableExtraWorkloadForCanary bool `json:"enableExtraWorkloadForCanary,omitempty"`
|
||||
// TrafficRoutingRef is TrafficRouting's Name
|
||||
TrafficRoutingRef string `json:"trafficRoutingRef,omitempty"`
|
||||
}
|
||||
|
||||
type PatchPodTemplateMetadata struct {
|
||||
|
@ -133,7 +112,7 @@ type PatchPodTemplateMetadata struct {
|
|||
|
||||
// CanaryStep defines a step of a canary workload.
|
||||
type CanaryStep struct {
|
||||
v1alpha1.TrafficRoutingStrategy `json:",inline"`
|
||||
TrafficRoutingStrategy `json:",inline"`
|
||||
// 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.
|
||||
Replicas *intstr.IntOrString `json:"replicas,omitempty"`
|
||||
|
@ -142,6 +121,35 @@ type CanaryStep struct {
|
|||
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 {
|
||||
// Headers specifies HTTP request header matchers. Multiple match values are
|
||||
// ANDed together, meaning, a request must match all the specified headers
|
||||
|
|
|
@ -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"`
|
||||
}
|
|
@ -22,7 +22,6 @@ limitations under the License.
|
|||
package v1beta1
|
||||
|
||||
import (
|
||||
"github.com/openkruise/rollouts/api/v1alpha1"
|
||||
"k8s.io/api/apps/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
"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.
|
||||
func (in *BatchReleaseSpec) DeepCopyInto(out *BatchReleaseSpec) {
|
||||
*out = *in
|
||||
in.TargetRef.DeepCopyInto(&out.TargetRef)
|
||||
out.WorkloadRef = in.WorkloadRef
|
||||
in.ReleasePlan.DeepCopyInto(&out.ReleasePlan)
|
||||
}
|
||||
|
||||
|
@ -210,7 +209,7 @@ func (in *CanaryStrategy) DeepCopyInto(out *CanaryStrategy) {
|
|||
}
|
||||
if in.TrafficRoutings != nil {
|
||||
in, out := &in.TrafficRoutings, &out.TrafficRoutings
|
||||
*out = make([]v1alpha1.TrafficRoutingRef, len(*in))
|
||||
*out = make([]TrafficRoutingRef, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
|
@ -273,6 +272,26 @@ func (in *DeploymentStrategy) DeepCopy() *DeploymentStrategy {
|
|||
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.
|
||||
func (in *HttpRouteMatch) DeepCopyInto(out *HttpRouteMatch) {
|
||||
*out = *in
|
||||
|
@ -295,14 +314,24 @@ func (in *HttpRouteMatch) DeepCopy() *HttpRouteMatch {
|
|||
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.
|
||||
func (in *ObjectRef) DeepCopyInto(out *ObjectRef) {
|
||||
*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.
|
||||
|
@ -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.
|
||||
func (in *RolloutSpec) DeepCopyInto(out *RolloutSpec) {
|
||||
*out = *in
|
||||
in.ObjectRef.DeepCopyInto(&out.ObjectRef)
|
||||
out.WorkloadRef = in.WorkloadRef
|
||||
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.
|
||||
func (in *WorkloadRef) DeepCopyInto(out *WorkloadRef) {
|
||||
func (in *TrafficRoutingRef) DeepCopyInto(out *TrafficRoutingRef) {
|
||||
*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.
|
||||
func (in *WorkloadRef) DeepCopy() *WorkloadRef {
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficRoutingRef.
|
||||
func (in *TrafficRoutingRef) DeepCopy() *TrafficRoutingRef {
|
||||
if in == 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)
|
||||
return out
|
||||
}
|
||||
|
|
|
@ -345,6 +345,13 @@ spec:
|
|||
- canaryReplicas
|
||||
type: object
|
||||
type: array
|
||||
enableExtraWorkloadForCanary:
|
||||
description: 'If true, then it will create new deployment for
|
||||
canary, such as: workload-demo-canary. When user verifies that
|
||||
the canary version is ready, we will remove the canary deployment
|
||||
and release the deployment workload-demo in full. Current only
|
||||
support k8s native deployment'
|
||||
type: boolean
|
||||
failureThreshold:
|
||||
anyOf:
|
||||
- type: integer
|
||||
|
@ -378,33 +385,29 @@ spec:
|
|||
rolloutID:
|
||||
description: RolloutID indicates an id for each rollout progress
|
||||
type: string
|
||||
required:
|
||||
- enableExtraWorkloadForCanary
|
||||
type: object
|
||||
targetReference:
|
||||
description: TargetRef contains the GVK and name of the workload that
|
||||
we need to upgrade to.
|
||||
workloadRef:
|
||||
description: WorkloadRef contains enough information to let you identify
|
||||
a workload for Rollout Batch release of the bypass
|
||||
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
|
||||
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:
|
||||
- releasePlan
|
||||
- targetReference
|
||||
type: object
|
||||
status:
|
||||
description: BatchReleaseStatus defines the observed state of a release
|
||||
|
|
|
@ -567,37 +567,6 @@ spec:
|
|||
description: if a rollout disabled, then the rollout would not watch
|
||||
changes of workload
|
||||
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:
|
||||
description: rollout strategy
|
||||
properties:
|
||||
|
@ -605,6 +574,13 @@ spec:
|
|||
description: CanaryStrategy defines parameters for a Replica Based
|
||||
Canary
|
||||
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:
|
||||
anyOf:
|
||||
- type: integer
|
||||
|
@ -830,13 +806,15 @@ spec:
|
|||
- name
|
||||
x-kubernetes-list-type: map
|
||||
type: object
|
||||
weight:
|
||||
description: Weight indicate how many percentage of
|
||||
traffic:
|
||||
description: Traffic indicate how many percentage of
|
||||
traffic the canary pods should receive
|
||||
format: int32
|
||||
type: integer
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
trafficRoutingRef:
|
||||
description: TrafficRoutingRef is TrafficRouting's Name
|
||||
type: string
|
||||
trafficRoutings:
|
||||
description: TrafficRoutings hosts all the supported service
|
||||
meshes supported to enable more fine-grained traffic routing
|
||||
|
@ -850,12 +828,17 @@ spec:
|
|||
description: CustomNetworkRefs hold a list of custom
|
||||
providers to route traffic
|
||||
items:
|
||||
description: ObjectRef holds a references to the Kubernetes
|
||||
object
|
||||
properties:
|
||||
apiVersion:
|
||||
description: API Version of the referent
|
||||
type: string
|
||||
kind:
|
||||
description: Kind of the referent
|
||||
type: string
|
||||
name:
|
||||
description: Name of the referent
|
||||
type: string
|
||||
required:
|
||||
- apiVersion
|
||||
|
@ -911,9 +894,29 @@ spec:
|
|||
value is false
|
||||
type: boolean
|
||||
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:
|
||||
- objectRef
|
||||
- strategy
|
||||
- workloadRef
|
||||
type: object
|
||||
status:
|
||||
description: RolloutStatus defines the observed state of Rollout
|
||||
|
|
|
@ -11,8 +11,8 @@ resources:
|
|||
patchesStrategicMerge:
|
||||
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
|
||||
# patches here are for enabling the conversion webhook for each CRD
|
||||
#- patches/webhook_in_rollouts.yaml
|
||||
#- patches/webhook_in_batchreleases.yaml
|
||||
- patches/webhook_in_rollouts.yaml
|
||||
- patches/webhook_in_batchreleases.yaml
|
||||
#+kubebuilder:scaffold:crdkustomizewebhookpatch
|
||||
|
||||
# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix.
|
||||
|
|
|
@ -13,4 +13,4 @@ spec:
|
|||
name: webhook-service
|
||||
path: /convert
|
||||
conversionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
|
|
|
@ -13,4 +13,4 @@ spec:
|
|||
name: webhook-service
|
||||
path: /convert
|
||||
conversionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
|
|
|
@ -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
|
||||
```
|
|
@ -14,6 +14,7 @@ import (
|
|||
lua "github.com/yuin/gopher-lua"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
utilpointer "k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
@ -82,8 +83,12 @@ func objectToTable(path string) error {
|
|||
if rollout != nil {
|
||||
steps := rollout.Spec.Strategy.Canary.Steps
|
||||
for i, step := range steps {
|
||||
weight := step.TrafficRoutingStrategy.Weight
|
||||
if step.TrafficRoutingStrategy.Weight == nil {
|
||||
var weight *int32
|
||||
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)
|
||||
}
|
||||
var canaryService string
|
||||
|
@ -111,13 +116,19 @@ func objectToTable(path string) error {
|
|||
var canaryService string
|
||||
stableService := trafficRouting.Spec.ObjectRef[0].Service
|
||||
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.Data{
|
||||
Labels: testCase.Original.GetLabels(),
|
||||
Annotations: testCase.Original.GetAnnotations(),
|
||||
Spec: testCase.Original.Object["spec"],
|
||||
},
|
||||
Matches: trafficRouting.Spec.Strategy.Matches,
|
||||
Matches: matches,
|
||||
CanaryWeight: *weight,
|
||||
StableWeight: 100 - *weight,
|
||||
CanaryService: canaryService,
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
rollout:
|
||||
apiVersion: rollouts.kruise.io/v1alpha1
|
||||
rollout:
|
||||
apiVersion: rollouts.kruise.io/v1beta1
|
||||
kind: Rollout
|
||||
metadata:
|
||||
name: rollouts-demo
|
||||
annotations:
|
||||
rollouts.kruise.io/rolling-style: canary
|
||||
spec:
|
||||
disabled: false
|
||||
objectRef:
|
||||
workloadRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: deploy-demo
|
||||
workloadRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: deploy-demo
|
||||
strategy:
|
||||
canary:
|
||||
steps:
|
||||
|
@ -32,7 +28,7 @@ rollout:
|
|||
- type: RegularExpression
|
||||
name: name
|
||||
value: ".*demo"
|
||||
- weight: 50
|
||||
- traffic: "50%"
|
||||
trafficRoutings:
|
||||
- service: svc-demo
|
||||
customNetworkRefs:
|
||||
|
|
|
@ -114,7 +114,7 @@ function GenerateRoutes(spec, stableService, canaryService, stableWeight, canary
|
|||
end
|
||||
end
|
||||
|
||||
if (obj.matches)
|
||||
if (obj.matches and next(obj.matches) ~= nil)
|
||||
then
|
||||
GenerateRoutesWithMatches(spec, obj.matches, obj.stableService, obj.canaryService)
|
||||
else
|
||||
|
|
|
@ -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)
|
||||
|
||||
// If workload watcher does not exist, then add the watcher dynamically
|
||||
workloadRef := release.Spec.TargetRef.WorkloadRef
|
||||
workloadGVK := util.GetGVKFrom(workloadRef)
|
||||
workloadRef := release.Spec.WorkloadRef
|
||||
workloadGVK := util.GetGVKFrom(&workloadRef)
|
||||
_, exists := watchedWorkload.Load(workloadGVK.String())
|
||||
if workloadRef != nil && !exists {
|
||||
if !exists {
|
||||
succeeded, err := util.AddWatcherDynamically(runtimeController, workloadHandler, workloadGVK)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
|
|
|
@ -61,15 +61,14 @@ var (
|
|||
UID: types.UID("87076677"),
|
||||
},
|
||||
Spec: v1beta1.BatchReleaseSpec{
|
||||
TargetRef: v1beta1.ObjectRef{
|
||||
WorkloadRef: &v1beta1.WorkloadRef{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "sample",
|
||||
},
|
||||
WorkloadRef: v1beta1.ObjectRef{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "sample",
|
||||
},
|
||||
ReleasePlan: v1beta1.ReleasePlan{
|
||||
BatchPartition: pointer.Int32(0),
|
||||
EnableExtraWorkloadForCanary: true,
|
||||
BatchPartition: pointer.Int32(0),
|
||||
Batches: []v1beta1.ReleaseBatch{
|
||||
{
|
||||
CanaryReplicas: intstr.FromString("10%"),
|
||||
|
@ -141,12 +140,10 @@ var (
|
|||
UID: types.UID("87076677"),
|
||||
},
|
||||
Spec: v1beta1.BatchReleaseSpec{
|
||||
TargetRef: v1beta1.ObjectRef{
|
||||
WorkloadRef: &v1beta1.WorkloadRef{
|
||||
APIVersion: "apps.kruise.io/v1alpha1",
|
||||
Kind: "CloneSet",
|
||||
Name: "sample",
|
||||
},
|
||||
WorkloadRef: v1beta1.ObjectRef{
|
||||
APIVersion: "apps.kruise.io/v1alpha1",
|
||||
Kind: "CloneSet",
|
||||
Name: "sample",
|
||||
},
|
||||
ReleasePlan: v1beta1.ReleasePlan{
|
||||
BatchPartition: pointer.Int32Ptr(0),
|
||||
|
|
|
@ -240,14 +240,14 @@ func getBatchRelease(c client.Reader, workloadNamespaceName types.NamespacedName
|
|||
|
||||
for i := range brList.Items {
|
||||
br := &brList.Items[i]
|
||||
targetRef := br.Spec.TargetRef
|
||||
targetGV, err := schema.ParseGroupVersion(targetRef.WorkloadRef.APIVersion)
|
||||
targetRef := br.Spec.WorkloadRef
|
||||
targetGV, err := schema.ParseGroupVersion(targetRef.APIVersion)
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,11 +19,9 @@ package batchrelease
|
|||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
appsv1alpha1 "github.com/openkruise/kruise-api/apps/v1alpha1"
|
||||
"github.com/openkruise/rollouts/api/v1alpha1"
|
||||
"github.com/openkruise/rollouts/api/v1beta1"
|
||||
"github.com/openkruise/rollouts/pkg/controller/batchrelease/control"
|
||||
"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
|
||||
func (r *Executor) getReleaseController(release *v1beta1.BatchRelease, newStatus *v1beta1.BatchReleaseStatus) (control.Interface, error) {
|
||||
targetRef := release.Spec.TargetRef.WorkloadRef
|
||||
if targetRef == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
targetRef := release.Spec.WorkloadRef
|
||||
gvk := schema.FromAPIVersionAndKind(targetRef.APIVersion, targetRef.Kind)
|
||||
if !util.IsSupportedWorkload(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():
|
||||
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)
|
||||
return partitionstyle.NewControlPlane(partitiondeployment.NewController, r.client, r.recorder, release, newStatus, targetKey, gvk), nil
|
||||
} else {
|
||||
|
|
|
@ -131,12 +131,10 @@ var (
|
|||
},
|
||||
},
|
||||
},
|
||||
TargetRef: v1beta1.ObjectRef{
|
||||
WorkloadRef: &v1beta1.WorkloadRef{
|
||||
APIVersion: deploymentDemo.APIVersion,
|
||||
Kind: deploymentDemo.Kind,
|
||||
Name: deploymentDemo.Name,
|
||||
},
|
||||
WorkloadRef: v1beta1.ObjectRef{
|
||||
APIVersion: deploymentDemo.APIVersion,
|
||||
Kind: deploymentDemo.Kind,
|
||||
Name: deploymentDemo.Name,
|
||||
},
|
||||
},
|
||||
Status: v1beta1.BatchReleaseStatus{
|
||||
|
|
|
@ -129,12 +129,10 @@ var (
|
|||
},
|
||||
},
|
||||
},
|
||||
TargetRef: v1beta1.ObjectRef{
|
||||
WorkloadRef: &v1beta1.WorkloadRef{
|
||||
APIVersion: cloneDemo.APIVersion,
|
||||
Kind: cloneDemo.Kind,
|
||||
Name: cloneDemo.Name,
|
||||
},
|
||||
WorkloadRef: v1beta1.ObjectRef{
|
||||
APIVersion: cloneDemo.APIVersion,
|
||||
Kind: cloneDemo.Kind,
|
||||
Name: cloneDemo.Name,
|
||||
},
|
||||
},
|
||||
Status: v1beta1.BatchReleaseStatus{
|
||||
|
|
|
@ -117,12 +117,10 @@ var (
|
|||
},
|
||||
},
|
||||
},
|
||||
TargetRef: v1beta1.ObjectRef{
|
||||
WorkloadRef: &v1beta1.WorkloadRef{
|
||||
APIVersion: daemonDemo.APIVersion,
|
||||
Kind: daemonDemo.Kind,
|
||||
Name: daemonDemo.Name,
|
||||
},
|
||||
WorkloadRef: v1beta1.ObjectRef{
|
||||
APIVersion: daemonDemo.APIVersion,
|
||||
Kind: daemonDemo.Kind,
|
||||
Name: daemonDemo.Name,
|
||||
},
|
||||
},
|
||||
Status: v1beta1.BatchReleaseStatus{
|
||||
|
|
|
@ -131,12 +131,10 @@ var (
|
|||
},
|
||||
},
|
||||
},
|
||||
TargetRef: v1beta1.ObjectRef{
|
||||
WorkloadRef: &v1beta1.WorkloadRef{
|
||||
APIVersion: deploymentDemo.APIVersion,
|
||||
Kind: deploymentDemo.Kind,
|
||||
Name: deploymentDemo.Name,
|
||||
},
|
||||
WorkloadRef: v1beta1.ObjectRef{
|
||||
APIVersion: deploymentDemo.APIVersion,
|
||||
Kind: deploymentDemo.Kind,
|
||||
Name: deploymentDemo.Name,
|
||||
},
|
||||
},
|
||||
Status: v1beta1.BatchReleaseStatus{
|
||||
|
|
|
@ -136,12 +136,10 @@ var (
|
|||
},
|
||||
},
|
||||
},
|
||||
TargetRef: v1beta1.ObjectRef{
|
||||
WorkloadRef: &v1beta1.WorkloadRef{
|
||||
APIVersion: stsDemo.APIVersion,
|
||||
Kind: stsDemo.Kind,
|
||||
Name: stsDemo.Name,
|
||||
},
|
||||
WorkloadRef: v1beta1.ObjectRef{
|
||||
APIVersion: stsDemo.APIVersion,
|
||||
Kind: stsDemo.Kind,
|
||||
Name: stsDemo.Name,
|
||||
},
|
||||
},
|
||||
Status: v1beta1.BatchReleaseStatus{
|
||||
|
|
|
@ -159,8 +159,8 @@ func TestSyncDeployment(t *testing.T) {
|
|||
rs.Status.Replicas = replicas
|
||||
if strings.HasPrefix(name, "scale") {
|
||||
rs.Annotations = map[string]string{
|
||||
util.DesiredReplicasAnnotation: strconv.Itoa(-1),
|
||||
util.MaxReplicasAnnotation: strconv.Itoa(int(test.dAvailable + test.maxSurge.IntVal)),
|
||||
util.ReplicasAnnotation: strconv.Itoa(-1),
|
||||
util.MaxReplicasAnnotation: strconv.Itoa(int(test.dAvailable + test.maxSurge.IntVal)),
|
||||
}
|
||||
}
|
||||
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)
|
||||
if strings.HasPrefix(name, "scale") {
|
||||
newRS.Annotations = map[string]string{
|
||||
util.DesiredReplicasAnnotation: strconv.Itoa(-1),
|
||||
util.MaxReplicasAnnotation: strconv.Itoa(int(test.dAvailable + test.maxSurge.IntVal)),
|
||||
util.ReplicasAnnotation: strconv.Itoa(-1),
|
||||
util.MaxReplicasAnnotation: strconv.Itoa(int(test.dAvailable + test.maxSurge.IntVal)),
|
||||
}
|
||||
}
|
||||
newRS.Status.Replicas = test.newRSReplicas
|
||||
|
|
|
@ -270,9 +270,9 @@ func ScaleDownLimitForOld(oldRSs []*apps.ReplicaSet, newRS *apps.ReplicaSet, dep
|
|||
klog.V(4).InfoS("Calculate scale down limit for ",
|
||||
"Deployment", klog.KObj(deployment),
|
||||
// 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
|
||||
"ReplicaS(Old)", oldPodsCount, "DesiredReplicas(Old)", oldRSDesiredCount, "ScaleDownLimit(Old)", scaleDownOldLimit,
|
||||
"ReplicaS(Old)", oldPodsCount, "Replicas(Old)", oldRSDesiredCount, "ScaleDownLimit(Old)", scaleDownOldLimit,
|
||||
// About the deployment
|
||||
"Replicas(Deployment)", *(deployment.Spec.Replicas), "Partition(Deployment)", newRSUpdateLimit)
|
||||
|
||||
|
|
|
@ -525,7 +525,7 @@ func (dc *DeploymentController) isScalingEvent(ctx context.Context, d *apps.Depl
|
|||
}
|
||||
allRSs := append(oldRSs, newRS)
|
||||
for _, rs := range deploymentutil.FilterActiveReplicaSets(allRSs) {
|
||||
desired, ok := deploymentutil.GetDesiredReplicasAnnotation(rs)
|
||||
desired, ok := deploymentutil.GetReplicasAnnotation(rs)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -44,10 +44,10 @@ const (
|
|||
RevisionAnnotation = "deployment.kubernetes.io/revision"
|
||||
// RevisionHistoryAnnotation maintains the history of all old revisions that a replica set has served for a deployment.
|
||||
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
|
||||
// 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
|
||||
// is deployment.spec.replicas + maxSurge. Used by the underlying replica sets to estimate their
|
||||
// proportions in case the deployment has surge replicas.
|
||||
|
@ -267,7 +267,7 @@ var annotationsToSkip = map[string]bool{
|
|||
v1.LastAppliedConfigAnnotation: true,
|
||||
RevisionAnnotation: true,
|
||||
RevisionHistoryAnnotation: true,
|
||||
DesiredReplicasAnnotation: true,
|
||||
ReplicasAnnotation: true,
|
||||
MaxReplicasAnnotation: true,
|
||||
apps.DeprecatedRollbackTo: true,
|
||||
}
|
||||
|
@ -325,9 +325,9 @@ func FindActiveOrLatest(newRS *apps.ReplicaSet, oldRSs []*apps.ReplicaSet) *apps
|
|||
}
|
||||
}
|
||||
|
||||
// GetDesiredReplicasAnnotation returns the number of desired replicas
|
||||
func GetDesiredReplicasAnnotation(rs *apps.ReplicaSet) (int32, bool) {
|
||||
return getIntFromAnnotation(rs, DesiredReplicasAnnotation)
|
||||
// GetReplicasAnnotation returns the number of desired replicas
|
||||
func GetReplicasAnnotation(rs *apps.ReplicaSet) (int32, bool) {
|
||||
return getIntFromAnnotation(rs, ReplicasAnnotation)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
desiredString := fmt.Sprintf("%d", desiredReplicas)
|
||||
if hasString := rs.Annotations[DesiredReplicasAnnotation]; hasString != desiredString {
|
||||
rs.Annotations[DesiredReplicasAnnotation] = desiredString
|
||||
if hasString := rs.Annotations[ReplicasAnnotation]; hasString != desiredString {
|
||||
rs.Annotations[ReplicasAnnotation] = desiredString
|
||||
updated = true
|
||||
}
|
||||
maxString := fmt.Sprintf("%d", maxReplicas)
|
||||
|
@ -372,7 +372,7 @@ func ReplicasAnnotationsNeedUpdate(rs *apps.ReplicaSet, desiredReplicas, maxRepl
|
|||
return true
|
||||
}
|
||||
desiredString := fmt.Sprintf("%d", desiredReplicas)
|
||||
if hasString := rs.Annotations[DesiredReplicasAnnotation]; hasString != desiredString {
|
||||
if hasString := rs.Annotations[ReplicasAnnotation]; hasString != desiredString {
|
||||
return true
|
||||
}
|
||||
maxString := fmt.Sprintf("%d", maxReplicas)
|
||||
|
@ -742,7 +742,7 @@ func IsSaturated(deployment *apps.Deployment, rs *apps.ReplicaSet) bool {
|
|||
if rs == nil {
|
||||
return false
|
||||
}
|
||||
desiredString := rs.Annotations[DesiredReplicasAnnotation]
|
||||
desiredString := rs.Annotations[ReplicasAnnotation]
|
||||
desired, err := strconv.Atoi(desiredString)
|
||||
if err != nil {
|
||||
return false
|
||||
|
|
|
@ -1104,15 +1104,15 @@ func TestAnnotationUtils(t *testing.T) {
|
|||
if !updated {
|
||||
t.Errorf("SetReplicasAnnotations() failed")
|
||||
}
|
||||
value, ok := tRS.Annotations[DesiredReplicasAnnotation]
|
||||
value, ok := tRS.Annotations[ReplicasAnnotation]
|
||||
if !ok {
|
||||
t.Errorf("SetReplicasAnnotations did not set DesiredReplicasAnnotation")
|
||||
t.Errorf("SetReplicasAnnotations did not set ReplicasAnnotation")
|
||||
}
|
||||
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 {
|
||||
t.Errorf("SetReplicasAnnotations did not set DesiredReplicasAnnotation")
|
||||
t.Errorf("SetReplicasAnnotations did not set ReplicasAnnotation")
|
||||
}
|
||||
if value != "11" {
|
||||
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
|
||||
tRS.Annotations[DesiredReplicasAnnotation] = "1"
|
||||
tRS.Annotations[ReplicasAnnotation] = "1"
|
||||
tRS.Status.AvailableReplicas = 1
|
||||
tRS.Spec.Replicas = new(int32)
|
||||
*tRS.Spec.Replicas = 1
|
||||
|
@ -1160,7 +1160,7 @@ func TestReplicasAnnotationsNeedUpdate(t *testing.T) {
|
|||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "hello",
|
||||
Namespace: "test",
|
||||
Annotations: map[string]string{DesiredReplicasAnnotation: "8", MaxReplicasAnnotation: maxReplicas},
|
||||
Annotations: map[string]string{ReplicasAnnotation: "8", MaxReplicasAnnotation: maxReplicas},
|
||||
},
|
||||
Spec: apps.ReplicaSetSpec{
|
||||
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
|
||||
|
@ -1174,7 +1174,7 @@ func TestReplicasAnnotationsNeedUpdate(t *testing.T) {
|
|||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "hello",
|
||||
Namespace: "test",
|
||||
Annotations: map[string]string{DesiredReplicasAnnotation: desiredReplicas, MaxReplicasAnnotation: "16"},
|
||||
Annotations: map[string]string{ReplicasAnnotation: desiredReplicas, MaxReplicasAnnotation: "16"},
|
||||
},
|
||||
Spec: apps.ReplicaSetSpec{
|
||||
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
|
||||
|
@ -1188,7 +1188,7 @@ func TestReplicasAnnotationsNeedUpdate(t *testing.T) {
|
|||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "hello",
|
||||
Namespace: "test",
|
||||
Annotations: map[string]string{DesiredReplicasAnnotation: desiredReplicas, MaxReplicasAnnotation: maxReplicas},
|
||||
Annotations: map[string]string{ReplicasAnnotation: desiredReplicas, MaxReplicasAnnotation: maxReplicas},
|
||||
},
|
||||
Spec: apps.ReplicaSetSpec{
|
||||
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
|
||||
|
|
|
@ -20,7 +20,6 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/openkruise/rollouts/api/v1alpha1"
|
||||
|
@ -32,7 +31,6 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/client-go/util/retry"
|
||||
"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,
|
||||
// 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]
|
||||
if currentStep.Weight == nil && len(currentStep.Matches) == 0 {
|
||||
if currentStep.Traffic == nil && len(currentStep.Matches) == 0 {
|
||||
tr := newTrafficRoutingContext(c)
|
||||
done, err := m.trafficRoutingManager.FinalisingTrafficRouting(tr, false)
|
||||
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)
|
||||
// If it is the last step, and 100% of pods, then return true
|
||||
if int32(steps) == canaryStatus.CurrentStepIndex {
|
||||
if currentStep.Weight != nil && *currentStep.Weight == 100 ||
|
||||
currentStep.Replicas != nil && currentStep.Replicas.StrVal == "100%" {
|
||||
if currentStep.Replicas != nil && currentStep.Replicas.StrVal == "100%" {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
@ -276,7 +273,7 @@ func (m *canaryReleaseManager) removeRolloutProgressingAnnotation(c *RolloutCont
|
|||
if _, ok := c.Workload.Annotations[util.InRolloutProgressingAnnotation]; !ok {
|
||||
return nil
|
||||
}
|
||||
workloadRef := c.Rollout.Spec.ObjectRef.WorkloadRef
|
||||
workloadRef := c.Rollout.Spec.WorkloadRef
|
||||
workloadGVK := schema.FromAPIVersionAndKind(workloadRef.APIVersion, workloadRef.Kind)
|
||||
obj := util.GetEmptyWorkloadObject(workloadGVK)
|
||||
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)
|
||||
return true, br, nil
|
||||
}
|
||||
|
||||
// update batchRelease to the latest version
|
||||
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 {
|
||||
|
@ -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 {
|
||||
var batches []v1beta1.ReleaseBatch
|
||||
for _, step := range rollout.Spec.Strategy.Canary.Steps {
|
||||
if step.Replicas == nil {
|
||||
batches = append(batches, v1beta1.ReleaseBatch{CanaryReplicas: intstr.FromString(strconv.Itoa(int(*step.Weight)) + "%")})
|
||||
} else {
|
||||
batches = append(batches, v1beta1.ReleaseBatch{CanaryReplicas: *step.Replicas})
|
||||
}
|
||||
batches = append(batches, v1beta1.ReleaseBatch{CanaryReplicas: *step.Replicas})
|
||||
}
|
||||
br := &v1beta1.BatchRelease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
@ -354,19 +346,18 @@ func createBatchRelease(rollout *v1beta1.Rollout, rolloutID string, batch int32,
|
|||
OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(rollout, rolloutControllerKind)},
|
||||
},
|
||||
Spec: v1beta1.BatchReleaseSpec{
|
||||
TargetRef: v1beta1.ObjectRef{
|
||||
WorkloadRef: &v1beta1.WorkloadRef{
|
||||
APIVersion: rollout.Spec.ObjectRef.WorkloadRef.APIVersion,
|
||||
Kind: rollout.Spec.ObjectRef.WorkloadRef.Kind,
|
||||
Name: rollout.Spec.ObjectRef.WorkloadRef.Name,
|
||||
},
|
||||
WorkloadRef: v1beta1.ObjectRef{
|
||||
APIVersion: rollout.Spec.WorkloadRef.APIVersion,
|
||||
Kind: rollout.Spec.WorkloadRef.Kind,
|
||||
Name: rollout.Spec.WorkloadRef.Name,
|
||||
},
|
||||
ReleasePlan: v1beta1.ReleasePlan{
|
||||
Batches: batches,
|
||||
RolloutID: rolloutID,
|
||||
BatchPartition: utilpointer.Int32Ptr(batch),
|
||||
FailureThreshold: rollout.Spec.Strategy.Canary.FailureThreshold,
|
||||
PatchPodTemplateMetadata: rollout.Spec.Strategy.Canary.PatchPodTemplateMetadata,
|
||||
Batches: batches,
|
||||
RolloutID: rolloutID,
|
||||
BatchPartition: utilpointer.Int32Ptr(batch),
|
||||
FailureThreshold: rollout.Spec.Strategy.Canary.FailureThreshold,
|
||||
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 {
|
||||
annotations[v1alpha1.RollbackInBatchAnnotation] = rollout.Annotations[v1alpha1.RollbackInBatchAnnotation]
|
||||
}
|
||||
if style, ok := rollout.Annotations[v1beta1.RolloutStyleAnnotation]; ok {
|
||||
annotations[v1beta1.RolloutStyleAnnotation] = style
|
||||
}
|
||||
if len(annotations) > 0 {
|
||||
br.Annotations = annotations
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ func TestRunCanary(t *testing.T) {
|
|||
},
|
||||
getRollout: func() (*v1beta1.Rollout, *v1beta1.BatchRelease) {
|
||||
obj := rolloutDemo.DeepCopy()
|
||||
obj.Spec.Strategy.Canary.EnableExtraWorkloadForCanary = true
|
||||
obj.Status.CanaryStatus.ObservedWorkloadGeneration = 2
|
||||
obj.Status.CanaryStatus.RolloutHash = "f55bvd874d5f2fzvw46bv966x4bwbdv4wx6bd9f7b46ww788954b8z8w29b7wxfd"
|
||||
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.EnableExtraWorkloadForCanary = true
|
||||
return br
|
||||
},
|
||||
},
|
||||
|
@ -130,6 +132,7 @@ func TestRunCanary(t *testing.T) {
|
|||
},
|
||||
getRollout: func() (*v1beta1.Rollout, *v1beta1.BatchRelease) {
|
||||
obj := rolloutDemo.DeepCopy()
|
||||
obj.Spec.Strategy.Canary.EnableExtraWorkloadForCanary = true
|
||||
obj.Status.CanaryStatus.ObservedWorkloadGeneration = 2
|
||||
obj.Status.CanaryStatus.RolloutHash = "f55bvd874d5f2fzvw46bv966x4bwbdv4wx6bd9f7b46ww788954b8z8w29b7wxfd"
|
||||
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.EnableExtraWorkloadForCanary = true
|
||||
br.Status = v1beta1.BatchReleaseStatus{
|
||||
ObservedGeneration: 1,
|
||||
ObservedReleasePlanHash: "6d6a40791161e88ec0483688e951b589a4cbd0bf351974827706b79f99378fd5",
|
||||
ObservedReleasePlanHash: "d444a1007776da957d7d8549e3375c96179621b85670ad1e2bb0fc5fea16446a",
|
||||
CanaryStatus: v1beta1.BatchReleaseCanaryStatus{
|
||||
CurrentBatchState: v1beta1.ReadyBatchState,
|
||||
CurrentBatch: 0,
|
||||
|
@ -200,6 +204,7 @@ func TestRunCanary(t *testing.T) {
|
|||
},
|
||||
}
|
||||
br.Spec.ReleasePlan.BatchPartition = utilpointer.Int32(0)
|
||||
br.Spec.ReleasePlan.EnableExtraWorkloadForCanary = true
|
||||
return br
|
||||
},
|
||||
},
|
||||
|
|
|
@ -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))
|
||||
|
||||
// If workload watcher does not exist, then add the watcher dynamically
|
||||
workloadRef := rollout.Spec.ObjectRef.WorkloadRef
|
||||
workloadGVK := util.GetGVKFrom(workloadRef)
|
||||
workloadRef := rollout.Spec.WorkloadRef
|
||||
workloadGVK := util.GetGVKFrom(&workloadRef)
|
||||
_, exists := watchedWorkload.Load(workloadGVK.String())
|
||||
if workloadRef != nil && !exists {
|
||||
if !exists {
|
||||
succeeded, err := util.AddWatcherDynamically(runtimeController, workloadHandler, workloadGVK)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
|
|
|
@ -48,45 +48,44 @@ var (
|
|||
},
|
||||
},
|
||||
Spec: v1beta1.RolloutSpec{
|
||||
ObjectRef: v1beta1.ObjectRef{
|
||||
WorkloadRef: &v1beta1.WorkloadRef{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "echoserver",
|
||||
},
|
||||
WorkloadRef: v1beta1.ObjectRef{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "echoserver",
|
||||
},
|
||||
Strategy: v1beta1.RolloutStrategy{
|
||||
Canary: &v1beta1.CanaryStrategy{
|
||||
EnableExtraWorkloadForCanary: true,
|
||||
Steps: []v1beta1.CanaryStep{
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(5),
|
||||
TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
|
||||
Traffic: utilpointer.String("5%"),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{IntVal: 1},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(20),
|
||||
TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
|
||||
Traffic: utilpointer.String("20%"),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{IntVal: 2},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(60),
|
||||
TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
|
||||
Traffic: utilpointer.String("60%"),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{IntVal: 6},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(100),
|
||||
TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
|
||||
Traffic: utilpointer.String("100%"),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{IntVal: 10},
|
||||
},
|
||||
},
|
||||
TrafficRoutings: []v1alpha1.TrafficRoutingRef{
|
||||
TrafficRoutings: []v1beta1.TrafficRoutingRef{
|
||||
{
|
||||
Service: "echoserver",
|
||||
Ingress: &v1alpha1.IngressTrafficRouting{
|
||||
Ingress: &v1beta1.IngressTrafficRouting{
|
||||
Name: "echoserver",
|
||||
},
|
||||
},
|
||||
|
@ -207,12 +206,10 @@ var (
|
|||
Generation: 1,
|
||||
},
|
||||
Spec: v1beta1.BatchReleaseSpec{
|
||||
TargetRef: v1beta1.ObjectRef{
|
||||
WorkloadRef: &v1beta1.WorkloadRef{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "echoserver",
|
||||
},
|
||||
WorkloadRef: v1beta1.ObjectRef{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "echoserver",
|
||||
},
|
||||
},
|
||||
Status: v1beta1.BatchReleaseStatus{},
|
||||
|
|
|
@ -85,7 +85,7 @@ func (w *enqueueRequestForWorkload) getRolloutForWorkload(key types.NamespacedNa
|
|||
}
|
||||
|
||||
for _, rollout := range rList.Items {
|
||||
targetRef := rollout.Spec.ObjectRef.WorkloadRef
|
||||
targetRef := rollout.Spec.WorkloadRef
|
||||
targetGV, err := schema.ParseGroupVersion(targetRef.APIVersion)
|
||||
if err != nil {
|
||||
klog.Errorf("failed to parse rollout(%s/%s) targetRef's group version: %s", rollout.Namespace, rollout.Name, targetRef.APIVersion)
|
||||
|
|
|
@ -19,7 +19,6 @@ package rollout
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"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 {
|
||||
step := c.Rollout.Spec.Strategy.Canary.Steps[i]
|
||||
var desiredReplicas int
|
||||
if step.Replicas != nil {
|
||||
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)
|
||||
}
|
||||
desiredReplicas, _ = intstr.GetScaledValueFromIntOrPercent(step.Replicas, int(c.Workload.Replicas), true)
|
||||
stepIndex = int32(i + 1)
|
||||
if currentReplicas <= desiredReplicas {
|
||||
break
|
||||
|
|
|
@ -660,19 +660,13 @@ func TestReCalculateCanaryStepIndex(t *testing.T) {
|
|||
obj := rolloutDemo.DeepCopy()
|
||||
obj.Spec.Strategy.Canary.Steps = []v1beta1.CanaryStep{
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(20),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "20%"},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(50),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "50%"},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(100),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "100%"},
|
||||
},
|
||||
}
|
||||
return obj
|
||||
|
@ -705,19 +699,13 @@ func TestReCalculateCanaryStepIndex(t *testing.T) {
|
|||
obj := rolloutDemo.DeepCopy()
|
||||
obj.Spec.Strategy.Canary.Steps = []v1beta1.CanaryStep{
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(20),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "20%"},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(40),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "40%"},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(100),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "100%"},
|
||||
},
|
||||
}
|
||||
return obj
|
||||
|
@ -750,19 +738,13 @@ func TestReCalculateCanaryStepIndex(t *testing.T) {
|
|||
obj := rolloutDemo.DeepCopy()
|
||||
obj.Spec.Strategy.Canary.Steps = []v1beta1.CanaryStep{
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(40),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "40%"},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(60),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "60%"},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(100),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "100%"},
|
||||
},
|
||||
}
|
||||
return obj
|
||||
|
@ -795,19 +777,13 @@ func TestReCalculateCanaryStepIndex(t *testing.T) {
|
|||
obj := rolloutDemo.DeepCopy()
|
||||
obj.Spec.Strategy.Canary.Steps = []v1beta1.CanaryStep{
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(10),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "10%"},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(30),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "30%"},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(100),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{Type: intstr.String, StrVal: "100%"},
|
||||
},
|
||||
}
|
||||
return obj
|
||||
|
@ -840,8 +816,8 @@ func TestReCalculateCanaryStepIndex(t *testing.T) {
|
|||
obj := rolloutDemo.DeepCopy()
|
||||
obj.Spec.Strategy.Canary.Steps = []v1beta1.CanaryStep{
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(2),
|
||||
TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
|
||||
Traffic: utilpointer.String("2%"),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{
|
||||
Type: intstr.String,
|
||||
|
@ -849,8 +825,8 @@ func TestReCalculateCanaryStepIndex(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(3),
|
||||
TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
|
||||
Traffic: utilpointer.String("3%"),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{
|
||||
Type: intstr.String,
|
||||
|
|
|
@ -20,7 +20,6 @@ import (
|
|||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/openkruise/rollouts/api/v1alpha1"
|
||||
"github.com/openkruise/rollouts/api/v1beta1"
|
||||
"github.com/openkruise/rollouts/pkg/trafficrouting"
|
||||
"github.com/openkruise/rollouts/pkg/util"
|
||||
|
@ -43,7 +42,7 @@ func TestCalculateRolloutHash(t *testing.T) {
|
|||
return obj
|
||||
},
|
||||
expectHash: func() string {
|
||||
return "626fd556c5d5v2d9b4f7c2xvbc9dxddxzd48xvb9w9wfcdvdz6v959fbzd84b57x"
|
||||
return "75746v7d5z9x59v5c7dff4wd9cv9cc28czf6c2z664w7zbb7vw2bzv76v99z6bd9"
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -56,7 +55,7 @@ func TestCalculateRolloutHash(t *testing.T) {
|
|||
return obj
|
||||
},
|
||||
expectHash: func() string {
|
||||
return "626fd556c5d5v2d9b4f7c2xvbc9dxddxzd48xvb9w9wfcdvdz6v959fbzd84b57x"
|
||||
return "75746v7d5z9x59v5c7dff4wd9cv9cc28czf6c2z664w7zbb7vw2bzv76v99z6bd9"
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -65,20 +64,20 @@ func TestCalculateRolloutHash(t *testing.T) {
|
|||
obj := rolloutDemo.DeepCopy()
|
||||
obj.Spec.Strategy.Canary.Steps = []v1beta1.CanaryStep{
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(50),
|
||||
TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
|
||||
Traffic: utilpointer.String("50%"),
|
||||
},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(100),
|
||||
TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
|
||||
Traffic: utilpointer.String("100%"),
|
||||
},
|
||||
},
|
||||
}
|
||||
return obj
|
||||
},
|
||||
expectHash: func() string {
|
||||
return "8c449wxc46x8dd764x4v4wzvc7454f48478vd9db27fv8v9dw5cwbcb6b42b75dc"
|
||||
return "db9c2x47d282c84z6684d598bzwf9b4x6ffb45fc456xdfv97945v2vb79w72c7z"
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -215,12 +215,15 @@ func (r *TrafficRoutingReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
|||
}
|
||||
|
||||
func newTrafficRoutingContext(tr *v1alpha1.TrafficRouting) *trafficrouting.TrafficRoutingContext {
|
||||
return &trafficrouting.TrafficRoutingContext{
|
||||
c := &trafficrouting.TrafficRoutingContext{
|
||||
Key: fmt.Sprintf("TrafficRouting(%s/%s)", tr.Namespace, tr.Name),
|
||||
Namespace: tr.Namespace,
|
||||
ObjectRef: tr.Spec.ObjectRef,
|
||||
Strategy: tr.Spec.Strategy,
|
||||
Strategy: v1alpha1.ConversionToV1beta1TrafficRoutingStrategy(tr.Spec.Strategy),
|
||||
OwnerRef: *metav1.NewControllerRef(tr, trControllerKind),
|
||||
OnlyTrafficRouting: true,
|
||||
}
|
||||
for _, ref := range tr.Spec.ObjectRef {
|
||||
c.ObjectRef = append(c.ObjectRef, v1alpha1.ConversionToV1beta1TrafficRoutingRef(ref))
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/openkruise/rollouts/api/v1alpha1"
|
||||
"github.com/openkruise/rollouts/api/v1beta1"
|
||||
"github.com/openkruise/rollouts/pkg/trafficrouting/network"
|
||||
custom "github.com/openkruise/rollouts/pkg/trafficrouting/network/customNetworkProvider"
|
||||
"github.com/openkruise/rollouts/pkg/trafficrouting/network/gateway"
|
||||
|
@ -44,8 +44,8 @@ type TrafficRoutingContext struct {
|
|||
// only for log info
|
||||
Key string
|
||||
Namespace string
|
||||
ObjectRef []v1alpha1.TrafficRoutingRef
|
||||
Strategy v1alpha1.TrafficRoutingStrategy
|
||||
ObjectRef []v1beta1.TrafficRoutingRef
|
||||
Strategy v1beta1.TrafficRoutingStrategy
|
||||
// OnlyTrafficRouting
|
||||
OnlyTrafficRouting bool
|
||||
OwnerRef metav1.OwnerReference
|
||||
|
@ -100,7 +100,7 @@ func (m *Manager) DoTrafficRouting(c *TrafficRoutingContext) (bool, error) {
|
|||
if trafficRouting.GracePeriodSeconds <= 0 {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -228,7 +228,7 @@ func (m *Manager) FinalisingTrafficRouting(c *TrafficRoutingContext, onlyRestore
|
|||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
|
|
@ -113,45 +113,43 @@ var (
|
|||
},
|
||||
},
|
||||
Spec: v1beta1.RolloutSpec{
|
||||
ObjectRef: v1beta1.ObjectRef{
|
||||
WorkloadRef: &v1beta1.WorkloadRef{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "echoserver",
|
||||
},
|
||||
WorkloadRef: v1beta1.ObjectRef{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "echoserver",
|
||||
},
|
||||
Strategy: v1beta1.RolloutStrategy{
|
||||
Canary: &v1beta1.CanaryStrategy{
|
||||
Steps: []v1beta1.CanaryStep{
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(5),
|
||||
TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
|
||||
Traffic: utilpointer.String("5%"),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{IntVal: 1},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(20),
|
||||
TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
|
||||
Traffic: utilpointer.String("20%"),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{IntVal: 2},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(60),
|
||||
TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
|
||||
Traffic: utilpointer.String("60%"),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{IntVal: 6},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: v1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(100),
|
||||
TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
|
||||
Traffic: utilpointer.String("100%"),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{IntVal: 10},
|
||||
},
|
||||
},
|
||||
TrafficRoutings: []v1alpha1.TrafficRoutingRef{
|
||||
TrafficRoutings: []v1beta1.TrafficRoutingRef{
|
||||
{
|
||||
Service: "echoserver",
|
||||
Ingress: &v1alpha1.IngressTrafficRouting{
|
||||
Ingress: &v1beta1.IngressTrafficRouting{
|
||||
Name: "echoserver",
|
||||
},
|
||||
},
|
||||
|
|
|
@ -23,20 +23,20 @@ import (
|
|||
"reflect"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
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/util"
|
||||
"github.com/openkruise/rollouts/pkg/util/configuration"
|
||||
"github.com/openkruise/rollouts/pkg/util/luamanager"
|
||||
lua "github.com/yuin/gopher-lua"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/klog/v2"
|
||||
utilpointer "k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
@ -50,7 +50,7 @@ type LuaData struct {
|
|||
Data Data
|
||||
CanaryWeight int32
|
||||
StableWeight int32
|
||||
Matches []rolloutv1alpha1.HttpRouteMatch
|
||||
Matches []v1beta1.HttpRouteMatch
|
||||
CanaryService string
|
||||
StableService string
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ type Config struct {
|
|||
CanaryService string
|
||||
StableService string
|
||||
// network providers need to be created
|
||||
TrafficConf []rolloutv1alpha1.CustomNetworkRef
|
||||
TrafficConf []v1beta1.ObjectRef
|
||||
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
|
||||
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
|
||||
// *strategy.Weight == 0 indicates traffic routing is doing finalising and tries to route whole traffic to stable service
|
||||
// then directly do finalising
|
||||
if strategy.Weight != nil && *strategy.Weight == 0 {
|
||||
if strategy.Traffic != nil && *strategy.Traffic == "0%" {
|
||||
return true, nil
|
||||
}
|
||||
var err error
|
||||
|
@ -254,8 +254,13 @@ func (r *customController) restoreObject(obj *unstructured.Unstructured) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *customController) executeLuaForCanary(spec Data, strategy *rolloutv1alpha1.TrafficRoutingStrategy, luaScript string) (Data, error) {
|
||||
weight := strategy.Weight
|
||||
func (r *customController) executeLuaForCanary(spec Data, strategy *v1beta1.TrafficRoutingStrategy, luaScript string) (Data, error) {
|
||||
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
|
||||
if weight == nil {
|
||||
// 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())
|
||||
}
|
||||
|
||||
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
|
||||
// luaScript.Provider: CRDGroupt/Kind
|
||||
group := strings.Split(ref.APIVersion, "/")[0]
|
||||
|
|
|
@ -28,7 +28,7 @@ import (
|
|||
|
||||
rolloutapi "github.com/openkruise/rollouts/api"
|
||||
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/configuration"
|
||||
"github.com/openkruise/rollouts/pkg/util/luamanager"
|
||||
|
@ -38,6 +38,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/klog/v2"
|
||||
utilpointer "k8s.io/utils/pointer"
|
||||
|
@ -141,7 +142,7 @@ func TestInitialize(t *testing.T) {
|
|||
return Config{
|
||||
StableService: "echoserver",
|
||||
CanaryService: "echoserver-canary",
|
||||
TrafficConf: []rolloutsv1alpha1.CustomNetworkRef{
|
||||
TrafficConf: []v1beta1.ObjectRef{
|
||||
{
|
||||
APIVersion: "networking.istio.io/v1alpha3",
|
||||
Kind: "VirtualService",
|
||||
|
@ -174,7 +175,7 @@ func TestInitialize(t *testing.T) {
|
|||
return Config{
|
||||
StableService: "echoserver",
|
||||
CanaryService: "echoserver-canary",
|
||||
TrafficConf: []rolloutsv1alpha1.CustomNetworkRef{
|
||||
TrafficConf: []v1beta1.ObjectRef{
|
||||
{
|
||||
APIVersion: "networking.test.io/v1alpha3",
|
||||
Kind: "VirtualService",
|
||||
|
@ -237,7 +238,7 @@ func TestEnsureRoutes(t *testing.T) {
|
|||
cases := []struct {
|
||||
name string
|
||||
getLua func() map[string]string
|
||||
getRoutes func() *rolloutsv1alpha1.TrafficRoutingStrategy
|
||||
getRoutes func() *v1beta1.TrafficRoutingStrategy
|
||||
getUnstructureds func() []*unstructured.Unstructured
|
||||
getConfig func() Config
|
||||
expectState func() (bool, bool)
|
||||
|
@ -245,9 +246,9 @@ func TestEnsureRoutes(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
name: "test1, do traffic routing for VirtualService and DestinationRule",
|
||||
getRoutes: func() *rolloutsv1alpha1.TrafficRoutingStrategy {
|
||||
return &rolloutsv1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(5),
|
||||
getRoutes: func() *v1beta1.TrafficRoutingStrategy {
|
||||
return &v1beta1.TrafficRoutingStrategy{
|
||||
Traffic: utilpointer.String("5%"),
|
||||
}
|
||||
},
|
||||
getUnstructureds: func() []*unstructured.Unstructured {
|
||||
|
@ -268,7 +269,7 @@ func TestEnsureRoutes(t *testing.T) {
|
|||
Key: "rollout-demo",
|
||||
StableService: "echoserver",
|
||||
CanaryService: "echoserver-canary",
|
||||
TrafficConf: []rolloutsv1alpha1.CustomNetworkRef{
|
||||
TrafficConf: []v1beta1.ObjectRef{
|
||||
{
|
||||
APIVersion: "networking.istio.io/v1alpha3",
|
||||
Kind: "VirtualService",
|
||||
|
@ -317,9 +318,9 @@ func TestEnsureRoutes(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "test2, do traffic routing but failed to execute lua",
|
||||
getRoutes: func() *rolloutsv1alpha1.TrafficRoutingStrategy {
|
||||
return &rolloutsv1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(5),
|
||||
getRoutes: func() *v1beta1.TrafficRoutingStrategy {
|
||||
return &v1beta1.TrafficRoutingStrategy{
|
||||
Traffic: utilpointer.String("5%"),
|
||||
}
|
||||
},
|
||||
getUnstructureds: func() []*unstructured.Unstructured {
|
||||
|
@ -340,7 +341,7 @@ func TestEnsureRoutes(t *testing.T) {
|
|||
Key: "rollout-demo",
|
||||
StableService: "echoserver",
|
||||
CanaryService: "echoserver-canary",
|
||||
TrafficConf: []rolloutsv1alpha1.CustomNetworkRef{
|
||||
TrafficConf: []v1beta1.ObjectRef{
|
||||
{
|
||||
APIVersion: "networking.istio.io/v1alpha3",
|
||||
Kind: "VirtualService",
|
||||
|
@ -446,7 +447,7 @@ func TestFinalise(t *testing.T) {
|
|||
return Config{
|
||||
StableService: "echoserver",
|
||||
CanaryService: "echoserver-canary",
|
||||
TrafficConf: []rolloutsv1alpha1.CustomNetworkRef{
|
||||
TrafficConf: []v1beta1.ObjectRef{
|
||||
{
|
||||
APIVersion: "networking.istio.io/v1alpha3",
|
||||
Kind: "VirtualService",
|
||||
|
@ -482,7 +483,7 @@ func TestFinalise(t *testing.T) {
|
|||
}
|
||||
|
||||
type TestCase struct {
|
||||
Rollout *rolloutsv1beta1.Rollout `json:"rollout,omitempty"`
|
||||
Rollout *v1beta1.Rollout `json:"rollout,omitempty"`
|
||||
TrafficRouting *rolloutsv1alpha1.TrafficRouting `json:"trafficRouting,omitempty"`
|
||||
Original *unstructured.Unstructured `json:"original,omitempty"`
|
||||
Expected []*unstructured.Unstructured `json:"expected,omitempty"`
|
||||
|
@ -522,8 +523,12 @@ func TestLuaScript(t *testing.T) {
|
|||
if rollout != nil {
|
||||
steps := rollout.Spec.Strategy.Canary.Steps
|
||||
for i, step := range steps {
|
||||
weight := step.TrafficRoutingStrategy.Weight
|
||||
if weight == nil {
|
||||
var weight *int32
|
||||
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)
|
||||
}
|
||||
var canaryService string
|
||||
|
@ -563,13 +568,19 @@ func TestLuaScript(t *testing.T) {
|
|||
var canaryService string
|
||||
stableService := trafficRouting.Spec.ObjectRef[0].Service
|
||||
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: Data{
|
||||
Labels: testCase.Original.GetLabels(),
|
||||
Annotations: testCase.Original.GetAnnotations(),
|
||||
Spec: testCase.Original.Object["spec"],
|
||||
},
|
||||
Matches: trafficRouting.Spec.Strategy.Matches,
|
||||
Matches: matches,
|
||||
CanaryWeight: *weight,
|
||||
StableWeight: 100 - *weight,
|
||||
CanaryService: canaryService,
|
||||
|
@ -615,6 +626,7 @@ func getLuaTestCase(t *testing.T, path string) (*TestCase, error) {
|
|||
t.Errorf("failed to read file %s", path)
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println(string(yamlFile))
|
||||
luaTestCase := &TestCase{}
|
||||
err = yaml.Unmarshal(yamlFile, luaTestCase)
|
||||
if err != nil {
|
||||
|
|
|
@ -17,10 +17,11 @@ import (
|
|||
"context"
|
||||
"reflect"
|
||||
|
||||
rolloutv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
|
||||
"github.com/openkruise/rollouts/api/v1beta1"
|
||||
"github.com/openkruise/rollouts/pkg/trafficrouting/network"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/client-go/util/retry"
|
||||
"k8s.io/klog/v2"
|
||||
utilpointer "k8s.io/utils/pointer"
|
||||
|
@ -34,7 +35,7 @@ type Config struct {
|
|||
Namespace string
|
||||
CanaryService string
|
||||
StableService string
|
||||
TrafficConf *rolloutv1alpha1.GatewayTrafficRouting
|
||||
TrafficConf *v1beta1.GatewayTrafficRouting
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (r *gatewayController) EnsureRoutes(ctx context.Context, strategy *rolloutv1alpha1.TrafficRoutingStrategy) (bool, error) {
|
||||
weight := strategy.Weight
|
||||
func (r *gatewayController) EnsureRoutes(ctx context.Context, strategy *v1beta1.TrafficRoutingStrategy) (bool, error) {
|
||||
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
|
||||
// headerModifier := strategy.RequestHeaderModifier
|
||||
var httpRoute gatewayv1alpha2.HTTPRoute
|
||||
|
@ -118,7 +124,7 @@ func (r *gatewayController) Finalise(ctx context.Context) error {
|
|||
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 {
|
||||
var desired []gatewayv1alpha2.HTTPRouteRule
|
||||
// Only when finalize method parameter weight=-1,
|
||||
|
@ -146,7 +152,7 @@ func (r *gatewayController) buildDesiredHTTPRoute(rules []gatewayv1alpha2.HTTPRo
|
|||
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 canarys []gatewayv1alpha2.HTTPRouteRule
|
||||
for i := range rules {
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
rolloutsv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
|
||||
"github.com/openkruise/rollouts/api/v1beta1"
|
||||
"github.com/openkruise/rollouts/pkg/util"
|
||||
utilpointer "k8s.io/utils/pointer"
|
||||
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
|
||||
|
@ -130,7 +130,7 @@ func TestBuildDesiredHTTPRoute(t *testing.T) {
|
|||
cases := []struct {
|
||||
name string
|
||||
getRouteRules func() []gatewayv1alpha2.HTTPRouteRule
|
||||
getRoutes func() (*int32, []rolloutsv1alpha1.HttpRouteMatch)
|
||||
getRoutes func() (*int32, []v1beta1.HttpRouteMatch)
|
||||
desiredRules func() []gatewayv1alpha2.HTTPRouteRule
|
||||
}{
|
||||
{
|
||||
|
@ -139,9 +139,9 @@ func TestBuildDesiredHTTPRoute(t *testing.T) {
|
|||
rules := routeDemo.DeepCopy().Spec.Rules
|
||||
return rules
|
||||
},
|
||||
getRoutes: func() (*int32, []rolloutsv1alpha1.HttpRouteMatch) {
|
||||
getRoutes: func() (*int32, []v1beta1.HttpRouteMatch) {
|
||||
iType := gatewayv1alpha2.HeaderMatchRegularExpression
|
||||
return nil, []rolloutsv1alpha1.HttpRouteMatch{
|
||||
return nil, []v1beta1.HttpRouteMatch{
|
||||
// header
|
||||
{
|
||||
Headers: []gatewayv1alpha2.HTTPHeaderMatch{
|
||||
|
@ -360,7 +360,7 @@ func TestBuildDesiredHTTPRoute(t *testing.T) {
|
|||
}
|
||||
return rules
|
||||
},
|
||||
getRoutes: func() (*int32, []rolloutsv1alpha1.HttpRouteMatch) {
|
||||
getRoutes: func() (*int32, []v1beta1.HttpRouteMatch) {
|
||||
return utilpointer.Int32(20), nil
|
||||
},
|
||||
desiredRules: func() []gatewayv1alpha2.HTTPRouteRule {
|
||||
|
@ -494,7 +494,7 @@ func TestBuildDesiredHTTPRoute(t *testing.T) {
|
|||
})
|
||||
return rules
|
||||
},
|
||||
getRoutes: func() (*int32, []rolloutsv1alpha1.HttpRouteMatch) {
|
||||
getRoutes: func() (*int32, []v1beta1.HttpRouteMatch) {
|
||||
return utilpointer.Int32(-1), nil
|
||||
},
|
||||
desiredRules: func() []gatewayv1alpha2.HTTPRouteRule {
|
||||
|
|
|
@ -23,7 +23,7 @@ import (
|
|||
"reflect"
|
||||
|
||||
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/util"
|
||||
"github.com/openkruise/rollouts/pkg/util/configuration"
|
||||
|
@ -35,6 +35,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/klog/v2"
|
||||
utilpointer "k8s.io/utils/pointer"
|
||||
|
@ -56,7 +57,7 @@ type Config struct {
|
|||
Namespace string
|
||||
CanaryService string
|
||||
StableService string
|
||||
TrafficConf *rolloutv1alpha1.IngressTrafficRouting
|
||||
TrafficConf *v1beta1.IngressTrafficRouting
|
||||
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)
|
||||
}
|
||||
|
||||
func (r *ingressController) EnsureRoutes(ctx context.Context, strategy *rolloutv1alpha1.TrafficRoutingStrategy) (bool, error) {
|
||||
weight := strategy.Weight
|
||||
func (r *ingressController) EnsureRoutes(ctx context.Context, strategy *v1beta1.TrafficRoutingStrategy) (bool, error) {
|
||||
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
|
||||
headerModifier := strategy.RequestHeaderModifier
|
||||
|
||||
|
@ -217,7 +223,7 @@ func defaultCanaryIngressName(name string) string {
|
|||
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) {
|
||||
|
||||
if weight == nil {
|
||||
|
@ -228,7 +234,7 @@ func (r *ingressController) executeLuaForCanary(annotations map[string]string, w
|
|||
type LuaData struct {
|
||||
Annotations map[string]string
|
||||
Weight string
|
||||
Matches []rolloutv1alpha1.HttpRouteMatch
|
||||
Matches []v1beta1.HttpRouteMatch
|
||||
CanaryService string
|
||||
RequestHeaderModifier *gatewayv1alpha2.HTTPRequestHeaderFilter
|
||||
}
|
||||
|
|
|
@ -20,8 +20,7 @@ import (
|
|||
"testing"
|
||||
|
||||
rolloutsapi "github.com/openkruise/rollouts/api"
|
||||
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/configuration"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
@ -279,7 +278,7 @@ func TestInitialize(t *testing.T) {
|
|||
Key: "rollout-demo",
|
||||
StableService: "echoserver",
|
||||
CanaryService: "echoserver-canary",
|
||||
TrafficConf: &rolloutsv1alpha1.IngressTrafficRouting{
|
||||
TrafficConf: &v1beta1.IngressTrafficRouting{
|
||||
Name: "echoserver",
|
||||
},
|
||||
}
|
||||
|
@ -309,7 +308,7 @@ func TestEnsureRoutes(t *testing.T) {
|
|||
name string
|
||||
getConfigmap func() *corev1.ConfigMap
|
||||
getIngress func() []*netv1.Ingress
|
||||
getRoutes func() *rolloutsv1beta1.CanaryStep
|
||||
getRoutes func() *v1beta1.CanaryStep
|
||||
expectIngress func() *netv1.Ingress
|
||||
ingressType string
|
||||
}{
|
||||
|
@ -329,11 +328,11 @@ func TestEnsureRoutes(t *testing.T) {
|
|||
canary.Spec.Rules[1].HTTP.Paths[0].Backend.Service.Name = "echoserver-canary"
|
||||
return []*netv1.Ingress{demoIngress.DeepCopy(), canary}
|
||||
},
|
||||
getRoutes: func() *rolloutsv1beta1.CanaryStep {
|
||||
return &rolloutsv1beta1.CanaryStep{
|
||||
TrafficRoutingStrategy: rolloutsv1alpha1.TrafficRoutingStrategy{
|
||||
Weight: nil,
|
||||
Matches: []rolloutsv1alpha1.HttpRouteMatch{
|
||||
getRoutes: func() *v1beta1.CanaryStep {
|
||||
return &v1beta1.CanaryStep{
|
||||
TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
|
||||
Traffic: nil,
|
||||
Matches: []v1beta1.HttpRouteMatch{
|
||||
// header
|
||||
{
|
||||
Headers: []gatewayv1alpha2.HTTPHeaderMatch{
|
||||
|
@ -400,10 +399,10 @@ func TestEnsureRoutes(t *testing.T) {
|
|||
canary.Spec.Rules[1].HTTP.Paths[0].Backend.Service.Name = "echoserver-canary"
|
||||
return []*netv1.Ingress{demoIngress.DeepCopy(), canary}
|
||||
},
|
||||
getRoutes: func() *rolloutsv1beta1.CanaryStep {
|
||||
return &rolloutsv1beta1.CanaryStep{
|
||||
TrafficRoutingStrategy: rolloutsv1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(40),
|
||||
getRoutes: func() *v1beta1.CanaryStep {
|
||||
return &v1beta1.CanaryStep{
|
||||
TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
|
||||
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"
|
||||
return []*netv1.Ingress{demoIngress.DeepCopy(), canary}
|
||||
},
|
||||
getRoutes: func() *rolloutsv1beta1.CanaryStep {
|
||||
getRoutes: func() *v1beta1.CanaryStep {
|
||||
iType := gatewayv1alpha2.HeaderMatchRegularExpression
|
||||
return &rolloutsv1beta1.CanaryStep{
|
||||
TrafficRoutingStrategy: rolloutsv1alpha1.TrafficRoutingStrategy{
|
||||
Matches: []rolloutsv1alpha1.HttpRouteMatch{
|
||||
return &v1beta1.CanaryStep{
|
||||
TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
|
||||
Matches: []v1beta1.HttpRouteMatch{
|
||||
// header
|
||||
{
|
||||
Headers: []gatewayv1alpha2.HTTPHeaderMatch{
|
||||
|
@ -483,10 +482,10 @@ func TestEnsureRoutes(t *testing.T) {
|
|||
canary.Spec.Rules[1].HTTP.Paths[0].Backend.Service.Name = "echoserver-canary"
|
||||
return []*netv1.Ingress{demoIngress.DeepCopy(), canary}
|
||||
},
|
||||
getRoutes: func() *rolloutsv1beta1.CanaryStep {
|
||||
return &rolloutsv1beta1.CanaryStep{
|
||||
TrafficRoutingStrategy: rolloutsv1alpha1.TrafficRoutingStrategy{
|
||||
Matches: []rolloutsv1alpha1.HttpRouteMatch{
|
||||
getRoutes: func() *v1beta1.CanaryStep {
|
||||
return &v1beta1.CanaryStep{
|
||||
TrafficRoutingStrategy: v1beta1.TrafficRoutingStrategy{
|
||||
Matches: []v1beta1.HttpRouteMatch{
|
||||
// header
|
||||
{
|
||||
Headers: []gatewayv1alpha2.HTTPHeaderMatch{
|
||||
|
@ -526,7 +525,7 @@ func TestEnsureRoutes(t *testing.T) {
|
|||
Key: "rollout-demo",
|
||||
StableService: "echoserver",
|
||||
CanaryService: "echoserver-canary",
|
||||
TrafficConf: &rolloutsv1alpha1.IngressTrafficRouting{
|
||||
TrafficConf: &v1beta1.IngressTrafficRouting{
|
||||
Name: "echoserver",
|
||||
},
|
||||
}
|
||||
|
@ -596,7 +595,7 @@ func TestFinalise(t *testing.T) {
|
|||
Key: "rollout-demo",
|
||||
StableService: "echoserver",
|
||||
CanaryService: "echoserver-canary",
|
||||
TrafficConf: &rolloutsv1alpha1.IngressTrafficRouting{
|
||||
TrafficConf: &v1beta1.IngressTrafficRouting{
|
||||
Name: "echoserver",
|
||||
},
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ package network
|
|||
import (
|
||||
"context"
|
||||
|
||||
rolloutv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
|
||||
"github.com/openkruise/rollouts/api/v1beta1"
|
||||
)
|
||||
|
||||
// NetworkProvider common function across all TrafficRouting implementation
|
||||
|
@ -33,7 +33,7 @@ type NetworkProvider interface {
|
|||
// 1. check if canary has been set 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
|
||||
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 is called with a 3-second delay after completing the canary.
|
||||
Finalise(ctx context.Context) error
|
||||
|
|
|
@ -66,7 +66,7 @@ type Workload struct {
|
|||
|
||||
// ControllerFinderFunc is a function type that maps a pod to a list of
|
||||
// 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 {
|
||||
client.Client
|
||||
|
@ -86,29 +86,17 @@ func NewControllerFinder(c client.Client) *ControllerFinder {
|
|||
// +kubebuilder:rbac:groups=apps,resources=replicasets/status,verbs=get;update;patch
|
||||
|
||||
func (r *ControllerFinder) GetWorkloadForRef(rollout *rolloutv1beta1.Rollout) (*Workload, error) {
|
||||
workloadRef := rollout.Spec.ObjectRef.WorkloadRef
|
||||
if workloadRef == nil {
|
||||
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:
|
||||
workloadRef := rollout.Spec.WorkloadRef
|
||||
if rollout.Spec.Strategy.Canary.EnableExtraWorkloadForCanary {
|
||||
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 {
|
||||
return workload, err
|
||||
}
|
||||
|
@ -138,7 +126,7 @@ var (
|
|||
)
|
||||
|
||||
// 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
|
||||
ok, _ := verifyGroupKind(ref, ControllerKruiseKindCS.Kind, []string{ControllerKruiseKindCS.Group})
|
||||
if !ok {
|
||||
|
@ -180,7 +168,7 @@ func (r *ControllerFinder) getKruiseCloneSet(namespace string, ref *rolloutv1bet
|
|||
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
|
||||
ok, _ := verifyGroupKind(ref, ControllerKruiseKindDS.Kind, []string{ControllerKruiseKindDS.Group})
|
||||
if !ok {
|
||||
|
@ -224,7 +212,7 @@ func (r *ControllerFinder) getKruiseDaemonSet(namespace string, ref *rolloutv1be
|
|||
}
|
||||
|
||||
// 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
|
||||
ok, _ := verifyGroupKind(ref, ControllerKindDep.Kind, []string{ControllerKindDep.Group})
|
||||
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.
|
||||
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
|
||||
ok, _ := verifyGroupKind(ref, ControllerKindDep.Kind, []string{ControllerKindDep.Group})
|
||||
if !ok {
|
||||
|
@ -335,7 +323,7 @@ func (r *ControllerFinder) getDeployment(namespace string, ref *rolloutv1beta1.W
|
|||
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 {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -446,7 +434,7 @@ func (r *ControllerFinder) getDeploymentStableRs(obj *apps.Deployment) (*apps.Re
|
|||
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)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
|
|
@ -50,7 +50,7 @@ func IsRollbackInBatchPolicy(rollout *rolloutv1beta1.Rollout, labels map[string]
|
|||
if len(rollout.Spec.Strategy.Canary.TrafficRoutings) > 0 {
|
||||
return false
|
||||
}
|
||||
workloadRef := rollout.Spec.ObjectRef.WorkloadRef
|
||||
workloadRef := rollout.Spec.WorkloadRef
|
||||
//currently, only CloneSet, StatefulSet support this policy
|
||||
if workloadRef.Kind == ControllerKindSts.Kind ||
|
||||
workloadRef.Kind == ControllerKruiseKindCS.Kind ||
|
||||
|
@ -131,7 +131,7 @@ func DiscoverGVK(gvk schema.GroupVersionKind) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func GetGVKFrom(workloadRef *rolloutv1beta1.WorkloadRef) schema.GroupVersionKind {
|
||||
func GetGVKFrom(workloadRef *rolloutv1beta1.ObjectRef) schema.GroupVersionKind {
|
||||
if workloadRef == nil {
|
||||
return schema.GroupVersionKind{}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
appsv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
|
||||
appsv1beta1 "github.com/openkruise/rollouts/api/v1beta1"
|
||||
|
@ -131,15 +130,15 @@ func (h *RolloutCreateUpdateHandler) validateRolloutUpdate(oldObj, newObj *appsv
|
|||
switch latestObject.Status.Phase {
|
||||
// The workloadRef and TrafficRouting are not allowed to be modified in the Progressing, Terminating state
|
||||
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")}
|
||||
}
|
||||
// canary strategy
|
||||
if !reflect.DeepEqual(oldObj.Spec.Strategy.Canary.TrafficRoutings, newObj.Spec.Strategy.Canary.TrafficRoutings) {
|
||||
return field.ErrorList{field.Forbidden(field.NewPath("Spec.Strategy.Canary.TrafficRoutings"), "Rollout 'Strategy.Canary.TrafficRoutings' field is immutable")}
|
||||
}
|
||||
if !strings.EqualFold(oldObj.Annotations[appsv1beta1.RolloutStyleAnnotation], newObj.Annotations[appsv1beta1.RolloutStyleAnnotation]) {
|
||||
return field.ErrorList{field.Forbidden(field.NewPath("Metadata.Annotation"), "Rollout 'Rolling-Style' annotation is immutable")}
|
||||
if oldObj.Spec.Strategy.Canary.EnableExtraWorkloadForCanary != newObj.Spec.Strategy.Canary.EnableExtraWorkloadForCanary {
|
||||
return field.ErrorList{field.Forbidden(field.NewPath("Spec.Strategy.Canary"), "Rollout enableExtraWorkloadForCanary is immutable")}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,7 +170,7 @@ func (h *RolloutCreateUpdateHandler) validateRolloutConflict(rollout *appsv1beta
|
|||
}
|
||||
for i := range rolloutList.Items {
|
||||
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
|
||||
}
|
||||
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 {
|
||||
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, validateRolloutSpecStrategy(&rollout.Spec.Strategy, fldPath.Child("Strategy"))...)
|
||||
return errList
|
||||
}
|
||||
|
||||
func validateRolloutRollingStyle(rollout *appsv1beta1.Rollout, fldPath *field.Path) field.ErrorList {
|
||||
switch strings.ToLower(rollout.Annotations[appsv1beta1.RolloutStyleAnnotation]) {
|
||||
case "", strings.ToLower(string(appsv1alpha1.CanaryRollingStyle)), strings.ToLower(string(appsv1alpha1.PartitionRollingStyle)):
|
||||
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 {
|
||||
workloadRef := rollout.Spec.WorkloadRef
|
||||
if workloadRef.Kind == util.ControllerKindDep.Kind {
|
||||
return nil // Deployment support all rolling styles, no need to validate.
|
||||
}
|
||||
if strings.EqualFold(rollout.Annotations[appsv1beta1.RolloutStyleAnnotation], string(appsv1alpha1.CanaryRollingStyle)) {
|
||||
return field.ErrorList{field.Invalid(fldPath, rollout.Annotations[appsv1beta1.RolloutStyleAnnotation],
|
||||
"Only Deployment support canary rolling style")}
|
||||
if rollout.Spec.Strategy.Canary.EnableExtraWorkloadForCanary {
|
||||
return field.ErrorList{field.Invalid(fldPath, rollout.Spec.Strategy.Canary.EnableExtraWorkloadForCanary,
|
||||
"Only Deployment can set enableExtraWorkloadForCanary=true")}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateRolloutSpecObjectRef(objectRef *appsv1beta1.ObjectRef, fldPath *field.Path) field.ErrorList {
|
||||
if objectRef.WorkloadRef == nil {
|
||||
return field.ErrorList{field.Invalid(fldPath.Child("WorkloadRef"), objectRef.WorkloadRef, "WorkloadRef is required")}
|
||||
func validateRolloutSpecObjectRef(workloadRef *appsv1beta1.ObjectRef, fldPath *field.Path) field.ErrorList {
|
||||
if workloadRef == nil {
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
@ -237,7 +229,7 @@ func validateRolloutSpecCanaryStrategy(canary *appsv1beta1.CanaryStrategy, fldPa
|
|||
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{}
|
||||
if len(traffic.Service) == 0 {
|
||||
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 {
|
||||
s := &steps[i]
|
||||
if s.Weight == nil && s.Replicas == nil {
|
||||
return field.ErrorList{field.Invalid(fldPath.Index(i).Child("steps"), steps, `weight and replicas cannot be empty at the same time`)}
|
||||
if s.Replicas == nil {
|
||||
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)
|
||||
if err != nil ||
|
||||
canaryReplicas <= 0 ||
|
||||
(canaryReplicas > 100 && s.Replicas.Type == intstr.String) {
|
||||
return field.ErrorList{field.Invalid(fldPath.Index(i).Child("Replicas"),
|
||||
s.Replicas, `replicas must be positive number, or a percentage with "0%" < canaryReplicas <= "100%"`)}
|
||||
}
|
||||
canaryReplicas, err := intstr.GetScaledValueFromIntOrPercent(s.Replicas, 100, true)
|
||||
if err != nil ||
|
||||
canaryReplicas <= 0 ||
|
||||
(canaryReplicas > 100 && s.Replicas.Type == intstr.String) {
|
||||
return field.ErrorList{field.Invalid(fldPath.Index(i).Child("Replicas"),
|
||||
s.Replicas, `replicas must be positive number, or a percentage with "0%" < canaryReplicas <= "100%"`)}
|
||||
}
|
||||
if !isTraffic {
|
||||
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++ {
|
||||
prev := &steps[i-1]
|
||||
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 IsPercentageCanaryReplicasType(prev.Replicas) != IsPercentageCanaryReplicasType(curr.Replicas) {
|
||||
continue
|
||||
}
|
||||
|
||||
prevCanaryReplicas, _ := intstr.GetScaledValueFromIntOrPercent(prev.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 {
|
||||
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
|
||||
}
|
||||
|
||||
func IsSameWorkloadRefGVKName(a, b *appsv1beta1.WorkloadRef) bool {
|
||||
func IsSameWorkloadRefGVKName(a, b *appsv1beta1.ObjectRef) bool {
|
||||
if a == nil || b == nil {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import (
|
|||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
rolloutapi "github.com/openkruise/rollouts/api"
|
||||
appsv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
|
||||
appsv1beta1 "github.com/openkruise/rollouts/api/v1beta1"
|
||||
apps "k8s.io/api/apps/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -47,56 +46,46 @@ var (
|
|||
Annotations: map[string]string{},
|
||||
},
|
||||
Spec: appsv1beta1.RolloutSpec{
|
||||
ObjectRef: appsv1beta1.ObjectRef{
|
||||
WorkloadRef: &appsv1beta1.WorkloadRef{
|
||||
APIVersion: apps.SchemeGroupVersion.String(),
|
||||
Kind: "Deployment",
|
||||
Name: "deployment-demo",
|
||||
},
|
||||
WorkloadRef: appsv1beta1.ObjectRef{
|
||||
APIVersion: apps.SchemeGroupVersion.String(),
|
||||
Kind: "Deployment",
|
||||
Name: "deployment-demo",
|
||||
},
|
||||
Strategy: appsv1beta1.RolloutStrategy{
|
||||
Canary: &appsv1beta1.CanaryStrategy{
|
||||
Steps: []appsv1beta1.CanaryStep{
|
||||
{
|
||||
TrafficRoutingStrategy: appsv1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(10),
|
||||
TrafficRoutingStrategy: appsv1beta1.TrafficRoutingStrategy{
|
||||
Traffic: utilpointer.String("10%"),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)},
|
||||
Pause: appsv1beta1.RolloutPause{},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: appsv1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(10),
|
||||
TrafficRoutingStrategy: appsv1beta1.TrafficRoutingStrategy{
|
||||
Traffic: utilpointer.String("10%"),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(3)},
|
||||
Pause: appsv1beta1.RolloutPause{Duration: utilpointer.Int32(1 * 24 * 60 * 60)},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: appsv1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(30),
|
||||
TrafficRoutingStrategy: appsv1beta1.TrafficRoutingStrategy{
|
||||
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{
|
||||
Weight: utilpointer.Int32(100),
|
||||
},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: appsv1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(101),
|
||||
},
|
||||
},
|
||||
{
|
||||
TrafficRoutingStrategy: appsv1alpha1.TrafficRoutingStrategy{
|
||||
Weight: utilpointer.Int32(200),
|
||||
TrafficRoutingStrategy: appsv1beta1.TrafficRoutingStrategy{
|
||||
Traffic: utilpointer.String("100%"),
|
||||
},
|
||||
Replicas: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(20)},
|
||||
},
|
||||
},
|
||||
TrafficRoutings: []appsv1alpha1.TrafficRoutingRef{
|
||||
TrafficRoutings: []appsv1beta1.TrafficRoutingRef{
|
||||
{
|
||||
Service: "service-demo",
|
||||
Ingress: &appsv1alpha1.IngressTrafficRouting{
|
||||
Ingress: &appsv1beta1.IngressTrafficRouting{
|
||||
ClassType: "nginx",
|
||||
Name: "ingress-nginx-demo",
|
||||
},
|
||||
|
@ -129,21 +118,13 @@ func TestRolloutValidateCreate(t *testing.T) {
|
|||
Name: "Normal case",
|
||||
Succeed: true,
|
||||
GetObject: func() []client.Object {
|
||||
return []client.Object{rollout.DeepCopy()}
|
||||
obj := rollout.DeepCopy()
|
||||
return []client.Object{obj}
|
||||
},
|
||||
},
|
||||
/***********************************************************
|
||||
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",
|
||||
Succeed: false,
|
||||
|
@ -188,29 +169,21 @@ func TestRolloutValidateCreate(t *testing.T) {
|
|||
Succeed: true,
|
||||
GetObject: func() []client.Object {
|
||||
object := rollout.DeepCopy()
|
||||
object.Spec.ObjectRef.WorkloadRef = &appsv1beta1.WorkloadRef{
|
||||
object.Spec.WorkloadRef = appsv1beta1.ObjectRef{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "StatefulSet",
|
||||
Name: "whatever",
|
||||
}
|
||||
|
||||
return []client.Object{object}
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Steps.Weight is a decreasing sequence",
|
||||
Name: "Steps.Traffic is a decreasing sequence",
|
||||
Succeed: false,
|
||||
GetObject: func() []client.Object {
|
||||
object := rollout.DeepCopy()
|
||||
object.Spec.Strategy.Canary.Steps[2].Weight = utilpointer.Int32Ptr(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%"}
|
||||
object.Spec.Strategy.Canary.Steps[2].Traffic = utilpointer.String("%5")
|
||||
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,
|
||||
GetObject: func() []client.Object {
|
||||
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}
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Steps.Weight is illegal value, 101",
|
||||
Name: "Steps.Traffic is illegal value, 101",
|
||||
Succeed: false,
|
||||
GetObject: func() []client.Object {
|
||||
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}
|
||||
},
|
||||
},
|
||||
|
@ -264,9 +237,7 @@ func TestRolloutValidateCreate(t *testing.T) {
|
|||
Succeed: true,
|
||||
GetObject: func() []client.Object {
|
||||
object := rollout.DeepCopy()
|
||||
object.Annotations = map[string]string{
|
||||
appsv1beta1.RolloutStyleAnnotation: "Canary",
|
||||
}
|
||||
object.Spec.Strategy.Canary.EnableExtraWorkloadForCanary = true
|
||||
return []client.Object{object}
|
||||
},
|
||||
},
|
||||
|
@ -275,20 +246,6 @@ func TestRolloutValidateCreate(t *testing.T) {
|
|||
Succeed: true,
|
||||
GetObject: func() []client.Object {
|
||||
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}
|
||||
},
|
||||
},
|
||||
|
@ -297,21 +254,19 @@ func TestRolloutValidateCreate(t *testing.T) {
|
|||
Succeed: false,
|
||||
GetObject: func() []client.Object {
|
||||
object := rollout.DeepCopy()
|
||||
object.Annotations = map[string]string{
|
||||
appsv1beta1.RolloutStyleAnnotation: "Canary",
|
||||
}
|
||||
object.Spec.ObjectRef.WorkloadRef.APIVersion = "apps.kruise.io/v1alpha1"
|
||||
object.Spec.ObjectRef.WorkloadRef.Kind = "CloneSet"
|
||||
object.Spec.Strategy.Canary.EnableExtraWorkloadForCanary = true
|
||||
object.Spec.WorkloadRef.APIVersion = "apps.kruise.io/v1alpha1"
|
||||
object.Spec.WorkloadRef.Kind = "CloneSet"
|
||||
return []client.Object{object}
|
||||
},
|
||||
},
|
||||
//{
|
||||
// Name: "The last Steps.Weight is not 100",
|
||||
// Name: "The last Steps.Traffic is not 100",
|
||||
// Succeed: false,
|
||||
// GetObject: func() []client.Object {
|
||||
// object := rollout.DeepCopy()
|
||||
// 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}
|
||||
// },
|
||||
//},
|
||||
|
@ -326,15 +281,15 @@ func TestRolloutValidateCreate(t *testing.T) {
|
|||
|
||||
object1 := rollout.DeepCopy()
|
||||
object1.Name = "object-1"
|
||||
object1.Spec.ObjectRef.WorkloadRef.Name = "another"
|
||||
object1.Spec.WorkloadRef.Name = "another"
|
||||
|
||||
object2 := rollout.DeepCopy()
|
||||
object2.Name = "object-2"
|
||||
object2.Spec.ObjectRef.WorkloadRef.APIVersion = "another"
|
||||
object2.Spec.WorkloadRef.APIVersion = "another"
|
||||
|
||||
object3 := rollout.DeepCopy()
|
||||
object3.Name = "object-3"
|
||||
object3.Spec.ObjectRef.WorkloadRef.Kind = "another"
|
||||
object3.Spec.WorkloadRef.Kind = "another"
|
||||
|
||||
return []client.Object{
|
||||
object, object1, object2, object3,
|
||||
|
@ -349,15 +304,15 @@ func TestRolloutValidateCreate(t *testing.T) {
|
|||
|
||||
object1 := rollout.DeepCopy()
|
||||
object1.Name = "object-1"
|
||||
object1.Spec.ObjectRef.WorkloadRef.Name = "another"
|
||||
object1.Spec.WorkloadRef.Name = "another"
|
||||
|
||||
object2 := rollout.DeepCopy()
|
||||
object2.Name = "object-2"
|
||||
object2.Spec.ObjectRef.WorkloadRef.APIVersion = "another"
|
||||
object2.Spec.WorkloadRef.APIVersion = "another"
|
||||
|
||||
object3 := rollout.DeepCopy()
|
||||
object3.Name = "object-3"
|
||||
object3.Spec.ObjectRef.WorkloadRef.Kind = "another"
|
||||
object3.Spec.WorkloadRef.Kind = "another"
|
||||
|
||||
object4 := rollout.DeepCopy()
|
||||
object4.Name = "object-4"
|
||||
|
@ -400,7 +355,7 @@ func TestRolloutValidateUpdate(t *testing.T) {
|
|||
},
|
||||
GetNewObject: func() client.Object {
|
||||
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
|
||||
},
|
||||
},
|
||||
|
@ -430,7 +385,7 @@ func TestRolloutValidateUpdate(t *testing.T) {
|
|||
GetNewObject: func() client.Object {
|
||||
object := rollout.DeepCopy()
|
||||
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
|
||||
},
|
||||
},
|
||||
|
@ -439,14 +394,13 @@ func TestRolloutValidateUpdate(t *testing.T) {
|
|||
Succeed: false,
|
||||
GetOldObject: func() client.Object {
|
||||
object := rollout.DeepCopy()
|
||||
object.Annotations[appsv1beta1.RolloutStyleAnnotation] = "Partition"
|
||||
object.Status.Phase = appsv1beta1.RolloutPhaseProgressing
|
||||
return object
|
||||
},
|
||||
GetNewObject: func() client.Object {
|
||||
object := rollout.DeepCopy()
|
||||
object.Status.Phase = appsv1beta1.RolloutPhaseProgressing
|
||||
object.Annotations[appsv1beta1.RolloutStyleAnnotation] = "Canary"
|
||||
object.Spec.Strategy.Canary.EnableExtraWorkloadForCanary = true
|
||||
return object
|
||||
},
|
||||
},
|
||||
|
@ -476,7 +430,7 @@ func TestRolloutValidateUpdate(t *testing.T) {
|
|||
GetNewObject: func() client.Object {
|
||||
object := rollout.DeepCopy()
|
||||
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
|
||||
},
|
||||
},
|
||||
|
@ -491,7 +445,7 @@ func TestRolloutValidateUpdate(t *testing.T) {
|
|||
GetNewObject: func() client.Object {
|
||||
object := rollout.DeepCopy()
|
||||
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
|
||||
},
|
||||
},
|
||||
|
|
|
@ -107,15 +107,6 @@ func validateV1alpha1RolloutRollingStyle(rollout *appsv1alpha1.Rollout, fldPath
|
|||
return field.ErrorList{field.Invalid(fldPath, rollout.Annotations[appsv1alpha1.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.
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -145,11 +136,35 @@ func validateV1alpha1RolloutSpecCanaryStrategy(canary *appsv1alpha1.CanaryStrate
|
|||
errList = append(errList, field.Invalid(fldPath, canary.TrafficRoutings, "Rollout currently only support single TrafficRouting."))
|
||||
}
|
||||
for _, traffic := range canary.TrafficRoutings {
|
||||
errList = append(errList, validateRolloutSpecCanaryTraffic(traffic, fldPath.Child("TrafficRouting"))...)
|
||||
errList = append(errList, validateV1alpha1RolloutSpecCanaryTraffic(traffic, fldPath.Child("TrafficRouting"))...)
|
||||
}
|
||||
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 {
|
||||
stepCount := len(steps)
|
||||
if stepCount == 0 {
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/conversion"
|
||||
)
|
||||
|
||||
type GateFunc func() (enabled bool)
|
||||
|
@ -90,11 +91,12 @@ func SetupWithManager(mgr manager.Manager) error {
|
|||
server.Register(path, &webhook.Admission{Handler: handler})
|
||||
klog.V(3).Infof("Registered webhook handler %s", path)
|
||||
}
|
||||
|
||||
err := initialize(context.TODO(), mgr.GetConfig())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// register conversion webhook
|
||||
server.Register("/convert", &conversion.Webhook{})
|
||||
klog.Infof("webhook init done")
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
|
||||
webhookutil "github.com/openkruise/rollouts/pkg/webhook/util"
|
||||
"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/writer"
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
|
@ -266,6 +267,10 @@ func (c *Controller) sync() error {
|
|||
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() {
|
||||
close(uninit)
|
||||
})
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -422,14 +422,14 @@ func (h *WorkloadHandler) fetchMatchedRollout(obj client.Object) (*appsv1beta1.R
|
|||
}
|
||||
for i := range rolloutList.Items {
|
||||
rollout := &rolloutList.Items[i]
|
||||
if !rollout.DeletionTimestamp.IsZero() || rollout.Spec.ObjectRef.WorkloadRef == nil {
|
||||
if !rollout.DeletionTimestamp.IsZero() {
|
||||
continue
|
||||
}
|
||||
if rollout.Status.Phase == appsv1beta1.RolloutPhaseDisabled {
|
||||
klog.Infof("Disabled rollout(%s/%s) fetched when fetching matched rollout", rollout.Namespace, rollout.Name)
|
||||
continue
|
||||
}
|
||||
ref := rollout.Spec.ObjectRef.WorkloadRef
|
||||
ref := rollout.Spec.WorkloadRef
|
||||
gv, err := schema.ParseGroupVersion(ref.APIVersion)
|
||||
if err != nil {
|
||||
klog.Warningf("ParseGroupVersion rollout(%s/%s) ref failed: %s", rollout.Namespace, rollout.Name, err.Error())
|
||||
|
|
|
@ -274,12 +274,10 @@ var (
|
|||
Labels: map[string]string{},
|
||||
},
|
||||
Spec: appsv1beta1.RolloutSpec{
|
||||
ObjectRef: appsv1beta1.ObjectRef{
|
||||
WorkloadRef: &appsv1beta1.WorkloadRef{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "echoserver",
|
||||
},
|
||||
WorkloadRef: appsv1beta1.ObjectRef{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "echoserver",
|
||||
},
|
||||
Strategy: appsv1beta1.RolloutStrategy{
|
||||
Canary: &appsv1beta1.CanaryStrategy{},
|
||||
|
@ -345,7 +343,7 @@ func TestHandlerDeployment(t *testing.T) {
|
|||
},
|
||||
getRollout: func() *appsv1beta1.Rollout {
|
||||
obj := rolloutDemo.DeepCopy()
|
||||
obj.Spec.ObjectRef.WorkloadRef = &appsv1beta1.WorkloadRef{
|
||||
obj.Spec.WorkloadRef = appsv1beta1.ObjectRef{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "other",
|
||||
|
@ -377,10 +375,10 @@ func TestHandlerDeployment(t *testing.T) {
|
|||
getRollout: func() *appsv1beta1.Rollout {
|
||||
demo := rolloutDemo.DeepCopy()
|
||||
demo.Spec.Strategy.Canary = &appsv1beta1.CanaryStrategy{
|
||||
TrafficRoutings: []appsv1alpha1.TrafficRoutingRef{
|
||||
TrafficRoutings: []appsv1beta1.TrafficRoutingRef{
|
||||
{
|
||||
Service: "echoserver",
|
||||
Ingress: &appsv1alpha1.IngressTrafficRouting{
|
||||
Ingress: &appsv1beta1.IngressTrafficRouting{
|
||||
Name: "echoserver",
|
||||
},
|
||||
},
|
||||
|
@ -582,7 +580,7 @@ func TestHandlerCloneSet(t *testing.T) {
|
|||
},
|
||||
getRollout: func() *appsv1beta1.Rollout {
|
||||
obj := rolloutDemo.DeepCopy()
|
||||
obj.Spec.ObjectRef.WorkloadRef = &appsv1beta1.WorkloadRef{
|
||||
obj.Spec.WorkloadRef = appsv1beta1.ObjectRef{
|
||||
APIVersion: "apps.kruise.io/v1alpha1",
|
||||
Kind: "CloneSet",
|
||||
Name: "echoserver",
|
||||
|
@ -646,7 +644,7 @@ func TestHandlerDaemonSet(t *testing.T) {
|
|||
},
|
||||
getRollout: func() *appsv1beta1.Rollout {
|
||||
obj := rolloutDemo.DeepCopy()
|
||||
obj.Spec.ObjectRef.WorkloadRef = &appsv1beta1.WorkloadRef{
|
||||
obj.Spec.WorkloadRef = appsv1beta1.ObjectRef{
|
||||
APIVersion: "apps.kruise.io/v1alpha1",
|
||||
Kind: "DaemonSet",
|
||||
Name: "echoserver",
|
||||
|
@ -710,7 +708,7 @@ func TestHandleStatefulSet(t *testing.T) {
|
|||
},
|
||||
getRollout: func() *appsv1beta1.Rollout {
|
||||
obj := rolloutDemo.DeepCopy()
|
||||
obj.Spec.ObjectRef.WorkloadRef = &appsv1beta1.WorkloadRef{
|
||||
obj.Spec.WorkloadRef = appsv1beta1.ObjectRef{
|
||||
APIVersion: "apps.kruise.io/v1beta1",
|
||||
Kind: "StatefulSet",
|
||||
Name: "echoserver",
|
||||
|
|
Loading…
Reference in New Issue