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 })