remove appContext from app/appRollout controller (#1774)

* refine  assemble and dispatch

Signed-off-by: roy wang <seiwy2010@gmail.com>

* remove app context in app controller

modify clean up app revision

remove old resource tracker related logic

fix unit tests

Signed-off-by: roy wang <seiwy2010@gmail.com>

* fix e2e-test

- get rid of appCtx in test cases
- fix test cases according other logic changes in app controller

remove whole appcontext_test.go file

disable rollout related e2e test provisionally

disable resource tracker related e2e test provisionally

Signed-off-by: roy wang <seiwy2010@gmail.com>

* add finalizer logic for app controller

Signed-off-by: roywang <seiwy2010@gmail.com>

* add new apply option MustBeControllableByAny

make dispatch idempotent

Signed-off-by: roywang <seiwy2010@gmail.com>

* refactor rollout

* fix rollout finalize succeed

Signed-off-by: roywang <seiwy2010@gmail.com>

* add update trait and gc test

fix lint

* fix flaky e2e test

Signed-off-by: roywang <seiwy2010@gmail.com>

* fix comment

* fix comments and add sourceRevision dispatch

delete useless

Signed-off-by: Yue Wang <seiwy2010@gmail.com>

* fix app finalizer backward compatible

Signed-off-by: roywang <seiwy2010@gmail.com>

* fix backward compatability for deprecation of appContext

add unit test for apply option

add e2e test

Signed-off-by: Yue Wang <seiwy2010@gmail.com>

* fix app controller unit test

Signed-off-by: Yue Wang <seiwy2010@gmail.com>

* refine app controller apply logic

Signed-off-by: Yue Wang <seiwy2010@gmail.com>

* fix e2e test of resource tracker

fix e2e test of rollout plan

fix flaky e2e tests

Signed-off-by: Yue Wang <seiwy2010@gmail.com>

* refine comments and remove useless codes

Signed-off-by: Yue Wang <seiwy2010@gmail.com>

* disable appCtx controller

add Component handler into app controller

Signed-off-by: Yue Wang <seiwy2010@gmail.com>

Co-authored-by: wangyike <wangyike.wyk@alibaba-inc.com>
This commit is contained in:
Yue Wang
2021-06-12 15:46:32 +09:00
committed by GitHub
parent 9de6aea5ab
commit 889e38e984
41 changed files with 1983 additions and 2098 deletions

View File

@@ -33,9 +33,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
apicommon "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
"github.com/oam-dev/kubevela/apis/standard.oam.dev/v1alpha1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/controller/utils"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
@@ -137,8 +135,7 @@ var _ = Describe("Cloneset based app embed rollout tests", func() {
Expect(k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationBackground))).Should(BeNil())
})
verifyRolloutSucceeded := func(targetAppContextName string, cpu string) {
By(fmt.Sprintf("Wait for the rollout `%s` to succeed", targetAppContextName))
verifyRolloutSucceeded := func(targetAppRevisionName string, cpu string) {
Eventually(
func() error {
app = v1beta1.Application{}
@@ -155,23 +152,6 @@ var _ = Describe("Cloneset based app embed rollout tests", func() {
Expect(app.Status.Rollout.UpgradedReplicas).Should(BeEquivalentTo(app.Status.Rollout.RolloutTargetSize))
clonesetName := app.Spec.Components[0].Name
Expect(app.Status.Phase).Should(BeEquivalentTo(apicommon.ApplicationRunning))
By("Verify AppContext rolling status")
appContext := &v1alpha2.ApplicationContext{}
Eventually(
func() error {
if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespaceName, Name: targetAppContextName}, appContext); err != nil {
return err
}
if appContext.Status.RollingStatus != types.RollingCompleted {
return fmt.Errorf("appcontext %s rolling state mismatch actualy %s", targetAppContextName, appContext.Status.RollingStatus)
}
owner := metav1.GetControllerOf(appContext)
if owner.Name != appName && owner.Kind != app.Kind && owner.APIVersion != app.APIVersion {
return fmt.Errorf("appcontext owner mismatch")
}
return nil
},
time.Second*120, time.Microsecond*300).Should(BeNil())
By("Verify cloneset status")
var clonesetOwner *metav1.OwnerReference
@@ -181,16 +161,22 @@ var _ = Describe("Cloneset based app embed rollout tests", func() {
return err
}
clonesetOwner = metav1.GetControllerOf(&kc)
if clonesetOwner.Kind != v1alpha2.ApplicationContextKind {
return fmt.Errorf("cloneset owner mismatch actually %s", v1alpha2.ApplicationContextKind)
if clonesetOwner == nil {
return fmt.Errorf("cloneset don't have any controller owner")
}
if clonesetOwner.Kind != v1beta1.ResourceTrackerKind {
return fmt.Errorf("cloneset owner mismatch wants %s actually %s", v1beta1.ResourceTrackerKind, clonesetOwner.Kind)
}
if kc.Status.UpdatedReplicas != *kc.Spec.Replicas {
return fmt.Errorf("upgraded pod number error")
}
resourceTrackerName := fmt.Sprintf("%s-%s", targetAppRevisionName, app.Namespace)
if clonesetOwner.Name != resourceTrackerName {
return fmt.Errorf("resourceTracker haven't take back controller owner")
}
return nil
},
time.Second*30, time.Millisecond*500).Should(BeNil())
Expect(clonesetOwner.Name).Should(BeEquivalentTo(targetAppContextName))
By("Verify pod status")
Eventually(func() error {
podList := corev1.PodList{}

View File

@@ -59,11 +59,11 @@ var _ = Describe("Test application cross namespace resource", func() {
Eventually(func() error {
ns := new(corev1.Namespace)
return k8sClient.Get(ctx, types.NamespacedName{Name: namespace}, ns)
}, time.Second*60, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*300).Should(BeNil())
Eventually(func() error {
ns := new(corev1.Namespace)
return k8sClient.Get(ctx, types.NamespacedName{Name: crossNamespace}, ns)
}, time.Second*60, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*300).Should(BeNil())
})
AfterEach(func() {
@@ -253,7 +253,7 @@ var _ = Describe("Test application cross namespace resource", func() {
}, 20*time.Second, 2*time.Second).Should(SatisfyAll(&util.NotFoundMatcher{}))
})
It("Test application have cross-namespace workload", func() {
It("Test application have cross-namespace workload", func() {
// install component definition
crossCdJson, _ := yaml.YAMLToJSON([]byte(fmt.Sprintf(crossCompDefYaml, namespace, crossNamespace)))
ccd := new(v1beta1.ComponentDefinition)
@@ -271,7 +271,7 @@ var _ = Describe("Test application cross namespace resource", func() {
},
Spec: v1beta1.ApplicationSpec{
Components: []v1beta1.ApplicationComponent{
v1beta1.ApplicationComponent{
{
Name: componentName,
Type: "cross-worker",
Properties: runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
@@ -287,27 +287,20 @@ var _ = Describe("Test application cross namespace resource", func() {
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app); err != nil {
return fmt.Errorf("app not found %v", err)
}
if err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker); err != nil {
if err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 1), resourceTracker); err != nil {
return err
}
if app.Status.Phase != common.ApplicationRunning {
return fmt.Errorf("application status is not running")
}
if app.Status.ResourceTracker == nil || app.Status.ResourceTracker.UID != resourceTracker.UID {
return fmt.Errorf("appication status error ")
}
return nil
}, time.Second*300, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*300).Should(BeNil())
By("check resource is generated correctly")
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app)).Should(BeNil())
var workload appsv1.Deployment
Eventually(func() error {
appContext := &v1alpha2.ApplicationContext{}
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, appContext); err != nil {
return fmt.Errorf("cannot generate AppContext %v", err)
}
checkRt := new(v1beta1.ResourceTracker)
if err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), checkRt); err != nil {
if err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 1), checkRt); err != nil {
return err
}
component := &v1alpha2.Component{}
@@ -339,14 +332,13 @@ var _ = Describe("Test application cross namespace resource", func() {
return fmt.Errorf("resourceTracker status recode trackedResource name mismatch recorded %s, actually %s", checkRt.Status.TrackedResources[0].Name, workload.Name)
}
return nil
}, time.Second*50, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*300).Should(BeNil())
By("deleting application will remove resourceTracker and related workload will be removed")
time.Sleep(3 * time.Second) // wait informer cache to be synced
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app)).Should(BeNil())
Expect(k8sClient.Delete(ctx, app)).Should(BeNil())
Eventually(func() error {
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker)
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 1), resourceTracker)
if err == nil {
return fmt.Errorf("resourceTracker still exist")
}
@@ -361,7 +353,7 @@ var _ = Describe("Test application cross namespace resource", func() {
return err
}
return nil
}, time.Second*30, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*300).Should(BeNil())
})
It("Test update application by add a cross namespace trait resource", func() {
@@ -389,7 +381,7 @@ var _ = Describe("Test application cross namespace resource", func() {
},
Spec: v1beta1.ApplicationSpec{
Components: []v1beta1.ApplicationComponent{
v1beta1.ApplicationComponent{
{
Name: componentName,
Type: "normal-worker",
Properties: runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
@@ -421,18 +413,14 @@ var _ = Describe("Test application cross namespace resource", func() {
return fmt.Errorf("error workload number %v", err)
}
workload := depolys.Items[0]
if len(workload.OwnerReferences) != 1 || workload.OwnerReferences[0].Kind != v1alpha2.ApplicationContextKind {
if len(workload.OwnerReferences) != 1 || workload.OwnerReferences[0].Kind != v1beta1.ResourceTrackerKind {
return fmt.Errorf("workload owneRefernece err")
}
err = k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker)
if err == nil {
return fmt.Errorf("resourceTracker should not be created")
}
if !apierrors.IsNotFound(err) {
if err = k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 1), resourceTracker); err != nil {
return err
}
return nil
}, time.Second*60, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*500).Should(BeNil())
Eventually(func() error {
err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app)
@@ -440,13 +428,13 @@ var _ = Describe("Test application cross namespace resource", func() {
return err
}
app.Spec.Components[0].Traits = []v1beta1.ApplicationTrait{
v1beta1.ApplicationTrait{
{
Type: "cross-scaler",
Properties: runtime.RawExtension{Raw: []byte(`{"replicas": 1}`)},
},
}
return k8sClient.Update(ctx, app)
}, time.Second*30, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*300).Should(BeNil())
By("add a cross namespace trait, check resourceTracker and trait status")
Eventually(func() error {
@@ -457,7 +445,7 @@ var _ = Describe("Test application cross namespace resource", func() {
if app.Status.Phase != common.ApplicationRunning {
return fmt.Errorf("application status not running")
}
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker)
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 2), resourceTracker)
if err != nil {
return fmt.Errorf("resourceTracker not generated %v", err)
}
@@ -476,14 +464,11 @@ var _ = Describe("Test application cross namespace resource", func() {
if len(trait.OwnerReferences) != 1 || trait.OwnerReferences[0].UID != resourceTracker.UID {
return fmt.Errorf("trait owner reference missmatch")
}
if len(resourceTracker.Status.TrackedResources) != 1 {
return fmt.Errorf("resourceTracker status recode trackedResource length missmatch")
}
if resourceTracker.Status.TrackedResources[0].Name != trait.Name {
return fmt.Errorf("resourceTracker status recode trackedResource name mismatch recorded %s, actually %s", resourceTracker.Status.TrackedResources[0].Name, trait.Name)
if len(resourceTracker.Status.TrackedResources) != 2 {
return fmt.Errorf("expect track %q resources, but got %q", 2, len(resourceTracker.Status.TrackedResources))
}
return nil
}, time.Second*60, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*500).Should(BeNil())
})
It("Test update application by delete a cross namespace trait resource", func() {
@@ -511,12 +496,12 @@ var _ = Describe("Test application cross namespace resource", func() {
},
Spec: v1beta1.ApplicationSpec{
Components: []v1beta1.ApplicationComponent{
v1beta1.ApplicationComponent{
{
Name: componentName,
Type: "normal-worker",
Properties: runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
Traits: []v1beta1.ApplicationTrait{
v1beta1.ApplicationTrait{
{
Type: "cross-scaler",
Properties: runtime.RawExtension{Raw: []byte(`{"replicas": 1}`)},
},
@@ -526,7 +511,6 @@ var _ = Describe("Test application cross namespace resource", func() {
},
}
Expect(k8sClient.Create(ctx, app)).Should(BeNil())
time.Sleep(3 * time.Second) // give informer cache to sync
resourceTracker := new(v1beta1.ResourceTracker)
By("create application will create a cross ns trait, and resourceTracker. check those status")
Eventually(func() error {
@@ -537,7 +521,7 @@ var _ = Describe("Test application cross namespace resource", func() {
if app.Status.Phase != common.ApplicationRunning {
return fmt.Errorf("application status not running")
}
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker)
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 1), resourceTracker)
if err != nil {
return fmt.Errorf("error to get resourceTracker %v", err)
}
@@ -556,22 +540,19 @@ var _ = Describe("Test application cross namespace resource", func() {
if len(trait.OwnerReferences) != 1 || trait.OwnerReferences[0].UID != resourceTracker.UID {
return fmt.Errorf("trait owner reference missmatch")
}
if len(resourceTracker.Status.TrackedResources) != 1 {
return fmt.Errorf("resourceTracker status recode trackedResource length missmatch")
}
if resourceTracker.Status.TrackedResources[0].Name != trait.Name {
return fmt.Errorf("resourceTracker status recode trackedResource name mismatch recorded %s, actually %s", resourceTracker.Status.TrackedResources[0].Name, trait.Name)
if len(resourceTracker.Status.TrackedResources) != 2 {
return fmt.Errorf("expect track %q resources, but got %q", 2, len(resourceTracker.Status.TrackedResources))
}
return nil
}, time.Second*60, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*300).Should(BeNil())
By("update application trait by delete cross ns trait, will delete resourceTracker and related trait resource")
By("update application trait by delete cross ns trait")
Eventually(func() error {
app = new(v1beta1.Application)
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app)).Should(BeNil())
app.Spec.Components[0].Traits = []v1beta1.ApplicationTrait{}
return k8sClient.Update(ctx, app)
}, time.Second*30, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*300).Should(BeNil())
fmt.Println(app.ResourceVersion)
Eventually(func() error {
app = new(v1beta1.Application)
@@ -581,9 +562,8 @@ var _ = Describe("Test application cross namespace resource", func() {
if app.Status.Phase != common.ApplicationRunning {
return fmt.Errorf("application status not running")
}
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker)
if err == nil {
return fmt.Errorf("resourceTracker still exist")
if err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 2), resourceTracker); err != nil {
return err
}
mts := new(v1alpha2.ManualScalerTraitList)
opts := []client.ListOption{
@@ -596,11 +576,8 @@ var _ = Describe("Test application cross namespace resource", func() {
if err != nil || len(mts.Items) != 0 {
return fmt.Errorf("cross ns trait still exist")
}
if app.Status.ResourceTracker != nil {
return fmt.Errorf("application status resourceTracker field still exist %s", string(util.JSONMarshal(app.Status.ResourceTracker)))
}
return nil
}, time.Second*60, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*500).Should(BeNil())
})
It("Test application have two different workload", func() {
@@ -629,12 +606,12 @@ var _ = Describe("Test application cross namespace resource", func() {
},
Spec: v1beta1.ApplicationSpec{
Components: []v1beta1.ApplicationComponent{
v1beta1.ApplicationComponent{
{
Name: component1Name,
Type: "normal-worker",
Properties: runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
},
v1beta1.ApplicationComponent{
{
Name: component2Name,
Type: "cross-worker",
Properties: runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
@@ -644,7 +621,6 @@ var _ = Describe("Test application cross namespace resource", func() {
}
Expect(k8sClient.Create(ctx, app)).Should(BeNil())
time.Sleep(3 * time.Second) // give informer cache to sync
resourceTracker := new(v1beta1.ResourceTracker)
By("create application will generate two workload, and generate resourceTracker")
@@ -656,7 +632,7 @@ var _ = Describe("Test application cross namespace resource", func() {
if app.Status.Phase != common.ApplicationRunning {
return fmt.Errorf("application status not running")
}
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker)
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 1), resourceTracker)
if err != nil {
return fmt.Errorf("error to generate resourceTracker %v", err)
}
@@ -678,7 +654,7 @@ var _ = Describe("Test application cross namespace resource", func() {
return fmt.Errorf("failed generate same namespace workload")
}
sameDeplpoy := same.Items[0]
if len(sameDeplpoy.OwnerReferences) != 1 || sameDeplpoy.OwnerReferences[0].Kind != v1alpha2.ApplicationContextKind {
if len(sameDeplpoy.OwnerReferences) != 1 || sameDeplpoy.OwnerReferences[0].Kind != v1beta1.ResourceTrackerKind {
return fmt.Errorf("same ns deploy have error ownerReference")
}
err = k8sClient.List(ctx, cross, crossOpts...)
@@ -689,24 +665,18 @@ var _ = Describe("Test application cross namespace resource", func() {
if len(sameDeplpoy.OwnerReferences) != 1 || crossDeplpoy.OwnerReferences[0].UID != resourceTracker.UID {
return fmt.Errorf("same ns deploy have error ownerReference")
}
if app.Status.ResourceTracker == nil || app.Status.ResourceTracker.UID != resourceTracker.UID {
return fmt.Errorf("app status resourceTracker error")
}
if len(resourceTracker.Status.TrackedResources) != 1 {
return fmt.Errorf("resourceTracker status recode trackedResource length missmatch")
}
if resourceTracker.Status.TrackedResources[0].Name != crossDeplpoy.Name {
return fmt.Errorf("resourceTracker status recode trackedResource name mismatch recorded %s, actually %s", resourceTracker.Status.TrackedResources[0].Name, crossDeplpoy.Name)
if len(resourceTracker.Status.TrackedResources) != 2 {
return fmt.Errorf("expect track %q resources, but got %q", 2, len(resourceTracker.Status.TrackedResources))
}
return nil
}, time.Second*60, time.Microsecond*300).Should(BeNil())
By("update application by delete cross namespace workload, resource tracker will be deleted, then check app status")
}, time.Second*5, time.Millisecond*500).Should(BeNil())
By("update application by delete cross namespace workload")
Eventually(func() error {
app = new(v1beta1.Application)
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app)).Should(BeNil())
app.Spec.Components = app.Spec.Components[:1] // delete a component
return k8sClient.Update(ctx, app)
}, time.Second*30, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*300).Should(BeNil())
Eventually(func() error {
app = new(v1beta1.Application)
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app); err != nil {
@@ -715,9 +685,8 @@ var _ = Describe("Test application cross namespace resource", func() {
if app.Status.Phase != common.ApplicationRunning {
return fmt.Errorf("application status not running")
}
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker)
if err == nil {
return fmt.Errorf("resourceTracker still exist")
if err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 2), resourceTracker); err != nil {
return err
}
sameOpts := []client.ListOption{
client.InNamespace(namespace),
@@ -737,18 +706,15 @@ var _ = Describe("Test application cross namespace resource", func() {
return fmt.Errorf("failed generate same namespace workload")
}
sameDeplpoy := same.Items[0]
if len(sameDeplpoy.OwnerReferences) != 1 || sameDeplpoy.OwnerReferences[0].Kind != v1alpha2.ApplicationContextKind {
if len(sameDeplpoy.OwnerReferences) != 1 || sameDeplpoy.OwnerReferences[0].Kind != v1beta1.ResourceTrackerKind {
return fmt.Errorf("same ns deploy have error ownerReference")
}
err = k8sClient.List(ctx, cross, crossOpts...)
if err != nil || len(cross.Items) != 0 {
return fmt.Errorf("error : cross namespace workload still exist")
}
if app.Status.ResourceTracker != nil {
return fmt.Errorf("error app status resourceTracker")
}
return nil
}, time.Second*60, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*300).Should(BeNil())
})
It("Update a cross namespace workload of application", func() {
@@ -769,7 +735,7 @@ var _ = Describe("Test application cross namespace resource", func() {
},
Spec: v1beta1.ApplicationSpec{
Components: []v1beta1.ApplicationComponent{
v1beta1.ApplicationComponent{
{
Name: componentName,
Type: "cross-worker",
Properties: runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
@@ -785,25 +751,18 @@ var _ = Describe("Test application cross namespace resource", func() {
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app); err != nil {
return fmt.Errorf("app not found %v", err)
}
if err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker); err != nil {
if err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 1), resourceTracker); err != nil {
return err
}
if app.Status.Phase != common.ApplicationRunning {
return fmt.Errorf("application status is not running")
}
if app.Status.ResourceTracker == nil || app.Status.ResourceTracker.UID != resourceTracker.UID {
return fmt.Errorf("appication status error ")
}
return nil
}, time.Second*600, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*300).Should(BeNil())
By("check resource is generated correctly")
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app)).Should(BeNil())
var workload appsv1.Deployment
Eventually(func() error {
appContext := &v1alpha2.ApplicationContext{}
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, appContext); err != nil {
return fmt.Errorf("cannot generate AppContext %v", err)
}
component := &v1alpha2.Component{}
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: componentName}, component); err != nil {
return fmt.Errorf("cannot generate component %v", err)
@@ -823,7 +782,7 @@ var _ = Describe("Test application cross namespace resource", func() {
return fmt.Errorf("error workload number %v", err)
}
workload = depolys.Items[0]
if err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker); err != nil {
if err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 1), resourceTracker); err != nil {
return err
}
if len(workload.OwnerReferences) != 1 || workload.OwnerReferences[0].UID != resourceTracker.UID {
@@ -833,13 +792,10 @@ var _ = Describe("Test application cross namespace resource", func() {
return fmt.Errorf("container image not match")
}
if len(resourceTracker.Status.TrackedResources) != 1 {
return fmt.Errorf("resourceTracker status recode trackedResource length missmatch")
}
if resourceTracker.Status.TrackedResources[0].Name != workload.Name {
return fmt.Errorf("resourceTracker status recode trackedResource name mismatch recorded %s, actually %s", resourceTracker.Status.TrackedResources[0].Name, workload.Name)
return fmt.Errorf("expect track %q resources, but got %q", 1, len(resourceTracker.Status.TrackedResources))
}
return nil
}, time.Second*50, time.Microsecond*300).Should(BeNil())
}, time.Second*50, time.Millisecond*300).Should(BeNil())
By("update application and check resource status")
Eventually(func() error {
@@ -854,13 +810,9 @@ var _ = Describe("Test application cross namespace resource", func() {
return err
}
return nil
}, time.Second*60, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*300).Should(BeNil())
Eventually(func() error {
appContext := &v1alpha2.ApplicationContext{}
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, appContext); err != nil {
return fmt.Errorf("cannot generate AppContext %v", err)
}
component := &v1alpha2.Component{}
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: componentName}, component); err != nil {
return fmt.Errorf("cannot generate component %v", err)
@@ -868,6 +820,12 @@ var _ = Describe("Test application cross namespace resource", func() {
if component.ObjectMeta.Labels[oam.LabelAppName] != appName {
return fmt.Errorf("component error label ")
}
if err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 2), resourceTracker); err != nil {
return err
}
if len(resourceTracker.Status.TrackedResources) != 1 {
return fmt.Errorf("expect track %q resources, but got %q", 1, len(resourceTracker.Status.TrackedResources))
}
depolys := new(appsv1.DeploymentList)
opts := []client.ListOption{
client.InNamespace(crossNamespace),
@@ -886,24 +844,14 @@ var _ = Describe("Test application cross namespace resource", func() {
if workload.Spec.Template.Spec.Containers[0].Image != "nginx" {
return fmt.Errorf("container image not match")
}
if err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker); err != nil {
return err
}
if len(resourceTracker.Status.TrackedResources) != 1 {
return fmt.Errorf("resourceTracker status recode trackedResource length missmatch")
}
if resourceTracker.Status.TrackedResources[0].Name != workload.Name {
return fmt.Errorf("resourceTracker status recode trackedResource name mismatch recorded %s, actually %s", resourceTracker.Status.TrackedResources[0].Name, workload.Name)
}
return nil
}, time.Second*60, time.Microsecond*1000).Should(BeNil())
}, time.Second*5, time.Millisecond*500).Should(BeNil())
By("deleting application will remove resourceTracker and related workload will be removed")
time.Sleep(3 * time.Second) // wait informer cache to be synced
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app)).Should(BeNil())
Expect(k8sClient.Delete(ctx, app)).Should(BeNil())
Eventually(func() error {
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker)
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 2), resourceTracker)
if err == nil {
return fmt.Errorf("resourceTracker still exist")
}
@@ -918,7 +866,7 @@ var _ = Describe("Test application cross namespace resource", func() {
return err
}
return nil
}, time.Second*30, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*500).Should(BeNil())
})
It("Test cross-namespace resource gc logic, delete a cross-ns component", func() {
@@ -943,12 +891,12 @@ var _ = Describe("Test application cross namespace resource", func() {
},
Spec: v1beta1.ApplicationSpec{
Components: []v1beta1.ApplicationComponent{
v1beta1.ApplicationComponent{
{
Name: component1Name,
Type: "cross-worker",
Properties: runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
},
v1beta1.ApplicationComponent{
{
Name: component2Name,
Type: "cross-worker",
Properties: runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
@@ -958,7 +906,6 @@ var _ = Describe("Test application cross namespace resource", func() {
}
Expect(k8sClient.Create(ctx, app)).Should(BeNil())
time.Sleep(3 * time.Second) // give informer cache to sync
resourceTracker := new(v1beta1.ResourceTracker)
By("create application will generate two workload, and generate resourceTracker")
@@ -970,7 +917,7 @@ var _ = Describe("Test application cross namespace resource", func() {
if app.Status.Phase != common.ApplicationRunning {
return fmt.Errorf("application status not running")
}
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker)
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 1), resourceTracker)
if err != nil {
return fmt.Errorf("error to generate resourceTracker %v", err)
}
@@ -994,11 +941,8 @@ var _ = Describe("Test application cross namespace resource", func() {
if len(deploy2.OwnerReferences) != 1 || deploy2.OwnerReferences[0].UID != resourceTracker.UID {
return fmt.Errorf("deploy2 have error ownerReference")
}
if app.Status.ResourceTracker == nil || app.Status.ResourceTracker.UID != resourceTracker.UID {
return fmt.Errorf("app status resourceTracker error")
}
if len(resourceTracker.Status.TrackedResources) != 2 {
return fmt.Errorf("resourceTracker status recode trackedResource length missmatch")
return fmt.Errorf("expect track %q resources, but got %q", 2, len(resourceTracker.Status.TrackedResources))
}
if resourceTracker.Status.TrackedResources[0].Namespace != crossNamespace || resourceTracker.Status.TrackedResources[1].Namespace != crossNamespace {
return fmt.Errorf("resourceTracker recorde namespace mismatch")
@@ -1010,14 +954,14 @@ var _ = Describe("Test application cross namespace resource", func() {
return fmt.Errorf("resourceTracker status recode trackedResource name mismatch recorded %s, actually %s", resourceTracker.Status.TrackedResources[0].Name, deploy2.Name)
}
return nil
}, time.Second*60, time.Microsecond*300).Should(BeNil())
By("update application by delete a cross namespace workload, resource tracker will still exist, then check app status")
}, time.Second*5, time.Millisecond*300).Should(BeNil())
By("update application by delete a cross namespace workload")
Eventually(func() error {
app = new(v1beta1.Application)
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app)).Should(BeNil())
app.Spec.Components = app.Spec.Components[:1] // delete a component
return k8sClient.Update(ctx, app)
}, time.Second*30, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*300).Should(BeNil())
Eventually(func() error {
app = new(v1beta1.Application)
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app); err != nil {
@@ -1026,7 +970,7 @@ var _ = Describe("Test application cross namespace resource", func() {
if app.Status.Phase != common.ApplicationRunning {
return fmt.Errorf("application status not running")
}
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker)
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 2), resourceTracker)
if err != nil {
return fmt.Errorf("failed to get resourceTracker %v", err)
}
@@ -1046,28 +990,22 @@ var _ = Describe("Test application cross namespace resource", func() {
return fmt.Errorf("same ns deploy have error ownerReference")
}
checkRt := new(v1beta1.ResourceTracker)
err = k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), checkRt)
err = k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 2), checkRt)
if err != nil {
return fmt.Errorf("error get resourceTracker")
}
if app.Status.ResourceTracker == nil {
return fmt.Errorf("app status resourceTracker error")
}
if app.Status.ResourceTracker.UID != checkRt.UID {
return fmt.Errorf("error app status resourceTracker UID")
}
if len(checkRt.Status.TrackedResources) != 1 {
return fmt.Errorf("error resourceTracker status trackedResource")
return fmt.Errorf("expect track %q resources, but got %q", 1, len(checkRt.Status.TrackedResources))
}
return nil
}, time.Second*80, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*500).Should(BeNil())
By("deleting application will remove resourceTracker and related resourceTracker will be removed")
By("deleting application will remove resourceTracker")
app = new(v1beta1.Application)
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app)).Should(BeNil())
Expect(k8sClient.Delete(ctx, app)).Should(BeNil())
Eventually(func() error {
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker)
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 2), resourceTracker)
if err == nil {
return fmt.Errorf("resourceTracker still exist")
}
@@ -1075,7 +1013,7 @@ var _ = Describe("Test application cross namespace resource", func() {
return err
}
return nil
}, time.Second*60, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*500).Should(BeNil())
})
It("Test cross-namespace resource gc logic, delete a cross-ns trait", func() {
@@ -1105,12 +1043,12 @@ var _ = Describe("Test application cross namespace resource", func() {
},
Spec: v1beta1.ApplicationSpec{
Components: []v1beta1.ApplicationComponent{
v1beta1.ApplicationComponent{
{
Name: componentName,
Type: "cross-worker",
Properties: runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
Traits: []v1beta1.ApplicationTrait{
v1beta1.ApplicationTrait{
{
Type: "cross-scaler",
Properties: runtime.RawExtension{Raw: []byte(`{"replicas": 0}`)},
},
@@ -1121,7 +1059,6 @@ var _ = Describe("Test application cross namespace resource", func() {
}
Expect(k8sClient.Create(ctx, app)).Should(BeNil())
time.Sleep(3 * time.Second) // give informer cache to sync
resourceTracker := new(v1beta1.ResourceTracker)
By("create app and check resource and app status")
Eventually(func() error {
@@ -1132,7 +1069,7 @@ var _ = Describe("Test application cross namespace resource", func() {
if app.Status.Phase != common.ApplicationRunning {
return fmt.Errorf("application status not running")
}
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker)
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 1), resourceTracker)
if err != nil {
return fmt.Errorf("error to get resourceTracker %v", err)
}
@@ -1148,7 +1085,7 @@ var _ = Describe("Test application cross namespace resource", func() {
return fmt.Errorf("failed generate cross namespace trait")
}
if len(resourceTracker.Status.TrackedResources) != 2 {
return fmt.Errorf("resourceTracker status recode trackedResource length missmatch")
return fmt.Errorf("expect track %q resources, but got %q", 2, len(resourceTracker.Status.TrackedResources))
}
trait := mts.Items[0]
if len(trait.OwnerReferences) != 1 || trait.OwnerReferences[0].UID != resourceTracker.UID {
@@ -1172,7 +1109,7 @@ var _ = Describe("Test application cross namespace resource", func() {
}
}
return nil
}, time.Second*60, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*500).Should(BeNil())
By("update application trait by delete cross ns trait, resourceTracker will still exist")
Eventually(func() error {
@@ -1180,7 +1117,7 @@ var _ = Describe("Test application cross namespace resource", func() {
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app)).Should(BeNil())
app.Spec.Components[0].Traits = []v1beta1.ApplicationTrait{}
return k8sClient.Update(ctx, app)
}, time.Second*30, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*300).Should(BeNil())
Eventually(func() error {
app = new(v1beta1.Application)
@@ -1190,7 +1127,7 @@ var _ = Describe("Test application cross namespace resource", func() {
if app.Status.Phase != common.ApplicationRunning {
return fmt.Errorf("application status not running")
}
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker)
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 2), resourceTracker)
if err != nil {
return fmt.Errorf("error to get resourceTracker %v", err)
}
@@ -1206,7 +1143,7 @@ var _ = Describe("Test application cross namespace resource", func() {
return fmt.Errorf("cross namespace trait still exist")
}
if len(resourceTracker.Status.TrackedResources) != 1 {
return fmt.Errorf("resourceTracker status recode trackedResource length missmatch")
return fmt.Errorf("expect track %d resources, but got %d", 1, len(resourceTracker.Status.TrackedResources))
}
deploys := new(appsv1.DeploymentList)
err = k8sClient.List(ctx, deploys, opts...)
@@ -1221,13 +1158,13 @@ var _ = Describe("Test application cross namespace resource", func() {
return fmt.Errorf("error to record deploy name in app status")
}
return nil
}, time.Second*60, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*500).Should(BeNil())
By("deleting application will remove resourceTracker and related resourceTracker will be removed")
app = new(v1beta1.Application)
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app)).Should(BeNil())
Expect(k8sClient.Delete(ctx, app)).Should(BeNil())
Eventually(func() error {
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker)
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 2), resourceTracker)
if err == nil {
return fmt.Errorf("resourceTracker still exist")
}
@@ -1235,7 +1172,7 @@ var _ = Describe("Test application cross namespace resource", func() {
return err
}
return nil
}, time.Second*60, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*500).Should(BeNil())
})
It("Test cross-namespace resource gc logic, update a cross-ns workload's namespace", func() {
@@ -1262,7 +1199,7 @@ var _ = Describe("Test application cross namespace resource", func() {
},
Spec: v1beta1.ApplicationSpec{
Components: []v1beta1.ApplicationComponent{
v1beta1.ApplicationComponent{
{
Name: componentName,
Type: "cross-worker",
Properties: runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
@@ -1278,23 +1215,20 @@ var _ = Describe("Test application cross namespace resource", func() {
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app); err != nil {
return fmt.Errorf("app not found %v", err)
}
if err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker); err != nil {
if err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 1), resourceTracker); err != nil {
return err
}
if app.Status.Phase != common.ApplicationRunning {
return fmt.Errorf("application status is not running")
}
if app.Status.ResourceTracker == nil || app.Status.ResourceTracker.UID != resourceTracker.UID {
return fmt.Errorf("appication status error ")
}
return nil
}, time.Second*60, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*500).Should(BeNil())
By("check resource is generated correctly")
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app)).Should(BeNil())
var workload appsv1.Deployment
Eventually(func() error {
checkRt := new(v1beta1.ResourceTracker)
if err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), checkRt); err != nil {
if err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 1), checkRt); err != nil {
return err
}
depolys := new(appsv1.DeploymentList)
@@ -1319,10 +1253,9 @@ var _ = Describe("Test application cross namespace resource", func() {
return fmt.Errorf("resourceTracker status recode trackedResource name mismatch recorded %s, actually %s", checkRt.Status.TrackedResources[0].Name, workload.Name)
}
return nil
}, time.Second*60, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*500).Should(BeNil())
By("update application modify workload namespace will remove resourceTracker and related old workload will be removed")
time.Sleep(3 * time.Second) // wait informer cache to be synced
By("update application modify workload namespace")
Eventually(func() error {
app = new(v1beta1.Application)
err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app)
@@ -1335,16 +1268,12 @@ var _ = Describe("Test application cross namespace resource", func() {
return err
}
return nil
}, time.Second*30, time.Microsecond).Should(BeNil())
}, time.Second*5, time.Millisecond*500).Should(BeNil())
Eventually(func() error {
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name), resourceTracker)
if err == nil {
return fmt.Errorf("resourceTracker still exist")
}
if !apierrors.IsNotFound(err) {
if err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 2), resourceTracker); err != nil {
return err
}
err = k8sClient.Get(ctx, types.NamespacedName{Namespace: crossNamespace, Name: workload.GetName()}, &workload)
err := k8sClient.Get(ctx, types.NamespacedName{Namespace: crossNamespace, Name: workload.GetName()}, &workload)
if err == nil {
return fmt.Errorf("wrokload still exist")
}
@@ -1357,12 +1286,12 @@ var _ = Describe("Test application cross namespace resource", func() {
return fmt.Errorf("generate same namespace workload error")
}
return nil
}, time.Second*60, time.Microsecond*300).Should(BeNil())
}, time.Second*5, time.Millisecond*500).Should(BeNil())
})
})
func generateResourceTrackerKey(namespace string, name string) types.NamespacedName {
return types.NamespacedName{Name: fmt.Sprintf("%s-%s", namespace, name)}
func generateResourceTrackerKey(namespace string, appName string, revision int) types.NamespacedName {
return types.NamespacedName{Name: fmt.Sprintf("%s-v%d-%s", appName, revision, namespace)}
}
const (

View File

@@ -31,9 +31,9 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/standard.oam.dev/v1alpha1"
"github.com/oam-dev/kubevela/pkg/oam"
@@ -46,12 +46,12 @@ var (
var _ = Describe("Test application controller clean up appRevision", func() {
ctx := context.TODO()
namespace := "clean-up-revision"
var namespace string
cd := &v1beta1.ComponentDefinition{}
cdDefJson, _ := yaml.YAMLToJSON([]byte(compDefYaml))
BeforeEach(func() {
namespace = randomNamespaceName("clean-up-revision-test")
ns := v1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: namespace,
@@ -59,6 +59,7 @@ var _ = Describe("Test application controller clean up appRevision", func() {
}
Expect(k8sClient.Create(ctx, &ns)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
cdDefJson, _ := yaml.YAMLToJSON([]byte(fmt.Sprintf(compDefYaml, namespace)))
Expect(json.Unmarshal(cdDefJson, cd)).Should(BeNil())
Expect(k8sClient.Create(ctx, cd.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
})
@@ -71,15 +72,6 @@ var _ = Describe("Test application controller clean up appRevision", func() {
k8sClient.DeleteAllOf(ctx, &v1beta1.TraitDefinition{}, client.InNamespace(namespace))
Expect(k8sClient.Delete(ctx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}, client.PropagationPolicy(metav1.DeletePropagationForeground))).Should(Succeed())
// guarantee namespace have been deleted
Eventually(func() error {
ns := new(v1.Namespace)
err := k8sClient.Get(ctx, types.NamespacedName{Name: namespace}, ns)
if err == nil {
return fmt.Errorf("namespace still exist")
}
return nil
}, time.Second*60, time.Microsecond*300).Should(BeNil())
})
It("Test clean up appRevision", func() {
@@ -96,7 +88,7 @@ var _ = Describe("Test application controller clean up appRevision", func() {
return fmt.Errorf("application point to wrong revision")
}
return nil
}, time.Second*30, time.Microsecond*300).Should(BeNil())
}, time.Second*10, time.Millisecond*500).Should(BeNil())
Eventually(func() error {
checkApp = new(v1beta1.Application)
Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil())
@@ -106,10 +98,8 @@ var _ = Describe("Test application controller clean up appRevision", func() {
return err
}
return nil
}, time.Second*30, time.Microsecond*300).Should(BeNil())
}, time.Second*10, time.Millisecond*500).Should(BeNil())
}
appContext := new(v1alpha2.ApplicationContext)
Expect(k8sClient.Get(ctx, appKey, appContext)).Should(BeNil())
listOpts := []client.ListOption{
client.InNamespace(namespace),
client.MatchingLabels{
@@ -126,13 +116,7 @@ var _ = Describe("Test application controller clean up appRevision", func() {
return fmt.Errorf("error appRevison number wants %d, actually %d", appRevisionLimit+1, len(appRevisionList.Items))
}
return nil
}, time.Second*30, time.Microsecond*300).Should(BeNil())
Eventually(func() error {
if err := k8sClient.Get(ctx, appKey, appContext); err != nil {
return err
}
return nil
}, time.Second*30, time.Microsecond*300).Should(BeNil())
}, time.Second*10, time.Millisecond*500).Should(BeNil())
By("create new appRevision will remove appRevison1")
Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil())
property := fmt.Sprintf(`{"cmd":["sleep","1000"],"image":"busybox:%d"}`, 6)
@@ -156,7 +140,7 @@ var _ = Describe("Test application controller clean up appRevision", func() {
return fmt.Errorf("appRevision collection mismatch")
}
return nil
}, time.Second*30, time.Microsecond*300).Should(BeNil())
}, time.Second*10, time.Millisecond*500).Should(BeNil())
By("update app again will gc appRevision2")
Eventually(func() error {
@@ -187,7 +171,7 @@ var _ = Describe("Test application controller clean up appRevision", func() {
return fmt.Errorf("appRevision collection mismatch")
}
return nil
}, time.Second*30, time.Microsecond*300).Should(BeNil())
}, time.Second*10, time.Millisecond*500).Should(BeNil())
})
It("Test clean up rollout appRevision", func() {
@@ -215,10 +199,8 @@ var _ = Describe("Test application controller clean up appRevision", func() {
return err
}
return nil
}, time.Second*30, time.Microsecond*300).Should(BeNil())
}, time.Second*10, time.Millisecond*500).Should(BeNil())
}
appContext := new(v1alpha2.ApplicationContext)
Expect(k8sClient.Get(ctx, appKey, appContext)).Should(util.NotFoundMatcher{})
listOpts := []client.ListOption{
client.InNamespace(namespace),
client.MatchingLabels{
@@ -260,7 +242,7 @@ var _ = Describe("Test application controller clean up appRevision", func() {
return fmt.Errorf("appRevision collection mismatch")
}
return nil
}, time.Second*30, time.Microsecond*300).Should(BeNil())
}, time.Second*10, time.Millisecond*500).Should(BeNil())
By("update app again will gc appRevision2")
Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil())
@@ -284,7 +266,7 @@ var _ = Describe("Test application controller clean up appRevision", func() {
return fmt.Errorf("appRevision collection mismatch")
}
return nil
}, time.Second*30, time.Microsecond*300).Should(BeNil())
}, time.Second*10, time.Millisecond*500).Should(BeNil())
appRollout := &v1beta1.AppRollout{
TypeMeta: metav1.TypeMeta{
APIVersion: v1beta1.AppRolloutKindAPIVersion,
@@ -298,9 +280,10 @@ var _ = Describe("Test application controller clean up appRevision", func() {
TargetAppRevisionName: appName + "-v3",
ComponentList: []string{"comp1"},
RolloutPlan: v1alpha1.RolloutPlan{
TargetSize: pointer.Int32Ptr(2),
RolloutBatches: []v1alpha1.RolloutBatch{
{
Replicas: intstr.FromInt(1),
Replicas: intstr.FromInt(2),
},
},
},
@@ -318,7 +301,7 @@ var _ = Describe("Test application controller clean up appRevision", func() {
return fmt.Errorf("application point to wrong revision")
}
return nil
}, time.Second*30, time.Microsecond*300).Should(BeNil())
}, time.Second*10, time.Millisecond*500).Should(BeNil())
Eventually(func() error {
if err := k8sClient.Get(ctx, appKey, checkApp); err != nil {
return err
@@ -354,7 +337,7 @@ var _ = Describe("Test application controller clean up appRevision", func() {
return fmt.Errorf("appRevision collection mismatch")
}
return nil
}, time.Second*30, time.Microsecond*300).Should(BeNil())
}, time.Second*60, time.Microsecond*300).Should(BeNil())
})
})
@@ -364,7 +347,7 @@ apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
name: normal-worker
namespace: clean-up-revision
namespace: %s
annotations:
definition.oam.dev/description: "Long-running scalable backend worker without network endpoint"
spec:

