/* 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) } }