github.com/oam-dev/kubevela@v1.9.11/test/e2e-multicluster-test/multicluster_standalone_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  	"fmt"
    22  	"os"
    23  	"time"
    24  
    25  	. "github.com/onsi/ginkgo/v2"
    26  	. "github.com/onsi/gomega"
    27  	v1 "k8s.io/api/apps/v1"
    28  	corev1 "k8s.io/api/core/v1"
    29  	"k8s.io/apimachinery/pkg/api/errors"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    32  	"k8s.io/apimachinery/pkg/runtime"
    33  	"k8s.io/apimachinery/pkg/types"
    34  	"k8s.io/utils/pointer"
    35  	"sigs.k8s.io/controller-runtime/pkg/client"
    36  	"sigs.k8s.io/yaml"
    37  
    38  	workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
    39  
    40  	oamcomm "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
    41  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
    42  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    43  	"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1beta1/application"
    44  	"github.com/oam-dev/kubevela/pkg/oam"
    45  	"github.com/oam-dev/kubevela/pkg/workflow/operation"
    46  )
    47  
    48  var _ = Describe("Test multicluster standalone scenario", func() {
    49  	waitObject := func(ctx context.Context, un unstructured.Unstructured) {
    50  		Eventually(func(g Gomega) error {
    51  			return k8sClient.Get(ctx, client.ObjectKeyFromObject(&un), &un)
    52  		}, 10*time.Second).Should(Succeed())
    53  	}
    54  	var namespace string
    55  	var hubCtx context.Context
    56  	var workerCtx context.Context
    57  
    58  	readFile := func(filename string) *unstructured.Unstructured {
    59  		bs, err := os.ReadFile("./testdata/app/standalone/" + filename)
    60  		Expect(err).Should(Succeed())
    61  		un := &unstructured.Unstructured{}
    62  		Expect(yaml.Unmarshal(bs, un)).Should(Succeed())
    63  		un.SetNamespace(namespace)
    64  		return un
    65  	}
    66  
    67  	applyFile := func(filename string) {
    68  		un := readFile(filename)
    69  		Eventually(func(g Gomega) {
    70  			g.Expect(k8sClient.Create(context.Background(), un)).Should(Succeed())
    71  		}).WithTimeout(10 * time.Second).WithPolling(2 * time.Second).Should(Succeed())
    72  	}
    73  
    74  	BeforeEach(func() {
    75  		hubCtx, workerCtx, namespace = initializeContextAndNamespace()
    76  	})
    77  
    78  	AfterEach(func() {
    79  		cleanUpNamespace(hubCtx, workerCtx, namespace)
    80  	})
    81  
    82  	It("Test standalone app", func() {
    83  		By("Apply resources")
    84  		applyFile("deployment.yaml")
    85  		applyFile("configmap-1.yaml")
    86  		applyFile("configmap-2.yaml")
    87  		applyFile("workflow.yaml")
    88  		applyFile("policy.yaml")
    89  		applyFile("app.yaml")
    90  
    91  		Eventually(func(g Gomega) {
    92  			deploys := &v1.DeploymentList{}
    93  			g.Expect(k8sClient.List(workerCtx, deploys, client.InNamespace(namespace))).Should(Succeed())
    94  			g.Expect(len(deploys.Items)).Should(Equal(1))
    95  			g.Expect(deploys.Items[0].Spec.Replicas).Should(Equal(pointer.Int32(3)))
    96  			cms := &corev1.ConfigMapList{}
    97  			g.Expect(k8sClient.List(workerCtx, cms, client.InNamespace(namespace), client.MatchingLabels(map[string]string{"app": "podinfo"}))).Should(Succeed())
    98  			g.Expect(len(cms.Items)).Should(Equal(2))
    99  		}, 30*time.Second).Should(Succeed())
   100  
   101  		Eventually(func(g Gomega) {
   102  			app := &v1beta1.Application{}
   103  			g.Expect(k8sClient.Get(context.Background(), types.NamespacedName{Namespace: namespace, Name: "podinfo"}, app)).Should(Succeed())
   104  			g.Expect(app.Status.Workflow).ShouldNot(BeNil())
   105  			g.Expect(app.Status.Workflow.Mode).Should(Equal("DAG-DAG"))
   106  			g.Expect(k8sClient.Delete(context.Background(), app)).Should(Succeed())
   107  		}, 15*time.Second).Should(Succeed())
   108  
   109  		Eventually(func(g Gomega) {
   110  			deploys := &v1.DeploymentList{}
   111  			g.Expect(k8sClient.List(workerCtx, deploys, client.InNamespace(namespace))).Should(Succeed())
   112  			g.Expect(len(deploys.Items)).Should(Equal(0))
   113  			cms := &corev1.ConfigMapList{}
   114  			g.Expect(k8sClient.List(workerCtx, cms, client.InNamespace(namespace), client.MatchingLabels(map[string]string{"app": "podinfo"}))).Should(Succeed())
   115  			g.Expect(len(cms.Items)).Should(Equal(0))
   116  		}, 30*time.Second).Should(Succeed())
   117  	})
   118  
   119  	It("Test standalone app with publish version", func() {
   120  		By("Apply resources")
   121  
   122  		nsLocal := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace + "-local"}}
   123  		Expect(k8sClient.Create(hubCtx, nsLocal)).Should(Succeed())
   124  		defer func() {
   125  			_ = k8sClient.Delete(hubCtx, nsLocal)
   126  		}()
   127  
   128  		deploy := readFile("deployment.yaml")
   129  		Expect(k8sClient.Create(hubCtx, deploy)).Should(Succeed())
   130  		waitObject(hubCtx, *deploy)
   131  		workflow := readFile("workflow-suspend.yaml")
   132  		Expect(k8sClient.Create(hubCtx, workflow)).Should(Succeed())
   133  		waitObject(hubCtx, *workflow)
   134  		policy := readFile("policy-zero-replica.yaml")
   135  		Expect(k8sClient.Create(hubCtx, policy)).Should(Succeed())
   136  		waitObject(hubCtx, *policy)
   137  		app := readFile("app-with-publish-version.yaml")
   138  		Eventually(func(g Gomega) {
   139  			g.Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
   140  		}).WithTimeout(10 * time.Second).WithPolling(2 * time.Second).Should(Succeed())
   141  		appKey := client.ObjectKeyFromObject(app)
   142  
   143  		Eventually(func(g Gomega) {
   144  			_app := &v1beta1.Application{}
   145  			g.Expect(k8sClient.Get(hubCtx, appKey, _app)).Should(Succeed())
   146  			g.Expect(_app.Status.Phase).Should(Equal(oamcomm.ApplicationWorkflowSuspending))
   147  		}, 15*time.Second).Should(Succeed())
   148  
   149  		Expect(k8sClient.Delete(hubCtx, workflow)).Should(Succeed())
   150  		Expect(k8sClient.Delete(hubCtx, policy)).Should(Succeed())
   151  
   152  		Eventually(func(g Gomega) {
   153  			_app := &v1beta1.Application{}
   154  			g.Expect(k8sClient.Get(hubCtx, appKey, _app)).Should(Succeed())
   155  			g.Expect(operation.ResumeWorkflow(hubCtx, k8sClient, _app, "")).Should(Succeed())
   156  		}, 15*time.Second).Should(Succeed())
   157  
   158  		// test application can run without external policies and workflow since they are recorded in the application revision
   159  		_app := &v1beta1.Application{}
   160  		Eventually(func(g Gomega) {
   161  			deploys := &v1.DeploymentList{}
   162  			g.Expect(k8sClient.List(workerCtx, deploys, client.InNamespace(namespace))).Should(Succeed())
   163  			g.Expect(len(deploys.Items)).Should(Equal(1))
   164  			g.Expect(deploys.Items[0].Spec.Replicas).Should(Equal(pointer.Int32(0)))
   165  			g.Expect(k8sClient.Get(hubCtx, appKey, _app)).Should(Succeed())
   166  			g.Expect(_app.Status.Phase).Should(Equal(oamcomm.ApplicationRunning))
   167  		}, 30*time.Second).Should(Succeed())
   168  
   169  		// update application without updating publishVersion
   170  		Eventually(func(g Gomega) {
   171  			g.Expect(k8sClient.Get(hubCtx, appKey, _app)).Should(Succeed())
   172  			_app.Spec.Policies[0].Properties = &runtime.RawExtension{Raw: []byte(fmt.Sprintf(`{"clusters":["local"],"namespace":"%s"}`, nsLocal.Name))}
   173  			g.Expect(k8sClient.Update(hubCtx, _app)).Should(Succeed())
   174  		}, 10*time.Second).Should(Succeed())
   175  
   176  		// application should no re-run workflow
   177  		time.Sleep(10 * time.Second)
   178  		Eventually(func(g Gomega) {
   179  			g.Expect(k8sClient.Get(hubCtx, appKey, _app)).Should(Succeed())
   180  			g.Expect(_app.Status.Phase).Should(Equal(oamcomm.ApplicationRunning))
   181  			apprevs := &v1beta1.ApplicationRevisionList{}
   182  			g.Expect(k8sClient.List(hubCtx, apprevs, client.InNamespace(namespace))).Should(Succeed())
   183  			g.Expect(len(apprevs.Items)).Should(Equal(1))
   184  		}, 10*time.Second).Should(Succeed())
   185  
   186  		// update application with publishVersion
   187  		applyFile("policy.yaml")
   188  		applyFile("workflow.yaml")
   189  		Eventually(func(g Gomega) {
   190  			g.Expect(k8sClient.Get(hubCtx, appKey, _app)).Should(Succeed())
   191  			_app.Annotations[oam.AnnotationPublishVersion] = "beta"
   192  			g.Expect(k8sClient.Update(hubCtx, _app)).Should(Succeed())
   193  		}, 10*time.Second).Should(Succeed())
   194  
   195  		Eventually(func(g Gomega) {
   196  			g.Expect(k8sClient.Get(hubCtx, appKey, _app)).Should(Succeed())
   197  			g.Expect(_app.Status.Phase).Should(Equal(oamcomm.ApplicationRunning))
   198  			deploys := &v1.DeploymentList{}
   199  			g.Expect(k8sClient.List(workerCtx, deploys, client.InNamespace(namespace))).Should(Succeed())
   200  			g.Expect(len(deploys.Items)).Should(Equal(0))
   201  			g.Expect(k8sClient.List(hubCtx, deploys, client.InNamespace(nsLocal.Name))).Should(Succeed())
   202  			g.Expect(len(deploys.Items)).Should(Equal(1))
   203  			g.Expect(deploys.Items[0].Spec.Replicas).Should(Equal(pointer.Int32(3)))
   204  		}, 30*time.Second).Should(Succeed())
   205  	})
   206  
   207  	It("Test rollback application with publish version", func() {
   208  		By("Apply application successfully")
   209  		applyFile("topology-policy.yaml")
   210  		applyFile("workflow-deploy-worker.yaml")
   211  		applyFile("deployment-busybox.yaml")
   212  		applyFile("app-with-publish-version-busybox.yaml")
   213  		app := &v1beta1.Application{}
   214  		appKey := types.NamespacedName{Namespace: namespace, Name: "busybox"}
   215  		Eventually(func(g Gomega) {
   216  			g.Expect(k8sClient.Get(hubCtx, appKey, app)).Should(Succeed())
   217  			g.Expect(app.Status.Phase).Should(Equal(oamcomm.ApplicationRunning))
   218  		}, 30*time.Second).Should(Succeed())
   219  
   220  		By("Update Application to first failed version")
   221  		Eventually(func(g Gomega) {
   222  			g.Expect(k8sClient.Get(hubCtx, appKey, app)).Should(Succeed())
   223  			app.Annotations[oam.AnnotationPublishVersion] = "alpha2"
   224  			app.Spec.Components[0].Properties = &runtime.RawExtension{Raw: []byte(`{"image":"busybox:bad"}`)}
   225  			g.Expect(k8sClient.Update(hubCtx, app)).Should(Succeed())
   226  		}, 30*time.Second).Should(Succeed())
   227  
   228  		Eventually(func(g Gomega) {
   229  			g.Expect(k8sClient.Get(hubCtx, appKey, app)).Should(Succeed())
   230  			g.Expect(app.Status.Phase).Should(Equal(oamcomm.ApplicationRunningWorkflow))
   231  		}, 30*time.Second).Should(Succeed())
   232  
   233  		By("Update Application to second failed version")
   234  		Eventually(func(g Gomega) {
   235  			g.Expect(k8sClient.Get(hubCtx, appKey, app)).Should(Succeed())
   236  			app.Annotations[oam.AnnotationPublishVersion] = "alpha3"
   237  			app.Spec.Components[0].Name = "busybox-bad"
   238  			g.Expect(k8sClient.Update(hubCtx, app)).Should(Succeed())
   239  		}, 30*time.Second).Should(Succeed())
   240  		Eventually(func(g Gomega) {
   241  			deploy := &v1.Deployment{}
   242  			g.Expect(k8sClient.Get(workerCtx, types.NamespacedName{Namespace: namespace, Name: "busybox"}, deploy)).Should(Succeed())
   243  			g.Expect(k8sClient.Delete(workerCtx, deploy)).Should(Succeed())
   244  		}, 30*time.Second).Should(Succeed())
   245  
   246  		By("Change external policy")
   247  		Eventually(func(g Gomega) {
   248  			g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "busybox-v3"}, &v1beta1.ApplicationRevision{})).Should(Succeed())
   249  			policy := &v1alpha1.Policy{}
   250  			g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "topology-worker"}, policy)).Should(Succeed())
   251  			policy.Properties = &runtime.RawExtension{Raw: []byte(`{"clusters":["changed"]}`)}
   252  			g.Expect(k8sClient.Update(hubCtx, policy)).Should(Succeed())
   253  		}, 30*time.Second).Should(Succeed())
   254  
   255  		By("Change referred objects")
   256  		Eventually(func(g Gomega) {
   257  			deploy := &v1.Deployment{}
   258  			g.Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: namespace, Name: "busybox-ref"}, deploy)).Should(Succeed())
   259  			deploy.Spec.Replicas = pointer.Int32(1)
   260  			g.Expect(k8sClient.Update(hubCtx, deploy)).Should(Succeed())
   261  		}, 30*time.Second).Should(Succeed())
   262  
   263  		By("Live-diff application")
   264  		outputs, err := execCommand("live-diff", "-r", "busybox-v3,busybox-v1", "-n", namespace)
   265  		Expect(err).Should(Succeed())
   266  		Expect(outputs).Should(SatisfyAll(
   267  			ContainSubstring("Application (busybox) has been modified(*)"),
   268  			ContainSubstring("External Policy (topology-worker) has no change"),
   269  			ContainSubstring("External Workflow (deploy-worker) has no change"),
   270  			ContainSubstring(fmt.Sprintf("Referred Object (apps/v1 Deployment %s/busybox-ref) has no change", namespace)),
   271  		))
   272  		outputs, err = execCommand("live-diff", "busybox", "-n", namespace)
   273  		Expect(err).Should(Succeed())
   274  		Expect(outputs).Should(SatisfyAll(
   275  			ContainSubstring("Application (busybox) has no change"),
   276  			ContainSubstring("External Policy (topology-worker) has been modified(*)"),
   277  			ContainSubstring("External Workflow (deploy-worker) has no change"),
   278  			ContainSubstring(fmt.Sprintf("Referred Object (apps/v1 Deployment %s/busybox-ref) has been modified", namespace)),
   279  		))
   280  
   281  		By("Rollback application")
   282  		Eventually(func(g Gomega) {
   283  			_, err = execCommand("workflow", "suspend", "busybox", "-n", namespace)
   284  			g.Expect(err).Should(Succeed())
   285  		}).WithTimeout(10 * time.Second).Should(Succeed())
   286  		Eventually(func(g Gomega) {
   287  			_, err = execCommand("workflow", "rollback", "busybox", "-n", namespace)
   288  			g.Expect(err).Should(Succeed())
   289  		}).WithTimeout(10 * time.Second).Should(Succeed())
   290  
   291  		Eventually(func(g Gomega) {
   292  			g.Expect(k8sClient.Get(hubCtx, appKey, app)).Should(Succeed())
   293  			g.Expect(app.Status.Phase).Should(Equal(oamcomm.ApplicationRunning))
   294  			deploy := &v1.Deployment{}
   295  			g.Expect(k8sClient.Get(workerCtx, types.NamespacedName{Namespace: namespace, Name: "busybox"}, deploy)).Should(Succeed())
   296  			g.Expect(deploy.Spec.Template.Spec.Containers[0].Image).Should(Equal("busybox"))
   297  			g.Expect(k8sClient.Get(workerCtx, types.NamespacedName{Namespace: namespace, Name: "busybox-ref"}, deploy)).Should(Succeed())
   298  			g.Expect(deploy.Spec.Replicas).Should(Equal(pointer.Int32(0)))
   299  			revs, err := application.GetSortedAppRevisions(hubCtx, k8sClient, app.Name, namespace)
   300  			g.Expect(err).Should(Succeed())
   301  			g.Expect(len(revs)).Should(Equal(1))
   302  		}).WithTimeout(time.Minute).WithPolling(2 * time.Second).Should(Succeed())
   303  	})
   304  
   305  	It("Test large application parallel apply and delete", func() {
   306  		newApp := &v1beta1.Application{ObjectMeta: metav1.ObjectMeta{Namespace: namespace, Name: "large-app"}}
   307  		size := 30
   308  		for i := 0; i < size; i++ {
   309  			newApp.Spec.Components = append(newApp.Spec.Components, oamcomm.ApplicationComponent{
   310  				Name:       fmt.Sprintf("comp-%d", i),
   311  				Type:       "webservice",
   312  				Properties: &runtime.RawExtension{Raw: []byte(`{"image":"busybox","imagePullPolicy":"IfNotPresent","cmd":["sleep","86400"]}`)},
   313  			})
   314  		}
   315  		newApp.Spec.Policies = append(newApp.Spec.Policies, v1beta1.AppPolicy{
   316  			Name:       "topology-deploy",
   317  			Type:       "topology",
   318  			Properties: &runtime.RawExtension{Raw: []byte(fmt.Sprintf(`{"clusters":["%s"]}`, WorkerClusterName))},
   319  		})
   320  		newApp.Spec.Workflow = &v1beta1.Workflow{
   321  			Steps: []workflowv1alpha1.WorkflowStep{{
   322  				WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{
   323  					Name:       "deploy",
   324  					Type:       "deploy",
   325  					Properties: &runtime.RawExtension{Raw: []byte(`{"policies":["topology-deploy"],"parallelism":10}`)},
   326  				},
   327  			}},
   328  		}
   329  		Expect(k8sClient.Create(context.Background(), newApp)).Should(Succeed())
   330  		Eventually(func(g Gomega) {
   331  			deploys := &v1.DeploymentList{}
   332  			g.Expect(k8sClient.List(workerCtx, deploys, client.InNamespace(namespace))).Should(Succeed())
   333  			g.Expect(len(deploys.Items)).Should(Equal(size))
   334  		}, 2*time.Minute).Should(Succeed())
   335  
   336  		Eventually(func(g Gomega) {
   337  			app := &v1beta1.Application{}
   338  			g.Expect(k8sClient.Get(context.Background(), client.ObjectKeyFromObject(newApp), app)).Should(Succeed())
   339  			g.Expect(k8sClient.Delete(context.Background(), app)).Should(Succeed())
   340  		}, 15*time.Second).Should(Succeed())
   341  
   342  		Eventually(func(g Gomega) {
   343  			app := &v1beta1.Application{}
   344  			err := k8sClient.Get(context.Background(), client.ObjectKeyFromObject(newApp), app)
   345  			g.Expect(errors.IsNotFound(err)).Should(BeTrue())
   346  		}, time.Minute).Should(Succeed())
   347  	})
   348  
   349  	It("Test ref-objects with url", func() {
   350  		newApp := &v1beta1.Application{
   351  			ObjectMeta: metav1.ObjectMeta{Namespace: namespace, Name: "app"},
   352  			Spec: v1beta1.ApplicationSpec{
   353  				Components: []oamcomm.ApplicationComponent{{
   354  					Name:       "example",
   355  					Type:       "ref-objects",
   356  					Properties: &runtime.RawExtension{Raw: []byte(`{"urls":["https://gist.githubusercontent.com/Somefive/b189219a9222eaa70b8908cf4379402b/raw/920e83b1a2d56b584f9d8c7a97810a505a0bbaad/example-busybox-resources.yaml"]}`)},
   357  				}},
   358  			},
   359  		}
   360  
   361  		By("Create application")
   362  		Expect(k8sClient.Create(hubCtx, newApp)).Should(Succeed())
   363  		Eventually(func(g Gomega) {
   364  			g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(newApp), newApp)).Should(Succeed())
   365  			g.Expect(newApp.Status.Phase).Should(Equal(oamcomm.ApplicationRunning))
   366  		}, 15*time.Second).Should(Succeed())
   367  		key := types.NamespacedName{Namespace: namespace, Name: "busybox"}
   368  		Expect(k8sClient.Get(hubCtx, key, &v1.Deployment{})).Should(Succeed())
   369  		Expect(k8sClient.Get(hubCtx, key, &corev1.ConfigMap{})).Should(Succeed())
   370  
   371  		By("Delete application")
   372  		Expect(k8sClient.Delete(hubCtx, newApp)).Should(Succeed())
   373  		Eventually(func(g Gomega) {
   374  			g.Expect(k8sClient.Get(hubCtx, client.ObjectKeyFromObject(newApp), newApp)).Should(Satisfy(errors.IsNotFound))
   375  		}, 15*time.Second).Should(Succeed())
   376  		Expect(k8sClient.Get(hubCtx, key, &v1.Deployment{})).Should(Satisfy(errors.IsNotFound))
   377  		Expect(k8sClient.Get(hubCtx, key, &corev1.ConfigMap{})).Should(Satisfy(errors.IsNotFound))
   378  	})
   379  })