View File

@@ -1,349 +0,0 @@
/*
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"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/application"
"github.com/oam-dev/kubevela/pkg/oam/util"
)
var _ = Describe("Test applicationContext reconcile", func() {
ctx := context.Background()
var (
namespace = "appcontext-test-ns"
acName1 = "applicationconfig1"
acName2 = "applicationconfig2"
compName1 = "component1"
compName2 = "component2"
containerName = "test-container"
containerImage = "notarealimage"
cwName1 = "appcontext-test-cw1"
cwName2 = "appcontext-test-cw2"
arName1 = "ar1"
arName2 = "ar2"
appContextName = "appcontext1"
traitName1 = "trait1"
traitName2 = "trait2"
key = types.NamespacedName{Namespace: namespace, Name: appContextName}
)
var ns = corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}
workload1 := cw(
cwWithName(cwName1),
cwWithContainers([]v1alpha2.Container{
{
Name: containerName,
Image: containerImage,
Command: []string{"sleep", "30s"},
Ports: []v1alpha2.ContainerPort{
v1alpha2.ContainerPort{
Name: "http",
Port: 80,
},
},
},
}),
)
rawWorkload1 := runtime.RawExtension{Object: workload1}
co1 := comp(
compWithName(compName1),
compWithNamespace(namespace),
compWithWorkload(rawWorkload1),
)
ac1 := ac(
acWithName(acName1),
acWithNamspace(namespace),
acWithComps([]v1alpha2.ApplicationConfigurationComponent{
{
ComponentName: compName1,
Traits: []v1alpha2.ComponentTrait{},
},
}),
)
workload2 := cw(
cwWithName(cwName2),
cwWithContainers([]v1alpha2.Container{
{
Name: containerName,
Image: containerImage,
Command: []string{"sleep", "30s"},
Ports: []v1alpha2.ContainerPort{
v1alpha2.ContainerPort{
Name: "http",
Port: 80,
},
},
},
}),
)
rawWorkload2 := runtime.RawExtension{Object: workload2}
co2 := comp(
compWithName(compName2),
compWithNamespace(namespace),
compWithWorkload(rawWorkload2),
)
dummyApp := &v1alpha2.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "dummy",
Namespace: namespace,
},
Spec: v1alpha2.ApplicationSpec{
Components: []v1alpha2.ApplicationComponent{},
},
}
ar1 := &v1alpha2.ApplicationRevision{
ObjectMeta: metav1.ObjectMeta{
Name: arName1,
Namespace: namespace,
},
Spec: v1alpha2.ApplicationRevisionSpec{
Components: application.ConvertComponents2RawRevisions([]*v1alpha2.Component{co1}),
ApplicationConfiguration: util.Object2RawExtension(ac1),
Application: *dummyApp,
}}
trait2 := &v1alpha2.ManualScalerTrait{
TypeMeta: metav1.TypeMeta{
Kind: v1alpha2.ManualScalerTraitKind,
APIVersion: v1alpha2.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: traitName2,
Namespace: namespace,
},
Spec: v1alpha2.ManualScalerTraitSpec{
ReplicaCount: 3,
},
}
rawTrait2 := runtime.RawExtension{Object: trait2}
ac2 := ac(
acWithName(acName2),
acWithNamspace(namespace),
acWithComps([]v1alpha2.ApplicationConfigurationComponent{
{
ComponentName: compName2,
Traits: []v1alpha2.ComponentTrait{
v1alpha2.ComponentTrait{
Trait: rawTrait2,
},
},
},
}),
)
ar2 := &v1alpha2.ApplicationRevision{
ObjectMeta: metav1.ObjectMeta{
Name: arName2,
Namespace: namespace,
},
Spec: v1alpha2.ApplicationRevisionSpec{
ApplicationConfiguration: util.Object2RawExtension(ac2),
Components: application.ConvertComponents2RawRevisions([]*v1alpha2.Component{co2}),
Application: *dummyApp,
}}
appContext := &v1alpha2.ApplicationContext{
ObjectMeta: metav1.ObjectMeta{
Name: appContextName,
Namespace: namespace,
},
Spec: v1alpha2.ApplicationContextSpec{
ApplicationRevisionName: arName1,
},
}
BeforeEach(func() {
Expect(k8sClient.Create(ctx, &ns)).Should(SatisfyAny(BeNil()))
Eventually(
func() error {
return k8sClient.Get(ctx, types.NamespacedName{Name: namespace}, &ns)
},
time.Second*3, time.Millisecond*300).Should(BeNil())
Expect(k8sClient.Create(ctx, co1)).Should(Succeed())
Expect(k8sClient.Create(ctx, co2)).Should(Succeed())
Expect(k8sClient.Create(ctx, ar1)).Should(Succeed())
Expect(k8sClient.Create(ctx, ar2)).Should(Succeed())
})
AfterEach(func() {
By("Clean up resources after a test")
Expect(k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationForeground))).Should(Succeed())
time.Sleep(15 * time.Second)
})
It("Test appContext reconcile logic ", func() {
By("Test AppRevision1 only have 1 workload on trait")
Expect(k8sClient.Create(ctx, appContext)).Should(Succeed())
updateTime := time.Now()
Eventually(func() error {
appCtx := new(v1alpha2.ApplicationContext)
err := k8sClient.Get(ctx, key, appCtx)
if err != nil {
return err
}
now := time.Now()
if now.Sub(updateTime) > 4*time.Second {
requestReconcileNow(ctx, appCtx)
updateTime = now
}
if len(appCtx.Status.Workloads) != 1 {
return fmt.Errorf("appContext status error:the number of workloads not right")
}
if appCtx.Status.Workloads[0].ComponentName != compName1 {
return fmt.Errorf("appContext status error:the name of component not right, expect %s", compName1)
}
cw := new(v1alpha2.ContainerizedWorkload)
return k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: cwName1}, cw)
}, time.Second*60, time.Millisecond*300).Should(BeNil())
By("Test revision have both workload and trait , switch AppContext to revision2")
Eventually(func() error {
updateContext := new(v1alpha2.ApplicationContext)
err := k8sClient.Get(ctx, key, updateContext)
if err != nil {
return err
}
updateContext.Spec.ApplicationRevisionName = arName2
err = k8sClient.Update(ctx, updateContext)
if err != nil {
return err
}
return nil
}, time.Second*60, time.Microsecond*300).Should(BeNil())
updateTime = time.Now()
Eventually(func() error {
appCtx := new(v1alpha2.ApplicationContext)
err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appContextName}, appCtx)
if err != nil {
return err
}
now := time.Now()
if now.Sub(updateTime) > 4*time.Second {
requestReconcileNow(ctx, appCtx)
updateTime = now
}
if len(appCtx.Status.Workloads) != 1 {
return fmt.Errorf("appContext status error:the number of workloads not right")
}
if appCtx.Status.Workloads[0].ComponentName != compName2 {
return fmt.Errorf("appContext status error:the name of component not right, expect %s", compName2)
}
cw := new(v1alpha2.ContainerizedWorkload)
err = k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: cwName2}, cw)
if err != nil {
return fmt.Errorf("cannot get workload 2 %v", err)
}
if len(appCtx.Status.Workloads[0].Traits) != 1 {
return fmt.Errorf("appContext status error:the number of traits status not right")
}
mt := new(v1alpha2.ManualScalerTrait)
err = k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: traitName2}, mt)
if err != nil {
return fmt.Errorf("cannot get component trait %v", err)
}
return nil
}, time.Second*60, time.Millisecond*300).Should(BeNil())
By("Test add trait in AppRevision1, and switch context to AppRevision1")
trait1 := &v1alpha2.ManualScalerTrait{
TypeMeta: metav1.TypeMeta{
Kind: v1alpha2.ManualScalerTraitKind,
APIVersion: v1alpha2.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: traitName1,
Namespace: namespace,
},
Spec: v1alpha2.ManualScalerTraitSpec{
ReplicaCount: 2,
},
}
rawTrait1 := runtime.RawExtension{Object: trait1}
ac1.Spec.Components[0].Traits = []v1alpha2.ComponentTrait{
v1alpha2.ComponentTrait{
Trait: rawTrait1,
},
}
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: arName1}, ar1)).Should(BeNil())
ar1.Spec.ApplicationConfiguration = util.Object2RawExtension(ac1)
Expect(k8sClient.Update(ctx, ar1)).Should(Succeed())
Eventually(func() error {
updateContext := new(v1alpha2.ApplicationContext)
err := k8sClient.Get(ctx, key, updateContext)
if err != nil {
return err
}
updateContext.Spec.ApplicationRevisionName = arName1
err = k8sClient.Update(ctx, updateContext)
if err != nil {
return err
}
return nil
}, time.Second*60, time.Microsecond*300).Should(BeNil())
Eventually(func() error {
mt := new(v1alpha2.ManualScalerTrait)
err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: traitName1}, mt)
if err != nil {
return err
}
if mt.Spec.ReplicaCount != 2 {
return fmt.Errorf("repica number missmatch , actual: %d", mt.Spec.ReplicaCount)
}
return nil
}, time.Second*60, time.Millisecond*300).Should(BeNil())
ac1.Spec.Components[0].Traits = []v1alpha2.ComponentTrait{}
By("Test delete trait in AppRevision2, and switch context to AppRevision2")
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: arName2}, ar2)).Should(BeNil())
ac2.Spec.Components[0].Traits = []v1alpha2.ComponentTrait{}
ar1.Spec.ApplicationConfiguration = util.Object2RawExtension(ac2)
Expect(k8sClient.Update(ctx, ar2)).Should(BeNil())
Eventually(func() error {
updateContext := new(v1alpha2.ApplicationContext)
err := k8sClient.Get(ctx, key, updateContext)
if err != nil {
return err
}
updateContext.Spec.ApplicationRevisionName = arName2
err = k8sClient.Update(ctx, updateContext)
if err != nil {
return err
}
return nil
}, time.Second*60, time.Microsecond*300).Should(BeNil())
Eventually(func() error {
mt := new(v1alpha2.ManualScalerTrait)
return k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: traitName2}, mt)
}, time.Second*60, time.Millisecond*300).Should(util.NotFoundMatcher{})
})
})

View File

@@ -0,0 +1,368 @@
/*
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"
"github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
"github.com/crossplane/crossplane-runtime/pkg/meta"
"github.com/ghodss/yaml"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/kind/pkg/errors"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/utils/pointer"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
"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/apply"
)
// For legacy clusters that use appContext to create/update resources, we should guarantee backward compatibility while we
// deprecate appContext and replace it with assemble/dispatch modules.
// This test is to simulate a scenario where a cluster has an application whose resources are already created and owned
// by appContext and resource tracker(for cx-ns), and once we create a new application whose resources are same as
// existing ones, existing resources's ctrl-owner should be changed from appContext to resource tracker.
var _ = Describe("Test compatibility for deprecation of appContext", func() {
ctx := context.Background()
var namespaceName string
var ns corev1.Namespace
BeforeEach(func() {
namespaceName = randomNamespaceName("deprecation-appctx-test")
ns = corev1.Namespace{}
ns.SetName(namespaceName)
Expect(k8sClient.Create(ctx, &ns)).Should(Succeed())
_, err := createFromYAML(ctx, pvTraitDefinition, namespaceName, nil)
Expect(err).Should(BeNil())
})
AfterEach(func() {
Expect(k8sClient.Delete(ctx, &ns)).Should(Succeed())
Expect(k8sClient.DeleteAllOf(ctx, &v1beta1.ResourceTracker{})).Should(Succeed())
Expect(k8sClient.DeleteAllOf(ctx, &corev1.PersistentVolume{})).Should(Succeed())
})
It("Test application can update its resources' owners", func() {
var err error
var appCtxKey, rtKey *client.ObjectKey
By("Mock existing owners in a legacy cluster")
appCtxKey, err = createFromYAML(ctx, legacyAppCtx, namespaceName, nil)
Expect(err).Should(BeNil())
rtKey, err = createFromYAML(ctx, legacyResourceTracker, namespaceName, nil)
Expect(err).Should(BeNil())
By("Mock owner references: appCtx owns Deployment and Service")
appCtx := &v1alpha2.ApplicationContext{}
Expect(k8sClient.Get(ctx, *appCtxKey, appCtx)).Should(Succeed())
appCtxOwnRef := meta.AsController(&v1alpha1.TypedReference{
APIVersion: "core.oam.dev/v1alpha2",
Kind: "ApplicationContext",
Name: appCtx.GetName(),
UID: appCtx.GetUID(),
})
By("Mock owner references: rscTracker owns PersistentVolume")
rt := &v1beta1.ResourceTracker{}
Expect(k8sClient.Get(ctx, *rtKey, rt)).Should(Succeed())
rtOwnerRef := meta.AsController(&v1alpha1.TypedReference{
APIVersion: "core.oam.dev/v1beta1",
Kind: "ResourceTracker",
Name: rt.GetName(),
UID: rt.GetUID(),
})
var deployKey, svcKey, pvKey *client.ObjectKey
By("Mock existing resources in a legacy cluster")
deployKey, err = createFromYAML(ctx, legacyDeploy, namespaceName, &appCtxOwnRef)
Expect(err).Should(BeNil())
svcKey, err = createFromYAML(ctx, legacyService, namespaceName, &appCtxOwnRef)
Expect(err).Should(BeNil())
pvKey, err = createFromYAML(ctx, legacyPersistentVolume, namespaceName, &rtOwnerRef)
Expect(err).Should(BeNil())
By("Create an application")
_, err = createFromYAML(ctx, newApplication, namespaceName, nil)
Expect(err).Should(BeNil())
By("Wait for new resource tracker is created")
Eventually(func() error {
rt = &v1beta1.ResourceTracker{}
if err := k8sClient.Get(ctx, client.ObjectKey{Name: "myapp-v1-" + namespaceName}, rt); err != nil {
return errors.Wrap(err, "cannot get new resource tracker")
}
return nil
}, 10*time.Second, 500*time.Millisecond).Should(Succeed())
wantNewOwner := metav1.OwnerReference{
APIVersion: "core.oam.dev/v1beta1",
Kind: "ResourceTracker",
Name: rt.GetName(),
UID: rt.GetUID(),
Controller: pointer.BoolPtr(true),
BlockOwnerDeletion: pointer.BoolPtr(true),
}
By("Verify existing resources' new owners")
deploy := &appsv1.Deployment{}
Expect(k8sClient.Get(ctx, *deployKey, deploy)).Should(Succeed())
newDeployOwner := metav1.GetControllerOf(deploy)
Expect(newDeployOwner).ShouldNot(BeNil())
Expect(*newDeployOwner).Should(BeEquivalentTo(wantNewOwner))
svc := &corev1.Service{}
Expect(k8sClient.Get(ctx, *svcKey, svc)).Should(Succeed())
newSvcOwner := metav1.GetControllerOf(svc)
Expect(newSvcOwner).ShouldNot(BeNil())
Expect(*newSvcOwner).Should(Equal(wantNewOwner))
pv := &corev1.PersistentVolume{}
Expect(k8sClient.Get(ctx, *pvKey, pv)).Should(Succeed())
newPVOwner := metav1.GetControllerOf(svc)
Expect(newPVOwner).ShouldNot(BeNil())
Expect(*newPVOwner).Should(Equal(wantNewOwner))
By("Delete the application")
app := &v1beta1.Application{}
app.SetName("myapp")
app.SetNamespace(namespaceName)
Expect(k8sClient.Delete(ctx, app)).Should(Succeed())
By("Verify all resources can be deleted")
Eventually(func() error {
return k8sClient.Get(ctx, client.ObjectKey{Name: "myapp-v1-" + namespaceName}, rt)
}, 10*time.Second, 500*time.Millisecond).Should(util.NotFoundMatcher{})
Eventually(func() error {
return k8sClient.Get(ctx, *deployKey, deploy)
}, 30*time.Second, 500*time.Millisecond).Should(util.NotFoundMatcher{})
Eventually(func() error {
return k8sClient.Get(ctx, *svcKey, svc)
}, 30*time.Second, 500*time.Millisecond).Should(util.NotFoundMatcher{})
Eventually(func() error {
return k8sClient.Get(ctx, *pvKey, pv)
}, 30*time.Second, 500*time.Millisecond).Should(util.NotFoundMatcher{})
})
It("Test delete an application with a legacy finalizer", func() {
var err error
var rtKey *client.ObjectKey
// simulate a resource tracker created by a legacy application
rtKey, err = createFromYAML(ctx, legacyResourceTracker, namespaceName, nil)
Expect(err).Should(BeNil())
By("Create the application")
app := &v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "myapp",
Namespace: namespaceName,
},
Spec: v1beta1.ApplicationSpec{
Components: []v1beta1.ApplicationComponent{{
Name: "mycomp",
Type: "worker",
Properties: runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
}},
},
}
app.SetFinalizers([]string{
// this finalizer only apperars in a legacy application
// this case is to test whether a legacy application with this finalizer can be deleted
"resourceTracker.finalizer.core.oam.dev",
})
Expect(k8sClient.Create(ctx, app.DeepCopy())).Should(Succeed())
By("Delete the application")
Expect(k8sClient.Delete(ctx, app)).Should(Succeed())
By("Verify legacy resource tracker is deleted")
Eventually(func() error {
return k8sClient.Get(ctx, client.ObjectKey{Name: rtKey.Name}, &v1beta1.ResourceTracker{})
}, 10*time.Second, 500*time.Millisecond).Should(util.NotFoundMatcher{})
})
})
var createFromYAML = func(ctx context.Context, objYAML string, ns string, owner *metav1.OwnerReference) (*client.ObjectKey, error) {
u := &unstructured.Unstructured{}
if err := yaml.Unmarshal([]byte(fmt.Sprintf(objYAML, ns)), u); err != nil {
return nil, err
}
if owner != nil {
u.SetOwnerReferences([]metav1.OwnerReference{*owner})
}
objKey := client.ObjectKey{
Name: u.GetName(),
Namespace: ns,
}
// use apply.Applicator to simulate real scenario
applicator := apply.NewAPIApplicator(k8sClient)
if err := applicator.Apply(ctx, u); err != nil {
return nil, err
}
return &objKey, nil
}
var (
legacyDeploy = `apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.oam.dev/app-revision-hash: 3dc894b5cb767c4b
app.oam.dev/appRevision: myapp-v1
app.oam.dev/component: myworker
app.oam.dev/name: myapp
app.oam.dev/resourceType: WORKLOAD
app.oam.dev/revision: myworker-v1
workload.oam.dev/type: worker
name: myworker
namespace: %s
spec:
selector:
matchLabels:
app.oam.dev/component: myworker
template:
metadata:
labels:
app.oam.dev/component: myworker
spec:
containers:
- image: nginx:latest
name: myworker`
legacyService = `apiVersion: v1
kind: Service
metadata:
labels:
app.oam.dev/app-revision-hash: 3dc894b5cb767c4b
app.oam.dev/appRevision: myapp-v1
app.oam.dev/component: myworker
app.oam.dev/name: myapp
app.oam.dev/resourceType: TRAIT
app.oam.dev/revision: myworker-v1
trait.oam.dev/resource: service
trait.oam.dev/type: ingress
name: myworker
namespace: %s
spec:
ports:
- port: 8080
protocol: TCP
targetPort: 8080
selector:
app.oam.dev/component: myworker
type: ClusterIP`
legacyPersistentVolume = `apiVersion: v1
kind: PersistentVolume
metadata:
labels:
app.oam.dev/appRevision: myapp-v1
app.oam.dev/component: myworker
app.oam.dev/name: myapp
app.oam.dev/resourceType: TRAIT
app.oam.dev/revision: myworker-v1
trait.oam.dev/resource: pv
trait.oam.dev/type: testpv
name: csi-gcs-pv-myworker
namespace: %s
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 5Gi
nfs:
server: 1.1.1.1
path: "/"
persistentVolumeReclaimPolicy: Retain
storageClassName: csi-gcs-test-sc`
legacyAppCtx = `apiVersion: core.oam.dev/v1alpha2
kind: ApplicationContext
metadata:
labels:
app.oam.dev/app-revision-hash: 3dc894b5cb767c4b
name: myapp
namespace: %s
spec:
applicationRevisionName: myapp-v1`
legacyResourceTracker = `apiVersion: core.oam.dev/v1beta1
kind: ResourceTracker
metadata:
name: %s-myapp`
newApplication = `apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: myapp
namespace: %s
spec:
components:
- name: myworker
type: worker
properties:
image: "nginx:latest"
traits:
- type: ingress
properties:
domain: localhost
http:
"/": 8080
- type: testpv
properties:
secretName: testSecret`
pvTraitDefinition = `apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
name: testpv
namespace: %s
annotations:
definition.oam.dev/description: Mock a cluster-scope resource
spec:
schematic:
cue:
template: |
parameter: {
secretName: string
}
outputs: {
pv: {
apiVersion: "v1"
kind: "PersistentVolume"
metadata: name: "csi-gcs-pv-\(context.name)"
spec: {
accessModes: ["ReadWriteOnce"]
capacity: storage: "5Gi"
persistentVolumeReclaimPolicy: "Retain"
storageClassName: "csi-gcs-test-sc"
nfs: {
server: "1.1.1.1"
path: "/"
}
}
}
}`
)

View File

@@ -24,6 +24,7 @@ import (
"github.com/ghodss/yaml"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/pkg/errors"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
@@ -33,7 +34,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/oam/util"
)
@@ -198,26 +198,15 @@ var _ = Describe("Test application of the specified definition version", func()
By("Create application")
Expect(k8sClient.Create(ctx, &app)).Should(Succeed())
ac := &v1alpha2.ApplicationContext{}
acName := appName
By("Verify the ApplicationContext is created & reconciled successfully")
Eventually(func() bool {
if err := k8sClient.Get(ctx, client.ObjectKey{Name: acName, Namespace: namespace}, ac); err != nil {
return false
}
return len(ac.Status.Workloads) > 0
}, 60*time.Second, time.Second).Should(BeTrue())
By("Verify the workload(deployment) is created successfully")
Expect(len(ac.Status.Workloads)).Should(Equal(len(app.Spec.Components)))
webServiceDeploy := &appsv1.Deployment{}
deployName := ac.Status.Workloads[0].Reference.Name
deployName := comp1Name
Eventually(func() error {
return k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, webServiceDeploy)
}, 30*time.Second, 3*time.Second).Should(Succeed())
workerDeploy := &appsv1.Deployment{}
deployName = ac.Status.Workloads[1].Reference.Name
deployName = comp2Name
Eventually(func() error {
return k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, workerDeploy)
}, 30*time.Second, 3*time.Second).Should(Succeed())
@@ -264,25 +253,29 @@ var _ = Describe("Test application of the specified definition version", func()
}
Expect(k8sClient.Patch(ctx, &app, client.Merge)).Should(Succeed())
By("Verify the ApplicationContext is update successfully")
Eventually(func() bool {
if err := k8sClient.Get(ctx, client.ObjectKey{Name: acName, Namespace: namespace}, ac); err != nil {
return false
By("Wait for dispatching v2 resources successfully")
Eventually(func() error {
requestReconcileNow(ctx, &app)
rt := &v1beta1.ResourceTracker{}
if err := k8sClient.Get(ctx, client.ObjectKey{Name: fmt.Sprintf("%s-v2-%s", appName, namespace)}, rt); err != nil {
return err
}
return ac.Generation == 2
}, 10*time.Second, time.Second).Should(BeTrue())
if len(rt.Status.TrackedResources) != 0 {
return nil
}
return errors.New("v2 resources have not been dispatched")
}, 10*time.Second, 500*time.Millisecond).Should(Succeed())
By("Verify the workload(deployment) is created successfully")
Expect(len(ac.Status.Workloads)).Should(Equal(len(app.Spec.Components)))
webServiceV1Deploy := &appsv1.Deployment{}
deployName = ac.Status.Workloads[0].Reference.Name
deployName = comp1Name
Eventually(func() error {
return k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, webServiceV1Deploy)
}, 30*time.Second, 3*time.Second).Should(Succeed())
By("Verify the workload(job) is created successfully")
workerJob := &batchv1.Job{}
jobName := ac.Status.Workloads[1].Reference.Name
jobName := comp2Name
Eventually(func() error {
return k8sClient.Get(ctx, client.ObjectKey{Name: jobName, Namespace: namespace}, workerJob)
}, 30*time.Second, 3*time.Second).Should(Succeed())
@@ -417,13 +410,6 @@ var _ = Describe("Test application of the specified definition version", func()
By("Create application")
Expect(k8sClient.Create(ctx, &app)).Should(Succeed())
ac := &v1alpha2.ApplicationContext{}
acName := appName
By("Verify the ApplicationContext is created successfully")
Eventually(func() error {
return k8sClient.Get(ctx, client.ObjectKey{Name: acName, Namespace: namespace}, ac)
}, 30*time.Second, time.Second).Should(Succeed())
By("Verify the workload(deployment) is created successfully by Helm")
deploy := &appsv1.Deployment{}
deployName := fmt.Sprintf("%s-%s-podinfo", appName, compName)
@@ -438,7 +424,6 @@ var _ = Describe("Test application of the specified definition version", func()
By("Verify trait is applied to the workload")
Eventually(func() bool {
requestReconcileNow(ctx, ac)
deploy := &appsv1.Deployment{}
if err := k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy); err != nil {
return false
@@ -471,15 +456,6 @@ var _ = Describe("Test application of the specified definition version", func()
By("Create application")
Expect(k8sClient.Patch(ctx, &app, client.Merge)).Should(Succeed())
By("Verify the ApplicationContext is updated")
Eventually(func() bool {
ac = &v1alpha2.ApplicationContext{}
if err := k8sClient.Get(ctx, client.ObjectKey{Name: acName, Namespace: namespace}, ac); err != nil {
return false
}
return ac.GetGeneration() == 2
}, 15*time.Second, 3*time.Second).Should(BeTrue())
By("Verify the workload(deployment) is update successfully by Helm")
deploy = &appsv1.Deployment{}
Eventually(func() bool {
@@ -589,19 +565,9 @@ var _ = Describe("Test application of the specified definition version", func()
By("Create application")
Expect(k8sClient.Create(ctx, &app)).Should(Succeed())
ac := &v1alpha2.ApplicationContext{}
acName := appName
By("Verify the ApplicationContext is created & reconciled successfully")
Eventually(func() bool {
if err := k8sClient.Get(ctx, client.ObjectKey{Name: acName, Namespace: namespace}, ac); err != nil {
return false
}
return len(ac.Status.Workloads) > 0
}, 60*time.Second, time.Second).Should(BeTrue())
By("Verify the workload(job) is created successfully")
job := &batchv1.Job{}
jobName := ac.Status.Workloads[0].Reference.Name
jobName := compName
Eventually(func() error {
return k8sClient.Get(ctx, client.ObjectKey{Name: jobName, Namespace: namespace}, job)
}, 30*time.Second, 3*time.Second).Should(Succeed())
@@ -640,18 +606,9 @@ var _ = Describe("Test application of the specified definition version", func()
}
Expect(k8sClient.Patch(ctx, &app, client.Merge)).Should(Succeed())
By("Verify the ApplicationContext is update successfully")
Eventually(func() bool {
if err := k8sClient.Get(ctx, client.ObjectKey{Name: acName, Namespace: namespace}, ac); err != nil {
return false
}
return ac.Generation == 2
}, 10*time.Second, time.Second).Should(BeTrue())
By("Verify the workload(deployment) is created successfully")
Expect(len(ac.Status.Workloads)).Should(Equal(len(app.Spec.Components)))
deploy := &appsv1.Deployment{}
deployName := ac.Status.Workloads[0].Reference.Name
deployName := compName
Eventually(func() error {
return k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy)
}, 30*time.Second, 3*time.Second).Should(Succeed())

View File

@@ -29,7 +29,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
v1beta1 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/oam/util"
. "github.com/onsi/ginkgo"
@@ -46,7 +46,7 @@ var _ = Describe("Test application containing helm module", func() {
tdName = "virtualgroup"
)
var namespace string
var app v1alpha2.Application
var app v1beta1.Application
var ns corev1.Namespace
BeforeEach(func() {
@@ -58,7 +58,7 @@ var _ = Describe("Test application containing helm module", func() {
},
time.Second*3, time.Millisecond*300).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
cd := v1alpha2.ComponentDefinition{}
cd := v1beta1.ComponentDefinition{}
cd.SetName(cdName)
cd.SetNamespace(namespace)
cd.Spec.Workload.Definition = common.WorkloadGVK{APIVersion: "apps/v1", Kind: "Deployment"}
@@ -80,7 +80,7 @@ var _ = Describe("Test application containing helm module", func() {
Expect(k8sClient.Create(ctx, &cd)).Should(Succeed())
By("Install a patch trait used to test CUE module")
td := v1alpha2.TraitDefinition{}
td := v1beta1.TraitDefinition{}
td.SetName(tdName)
td.SetNamespace(namespace)
td.Spec.AppliesToWorkloads = []string{"deployments.apps"}
@@ -107,7 +107,7 @@ var _ = Describe("Test application containing helm module", func() {
Expect(k8sClient.Create(ctx, &td)).Should(Succeed())
By("Add 'deployments.apps' to scaler's appliesToWorkloads")
scalerTd := v1alpha2.TraitDefinition{}
scalerTd := v1beta1.TraitDefinition{}
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: "scaler", Namespace: "vela-system"}, &scalerTd)).Should(Succeed())
scalerTd.Spec.AppliesToWorkloads = []string{"deployments.apps", "webservice", "worker"}
scalerTd.SetResourceVersion("")
@@ -116,15 +116,15 @@ var _ = Describe("Test application containing helm module", func() {
AfterEach(func() {
By("Clean up resources after a test")
k8sClient.DeleteAllOf(ctx, &v1alpha2.Application{}, client.InNamespace(namespace))
k8sClient.DeleteAllOf(ctx, &v1alpha2.ComponentDefinition{}, client.InNamespace(namespace))
k8sClient.DeleteAllOf(ctx, &v1alpha2.WorkloadDefinition{}, client.InNamespace(namespace))
k8sClient.DeleteAllOf(ctx, &v1alpha2.TraitDefinition{}, client.InNamespace(namespace))
k8sClient.DeleteAllOf(ctx, &v1beta1.Application{}, client.InNamespace(namespace))
k8sClient.DeleteAllOf(ctx, &v1beta1.ComponentDefinition{}, client.InNamespace(namespace))
k8sClient.DeleteAllOf(ctx, &v1beta1.WorkloadDefinition{}, client.InNamespace(namespace))
k8sClient.DeleteAllOf(ctx, &v1beta1.TraitDefinition{}, client.InNamespace(namespace))
By(fmt.Sprintf("Delete the entire namespaceName %s", ns.Name))
Expect(k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationForeground))).Should(Succeed())
By("Remove 'deployments.apps' from scaler's appliesToWorkloads")
scalerTd := v1alpha2.TraitDefinition{}
scalerTd := v1beta1.TraitDefinition{}
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: "scaler", Namespace: "vela-system"}, &scalerTd)).Should(Succeed())
scalerTd.Spec.AppliesToWorkloads = []string{"webservice", "worker"}
scalerTd.SetResourceVersion("")
@@ -132,30 +132,30 @@ var _ = Describe("Test application containing helm module", func() {
})
It("Test deploy an application containing helm module", func() {
app = v1alpha2.Application{
app = v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: appName,
Namespace: namespace,
},
Spec: v1alpha2.ApplicationSpec{
Components: []v1alpha2.ApplicationComponent{
Spec: v1beta1.ApplicationSpec{
Components: []v1beta1.ApplicationComponent{
{
Name: compName,
WorkloadType: cdName,
Settings: util.Object2RawExtension(map[string]interface{}{
Name: compName,
Type: cdName,
Properties: util.Object2RawExtension(map[string]interface{}{
"image": map[string]interface{}{
"tag": "5.1.2",
},
}),
Traits: []v1alpha2.ApplicationTrait{
Traits: []v1beta1.ApplicationTrait{
{
Name: "scaler",
Type: "scaler",
Properties: util.Object2RawExtension(map[string]interface{}{
"replicas": 2,
}),
},
{
Name: tdName,
Type: tdName,
Properties: util.Object2RawExtension(map[string]interface{}{
"group": "my-group",
"type": "cluster",
@@ -169,13 +169,6 @@ var _ = Describe("Test application containing helm module", func() {
By("Create application")
Expect(k8sClient.Create(ctx, &app)).Should(Succeed())
ac := &v1alpha2.ApplicationContext{}
acName := appName
By("Verify the ApplicationContext is created successfully")
Eventually(func() error {
return k8sClient.Get(ctx, client.ObjectKey{Name: acName, Namespace: namespace}, ac)
}, 30*time.Second, time.Second).Should(Succeed())
By("Verify the workload(deployment) is created successfully by Helm")
deploy := &appsv1.Deployment{}
deployName := fmt.Sprintf("%s-%s-podinfo", appName, compName)
@@ -185,7 +178,6 @@ var _ = Describe("Test application containing helm module", func() {
By("Verify two traits are applied to the workload")
Eventually(func() bool {
requestReconcileNow(ctx, ac)
deploy := &appsv1.Deployment{}
if err := k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy); err != nil {
return false
@@ -206,30 +198,30 @@ var _ = Describe("Test application containing helm module", func() {
}, 120*time.Second, 10*time.Second).Should(BeTrue())
By("Update the application")
app = v1alpha2.Application{
app = v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: appName,
Namespace: namespace,
},
Spec: v1alpha2.ApplicationSpec{
Components: []v1alpha2.ApplicationComponent{
Spec: v1beta1.ApplicationSpec{
Components: []v1beta1.ApplicationComponent{
{
Name: compName,
WorkloadType: cdName,
Settings: util.Object2RawExtension(map[string]interface{}{
Name: compName,
Type: cdName,
Properties: util.Object2RawExtension(map[string]interface{}{
"image": map[string]interface{}{
"tag": "5.1.3", // change 5.1.4 => 5.1.3
},
}),
Traits: []v1alpha2.ApplicationTrait{
Traits: []v1beta1.ApplicationTrait{
{
Name: "scaler",
Type: "scaler",
Properties: util.Object2RawExtension(map[string]interface{}{
"replicas": 3, // change 2 => 3
}),
},
{
Name: tdName,
Type: tdName,
Properties: util.Object2RawExtension(map[string]interface{}{
"group": "my-group-0", // change my-group => my-group-0
"type": "cluster",
@@ -242,19 +234,8 @@ var _ = Describe("Test application containing helm module", func() {
}
Expect(k8sClient.Patch(ctx, &app, client.Merge)).Should(Succeed())
By("Verify the ApplicationContext is updated")
deploy = &appsv1.Deployment{}
Eventually(func() bool {
ac = &v1alpha2.ApplicationContext{}
if err := k8sClient.Get(ctx, client.ObjectKey{Name: acName, Namespace: namespace}, ac); err != nil {
return false
}
return ac.GetGeneration() == 2
}, 15*time.Second, 3*time.Second).Should(BeTrue())
By("Verify the changes are applied to the workload")
Eventually(func() bool {
requestReconcileNow(ctx, ac)
deploy := &appsv1.Deployment{}
if err := k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy); err != nil {
return false
@@ -277,7 +258,7 @@ var _ = Describe("Test application containing helm module", func() {
It("Test deploy an application containing helm module defined by workloadDefinition", func() {
workloaddef := v1alpha2.WorkloadDefinition{}
workloaddef := v1beta1.WorkloadDefinition{}
workloaddef.SetName(wdName)
workloaddef.SetNamespace(namespace)
workloaddef.Spec.Reference = common.DefinitionReference{Name: "deployments.apps", Version: "v1"}
@@ -300,17 +281,17 @@ var _ = Describe("Test application containing helm module", func() {
Expect(k8sClient.Create(ctx, &workloaddef)).Should(Succeed())
appTestName := "test-app-refer-to-workloaddef"
appTest := v1alpha2.Application{
appTest := v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: appTestName,
Namespace: namespace,
},
Spec: v1alpha2.ApplicationSpec{
Components: []v1alpha2.ApplicationComponent{
Spec: v1beta1.ApplicationSpec{
Components: []v1beta1.ApplicationComponent{
{
Name: compName,
WorkloadType: wdName,
Settings: util.Object2RawExtension(map[string]interface{}{
Name: compName,
Type: wdName,
Properties: util.Object2RawExtension(map[string]interface{}{
"image": map[string]interface{}{
"tag": "5.1.2",
},
@@ -322,13 +303,6 @@ var _ = Describe("Test application containing helm module", func() {
By("Create application")
Expect(k8sClient.Create(ctx, &appTest)).Should(Succeed())
ac := &v1alpha2.ApplicationContext{}
acName := appTestName
By("Verify the AppConfig is created successfully")
Eventually(func() error {
return k8sClient.Get(ctx, client.ObjectKey{Name: acName, Namespace: namespace}, ac)
}, 30*time.Second, time.Second).Should(Succeed())
By("Verify the workload(deployment) is created successfully by Helm")
deploy := &appsv1.Deployment{}
deployName := fmt.Sprintf("%s-%s-podinfo", appTestName, compName)
@@ -338,7 +312,7 @@ var _ = Describe("Test application containing helm module", func() {
})
It("Test deploy an application containing helm module and the componet refer to autodetect type worklaod", func() {
cd := v1alpha2.ComponentDefinition{}
cd := v1beta1.ComponentDefinition{}
cd.SetName("podinfo")
cd.SetNamespace(namespace)
cd.Spec.Schematic = &common.Schematic{
@@ -359,17 +333,17 @@ var _ = Describe("Test application containing helm module", func() {
Expect(k8sClient.Create(ctx, &cd)).Should(Succeed())
newAppName := "test-autodetect"
newApp := v1alpha2.Application{
newApp := v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: newAppName,
Namespace: namespace,
},
Spec: v1alpha2.ApplicationSpec{
Components: []v1alpha2.ApplicationComponent{
Spec: v1beta1.ApplicationSpec{
Components: []v1beta1.ApplicationComponent{
{
Name: compName,
WorkloadType: "podinfo",
Settings: util.Object2RawExtension(map[string]interface{}{
Name: compName,
Type: "podinfo",
Properties: util.Object2RawExtension(map[string]interface{}{
"image": map[string]interface{}{
"tag": "5.1.2",
},

View File

@@ -31,7 +31,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/oam/util"
@@ -200,26 +199,15 @@ spec:
By("Create application")
Expect(k8sClient.Create(ctx, &app)).Should(Succeed())
ac := &v1alpha2.ApplicationContext{}
acName := appName
By("Verify the ApplicationContext is created & reconciled successfully")
Eventually(func() bool {
if err := k8sClient.Get(ctx, client.ObjectKey{Name: acName, Namespace: namespace}, ac); err != nil {
return false
}
return len(ac.Status.Workloads) > 0
}, 60*time.Second, time.Second).Should(BeTrue())
By("Verify the workload(deployment) is created successfully")
deploy := &appsv1.Deployment{}
deployName := ac.Status.Workloads[0].Reference.Name
deployName := compName
Eventually(func() error {
return k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy)
}, 30*time.Second, 3*time.Second).Should(Succeed())
By("Verify two traits are applied to the workload")
Eventually(func() bool {
requestReconcileNow(ctx, ac)
deploy := &appsv1.Deployment{}
if err := k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy); err != nil {
return false
@@ -272,21 +260,12 @@ spec:
}
Expect(k8sClient.Patch(ctx, &app, client.Merge)).Should(Succeed())
By("Verify the ApplicationContext is update successfully")
Eventually(func() bool {
if err := k8sClient.Get(ctx, client.ObjectKey{Name: acName, Namespace: namespace}, ac); err != nil {
return false
}
return ac.Generation == 2
}, 10*time.Second, time.Second).Should(BeTrue())
By("Verify the workload(deployment) is created successfully")
deploy = &appsv1.Deployment{}
deployName = ac.Status.Workloads[0].Reference.Name
deployName = compName
By("Verify the changes are applied to the workload")
Eventually(func() bool {
requestReconcileNow(ctx, ac)
deploy := &appsv1.Deployment{}
if err := k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy); err != nil {
return false
@@ -348,19 +327,9 @@ spec:
By("Create application")
Expect(k8sClient.Create(ctx, &appTest)).Should(Succeed())
ac := &v1alpha2.ApplicationContext{}
acName := appTestName
By("Verify the ApplicationContext is created & reconciled successfully")
Eventually(func() bool {
if err := k8sClient.Get(ctx, client.ObjectKey{Name: acName, Namespace: namespace}, ac); err != nil {
return false
}
return len(ac.Status.Workloads) > 0
}, 15*time.Second, time.Second).Should(BeTrue())
By("Verify the workload(deployment) is created successfully")
deploy := &appsv1.Deployment{}
deployName := ac.Status.Workloads[0].Reference.Name
deployName := compName
Eventually(func() error {
return k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy)
}, 15*time.Second, 3*time.Second).Should(Succeed())

View File

@@ -21,24 +21,25 @@ import (
"fmt"
"time"
"github.com/oam-dev/kubevela/pkg/oam"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
kruise "github.com/openkruise/kruise-api/apps/v1alpha1"
corev1 "k8s.io/api/core/v1"
corev1beta1 "k8s.io/api/networking/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client"
kruise "github.com/openkruise/kruise-api/apps/v1alpha1"
oamcomm "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
oamstd "github.com/oam-dev/kubevela/apis/standard.oam.dev/v1alpha1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/application/dispatch"
"github.com/oam-dev/kubevela/pkg/controller/utils"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/util"
"github.com/oam-dev/kubevela/pkg/utils/common"
)
@@ -92,6 +93,18 @@ var _ = Describe("Cloneset based rollout tests", func() {
time.Second*3, time.Millisecond*300).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
}
CreateIngressDef := func() {
By("Install Ingress trait definition")
var td v1beta1.TraitDefinition
Expect(common.ReadYamlToObject("testdata/rollout/cloneset/ingressDefinition.yaml", &td)).Should(BeNil())
// create the traitDefinition if not exist
Eventually(
func() error {
return k8sClient.Create(ctx, &td)
},
time.Second*3, time.Millisecond*300).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
}
applySourceApp := func(source string) {
By("Apply an application")
var newApp v1beta1.Application
@@ -119,21 +132,22 @@ var _ = Describe("Cloneset based rollout tests", func() {
Eventually(
func() error {
k8sClient.Get(ctx, client.ObjectKey{Namespace: namespaceName, Name: app.Name}, &app)
app.Spec = targetApp.Spec
return k8sClient.Update(ctx, &app)
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() bool {
var appRevList = &v1beta1.ApplicationRevisionList{}
_ = k8sClient.List(ctx, appRevList, client.MatchingLabels(map[string]string{oam.LabelAppName: targetApp.Name}))
_ = k8sClient.List(ctx, appRevList, client.InNamespace(namespaceName),
client.MatchingLabels(map[string]string{oam.LabelAppName: targetApp.Name}))
if appRevList != nil {
return len(appRevList.Items) >= 2
}
return false
},
time.Second*30, time.Millisecond*500).Should(BeTrue())
time.Second*15, time.Millisecond*500).Should(BeTrue())
}
createAppRolling := func(newAppRollout *v1beta1.AppRollout) {
@@ -183,53 +197,80 @@ var _ = Describe("Cloneset based rollout tests", func() {
Expect(appRollout.Status.UpgradedReplicas).Should(BeEquivalentTo(appRollout.Status.RolloutTargetSize))
clonesetName := appRollout.Spec.ComponentList[0]
By("Verify AppContext rolling status")
var appContext v1alpha2.ApplicationContext
Eventually(
func() types.RollingStatus {
k8sClient.Get(ctx, client.ObjectKey{Namespace: namespaceName, Name: targetAppName}, &appContext)
return appContext.Status.RollingStatus
},
time.Second*60, time.Second).Should(BeEquivalentTo(types.RollingCompleted))
By("Wait for resourceTracker to resume the control of cloneset")
By("Wait for AppContext to resume the control of cloneset")
var clonesetOwner *metav1.OwnerReference
Eventually(
func() string {
func() error {
var clonesetOwner *metav1.OwnerReference
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespaceName, Name: clonesetName}, &kc)
if err != nil {
return ""
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)
}
clonesetOwner = metav1.GetControllerOf(&kc)
if clonesetOwner != nil {
return clonesetOwner.Kind
if clonesetOwner == nil {
return fmt.Errorf("controller owner missed")
}
return ""
if clonesetOwner.Kind != v1beta1.ResourceTrackerKind {
return fmt.Errorf("controller kind missmatch wants %s actually %s", v1beta1.ResourceTrackerKind, clonesetOwner.Kind)
}
resourceTrackerName := dispatch.ConstructResourceTrackerName(targetAppName, namespaceName)
if resourceTrackerName != clonesetOwner.Name {
return fmt.Errorf("controller name missmatch wants %s actually %s", resourceTrackerName, clonesetOwner.Name)
}
return nil
},
time.Second*30, time.Millisecond*500).Should(BeEquivalentTo(v1alpha2.ApplicationContextKind))
Expect(clonesetOwner.Name).Should(BeEquivalentTo(targetAppName))
Expect(kc.Status.UpdatedReplicas).Should(BeEquivalentTo(*kc.Spec.Replicas))
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{}
Expect(k8sClient.List(ctx, &podList, client.MatchingLabels(kc.Spec.Template.Labels),
client.InNamespace(namespaceName))).Should(Succeed())
Expect(len(podList.Items)).Should(BeEquivalentTo(*kc.Spec.Replicas))
for _, pod := range podList.Items {
Expect(pod.Spec.Containers[0].Image).Should(Equal(image))
Expect(pod.Status.Phase).Should(Equal(corev1.PodRunning))
}
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())
}
verifyAppConfigInactive := func(appContextName string) {
var appContext v1alpha2.ApplicationContext
By("Verify AppConfig is inactive")
Eventually(
func() types.RollingStatus {
k8sClient.Get(ctx, client.ObjectKey{Namespace: namespaceName, Name: appContextName}, &appContext)
return appContext.Status.RollingStatus
},
time.Second*30, time.Millisecond*500).Should(BeEquivalentTo(types.InactiveAfterRollingCompleted))
verifyIngress := func(domain string) {
ingress := &corev1beta1.Ingress{}
Eventually(func() error {
var err error
if err = k8sClient.Get(ctx, types.NamespacedName{Namespace: namespaceName, Name: appRollout.Spec.ComponentList[0]}, ingress); err != nil {
return err
}
owner := metav1.GetControllerOf(ingress)
if owner == nil {
return fmt.Errorf("ingress don't have controller owner")
}
if owner.Kind != v1beta1.ResourceTrackerKind {
return fmt.Errorf("ingress owner kind miss match wants %s actually %s", v1beta1.ResourceTrackerKind, owner.Kind)
}
rtName := dispatch.ConstructResourceTrackerName(appRollout.Spec.TargetAppRevisionName, appRollout.Namespace)
if owner.Name != rtName {
return fmt.Errorf("ingress owner error wants %s actually %s", rtName, owner.Name)
}
if ingress.Spec.Rules[0].Host != domain {
return fmt.Errorf("domain mismatch wants %s actually %s", domain, ingress.Spec.Rules[0].Host)
}
return nil
}, time.Second*30, time.Microsecond*300).Should(BeNil())
}
applyTwoAppVersion := func() {
@@ -275,7 +316,6 @@ var _ = Describe("Cloneset based rollout tests", func() {
time.Second*10, time.Millisecond*10).Should(BeEquivalentTo(oamstd.RollingInBatchesState))
verifyRolloutSucceeded(appRollout.Spec.TargetAppRevisionName)
verifyAppConfigInactive(appRollout.Spec.SourceAppRevisionName)
}
BeforeEach(func() {
@@ -363,7 +403,6 @@ var _ = Describe("Cloneset based rollout tests", func() {
RolloutBatches) - 1))
Expect(k8sClient.Update(ctx, &appRollout)).Should(Succeed())
verifyRolloutSucceeded(appRollout.Spec.TargetAppRevisionName)
verifyAppConfigInactive(appRollout.Spec.SourceAppRevisionName)
})
It("Test pause and modify rollout plan after rolling succeeded", func() {
@@ -461,8 +500,6 @@ var _ = Describe("Cloneset based rollout tests", func() {
time.Second*10, time.Millisecond*10).Should(BeEquivalentTo(oamstd.RollingInBatchesState))
verifyRolloutSucceeded(appRollout.Spec.TargetAppRevisionName)
verifyAppConfigInactive(appRollout.Spec.SourceAppRevisionName)
rollForwardToSource()
})
@@ -570,12 +607,9 @@ var _ = Describe("Cloneset based rollout tests", func() {
appRollout.Spec.RolloutPlan.BatchPartition = nil
return k8sClient.Update(ctx, &appRollout)
}, time.Second*15, time.Millisecond*500).Should(Succeed())
verifyRolloutSucceeded(appRollout.Spec.TargetAppRevisionName)
verifyAppConfigInactive(appRollout.Spec.SourceAppRevisionName)
// wait for a bit until the application takes back control
By("Verify that application does not control the cloneset")
By("Verify that resourceTracker control the cloneset")
clonesetName := appRollout.Spec.ComponentList[0]
Eventually(
func() string {
@@ -585,7 +619,75 @@ var _ = Describe("Cloneset based rollout tests", func() {
return ""
}
return clonesetOwner.Kind
}, time.Second*30, time.Second).Should(BeEquivalentTo(v1alpha2.ApplicationContextKind))
}, time.Second*30, time.Second).Should(BeEquivalentTo(v1beta1.ResourceTrackerKind))
})
It("Test rollout will update same name trait", func() {
CreateClonesetDef()
CreateIngressDef()
applySourceApp("app-with-ingress-source.yaml")
By("Apply the application rollout go directly to the target")
appRollout = v1beta1.AppRollout{}
Expect(common.ReadYamlToObject("testdata/rollout/cloneset/appRollout.yaml", &appRollout)).Should(BeNil())
appRollout.Namespace = namespaceName
appRollout.Spec.SourceAppRevisionName = ""
appRollout.Spec.TargetAppRevisionName = utils.ConstructRevisionName(app.GetName(), 1)
appRollout.Spec.RolloutPlan.TargetSize = pointer.Int32Ptr(7)
appRollout.Spec.RolloutPlan.BatchPartition = nil
createAppRolling(&appRollout)
appRolloutName = appRollout.Name
verifyRolloutSucceeded(appRollout.Spec.TargetAppRevisionName)
By("verify ingress status")
verifyIngress("test.example.com")
By("rollout to revision 2")
updateApp("app-with-ingress-target.yaml")
Eventually(
func() error {
k8sClient.Get(ctx, client.ObjectKey{Namespace: namespaceName, Name: appRollout.Name}, &appRollout)
appRollout.Spec.SourceAppRevisionName = utils.ConstructRevisionName(app.GetName(), 1)
appRollout.Spec.TargetAppRevisionName = utils.ConstructRevisionName(app.GetName(), 2)
appRollout.Spec.RolloutPlan.BatchPartition = nil
return k8sClient.Update(ctx, &appRollout)
}, time.Second*10, time.Millisecond*500).Should(Succeed())
verifyRolloutSucceeded(appRollout.Spec.TargetAppRevisionName)
By("verify after rollout ingress status")
verifyIngress("test-1.example.com")
})
It("Test rollout succeed will gc useless trait", func() {
CreateClonesetDef()
CreateIngressDef()
applySourceApp("app-with-ingress-source.yaml")
By("Apply the application rollout go directly to the target")
appRollout = v1beta1.AppRollout{}
Expect(common.ReadYamlToObject("testdata/rollout/cloneset/appRollout.yaml", &appRollout)).Should(BeNil())
appRollout.Namespace = namespaceName
appRollout.Spec.SourceAppRevisionName = ""
appRollout.Spec.TargetAppRevisionName = utils.ConstructRevisionName(app.GetName(), 1)
appRollout.Spec.RolloutPlan.TargetSize = pointer.Int32Ptr(7)
appRollout.Spec.RolloutPlan.BatchPartition = nil
createAppRolling(&appRollout)
appRolloutName = appRollout.Name
verifyRolloutSucceeded(appRollout.Spec.TargetAppRevisionName)
By("verify ingress status")
verifyIngress("test.example.com")
By("rollout to revision 2 to disable ingress trait")
updateApp("app-remove-ingress.yaml")
Eventually(
func() error {
k8sClient.Get(ctx, client.ObjectKey{Namespace: namespaceName, Name: appRollout.Name}, &appRollout)
appRollout.Spec.SourceAppRevisionName = utils.ConstructRevisionName(app.GetName(), 1)
appRollout.Spec.TargetAppRevisionName = utils.ConstructRevisionName(app.GetName(), 2)
appRollout.Spec.RolloutPlan.BatchPartition = nil
return k8sClient.Update(ctx, &appRollout)
}, time.Second*10, time.Millisecond*500).Should(Succeed())
verifyRolloutSucceeded(appRollout.Spec.TargetAppRevisionName)
By("verify after rollout ingress have been removed")
Eventually(func() error {
ingress := &corev1beta1.Ingress{}
return k8sClient.Get(ctx, types.NamespacedName{Namespace: namespaceName, Name: appRollout.Spec.ComponentList[0]}, ingress)
}, time.Second*30, 300*time.Microsecond).Should(util.NotFoundMatcher{})
})
PIt("Test rolling by changing the definition", func() {
@@ -611,7 +713,6 @@ var _ = Describe("Cloneset based rollout tests", func() {
createAppRolling(&newAppRollout)
verifyRolloutSucceeded(appRollout.Spec.TargetAppRevisionName)
verifyAppConfigInactive(appRollout.Spec.SourceAppRevisionName)
// Clean up
k8sClient.Delete(ctx, &appRollout)
})

View File

@@ -0,0 +1,17 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: test-e2e-rolling
annotations:
"app.oam.dev/rollout-template": "true"
spec:
components:
- name: metrics-provider
type: clonesetservice
properties:
cmd:
- ./podinfo
- stress-cpu=1
image: stefanprodan/podinfo:5.0.2
port: 8080
updateStrategyType: InPlaceOnly

View File

@@ -0,0 +1,23 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: test-e2e-rolling
annotations:
"app.oam.dev/rollout-template": "true"
spec:
components:
- name: metrics-provider
type: clonesetservice
properties:
cmd:
- ./podinfo
- stress-cpu=1
image: stefanprodan/podinfo:4.0.3
port: 8080
updateStrategyType: InPlaceOnly
traits:
- type: ingress
properties:
domain: test.example.com
http:
"/": 8080

View File

@@ -0,0 +1,23 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: test-e2e-rolling
annotations:
"app.oam.dev/rollout-template": "true"
spec:
components:
- name: metrics-provider
type: clonesetservice
properties:
cmd:
- ./podinfo
- stress-cpu=1
image: stefanprodan/podinfo:5.0.2
port: 8080
updateStrategyType: InPlaceOnly
traits:
- type: ingress
properties:
domain: test-1.example.com
http:
"/": 8080

View File

@@ -0,0 +1,79 @@
apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: "Enable public web traffic for the component."
name: ingress
namespace: vela-system
spec:
status:
customStatus: |-
let igs = context.outputs.ingress.status.loadBalancer.ingress
if igs == _|_ {
message: "No loadBalancer found, visiting by using 'vela port-forward " + context.appName + " --route'\n"
}
if len(igs) > 0 {
if igs[0].ip != _|_ {
message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host + ", IP: " + igs[0].ip
}
if igs[0].ip == _|_ {
message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host
}
}
healthPolicy: |
isHealth: len(context.outputs.service.spec.clusterIP) > 0
appliesToWorkloads:
- deployments.apps
podDisruptive: false
schematic:
cue:
template: |
// trait template can have multiple outputs in one trait
outputs: service: {
apiVersion: "v1"
kind: "Service"
metadata:
name: context.name
spec: {
selector: {
"app.oam.dev/component": context.name
}
ports: [
for k, v in parameter.http {
port: v
targetPort: v
},
]
}
}
outputs: ingress: {
apiVersion: "networking.k8s.io/v1beta1"
kind: "Ingress"
metadata:
name: context.name
spec: {
rules: [{
host: parameter.domain
http: {
paths: [
for k, v in parameter.http {
path: k
backend: {
serviceName: context.name
servicePort: v
}
},
]
}
}]
}
}
parameter: {
// +usage=Specify the domain you want to expose
domain: string
// +usage=Specify the mapping relationship between the http path and the workload port
http: [string]: int
}