rollouts/pkg/controller/batchrelease/control/bluegreenstyle/deployment/control_test.go

730 lines
25 KiB
Go

/*
Copyright 2022 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 deployment
import (
"context"
"encoding/json"
"fmt"
"reflect"
"strconv"
"testing"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
kruiseappsv1alpha1 "github.com/openkruise/kruise-api/apps/v1alpha1"
apps "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/rand"
"k8s.io/apimachinery/pkg/util/uuid"
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
rolloutapi "github.com/openkruise/rollouts/api"
"github.com/openkruise/rollouts/api/v1beta1"
batchcontext "github.com/openkruise/rollouts/pkg/controller/batchrelease/context"
control "github.com/openkruise/rollouts/pkg/controller/batchrelease/control"
"github.com/openkruise/rollouts/pkg/util"
"github.com/openkruise/rollouts/pkg/util/errors"
)
var (
scheme = runtime.NewScheme()
deploymentKey = types.NamespacedName{
Name: "deployment",
Namespace: "default",
}
deploymentDemo = &apps.Deployment{
TypeMeta: metav1.TypeMeta{
APIVersion: "apps/v1",
Kind: "Deployment",
},
ObjectMeta: metav1.ObjectMeta{
Name: deploymentKey.Name,
Namespace: deploymentKey.Namespace,
Generation: 1,
Labels: map[string]string{
"app": "busybox",
},
Annotations: map[string]string{
"type": "unit-test",
},
},
Spec: apps.DeploymentSpec{
Paused: true,
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "busybox",
},
},
Replicas: pointer.Int32(10),
Strategy: apps.DeploymentStrategy{
Type: apps.RollingUpdateDeploymentStrategyType,
RollingUpdate: &apps.RollingUpdateDeployment{
MaxUnavailable: &intstr.IntOrString{Type: intstr.Int, IntVal: 1},
MaxSurge: &intstr.IntOrString{Type: intstr.String, StrVal: "20%"},
},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app": "busybox",
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "busybox",
Image: "busybox:latest",
},
},
},
},
},
Status: apps.DeploymentStatus{
Replicas: 10,
UpdatedReplicas: 0,
ReadyReplicas: 10,
AvailableReplicas: 10,
CollisionCount: pointer.Int32(1),
ObservedGeneration: 1,
},
}
deploymentDemo2 = &apps.Deployment{
TypeMeta: metav1.TypeMeta{
APIVersion: apps.SchemeGroupVersion.String(),
Kind: "Deployment",
},
ObjectMeta: metav1.ObjectMeta{
Name: "deployment",
Namespace: "default",
UID: types.UID("87076677"),
Generation: 2,
Labels: map[string]string{
"app": "busybox",
apps.DefaultDeploymentUniqueLabelKey: "update-pod-hash",
},
},
Spec: apps.DeploymentSpec{
Replicas: pointer.Int32(10),
Strategy: apps.DeploymentStrategy{
Type: apps.RollingUpdateDeploymentStrategyType,
RollingUpdate: &apps.RollingUpdateDeployment{
MaxSurge: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)},
MaxUnavailable: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(0)},
},
},
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "busybox",
},
},
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: containers("v2"),
},
},
},
Status: apps.DeploymentStatus{
Replicas: 10,
ReadyReplicas: 10,
UpdatedReplicas: 0,
AvailableReplicas: 10,
},
}
releaseDemo = &v1beta1.BatchRelease{
TypeMeta: metav1.TypeMeta{
APIVersion: "rollouts.kruise.io/v1alpha1",
Kind: "BatchRelease",
},
ObjectMeta: metav1.ObjectMeta{
Name: "release",
Namespace: deploymentKey.Namespace,
UID: uuid.NewUUID(),
},
Spec: v1beta1.BatchReleaseSpec{
ReleasePlan: v1beta1.ReleasePlan{
FinalizingPolicy: v1beta1.WaitResumeFinalizingPolicyType,
Batches: []v1beta1.ReleaseBatch{
{
CanaryReplicas: intstr.FromString("10%"),
},
{
CanaryReplicas: intstr.FromString("50%"),
},
{
CanaryReplicas: intstr.FromString("100%"),
},
},
},
WorkloadRef: v1beta1.ObjectRef{
APIVersion: deploymentDemo.APIVersion,
Kind: deploymentDemo.Kind,
Name: deploymentDemo.Name,
},
},
Status: v1beta1.BatchReleaseStatus{
CanaryStatus: v1beta1.BatchReleaseCanaryStatus{
CurrentBatch: 1,
},
},
}
hpaDemo = &autoscalingv1.HorizontalPodAutoscaler{
TypeMeta: metav1.TypeMeta{
APIVersion: "autoscaling/v1",
Kind: "HorizontalPodAutoscaler",
},
ObjectMeta: metav1.ObjectMeta{
Name: "hpa",
Namespace: deploymentKey.Namespace,
},
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
APIVersion: apps.SchemeGroupVersion.String(),
Kind: "Deployment",
Name: deploymentDemo.Name,
},
MinReplicas: pointer.Int32(1),
MaxReplicas: 10,
},
}
)
func init() {
apps.AddToScheme(scheme)
rolloutapi.AddToScheme(scheme)
kruiseappsv1alpha1.AddToScheme(scheme)
autoscalingv1.AddToScheme(scheme)
}
func TestControlPackage(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Deployment Control Package Suite")
}
var _ = Describe("Deployment Control", func() {
var (
c client.Client
rc *realController
deployment *apps.Deployment
release *v1beta1.BatchRelease
hpa *autoscalingv1.HorizontalPodAutoscaler
stableRS *apps.ReplicaSet
canaryRS *apps.ReplicaSet
)
BeforeEach(func() {
deployment = deploymentDemo.DeepCopy()
release = releaseDemo.DeepCopy()
hpa = hpaDemo.DeepCopy()
deployment = getStableWithReady(deployment, "v1").(*apps.Deployment)
stableRS = makeStableReplicaSets(deployment).(*apps.ReplicaSet)
stableRS.Spec.MinReadySeconds = 0
stableRS.Status.ReadyReplicas = *deployment.Spec.Replicas
stableRS.Status.AvailableReplicas = *deployment.Spec.Replicas
canaryRS = makeCanaryReplicaSets(deployment).(*apps.ReplicaSet)
canaryRS.Status.ReadyReplicas = 0
canaryRS.Status.AvailableReplicas = 0
c = fake.NewClientBuilder().
WithScheme(scheme).
WithObjects(deployment, release, hpa, stableRS, canaryRS).
Build()
rc = &realController{
key: types.NamespacedName{Namespace: deployment.Namespace, Name: deployment.Name},
client: c,
finder: util.NewControllerFinder(c),
}
})
It("should initialize Deployment successfully", func() {
// build controller
_, err := rc.BuildController()
Expect(err).NotTo(HaveOccurred())
// call Initialize method
err = retryFunction(3, func() error {
return rc.Initialize(release)
})
Expect(err).NotTo(HaveOccurred())
// inspect if HPA is disabled
disabledHPA := &autoscalingv1.HorizontalPodAutoscaler{}
err = c.Get(context.TODO(), types.NamespacedName{Namespace: hpa.Namespace, Name: hpa.Name}, disabledHPA)
Expect(err).NotTo(HaveOccurred())
Expect(disabledHPA.Spec.ScaleTargetRef.Name).To(Equal(deployment.Name + "-DisableByRollout"))
// inspect if MinReadySeconds of stable ReplicaSet is updated
stableRSAfter := &apps.ReplicaSet{}
err = c.Get(context.TODO(), client.ObjectKeyFromObject(stableRS), stableRSAfter)
Expect(err).NotTo(HaveOccurred())
Expect(stableRSAfter.Spec.MinReadySeconds).To(Equal(int32(v1beta1.MaxReadySeconds)))
// inspect if Deployment is patched properly
updatedDeployment := &apps.Deployment{}
err = c.Get(context.TODO(), client.ObjectKeyFromObject(deployment), updatedDeployment)
Expect(err).NotTo(HaveOccurred())
// inspect if annotations are added
Expect(updatedDeployment.Annotations).To(HaveKey(v1beta1.OriginalDeploymentStrategyAnnotation))
Expect(updatedDeployment.Annotations).To(HaveKey(util.BatchReleaseControlAnnotation))
Expect(updatedDeployment.Annotations[util.BatchReleaseControlAnnotation]).To(Equal(getControlInfo(release)))
// inspect if strategy is updated
Expect(updatedDeployment.Spec.Strategy.RollingUpdate).NotTo(BeNil())
Expect(updatedDeployment.Spec.Strategy.RollingUpdate.MaxSurge.IntVal).To(Equal(int32(1)))
Expect(updatedDeployment.Spec.Strategy.RollingUpdate.MaxUnavailable.IntVal).To(Equal(int32(0)))
Expect(updatedDeployment.Spec.MinReadySeconds).To(Equal(int32(v1beta1.MaxReadySeconds)))
Expect(*updatedDeployment.Spec.ProgressDeadlineSeconds).To(Equal(int32(v1beta1.MaxProgressSeconds)))
})
It("should finalize Deployment successfully", func() {
// build controller
rc.object = nil
_, err := rc.BuildController()
Expect(err).NotTo(HaveOccurred())
// call Finalize method
err = retryFunction(3, func() error {
return rc.Finalize(release)
})
Expect(err).NotTo(HaveOccurred())
// inspect if Deployment is patched properly
updatedDeployment := &apps.Deployment{}
err = c.Get(context.TODO(), client.ObjectKeyFromObject(deployment), updatedDeployment)
Expect(err).NotTo(HaveOccurred())
// inspect if annotations are removed
Expect(updatedDeployment.Annotations).NotTo(HaveKey(v1beta1.OriginalDeploymentStrategyAnnotation))
Expect(updatedDeployment.Annotations).NotTo(HaveKey(util.BatchReleaseControlAnnotation))
// inspect if strategy is restored
Expect(updatedDeployment.Spec.Strategy.RollingUpdate).NotTo(BeNil())
Expect(*updatedDeployment.Spec.Strategy.RollingUpdate.MaxSurge).To(Equal(intstr.IntOrString{Type: intstr.String, StrVal: "20%"}))
Expect(*updatedDeployment.Spec.Strategy.RollingUpdate.MaxUnavailable).To(Equal(intstr.IntOrString{Type: intstr.Int, IntVal: 1}))
Expect(updatedDeployment.Spec.MinReadySeconds).To(Equal(int32(0)))
Expect(updatedDeployment.Spec.ProgressDeadlineSeconds).To(BeNil())
// inspect if HPA is restored
restoredHPA := &autoscalingv1.HorizontalPodAutoscaler{}
err = c.Get(context.TODO(), types.NamespacedName{Namespace: hpa.Namespace, Name: hpa.Name}, restoredHPA)
Expect(err).NotTo(HaveOccurred())
Expect(restoredHPA.Spec.ScaleTargetRef.Name).To(Equal(deployment.Name))
// inspect if MinReadySeconds of stable ReplicaSet is restored
stableRSAfter := &apps.ReplicaSet{}
err = c.Get(context.TODO(), client.ObjectKeyFromObject(stableRS), stableRSAfter)
Expect(err).NotTo(HaveOccurred())
Expect(stableRSAfter.Spec.MinReadySeconds).To(Equal(int32(0)))
})
It("should upgradBatch for Deployment successfully", func() {
// call Initialize method
_, err := rc.BuildController()
Expect(err).NotTo(HaveOccurred())
err = retryFunction(3, func() error {
return rc.Initialize(release)
})
Expect(err).NotTo(HaveOccurred())
// call UpgradeBatch method
rc.object = nil
_, err = rc.BuildController()
Expect(err).NotTo(HaveOccurred())
batchContext, err := rc.CalculateBatchContext(release)
Expect(err).NotTo(HaveOccurred())
err = rc.UpgradeBatch(batchContext)
Expect(err).NotTo(HaveOccurred())
// inspect if Deployment is patched properly
updatedDeployment := &apps.Deployment{}
err = c.Get(context.TODO(), client.ObjectKeyFromObject(deployment), updatedDeployment)
Expect(err).NotTo(HaveOccurred())
Expect(updatedDeployment.Spec.Paused).To(BeFalse())
Expect(*updatedDeployment.Spec.Strategy.RollingUpdate.MaxSurge).To(Equal(intstr.IntOrString{Type: intstr.String, StrVal: "50%"}))
Expect(*updatedDeployment.Spec.Strategy.RollingUpdate.MaxUnavailable).To(Equal(intstr.IntOrString{Type: intstr.Int, IntVal: 0}))
})
})
func TestCalculateBatchContext(t *testing.T) {
RegisterFailHandler(Fail)
cases := map[string]struct {
workload func() []client.Object
release func() *v1beta1.BatchRelease
result *batchcontext.BatchContext
}{
"noraml case": {
workload: func() []client.Object {
deployment := getStableWithReady(deploymentDemo2, "v2").(*apps.Deployment)
deployment.Status = apps.DeploymentStatus{
Replicas: 15,
UpdatedReplicas: 5,
AvailableReplicas: 12,
ReadyReplicas: 12,
}
// current partition, ie. maxSurge
deployment.Spec.Strategy.RollingUpdate.MaxSurge = &intstr.IntOrString{Type: intstr.String, StrVal: "50%"}
deployment.Spec.Replicas = pointer.Int32(10)
newRss := makeCanaryReplicaSets(deployment).(*apps.ReplicaSet)
newRss.Status.ReadyReplicas = 2
return []client.Object{deployment, newRss, makeStableReplicaSets(deployment)}
},
release: func() *v1beta1.BatchRelease {
r := &v1beta1.BatchRelease{
Spec: v1beta1.BatchReleaseSpec{
ReleasePlan: v1beta1.ReleasePlan{
FinalizingPolicy: v1beta1.WaitResumeFinalizingPolicyType,
Batches: []v1beta1.ReleaseBatch{
{
CanaryReplicas: intstr.IntOrString{Type: intstr.String, StrVal: "50%"},
},
{
CanaryReplicas: intstr.IntOrString{Type: intstr.String, StrVal: "100%"},
},
},
},
},
Status: v1beta1.BatchReleaseStatus{
CanaryStatus: v1beta1.BatchReleaseCanaryStatus{
CurrentBatch: 1,
},
UpdateRevision: "version-2",
},
}
return r
},
result: &batchcontext.BatchContext{
CurrentBatch: 1,
UpdateRevision: "version-2",
DesiredSurge: intstr.IntOrString{Type: intstr.String, StrVal: "100%"},
CurrentSurge: intstr.IntOrString{Type: intstr.String, StrVal: "50%"},
Replicas: 10,
UpdatedReplicas: 5,
UpdatedReadyReplicas: 2,
PlannedUpdatedReplicas: 10,
DesiredUpdatedReplicas: 10,
},
},
"maxSurge=99%, replicas=5": {
workload: func() []client.Object {
deployment := getStableWithReady(deploymentDemo2, "v2").(*apps.Deployment)
deployment.Status = apps.DeploymentStatus{
Replicas: 9,
UpdatedReplicas: 4,
AvailableReplicas: 9,
ReadyReplicas: 9,
}
deployment.Spec.Replicas = pointer.Int32(5)
// current partition, ie. maxSurge
deployment.Spec.Strategy.RollingUpdate.MaxSurge = &intstr.IntOrString{Type: intstr.String, StrVal: "90%"}
newRss := makeCanaryReplicaSets(deployment).(*apps.ReplicaSet)
newRss.Status.ReadyReplicas = 4
return []client.Object{deployment, newRss, makeStableReplicaSets(deployment)}
},
release: func() *v1beta1.BatchRelease {
r := &v1beta1.BatchRelease{
Spec: v1beta1.BatchReleaseSpec{
ReleasePlan: v1beta1.ReleasePlan{
FinalizingPolicy: v1beta1.WaitResumeFinalizingPolicyType,
Batches: []v1beta1.ReleaseBatch{
{
CanaryReplicas: intstr.IntOrString{Type: intstr.String, StrVal: "90%"},
},
{
CanaryReplicas: intstr.IntOrString{Type: intstr.String, StrVal: "99%"},
},
},
},
},
Status: v1beta1.BatchReleaseStatus{
CanaryStatus: v1beta1.BatchReleaseCanaryStatus{
CurrentBatch: 1,
},
UpdateRevision: "version-2",
},
}
return r
},
result: &batchcontext.BatchContext{
CurrentBatch: 1,
UpdateRevision: "version-2",
DesiredSurge: intstr.FromString("99%"),
CurrentSurge: intstr.FromString("90%"),
Replicas: 5,
UpdatedReplicas: 4,
UpdatedReadyReplicas: 4,
PlannedUpdatedReplicas: 4,
DesiredUpdatedReplicas: 4,
},
},
// test case for continuous release
// "maxSurge=100%, but it is initialized value": {
// workload: func() []client.Object {
// deployment := getStableWithReady(deploymentDemo2, "v2").(*apps.Deployment)
// deployment.Status = apps.DeploymentStatus{
// Replicas: 10,
// UpdatedReplicas: 0,
// AvailableReplicas: 10,
// ReadyReplicas: 10,
// }
// // current partition, ie. maxSurge
// deployment.Spec.Strategy.RollingUpdate.MaxSurge = &intstr.IntOrString{Type: intstr.String, StrVal: "100%"}
// newRss := makeCanaryReplicaSets(deployment).(*apps.ReplicaSet)
// newRss.Status.ReadyReplicas = 0
// return []client.Object{deployment, newRss, makeStableReplicaSets(deployment)}
// },
// release: func() *v1beta1.BatchRelease {
// r := &v1beta1.BatchRelease{
// Spec: v1beta1.BatchReleaseSpec{
// ReleasePlan: v1beta1.ReleasePlan{
// FailureThreshold: &percent,
// FinalizingPolicy: v1beta1.WaitResumeFinalizingPolicyType,
// Batches: []v1beta1.ReleaseBatch{
// {
// CanaryReplicas: intstr.IntOrString{Type: intstr.String, StrVal: "50%"},
// },
// },
// },
// },
// Status: v1beta1.BatchReleaseStatus{
// CanaryStatus: v1beta1.BatchReleaseCanaryStatus{
// CurrentBatch: 0,
// },
// UpdateRevision: "version-2",
// },
// }
// return r
// },
// result: &batchcontext.BatchContext{
// CurrentBatch: 0,
// UpdateRevision: "version-2",
// DesiredPartition: intstr.FromString("50%"),
// FailureThreshold: &percent,
// CurrentPartition: intstr.FromString("0%"), // mainly check this
// Replicas: 10,
// UpdatedReplicas: 0,
// UpdatedReadyReplicas: 0,
// PlannedUpdatedReplicas: 5,
// DesiredUpdatedReplicas: 5,
// },
// },
}
for name, cs := range cases {
t.Run(name, func(t *testing.T) {
cliBuilder := fake.NewClientBuilder().WithScheme(scheme).WithObjects(cs.workload()...)
cli := cliBuilder.Build()
control := realController{
client: cli,
key: deploymentKey,
}
_, err := control.BuildController()
Expect(err).NotTo(HaveOccurred())
got, err := control.CalculateBatchContext(cs.release())
Expect(err).NotTo(HaveOccurred())
fmt.Printf("expect %s, but got %s", cs.result.Log(), got.Log())
Expect(got.Log()).Should(Equal(cs.result.Log()))
})
}
}
func TestRealController(t *testing.T) {
RegisterFailHandler(Fail)
release := releaseDemo.DeepCopy()
clone := deploymentDemo.DeepCopy()
stableRs, canaryRs := makeStableReplicaSets(clone), makeCanaryReplicaSets(clone)
cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects(release, clone, stableRs, canaryRs).Build()
// build new controller
c := NewController(cli, deploymentKey, clone.GroupVersionKind()).(*realController)
controller, err := c.BuildController()
Expect(err).NotTo(HaveOccurred())
// call Initialize
err = controller.Initialize(release)
Expect(err).NotTo(HaveOccurred())
fetch := &apps.Deployment{}
Expect(cli.Get(context.TODO(), deploymentKey, fetch)).NotTo(HaveOccurred())
// check strategy
Expect(fetch.Spec.Paused).Should(BeTrue())
Expect(fetch.Spec.Strategy.Type).Should(Equal(apps.RollingUpdateDeploymentStrategyType))
Expect(reflect.DeepEqual(fetch.Spec.Strategy.RollingUpdate.MaxSurge, &intstr.IntOrString{Type: intstr.Int, IntVal: 1})).Should(BeTrue())
Expect(reflect.DeepEqual(fetch.Spec.Strategy.RollingUpdate.MaxUnavailable, &intstr.IntOrString{Type: intstr.Int, IntVal: 0})).Should(BeTrue())
Expect(fetch.Spec.MinReadySeconds).Should(Equal(int32(v1beta1.MaxReadySeconds)))
Expect(*fetch.Spec.ProgressDeadlineSeconds).Should(Equal(int32(v1beta1.MaxProgressSeconds)))
// check annotations
Expect(fetch.Annotations[util.BatchReleaseControlAnnotation]).Should(Equal(getControlInfo(release)))
fmt.Println(fetch.Annotations[v1beta1.OriginalDeploymentStrategyAnnotation])
Expect(fetch.Annotations[v1beta1.OriginalDeploymentStrategyAnnotation]).Should(Equal(util.DumpJSON(&control.OriginalDeploymentStrategy{
MaxUnavailable: &intstr.IntOrString{Type: intstr.Int, IntVal: 1},
MaxSurge: &intstr.IntOrString{Type: intstr.String, StrVal: "20%"},
MinReadySeconds: 0,
ProgressDeadlineSeconds: pointer.Int32(600),
})))
// check minReadyseconds field of stable replicaset
fetchRS := &apps.ReplicaSet{}
Expect(cli.Get(context.TODO(), types.NamespacedName{Name: stableRs.GetName(), Namespace: stableRs.GetNamespace()}, fetchRS)).NotTo(HaveOccurred())
Expect(fetchRS.Spec.MinReadySeconds).Should(Equal(int32(v1beta1.MaxReadySeconds)))
c.object = fetch // mock
for {
batchContext, err := controller.CalculateBatchContext(release)
Expect(err).NotTo(HaveOccurred())
err = controller.UpgradeBatch(batchContext)
fetch := &apps.Deployment{}
// mock
Expect(cli.Get(context.TODO(), deploymentKey, fetch)).NotTo(HaveOccurred())
c.object = fetch
if err == nil {
break
}
}
fetch = &apps.Deployment{}
Expect(cli.Get(context.TODO(), deploymentKey, fetch)).NotTo(HaveOccurred())
// currentBatch is 1, which means br is in the second batch, maxSurge is 50%
Expect(reflect.DeepEqual(fetch.Spec.Strategy.RollingUpdate.MaxSurge, &intstr.IntOrString{Type: intstr.String, StrVal: "50%"})).Should(BeTrue())
release.Spec.ReleasePlan.BatchPartition = nil
err = controller.Finalize(release)
Expect(errors.IsRetryError(err)).Should(BeTrue())
fetch = &apps.Deployment{}
Expect(cli.Get(context.TODO(), deploymentKey, fetch)).NotTo(HaveOccurred())
// check workload strategy
Expect(fetch.Spec.Paused).Should(BeFalse())
Expect(fetch.Spec.Strategy.Type).Should(Equal(apps.RollingUpdateDeploymentStrategyType))
Expect(reflect.DeepEqual(fetch.Spec.Strategy.RollingUpdate.MaxSurge, &intstr.IntOrString{Type: intstr.String, StrVal: "20%"})).Should(BeTrue())
Expect(reflect.DeepEqual(fetch.Spec.Strategy.RollingUpdate.MaxUnavailable, &intstr.IntOrString{Type: intstr.Int, IntVal: 1})).Should(BeTrue())
Expect(fetch.Spec.MinReadySeconds).Should(Equal(int32(0)))
Expect(*fetch.Spec.ProgressDeadlineSeconds).Should(Equal(int32(600)))
}
func getControlInfo(release *v1beta1.BatchRelease) string {
owner, _ := json.Marshal(metav1.NewControllerRef(release, release.GetObjectKind().GroupVersionKind()))
return string(owner)
}
func makeCanaryReplicaSets(d client.Object) client.Object {
deploy := d.(*apps.Deployment)
labels := deploy.Spec.Selector.DeepCopy().MatchLabels
labels[apps.DefaultDeploymentUniqueLabelKey] = util.ComputeHash(&deploy.Spec.Template, nil)
return &apps.ReplicaSet{
TypeMeta: metav1.TypeMeta{
APIVersion: apps.SchemeGroupVersion.String(),
Kind: "ReplicaSet",
},
ObjectMeta: metav1.ObjectMeta{
Name: deploy.Name + rand.String(5),
Namespace: deploy.Namespace,
UID: uuid.NewUUID(),
Labels: labels,
OwnerReferences: []metav1.OwnerReference{
*metav1.NewControllerRef(deploy, deploy.GroupVersionKind()),
},
CreationTimestamp: metav1.Now(),
},
Spec: apps.ReplicaSetSpec{
Replicas: deploy.Spec.Replicas,
Selector: deploy.Spec.Selector.DeepCopy(),
Template: *deploy.Spec.Template.DeepCopy(),
},
}
}
func makeStableReplicaSets(d client.Object) client.Object {
deploy := d.(*apps.Deployment)
stableTemplate := deploy.Spec.Template.DeepCopy()
stableTemplate.Spec.Containers = containers("v1")
labels := deploy.Spec.Selector.DeepCopy().MatchLabels
labels[apps.DefaultDeploymentUniqueLabelKey] = util.ComputeHash(stableTemplate, nil)
return &apps.ReplicaSet{
TypeMeta: metav1.TypeMeta{
APIVersion: apps.SchemeGroupVersion.String(),
Kind: "ReplicaSet",
},
ObjectMeta: metav1.ObjectMeta{
Name: deploy.Name + rand.String(5),
Namespace: deploy.Namespace,
UID: uuid.NewUUID(),
Labels: labels,
OwnerReferences: []metav1.OwnerReference{
*metav1.NewControllerRef(deploy, deploy.GroupVersionKind()),
},
CreationTimestamp: metav1.NewTime(time.Now().Add(-time.Hour)),
},
Spec: apps.ReplicaSetSpec{
Replicas: deploy.Spec.Replicas,
Selector: deploy.Spec.Selector.DeepCopy(),
Template: *stableTemplate,
},
}
}
func containers(version string) []corev1.Container {
return []corev1.Container{
{
Name: "busybox",
Image: fmt.Sprintf("busybox:%v", version),
},
}
}
func getStableWithReady(workload client.Object, version string) client.Object {
switch workload.(type) {
case *apps.Deployment:
deploy := workload.(*apps.Deployment)
d := deploy.DeepCopy()
d.Spec.Paused = true
d.ResourceVersion = strconv.Itoa(rand.Intn(100000000000))
d.Spec.Template.Spec.Containers = containers(version)
d.Status.ObservedGeneration = deploy.Generation
return d
case *kruiseappsv1alpha1.CloneSet:
clone := workload.(*kruiseappsv1alpha1.CloneSet)
c := clone.DeepCopy()
c.ResourceVersion = strconv.Itoa(rand.Intn(100000000000))
c.Spec.UpdateStrategy.Paused = true
c.Spec.UpdateStrategy.Partition = &intstr.IntOrString{Type: intstr.String, StrVal: "100%"}
c.Spec.Template.Spec.Containers = containers(version)
c.Status.ObservedGeneration = clone.Generation
return c
}
return nil
}
func retryFunction(limit int, f func() error) (err error) {
for i := limit; i >= 0; i-- {
if err = f(); err == nil {
return nil
}
}
return err
}