github.com/oam-dev/kubevela@v1.9.11/pkg/controller/core.oam.dev/v1beta1/application/gc_policy_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 application 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "reflect" 24 "strconv" 25 "time" 26 27 v1 "k8s.io/api/apps/v1" 28 networkingv1 "k8s.io/api/networking/v1" 29 30 . "github.com/onsi/ginkgo/v2" 31 . "github.com/onsi/gomega" 32 "github.com/pkg/errors" 33 corev1 "k8s.io/api/core/v1" 34 kerrors "k8s.io/apimachinery/pkg/api/errors" 35 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 36 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 37 "k8s.io/apimachinery/pkg/runtime" 38 "sigs.k8s.io/controller-runtime/pkg/client" 39 "sigs.k8s.io/controller-runtime/pkg/reconcile" 40 "sigs.k8s.io/yaml" 41 42 workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1" 43 44 "github.com/oam-dev/kubevela/apis/core.oam.dev/common" 45 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" 46 "github.com/oam-dev/kubevela/pkg/oam" 47 "github.com/oam-dev/kubevela/pkg/oam/testutil" 48 "github.com/oam-dev/kubevela/pkg/oam/util" 49 "github.com/oam-dev/kubevela/pkg/resourcekeeper" 50 ) 51 52 var _ = Describe("Test Application with GC options", func() { 53 ctx := context.Background() 54 55 ns := &corev1.Namespace{ 56 ObjectMeta: metav1.ObjectMeta{ 57 Name: "vela-test-app-with-gc-options", 58 }, 59 } 60 61 worker := &v1beta1.ComponentDefinition{} 62 workerCdDefJson, _ := yaml.YAMLToJSON([]byte(componentDefYaml)) 63 64 ingressTrait := &v1beta1.TraitDefinition{} 65 ingressTdDefJson, _ := yaml.YAMLToJSON([]byte(ingressTraitDefYaml)) 66 67 configMap := &v1beta1.TraitDefinition{} 68 configMapTdDefJson, _ := yaml.YAMLToJSON([]byte(configMapTraitDefYaml)) 69 70 BeforeEach(func() { 71 Expect(k8sClient.Create(ctx, ns.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) 72 73 Expect(json.Unmarshal(workerCdDefJson, worker)).Should(BeNil()) 74 Expect(k8sClient.Create(ctx, worker.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) 75 76 Expect(json.Unmarshal(ingressTdDefJson, ingressTrait)).Should(BeNil()) 77 Expect(k8sClient.Create(ctx, ingressTrait.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) 78 79 Expect(json.Unmarshal(configMapTdDefJson, configMap)).Should(BeNil()) 80 Expect(k8sClient.Create(ctx, configMap.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) 81 }) 82 83 Context("Test Application enable gc option keepLegacyResource", func() { 84 baseApp := &v1beta1.Application{ 85 TypeMeta: metav1.TypeMeta{ 86 Kind: "Application", 87 APIVersion: "core.oam.dev/v1beta1", 88 }, 89 ObjectMeta: metav1.ObjectMeta{ 90 Name: "baseApp", 91 }, 92 Spec: v1beta1.ApplicationSpec{ 93 Components: []common.ApplicationComponent{ 94 { 95 Name: "worker", 96 Type: "worker", 97 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 98 Traits: []common.ApplicationTrait{{ 99 Type: "ingress-without-healthcheck", 100 Properties: &runtime.RawExtension{Raw: []byte(`{"domain":"test.com","http":{"/": 80}}`)}, 101 }}, 102 }, 103 }, 104 Policies: []v1beta1.AppPolicy{{ 105 Name: "keep-legacy-resource", 106 Type: "garbage-collect", 107 Properties: &runtime.RawExtension{Raw: []byte(`{"keepLegacyResource": true}`)}, 108 }}, 109 }, 110 } 111 112 It("Each update will create a new workload and trait object", func() { 113 resourcekeeper.MarkWithProbability = 1.0 114 app := baseApp.DeepCopy() 115 app.SetNamespace(ns.Name) 116 app.SetName("app-with-worker-ingress") 117 118 Expect(k8sClient.Create(ctx, app)).Should(BeNil()) 119 appV1 := new(v1beta1.Application) 120 Eventually(func() error { 121 _, err := testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)}) 122 if err != nil { 123 return err 124 } 125 if err = k8sClient.Get(ctx, client.ObjectKeyFromObject(app), appV1); err != nil { 126 return err 127 } 128 if appV1.Status.Phase != common.ApplicationRunning { 129 return errors.New("app is not in running status") 130 } 131 return nil 132 }, 3*time.Second, 300*time.Second).Should(BeNil()) 133 134 By("update app with new component name") 135 for i := 2; i <= 6; i++ { 136 Eventually(func() error { 137 oldApp := new(v1beta1.Application) 138 Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(app), oldApp)).Should(BeNil()) 139 updateApp := oldApp.DeepCopy() 140 updateApp.Spec.Components[0].Name = fmt.Sprintf("%s-v%d", "worker", i) 141 return k8sClient.Update(ctx, updateApp) 142 }, time.Second*3, time.Microsecond*300).Should(BeNil()) 143 144 testutil.ReconcileRetry(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)}) 145 newApp := new(v1beta1.Application) 146 Eventually(func() error { 147 _, err := testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)}) 148 if err != nil { 149 return err 150 } 151 if err = k8sClient.Get(ctx, client.ObjectKeyFromObject(app), newApp); err != nil { 152 return err 153 } 154 if newApp.Status.Phase != common.ApplicationRunning { 155 return errors.New("app is not in running status") 156 } 157 return nil 158 }, 3*time.Second, 300*time.Second).Should(BeNil()) 159 Expect(newApp.Status.LatestRevision.Revision).Should(Equal(int64(i))) 160 161 rtObjKey := client.ObjectKey{Name: fmt.Sprintf("%s-v%d-%s", app.Name, i, ns.Name)} 162 rt := new(v1beta1.ResourceTracker) 163 Expect(k8sClient.Get(ctx, rtObjKey, rt)).Should(BeNil()) 164 Expect(len(rt.Spec.ManagedResources)).Should(Equal(3)) 165 166 for _, obj := range rt.Spec.ManagedResources { 167 un := new(unstructured.Unstructured) 168 un.SetGroupVersionKind(obj.GroupVersionKind()) 169 Expect(k8sClient.Get(ctx, client.ObjectKey{Name: obj.Name, Namespace: obj.Namespace}, un)).Should(BeNil()) 170 } 171 } 172 173 By("check the resourceTrackers number") 174 listOpts := []client.ListOption{ 175 client.MatchingLabels{ 176 oam.LabelAppName: app.Name, 177 oam.LabelAppNamespace: app.Namespace, 178 }} 179 180 rtList := &v1beta1.ResourceTrackerList{} 181 Expect(k8sClient.List(ctx, rtList, listOpts...)) 182 Expect(len(rtList.Items)).Should(Equal(6)) 183 184 By("delete one resourceTracker to test the gc of legacy resources") 185 testRT := &v1beta1.ResourceTracker{ 186 ObjectMeta: metav1.ObjectMeta{ 187 Name: fmt.Sprintf("%s-v%d-%s", app.Name, 1, ns.Name), 188 }, 189 } 190 Expect(k8sClient.Delete(ctx, testRT)).Should(BeNil()) 191 for _, obj := range testRT.Spec.ManagedResources { 192 un := &unstructured.Unstructured{} 193 un.SetGroupVersionKind(obj.GroupVersionKind()) 194 Eventually(func() error { 195 if err := k8sClient.Get(ctx, client.ObjectKey{Name: obj.Name, Namespace: obj.Name}, un); kerrors.IsNotFound(err) { 196 return nil 197 } 198 return errors.Errorf("failed to gc resource %v:%s", un.GroupVersionKind(), un.GetName()) 199 }, 3*time.Second, 300*time.Millisecond).Should(BeNil()) 200 } 201 202 By("check the latest resources created by application") 203 latestApp := new(v1beta1.Application) 204 Expect(k8sClient.Get(ctx, client.ObjectKey{Name: app.Name, Namespace: app.Namespace}, latestApp)).Should(BeNil()) 205 appliedResource := latestApp.Status.AppliedResources 206 for _, obj := range appliedResource { 207 un := &unstructured.Unstructured{} 208 un.SetGroupVersionKind(obj.GroupVersionKind()) 209 Expect(k8sClient.Get(ctx, client.ObjectKey{Name: obj.Name, Namespace: obj.Namespace}, un)).Should(BeNil()) 210 } 211 212 By("delete part legacy resource") 213 for i := 2; i <= 4; i++ { 214 deploy := new(v1.Deployment) 215 deploy.SetName(fmt.Sprintf("worker-v%d", i)) 216 deploy.SetNamespace(ns.Name) 217 Expect(k8sClient.Delete(ctx, deploy)) 218 219 ingress := new(networkingv1.Ingress) 220 ingress.SetName(fmt.Sprintf("worker-v%d", i)) 221 ingress.SetNamespace(ns.Name) 222 Expect(k8sClient.Delete(ctx, ingress)) 223 224 svc := new(corev1.Service) 225 svc.SetName(fmt.Sprintf("worker-v%d", i)) 226 svc.SetNamespace(ns.Name) 227 Expect(k8sClient.Delete(ctx, svc)) 228 } 229 230 By("update app with new component name") 231 232 Eventually(func() error { 233 oldApp := new(v1beta1.Application) 234 Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(app), oldApp)).Should(BeNil()) 235 updateApp := oldApp.DeepCopy() 236 updateApp.Spec.Components[0].Name = fmt.Sprintf("%s-v%d", "worker", 12) 237 return k8sClient.Update(ctx, updateApp) 238 }, time.Second*3, time.Microsecond*300).Should(BeNil()) 239 240 testutil.ReconcileRetry(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)}) 241 newApp := new(v1beta1.Application) 242 Eventually(func() error { 243 _, err := testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)}) 244 if err != nil { 245 return err 246 } 247 if err = k8sClient.Get(ctx, client.ObjectKeyFromObject(app), newApp); err != nil { 248 return err 249 } 250 if newApp.Status.Phase != common.ApplicationRunning { 251 return errors.New("app is not in running status") 252 } 253 return nil 254 }, 3*time.Second, 300*time.Microsecond).Should(BeNil()) 255 Expect(newApp.Status.LatestRevision.Revision).Should(Equal(int64(7))) 256 257 By("check the resourceTrackers number") 258 newRTList := &v1beta1.ResourceTrackerList{} 259 Expect(k8sClient.List(ctx, newRTList, listOpts...)) 260 Expect(len(newRTList.Items)).Should(Equal(3)) 261 262 By("delete all resources") 263 Expect(k8sClient.Delete(ctx, app)).Should(BeNil()) 264 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)}) 265 Expect(k8sClient.List(ctx, rtList, listOpts...)) 266 Expect(len(rtList.Items)).Should(Equal(0)) 267 }) 268 269 It("Each update will create a new workload and update a trait object", func() { 270 app := baseApp.DeepCopy() 271 app.SetNamespace(ns.Name) 272 app.SetName("app-with-work-ingress-configmap") 273 274 app.Spec.Components[0].Name = "job" 275 app.Spec.Components[0].Traits = append(app.Spec.Components[0].Traits, common.ApplicationTrait{ 276 Type: "configmap", 277 Properties: &runtime.RawExtension{Raw: []byte(`{"volumes": [{"name": "test-cm", "mountPath":"/tmp/test"}]}`)}, 278 }) 279 280 Expect(k8sClient.Create(ctx, app)).Should(BeNil()) 281 appV1 := new(v1beta1.Application) 282 Eventually(func() error { 283 _, err := testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)}) 284 if err != nil { 285 return err 286 } 287 if err = k8sClient.Get(ctx, client.ObjectKeyFromObject(app), appV1); err != nil { 288 return err 289 } 290 if appV1.Status.Phase != common.ApplicationRunning { 291 return errors.New("app is not in running status") 292 } 293 return nil 294 }, 3*time.Second, 300*time.Second).Should(BeNil()) 295 296 By("update app's component name and the properties of configmap") 297 for i := 2; i <= 6; i++ { 298 Eventually(func() error { 299 oldApp := new(v1beta1.Application) 300 Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(app), oldApp)).Should(BeNil()) 301 updateApp := oldApp.DeepCopy() 302 updateApp.Spec.Components[0].Name = fmt.Sprintf("%s-v%d", "job", i) 303 updateApp.Spec.Components[0].Traits = append(app.Spec.Components[0].Traits, common.ApplicationTrait{ 304 Type: "configmap", 305 Properties: &runtime.RawExtension{Raw: []byte(fmt.Sprintf(`{"volumes": [{"name": "test-cm","mountPath": "/tmp/test","data": {"test": "%d"}}]}`, i))}, 306 }) 307 return k8sClient.Update(ctx, updateApp) 308 }, time.Second*3, time.Microsecond*300).Should(BeNil()) 309 310 newApp := new(v1beta1.Application) 311 Eventually(func() error { 312 _, err := testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)}) 313 if err != nil { 314 return err 315 } 316 if err = k8sClient.Get(ctx, client.ObjectKeyFromObject(app), newApp); err != nil { 317 return err 318 } 319 if newApp.Status.Phase != common.ApplicationRunning { 320 return errors.New("app is not in running status") 321 } 322 return nil 323 }, 5*time.Second, 300*time.Second).Should(BeNil()) 324 Expect(newApp.Status.LatestRevision.Revision).Should(Equal(int64(i))) 325 326 rtObjKey := client.ObjectKey{Name: fmt.Sprintf("%s-v%d-%s", app.Name, i, ns.Name)} 327 rt := new(v1beta1.ResourceTracker) 328 Expect(k8sClient.Get(ctx, rtObjKey, rt)).Should(BeNil()) 329 Expect(len(rt.Spec.ManagedResources)).Should(Equal(4)) 330 331 for _, obj := range rt.Spec.ManagedResources { 332 if obj.Kind == reflect.TypeOf(corev1.ConfigMap{}).Name() { 333 Expect(obj.Name).Should(Equal("test-cm")) 334 cm := new(corev1.ConfigMap) 335 Expect(k8sClient.Get(ctx, client.ObjectKey{Name: obj.Name, Namespace: obj.Namespace}, cm)).Should(BeNil()) 336 Expect(cm.Data["test"]).Should(Equal(strconv.Itoa(i))) 337 continue 338 } 339 un := new(unstructured.Unstructured) 340 un.SetGroupVersionKind(obj.GroupVersionKind()) 341 Expect(k8sClient.Get(ctx, client.ObjectKey{Name: obj.Name, Namespace: obj.Namespace}, un)).Should(BeNil()) 342 } 343 } 344 345 By("check the resourceTrackers number") 346 listOpts := []client.ListOption{ 347 client.MatchingLabels{ 348 oam.LabelAppName: app.Name, 349 oam.LabelAppNamespace: app.Namespace, 350 }} 351 352 rtList := &v1beta1.ResourceTrackerList{} 353 Expect(k8sClient.List(ctx, rtList, listOpts...)) 354 Expect(len(rtList.Items)).Should(Equal(6)) 355 356 By("delete one resourceTracker to test the gc of legacy resources") 357 testRT := &v1beta1.ResourceTracker{ 358 ObjectMeta: metav1.ObjectMeta{ 359 Name: fmt.Sprintf("%s-v%d-%s", app.Name, 1, ns.Name), 360 }, 361 } 362 363 Expect(k8sClient.Delete(ctx, testRT)).Should(BeNil()) 364 for _, obj := range testRT.Spec.ManagedResources { 365 if obj.Kind == reflect.TypeOf(corev1.ConfigMap{}).Name() { 366 cm := new(corev1.ConfigMap) 367 Expect(k8sClient.Get(ctx, client.ObjectKey{Name: obj.Name, Namespace: obj.Namespace}, cm)).Should(BeNil()) 368 continue 369 } 370 un := &unstructured.Unstructured{} 371 un.SetGroupVersionKind(obj.GroupVersionKind()) 372 Eventually(func() error { 373 if err := k8sClient.Get(ctx, client.ObjectKey{Name: obj.Name, Namespace: obj.Name}, un); kerrors.IsNotFound(err) { 374 return nil 375 } 376 return errors.Errorf("failed to gc resource %v:%s", un.GroupVersionKind(), un.GetName()) 377 }, 3*time.Second, 300*time.Millisecond).Should(BeNil()) 378 } 379 380 By("check the latest resources created by application") 381 latestApp := new(v1beta1.Application) 382 Expect(k8sClient.Get(ctx, client.ObjectKey{Name: app.Name, Namespace: app.Namespace}, latestApp)).Should(BeNil()) 383 appliedResource := latestApp.Status.AppliedResources 384 for _, obj := range appliedResource { 385 un := &unstructured.Unstructured{} 386 un.SetGroupVersionKind(obj.GroupVersionKind()) 387 Expect(k8sClient.Get(ctx, client.ObjectKey{Name: obj.Name, Namespace: obj.Namespace}, un)).Should(BeNil()) 388 } 389 390 By("delete all resources") 391 Expect(k8sClient.Delete(ctx, app)).Should(BeNil()) 392 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)}) 393 Expect(k8sClient.List(ctx, rtList, listOpts...)) 394 Expect(len(rtList.Items)).Should(Equal(0)) 395 }) 396 397 It("Each update will only update workload", func() { 398 resourcekeeper.MarkWithProbability = 1.0 399 app := baseApp.DeepCopy() 400 app.Spec.Components[0].Traits = nil 401 app.Spec.Components[0].Name = "only-work" 402 app.SetNamespace(ns.Name) 403 app.SetName("app-with-worker") 404 405 Expect(k8sClient.Create(ctx, app)).Should(BeNil()) 406 appV1 := new(v1beta1.Application) 407 Eventually(func() error { 408 _, err := testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)}) 409 if err != nil { 410 return err 411 } 412 if err = k8sClient.Get(ctx, client.ObjectKeyFromObject(app), appV1); err != nil { 413 return err 414 } 415 if appV1.Status.Phase != common.ApplicationRunning { 416 return errors.New("app is not in running status") 417 } 418 return nil 419 }, 3*time.Second, 300*time.Second).Should(BeNil()) 420 421 By("update component with new properties") 422 for i := 2; i <= 11; i++ { 423 Eventually(func() error { 424 oldApp := new(v1beta1.Application) 425 Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(app), oldApp)).Should(BeNil()) 426 updateApp := oldApp.DeepCopy() 427 updateApp.Spec.Components[0].Properties = &runtime.RawExtension{Raw: []byte(fmt.Sprintf(`{"cmd":["sleep","%d"],"image":"busybox"}`, i))} 428 return k8sClient.Update(ctx, updateApp) 429 }, time.Second*3, time.Microsecond*300).Should(BeNil()) 430 431 testutil.ReconcileRetry(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)}) 432 newApp := new(v1beta1.Application) 433 Eventually(func() error { 434 _, err := testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)}) 435 if err != nil { 436 return err 437 } 438 if err = k8sClient.Get(ctx, client.ObjectKeyFromObject(app), newApp); err != nil { 439 return err 440 } 441 if newApp.Status.Phase != common.ApplicationRunning { 442 return errors.New("app is not in running status") 443 } 444 return nil 445 }, 3*time.Second, 300*time.Second).Should(BeNil()) 446 Expect(newApp.Status.LatestRevision.Revision).Should(Equal(int64(i))) 447 448 rtObjKey := client.ObjectKey{Name: fmt.Sprintf("%s-v%d-%s", app.Name, i, ns.Name)} 449 rt := new(v1beta1.ResourceTracker) 450 Expect(k8sClient.Get(ctx, rtObjKey, rt)).Should(BeNil()) 451 Expect(len(rt.Spec.ManagedResources)).Should(Equal(1)) 452 453 for _, obj := range rt.Spec.ManagedResources { 454 un := new(unstructured.Unstructured) 455 un.SetGroupVersionKind(obj.GroupVersionKind()) 456 Expect(k8sClient.Get(ctx, client.ObjectKey{Name: obj.Name, Namespace: obj.Namespace}, un)).Should(BeNil()) 457 } 458 } 459 460 By("check the resourceTrackers number") 461 listOpts := []client.ListOption{ 462 client.MatchingLabels{ 463 oam.LabelAppName: app.Name, 464 oam.LabelAppNamespace: app.Namespace, 465 }} 466 467 rtList := &v1beta1.ResourceTrackerList{} 468 Expect(k8sClient.List(ctx, rtList, listOpts...)) 469 Expect(len(rtList.Items)).Should(Equal(1)) 470 471 By("delete all resources") 472 Expect(k8sClient.Delete(ctx, app)).Should(BeNil()) 473 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)}) 474 Expect(k8sClient.List(ctx, rtList, listOpts...)) 475 Expect(len(rtList.Items)).Should(Equal(0)) 476 }) 477 }) 478 479 Context("Test Application enable gc option sequential", func() { 480 baseApp := &v1beta1.Application{ 481 TypeMeta: metav1.TypeMeta{ 482 Kind: "Application", 483 APIVersion: "core.oam.dev/v1beta1", 484 }, 485 ObjectMeta: metav1.ObjectMeta{ 486 Name: "sequential-gc", 487 Namespace: "default", 488 }, 489 Spec: v1beta1.ApplicationSpec{ 490 Components: []common.ApplicationComponent{ 491 { 492 Name: "worker1", 493 Type: "worker", 494 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 495 DependsOn: []string{ 496 "worker2", 497 }, 498 }, 499 { 500 Name: "worker2", 501 Type: "worker", 502 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 503 Inputs: workflowv1alpha1.StepInputs{ 504 { 505 From: "worker3-output", 506 ParameterKey: "test", 507 }, 508 }, 509 }, 510 { 511 Name: "worker3", 512 Type: "worker", 513 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 514 Outputs: workflowv1alpha1.StepOutputs{ 515 { 516 Name: "worker3-output", 517 ValueFrom: "output.metadata.name", 518 }, 519 }, 520 }, 521 }, 522 Policies: []v1beta1.AppPolicy{{ 523 Name: "reverse-dependency", 524 Type: "garbage-collect", 525 Properties: &runtime.RawExtension{Raw: []byte(`{"order": "dependency"}`)}, 526 }}, 527 }, 528 } 529 530 It("Test GC with sequential", func() { 531 resourcekeeper.MarkWithProbability = 1.0 532 app := baseApp.DeepCopy() 533 534 Expect(k8sClient.Create(ctx, app)).Should(BeNil()) 535 appV1 := new(v1beta1.Application) 536 Eventually(func() error { 537 _, err := testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)}) 538 if err != nil { 539 return err 540 } 541 if err = k8sClient.Get(ctx, client.ObjectKeyFromObject(app), appV1); err != nil { 542 return err 543 } 544 if appV1.Status.Phase != common.ApplicationRunning { 545 return errors.New("app is not in running status") 546 } 547 return nil 548 }, 3*time.Second, 300*time.Second).Should(BeNil()) 549 550 By("check the resourceTrackers number") 551 listOpts := []client.ListOption{ 552 client.MatchingLabels{ 553 oam.LabelAppName: app.Name, 554 oam.LabelAppNamespace: app.Namespace, 555 }} 556 557 rtList := &v1beta1.ResourceTrackerList{} 558 Expect(k8sClient.List(ctx, rtList, listOpts...)).Should(BeNil()) 559 Expect(len(rtList.Items)).Should(Equal(1)) 560 workerList := &v1.DeploymentList{} 561 Expect(k8sClient.List(ctx, workerList, listOpts...)).Should(BeNil()) 562 Expect(len(workerList.Items)).Should(Equal(3)) 563 564 By("delete application") 565 Expect(k8sClient.Delete(ctx, app)).Should(BeNil()) 566 By("worker1 will be deleted") 567 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)}) 568 Expect(k8sClient.List(ctx, workerList, listOpts...)).Should(BeNil()) 569 for _, worker := range workerList.Items { 570 Expect(worker.Name).ShouldNot(Equal("worker1")) 571 } 572 Expect(len(workerList.Items)).Should(Equal(2)) 573 By("worker2 will be deleted") 574 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)}) 575 Expect(k8sClient.List(ctx, workerList, listOpts...)).Should(BeNil()) 576 Expect(len(workerList.Items)).Should(Equal(1)) 577 for _, worker := range workerList.Items { 578 Expect(worker.Name).ShouldNot(Equal("worker2")) 579 } 580 By("worker3 will be deleted") 581 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)}) 582 Expect(k8sClient.List(ctx, workerList, listOpts...)).Should(BeNil()) 583 Expect(len(workerList.Items)).Should(Equal(0)) 584 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(app)}) 585 Expect(k8sClient.List(ctx, rtList, listOpts...)).Should(BeNil()) 586 Expect(len(rtList.Items)).Should(Equal(0)) 587 }) 588 }) 589 }) 590 591 const ( 592 ingressTraitDefYaml = ` 593 apiVersion: core.oam.dev/v1beta1 594 kind: TraitDefinition 595 metadata: 596 name: ingress-without-healthcheck 597 namespace: vela-system 598 spec: 599 schematic: 600 cue: 601 template: | 602 parameter: { 603 domain: string 604 http: [string]: int 605 } 606 // trait template can have multiple outputs in one trait 607 outputs: service: { 608 apiVersion: "v1" 609 kind: "Service" 610 metadata: 611 name: context.name 612 spec: { 613 selector: 614 app: context.name 615 ports: [ 616 for k, v in parameter.http { 617 port: v 618 targetPort: v 619 }, 620 ] 621 } 622 } 623 outputs: ingress: { 624 apiVersion: "networking.k8s.io/v1" 625 kind: "Ingress" 626 metadata: 627 name: context.name 628 spec: { 629 rules: [{ 630 host: parameter.domain 631 http: { 632 paths: [ 633 for k, v in parameter.http { 634 path: k 635 pathType: "Prefix" 636 backend: { 637 service: { 638 name: context.name 639 port: { 640 number: v 641 } 642 } 643 } 644 }, 645 ] 646 } 647 }] 648 } 649 } 650 ` 651 configMapTraitDefYaml = ` 652 apiVersion: core.oam.dev/v1beta1 653 kind: TraitDefinition 654 metadata: 655 annotations: 656 definition.oam.dev/description: Create/Attach configmaps on K8s pod for your workload which follows the pod spec in path 'spec.template'. 657 name: configmap 658 namespace: vela-system 659 spec: 660 appliesToWorkloads: 661 - '*' 662 podDisruptive: true 663 schematic: 664 cue: 665 template: | 666 patch: spec: template: spec: { 667 containers: [{ 668 // +patchKey=name 669 volumeMounts: [ 670 for v in parameter.volumes { 671 { 672 name: "volume-\(v.name)" 673 mountPath: v.mountPath 674 readOnly: v.readOnly 675 } 676 }, 677 ] 678 }, ...] 679 // +patchKey=name 680 volumes: [ 681 for v in parameter.volumes { 682 { 683 name: "volume-\(v.name)" 684 configMap: name: v.name 685 } 686 }, 687 ] 688 } 689 outputs: { 690 for v in parameter.volumes { 691 if v.data != _|_ { 692 "\(v.name)": { 693 apiVersion: "v1" 694 kind: "ConfigMap" 695 metadata: name: v.name 696 data: v.data 697 } 698 } 699 } 700 } 701 parameter: { 702 // +usage=Specify mounted configmap names and their mount paths in the container 703 volumes: [...{ 704 name: string 705 mountPath: string 706 readOnly: *false | bool 707 data?: [string]: string 708 }] 709 } 710 ` 711 )