Files
kubevela/test/e2e-test/rollout_test.go
T
wyikeandGitHub 13fc4e2b25 rollout controller find sourceRevision (#1989)
* rollout controller use last target as source and add whole process e2e-test

* modify makefile use regex include all rollout related test

* add example

* fix golint

* more wait time

* pending pod may let rollout stock in rollingBatches

* fix docs and tests comments

modify docs example

modify cue and yaml

* less pod in test avoid pod pending
2021-08-03 20:27:11 +08:00

367 lines
13 KiB
Go

/*
Copyright 2021 The KubeVela 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 controllers_test
import (
"context"
"fmt"
"time"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"github.com/oam-dev/kubevela/apis/standard.oam.dev/v1alpha1"
kruise "github.com/openkruise/kruise-api/apps/v1alpha1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/pointer"
"github.com/oam-dev/kubevela/pkg/controller/utils"
appsv1 "k8s.io/api/apps/v1"
"github.com/oam-dev/kubevela/pkg/oam"
oamcomm "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/oam/util"
"github.com/oam-dev/kubevela/pkg/utils/common"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
var _ = Describe("rollout related e2e-test,Cloneset component rollout tests", func() {
ctx := context.Background()
var namespaceName, compnentName, rolloutName string
var ns corev1.Namespace
var app v1beta1.Application
//var kc kruise.CloneSet
var rollout v1alpha1.Rollout
var kc kruise.CloneSet
createNamespace := func() {
ns = corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: namespaceName,
},
}
// delete the namespaceName with all its resources
Eventually(
func() error {
return k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationForeground))
},
time.Second*120, time.Millisecond*500).Should(SatisfyAny(BeNil(), &util.NotFoundMatcher{}))
By("make sure all the resources are removed")
objectKey := client.ObjectKey{
Name: namespaceName,
}
res := &corev1.Namespace{}
Eventually(
func() error {
return k8sClient.Get(ctx, objectKey, res)
},
time.Second*120, time.Millisecond*500).Should(&util.NotFoundMatcher{})
Eventually(
func() error {
return k8sClient.Create(ctx, &ns)
},
time.Second*3, time.Millisecond*300).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
}
CreateClonesetDef := func() {
By("Install CloneSet based componentDefinition")
var cd v1beta1.ComponentDefinition
Expect(common.ReadYamlToObject("testdata/rollout/cloneset/clonesetDefinition.yaml", &cd)).Should(BeNil())
// create the componentDefinition if not exist
Eventually(
func() error {
return k8sClient.Create(ctx, &cd)
},
time.Second*3, time.Millisecond*300).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
}
applySourceApp := func(source string) {
By("Apply an application")
var newApp v1beta1.Application
Expect(common.ReadYamlToObject("testdata/rollout/cloneset/"+source, &newApp)).Should(BeNil())
newApp.Namespace = namespaceName
Eventually(func() error {
return k8sClient.Create(ctx, &newApp)
}, 10*time.Second, 500*time.Millisecond).Should(Succeed())
By("Get Application latest status")
Eventually(
func() *oamcomm.Revision {
k8sClient.Get(ctx, client.ObjectKey{Namespace: namespaceName, Name: newApp.Name}, &app)
if app.Status.LatestRevision != nil {
return app.Status.LatestRevision
}
return nil
},
time.Second*30, time.Millisecond*500).ShouldNot(BeNil())
}
verifyRolloutSucceeded := func(compRevName string) {
By("Wait for the rollout to succeed")
Eventually(
func() error {
rollout = v1alpha1.Rollout{}
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespaceName, Name: rolloutName}, &rollout)
if err != nil {
return err
}
if rollout.Status.LastUpgradedTargetRevision != compRevName {
return fmt.Errorf("component revision name error %s", compRevName)
}
if rollout.Status.RollingState != v1alpha1.RolloutSucceedState {
return fmt.Errorf("rollout isn't succeed acctauly %s", rollout.Status.RollingState)
}
return nil
},
time.Second*300, 300*time.Millisecond).Should(BeNil())
Expect(rollout.Status.UpgradedReadyReplicas).Should(BeEquivalentTo(rollout.Status.RolloutTargetSize))
Expect(rollout.Status.UpgradedReplicas).Should(BeEquivalentTo(rollout.Status.RolloutTargetSize))
clonesetName := rollout.Spec.ComponentName
By("Wait for resourceTracker to resume the control of cloneset")
Eventually(
func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespaceName, Name: clonesetName}, &kc)
if err != nil {
return err
}
if kc.Status.UpdatedReplicas != *kc.Spec.Replicas {
return fmt.Errorf("expect cloneset updated replicas %d, but got %d",
kc.Status.UpdatedReplicas, *kc.Spec.Replicas)
}
return nil
},
time.Second*60, time.Millisecond*500).Should(BeNil())
// make sure all pods are upgraded
image := kc.Spec.Template.Spec.Containers[0].Image
podList := corev1.PodList{}
Eventually(func() error {
if err := k8sClient.List(ctx, &podList, client.MatchingLabels(kc.Spec.Template.Labels),
client.InNamespace(namespaceName)); err != nil {
return err
}
if len(podList.Items) != int(*kc.Spec.Replicas) {
return fmt.Errorf("expect pod numbers %q, got %q", int(*kc.Spec.Replicas), len(podList.Items))
}
for _, pod := range podList.Items {
gotImage := pod.Spec.Containers[0].Image
if gotImage != image {
return fmt.Errorf("expect pod container image %q, got %q", image, gotImage)
}
if pod.Status.Phase != corev1.PodRunning {
return fmt.Errorf("expect pod phase %q, got %q", corev1.PodRunning, pod.Status.Phase)
}
}
return nil
}, 60*time.Second, 500*time.Millisecond).Should(Succeed())
}
initialScale := func() {
By("Apply the component scale to deploy")
var newRollout v1alpha1.Rollout
Expect(common.ReadYamlToObject("testdata/rollout/cloneset/comp-rollout.yaml", &newRollout)).Should(BeNil())
newRollout.Namespace = namespaceName
compRevName := utils.ConstructRevisionName(compnentName, 1)
newRollout.Spec.TargetRevisionName = compRevName
Expect(k8sClient.Create(ctx, &newRollout)).Should(BeNil())
rolloutName = newRollout.Name
verifyRolloutSucceeded(compRevName)
}
updateApp := func(target string, revision int) {
By("Update the application to target spec")
var targetApp v1beta1.Application
Expect(common.ReadYamlToObject("testdata/rollout/cloneset/"+target, &targetApp)).Should(BeNil())
Eventually(
func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespaceName, Name: app.Name}, &app)
if err != nil {
return err
}
if app.Status.Phase != oamcomm.ApplicationRunning {
return fmt.Errorf("application is still last generating apprev ")
}
var appRevList = &v1beta1.ApplicationRevisionList{}
err = k8sClient.List(ctx, appRevList, client.InNamespace(namespaceName),
client.MatchingLabels(map[string]string{oam.LabelAppName: targetApp.Name}))
if err != nil {
return err
}
if len(appRevList.Items) != revision-1 {
return fmt.Errorf("apprev mismatch actually %d", len(appRevList.Items))
}
app.Spec = targetApp.DeepCopy().Spec
return k8sClient.Update(ctx, app.DeepCopy())
}, time.Second*15, time.Millisecond*500).Should(Succeed())
By("Get Application Revision created with more than one")
Eventually(
func() error {
var appRevList = &v1beta1.ApplicationRevisionList{}
err := k8sClient.List(ctx, appRevList, client.InNamespace(namespaceName),
client.MatchingLabels(map[string]string{oam.LabelAppName: targetApp.Name}))
if err != nil {
return err
}
if len(appRevList.Items) != revision {
return fmt.Errorf("appRevision number mismatch acctually %d", len(appRevList.Items))
}
return nil
},
time.Second*30, time.Millisecond*300).Should(BeNil())
}
BeforeEach(func() {
By("Start to run a test, clean up previous resources")
namespaceName = randomNamespaceName("comp-rollout-e2e-test")
CreateClonesetDef()
createNamespace()
compnentName = "metrics-provider"
})
AfterEach(func() {
By("Clean up resources after a test")
Eventually(func() error {
err := k8sClient.Delete(ctx, &app)
if err == nil || apierrors.IsNotFound(err) {
return nil
}
return err
}, 15*time.Second, 300*time.Microsecond).Should(BeNil())
Eventually(func() error {
err := k8sClient.Delete(ctx, &rollout)
if err == nil || apierrors.IsNotFound(err) {
return nil
}
return err
}, 15*time.Second, 300*time.Microsecond).Should(BeNil())
By(fmt.Sprintf("Delete the entire namespaceName %s", ns.Name))
// delete the namespaceName with all its resources
Expect(k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationBackground))).Should(BeNil())
})
It("Test component rollout cloneset", func() {
var err error
applySourceApp("app-source.yaml")
updateApp("app-target.yaml", 2)
By("verify generate two controller revisions")
ctlRevList := appsv1.ControllerRevisionList{}
Eventually(func() error {
if err = k8sClient.List(ctx, &ctlRevList, client.InNamespace(namespaceName),
client.MatchingLabels(map[string]string{oam.LabelControllerRevisionComponent: compnentName})); err != nil {
return err
}
if len(ctlRevList.Items) < 2 {
return fmt.Errorf("component revision missmatch acctually %d", len(ctlRevList.Items))
}
return nil
}, time.Second*30, 300*time.Millisecond).Should(BeNil())
By("initial scale component revision")
initialScale()
clonesetName := rollout.Spec.ComponentName
By("rollout to compRev 2")
Eventually(func() error {
checkRollout := new(v1alpha1.Rollout)
if err = k8sClient.Get(ctx, types.NamespacedName{Namespace: namespaceName, Name: rolloutName}, checkRollout); err != nil {
return err
}
// we needn't specify sourceRevision, rollout use lastTarget as source
checkRollout.Spec.TargetRevisionName = utils.ConstructRevisionName(compnentName, 2)
checkRollout.Spec.RolloutPlan.BatchPartition = pointer.Int32Ptr(0)
if err = k8sClient.Update(ctx, checkRollout); err != nil {
return err
}
return nil
}, 30*time.Second, 15*time.Millisecond).Should(BeNil())
By("verify rollout pause in first batch")
checkRollout := new(v1alpha1.Rollout)
Eventually(func() error {
checkRollout = new(v1alpha1.Rollout)
if err = k8sClient.Get(ctx, types.NamespacedName{Namespace: namespaceName, Name: rolloutName}, checkRollout); err != nil {
return err
}
if checkRollout.Status.LastUpgradedTargetRevision != utils.ConstructRevisionName(compnentName, 2) {
return fmt.Errorf("last target error")
}
if checkRollout.Status.RollingState != v1alpha1.RollingInBatchesState {
return fmt.Errorf("rollout state error")
}
if checkRollout.Status.CurrentBatch != 0 {
return fmt.Errorf("current batch missmatch")
}
return nil
}, 60*time.Second, 300*time.Millisecond).Should(BeNil())
Eventually(
func() error {
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespaceName, Name: clonesetName}, &kc)
if err != nil {
return err
}
if len(kc.OwnerReferences) != 1 {
return fmt.Errorf("cloneset owner missmatch")
}
if kc.OwnerReferences[0].UID != checkRollout.UID || kc.OwnerReferences[0].Kind != v1alpha1.RolloutKind {
return fmt.Errorf("cloneset owner missmatch not rollout Uid %s", checkRollout.UID)
}
if kc.Status.UpdatedReplicas != 3 {
return fmt.Errorf("expect cloneset updated replicas %d, but got %d",
3, *kc.Spec.Replicas)
}
return nil
},
time.Second*120, time.Millisecond*500).Should(BeNil())
Eventually(func() error {
checkRollout := new(v1alpha1.Rollout)
if err = k8sClient.Get(ctx, types.NamespacedName{Namespace: namespaceName, Name: rolloutName}, checkRollout); err != nil {
return err
}
checkRollout.Spec.RolloutPlan.BatchPartition = nil
if err = k8sClient.Update(ctx, checkRollout); err != nil {
return err
}
return nil
}, 30*time.Second, 15*time.Millisecond).Should(BeNil())
verifyRolloutSucceeded(utils.ConstructRevisionName(compnentName, 2))
By("continue rollout forward")
Eventually(func() error {
checkRollout := new(v1alpha1.Rollout)
if err = k8sClient.Get(ctx, types.NamespacedName{Namespace: namespaceName, Name: rolloutName}, checkRollout); err != nil {
return err
}
// we needn't specify sourceRevision, rollout use lastTarget as source
checkRollout.Spec.TargetRevisionName = utils.ConstructRevisionName(compnentName, 1)
if err = k8sClient.Update(ctx, checkRollout); err != nil {
return err
}
return nil
}, 30*time.Second, 15*time.Millisecond).Should(BeNil())
verifyRolloutSucceeded(utils.ConstructRevisionName(compnentName, 1))
})
})