github.com/oam-dev/kubevela@v1.9.11/test/e2e-multicluster-test/multicluster_test.go (about)

     1  /*
     2   Copyright 2021. The KubeVela Authors.
     3  
     4   Licensed under the Apache License, Version 2.0 (the "License");
     5   you may not use this file except in compliance with the License.
     6   You may obtain a copy of the License at
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    10   Unless required by applicable law or agreed to in writing, software
    11   distributed under the License is distributed on an "AS IS" BASIS,
    12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   See the License for the specific language governing permissions and
    14   limitations under the License.
    15  */
    16  
    17  package e2e_multicluster_test
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"os"
    24  	"strings"
    25  	"time"
    26  
    27  	"github.com/kubevela/pkg/controller/reconciler"
    28  	workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
    29  	. "github.com/onsi/ginkgo/v2"
    30  	. "github.com/onsi/gomega"
    31  	appsv1 "k8s.io/api/apps/v1"
    32  	v1 "k8s.io/api/authentication/v1"
    33  	corev1 "k8s.io/api/core/v1"
    34  	rbacv1 "k8s.io/api/rbac/v1"
    35  	kerrors "k8s.io/apimachinery/pkg/api/errors"
    36  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    37  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    38  	"k8s.io/apimachinery/pkg/runtime"
    39  	"k8s.io/apimachinery/pkg/types"
    40  	"k8s.io/client-go/tools/clientcmd"
    41  	"sigs.k8s.io/controller-runtime/pkg/client"
    42  	"sigs.k8s.io/yaml"
    43  
    44  	"github.com/kubevela/pkg/util/rand"
    45  
    46  	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
    47  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
    48  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    49  	kubevelatypes "github.com/oam-dev/kubevela/apis/types"
    50  	"github.com/oam-dev/kubevela/pkg/multicluster"
    51  	"github.com/oam-dev/kubevela/pkg/oam"
    52  )
    53  
    54  func initializeContext() (hubCtx context.Context, workerCtx context.Context) {
    55  	hubCtx = context.Background()
    56  	workerCtx = multicluster.ContextWithClusterName(hubCtx, WorkerClusterName)
    57  	return
    58  }
    59  
    60  func initializeContextAndNamespace() (hubCtx context.Context, workerCtx context.Context, namespace string) {
    61  	hubCtx, workerCtx = initializeContext()
    62  	// initialize test namespace
    63  	namespace = "test-mc-" + rand.RandomString(4)
    64  	ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}
    65  	Expect(k8sClient.Create(hubCtx, ns.DeepCopy())).Should(Succeed())
    66  	Expect(k8sClient.Create(workerCtx, ns.DeepCopy())).Should(Succeed())
    67  	return
    68  }
    69  
    70  func cleanUpNamespace(hubCtx context.Context, workerCtx context.Context, namespace string) {
    71  	hubNs := &corev1.Namespace{}
    72  	Expect(k8sClient.Get(hubCtx, types.NamespacedName{Name: namespace}, hubNs)).Should(Succeed())
    73  	Expect(k8sClient.Delete(hubCtx, hubNs)).Should(Succeed())
    74  	workerNs := &corev1.Namespace{}
    75  	Expect(k8sClient.Get(workerCtx, types.NamespacedName{Name: namespace}, workerNs)).Should(Succeed())
    76  	Expect(k8sClient.Delete(workerCtx, workerNs)).Should(Succeed())
    77  }
    78  
    79  var _ = Describe("Test multicluster scenario", func() {
    80  
    81  	Context("Test vela cluster command", func() {
    82  
    83  		It("Test join cluster by X509 kubeconfig, rename it and detach it.", func() {
    84  			const oldClusterName = "test-worker-cluster"
    85  			const newClusterName = "test-cluster-worker"
    86  			_, err := execCommand("cluster", "list")
    87  			Expect(err).Should(Succeed())
    88  			_, err = execCommand("cluster", "join", "/tmp/worker.kubeconfig", "--name", oldClusterName)
    89  			Expect(err).Should(Succeed())
    90  			_, err = execCommand("cluster", "join", "/tmp/worker.kubeconfig", "--name", oldClusterName, "-y")
    91  			Expect(err).Should(Succeed())
    92  			out, err := execCommand("cluster", "list")
    93  			Expect(err).Should(Succeed())
    94  			Expect(out).Should(ContainSubstring(oldClusterName))
    95  			_, err = execCommand("cluster", "rename", oldClusterName, newClusterName)
    96  			Expect(err).Should(Succeed())
    97  			out, err = execCommand("cluster", "list")
    98  			Expect(err).Should(Succeed())
    99  			Expect(out).Should(ContainSubstring(newClusterName))
   100  			_, err = execCommand("cluster", "detach", newClusterName)
   101  			Expect(err).Should(Succeed())
   102  			out, err = execCommand("cluster", "list")
   103  			Expect(err).Should(Succeed())
   104  			Expect(out).ShouldNot(ContainSubstring(newClusterName))
   105  		})
   106  
   107  		It("Test manage labels for cluster", func() {
   108  			_, err := execCommand("cluster", "labels", "add", WorkerClusterName, "purpose=test,creator=e2e")
   109  			Expect(err).Should(Succeed())
   110  			out, err := execCommand("cluster", "list")
   111  			Expect(err).Should(Succeed())
   112  			Expect(out).Should(ContainSubstring("purpose"))
   113  			_, err = execCommand("cluster", "labels", "del", WorkerClusterName, "purpose")
   114  			Expect(err).Should(Succeed())
   115  			out, err = execCommand("cluster", "list")
   116  			Expect(err).Should(Succeed())
   117  			Expect(out).ShouldNot(ContainSubstring("purpose"))
   118  		})
   119  
   120  		It("Test alias for cluster", func() {
   121  			_, err := execCommand("cluster", "alias", WorkerClusterName, "alias-worker")
   122  			Expect(err).Should(Succeed())
   123  			out, err := execCommand("cluster", "list")
   124  			Expect(err).Should(Succeed())
   125  			Expect(out).Should(ContainSubstring("alias-worker"))
   126  		})
   127  
   128  		It("Test generate service account kubeconfig", func() {
   129  			_, workerCtx := initializeContext()
   130  			By("create service account kubeconfig in worker cluster")
   131  			key := time.Now().UnixNano()
   132  			serviceAccountName := fmt.Sprintf("test-service-account-%d", key)
   133  			serviceAccountNamespace := "kube-system"
   134  			serviceAccount := &corev1.ServiceAccount{
   135  				ObjectMeta: metav1.ObjectMeta{Namespace: serviceAccountNamespace, Name: serviceAccountName},
   136  			}
   137  			Expect(k8sClient.Create(workerCtx, serviceAccount)).Should(Succeed())
   138  			defer func() {
   139  				Expect(k8sClient.Get(workerCtx, types.NamespacedName{Namespace: "kube-system", Name: serviceAccountName}, serviceAccount)).Should(Succeed())
   140  				Expect(k8sClient.Delete(workerCtx, serviceAccount)).Should(Succeed())
   141  			}()
   142  			clusterRoleBindingName := fmt.Sprintf("test-cluster-role-binding-%d", key)
   143  			clusterRoleBinding := &rbacv1.ClusterRoleBinding{
   144  				ObjectMeta: metav1.ObjectMeta{Name: clusterRoleBindingName},
   145  				Subjects:   []rbacv1.Subject{{Kind: "ServiceAccount", Name: serviceAccountName, Namespace: serviceAccountNamespace}},
   146  				RoleRef:    rbacv1.RoleRef{Name: "cluster-admin", APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole"},
   147  			}
   148  			Expect(k8sClient.Create(workerCtx, clusterRoleBinding)).Should(Succeed())
   149  			defer func() {
   150  				Expect(k8sClient.Get(workerCtx, types.NamespacedName{Namespace: serviceAccountNamespace, Name: clusterRoleBindingName}, clusterRoleBinding)).Should(Succeed())
   151  				Expect(k8sClient.Delete(workerCtx, clusterRoleBinding)).Should(Succeed())
   152  			}()
   153  			serviceAccount = &corev1.ServiceAccount{}
   154  			By("Generating a token for SA")
   155  			tr := &v1.TokenRequest{}
   156  			token, err := k8sCli.CoreV1().ServiceAccounts(serviceAccountNamespace).CreateToken(workerCtx, serviceAccountName, tr, metav1.CreateOptions{})
   157  			Expect(err).Should(BeNil())
   158  			config, err := clientcmd.LoadFromFile(WorkerClusterKubeConfigPath)
   159  			Expect(err).Should(Succeed())
   160  			currentContext, ok := config.Contexts[config.CurrentContext]
   161  			Expect(ok).Should(BeTrue())
   162  			authInfo, ok := config.AuthInfos[currentContext.AuthInfo]
   163  			Expect(ok).Should(BeTrue())
   164  			authInfo.Token = token.Status.Token
   165  			authInfo.ClientKeyData = nil
   166  			authInfo.ClientCertificateData = nil
   167  			kubeconfigFilePath := fmt.Sprintf("/tmp/worker.sa-%d.kubeconfig", key)
   168  			Expect(clientcmd.WriteToFile(*config, kubeconfigFilePath)).Should(Succeed())
   169  			defer func() {
   170  				Expect(os.Remove(kubeconfigFilePath)).Should(Succeed())
   171  			}()
   172  			// try to join cluster with service account token based kubeconfig
   173  			clusterName := fmt.Sprintf("cluster-sa-%d", key)
   174  			_, err = execCommand("cluster", "join", kubeconfigFilePath, "--name", clusterName)
   175  			Expect(err).Should(Succeed())
   176  			_, err = execCommand("cluster", "detach", clusterName)
   177  			Expect(err).Should(Succeed())
   178  		})
   179  
   180  		It("Test vela cluster export-config", func() {
   181  			out, err := execCommand("cluster", "export-config")
   182  			Expect(err).Should(Succeed())
   183  			Expect(out).Should(ContainSubstring("name: " + WorkerClusterName))
   184  		})
   185  
   186  	})
   187  
   188  	Context("Test multi-cluster Application", func() {
   189  
   190  		var namespace string
   191  		var testNamespace string
   192  		var prodNamespace string
   193  		var hubCtx context.Context
   194  		var workerCtx context.Context
   195  
   196  		BeforeEach(func() {
   197  			hubCtx, workerCtx, namespace = initializeContextAndNamespace()
   198  			_, _, testNamespace = initializeContextAndNamespace()
   199  			_, _, prodNamespace = initializeContextAndNamespace()
   200  		})
   201  
   202  		AfterEach(func() {
   203  			cleanUpNamespace(hubCtx, workerCtx, namespace)
   204  			cleanUpNamespace(hubCtx, workerCtx, testNamespace)
   205  			cleanUpNamespace(hubCtx, workerCtx, prodNamespace)
   206  		})
   207  
   208  		It("Test deploy multi-cluster application with target", func() {
   209  			By("apply application")
   210  			app := &v1beta1.Application{
   211  				ObjectMeta: metav1.ObjectMeta{Namespace: namespace, Name: "test-app-target"},
   212  				Spec: v1beta1.ApplicationSpec{
   213  					Components: []common.ApplicationComponent{{
   214  						Name:       "test-busybox",
   215  						Type:       "webservice",
   216  						Properties: &runtime.RawExtension{Raw: []byte(`{"image":"busybox","cmd":["sleep","86400"]}`)},
   217  					}},
   218  					Policies: []v1beta1.AppPolicy{{
   219  						Name:       "topology-local",
   220  						Type:       "topology",
   221  						Properties: &runtime.RawExtension{Raw: []byte(fmt.Sprintf(`{"clusters":["local"],"namespace":"%s"}`, testNamespace))},
   222  					}, {
   223  						Name:       "topology-remote",
   224  						Type:       "topology",
   225  						Properties: &runtime.RawExtension{Raw: []byte(fmt.Sprintf(`{"clusters":["%s"],"namespace":"%s"}`, WorkerClusterName, prodNamespace))},
   226  					}},
   227  				},
   228  			}
   229  			Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
   230  			Eventually(func(g Gomega) {
   231  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Name: "test-busybox", Namespace: testNamespace}, &appsv1.Deployment{})).Should(Succeed())
   232  				g.Expect(k8sClient.Get(workerCtx, types.NamespacedName{Name: "test-busybox", Namespace: prodNamespace}, &appsv1.Deployment{})).Should(Succeed())
   233  			}, time.Minute).Should(Succeed())
   234  		})
   235  
   236  		It("Test re-deploy application with old revisions", func() {
   237  			By("apply application")
   238  			app := &v1beta1.Application{
   239  				ObjectMeta: metav1.ObjectMeta{Namespace: namespace, Name: "test-app-target"},
   240  				Spec: v1beta1.ApplicationSpec{
   241  					Components: []common.ApplicationComponent{{
   242  						Name:       "test-busybox",
   243  						Type:       "webservice",
   244  						Properties: &runtime.RawExtension{Raw: []byte(`{"image":"busybox","cmd":["sleep","86400"]}`)},
   245  					}},
   246  					Policies: []v1beta1.AppPolicy{{
   247  						Name:       "topology-local",
   248  						Type:       "topology",
   249  						Properties: &runtime.RawExtension{Raw: []byte(fmt.Sprintf(`{"clusters":["local"],"namespace":"%s"}`, testNamespace))},
   250  					},
   251  					}}}
   252  			oam.SetPublishVersion(app, "alpha")
   253  			Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
   254  			Eventually(func(g Gomega) {
   255  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Name: "test-busybox", Namespace: testNamespace}, &appsv1.Deployment{})).Should(Succeed())
   256  			}, time.Minute).Should(Succeed())
   257  
   258  			By("update application to new version")
   259  			appKey := client.ObjectKeyFromObject(app)
   260  			Eventually(func(g Gomega) {
   261  				g.Expect(k8sClient.Get(hubCtx, appKey, app)).Should(Succeed())
   262  				app.Spec.Components[0].Name = "test-busybox-v2"
   263  				oam.SetPublishVersion(app, "beta")
   264  				g.Expect(k8sClient.Update(hubCtx, app)).Should(Succeed())
   265  			}, 15*time.Second).Should(Succeed())
   266  			Eventually(func(g Gomega) {
   267  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Name: "test-busybox-v2", Namespace: testNamespace}, &appsv1.Deployment{})).Should(Succeed())
   268  				err := k8sClient.Get(hubCtx, types.NamespacedName{Name: "test-busybox", Namespace: testNamespace}, &appsv1.Deployment{})
   269  				g.Expect(kerrors.IsNotFound(err)).Should(BeTrue())
   270  			}, time.Minute).Should(Succeed())
   271  
   272  			By("Re-publish application to v1")
   273  			_, err := execCommand("up", appKey.Name, "-n", appKey.Namespace, "--revision", appKey.Name+"-v1", "--publish-version", "v1.0")
   274  			Expect(err).Should(Succeed())
   275  
   276  			Eventually(func(g Gomega) {
   277  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Name: "test-busybox", Namespace: testNamespace}, &appsv1.Deployment{})).Should(Succeed())
   278  				err := k8sClient.Get(hubCtx, types.NamespacedName{Name: "test-busybox-v2", Namespace: testNamespace}, &appsv1.Deployment{})
   279  				g.Expect(kerrors.IsNotFound(err)).Should(BeTrue())
   280  			}, 2*time.Minute).Should(Succeed())
   281  		})
   282  
   283  		It("Test applications sharing resources", func() {
   284  			createApp := func(name string) *v1beta1.Application {
   285  				return &v1beta1.Application{
   286  					ObjectMeta: metav1.ObjectMeta{Namespace: namespace, Name: name},
   287  					Spec: v1beta1.ApplicationSpec{
   288  						Components: []common.ApplicationComponent{{
   289  							Name:       "shared-resource-" + name,
   290  							Type:       "k8s-objects",
   291  							Properties: &runtime.RawExtension{Raw: []byte(`{"objects":[{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"shared"},"data":{"key":"value"}}]}`)},
   292  						}, {
   293  							Name:       "no-shared-resource-" + name,
   294  							Type:       "k8s-objects",
   295  							Properties: &runtime.RawExtension{Raw: []byte(`{"objects":[{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"non-shared-` + name + `"},"data":{"key":"value"}}]}`)},
   296  						}},
   297  						Policies: []v1beta1.AppPolicy{{
   298  							Type:       "shared-resource",
   299  							Name:       "shared-resource",
   300  							Properties: &runtime.RawExtension{Raw: []byte(`{"rules":[{"selector":{"componentNames":["shared-resource-` + name + `"]}}]}`)},
   301  						}},
   302  					},
   303  				}
   304  			}
   305  			app1 := createApp("app1")
   306  			Expect(k8sClient.Create(hubCtx, app1)).Should(Succeed())
   307  			Eventually(func(g Gomega) {
   308  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app1), app1)).Should(Succeed())
   309  				g.Expect(app1.Status.Phase).Should(Equal(common.ApplicationRunning))
   310  			}, 10*time.Second).Should(Succeed())
   311  			app2 := createApp("app2")
   312  			Expect(k8sClient.Create(hubCtx, app2)).Should(Succeed())
   313  			Eventually(func(g Gomega) {
   314  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app2), app2)).Should(Succeed())
   315  				g.Expect(app2.Status.Phase).Should(Equal(common.ApplicationRunning))
   316  			}, 10*time.Second).Should(Succeed())
   317  			app3 := createApp("app3")
   318  			Expect(k8sClient.Create(hubCtx, app3)).Should(Succeed())
   319  			Eventually(func(g Gomega) {
   320  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app3), app3)).Should(Succeed())
   321  				g.Expect(app3.Status.Phase).Should(Equal(common.ApplicationRunning))
   322  			}, 10*time.Second).Should(Succeed())
   323  			Eventually(func(g Gomega) {
   324  				cm := &corev1.ConfigMap{}
   325  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "shared"}, cm)).Should(Succeed())
   326  				g.Expect(cm.GetAnnotations()[oam.AnnotationAppSharedBy]).Should(SatisfyAll(ContainSubstring("app1"), ContainSubstring("app2"), ContainSubstring("app3")))
   327  				g.Expect(cm.GetLabels()[oam.LabelAppName]).Should(SatisfyAny(Equal("app1"), Equal("app2"), Equal("app3")))
   328  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "non-shared-app1"}, &corev1.ConfigMap{})).Should(Succeed())
   329  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "non-shared-app2"}, &corev1.ConfigMap{})).Should(Succeed())
   330  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "non-shared-app3"}, &corev1.ConfigMap{})).Should(Succeed())
   331  			}, 45*time.Second).Should(Succeed())
   332  			Expect(k8sClient.Delete(hubCtx, app2)).Should(Succeed())
   333  			Eventually(func(g Gomega) {
   334  				cm := &corev1.ConfigMap{}
   335  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "shared"}, cm)).Should(Succeed())
   336  				g.Expect(cm.GetAnnotations()[oam.AnnotationAppSharedBy]).Should(SatisfyAll(ContainSubstring("app1"), ContainSubstring("app3")))
   337  				g.Expect(cm.GetAnnotations()[oam.AnnotationAppSharedBy]).ShouldNot(SatisfyAny(ContainSubstring("app2")))
   338  				g.Expect(cm.GetLabels()[oam.LabelAppName]).Should(SatisfyAny(Equal("app1"), Equal("app3")))
   339  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "non-shared-app1"}, &corev1.ConfigMap{})).Should(Succeed())
   340  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "non-shared-app2"}, &corev1.ConfigMap{})).Should(Satisfy(kerrors.IsNotFound))
   341  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "non-shared-app3"}, &corev1.ConfigMap{})).Should(Succeed())
   342  			}, 10*time.Second).Should(Succeed())
   343  			Expect(k8sClient.Delete(hubCtx, app1)).Should(Succeed())
   344  			Eventually(func(g Gomega) {
   345  				cm := &corev1.ConfigMap{}
   346  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "shared"}, cm)).Should(Succeed())
   347  				g.Expect(cm.GetAnnotations()[oam.AnnotationAppSharedBy]).Should(SatisfyAll(ContainSubstring("app3")))
   348  				g.Expect(cm.GetAnnotations()[oam.AnnotationAppSharedBy]).ShouldNot(SatisfyAny(ContainSubstring("app1"), ContainSubstring("app2")))
   349  				g.Expect(cm.GetLabels()[oam.LabelAppName]).Should(Equal("app3"))
   350  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "non-shared-app1"}, &corev1.ConfigMap{})).Should(Satisfy(kerrors.IsNotFound))
   351  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "non-shared-app3"}, &corev1.ConfigMap{})).Should(Succeed())
   352  			}, 10*time.Second).Should(Succeed())
   353  			Expect(k8sClient.Delete(hubCtx, app3)).Should(Succeed())
   354  			Eventually(func(g Gomega) {
   355  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "shared"}, &corev1.ConfigMap{})).Should(Satisfy(kerrors.IsNotFound))
   356  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "non-shared-app3"}, &corev1.ConfigMap{})).Should(Satisfy(kerrors.IsNotFound))
   357  			}, 10*time.Second).Should(Succeed())
   358  		})
   359  
   360  		It("Test applications with bad resource", func() {
   361  			bs, err := os.ReadFile("./testdata/app/app-bad-resource.yaml")
   362  			Expect(err).Should(Succeed())
   363  			appYaml := strings.ReplaceAll(string(bs), "TEST_NAMESPACE", testNamespace)
   364  			app := &v1beta1.Application{}
   365  			Expect(yaml.Unmarshal([]byte(appYaml), app)).Should(Succeed())
   366  			ctx := context.Background()
   367  			Expect(k8sClient.Create(ctx, app)).Should(Succeed())
   368  			Eventually(func(g Gomega) {
   369  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app), app)).Should(Succeed())
   370  				g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunningWorkflow))
   371  				g.Expect(len(app.Status.Workflow.Steps) > 0).Should(BeTrue())
   372  				g.Expect(app.Status.Workflow.Steps[0].Message).Should(ContainSubstring("is invalid"))
   373  				rts := &v1beta1.ResourceTrackerList{}
   374  				g.Expect(k8sClient.List(hubCtx, rts, client.MatchingLabels{oam.LabelAppName: app.Name, oam.LabelAppNamespace: app.Namespace})).Should(Succeed())
   375  				g.Expect(len(rts.Items)).Should(Equal(0))
   376  			}, 20*time.Second).Should(Succeed())
   377  			Expect(k8sClient.Delete(ctx, app)).Should(Succeed())
   378  			Eventually(func(g Gomega) {
   379  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app), app)).Should(Satisfy(kerrors.IsNotFound))
   380  			}, 10*time.Second).Should(Succeed())
   381  		})
   382  
   383  		It("Test applications with env and storage trait", func() {
   384  			bs, err := os.ReadFile("./testdata/app/app-with-env-and-storage.yaml")
   385  			Expect(err).Should(Succeed())
   386  			appYaml := strings.ReplaceAll(string(bs), "TEST_NAMESPACE", testNamespace)
   387  			app := &v1beta1.Application{}
   388  			Expect(yaml.Unmarshal([]byte(appYaml), app)).Should(Succeed())
   389  			Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
   390  			Eventually(func(g Gomega) {
   391  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app), app)).Should(Succeed())
   392  				g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
   393  			}, 20*time.Second).Should(Succeed())
   394  		})
   395  
   396  		It("Test applications with gc policy change (onAppUpdate -> never)", func() {
   397  			bs, err := os.ReadFile("./testdata/app/app-gc-policy-change.yaml")
   398  			Expect(err).Should(Succeed())
   399  			appYaml := strings.ReplaceAll(string(bs), "TEST_NAMESPACE", testNamespace)
   400  			app := &v1beta1.Application{}
   401  			Expect(yaml.Unmarshal([]byte(appYaml), app)).Should(Succeed())
   402  			Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
   403  			Eventually(func(g Gomega) {
   404  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app), app)).Should(Succeed())
   405  				g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
   406  			}, 20*time.Second).Should(Succeed())
   407  
   408  			By("update gc policy to never")
   409  			Eventually(func(g Gomega) {
   410  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app), app))
   411  				gcPolicy := &v1alpha1.GarbageCollectPolicySpec{}
   412  				g.Expect(json.Unmarshal(app.Spec.Policies[0].Properties.Raw, gcPolicy)).Should(Succeed())
   413  				gcPolicy.Rules[0].Strategy = v1alpha1.GarbageCollectStrategyNever
   414  				bs, err = json.Marshal(gcPolicy)
   415  				g.Expect(err).Should(Succeed())
   416  				app.Spec.Policies[0].Properties = &runtime.RawExtension{Raw: bs}
   417  				g.Expect(k8sClient.Update(hubCtx, app)).Should(Succeed())
   418  			}, 10*time.Second).Should(Succeed())
   419  
   420  			By("check app updated and resource still exists")
   421  			Eventually(func(g Gomega) {
   422  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app), app)).Should(Succeed())
   423  				g.Expect(app.Status.ObservedGeneration).Should(Equal(int64(2)))
   424  				g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
   425  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: testNamespace, Name: "gc-policy-test"}, &corev1.ConfigMap{})).Should(Succeed())
   426  			}, 20*time.Second).Should(Succeed())
   427  
   428  			By("update app to new object")
   429  			Eventually(func(g Gomega) {
   430  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app), app))
   431  				app.Spec.Components[0].Properties = &runtime.RawExtension{Raw: []byte(`{"objects":[{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"another"},"data":{"key":"new-val"}}]}`)}
   432  				g.Expect(k8sClient.Update(hubCtx, app)).Should(Succeed())
   433  			}).Should(Succeed())
   434  
   435  			By("check app updated and resource still exists")
   436  			Eventually(func(g Gomega) {
   437  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app), app)).Should(Succeed())
   438  				g.Expect(app.Status.ObservedGeneration).Should(Equal(int64(3)))
   439  				g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
   440  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: testNamespace, Name: "gc-policy-test"}, &corev1.ConfigMap{})).Should(Succeed())
   441  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: testNamespace, Name: "another"}, &corev1.ConfigMap{})).Should(Succeed())
   442  			}, 20*time.Second).Should(Succeed())
   443  
   444  			By("delete app and check resource")
   445  			Eventually(func(g Gomega) {
   446  				g.Expect(client.IgnoreNotFound(k8sClient.Delete(hubCtx, app))).Should(Succeed())
   447  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: testNamespace, Name: "gc-policy-test"}, &corev1.ConfigMap{})).Should(Succeed())
   448  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: testNamespace, Name: "another"}, &corev1.ConfigMap{})).Should(Satisfy(kerrors.IsNotFound))
   449  			})
   450  		})
   451  
   452  		It("Test Application with env in webservice and labels & storage trait", func() {
   453  			bs, err := os.ReadFile("./testdata/app/app-with-env-labels-storage.yaml")
   454  			Expect(err).Should(Succeed())
   455  			app := &v1beta1.Application{}
   456  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
   457  			app.SetNamespace(namespace)
   458  			Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
   459  			deploy := &appsv1.Deployment{}
   460  			Eventually(func(g Gomega) {
   461  				g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "test"}, deploy)).Should(Succeed())
   462  			}, 15*time.Second).Should(Succeed())
   463  			Expect(deploy.GetLabels()["key"]).Should(Equal("val"))
   464  			Expect(len(deploy.Spec.Template.Spec.Containers[0].Env)).Should(Equal(1))
   465  			Expect(deploy.Spec.Template.Spec.Containers[0].Env[0].Name).Should(Equal("testKey"))
   466  			Expect(deploy.Spec.Template.Spec.Containers[0].Env[0].Value).Should(Equal("testValue"))
   467  			Expect(len(deploy.Spec.Template.Spec.Volumes)).Should(Equal(1))
   468  		})
   469  
   470  		It("Test application with collect-service-endpoint and export-data", func() {
   471  			By("create application")
   472  			bs, err := os.ReadFile("./testdata/app/app-collect-service-endpoint-and-export.yaml")
   473  			Expect(err).Should(Succeed())
   474  			app := &v1beta1.Application{}
   475  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
   476  			app.SetNamespace(testNamespace)
   477  			Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
   478  			Eventually(func(g Gomega) {
   479  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app), app)).Should(Succeed())
   480  				g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
   481  			}, 20*time.Second).Should(Succeed())
   482  
   483  			By("test dispatched resource")
   484  			svc := &corev1.Service{}
   485  			Expect(k8sClient.Get(hubCtx, client.ObjectKey{Namespace: testNamespace, Name: "busybox"}, svc)).Should(Succeed())
   486  			host := "busybox." + testNamespace
   487  			cm := &corev1.ConfigMap{}
   488  			Expect(k8sClient.Get(hubCtx, client.ObjectKey{Namespace: testNamespace, Name: app.Name}, cm)).Should(Succeed())
   489  			Expect(cm.Data["host"]).Should(Equal(host))
   490  			Expect(k8sClient.Get(workerCtx, client.ObjectKey{Namespace: testNamespace, Name: app.Name}, cm)).Should(Succeed())
   491  			Expect(cm.Data["host"]).Should(Equal(host))
   492  		})
   493  
   494  		It("Test application with workflow change will rerun", func() {
   495  			By("create application")
   496  			bs, err := os.ReadFile("./testdata/app/app-lite-with-workflow.yaml")
   497  			Expect(err).Should(Succeed())
   498  			app := &v1beta1.Application{}
   499  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
   500  			app.SetNamespace(testNamespace)
   501  			Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
   502  			Eventually(func(g Gomega) {
   503  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app), app)).Should(Succeed())
   504  				g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
   505  			}, 20*time.Second).Should(Succeed())
   506  			Expect(k8sClient.Get(hubCtx, client.ObjectKey{Namespace: testNamespace, Name: "data-worker"}, &appsv1.Deployment{})).Should(Succeed())
   507  			Eventually(func(g Gomega) {
   508  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app), app)).Should(Succeed())
   509  				app.Spec.Workflow.Steps[0].Properties = &runtime.RawExtension{Raw: []byte(`{"policies":["worker"]}`)}
   510  				g.Expect(k8sClient.Update(hubCtx, app)).Should(Succeed())
   511  			}, 10*time.Second).Should(Succeed())
   512  			Eventually(func(g Gomega) {
   513  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKey{Namespace: testNamespace, Name: "data-worker"}, &appsv1.Deployment{})).Should(Satisfy(kerrors.IsNotFound))
   514  				g.Expect(k8sClient.Get(workerCtx, client.ObjectKey{Namespace: testNamespace, Name: "data-worker"}, &appsv1.Deployment{})).Should(Succeed())
   515  			}, 20*time.Second).Should(Succeed())
   516  		})
   517  
   518  		It("Test application with apply-component and cluster", func() {
   519  			By("create application")
   520  			bs, err := os.ReadFile("./testdata/app/app-component-with-cluster.yaml")
   521  			Expect(err).Should(Succeed())
   522  			app := &v1beta1.Application{}
   523  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
   524  			app.SetNamespace(testNamespace)
   525  			Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
   526  			Eventually(func(g Gomega) {
   527  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app), app)).Should(Succeed())
   528  				g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
   529  			}, 20*time.Second).Should(Succeed())
   530  			Expect(k8sClient.Get(workerCtx, client.ObjectKey{Namespace: testNamespace, Name: "component-cluster"}, &appsv1.Deployment{})).Should(Succeed())
   531  		})
   532  
   533  		It("Test application with component using cluster context", func() {
   534  			By("Create definition")
   535  			bs, err := os.ReadFile("./testdata/def/cluster-config.yaml")
   536  			Expect(err).Should(Succeed())
   537  			def := &v1beta1.ComponentDefinition{}
   538  			Expect(yaml.Unmarshal(bs, def)).Should(Succeed())
   539  			def.SetNamespace(kubevelatypes.DefaultKubeVelaNS)
   540  			Expect(k8sClient.Create(hubCtx, def)).Should(Succeed())
   541  			defKey := client.ObjectKeyFromObject(def)
   542  			Eventually(func(g Gomega) {
   543  				g.Expect(k8sClient.Get(hubCtx, defKey, def)).Should(Succeed())
   544  			}, 5*time.Second).Should(Succeed())
   545  			bs, err = os.ReadFile("./testdata/app/app-component-with-cluster-context.yaml")
   546  			Expect(err).Should(Succeed())
   547  			app := &v1beta1.Application{}
   548  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
   549  			app.SetNamespace(testNamespace)
   550  			Eventually(func(g Gomega) { // informer may have latency for the added definition
   551  				g.Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
   552  			}).WithTimeout(10 * time.Second).WithPolling(2 * time.Second).Should(Succeed())
   553  			key := client.ObjectKeyFromObject(app)
   554  			Eventually(func(g Gomega) {
   555  				g.Expect(k8sClient.Get(hubCtx, key, app)).Should(Succeed())
   556  				g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
   557  			}, 20*time.Second).Should(Succeed())
   558  			cm := &corev1.ConfigMap{}
   559  			Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: testNamespace, Name: "test"}, cm)).Should(Succeed())
   560  			Expect(cm.Data["cluster"]).Should(Equal("local"))
   561  			Expect(k8sClient.Get(workerCtx, types.NamespacedName{Namespace: testNamespace, Name: "test"}, cm)).Should(Succeed())
   562  			Expect(cm.Data["cluster"]).Should(Equal("cluster-worker"))
   563  			Expect(k8sClient.Delete(hubCtx, def)).Should(Succeed())
   564  		})
   565  
   566  		It("Test application with read-only policy", func() {
   567  			By("create deployment")
   568  			bs, err := os.ReadFile("./testdata/app/standalone/deployment-busybox.yaml")
   569  			Expect(err).Should(Succeed())
   570  			deploy := &appsv1.Deployment{}
   571  			Expect(yaml.Unmarshal(bs, deploy)).Should(Succeed())
   572  			deploy.SetNamespace(namespace)
   573  			Expect(k8sClient.Create(hubCtx, deploy)).Should(Succeed())
   574  			By("create application")
   575  			bs, err = os.ReadFile("./testdata/app/app-readonly.yaml")
   576  			Expect(err).Should(Succeed())
   577  			app := &v1beta1.Application{}
   578  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
   579  			app.SetNamespace(namespace)
   580  			Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
   581  			Eventually(func(g Gomega) {
   582  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app), app)).Should(Succeed())
   583  				g.Expect(app.Status.Workflow).ShouldNot(BeNil())
   584  				g.Expect(len(app.Status.Workflow.Steps)).ShouldNot(Equal(0))
   585  				g.Expect(app.Status.Workflow.Steps[0].Phase).Should(Equal(workflowv1alpha1.WorkflowStepPhaseFailed))
   586  			}, 20*time.Second).Should(Succeed())
   587  			By("update application")
   588  			Eventually(func(g Gomega) {
   589  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app), app)).Should(Succeed())
   590  				app.Spec.Components[0].Name = "busybox-ref"
   591  				g.Expect(k8sClient.Update(hubCtx, app)).Should(Succeed())
   592  			}, 20*time.Second).Should(Succeed())
   593  			Eventually(func(g Gomega) {
   594  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app), app)).Should(Succeed())
   595  				g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
   596  			}, 20*time.Second).Should(Succeed())
   597  			By("delete application")
   598  			appKey := client.ObjectKeyFromObject(app)
   599  			deployKey := client.ObjectKeyFromObject(deploy)
   600  			Expect(k8sClient.Delete(hubCtx, app)).Should(Succeed())
   601  			Eventually(func(g Gomega) {
   602  				g.Expect(kerrors.IsNotFound(k8sClient.Get(hubCtx, appKey, app))).Should(BeTrue())
   603  			}, 20*time.Second).Should(Succeed())
   604  			Expect(k8sClient.Get(hubCtx, deployKey, deploy)).Should(Succeed())
   605  		})
   606  
   607  		It("Test application with take-over policy", func() {
   608  			By("create deployment")
   609  			bs, err := os.ReadFile("./testdata/app/standalone/deployment-busybox.yaml")
   610  			Expect(err).Should(Succeed())
   611  			deploy := &appsv1.Deployment{}
   612  			Expect(yaml.Unmarshal(bs, deploy)).Should(Succeed())
   613  			deploy.SetNamespace(namespace)
   614  			Expect(k8sClient.Create(hubCtx, deploy)).Should(Succeed())
   615  			By("create application")
   616  			bs, err = os.ReadFile("./testdata/app/app-takeover.yaml")
   617  			Expect(err).Should(Succeed())
   618  			app := &v1beta1.Application{}
   619  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
   620  			app.SetNamespace(namespace)
   621  			Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
   622  			Eventually(func(g Gomega) {
   623  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app), app)).Should(Succeed())
   624  				g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
   625  			}, 20*time.Second).Should(Succeed())
   626  			By("delete application")
   627  			appKey := client.ObjectKeyFromObject(app)
   628  			deployKey := client.ObjectKeyFromObject(deploy)
   629  			Expect(k8sClient.Delete(hubCtx, app)).Should(Succeed())
   630  			Eventually(func(g Gomega) {
   631  				g.Expect(kerrors.IsNotFound(k8sClient.Get(hubCtx, appKey, app))).Should(BeTrue())
   632  				g.Expect(kerrors.IsNotFound(k8sClient.Get(hubCtx, deployKey, deploy))).Should(BeTrue())
   633  			}, 20*time.Second).Should(Succeed())
   634  		})
   635  
   636  		It("Test application with input/output in deploy step", func() {
   637  			By("create application")
   638  			bs, err := os.ReadFile("./testdata/app/app-deploy-io.yaml")
   639  			Expect(err).Should(Succeed())
   640  			app := &v1beta1.Application{}
   641  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
   642  			app.SetNamespace(namespace)
   643  			Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
   644  			Eventually(func(g Gomega) {
   645  				g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(app), app)).Should(Succeed())
   646  				g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
   647  			}, 30*time.Second).Should(Succeed())
   648  
   649  			By("Check input/output work properly")
   650  			cm := &corev1.ConfigMap{}
   651  			cmKey := client.ObjectKey{Namespace: namespace, Name: "deployment-msg"}
   652  			var (
   653  				ipLocal  string
   654  				ipWorker string
   655  			)
   656  			Eventually(func(g Gomega) {
   657  				g.Expect(k8sClient.Get(hubCtx, cmKey, cm)).Should(Succeed())
   658  				g.Expect(cm.Data["msg"]).Should(Equal("Deployment has minimum availability."))
   659  				ipLocal = cm.Data["ip"]
   660  				g.Expect(ipLocal).ShouldNot(BeEmpty())
   661  			}, 20*time.Second).Should(Succeed())
   662  			Eventually(func(g Gomega) {
   663  				g.Expect(k8sClient.Get(workerCtx, cmKey, cm)).Should(Succeed())
   664  				g.Expect(cm.Data["msg"]).Should(Equal("Deployment has minimum availability."))
   665  				ipWorker = cm.Data["ip"]
   666  				g.Expect(ipWorker).ShouldNot(BeEmpty())
   667  			}, 20*time.Second).Should(Succeed())
   668  			Expect(ipLocal).ShouldNot(Equal(ipWorker))
   669  
   670  			By("delete application")
   671  			appKey := client.ObjectKeyFromObject(app)
   672  			Expect(k8sClient.Delete(hubCtx, app)).Should(Succeed())
   673  			Eventually(func(g Gomega) {
   674  				g.Expect(kerrors.IsNotFound(k8sClient.Get(hubCtx, appKey, app))).Should(BeTrue())
   675  			}, 20*time.Second).Should(Succeed())
   676  		})
   677  
   678  		It("Test application with failed gc and restart workflow", func() {
   679  			By("duplicate cluster")
   680  			secret := &corev1.Secret{}
   681  			const secretName = "disconnection-test"
   682  			Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: kubevelatypes.DefaultKubeVelaNS, Name: WorkerClusterName}, secret)).Should(Succeed())
   683  			secret.SetName(secretName)
   684  			secret.SetResourceVersion("")
   685  			Expect(k8sClient.Create(hubCtx, secret)).Should(Succeed())
   686  			defer func() {
   687  				_ = k8sClient.Delete(hubCtx, secret)
   688  			}()
   689  
   690  			By("create cluster normally")
   691  			bs, err := os.ReadFile("./testdata/app/app-disconnection-test.yaml")
   692  			Expect(err).Should(Succeed())
   693  			app := &v1beta1.Application{}
   694  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
   695  			app.SetNamespace(namespace)
   696  			Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
   697  			key := client.ObjectKeyFromObject(app)
   698  			Eventually(func(g Gomega) {
   699  				g.Expect(k8sClient.Get(hubCtx, key, app)).Should(Succeed())
   700  				g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
   701  			}).WithTimeout(10 * time.Second).WithPolling(2 * time.Second).Should(Succeed())
   702  
   703  			By("disconnect cluster")
   704  			Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: kubevelatypes.DefaultKubeVelaNS, Name: secretName}, secret)).Should(Succeed())
   705  			secret.Data["endpoint"] = []byte("https://1.2.3.4:9999")
   706  			Expect(k8sClient.Update(hubCtx, secret)).Should(Succeed())
   707  
   708  			By("update application")
   709  			Expect(k8sClient.Get(hubCtx, key, app)).Should(Succeed())
   710  			app.Spec.Policies = nil
   711  			Expect(k8sClient.Update(hubCtx, app)).Should(Succeed())
   712  			Eventually(func(g Gomega) {
   713  				g.Expect(k8sClient.Get(hubCtx, key, app)).Should(Succeed())
   714  				g.Expect(app.Status.ObservedGeneration).Should(Equal(app.Generation))
   715  				g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
   716  				rts := &v1beta1.ResourceTrackerList{}
   717  				g.Expect(k8sClient.List(hubCtx, rts, client.MatchingLabels{oam.LabelAppName: key.Name, oam.LabelAppNamespace: key.Namespace})).Should(Succeed())
   718  				cnt := 0
   719  				for _, item := range rts.Items {
   720  					if item.Spec.Type == v1beta1.ResourceTrackerTypeVersioned {
   721  						cnt++
   722  					}
   723  				}
   724  				g.Expect(cnt).Should(Equal(2))
   725  			}).WithTimeout(30 * time.Second).WithPolling(2 * time.Second).Should(Succeed())
   726  
   727  			By("try update application again")
   728  			Expect(k8sClient.Get(hubCtx, key, app)).Should(Succeed())
   729  			if app.Annotations == nil {
   730  				app.Annotations = map[string]string{}
   731  			}
   732  			app.Annotations[oam.AnnotationPublishVersion] = "test"
   733  			Expect(k8sClient.Update(hubCtx, app)).Should(Succeed())
   734  			Eventually(func(g Gomega) {
   735  				g.Expect(k8sClient.Get(hubCtx, key, app)).Should(Succeed())
   736  				g.Expect(app.Status.LatestRevision).ShouldNot(BeNil())
   737  				g.Expect(app.Status.LatestRevision.Revision).Should(Equal(int64(3)))
   738  				g.Expect(app.Status.ObservedGeneration).Should(Equal(app.Generation))
   739  				g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
   740  			}).WithTimeout(1 * time.Minute).WithPolling(2 * time.Second).Should(Succeed())
   741  
   742  			By("clear disconnection cluster secret")
   743  			Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: kubevelatypes.DefaultKubeVelaNS, Name: secretName}, secret)).Should(Succeed())
   744  			Expect(k8sClient.Delete(hubCtx, secret)).Should(Succeed())
   745  
   746  			By("wait gc application completed")
   747  			Eventually(func(g Gomega) {
   748  				rts := &v1beta1.ResourceTrackerList{}
   749  				g.Expect(k8sClient.List(hubCtx, rts, client.MatchingLabels{oam.LabelAppName: key.Name, oam.LabelAppNamespace: key.Namespace})).Should(Succeed())
   750  				cnt := 0
   751  				for _, item := range rts.Items {
   752  					if item.Spec.Type == v1beta1.ResourceTrackerTypeVersioned {
   753  						cnt++
   754  					}
   755  				}
   756  				g.Expect(cnt).Should(Equal(1))
   757  			}).WithTimeout(30 * time.Second).WithPolling(2 * time.Second).Should(Succeed())
   758  		})
   759  
   760  		It("Test application with gc policy and shared-resource policy", func() {
   761  			app := &v1beta1.Application{}
   762  			bs, err := os.ReadFile("./testdata/app/app-gc-shared.yaml")
   763  			Expect(err).Should(Succeed())
   764  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
   765  			app.SetNamespace(namespace)
   766  			Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
   767  			appKey := client.ObjectKeyFromObject(app)
   768  			Eventually(func(g Gomega) {
   769  				g.Expect(k8sClient.Get(hubCtx, appKey, app)).Should(Succeed())
   770  				g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
   771  				g.Expect(k8sClient.Get(hubCtx, appKey, &corev1.ConfigMap{})).Should(Succeed())
   772  			}).WithTimeout(10 * time.Second).Should(Succeed())
   773  			Expect(k8sClient.Get(hubCtx, appKey, app)).Should(Succeed())
   774  			Expect(k8sClient.Delete(hubCtx, app)).Should(Succeed())
   775  			Eventually(func(g Gomega) {
   776  				g.Expect(kerrors.IsNotFound(k8sClient.Get(hubCtx, appKey, app))).Should(BeTrue())
   777  				g.Expect(k8sClient.Get(hubCtx, appKey, &corev1.ConfigMap{})).Should(Succeed())
   778  			}).WithTimeout(10 * time.Second).Should(Succeed())
   779  		})
   780  
   781  		It("Test application skip webservice component health check", func() {
   782  			td := &v1beta1.TraitDefinition{
   783  				ObjectMeta: metav1.ObjectMeta{Name: "ignore-health-check", Namespace: namespace},
   784  				Spec: v1beta1.TraitDefinitionSpec{
   785  					Schematic: &common.Schematic{CUE: &common.CUE{
   786  						Template: `
   787  							patch: metadata: annotations: "app.oam.dev/disable-health-check": parameter.key
   788  							parameter: key: string
   789  						`,
   790  					}},
   791  					Status: &common.Status{HealthPolicy: `isHealth: context.parameter.key == "true"`},
   792  				},
   793  			}
   794  			Expect(k8sClient.Create(hubCtx, td)).Should(Succeed())
   795  
   796  			app := &v1beta1.Application{
   797  				ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: namespace},
   798  				Spec: v1beta1.ApplicationSpec{Components: []common.ApplicationComponent{{
   799  					Type:       "webservice",
   800  					Name:       "test",
   801  					Properties: &runtime.RawExtension{Raw: []byte(`{"image":"bad"}`)},
   802  					Traits: []common.ApplicationTrait{{
   803  						Type:       "ignore-health-check",
   804  						Properties: &runtime.RawExtension{Raw: []byte(`{"key":"false"}`)},
   805  					}},
   806  				}}},
   807  			}
   808  			Eventually(func(g Gomega) { // in case the trait definition has not been watched by vela-core
   809  				g.Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
   810  			}).WithTimeout(10 * time.Second).WithPolling(2 * time.Second).Should(Succeed())
   811  			appKey := client.ObjectKeyFromObject(app)
   812  			Eventually(func(g Gomega) {
   813  				g.Expect(k8sClient.Get(hubCtx, appKey, app)).Should(Succeed())
   814  				g.Expect(len(app.Status.Services) > 0).Should(BeTrue())
   815  				g.Expect(len(app.Status.Services[0].Traits) > 0).Should(BeTrue())
   816  				g.Expect(app.Status.Services[0].Traits[0].Healthy).Should(BeFalse())
   817  			}).WithTimeout(10 * time.Second).Should(Succeed())
   818  			Eventually(func(g Gomega) {
   819  				g.Expect(k8sClient.Get(hubCtx, appKey, app)).Should(Succeed())
   820  				app.Spec.Components[0].Traits[0].Properties.Raw = []byte(`{"key":"true"}`)
   821  				g.Expect(k8sClient.Update(hubCtx, app)).Should(Succeed())
   822  			}).WithTimeout(10 * time.Second).Should(Succeed())
   823  			Eventually(func(g Gomega) {
   824  				g.Expect(k8sClient.Get(hubCtx, appKey, app)).Should(Succeed())
   825  				g.Expect(len(app.Status.Services) > 0).Should(BeTrue())
   826  				g.Expect(len(app.Status.Services[0].Traits) > 0).Should(BeTrue())
   827  				g.Expect(app.Status.Services[0].Traits[0].Healthy).Should(BeTrue())
   828  			}).WithTimeout(20 * time.Second).Should(Succeed())
   829  		})
   830  
   831  		It("Test pause application", func() {
   832  			app := &v1beta1.Application{}
   833  			bs, err := os.ReadFile("./testdata/app/app-pause.yaml")
   834  			Expect(err).Should(Succeed())
   835  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
   836  			app.SetNamespace(namespace)
   837  			Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
   838  			time.Sleep(10 * time.Second)
   839  			appKey := client.ObjectKeyFromObject(app)
   840  			Expect(k8sClient.Get(hubCtx, appKey, app)).Should(Succeed())
   841  			Expect(app.Status.Workflow).Should(BeNil())
   842  			Eventually(func(g Gomega) {
   843  				g.Expect(k8sClient.Get(hubCtx, appKey, app)).Should(Succeed())
   844  				reconciler.SetPause(app, false)
   845  				g.Expect(k8sClient.Update(hubCtx, app)).Should(Succeed())
   846  			}).WithTimeout(5 * time.Second).WithPolling(time.Second).Should(Succeed())
   847  			Eventually(func(g Gomega) {
   848  				g.Expect(k8sClient.Get(hubCtx, appKey, app)).Should(Succeed())
   849  				g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
   850  			}).WithTimeout(15 * time.Second).WithPolling(3 * time.Second).Should(Succeed())
   851  			Expect(k8sClient.Delete(hubCtx, app)).Should(Succeed())
   852  		})
   853  
   854  		It("Test application carrying deploy step with inline policy", func() {
   855  			ctx := context.Background()
   856  			wsDef := &v1beta1.WorkflowStepDefinition{}
   857  			bs, err := os.ReadFile("./testdata/def/inline-deploy.yaml")
   858  			Expect(err).Should(Succeed())
   859  			Expect(yaml.Unmarshal(bs, wsDef)).Should(Succeed())
   860  			wsDef.SetNamespace(namespace)
   861  			Expect(k8sClient.Create(ctx, wsDef)).Should(Succeed())
   862  			app := &v1beta1.Application{}
   863  			bs, err = os.ReadFile("./testdata/app/app-carrying-deploy-step-with-inline-policy.yaml")
   864  			Expect(err).Should(Succeed())
   865  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
   866  			app.SetNamespace(namespace)
   867  			Eventually(func(g Gomega) {
   868  				g.Expect(k8sClient.Create(ctx, app)).Should(Succeed())
   869  			}).WithPolling(2 * time.Second).WithTimeout(5 * time.Second).Should(Succeed())
   870  			appKey := client.ObjectKeyFromObject(app)
   871  			Eventually(func(g Gomega) {
   872  				_app := &v1beta1.Application{}
   873  				g.Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
   874  				g.Expect(_app.Status.Phase).Should(Equal(common.ApplicationRunning))
   875  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
   876  			_deploy := &appsv1.Deployment{}
   877  			Expect(k8sClient.Get(ctx, appKey, _deploy)).Should(Succeed())
   878  			Expect(int(*_deploy.Spec.Replicas)).Should(Equal(0))
   879  		})
   880  
   881  		It("Test application with multiple gc & shared-resource policies", func() {
   882  			ctx := context.Background()
   883  			app := &v1beta1.Application{}
   884  			bs, err := os.ReadFile("./testdata/app/app-multi-resource-policies.yaml")
   885  			Expect(err).Should(Succeed())
   886  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
   887  			app.SetNamespace(namespace)
   888  			app2 := app.DeepCopy()
   889  			app2.SetName("test-2")
   890  			Eventually(func(g Gomega) {
   891  				g.Expect(k8sClient.Create(ctx, app)).Should(Succeed())
   892  			}).WithPolling(2 * time.Second).WithTimeout(5 * time.Second).Should(Succeed())
   893  			Eventually(func(g Gomega) {
   894  				g.Expect(k8sClient.Create(ctx, app2)).Should(Succeed())
   895  			}).WithPolling(2 * time.Second).WithTimeout(5 * time.Second).Should(Succeed())
   896  			appKey := client.ObjectKeyFromObject(app)
   897  			appKey2 := client.ObjectKeyFromObject(app2)
   898  			Eventually(func(g Gomega) {
   899  				_app := &v1beta1.Application{}
   900  				g.Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
   901  				g.Expect(_app.Status.Phase).Should(Equal(common.ApplicationRunning))
   902  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
   903  			Eventually(func(g Gomega) {
   904  				_app := &v1beta1.Application{}
   905  				g.Expect(k8sClient.Get(ctx, appKey2, _app)).Should(Succeed())
   906  				g.Expect(_app.Status.Phase).Should(Equal(common.ApplicationRunning))
   907  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
   908  			Eventually(func(g Gomega) {
   909  				_app := &v1beta1.Application{}
   910  				g.Expect(client.IgnoreNotFound(k8sClient.Get(ctx, appKey, _app))).Should(Succeed())
   911  				g.Expect(client.IgnoreNotFound(k8sClient.Delete(ctx, _app))).Should(Succeed())
   912  			}).WithPolling(2 * time.Second).WithTimeout(10 * time.Second).Should(Succeed())
   913  			Eventually(func(g Gomega) {
   914  				_app := &v1beta1.Application{}
   915  				g.Expect(client.IgnoreNotFound(k8sClient.Get(ctx, appKey2, _app))).Should(Succeed())
   916  				g.Expect(client.IgnoreNotFound(k8sClient.Delete(ctx, _app))).Should(Succeed())
   917  			}).WithPolling(2 * time.Second).WithTimeout(10 * time.Second).Should(Succeed())
   918  			Eventually(func(g Gomega) {
   919  				_app := &v1beta1.Application{}
   920  				g.Expect(kerrors.IsNotFound(k8sClient.Get(ctx, appKey, _app))).Should(BeTrue())
   921  				g.Expect(kerrors.IsNotFound(k8sClient.Get(ctx, appKey2, _app))).Should(BeTrue())
   922  				g.Expect(k8sClient.Get(ctx, appKey, &corev1.ConfigMap{})).Should(Succeed())
   923  				g.Expect(k8sClient.Get(ctx, appKey, &corev1.Secret{})).Should(Succeed())
   924  			}).WithPolling(2 * time.Second).WithTimeout(10 * time.Second).Should(Succeed())
   925  		})
   926  
   927  		It("Test application with anonymous policy", func() {
   928  			ctx := context.Background()
   929  			app := &v1beta1.Application{}
   930  			bs, err := os.ReadFile("./testdata/app/app-anonymous-policies.yaml")
   931  			Expect(err).Should(Succeed())
   932  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
   933  			app.SetNamespace(namespace)
   934  			Eventually(func(g Gomega) {
   935  				g.Expect(k8sClient.Create(ctx, app)).Should(Succeed())
   936  			}).WithPolling(2 * time.Second).WithTimeout(5 * time.Second).Should(Succeed())
   937  			appKey := client.ObjectKeyFromObject(app)
   938  			Eventually(func(g Gomega) {
   939  				_app := &v1beta1.Application{}
   940  				g.Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
   941  				g.Expect(_app.Status.Phase).Should(Equal(common.ApplicationRunning))
   942  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
   943  			_deploy := &appsv1.Deployment{}
   944  			Expect(k8sClient.Get(workerCtx, appKey, _deploy)).Should(Succeed())
   945  			Expect(int(*_deploy.Spec.Replicas)).Should(Equal(0))
   946  		})
   947  
   948  		It("Test application with customized application revision limit", func() {
   949  			ctx := context.Background()
   950  			app := &v1beta1.Application{}
   951  			bs, err := os.ReadFile("./testdata/app/app-lite.yaml")
   952  			Expect(err).Should(Succeed())
   953  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
   954  			app.SetNamespace(namespace)
   955  			Eventually(func(g Gomega) {
   956  				g.Expect(k8sClient.Create(ctx, app)).Should(Succeed())
   957  			}).WithPolling(2 * time.Second).WithTimeout(5 * time.Second).Should(Succeed())
   958  			appKey := client.ObjectKeyFromObject(app)
   959  			Eventually(func(g Gomega) {
   960  				_app := &v1beta1.Application{}
   961  				g.Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
   962  				g.Expect(_app.Status.Phase).Should(Equal(common.ApplicationRunning))
   963  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
   964  
   965  			By("update app and should have two revisions")
   966  			Eventually(func(g Gomega) {
   967  				_app := &v1beta1.Application{}
   968  				g.Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
   969  				_app.Spec.Components[0].Name = "dw"
   970  				g.Expect(k8sClient.Update(ctx, _app)).Should(Succeed())
   971  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
   972  			Eventually(func(g Gomega) {
   973  				_app := &v1beta1.Application{}
   974  				g.Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
   975  				g.Expect(_app.Status.Phase).Should(Equal(common.ApplicationRunning))
   976  				_revs := &v1beta1.ApplicationRevisionList{}
   977  				g.Expect(k8sClient.List(ctx, _revs, client.InNamespace(namespace))).Should(Succeed())
   978  				g.Expect(len(_revs.Items)).Should(Equal(2))
   979  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
   980  
   981  			By("update app with gc policy and should have one revision")
   982  			Eventually(func(g Gomega) {
   983  				_app := &v1beta1.Application{}
   984  				g.Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
   985  				_app.Spec.Components[0].Name = "dw2"
   986  				_app.Spec.Policies = []v1beta1.AppPolicy{{
   987  					Type:       "garbage-collect",
   988  					Name:       "gc",
   989  					Properties: &runtime.RawExtension{Raw: []byte(`{"applicationRevisionLimit":0}`)},
   990  				}}
   991  				g.Expect(k8sClient.Update(ctx, _app)).Should(Succeed())
   992  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
   993  			Eventually(func(g Gomega) {
   994  				_app := &v1beta1.Application{}
   995  				g.Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
   996  				g.Expect(_app.Status.Phase).Should(Equal(common.ApplicationRunning))
   997  				_revs := &v1beta1.ApplicationRevisionList{}
   998  				g.Expect(k8sClient.List(ctx, _revs, client.InNamespace(namespace))).Should(Succeed())
   999  				g.Expect(len(_revs.Items)).Should(Equal(1))
  1000  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
  1001  		})
  1002  
  1003  		It("Test application with resource-update policy", func() {
  1004  			ctx := context.Background()
  1005  			app := &v1beta1.Application{}
  1006  			bs, err := os.ReadFile("./testdata/app/app-recreate-test.yaml")
  1007  			Expect(err).Should(Succeed())
  1008  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
  1009  			app.SetNamespace(namespace)
  1010  			Eventually(func(g Gomega) {
  1011  				g.Expect(k8sClient.Create(ctx, app)).Should(Succeed())
  1012  			}).WithPolling(2 * time.Second).WithTimeout(5 * time.Second).Should(Succeed())
  1013  			appKey := client.ObjectKeyFromObject(app)
  1014  			Eventually(func(g Gomega) {
  1015  				_app := &v1beta1.Application{}
  1016  				g.Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
  1017  				g.Expect(_app.Status.Phase).Should(Equal(common.ApplicationRunning))
  1018  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
  1019  
  1020  			By("update configmap")
  1021  			Eventually(func(g Gomega) {
  1022  				cm := &corev1.ConfigMap{}
  1023  				g.Expect(k8sClient.Get(ctx, appKey, cm)).Should(Succeed())
  1024  				cm.Data["extra"] = "extra-val"
  1025  				g.Expect(k8sClient.Update(ctx, cm)).Should(Succeed())
  1026  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
  1027  
  1028  			By("update application")
  1029  			Expect(yaml.Unmarshal([]byte(strings.ReplaceAll(strings.ReplaceAll(string(bs), "key: dgo=", "key: dnZ2Cg=="), "key: val", "key: val2")), app)).Should(Succeed())
  1030  			Eventually(func(g Gomega) {
  1031  				_app := &v1beta1.Application{}
  1032  				g.Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
  1033  				app.ResourceVersion = _app.ResourceVersion
  1034  				g.Expect(k8sClient.Update(ctx, app)).Should(Succeed())
  1035  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
  1036  			Eventually(func(g Gomega) {
  1037  				_app := &v1beta1.Application{}
  1038  				g.Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
  1039  				g.Expect(_app.Status.Phase).Should(Equal(common.ApplicationRunning))
  1040  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
  1041  
  1042  			By("validate updated result")
  1043  			Eventually(func(g Gomega) {
  1044  				cm := &corev1.ConfigMap{}
  1045  				g.Expect(k8sClient.Get(ctx, appKey, cm)).Should(Succeed())
  1046  				g.Expect(len(cm.Data)).Should(Equal(1))
  1047  				secret := &corev1.Secret{}
  1048  				g.Expect(k8sClient.Get(ctx, appKey, secret)).Should(Succeed())
  1049  				g.Expect(string(secret.Data["key"])).Should(Equal("vvv\n"))
  1050  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
  1051  		})
  1052  
  1053  		It("Test application apply resources into managed cluster without installing CRD on the control plane", func() {
  1054  			ctx := context.Background()
  1055  			crd := &unstructured.Unstructured{}
  1056  			bs, err := os.ReadFile("./testdata/kube/sample-crd.yaml")
  1057  			Expect(err).Should(Succeed())
  1058  			Expect(yaml.Unmarshal(bs, crd)).Should(Succeed())
  1059  			Expect(client.IgnoreAlreadyExists(k8sClient.Create(workerCtx, crd))).Should(Succeed())
  1060  
  1061  			app := &v1beta1.Application{}
  1062  			bs, err = os.ReadFile("./testdata/app/app-remote-resource.yaml")
  1063  			Expect(err).Should(Succeed())
  1064  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
  1065  			app.SetNamespace(namespace)
  1066  			Eventually(func(g Gomega) {
  1067  				g.Expect(k8sClient.Create(ctx, app)).Should(Succeed())
  1068  			}).WithPolling(2 * time.Second).WithTimeout(5 * time.Second).Should(Succeed())
  1069  			appKey := client.ObjectKeyFromObject(app)
  1070  			Eventually(func(g Gomega) {
  1071  				_app := &v1beta1.Application{}
  1072  				g.Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
  1073  				g.Expect(_app.Status.Phase).Should(Equal(common.ApplicationRunning))
  1074  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
  1075  			obj := &unstructured.Unstructured{}
  1076  			obj.SetAPIVersion("sample.custom.io/v1alpha1")
  1077  			obj.SetKind("Foo")
  1078  			Expect(k8sClient.Get(workerCtx, appKey, obj)).Should(Succeed())
  1079  			Expect(obj.Object["spec"].(map[string]interface{})["key"]).Should(Equal("value"))
  1080  		})
  1081  
  1082  		It("Test application with fixed cluster to dispatch", func() {
  1083  			ctx := context.Background()
  1084  			app := &v1beta1.Application{}
  1085  			bs, err := os.ReadFile("./testdata/app/app-with-fixed-location.yaml")
  1086  			Expect(err).Should(Succeed())
  1087  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
  1088  			app.SetNamespace(namespace)
  1089  			Eventually(func(g Gomega) {
  1090  				g.Expect(k8sClient.Create(ctx, app)).Should(Succeed())
  1091  			}).WithPolling(2 * time.Second).WithTimeout(5 * time.Second).Should(Succeed())
  1092  			appKey := client.ObjectKeyFromObject(app)
  1093  			Eventually(func(g Gomega) {
  1094  				_app := &v1beta1.Application{}
  1095  				g.Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
  1096  				g.Expect(_app.Status.Phase).Should(Equal(common.ApplicationRunning))
  1097  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
  1098  			Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "x"}, &corev1.ConfigMap{})).Should(Succeed())
  1099  			Expect(k8sClient.Get(workerCtx, types.NamespacedName{Namespace: namespace, Name: "y"}, &corev1.ConfigMap{})).Should(Succeed())
  1100  
  1101  			By("Deleting")
  1102  			_app := &v1beta1.Application{}
  1103  			Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
  1104  			Expect(k8sClient.Delete(ctx, _app)).Should(Succeed())
  1105  			Eventually(func(g Gomega) {
  1106  				g.Expect(kerrors.IsNotFound(k8sClient.Get(ctx, appKey, _app))).Should(BeTrue())
  1107  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
  1108  			Expect(kerrors.IsNotFound(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "x"}, &corev1.ConfigMap{}))).Should(BeTrue())
  1109  			Expect(kerrors.IsNotFound(k8sClient.Get(workerCtx, types.NamespacedName{Namespace: namespace, Name: "y"}, &corev1.ConfigMap{}))).Should(BeTrue())
  1110  		})
  1111  
  1112  		It("Test application with garbage-collect propagation setting", func() {
  1113  			ctx := context.Background()
  1114  			app := &v1beta1.Application{}
  1115  			bs, err := os.ReadFile("./testdata/app/app-with-custom-gc-propagation.yaml")
  1116  			Expect(err).Should(Succeed())
  1117  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
  1118  			app.SetNamespace(namespace)
  1119  			Eventually(func(g Gomega) {
  1120  				g.Expect(k8sClient.Create(ctx, app)).Should(Succeed())
  1121  			}).WithPolling(2 * time.Second).WithTimeout(5 * time.Second).Should(Succeed())
  1122  			appKey := client.ObjectKeyFromObject(app)
  1123  			Eventually(func(g Gomega) {
  1124  				_app := &v1beta1.Application{}
  1125  				g.Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
  1126  				g.Expect(_app.Status.Phase).Should(Equal(common.ApplicationRunning))
  1127  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
  1128  			By("Deleting")
  1129  			_app := &v1beta1.Application{}
  1130  			Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
  1131  			Expect(k8sClient.Delete(ctx, _app)).Should(Succeed())
  1132  			Eventually(func(g Gomega) {
  1133  				_app := &v1beta1.Application{}
  1134  				g.Expect(kerrors.IsNotFound(k8sClient.Get(ctx, appKey, _app))).Should(BeTrue())
  1135  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
  1136  			Eventually(func(g Gomega) {
  1137  				pods := &corev1.PodList{}
  1138  				g.Expect(k8sClient.List(ctx, pods, client.InNamespace(namespace))).Should(Succeed())
  1139  				g.Expect(len(pods.Items)).Should(Equal(1))
  1140  				g.Expect(pods.Items[0].Name).Should(ContainSubstring("orphan"))
  1141  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
  1142  		})
  1143  
  1144  		It("Test application revision gc block application gc", func() {
  1145  			ctx := context.Background()
  1146  			app := &v1beta1.Application{}
  1147  			bs, err := os.ReadFile("./testdata/app/app-lite.yaml")
  1148  			Expect(err).Should(Succeed())
  1149  			Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
  1150  			app.SetNamespace(namespace)
  1151  			Eventually(func(g Gomega) {
  1152  				g.Expect(k8sClient.Create(ctx, app)).Should(Succeed())
  1153  			}).WithPolling(2 * time.Second).WithTimeout(5 * time.Second).Should(Succeed())
  1154  			appKey := client.ObjectKeyFromObject(app)
  1155  			Eventually(func(g Gomega) {
  1156  				_app := &v1beta1.Application{}
  1157  				g.Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
  1158  				g.Expect(_app.Status.Phase).Should(Equal(common.ApplicationRunning))
  1159  			}).WithPolling(2 * time.Second).WithTimeout(20 * time.Second).Should(Succeed())
  1160  
  1161  			By("Add finalizer to application revision")
  1162  			Eventually(func(g Gomega) {
  1163  				_rev := &v1beta1.ApplicationRevision{}
  1164  				g.Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appKey.Name + "-v1"}, _rev)).Should(Succeed())
  1165  				_rev.SetFinalizers([]string{"mine"})
  1166  				g.Expect(k8sClient.Update(ctx, _rev)).Should(Succeed())
  1167  			}).WithPolling(2 * time.Second).WithTimeout(10 * time.Second).Should(Succeed())
  1168  
  1169  			By("Deleting")
  1170  			_app := &v1beta1.Application{}
  1171  			Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
  1172  			Expect(k8sClient.Delete(ctx, _app)).Should(Succeed())
  1173  
  1174  			By("Check application existing after rt recycled")
  1175  			Eventually(func(g Gomega) {
  1176  				rts := &v1beta1.ResourceTrackerList{}
  1177  				g.Expect(k8sClient.List(ctx, rts, client.MatchingLabels{oam.LabelAppName: _app.Name, oam.LabelAppNamespace: _app.Namespace})).Should(Succeed())
  1178  				g.Expect(len(rts.Items)).Should(Equal(0))
  1179  			}).WithPolling(2 * time.Second).WithTimeout(10 * time.Second).Should(Succeed())
  1180  			Consistently(func(g Gomega) {
  1181  				g.Expect(k8sClient.Get(ctx, appKey, _app)).Should(Succeed())
  1182  			}).WithPolling(2 * time.Second).WithTimeout(10 * time.Second).Should(Succeed())
  1183  
  1184  			By("Remove finalizer from application revision")
  1185  			Eventually(func(g Gomega) {
  1186  				_rev := &v1beta1.ApplicationRevision{}
  1187  				g.Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appKey.Name + "-v1"}, _rev)).Should(Succeed())
  1188  				_rev.SetFinalizers([]string{})
  1189  				g.Expect(k8sClient.Update(ctx, _rev)).Should(Succeed())
  1190  			}).WithPolling(2 * time.Second).WithTimeout(10 * time.Second).Should(Succeed())
  1191  
  1192  			By("Check application deletion")
  1193  			Eventually(func(g Gomega) {
  1194  				g.Expect(kerrors.IsNotFound(k8sClient.Get(ctx, appKey, _app))).Should(BeTrue())
  1195  				g.Expect(kerrors.IsNotFound(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appKey.Name + "-v1"}, &v1beta1.ApplicationRevision{}))).Should(BeTrue())
  1196  			}).WithPolling(2 * time.Second).WithTimeout(10 * time.Second).Should(Succeed())
  1197  		})
  1198  	})
  1199  })