github.com/oam-dev/kubevela@v1.9.11/pkg/controller/core.oam.dev/v1beta1/application/application_controller_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 "path/filepath" 24 sysruntime "runtime" 25 "strings" 26 "testing" 27 "time" 28 29 "github.com/oam-dev/kubevela/pkg/features" 30 31 "github.com/google/go-cmp/cmp" 32 testdef "github.com/kubevela/pkg/util/test/definition" 33 wffeatures "github.com/kubevela/workflow/pkg/features" 34 . "github.com/onsi/ginkgo/v2" 35 . "github.com/onsi/gomega" 36 v1 "k8s.io/api/apps/v1" 37 autoscalingv1 "k8s.io/api/autoscaling/v1" 38 corev1 "k8s.io/api/core/v1" 39 networkingv1 "k8s.io/api/networking/v1" 40 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 41 "k8s.io/apimachinery/pkg/runtime" 42 "k8s.io/apimachinery/pkg/types" 43 utilfeature "k8s.io/apiserver/pkg/util/feature" 44 featuregatetesting "k8s.io/component-base/featuregate/testing" 45 "sigs.k8s.io/controller-runtime/pkg/client" 46 "sigs.k8s.io/controller-runtime/pkg/reconcile" 47 "sigs.k8s.io/yaml" 48 49 workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1" 50 "github.com/kubevela/workflow/pkg/debug" 51 wfTypes "github.com/kubevela/workflow/pkg/types" 52 53 "github.com/oam-dev/kubevela/apis/core.oam.dev/common" 54 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" 55 velatypes "github.com/oam-dev/kubevela/apis/types" 56 "github.com/oam-dev/kubevela/pkg/oam" 57 "github.com/oam-dev/kubevela/pkg/oam/testutil" 58 "github.com/oam-dev/kubevela/pkg/oam/util" 59 common2 "github.com/oam-dev/kubevela/pkg/utils/common" 60 "github.com/oam-dev/kubevela/version" 61 ) 62 63 // TODO: Refactor the tests to not copy and paste duplicated code 10 times 64 var _ = Describe("Test Application Controller", func() { 65 ctx := context.TODO() 66 appwithNoTrait := &v1beta1.Application{ 67 TypeMeta: metav1.TypeMeta{ 68 Kind: "Application", 69 APIVersion: "core.oam.dev/v1beta1", 70 }, 71 ObjectMeta: metav1.ObjectMeta{ 72 Name: "app-with-no-trait", 73 }, 74 Spec: v1beta1.ApplicationSpec{ 75 Components: []common.ApplicationComponent{ 76 { 77 Name: "myweb2", 78 Type: "worker", 79 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 80 }, 81 }, 82 }, 83 } 84 85 appFailParse := appwithNoTrait.DeepCopy() 86 appFailParse.SetName("app-fail-to-parsed") 87 appFailParse.Spec.Components[0].Type = "fakeWorker" 88 89 appFailRender := appwithNoTrait.DeepCopy() 90 appFailRender.SetName("app-fail-to-render") 91 appFailRender.Spec.Components[0].Properties = &runtime.RawExtension{ 92 Raw: []byte(`{"cmd1":["sleep","1000"],"image1":"busybox"}`), 93 } 94 appFailRender.Spec.Policies = []v1beta1.AppPolicy{ 95 { 96 Name: "policy1", 97 Type: "foopolicy", 98 Properties: &runtime.RawExtension{Raw: []byte(`{"test":"test"}`)}, 99 }, 100 } 101 102 var getExpDeployment = func(compName string, app *v1beta1.Application) *v1.Deployment { 103 var namespace = app.Namespace 104 if namespace == "" { 105 namespace = "default" 106 } 107 return &v1.Deployment{ 108 TypeMeta: metav1.TypeMeta{ 109 Kind: "Deployment", 110 APIVersion: "apps/v1", 111 }, 112 ObjectMeta: metav1.ObjectMeta{ 113 Labels: map[string]string{ 114 "workload.oam.dev/type": "worker", 115 "app.oam.dev/component": compName, 116 "app.oam.dev/name": app.Name, 117 "app.oam.dev/namespace": app.Namespace, 118 "app.oam.dev/appRevision": app.Name + "-v1", 119 "app.oam.dev/resourceType": "WORKLOAD", 120 }, 121 Name: compName, 122 Namespace: namespace, 123 }, 124 Spec: v1.DeploymentSpec{ 125 Selector: &metav1.LabelSelector{MatchLabels: map[string]string{ 126 "app.oam.dev/component": compName, 127 }}, 128 Template: corev1.PodTemplateSpec{ 129 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{ 130 "app.oam.dev/component": compName, 131 }}, 132 Spec: corev1.PodSpec{Containers: []corev1.Container{{ 133 Image: "busybox", 134 Name: compName, 135 Command: []string{"sleep", "1000"}, 136 }, 137 }}}, 138 }, 139 } 140 } 141 142 appWithTrait := appwithNoTrait.DeepCopy() 143 appWithTrait.SetName("app-with-trait") 144 appWithTrait.Spec.Components[0].Traits = []common.ApplicationTrait{ 145 { 146 Type: "scaler", 147 Properties: &runtime.RawExtension{Raw: []byte(`{"replicas":2}`)}, 148 }, 149 } 150 appWithTrait.Spec.Components[0].Name = "myweb3" 151 152 appWithTraitAndScope := appWithTrait.DeepCopy() 153 appWithTraitAndScope.SetName("app-with-trait-and-scope") 154 appWithTraitAndScope.Spec.Components[0].Name = "myweb4" 155 156 appWithTwoComp := appWithTraitAndScope.DeepCopy() 157 appWithTwoComp.SetName("app-with-two-comp") 158 appWithTwoComp.Spec.Components[0].Name = "myweb5" 159 appWithTwoComp.Spec.Components = append(appWithTwoComp.Spec.Components, common.ApplicationComponent{ 160 Name: "myweb6", 161 Type: "worker", 162 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox2","config":"myconfig"}`)}, 163 }) 164 165 appWithStorage := &v1beta1.Application{ 166 TypeMeta: metav1.TypeMeta{ 167 Kind: "Application", 168 APIVersion: "core.oam.dev/v1beta1", 169 }, 170 ObjectMeta: metav1.ObjectMeta{ 171 Name: "app-storage", 172 }, 173 Spec: v1beta1.ApplicationSpec{ 174 Components: []common.ApplicationComponent{ 175 { 176 Name: "myworker", 177 Type: "worker", 178 Properties: &runtime.RawExtension{Raw: []byte("{\"cmd\":[\"sleep\",\"1000\"],\"image\":\"busybox\",\"env\":[{\"name\":\"firstKey\",\"value\":\"firstValue\"}]}")}, 179 }, 180 }, 181 }, 182 } 183 appWithStorage.Spec.Components[0].Traits = []common.ApplicationTrait{ 184 { 185 Type: "storage", 186 Properties: &runtime.RawExtension{Raw: []byte("{\"configMap\":[{\"name\":\"myworker-cm\",\"mountPath\":\"/test/mount/cm\",\"data\":{\"secondKey\":\"secondValue\"}}]}")}, 187 }, 188 { 189 Type: "env", 190 Properties: &runtime.RawExtension{Raw: []byte("{\"env\":{\"firstKey\":\"newValue\"}}")}, 191 }, 192 } 193 194 appWithHttpsGateway := &v1beta1.Application{ 195 TypeMeta: metav1.TypeMeta{ 196 Kind: "Application", 197 APIVersion: "core.oam.dev/v1beta1", 198 }, 199 ObjectMeta: metav1.ObjectMeta{ 200 Name: "app-gateway", 201 }, 202 Spec: v1beta1.ApplicationSpec{ 203 Components: []common.ApplicationComponent{ 204 { 205 Name: "myworker", 206 Type: "worker", 207 Properties: &runtime.RawExtension{Raw: []byte("{\"cmd\":[\"sleep\",\"1000\"],\"image\":\"busybox\"}")}, 208 }, 209 }, 210 }, 211 } 212 appWithHttpsGateway.Spec.Components[0].Traits = []common.ApplicationTrait{ 213 { 214 Type: "gateway", 215 Properties: &runtime.RawExtension{Raw: []byte(`{"secretName":"myworker-secret","domain":"example.com","http":{"/":80}}`)}, 216 }, 217 } 218 219 appWithMountPath := &v1beta1.Application{ 220 TypeMeta: metav1.TypeMeta{ 221 Kind: "Application", 222 APIVersion: "core.oam.dev/v1beta1", 223 }, 224 ObjectMeta: metav1.ObjectMeta{ 225 Name: "app-storage", 226 }, 227 Spec: v1beta1.ApplicationSpec{ 228 Components: []common.ApplicationComponent{ 229 { 230 Name: "myworker", 231 Type: "worker", 232 Properties: &runtime.RawExtension{Raw: []byte("{\"cmd\":[\"sleep\",\"1000\"],\"image\":\"busybox\"}")}, 233 }, 234 }, 235 }, 236 } 237 appWithMountPath.Spec.Components[0].Traits = []common.ApplicationTrait{ 238 { 239 Type: "storage", 240 Properties: &runtime.RawExtension{Raw: []byte("{\"secret\":[{\"name\":\"myworker-secret\",\"mountToEnv\":{\"envName\":\"firstEnv\",\"secretKey\":\"firstKey\"},\"data\":{\"firstKey\":\"dmFsdWUwMQo=\"}}]}")}, 241 }, 242 { 243 Type: "storage", 244 Properties: &runtime.RawExtension{Raw: []byte("{\"configMap\":[{\"name\": \"myworker-cm\",\"mountToEnv\":{ \"envName\":\"secondEnv\",\"configMapKey\":\"secondKey\"},\"data\": {\"secondKey\":\"secondValue\"}}]}")}, 245 }, 246 } 247 248 appWithControlPlaneOnly := &v1beta1.Application{ 249 TypeMeta: metav1.TypeMeta{ 250 Kind: "Application", 251 APIVersion: "core.oam.dev/v1beta1", 252 }, 253 ObjectMeta: metav1.ObjectMeta{ 254 Name: "app-controlplaneonly", 255 }, 256 Spec: v1beta1.ApplicationSpec{ 257 Components: []common.ApplicationComponent{ 258 { 259 Name: "app-controlplaneonly-component", 260 Type: "worker", 261 Properties: &runtime.RawExtension{Raw: []byte("{\"cmd\":[\"sleep\",\"1000\"],\"image\":\"busybox\"}")}, 262 Traits: []common.ApplicationTrait{ 263 { 264 Type: "hubcpuscaler", 265 Properties: &runtime.RawExtension{Raw: []byte("{\"min\": 1,\"max\": 10,\"cpuPercent\": 60}")}, 266 }, 267 }, 268 }, 269 }, 270 }, 271 } 272 273 appWithHttpsHealthProbe := &v1beta1.Application{ 274 TypeMeta: metav1.TypeMeta{ 275 Kind: "Application", 276 APIVersion: "core.oam.dev/v1beta1", 277 }, 278 ObjectMeta: metav1.ObjectMeta{ 279 Name: "app-httphealthprobe", 280 }, 281 Spec: v1beta1.ApplicationSpec{ 282 Components: []common.ApplicationComponent{ 283 { 284 Name: "app-httphealthprobe-component", 285 Type: "worker", 286 Properties: &runtime.RawExtension{Raw: []byte("{\"cmd\":[\"sleep\",\"1000\"],\"image\":\"busybox\",\"livenessProbe\":{\"failureThreshold\":3,\"httpGet\":{\"path\":\"/v1/health\",\"port\":8080,\"scheme\":\"HTTP\"},\"initialDelaySeconds\":60,\"periodSeconds\":60,\"successThreshold\":1,\"timeoutSeconds\":5}}")}, 287 }, 288 }, 289 }, 290 } 291 292 appWithApplyOnce := &v1beta1.Application{ 293 TypeMeta: metav1.TypeMeta{ 294 Kind: "Application", 295 APIVersion: "core.oam.dev/v1beta1", 296 }, 297 ObjectMeta: metav1.ObjectMeta{ 298 Name: "app-apply-once", 299 }, 300 Spec: v1beta1.ApplicationSpec{ 301 Components: []common.ApplicationComponent{ 302 { 303 Name: "app-applyonce-component", 304 Type: "worker", 305 Properties: &runtime.RawExtension{Raw: []byte("{\"cmd\":[\"sleep\",\"1000\"],\"image\":\"busybox\"}")}, 306 }, 307 }, 308 Policies: []v1beta1.AppPolicy{ 309 { 310 Name: "apply-once-01", 311 Type: "apply-once", 312 Properties: &runtime.RawExtension{Raw: []byte(`{"enable": true,"rules": [{"selector": { "componentNames": ["fourierapp03-comp-01"], "resourceTypes": ["Deployment" ], "strategy": {"path": ["spec.replicas"] } }}]}`)}, 313 }, 314 }, 315 }, 316 } 317 appWithApplyOnce.Spec.Components[0].Traits = []common.ApplicationTrait{ 318 { 319 Type: "scaler", 320 Properties: &runtime.RawExtension{Raw: []byte(`{"replicas":2}`)}, 321 }, 322 } 323 324 appWithMountToEnvs := &v1beta1.Application{ 325 TypeMeta: metav1.TypeMeta{ 326 Kind: "Application", 327 APIVersion: "core.oam.dev/v1beta1", 328 }, 329 ObjectMeta: metav1.ObjectMeta{ 330 Name: "app-with-mount-to-envs", 331 }, 332 Spec: v1beta1.ApplicationSpec{ 333 Components: []common.ApplicationComponent{ 334 { 335 Name: "myweb", 336 Type: "worker", 337 Properties: &runtime.RawExtension{Raw: []byte("{\"cmd\":[\"sleep\",\"1000\"],\"image\":\"busybox\"}")}, 338 }, 339 }, 340 }, 341 } 342 appWithMountToEnvs.Spec.Components[0].Traits = []common.ApplicationTrait{ 343 { 344 Type: "storage", 345 Properties: &runtime.RawExtension{Raw: []byte("{\"secret\": [{\"name\": \"myweb-secret\",\"mountToEnv\": {\"envName\": \"firstEnv\",\"secretKey\": \"firstKey\"},\"mountToEnvs\": [{\"envName\": \"secondEnv\",\"secretKey\": \"secondKey\"}],\"data\": {\"firstKey\": \"dmFsdWUwMQo=\",\"secondKey\": \"dmFsdWUwMgo=\"}}]}")}, 346 }, 347 { 348 Type: "storage", 349 Properties: &runtime.RawExtension{Raw: []byte("{\"configMap\": [{\"name\": \"myweb-cm\",\"mountToEnvs\": [{\"envName\":\"thirdEnv\",\"configMapKey\":\"thirdKey\"},{\"envName\":\"fourthEnv\",\"configMapKey\":\"fourthKey\"}],\"data\": {\"thirdKey\": \"Value03\",\"fourthKey\": \"Value04\"}}]}")}, 350 }, 351 } 352 353 appWithAffinity := &v1beta1.Application{ 354 TypeMeta: metav1.TypeMeta{ 355 Kind: "Application", 356 APIVersion: "core.oam.dev/v1beta1", 357 }, 358 ObjectMeta: metav1.ObjectMeta{ 359 Name: "app-with-affinity", 360 }, 361 Spec: v1beta1.ApplicationSpec{ 362 Components: []common.ApplicationComponent{ 363 { 364 Name: "myweb", 365 Type: "worker", 366 Properties: &runtime.RawExtension{Raw: []byte("{\"cmd\":[\"sleep\",\"1000\"],\"image\":\"busybox\"}")}, 367 }, 368 }, 369 }, 370 } 371 appWithAffinity.Spec.Components[0].Traits = []common.ApplicationTrait{ 372 { 373 Type: "affinity", 374 Properties: &runtime.RawExtension{Raw: []byte("{\"podAffinity\":{\"preferred\":[{\"podAffinityTerm\":{\"labelSelector\":{\"matchExpressions\":[{\"key\": \"security\",\"values\": [\"S1\"]}]},\"namespaces\":[\"default\"],\"topologyKey\": \"kubernetes.io/hostname\"},\"weight\": 1}]}}")}, 375 }, 376 } 377 378 cd := &v1beta1.ComponentDefinition{} 379 cDDefJson, _ := yaml.YAMLToJSON([]byte(componentDefYaml)) 380 k8sObjectsCDJson, _ := yaml.YAMLToJSON([]byte(k8sObjectsComponentDefinitionYaml)) 381 382 pd := &v1beta1.PolicyDefinition{} 383 pd.Namespace = "vela-system" 384 pdDefJson, _ := yaml.YAMLToJSON([]byte(policyDefYaml)) 385 386 importWd := &v1beta1.WorkloadDefinition{} 387 importWdJson, _ := yaml.YAMLToJSON([]byte(wDImportYaml)) 388 389 importTd := &v1beta1.TraitDefinition{} 390 391 webserverwd := &v1beta1.ComponentDefinition{} 392 webserverwdJson, _ := yaml.YAMLToJSON([]byte(webComponentDefYaml)) 393 394 BeforeEach(func() { 395 Expect(json.Unmarshal(cDDefJson, cd)).Should(BeNil()) 396 Expect(k8sClient.Create(ctx, cd.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) 397 398 Expect(json.Unmarshal(k8sObjectsCDJson, cd)).Should(BeNil()) 399 Expect(k8sClient.Create(ctx, cd.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) 400 401 Expect(json.Unmarshal(pdDefJson, pd)).Should(BeNil()) 402 Expect(k8sClient.Create(ctx, pd.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) 403 404 Expect(json.Unmarshal(importWdJson, importWd)).Should(BeNil()) 405 Expect(k8sClient.Create(ctx, importWd.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) 406 importTdJson, err := yaml.YAMLToJSON([]byte(tdImportedYaml)) 407 Expect(err).ShouldNot(HaveOccurred()) 408 Expect(json.Unmarshal(importTdJson, importTd)).Should(BeNil()) 409 Expect(k8sClient.Create(ctx, importTd.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) 410 411 _, file, _, _ := sysruntime.Caller(0) 412 for _, trait := range []string{"gateway", "storage", "env", "affinity", "scaler"} { 413 Expect(testdef.InstallDefinitionFromYAML(ctx, k8sClient, filepath.Join(file, "../../../../../../charts/vela-core/templates/defwithtemplate/", trait+".yaml"), func(s string) string { 414 return strings.ReplaceAll(s, `{{ include "systemDefinitionNamespace" . }}`, "vela-system") 415 })).Should(SatisfyAny(Succeed(), &util.AlreadyExistMatcher{})) 416 } 417 for _, def := range []string{"panic", "hubcpuscaler", "storage-pre-dispatch", "storage-pre-dispatch-unhealthy"} { 418 Expect(testdef.InstallDefinitionFromYAML(ctx, k8sClient, filepath.Join(file, "../../application/testdata/definitions/", def+".yaml"), nil)). 419 Should(SatisfyAny(Succeed(), &util.AlreadyExistMatcher{})) 420 } 421 422 Expect(json.Unmarshal(webserverwdJson, webserverwd)).Should(BeNil()) 423 Expect(k8sClient.Create(ctx, webserverwd.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) 424 425 }) 426 427 It("app step will set event", func() { 428 ns := &corev1.Namespace{ 429 ObjectMeta: metav1.ObjectMeta{ 430 Name: "vela-test-app-without-trait-event", 431 }, 432 } 433 appwithNoTrait.SetNamespace(ns.Name) 434 Expect(k8sClient.Create(ctx, ns)).Should(BeNil()) 435 Expect(k8sClient.Create(ctx, appwithNoTrait.DeepCopy())).Should(BeNil()) 436 437 appKey := client.ObjectKey{ 438 Name: appwithNoTrait.Name, 439 Namespace: appwithNoTrait.Namespace, 440 } 441 442 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 443 444 events, err := recorder.GetEventsWithName(appwithNoTrait.Name) 445 Expect(err).Should(BeNil()) 446 Expect(len(events)).ShouldNot(Equal(0)) 447 for _, event := range events { 448 Expect(event.EventType).ShouldNot(Equal(corev1.EventTypeWarning)) 449 Expect(event.EventType).Should(Equal(corev1.EventTypeNormal)) 450 } 451 452 // fail to parse application 453 appFailParse.SetNamespace(ns.Name) 454 appFailParseKey := client.ObjectKey{ 455 Name: appFailParse.Name, 456 Namespace: appFailParse.Namespace, 457 } 458 459 Expect(k8sClient.Create(ctx, appFailParse.DeepCopy())).Should(BeNil()) 460 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appFailParseKey}) 461 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appFailParseKey}) 462 463 parseEvents, err := recorder.GetEventsWithName(appFailParse.Name) 464 Expect(err).Should(BeNil()) 465 Expect(len(parseEvents)).Should(Equal(1)) 466 for _, event := range parseEvents { 467 Expect(event.EventType).Should(Equal(corev1.EventTypeWarning)) 468 Expect(event.Reason).Should(Equal(velatypes.ReasonFailedParse)) 469 } 470 471 // fail to render application 472 appFailRender.SetNamespace(ns.Name) 473 appFailRenderKey := client.ObjectKey{ 474 Name: appFailRender.Name, 475 Namespace: appFailRender.Namespace, 476 } 477 Expect(k8sClient.Create(ctx, appFailRender.DeepCopy())).Should(BeNil()) 478 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appFailRenderKey}) 479 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appFailRenderKey}) 480 481 renderEvents, err := recorder.GetEventsWithName(appFailRender.Name) 482 Expect(err).Should(BeNil()) 483 Expect(len(renderEvents)).Should(Equal(3)) 484 }) 485 486 It("app-without-trait will only create workload", func() { 487 488 ns := &corev1.Namespace{ 489 ObjectMeta: metav1.ObjectMeta{ 490 Name: "vela-test-app-without-trait", 491 }, 492 } 493 appwithNoTrait.SetNamespace(ns.Name) 494 expDeployment := getExpDeployment("myweb2", appwithNoTrait) 495 Expect(k8sClient.Create(ctx, ns)).Should(BeNil()) 496 Expect(k8sClient.Create(ctx, appwithNoTrait.DeepCopy())).Should(BeNil()) 497 498 appKey := client.ObjectKey{ 499 Name: appwithNoTrait.Name, 500 Namespace: appwithNoTrait.Namespace, 501 } 502 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 503 By("Check Application Created") 504 checkApp := &v1beta1.Application{} 505 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 506 Expect(checkApp.Status.Phase).Should(Equal(common.ApplicationRunning)) 507 508 By("Check affiliated resource tracker is created") 509 expectRTName := fmt.Sprintf("%s-%s", checkApp.Status.LatestRevision.Name, checkApp.GetNamespace()) 510 Eventually(func() error { 511 return k8sClient.Get(ctx, client.ObjectKey{Name: expectRTName}, &v1beta1.ResourceTracker{}) 512 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 513 514 By("check AppRevision created with the expected workload spec") 515 appRev := &v1beta1.ApplicationRevision{} 516 Eventually(func() error { 517 return k8sClient.Get(ctx, client.ObjectKey{Name: checkApp.Name + "-v1", Namespace: checkApp.GetNamespace()}, appRev) 518 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 519 520 af, err := appParser.GenerateAppFileFromRevision(appRev) 521 Expect(err).Should(BeNil()) 522 comps, err := af.GenerateComponentManifests() 523 Expect(err).Should(BeNil()) 524 Expect(len(comps) > 0).Should(BeTrue()) 525 comp := comps[0] 526 527 Expect(comp.ComponentOutput).ShouldNot(BeNil()) 528 gotDeploy := &v1.Deployment{} 529 Expect(runtime.DefaultUnstructuredConverter.FromUnstructured(comp.ComponentOutput.Object, gotDeploy)).Should(Succeed()) 530 gotDeploy.Annotations = nil 531 Expect(cmp.Diff(gotDeploy, expDeployment)).Should(BeEmpty()) 532 533 By("Delete Application, clean the resource") 534 Expect(k8sClient.Delete(ctx, appwithNoTrait)).Should(BeNil()) 535 }) 536 537 It("app with a component refer to an existing WorkloadDefinition", func() { 538 appRefertoWd := appwithNoTrait.DeepCopy() 539 appRefertoWd.Spec.Components[0] = common.ApplicationComponent{ 540 Name: "mytask", 541 Type: "task", 542 Properties: &runtime.RawExtension{Raw: []byte(`{"image":"busybox", "cmd":["sleep","1000"]}`)}, 543 } 544 ns := &corev1.Namespace{ 545 ObjectMeta: metav1.ObjectMeta{ 546 Name: "vela-test-app-with-workload-task", 547 }, 548 } 549 appRefertoWd.SetName("test-app-with-workload-task") 550 appRefertoWd.SetNamespace(ns.Name) 551 552 taskWd := &v1beta1.WorkloadDefinition{} 553 wDDefJson, _ := yaml.YAMLToJSON([]byte(workloadDefYaml)) 554 Expect(json.Unmarshal(wDDefJson, taskWd)).Should(BeNil()) 555 taskWd.SetNamespace(ns.Name) 556 Expect(k8sClient.Create(ctx, ns)).Should(BeNil()) 557 Expect(k8sClient.Create(ctx, taskWd)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) 558 Expect(k8sClient.Create(ctx, appRefertoWd.DeepCopy())).Should(BeNil()) 559 560 appKey := client.ObjectKey{ 561 Name: appRefertoWd.Name, 562 Namespace: appRefertoWd.Namespace, 563 } 564 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 565 By("Check Application Created with the correct revision") 566 curApp := &v1beta1.Application{} 567 Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil()) 568 Expect(curApp.Status.Phase).Should(Equal(common.ApplicationRunning)) 569 Expect(curApp.Status.LatestRevision).ShouldNot(BeNil()) 570 Expect(curApp.Status.LatestRevision.Revision).Should(BeEquivalentTo(1)) 571 572 By("Check AppRevision created as expected") 573 appRevision := &v1beta1.ApplicationRevision{} 574 Expect(k8sClient.Get(ctx, client.ObjectKey{ 575 Namespace: curApp.Namespace, 576 Name: curApp.Status.LatestRevision.Name, 577 }, appRevision)).Should(BeNil()) 578 579 By("Check affiliated resource tracker is created") 580 expectRTName := fmt.Sprintf("%s-%s", appRevision.GetName(), appRevision.GetNamespace()) 581 Eventually(func() error { 582 return k8sClient.Get(ctx, client.ObjectKey{Name: expectRTName}, &v1beta1.ResourceTracker{}) 583 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 584 }) 585 586 It("app with two components and one component refer to an existing WorkloadDefinition", func() { 587 appMix := appWithTwoComp.DeepCopy() 588 appMix.Spec.Components[1] = common.ApplicationComponent{ 589 Name: "mytask", 590 Type: "task", 591 Properties: &runtime.RawExtension{Raw: []byte(`{"image":"busybox", "cmd":["sleep","1000"]}`)}, 592 } 593 ns := &corev1.Namespace{ 594 ObjectMeta: metav1.ObjectMeta{ 595 Name: "vela-test-app-with-mix-components", 596 }, 597 } 598 appMix.SetName("test-app-with-mix-components") 599 appMix.SetNamespace(ns.Name) 600 601 taskWd := &v1beta1.WorkloadDefinition{} 602 wDDefJson, _ := yaml.YAMLToJSON([]byte(workloadDefYaml)) 603 Expect(json.Unmarshal(wDDefJson, taskWd)).Should(BeNil()) 604 taskWd.SetNamespace(ns.Name) 605 Expect(k8sClient.Create(ctx, ns)).Should(BeNil()) 606 Expect(k8sClient.Create(ctx, taskWd)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) 607 Expect(k8sClient.Create(ctx, appMix.DeepCopy())).Should(BeNil()) 608 609 appKey := client.ObjectKey{ 610 Name: appMix.Name, 611 Namespace: appMix.Namespace, 612 } 613 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 614 By("Check Application Created with the correct revision") 615 curApp := &v1beta1.Application{} 616 Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil()) 617 Expect(curApp.Status.Phase).Should(Equal(common.ApplicationRunning)) 618 Expect(curApp.Status.LatestRevision).ShouldNot(BeNil()) 619 Expect(curApp.Status.LatestRevision.Revision).Should(BeEquivalentTo(1)) 620 621 By("Check AppRevision created as expected") 622 appRevision := &v1beta1.ApplicationRevision{} 623 Expect(k8sClient.Get(ctx, client.ObjectKey{ 624 Namespace: curApp.Namespace, 625 Name: curApp.Status.LatestRevision.Name, 626 }, appRevision)).Should(BeNil()) 627 }) 628 629 It("revision should be updated if the workflow is restarted", func() { 630 631 ns := &corev1.Namespace{ 632 ObjectMeta: metav1.ObjectMeta{ 633 Name: "vela-test-app-restart-revision", 634 }, 635 } 636 Expect(k8sClient.Create(ctx, ns)).Should(BeNil()) 637 638 app := &v1beta1.Application{ 639 ObjectMeta: metav1.ObjectMeta{ 640 Name: "vela-test-app-restart-revision", 641 Namespace: "vela-test-app-restart-revision", 642 }, 643 Spec: v1beta1.ApplicationSpec{ 644 Components: []common.ApplicationComponent{}, 645 Workflow: &v1beta1.Workflow{ 646 Steps: []workflowv1alpha1.WorkflowStep{ 647 { 648 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 649 Name: "suspend", 650 Type: "suspend", 651 }, 652 }, 653 }, 654 }, 655 }, 656 } 657 658 Expect(k8sClient.Create(ctx, app.DeepCopy())).Should(BeNil()) 659 660 appKey := client.ObjectKey{ 661 Name: app.Name, 662 Namespace: app.Namespace, 663 } 664 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 665 By("Check Application Created with the correct revision") 666 curApp := &v1beta1.Application{} 667 Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil()) 668 Expect(curApp.Status.Phase).Should(Equal(common.ApplicationWorkflowSuspending)) 669 Expect(curApp.Status.LatestRevision).ShouldNot(BeNil()) 670 Expect(curApp.Status.LatestRevision.Revision).Should(BeEquivalentTo(1)) 671 672 appRevision := &v1beta1.ApplicationRevision{} 673 Expect(k8sClient.Get(ctx, client.ObjectKey{ 674 Namespace: app.Namespace, 675 Name: curApp.Status.LatestRevision.Name, 676 }, appRevision)).Should(BeNil()) 677 Expect(appRevision.Status.Workflow).Should(BeNil()) 678 679 // update the app 680 curApp.Spec.Workflow.Steps[0].DependsOn = []string{"invalid"} 681 Expect(k8sClient.Update(ctx, curApp)).Should(BeNil()) 682 Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil()) 683 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 684 685 Expect(k8sClient.Get(ctx, client.ObjectKey{ 686 Namespace: app.Namespace, 687 Name: curApp.Status.LatestRevision.Name, 688 }, appRevision)).Should(BeNil()) 689 Expect(appRevision.Status.Workflow).ShouldNot(BeNil()) 690 Expect(appRevision.Status.Workflow.Finished).Should(BeTrue()) 691 Expect(appRevision.Status.Workflow.Terminated).Should(BeTrue()) 692 Expect(appRevision.Status.Workflow.EndTime.IsZero()).ShouldNot(BeTrue()) 693 Expect(appRevision.Status.Workflow.Phase).Should(Equal(workflowv1alpha1.WorkflowStateSuspending)) 694 695 By("Delete Application, clean the resource") 696 Expect(k8sClient.Delete(ctx, app)).Should(BeNil()) 697 }) 698 699 It("revision should exist in created workload render by context.appRevision", func() { 700 701 ns := &corev1.Namespace{ 702 ObjectMeta: metav1.ObjectMeta{ 703 Name: "vela-test-app-revisionname", 704 }, 705 } 706 Expect(k8sClient.Create(ctx, ns)).Should(BeNil()) 707 708 cd := &v1beta1.ComponentDefinition{} 709 Expect(common2.ReadYamlToObject("testdata/revision/cd1.yaml", cd)).Should(BeNil()) 710 cd.SetNamespace(ns.Name) 711 Expect(k8sClient.Create(ctx, cd.DeepCopy())).Should(BeNil()) 712 713 app := &v1beta1.Application{} 714 Expect(common2.ReadYamlToObject("testdata/revision/app1.yaml", app)).Should(BeNil()) 715 app.SetNamespace(ns.Name) 716 717 expDeployment := getExpDeployment("myweb", app) 718 expDeployment.Labels["workload.oam.dev/type"] = "cd1" 719 expDeployment.Spec.Template.Spec.Containers[0].Command = nil 720 expDeployment.Spec.Template.Labels["app.oam.dev/revision"] = "revision-app1-v1" 721 722 Expect(k8sClient.Create(ctx, app.DeepCopy())).Should(BeNil()) 723 724 appKey := client.ObjectKey{ 725 Name: app.Name, 726 Namespace: app.Namespace, 727 } 728 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 729 By("Check Application Created with the correct revision") 730 curApp := &v1beta1.Application{} 731 Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil()) 732 Expect(curApp.Status.Phase).Should(Equal(common.ApplicationRunning)) 733 Expect(curApp.Status.LatestRevision).ShouldNot(BeNil()) 734 Expect(curApp.Status.LatestRevision.Revision).Should(BeEquivalentTo(1)) 735 736 appRevision := &v1beta1.ApplicationRevision{} 737 Expect(k8sClient.Get(ctx, client.ObjectKey{ 738 Namespace: app.Namespace, 739 Name: curApp.Status.LatestRevision.Name, 740 }, appRevision)).Should(BeNil()) 741 742 af, err := appParser.GenerateAppFileFromRevision(appRevision) 743 Expect(err).Should(BeNil()) 744 comps, err := af.GenerateComponentManifests() 745 Expect(err).Should(BeNil()) 746 Expect(len(comps) > 0).Should(BeTrue()) 747 comp := comps[0] 748 749 gotD := &v1.Deployment{} 750 runtime.DefaultUnstructuredConverter.FromUnstructured(comp.ComponentOutput.Object, gotD) 751 gotD.Annotations = nil 752 Expect(cmp.Diff(gotD, expDeployment)).Should(BeEmpty()) 753 754 By("Delete Application, clean the resource") 755 Expect(k8sClient.Delete(ctx, app)).Should(BeNil()) 756 }) 757 758 It("application with dag workflow failed after retries", func() { 759 defer featuregatetesting.SetFeatureGateDuringTest(&testing.T{}, utilfeature.DefaultFeatureGate, wffeatures.EnableSuspendOnFailure, true)() 760 ns := corev1.Namespace{ 761 ObjectMeta: metav1.ObjectMeta{ 762 Name: "dag-failed-after-retries", 763 }, 764 } 765 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 766 app := &v1beta1.Application{ 767 TypeMeta: metav1.TypeMeta{ 768 Kind: "Application", 769 APIVersion: "core.oam.dev/v1beta1", 770 }, 771 ObjectMeta: metav1.ObjectMeta{ 772 Name: "dag-failed-after-retries", 773 Namespace: ns.Name, 774 }, 775 Spec: v1beta1.ApplicationSpec{ 776 Components: []common.ApplicationComponent{ 777 { 778 Name: "myweb1", 779 Type: "worker", 780 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 781 }, 782 { 783 Name: "failed-step", 784 Type: "k8s-objects", 785 Properties: &runtime.RawExtension{Raw: []byte(`{"objects":[{"apiVersion":"v1","kind":"invalid","metadata":{"name":"test1"}}]}`)}, 786 }, 787 }, 788 }, 789 } 790 791 Expect(k8sClient.Create(ctx, app)).Should(BeNil()) 792 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 793 794 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 795 checkApp := &v1beta1.Application{} 796 797 By("verify the first ten reconciles") 798 for i := 0; i < wfTypes.MaxWorkflowStepErrorRetryTimes; i++ { 799 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 800 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 801 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunningWorkflow)) 802 Expect(checkApp.Status.Workflow.Message).Should(BeEquivalentTo("")) 803 Expect(checkApp.Status.Workflow.Steps[1].Phase).Should(BeEquivalentTo(workflowv1alpha1.WorkflowStepPhaseFailed)) 804 } 805 806 By("application should be suspended after failed max reconciles") 807 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 808 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 809 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationWorkflowSuspending)) 810 Expect(checkApp.Status.Workflow.Message).Should(BeEquivalentTo(wfTypes.MessageSuspendFailedAfterRetries)) 811 Expect(checkApp.Status.Workflow.Steps[1].Phase).Should(BeEquivalentTo(workflowv1alpha1.WorkflowStepPhaseFailed)) 812 Expect(checkApp.Status.Workflow.Steps[1].Reason).Should(BeEquivalentTo(wfTypes.StatusReasonFailedAfterRetries)) 813 814 By("resume the suspended application") 815 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 816 checkApp.Status.Workflow.Suspend = false 817 Expect(k8sClient.Status().Patch(ctx, checkApp, client.Merge)).Should(BeNil()) 818 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 819 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 820 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunningWorkflow)) 821 Expect(checkApp.Status.Workflow.Message).Should(BeEquivalentTo("")) 822 Expect(checkApp.Status.Workflow.Steps[1].Phase).Should(BeEquivalentTo(workflowv1alpha1.WorkflowStepPhaseFailed)) 823 824 By("test failed-after-retries with running steps") 825 compDef, err := yaml.YAMLToJSON([]byte(unhealthyComponentDefYaml)) 826 Expect(err).Should(BeNil()) 827 component := &v1beta1.ComponentDefinition{} 828 component.Spec.Extension = &runtime.RawExtension{Raw: compDef} 829 Expect(json.Unmarshal([]byte(compDef), component)).Should(BeNil()) 830 Expect(k8sClient.Create(ctx, component)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) 831 checkApp.Spec.Components[0] = common.ApplicationComponent{ 832 Name: "unhealthy-worker", 833 Type: "unhealthy-worker", 834 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 835 } 836 Expect(k8sClient.Update(ctx, checkApp)).Should(BeNil()) 837 838 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 839 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 840 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 841 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunningWorkflow)) 842 843 for i := 0; i < wfTypes.MaxWorkflowStepErrorRetryTimes-1; i++ { 844 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 845 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 846 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunningWorkflow)) 847 Expect(checkApp.Status.Workflow.Steps[0].Phase).Should(BeEquivalentTo(workflowv1alpha1.WorkflowStepPhaseRunning)) 848 Expect(checkApp.Status.Workflow.Steps[1].Phase).Should(BeEquivalentTo(workflowv1alpha1.WorkflowStepPhaseFailed)) 849 } 850 851 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 852 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 853 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunningWorkflow)) 854 Expect(checkApp.Status.Workflow.Message).Should(BeEquivalentTo("")) 855 Expect(checkApp.Status.Workflow.Steps[0].Phase).Should(BeEquivalentTo(workflowv1alpha1.WorkflowStepPhaseRunning)) 856 Expect(checkApp.Status.Workflow.Steps[1].Phase).Should(BeEquivalentTo(workflowv1alpha1.WorkflowStepPhaseFailed)) 857 Expect(checkApp.Status.Workflow.Steps[1].Reason).Should(BeEquivalentTo(wfTypes.StatusReasonFailedAfterRetries)) 858 }) 859 860 It("application with step by step workflow failed after retries", func() { 861 defer featuregatetesting.SetFeatureGateDuringTest(&testing.T{}, utilfeature.DefaultFeatureGate, wffeatures.EnableSuspendOnFailure, true)() 862 ns := corev1.Namespace{ 863 ObjectMeta: metav1.ObjectMeta{ 864 Name: "step-by-step-failed-after-retries", 865 }, 866 } 867 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 868 app := &v1beta1.Application{ 869 TypeMeta: metav1.TypeMeta{ 870 Kind: "Application", 871 APIVersion: "core.oam.dev/v1beta1", 872 }, 873 ObjectMeta: metav1.ObjectMeta{ 874 Name: "step-by-step-failed-after-retries", 875 Namespace: ns.Name, 876 }, 877 Spec: v1beta1.ApplicationSpec{ 878 Components: []common.ApplicationComponent{ 879 { 880 Name: "myweb1", 881 Type: "worker", 882 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 883 }, 884 { 885 Name: "failed-step", 886 Type: "k8s-objects", 887 Properties: &runtime.RawExtension{Raw: []byte(`{"objects":[{"apiVersion":"v1","kind":"invalid","metadata":{"name":"test1"}}]}`)}, 888 }, 889 }, 890 Workflow: &v1beta1.Workflow{ 891 Steps: []workflowv1alpha1.WorkflowStep{ 892 { 893 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 894 Name: "myweb1", 895 Type: "apply-component", 896 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1"}`)}, 897 }, 898 }, 899 { 900 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 901 Name: "failed-step", 902 Type: "apply-component", 903 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"failed-step"}`)}, 904 }, 905 }, 906 }, 907 }, 908 }, 909 } 910 911 Expect(k8sClient.Create(ctx, app)).Should(BeNil()) 912 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 913 914 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 915 checkApp := &v1beta1.Application{} 916 917 By("verify the first twenty reconciles") 918 for i := 0; i < wfTypes.MaxWorkflowStepErrorRetryTimes; i++ { 919 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 920 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 921 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunningWorkflow)) 922 Expect(checkApp.Status.Workflow.Message).Should(BeEquivalentTo("")) 923 Expect(checkApp.Status.Workflow.Steps[1].Phase).Should(BeEquivalentTo(workflowv1alpha1.WorkflowStepPhaseFailed)) 924 } 925 926 By("application should be suspended after failed max reconciles") 927 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 928 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 929 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationWorkflowSuspending)) 930 Expect(checkApp.Status.Workflow.Message).Should(BeEquivalentTo(wfTypes.MessageSuspendFailedAfterRetries)) 931 Expect(checkApp.Status.Workflow.Steps[1].Phase).Should(BeEquivalentTo(workflowv1alpha1.WorkflowStepPhaseFailed)) 932 Expect(checkApp.Status.Workflow.Steps[1].Reason).Should(BeEquivalentTo(wfTypes.StatusReasonFailedAfterRetries)) 933 934 By("resume the suspended application") 935 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 936 checkApp.Status.Workflow.Suspend = false 937 Expect(k8sClient.Status().Patch(ctx, checkApp, client.Merge)).Should(BeNil()) 938 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 939 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 940 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunningWorkflow)) 941 Expect(checkApp.Status.Workflow.Message).Should(BeEquivalentTo("")) 942 Expect(checkApp.Status.Workflow.Steps[1].Phase).Should(BeEquivalentTo(workflowv1alpha1.WorkflowStepPhaseFailed)) 943 }) 944 945 It("application with mode in workflows", func() { 946 ns := corev1.Namespace{ 947 ObjectMeta: metav1.ObjectMeta{ 948 Name: "app-with-mode", 949 }, 950 } 951 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 952 healthComponentDef := &v1beta1.ComponentDefinition{} 953 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 954 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 955 healthComponentDef.Name = "worker-with-health" 956 healthComponentDef.Namespace = "app-with-mode" 957 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 958 app := &v1beta1.Application{ 959 TypeMeta: metav1.TypeMeta{ 960 Kind: "Application", 961 APIVersion: "core.oam.dev/v1beta1", 962 }, 963 ObjectMeta: metav1.ObjectMeta{ 964 Name: "app-with-mode", 965 Namespace: "app-with-mode", 966 }, 967 Spec: v1beta1.ApplicationSpec{ 968 Components: []common.ApplicationComponent{ 969 { 970 Name: "myweb1", 971 Type: "worker-with-health", 972 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 973 }, 974 { 975 Name: "myweb3", 976 Type: "worker", 977 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 978 }, 979 { 980 Name: "myweb2", 981 Type: "worker-with-health", 982 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 983 }, 984 }, 985 Workflow: &v1beta1.Workflow{ 986 Mode: &workflowv1alpha1.WorkflowExecuteMode{ 987 Steps: workflowv1alpha1.WorkflowModeDAG, 988 SubSteps: workflowv1alpha1.WorkflowModeStep, 989 }, 990 Steps: []workflowv1alpha1.WorkflowStep{ 991 { 992 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 993 Name: "myweb1", 994 Type: "apply-component", 995 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1"}`)}, 996 }, 997 }, 998 { 999 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1000 Name: "myweb2", 1001 Type: "step-group", 1002 }, 1003 SubSteps: []workflowv1alpha1.WorkflowStepBase{ 1004 { 1005 Name: "myweb2-sub1", 1006 Type: "apply-component", 1007 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb2"}`)}, 1008 }, 1009 { 1010 Name: "myweb2-sub2", 1011 Type: "apply-component", 1012 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb3"}`)}, 1013 }, 1014 }, 1015 }, 1016 }, 1017 }, 1018 }, 1019 } 1020 1021 Expect(k8sClient.Create(context.Background(), app)).Should(BeNil()) 1022 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 1023 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 1024 1025 expDeployment := &v1.Deployment{} 1026 web3Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb3"} 1027 Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(util.NotFoundMatcher{}) 1028 1029 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 1030 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil()) 1031 expDeployment.Status.Replicas = 1 1032 expDeployment.Status.ReadyReplicas = 1 1033 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 1034 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 1035 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(BeNil()) 1036 expDeployment.Status.Replicas = 1 1037 expDeployment.Status.ReadyReplicas = 1 1038 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 1039 1040 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1041 1042 Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(BeNil()) 1043 1044 checkApp := &v1beta1.Application{} 1045 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 1046 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunning)) 1047 Expect(checkApp.Status.Workflow.Mode).Should(BeEquivalentTo(fmt.Sprintf("%s-%s", workflowv1alpha1.WorkflowModeDAG, workflowv1alpha1.WorkflowModeStep))) 1048 }) 1049 1050 It("application with mode in workflow step group", func() { 1051 ns := corev1.Namespace{ 1052 ObjectMeta: metav1.ObjectMeta{ 1053 Name: "app-with-group-mode", 1054 }, 1055 } 1056 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 1057 healthComponentDef := &v1beta1.ComponentDefinition{} 1058 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 1059 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 1060 healthComponentDef.Name = "worker-with-health" 1061 healthComponentDef.Namespace = "app-with-group-mode" 1062 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 1063 app := &v1beta1.Application{ 1064 TypeMeta: metav1.TypeMeta{ 1065 Kind: "Application", 1066 APIVersion: "core.oam.dev/v1beta1", 1067 }, 1068 ObjectMeta: metav1.ObjectMeta{ 1069 Name: "app-with-group-mode", 1070 Namespace: "app-with-group-mode", 1071 }, 1072 Spec: v1beta1.ApplicationSpec{ 1073 Components: []common.ApplicationComponent{ 1074 { 1075 Name: "myweb1", 1076 Type: "worker-with-health", 1077 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 1078 }, 1079 { 1080 Name: "myweb3", 1081 Type: "worker", 1082 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 1083 }, 1084 { 1085 Name: "myweb2", 1086 Type: "worker-with-health", 1087 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 1088 }, 1089 }, 1090 Workflow: &v1beta1.Workflow{ 1091 Mode: &workflowv1alpha1.WorkflowExecuteMode{ 1092 Steps: workflowv1alpha1.WorkflowModeDAG, 1093 }, 1094 Steps: []workflowv1alpha1.WorkflowStep{ 1095 { 1096 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1097 Name: "myweb1", 1098 Type: "apply-component", 1099 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1"}`)}, 1100 }, 1101 }, 1102 { 1103 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1104 Name: "myweb2", 1105 Type: "step-group", 1106 }, 1107 Mode: workflowv1alpha1.WorkflowModeStep, 1108 SubSteps: []workflowv1alpha1.WorkflowStepBase{ 1109 { 1110 Name: "myweb2-sub1", 1111 Type: "apply-component", 1112 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb2"}`)}, 1113 }, 1114 { 1115 Name: "myweb2-sub2", 1116 Type: "apply-component", 1117 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb3"}`)}, 1118 }, 1119 }, 1120 }, 1121 }, 1122 }, 1123 }, 1124 } 1125 1126 Expect(k8sClient.Create(context.Background(), app)).Should(BeNil()) 1127 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 1128 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 1129 1130 expDeployment := &v1.Deployment{} 1131 web3Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb3"} 1132 Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(util.NotFoundMatcher{}) 1133 1134 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 1135 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil()) 1136 expDeployment.Status.Replicas = 1 1137 expDeployment.Status.ReadyReplicas = 1 1138 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 1139 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 1140 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(BeNil()) 1141 expDeployment.Status.Replicas = 1 1142 expDeployment.Status.ReadyReplicas = 1 1143 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 1144 1145 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1146 1147 Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(BeNil()) 1148 1149 checkApp := &v1beta1.Application{} 1150 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 1151 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunning)) 1152 Expect(checkApp.Status.Workflow.Mode).Should(BeEquivalentTo(fmt.Sprintf("%s-%s", workflowv1alpha1.WorkflowModeDAG, workflowv1alpha1.WorkflowModeDAG))) 1153 }) 1154 1155 It("application with sub steps", func() { 1156 ns := corev1.Namespace{ 1157 ObjectMeta: metav1.ObjectMeta{ 1158 Name: "app-with-sub-steps", 1159 }, 1160 } 1161 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 1162 healthComponentDef := &v1beta1.ComponentDefinition{} 1163 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 1164 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 1165 healthComponentDef.Name = "worker-with-health" 1166 healthComponentDef.Namespace = "app-with-sub-steps" 1167 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 1168 app := &v1beta1.Application{ 1169 TypeMeta: metav1.TypeMeta{ 1170 Kind: "Application", 1171 APIVersion: "core.oam.dev/v1beta1", 1172 }, 1173 ObjectMeta: metav1.ObjectMeta{ 1174 Name: "app-with-sub-steps", 1175 Namespace: "app-with-sub-steps", 1176 }, 1177 Spec: v1beta1.ApplicationSpec{ 1178 Components: []common.ApplicationComponent{ 1179 { 1180 Name: "myweb1", 1181 Type: "worker-with-health", 1182 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 1183 }, 1184 { 1185 Name: "myweb2", 1186 Type: "worker", 1187 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 1188 }, 1189 { 1190 Name: "myweb3", 1191 Type: "worker-with-health", 1192 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 1193 }, 1194 }, 1195 Workflow: &v1beta1.Workflow{ 1196 Steps: []workflowv1alpha1.WorkflowStep{ 1197 { 1198 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1199 Name: "myweb1", 1200 Type: "apply-component", 1201 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1"}`)}, 1202 }, 1203 }, 1204 { 1205 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1206 Name: "myweb2", 1207 Type: "step-group", 1208 }, 1209 SubSteps: []workflowv1alpha1.WorkflowStepBase{ 1210 { 1211 Name: "myweb2-sub1", 1212 Type: "apply-component", 1213 DependsOn: []string{"myweb2-sub2"}, 1214 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb2"}`)}, 1215 }, 1216 { 1217 Name: "myweb2-sub2", 1218 Type: "apply-component", 1219 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb3"}`)}, 1220 }, 1221 }, 1222 }, 1223 }, 1224 }, 1225 }, 1226 } 1227 1228 Expect(k8sClient.Create(context.Background(), app)).Should(BeNil()) 1229 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 1230 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 1231 1232 expDeployment := &v1.Deployment{} 1233 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 1234 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 1235 web3Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb3"} 1236 Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(util.NotFoundMatcher{}) 1237 1238 checkApp := &v1beta1.Application{} 1239 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 1240 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 1241 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil()) 1242 1243 expDeployment.Status.Replicas = 1 1244 expDeployment.Status.ReadyReplicas = 1 1245 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 1246 1247 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1248 1249 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 1250 Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(BeNil()) 1251 expDeployment.Status.Replicas = 1 1252 expDeployment.Status.ReadyReplicas = 1 1253 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 1254 1255 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1256 1257 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(BeNil()) 1258 expDeployment.Status.Replicas = 1 1259 expDeployment.Status.ReadyReplicas = 1 1260 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 1261 1262 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1263 checkApp = &v1beta1.Application{} 1264 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 1265 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunning)) 1266 }) 1267 1268 It("application with array inputs", func() { 1269 ns := corev1.Namespace{ 1270 ObjectMeta: metav1.ObjectMeta{ 1271 Name: "app-with-array-inputs", 1272 }, 1273 } 1274 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 1275 healthComponentDef := &v1beta1.ComponentDefinition{} 1276 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 1277 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 1278 healthComponentDef.Name = "worker-with-health" 1279 healthComponentDef.Namespace = "app-with-array-inputs" 1280 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 1281 app := &v1beta1.Application{ 1282 TypeMeta: metav1.TypeMeta{ 1283 Kind: "Application", 1284 APIVersion: "core.oam.dev/v1beta1", 1285 }, 1286 ObjectMeta: metav1.ObjectMeta{ 1287 Name: "app-with-array-inputs", 1288 Namespace: "app-with-array-inputs", 1289 }, 1290 Spec: v1beta1.ApplicationSpec{ 1291 Components: []common.ApplicationComponent{ 1292 { 1293 Name: "myweb1", 1294 Type: "worker-with-health", 1295 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 1296 Outputs: workflowv1alpha1.StepOutputs{ 1297 { 1298 Name: "output", 1299 ValueFrom: "context.name", 1300 }, 1301 }, 1302 }, 1303 { 1304 Name: "myweb2", 1305 Type: "worker", 1306 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep"],"image":"busybox"}`)}, 1307 Inputs: workflowv1alpha1.StepInputs{ 1308 { 1309 From: "output", 1310 ParameterKey: "cmd[1]", 1311 }, 1312 }, 1313 }, 1314 }, 1315 }, 1316 } 1317 1318 Expect(k8sClient.Create(context.Background(), app)).Should(BeNil()) 1319 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 1320 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1321 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1322 1323 expDeployment := &v1.Deployment{} 1324 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 1325 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil()) 1326 expDeployment.Status.Replicas = 1 1327 expDeployment.Status.ReadyReplicas = 1 1328 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 1329 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 1330 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 1331 1332 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1333 1334 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(BeNil()) 1335 1336 checkApp := &v1beta1.Application{} 1337 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 1338 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunning)) 1339 }) 1340 1341 It("application with timeout outputs in workflow", func() { 1342 ns := corev1.Namespace{ 1343 ObjectMeta: metav1.ObjectMeta{ 1344 Name: "app-with-timeout-output", 1345 }, 1346 } 1347 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 1348 healthComponentDef := &v1beta1.ComponentDefinition{} 1349 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 1350 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 1351 healthComponentDef.Name = "worker-with-health" 1352 healthComponentDef.Namespace = "app-with-timeout-output" 1353 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 1354 app := &v1beta1.Application{ 1355 TypeMeta: metav1.TypeMeta{ 1356 Kind: "Application", 1357 APIVersion: "core.oam.dev/v1beta1", 1358 }, 1359 ObjectMeta: metav1.ObjectMeta{ 1360 Name: "app-with-timeout-output", 1361 Namespace: "app-with-timeout-output", 1362 }, 1363 Spec: v1beta1.ApplicationSpec{ 1364 Components: []common.ApplicationComponent{ 1365 { 1366 Name: "myweb1", 1367 Type: "worker-with-health", 1368 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 1369 }, 1370 { 1371 Name: "myweb2", 1372 Type: "worker", 1373 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 1374 }, 1375 }, 1376 Workflow: &v1beta1.Workflow{ 1377 Steps: []workflowv1alpha1.WorkflowStep{ 1378 { 1379 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1380 Name: "myweb1", 1381 Type: "apply-component", 1382 Timeout: "1s", 1383 Outputs: workflowv1alpha1.StepOutputs{ 1384 { 1385 Name: "output", 1386 ValueFrom: "context.name", 1387 }, 1388 }, 1389 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1"}`)}, 1390 }, 1391 }, 1392 { 1393 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1394 Name: "myweb2", 1395 Inputs: workflowv1alpha1.StepInputs{ 1396 { 1397 From: "output", 1398 ParameterKey: "", 1399 }, 1400 }, 1401 If: `inputs.output == "app-with-timeout-output"`, 1402 Type: "apply-component", 1403 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb2"}`)}, 1404 }, 1405 }, 1406 }, 1407 }, 1408 }, 1409 } 1410 1411 Expect(k8sClient.Create(context.Background(), app)).Should(BeNil()) 1412 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 1413 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1414 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1415 1416 expDeployment := &v1.Deployment{} 1417 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 1418 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil()) 1419 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 1420 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 1421 1422 time.Sleep(time.Second) 1423 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1424 1425 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(BeNil()) 1426 1427 checkApp := &v1beta1.Application{} 1428 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 1429 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationWorkflowFailed)) 1430 }) 1431 1432 It("application with skip outputs in workflow", func() { 1433 ns := corev1.Namespace{ 1434 ObjectMeta: metav1.ObjectMeta{ 1435 Name: "app-with-skip-output", 1436 }, 1437 } 1438 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 1439 healthComponentDef := &v1beta1.ComponentDefinition{} 1440 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 1441 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 1442 healthComponentDef.Name = "worker-with-health" 1443 healthComponentDef.Namespace = "app-with-skip-output" 1444 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 1445 app := &v1beta1.Application{ 1446 TypeMeta: metav1.TypeMeta{ 1447 Kind: "Application", 1448 APIVersion: "core.oam.dev/v1beta1", 1449 }, 1450 ObjectMeta: metav1.ObjectMeta{ 1451 Name: "app-with-skip-output", 1452 Namespace: "app-with-skip-output", 1453 }, 1454 Spec: v1beta1.ApplicationSpec{ 1455 Components: []common.ApplicationComponent{ 1456 { 1457 Name: "myweb1", 1458 Type: "worker-with-health", 1459 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 1460 }, 1461 { 1462 Name: "myweb2", 1463 Type: "worker", 1464 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 1465 }, 1466 }, 1467 Workflow: &v1beta1.Workflow{ 1468 Steps: []workflowv1alpha1.WorkflowStep{ 1469 { 1470 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1471 Name: "myweb1", 1472 Type: "apply-component", 1473 If: "false", 1474 Outputs: workflowv1alpha1.StepOutputs{ 1475 { 1476 Name: "output", 1477 ValueFrom: "context.name", 1478 }, 1479 }, 1480 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1"}`)}, 1481 }, 1482 }, 1483 { 1484 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1485 Name: "myweb2", 1486 Inputs: workflowv1alpha1.StepInputs{ 1487 { 1488 From: "output", 1489 ParameterKey: "", 1490 }, 1491 }, 1492 If: `inputs.output == "app-with-timeout-output"`, 1493 Type: "apply-component", 1494 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb2"}`)}, 1495 }, 1496 }, 1497 }, 1498 }, 1499 }, 1500 } 1501 1502 Expect(k8sClient.Create(context.Background(), app)).Should(BeNil()) 1503 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 1504 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1505 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1506 1507 expDeployment := &v1.Deployment{} 1508 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 1509 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(util.NotFoundMatcher{}) 1510 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 1511 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 1512 1513 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1514 1515 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 1516 1517 checkApp := &v1beta1.Application{} 1518 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 1519 Expect(checkApp.Status.Workflow.Steps[0].Phase).Should(BeEquivalentTo(workflowv1alpha1.WorkflowStepPhaseSkipped)) 1520 Expect(checkApp.Status.Workflow.Steps[1].Phase).Should(BeEquivalentTo(workflowv1alpha1.WorkflowStepPhaseSkipped)) 1521 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunning)) 1522 }) 1523 1524 It("application with invalid inputs in workflow", func() { 1525 ns := corev1.Namespace{ 1526 ObjectMeta: metav1.ObjectMeta{ 1527 Name: "app-with-invalid-input", 1528 }, 1529 } 1530 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 1531 healthComponentDef := &v1beta1.ComponentDefinition{} 1532 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 1533 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 1534 healthComponentDef.Name = "worker-with-health" 1535 healthComponentDef.Namespace = "app-with-invalid-input" 1536 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 1537 app := &v1beta1.Application{ 1538 TypeMeta: metav1.TypeMeta{ 1539 Kind: "Application", 1540 APIVersion: "core.oam.dev/v1beta1", 1541 }, 1542 ObjectMeta: metav1.ObjectMeta{ 1543 Name: "app-with-invalid-input", 1544 Namespace: "app-with-invalid-input", 1545 }, 1546 Spec: v1beta1.ApplicationSpec{ 1547 Components: []common.ApplicationComponent{ 1548 { 1549 Name: "myweb1", 1550 Type: "worker-with-health", 1551 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 1552 }, 1553 { 1554 Name: "myweb2", 1555 Type: "worker", 1556 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 1557 }, 1558 }, 1559 Workflow: &v1beta1.Workflow{ 1560 Steps: []workflowv1alpha1.WorkflowStep{ 1561 { 1562 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1563 Name: "myweb1", 1564 Type: "apply-component", 1565 Outputs: workflowv1alpha1.StepOutputs{ 1566 { 1567 Name: "output", 1568 ValueFrom: "context.name", 1569 }, 1570 }, 1571 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1"}`)}, 1572 }, 1573 }, 1574 { 1575 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1576 Name: "myweb2", 1577 Inputs: workflowv1alpha1.StepInputs{ 1578 { 1579 From: "invalid", 1580 ParameterKey: "", 1581 }, 1582 }, 1583 Type: "apply-component", 1584 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb2"}`)}, 1585 }, 1586 }, 1587 }, 1588 }, 1589 }, 1590 } 1591 1592 Expect(k8sClient.Create(context.Background(), app)).Should(BeNil()) 1593 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 1594 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1595 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1596 1597 expDeployment := &v1.Deployment{} 1598 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 1599 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil()) 1600 expDeployment.Status.Replicas = 1 1601 expDeployment.Status.ReadyReplicas = 1 1602 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 1603 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 1604 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 1605 1606 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1607 1608 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 1609 1610 checkApp := &v1beta1.Application{} 1611 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 1612 Expect(checkApp.Status.Workflow.Steps[0].Phase).Should(BeEquivalentTo(workflowv1alpha1.WorkflowStepPhaseSucceeded)) 1613 Expect(checkApp.Status.Workflow.Steps[1].Phase).Should(BeEquivalentTo(workflowv1alpha1.WorkflowStepPhasePending)) 1614 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunningWorkflow)) 1615 }) 1616 1617 It("application with error inputs in workflow", func() { 1618 ns := corev1.Namespace{ 1619 ObjectMeta: metav1.ObjectMeta{ 1620 Name: "app-with-error-input", 1621 }, 1622 } 1623 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 1624 healthComponentDef := &v1beta1.ComponentDefinition{} 1625 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 1626 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 1627 healthComponentDef.Name = "worker-with-health" 1628 healthComponentDef.Namespace = "app-with-error-input" 1629 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 1630 app := &v1beta1.Application{ 1631 TypeMeta: metav1.TypeMeta{ 1632 Kind: "Application", 1633 APIVersion: "core.oam.dev/v1beta1", 1634 }, 1635 ObjectMeta: metav1.ObjectMeta{ 1636 Name: "app-with-error-input", 1637 Namespace: "app-with-error-input", 1638 }, 1639 Spec: v1beta1.ApplicationSpec{ 1640 Components: []common.ApplicationComponent{ 1641 { 1642 Name: "myweb1", 1643 Type: "worker-with-health", 1644 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 1645 }, 1646 { 1647 Name: "myweb2", 1648 Type: "worker", 1649 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 1650 }, 1651 }, 1652 Workflow: &v1beta1.Workflow{ 1653 Steps: []workflowv1alpha1.WorkflowStep{ 1654 { 1655 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1656 Name: "myweb1", 1657 Type: "apply-component", 1658 Outputs: workflowv1alpha1.StepOutputs{ 1659 { 1660 Name: "output", 1661 ValueFrom: "context.namespace", 1662 }, 1663 }, 1664 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1"}`)}, 1665 }, 1666 }, 1667 { 1668 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1669 Name: "myweb2", 1670 Inputs: workflowv1alpha1.StepInputs{ 1671 { 1672 From: "output", 1673 ParameterKey: "cmd", 1674 }, 1675 }, 1676 Type: "apply-component", 1677 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb2"}`)}, 1678 }, 1679 }, 1680 }, 1681 }, 1682 }, 1683 } 1684 1685 Expect(k8sClient.Create(context.Background(), app)).Should(BeNil()) 1686 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 1687 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1688 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1689 1690 expDeployment := &v1.Deployment{} 1691 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 1692 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil()) 1693 expDeployment.Status.Replicas = 1 1694 expDeployment.Status.ReadyReplicas = 1 1695 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 1696 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 1697 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 1698 1699 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1700 1701 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 1702 1703 checkApp := &v1beta1.Application{} 1704 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 1705 Expect(checkApp.Status.Workflow.Steps[0].Phase).Should(BeEquivalentTo(workflowv1alpha1.WorkflowStepPhaseSucceeded)) 1706 for i := 0; i < wfTypes.MaxWorkflowStepErrorRetryTimes-1; i++ { 1707 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1708 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 1709 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunningWorkflow)) 1710 Expect(checkApp.Status.Workflow.Message).Should(BeEquivalentTo("")) 1711 Expect(checkApp.Status.Workflow.Steps[1].Phase).Should(BeEquivalentTo(workflowv1alpha1.WorkflowStepPhaseFailed)) 1712 } 1713 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1714 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 1715 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationWorkflowFailed)) 1716 }) 1717 1718 It("application with invalid inputs in workflow in dag mode", func() { 1719 ns := corev1.Namespace{ 1720 ObjectMeta: metav1.ObjectMeta{ 1721 Name: "app-with-invalid-input-dag", 1722 }, 1723 } 1724 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 1725 healthComponentDef := &v1beta1.ComponentDefinition{} 1726 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 1727 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 1728 healthComponentDef.Name = "worker-with-health" 1729 healthComponentDef.Namespace = "app-with-invalid-input-dag" 1730 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 1731 app := &v1beta1.Application{ 1732 TypeMeta: metav1.TypeMeta{ 1733 Kind: "Application", 1734 APIVersion: "core.oam.dev/v1beta1", 1735 }, 1736 ObjectMeta: metav1.ObjectMeta{ 1737 Name: "app-with-invalid-input-dag", 1738 Namespace: "app-with-invalid-input-dag", 1739 }, 1740 Spec: v1beta1.ApplicationSpec{ 1741 Components: []common.ApplicationComponent{ 1742 { 1743 Name: "myweb1", 1744 Type: "worker-with-health", 1745 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 1746 }, 1747 { 1748 Name: "myweb2", 1749 Type: "worker", 1750 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 1751 }, 1752 }, 1753 Workflow: &v1beta1.Workflow{ 1754 Mode: &workflowv1alpha1.WorkflowExecuteMode{ 1755 Steps: workflowv1alpha1.WorkflowModeDAG, 1756 }, 1757 Steps: []workflowv1alpha1.WorkflowStep{ 1758 { 1759 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1760 Name: "myweb1", 1761 Type: "apply-component", 1762 Outputs: workflowv1alpha1.StepOutputs{ 1763 { 1764 Name: "output", 1765 ValueFrom: "context.name", 1766 }, 1767 }, 1768 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1"}`)}, 1769 }, 1770 }, 1771 { 1772 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1773 Name: "myweb2", 1774 Inputs: workflowv1alpha1.StepInputs{ 1775 { 1776 From: "invalid", 1777 ParameterKey: "", 1778 }, 1779 }, 1780 Type: "apply-component", 1781 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb2"}`)}, 1782 }, 1783 }, 1784 }, 1785 }, 1786 }, 1787 } 1788 1789 Expect(k8sClient.Create(context.Background(), app)).Should(BeNil()) 1790 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 1791 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1792 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1793 1794 expDeployment := &v1.Deployment{} 1795 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 1796 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil()) 1797 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 1798 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 1799 1800 checkApp := &v1beta1.Application{} 1801 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 1802 Expect(checkApp.Status.Workflow.Steps[0].Phase).Should(BeEquivalentTo(workflowv1alpha1.WorkflowStepPhaseRunning)) 1803 Expect(checkApp.Status.Workflow.Steps[1].Phase).Should(BeEquivalentTo(workflowv1alpha1.WorkflowStepPhasePending)) 1804 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunningWorkflow)) 1805 }) 1806 1807 It("application with if always in workflow", func() { 1808 ns := corev1.Namespace{ 1809 ObjectMeta: metav1.ObjectMeta{ 1810 Name: "app-with-if-always-workflow", 1811 }, 1812 } 1813 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 1814 healthComponentDef := &v1beta1.ComponentDefinition{} 1815 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 1816 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 1817 healthComponentDef.Name = "worker-with-health" 1818 healthComponentDef.Namespace = "app-with-if-always-workflow" 1819 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 1820 app := &v1beta1.Application{ 1821 TypeMeta: metav1.TypeMeta{ 1822 Kind: "Application", 1823 APIVersion: "core.oam.dev/v1beta1", 1824 }, 1825 ObjectMeta: metav1.ObjectMeta{ 1826 Name: "app-with-if-always-workflow", 1827 Namespace: "app-with-if-always-workflow", 1828 }, 1829 Spec: v1beta1.ApplicationSpec{ 1830 Components: []common.ApplicationComponent{ 1831 { 1832 Name: "myweb1", 1833 Type: "worker-with-health", 1834 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 1835 }, 1836 { 1837 Name: "myweb2", 1838 Type: "worker", 1839 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 1840 }, 1841 { 1842 Name: "failed-step", 1843 Type: "k8s-objects", 1844 Properties: &runtime.RawExtension{Raw: []byte(`{"objects":[{"apiVersion":"v1","kind":"invalid","metadata":{"name":"test1"}}]}`)}, 1845 }, 1846 }, 1847 Workflow: &v1beta1.Workflow{ 1848 Steps: []workflowv1alpha1.WorkflowStep{ 1849 { 1850 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1851 Name: "failed-step", 1852 Type: "apply-component", 1853 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"failed-step"}`)}, 1854 }, 1855 }, 1856 { 1857 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1858 Name: "myweb1", 1859 Type: "apply-component", 1860 If: "always", 1861 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1"}`)}, 1862 }, 1863 }, 1864 { 1865 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1866 Name: "myweb2", 1867 Type: "apply-component", 1868 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb2"}`)}, 1869 }, 1870 }, 1871 }, 1872 }, 1873 }, 1874 } 1875 1876 Expect(k8sClient.Create(context.Background(), app)).Should(BeNil()) 1877 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 1878 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1879 1880 By("verify the first ten reconciles") 1881 for i := 0; i < wfTypes.MaxWorkflowStepErrorRetryTimes; i++ { 1882 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1883 } 1884 1885 expDeployment := &v1.Deployment{} 1886 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 1887 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(util.NotFoundMatcher{}) 1888 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 1889 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 1890 1891 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1892 1893 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil()) 1894 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 1895 1896 expDeployment.Status.Replicas = 1 1897 expDeployment.Status.ReadyReplicas = 1 1898 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 1899 1900 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 1901 1902 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 1903 1904 checkApp := &v1beta1.Application{} 1905 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 1906 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationWorkflowFailed)) 1907 }) 1908 1909 It("application with if always in workflow sub steps", func() { 1910 ns := corev1.Namespace{ 1911 ObjectMeta: metav1.ObjectMeta{ 1912 Name: "app-with-if-always-workflow-sub-steps", 1913 }, 1914 } 1915 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 1916 healthComponentDef := &v1beta1.ComponentDefinition{} 1917 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 1918 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 1919 healthComponentDef.Name = "worker-with-health" 1920 healthComponentDef.Namespace = "app-with-if-always-workflow-sub-steps" 1921 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 1922 app := &v1beta1.Application{ 1923 TypeMeta: metav1.TypeMeta{ 1924 Kind: "Application", 1925 APIVersion: "core.oam.dev/v1beta1", 1926 }, 1927 ObjectMeta: metav1.ObjectMeta{ 1928 Name: "app-with-if-always-workflow-sub-steps", 1929 Namespace: "app-with-if-always-workflow-sub-steps", 1930 }, 1931 Spec: v1beta1.ApplicationSpec{ 1932 Components: []common.ApplicationComponent{ 1933 { 1934 Name: "myweb1", 1935 Type: "worker-with-health", 1936 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 1937 }, 1938 { 1939 Name: "myweb2", 1940 Type: "worker-with-health", 1941 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 1942 }, 1943 { 1944 Name: "myweb3", 1945 Type: "worker", 1946 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 1947 }, 1948 { 1949 Name: "failed-step", 1950 Type: "k8s-objects", 1951 Properties: &runtime.RawExtension{Raw: []byte(`{"objects":[{"apiVersion":"v1","kind":"invalid","metadata":{"name":"test1"}}]}`)}, 1952 }, 1953 }, 1954 Workflow: &v1beta1.Workflow{ 1955 Steps: []workflowv1alpha1.WorkflowStep{ 1956 { 1957 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1958 Name: "myweb1", 1959 Type: "step-group", 1960 }, 1961 SubSteps: []workflowv1alpha1.WorkflowStepBase{ 1962 { 1963 Name: "myweb1-sub1", 1964 Type: "apply-component", 1965 If: "always", 1966 DependsOn: []string{"myweb1-sub2"}, 1967 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1"}`)}, 1968 }, 1969 { 1970 Name: "myweb1-sub2", 1971 Type: "apply-component", 1972 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"failed-step"}`)}, 1973 }, 1974 { 1975 Name: "myweb1-sub3", 1976 Type: "apply-component", 1977 DependsOn: []string{"myweb1-sub1"}, 1978 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb2"}`)}, 1979 }, 1980 }, 1981 }, 1982 { 1983 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1984 Name: "myweb2", 1985 Type: "apply-component", 1986 If: "always", 1987 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb2"}`)}, 1988 }, 1989 }, 1990 { 1991 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 1992 Name: "myweb3", 1993 Type: "apply-component", 1994 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb3"}`)}, 1995 }, 1996 }, 1997 }, 1998 }, 1999 }, 2000 } 2001 2002 Expect(k8sClient.Create(context.Background(), app)).Should(BeNil()) 2003 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 2004 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2005 2006 By("verify the first ten reconciles") 2007 for i := 0; i < wfTypes.MaxWorkflowStepErrorRetryTimes; i++ { 2008 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2009 } 2010 2011 expDeployment := &v1.Deployment{} 2012 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 2013 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(util.NotFoundMatcher{}) 2014 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 2015 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 2016 web3Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb3"} 2017 Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(util.NotFoundMatcher{}) 2018 2019 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2020 2021 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil()) 2022 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 2023 Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(util.NotFoundMatcher{}) 2024 2025 expDeployment.Status.Replicas = 1 2026 expDeployment.Status.ReadyReplicas = 1 2027 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 2028 2029 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2030 2031 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(BeNil()) 2032 Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(util.NotFoundMatcher{}) 2033 expDeployment.Status.Replicas = 1 2034 expDeployment.Status.ReadyReplicas = 1 2035 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 2036 2037 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2038 2039 checkApp := &v1beta1.Application{} 2040 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 2041 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationWorkflowFailed)) 2042 }) 2043 2044 It("application with if expressions in workflow", func() { 2045 ns := corev1.Namespace{ 2046 ObjectMeta: metav1.ObjectMeta{ 2047 Name: "app-with-if-expressions", 2048 }, 2049 } 2050 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 2051 healthComponentDef := &v1beta1.ComponentDefinition{} 2052 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 2053 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 2054 healthComponentDef.Name = "worker-with-health" 2055 healthComponentDef.Namespace = "app-with-if-expressions" 2056 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 2057 app := &v1beta1.Application{ 2058 TypeMeta: metav1.TypeMeta{ 2059 Kind: "Application", 2060 APIVersion: "core.oam.dev/v1beta1", 2061 }, 2062 ObjectMeta: metav1.ObjectMeta{ 2063 Name: "app-with-if-expressions", 2064 Namespace: "app-with-if-expressions", 2065 }, 2066 Spec: v1beta1.ApplicationSpec{ 2067 Components: []common.ApplicationComponent{ 2068 { 2069 Name: "myweb1", 2070 Type: "worker-with-health", 2071 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 2072 }, 2073 { 2074 Name: "myweb2", 2075 Type: "worker", 2076 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 2077 }, 2078 }, 2079 Workflow: &v1beta1.Workflow{ 2080 Steps: []workflowv1alpha1.WorkflowStep{ 2081 { 2082 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2083 Name: "suspend", 2084 Type: "suspend", 2085 Timeout: "1s", 2086 Outputs: workflowv1alpha1.StepOutputs{ 2087 { 2088 Name: "suspend_output", 2089 ValueFrom: "context.name", 2090 }, 2091 }, 2092 }, 2093 }, 2094 { 2095 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2096 Name: "myweb1", 2097 Type: "apply-component", 2098 Inputs: workflowv1alpha1.StepInputs{ 2099 { 2100 From: "suspend_output", 2101 ParameterKey: "", 2102 }, 2103 }, 2104 If: `status.suspend.timeout && inputs.suspend_output == "app-with-if-expressions"`, 2105 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1"}`)}, 2106 }, 2107 }, 2108 { 2109 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2110 Name: "myweb2", 2111 If: "status.suspend.succeeded", 2112 Type: "apply-component", 2113 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb2"}`)}, 2114 }, 2115 }, 2116 }, 2117 }, 2118 }, 2119 } 2120 2121 Expect(k8sClient.Create(context.Background(), app)).Should(BeNil()) 2122 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 2123 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2124 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2125 2126 checkApp := &v1beta1.Application{} 2127 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 2128 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationWorkflowSuspending)) 2129 2130 expDeployment := &v1.Deployment{} 2131 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 2132 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(util.NotFoundMatcher{}) 2133 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 2134 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 2135 2136 time.Sleep(time.Second) 2137 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2138 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil()) 2139 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 2140 2141 expDeployment.Status.Replicas = 1 2142 expDeployment.Status.ReadyReplicas = 1 2143 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 2144 2145 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2146 2147 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 2148 2149 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 2150 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationWorkflowFailed)) 2151 }) 2152 2153 It("application with if expressions in workflow sub steps", func() { 2154 ns := corev1.Namespace{ 2155 ObjectMeta: metav1.ObjectMeta{ 2156 Name: "app-with-if-expressions-workflow-sub-steps", 2157 }, 2158 } 2159 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 2160 healthComponentDef := &v1beta1.ComponentDefinition{} 2161 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 2162 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 2163 healthComponentDef.Name = "worker-with-health" 2164 healthComponentDef.Namespace = "app-with-if-expressions-workflow-sub-steps" 2165 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 2166 app := &v1beta1.Application{ 2167 TypeMeta: metav1.TypeMeta{ 2168 Kind: "Application", 2169 APIVersion: "core.oam.dev/v1beta1", 2170 }, 2171 ObjectMeta: metav1.ObjectMeta{ 2172 Name: "app-with-if-expressions-workflow-sub-steps", 2173 Namespace: "app-with-if-expressions-workflow-sub-steps", 2174 }, 2175 Spec: v1beta1.ApplicationSpec{ 2176 Components: []common.ApplicationComponent{ 2177 { 2178 Name: "myweb1", 2179 Type: "worker-with-health", 2180 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 2181 }, 2182 { 2183 Name: "myweb1-sub", 2184 Type: "worker-with-health", 2185 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 2186 }, 2187 { 2188 Name: "myweb2", 2189 Type: "worker-with-health", 2190 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 2191 }, 2192 { 2193 Name: "myweb3", 2194 Type: "worker", 2195 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 2196 }, 2197 }, 2198 Workflow: &v1beta1.Workflow{ 2199 Steps: []workflowv1alpha1.WorkflowStep{ 2200 { 2201 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2202 Name: "myweb1", 2203 Type: "step-group", 2204 }, 2205 SubSteps: []workflowv1alpha1.WorkflowStepBase{ 2206 { 2207 Name: "myweb1_sub1", 2208 Type: "apply-component", 2209 If: "status.myweb1_sub2.timeout", 2210 DependsOn: []string{"myweb1_sub2"}, 2211 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1"}`)}, 2212 }, 2213 { 2214 Name: "myweb1_sub2", 2215 Type: "suspend", 2216 Properties: &runtime.RawExtension{Raw: []byte(`{"duration":"1s"}`)}, 2217 Outputs: workflowv1alpha1.StepOutputs{ 2218 { 2219 Name: "suspend_output", 2220 ValueFrom: "context.name", 2221 }, 2222 }, 2223 }, 2224 { 2225 Name: "myweb1_sub3", 2226 Type: "apply-component", 2227 DependsOn: []string{"myweb1_sub1"}, 2228 Inputs: workflowv1alpha1.StepInputs{ 2229 { 2230 From: "suspend_output", 2231 ParameterKey: "", 2232 }, 2233 }, 2234 If: `status.myweb1_sub1.timeout || inputs.suspend_output == "app-with-if-expressions-workflow-sub-steps"`, 2235 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1-sub"}`)}, 2236 }, 2237 }, 2238 }, 2239 { 2240 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2241 Name: "myweb2", 2242 Type: "apply-component", 2243 If: "status.myweb1.failed", 2244 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb2"}`)}, 2245 }, 2246 }, 2247 { 2248 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2249 Name: "myweb3", 2250 Type: "apply-component", 2251 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb3"}`)}, 2252 }, 2253 }, 2254 }, 2255 }, 2256 }, 2257 } 2258 2259 Expect(k8sClient.Create(context.Background(), app)).Should(BeNil()) 2260 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 2261 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2262 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2263 2264 checkApp := &v1beta1.Application{} 2265 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 2266 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationWorkflowSuspending)) 2267 2268 expDeployment := &v1.Deployment{} 2269 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 2270 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(util.NotFoundMatcher{}) 2271 web1SubKey := types.NamespacedName{Namespace: ns.Name, Name: "myweb1-sub"} 2272 Expect(k8sClient.Get(ctx, web1SubKey, expDeployment)).Should(util.NotFoundMatcher{}) 2273 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 2274 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 2275 web3Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb3"} 2276 Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(util.NotFoundMatcher{}) 2277 2278 time.Sleep(time.Second) 2279 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2280 2281 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 2282 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunningWorkflow)) 2283 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(util.NotFoundMatcher{}) 2284 Expect(k8sClient.Get(ctx, web1SubKey, expDeployment)).Should(BeNil()) 2285 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 2286 Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(util.NotFoundMatcher{}) 2287 2288 expDeployment.Status.Replicas = 1 2289 expDeployment.Status.ReadyReplicas = 1 2290 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 2291 2292 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2293 2294 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 2295 Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(BeNil()) 2296 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 2297 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunning)) 2298 }) 2299 2300 It("application with timeout in workflow", func() { 2301 ns := corev1.Namespace{ 2302 ObjectMeta: metav1.ObjectMeta{ 2303 Name: "app-with-timeout", 2304 }, 2305 } 2306 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 2307 healthComponentDef := &v1beta1.ComponentDefinition{} 2308 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 2309 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 2310 healthComponentDef.Name = "worker-with-health" 2311 healthComponentDef.Namespace = "app-with-timeout" 2312 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 2313 app := &v1beta1.Application{ 2314 TypeMeta: metav1.TypeMeta{ 2315 Kind: "Application", 2316 APIVersion: "core.oam.dev/v1beta1", 2317 }, 2318 ObjectMeta: metav1.ObjectMeta{ 2319 Name: "app-with-timeout", 2320 Namespace: "app-with-timeout", 2321 }, 2322 Spec: v1beta1.ApplicationSpec{ 2323 Components: []common.ApplicationComponent{ 2324 { 2325 Name: "myweb1", 2326 Type: "worker-with-health", 2327 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 2328 }, 2329 { 2330 Name: "myweb2", 2331 Type: "worker", 2332 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 2333 }, 2334 { 2335 Name: "myweb3", 2336 Type: "worker-with-health", 2337 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 2338 }, 2339 }, 2340 Workflow: &v1beta1.Workflow{ 2341 Steps: []workflowv1alpha1.WorkflowStep{ 2342 { 2343 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2344 Name: "timeout-step", 2345 Type: "apply-component", 2346 Timeout: "1s", 2347 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1"}`)}, 2348 }, 2349 }, 2350 { 2351 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2352 Name: "myweb2", 2353 Type: "apply-component", 2354 If: "always", 2355 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb2"}`)}, 2356 }, 2357 }, 2358 { 2359 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2360 Name: "myweb3", 2361 Type: "apply-component", 2362 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb3"}`)}, 2363 }, 2364 }, 2365 }, 2366 }, 2367 }, 2368 } 2369 2370 Expect(k8sClient.Create(context.Background(), app)).Should(BeNil()) 2371 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 2372 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2373 2374 expDeployment := &v1.Deployment{} 2375 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 2376 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(util.NotFoundMatcher{}) 2377 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 2378 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 2379 web3Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb3"} 2380 Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(util.NotFoundMatcher{}) 2381 2382 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2383 2384 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil()) 2385 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 2386 Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(util.NotFoundMatcher{}) 2387 time.Sleep(time.Second) 2388 2389 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2390 2391 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(BeNil()) 2392 Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(util.NotFoundMatcher{}) 2393 2394 checkApp := &v1beta1.Application{} 2395 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 2396 Expect(checkApp.Status.Workflow.Steps[0].Reason).Should(Equal(wfTypes.StatusReasonTimeout)) 2397 Expect(checkApp.Status.Workflow.Steps[1].Phase).Should(Equal(workflowv1alpha1.WorkflowStepPhaseSucceeded)) 2398 Expect(checkApp.Status.Workflow.Steps[2].Reason).Should(Equal(wfTypes.StatusReasonSkip)) 2399 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationWorkflowFailed)) 2400 }) 2401 2402 It("application with timeout and suspend in workflow", func() { 2403 ns := corev1.Namespace{ 2404 ObjectMeta: metav1.ObjectMeta{ 2405 Name: "app-with-timeout-suspend", 2406 }, 2407 } 2408 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 2409 healthComponentDef := &v1beta1.ComponentDefinition{} 2410 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 2411 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 2412 healthComponentDef.Name = "worker-with-health" 2413 healthComponentDef.Namespace = "app-with-timeout-suspend" 2414 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 2415 app := &v1beta1.Application{ 2416 TypeMeta: metav1.TypeMeta{ 2417 Kind: "Application", 2418 APIVersion: "core.oam.dev/v1beta1", 2419 }, 2420 ObjectMeta: metav1.ObjectMeta{ 2421 Name: "app-with-timeout-suspend", 2422 Namespace: "app-with-timeout-suspend", 2423 }, 2424 Spec: v1beta1.ApplicationSpec{ 2425 Components: []common.ApplicationComponent{ 2426 { 2427 Name: "myweb1", 2428 Type: "worker-with-health", 2429 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 2430 }, 2431 { 2432 Name: "myweb2", 2433 Type: "worker", 2434 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 2435 }, 2436 }, 2437 Workflow: &v1beta1.Workflow{ 2438 Steps: []workflowv1alpha1.WorkflowStep{ 2439 { 2440 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2441 Name: "timeout-step", 2442 Type: "suspend", 2443 Timeout: "1s", 2444 }, 2445 }, 2446 { 2447 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2448 Name: "myweb1", 2449 Type: "apply-component", 2450 If: "always", 2451 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1"}`)}, 2452 }, 2453 }, 2454 { 2455 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2456 Name: "myweb2", 2457 Type: "apply-component", 2458 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb2"}`)}, 2459 }, 2460 }, 2461 }, 2462 }, 2463 }, 2464 } 2465 2466 Expect(k8sClient.Create(context.Background(), app)).Should(BeNil()) 2467 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 2468 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2469 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2470 2471 expDeployment := &v1.Deployment{} 2472 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 2473 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(util.NotFoundMatcher{}) 2474 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 2475 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 2476 checkApp := &v1beta1.Application{} 2477 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 2478 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationWorkflowSuspending)) 2479 2480 time.Sleep(time.Second) 2481 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2482 2483 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil()) 2484 expDeployment.Status.Replicas = 1 2485 expDeployment.Status.ReadyReplicas = 1 2486 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 2487 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 2488 2489 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2490 2491 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 2492 2493 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 2494 Expect(checkApp.Status.Workflow.Steps[0].Reason).Should(Equal(wfTypes.StatusReasonTimeout)) 2495 Expect(checkApp.Status.Workflow.Steps[1].Phase).Should(Equal(workflowv1alpha1.WorkflowStepPhaseSucceeded)) 2496 Expect(checkApp.Status.Workflow.Steps[2].Reason).Should(Equal(wfTypes.StatusReasonSkip)) 2497 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationWorkflowFailed)) 2498 }) 2499 2500 It("application with timeout in workflow sub steps", func() { 2501 ns := corev1.Namespace{ 2502 ObjectMeta: metav1.ObjectMeta{ 2503 Name: "app-with-timeout-sub-steps", 2504 }, 2505 } 2506 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 2507 healthComponentDef := &v1beta1.ComponentDefinition{} 2508 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 2509 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 2510 healthComponentDef.Name = "worker-with-health" 2511 healthComponentDef.Namespace = "app-with-timeout-sub-steps" 2512 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 2513 app := &v1beta1.Application{ 2514 TypeMeta: metav1.TypeMeta{ 2515 Kind: "Application", 2516 APIVersion: "core.oam.dev/v1beta1", 2517 }, 2518 ObjectMeta: metav1.ObjectMeta{ 2519 Name: "app-with-timeout-sub-steps", 2520 Namespace: "app-with-timeout-sub-steps", 2521 }, 2522 Spec: v1beta1.ApplicationSpec{ 2523 Components: []common.ApplicationComponent{ 2524 { 2525 Name: "myweb1", 2526 Type: "worker-with-health", 2527 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 2528 }, 2529 { 2530 Name: "myweb2", 2531 Type: "worker-with-health", 2532 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 2533 }, 2534 { 2535 Name: "myweb3", 2536 Type: "worker-with-health", 2537 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 2538 }, 2539 { 2540 Name: "myweb4", 2541 Type: "worker-with-health", 2542 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 2543 }, 2544 }, 2545 Workflow: &v1beta1.Workflow{ 2546 Steps: []workflowv1alpha1.WorkflowStep{ 2547 { 2548 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2549 Name: "myweb1", 2550 Type: "step-group", 2551 }, 2552 SubSteps: []workflowv1alpha1.WorkflowStepBase{ 2553 { 2554 Name: "myweb1-sub1", 2555 Type: "apply-component", 2556 If: "always", 2557 DependsOn: []string{"myweb1-sub2"}, 2558 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb2"}`)}, 2559 }, 2560 { 2561 Name: "myweb1-sub2", 2562 Type: "apply-component", 2563 Timeout: "1s", 2564 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1"}`)}, 2565 }, 2566 { 2567 Name: "myweb1-sub3", 2568 Type: "apply-component", 2569 DependsOn: []string{"myweb1-sub1"}, 2570 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb3"}`)}, 2571 }, 2572 }, 2573 }, 2574 { 2575 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2576 Name: "myweb3", 2577 Type: "apply-component", 2578 If: "always", 2579 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb3"}`)}, 2580 }, 2581 }, 2582 { 2583 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2584 Name: "myweb4", 2585 Type: "apply-component", 2586 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb4"}`)}, 2587 }, 2588 }, 2589 }, 2590 }, 2591 }, 2592 } 2593 2594 Expect(k8sClient.Create(context.Background(), app)).Should(BeNil()) 2595 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 2596 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2597 2598 expDeployment := &v1.Deployment{} 2599 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 2600 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(util.NotFoundMatcher{}) 2601 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 2602 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 2603 web3Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb3"} 2604 Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(util.NotFoundMatcher{}) 2605 web4Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb4"} 2606 Expect(k8sClient.Get(ctx, web4Key, expDeployment)).Should(util.NotFoundMatcher{}) 2607 2608 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2609 2610 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil()) 2611 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 2612 Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(util.NotFoundMatcher{}) 2613 Expect(k8sClient.Get(ctx, web4Key, expDeployment)).Should(util.NotFoundMatcher{}) 2614 2615 time.Sleep(time.Second) 2616 2617 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2618 2619 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(BeNil()) 2620 Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(util.NotFoundMatcher{}) 2621 Expect(k8sClient.Get(ctx, web4Key, expDeployment)).Should(util.NotFoundMatcher{}) 2622 expDeployment.Status.Replicas = 1 2623 expDeployment.Status.ReadyReplicas = 1 2624 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 2625 2626 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2627 Expect(k8sClient.Get(ctx, web3Key, expDeployment)).Should(BeNil()) 2628 Expect(k8sClient.Get(ctx, web4Key, expDeployment)).Should(util.NotFoundMatcher{}) 2629 expDeployment.Status.Replicas = 1 2630 expDeployment.Status.ReadyReplicas = 1 2631 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 2632 2633 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2634 Expect(k8sClient.Get(ctx, web4Key, expDeployment)).Should(util.NotFoundMatcher{}) 2635 2636 checkApp := &v1beta1.Application{} 2637 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 2638 Expect(checkApp.Status.Workflow.Steps[0].Reason).Should(Equal(wfTypes.StatusReasonTimeout)) 2639 Expect(checkApp.Status.Workflow.Steps[1].Phase).Should(Equal(workflowv1alpha1.WorkflowStepPhaseSucceeded)) 2640 Expect(checkApp.Status.Workflow.Steps[2].Reason).Should(Equal(wfTypes.StatusReasonSkip)) 2641 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationWorkflowFailed)) 2642 }) 2643 2644 It("application with timeout and suspend in workflow sub steps", func() { 2645 ns := corev1.Namespace{ 2646 ObjectMeta: metav1.ObjectMeta{ 2647 Name: "app-with-timeout-suspend-sub-steps", 2648 }, 2649 } 2650 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 2651 healthComponentDef := &v1beta1.ComponentDefinition{} 2652 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 2653 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 2654 healthComponentDef.Name = "worker-with-health" 2655 healthComponentDef.Namespace = "app-with-timeout-suspend-sub-steps" 2656 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 2657 app := &v1beta1.Application{ 2658 TypeMeta: metav1.TypeMeta{ 2659 Kind: "Application", 2660 APIVersion: "core.oam.dev/v1beta1", 2661 }, 2662 ObjectMeta: metav1.ObjectMeta{ 2663 Name: "app-with-timeout-suspend-sub-steps", 2664 Namespace: "app-with-timeout-suspend-sub-steps", 2665 }, 2666 Spec: v1beta1.ApplicationSpec{ 2667 Components: []common.ApplicationComponent{ 2668 { 2669 Name: "myweb1", 2670 Type: "worker-with-health", 2671 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 2672 }, 2673 { 2674 Name: "myweb2", 2675 Type: "worker-with-health", 2676 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 2677 }, 2678 }, 2679 Workflow: &v1beta1.Workflow{ 2680 Steps: []workflowv1alpha1.WorkflowStep{ 2681 { 2682 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2683 Name: "group1", 2684 Type: "step-group", 2685 Timeout: "1s", 2686 }, 2687 SubSteps: []workflowv1alpha1.WorkflowStepBase{ 2688 { 2689 Name: "suspend", 2690 Type: "suspend", 2691 }, 2692 }, 2693 }, 2694 { 2695 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2696 Name: "group2", 2697 If: "always", 2698 Type: "step-group", 2699 }, 2700 SubSteps: []workflowv1alpha1.WorkflowStepBase{ 2701 { 2702 Name: "sub-suspend", 2703 Type: "suspend", 2704 Timeout: "1s", 2705 }, 2706 { 2707 Name: "myweb1", 2708 Type: "apply-component", 2709 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1"}`)}, 2710 }, 2711 }, 2712 }, 2713 { 2714 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2715 Name: "myweb2", 2716 Type: "apply-component", 2717 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb2"}`)}, 2718 }, 2719 }, 2720 }, 2721 }, 2722 }, 2723 } 2724 2725 Expect(k8sClient.Create(context.Background(), app)).Should(BeNil()) 2726 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 2727 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2728 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2729 2730 expDeployment := &v1.Deployment{} 2731 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 2732 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(util.NotFoundMatcher{}) 2733 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 2734 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 2735 checkApp := &v1beta1.Application{} 2736 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 2737 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationWorkflowSuspending)) 2738 2739 time.Sleep(time.Second) 2740 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2741 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 2742 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationWorkflowSuspending)) 2743 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil()) 2744 expDeployment.Status.Replicas = 1 2745 expDeployment.Status.ReadyReplicas = 1 2746 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 2747 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 2748 2749 time.Sleep(time.Second) 2750 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2751 2752 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(util.NotFoundMatcher{}) 2753 2754 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 2755 Expect(checkApp.Status.Workflow.Steps[0].Reason).Should(Equal(wfTypes.StatusReasonTimeout)) 2756 Expect(checkApp.Status.Workflow.Steps[1].Reason).Should(Equal(wfTypes.StatusReasonTimeout)) 2757 Expect(checkApp.Status.Workflow.Steps[2].Reason).Should(Equal(wfTypes.StatusReasonSkip)) 2758 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationWorkflowFailed)) 2759 }) 2760 2761 It("application with wait suspend in workflow", func() { 2762 ns := corev1.Namespace{ 2763 ObjectMeta: metav1.ObjectMeta{ 2764 Name: "app-with-wait-suspend", 2765 }, 2766 } 2767 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 2768 healthComponentDef := &v1beta1.ComponentDefinition{} 2769 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 2770 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 2771 healthComponentDef.Name = "worker-with-health" 2772 healthComponentDef.Namespace = "app-with-wait-suspend" 2773 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 2774 app := &v1beta1.Application{ 2775 TypeMeta: metav1.TypeMeta{ 2776 Kind: "Application", 2777 APIVersion: "core.oam.dev/v1beta1", 2778 }, 2779 ObjectMeta: metav1.ObjectMeta{ 2780 Name: "app-with-wait-suspend", 2781 Namespace: "app-with-wait-suspend", 2782 }, 2783 Spec: v1beta1.ApplicationSpec{ 2784 Components: []common.ApplicationComponent{ 2785 { 2786 Name: "myweb1", 2787 Type: "worker-with-health", 2788 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 2789 }, 2790 { 2791 Name: "myweb2", 2792 Type: "worker-with-health", 2793 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 2794 }, 2795 }, 2796 Workflow: &v1beta1.Workflow{ 2797 Steps: []workflowv1alpha1.WorkflowStep{ 2798 { 2799 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2800 Name: "suspend", 2801 Type: "suspend", 2802 Properties: &runtime.RawExtension{Raw: []byte(`{"duration":"1s"}`)}, 2803 }, 2804 }, 2805 { 2806 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 2807 Name: "myweb1", 2808 Type: "apply-component", 2809 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myweb1"}`)}, 2810 }, 2811 }, 2812 }, 2813 }, 2814 }, 2815 } 2816 2817 Expect(k8sClient.Create(context.Background(), app)).Should(BeNil()) 2818 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 2819 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2820 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2821 2822 expDeployment := &v1.Deployment{} 2823 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 2824 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(util.NotFoundMatcher{}) 2825 checkApp := &v1beta1.Application{} 2826 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 2827 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationWorkflowSuspending)) 2828 2829 time.Sleep(time.Second) 2830 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2831 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 2832 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunningWorkflow)) 2833 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil()) 2834 expDeployment.Status.Replicas = 1 2835 expDeployment.Status.ReadyReplicas = 1 2836 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 2837 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2838 2839 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 2840 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunning)) 2841 }) 2842 2843 It("application with input/output run as dag workflow", func() { 2844 ns := corev1.Namespace{ 2845 ObjectMeta: metav1.ObjectMeta{ 2846 Name: "app-with-input-output", 2847 }, 2848 } 2849 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 2850 healthComponentDef := &v1beta1.ComponentDefinition{} 2851 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 2852 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 2853 healthComponentDef.Name = "worker-with-health" 2854 healthComponentDef.Namespace = "app-with-input-output" 2855 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 2856 appwithInputOutput := &v1beta1.Application{ 2857 TypeMeta: metav1.TypeMeta{ 2858 Kind: "Application", 2859 APIVersion: "core.oam.dev/v1beta1", 2860 }, 2861 ObjectMeta: metav1.ObjectMeta{ 2862 Name: "app-with-input-output", 2863 Namespace: "app-with-input-output", 2864 }, 2865 Spec: v1beta1.ApplicationSpec{ 2866 Components: []common.ApplicationComponent{ 2867 { 2868 Name: "myweb1", 2869 Type: "worker-with-health", 2870 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep", "10"],"image":"busybox"}`)}, 2871 Inputs: workflowv1alpha1.StepInputs{ 2872 { 2873 From: "message", 2874 ParameterKey: "properties.enemies", 2875 }, 2876 { 2877 From: "message", 2878 ParameterKey: "properties.lives", 2879 }, 2880 { 2881 From: "sleepTime", 2882 ParameterKey: "properties.cmd[1]", 2883 }, 2884 }, 2885 }, 2886 { 2887 Name: "myweb2", 2888 Type: "worker-with-health", 2889 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 2890 Outputs: workflowv1alpha1.StepOutputs{ 2891 {Name: "message", ValueFrom: "output.status.conditions[0].message+\",\"+outputs.gameconfig.data.lives"}, 2892 {Name: "sleepTime", ValueFrom: "\"100\""}, 2893 }, 2894 }, 2895 }, 2896 }, 2897 } 2898 2899 Expect(k8sClient.Create(context.Background(), appwithInputOutput)).Should(BeNil()) 2900 appKey := types.NamespacedName{Namespace: ns.Name, Name: appwithInputOutput.Name} 2901 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 2902 2903 expDeployment := &v1.Deployment{} 2904 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 2905 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 2906 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(util.NotFoundMatcher{}) 2907 2908 checkApp := &v1beta1.Application{} 2909 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 2910 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(BeNil()) 2911 2912 expDeployment.Status.Replicas = 1 2913 expDeployment.Status.ReadyReplicas = 1 2914 expDeployment.Status.Conditions = []v1.DeploymentCondition{{ 2915 Message: "hello", 2916 }} 2917 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 2918 2919 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2920 expDeployment = &v1.Deployment{} 2921 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil()) 2922 expDeployment.Status.Replicas = 1 2923 expDeployment.Status.ReadyReplicas = 1 2924 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 2925 Expect(expDeployment.Spec.Template.Spec.Containers[0].Command).Should(BeEquivalentTo([]string{"sleep", "100"})) 2926 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2927 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 2928 checkApp = &v1beta1.Application{} 2929 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 2930 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunning)) 2931 2932 checkCM := &corev1.ConfigMap{} 2933 cmKey := types.NamespacedName{ 2934 Name: "myweb1game-config", 2935 Namespace: ns.Name, 2936 } 2937 Expect(k8sClient.Get(ctx, cmKey, checkCM)).Should(BeNil()) 2938 Expect(checkCM.Data["enemies"]).Should(BeEquivalentTo("hello,i am lives")) 2939 Expect(checkCM.Data["lives"]).Should(BeEquivalentTo("hello,i am lives")) 2940 }) 2941 2942 It("application with depends on run as dag workflow", func() { 2943 ns := corev1.Namespace{ 2944 ObjectMeta: metav1.ObjectMeta{ 2945 Name: "app-with-depends-on", 2946 }, 2947 } 2948 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 2949 healthComponentDef := &v1beta1.ComponentDefinition{} 2950 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 2951 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 2952 healthComponentDef.Name = "worker-with-health" 2953 healthComponentDef.Namespace = "app-with-depends-on" 2954 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 2955 appwithDependsOn := &v1beta1.Application{ 2956 TypeMeta: metav1.TypeMeta{ 2957 Kind: "Application", 2958 APIVersion: "core.oam.dev/v1beta1", 2959 }, 2960 ObjectMeta: metav1.ObjectMeta{ 2961 Name: "app-with-depends-on", 2962 Namespace: "app-with-depends-on", 2963 }, 2964 Spec: v1beta1.ApplicationSpec{ 2965 Components: []common.ApplicationComponent{ 2966 { 2967 Name: "myweb1", 2968 Type: "worker", 2969 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 2970 DependsOn: []string{"myweb2"}, 2971 }, 2972 { 2973 Name: "myweb2", 2974 Type: "worker-with-health", 2975 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 2976 }, 2977 }, 2978 }, 2979 } 2980 2981 Expect(k8sClient.Create(context.Background(), appwithDependsOn)).Should(BeNil()) 2982 appKey := types.NamespacedName{Namespace: ns.Name, Name: appwithDependsOn.Name} 2983 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 2984 2985 expDeployment := &v1.Deployment{} 2986 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 2987 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 2988 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(util.NotFoundMatcher{}) 2989 2990 checkApp := &v1beta1.Application{} 2991 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 2992 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(BeNil()) 2993 2994 expDeployment.Status.Replicas = 1 2995 expDeployment.Status.ReadyReplicas = 1 2996 expDeployment.Status.Conditions = []v1.DeploymentCondition{{ 2997 Message: "hello", 2998 }} 2999 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 3000 3001 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 3002 3003 expDeployment = &v1.Deployment{} 3004 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil()) 3005 expDeployment.Status.Replicas = 1 3006 expDeployment.Status.ReadyReplicas = 1 3007 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 3008 3009 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 3010 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 3011 checkApp = &v1beta1.Application{} 3012 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 3013 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunning)) 3014 }) 3015 3016 It("application with input/output and depends on", func() { 3017 ns := corev1.Namespace{ 3018 ObjectMeta: metav1.ObjectMeta{ 3019 Name: "app-with-input-output-depends-on", 3020 }, 3021 } 3022 Expect(k8sClient.Create(ctx, &ns)).Should(BeNil()) 3023 healthComponentDef := &v1beta1.ComponentDefinition{} 3024 hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml)) 3025 Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil()) 3026 healthComponentDef.Name = "worker-with-health" 3027 healthComponentDef.Namespace = "app-with-input-output-depends-on" 3028 Expect(k8sClient.Create(ctx, healthComponentDef)).Should(BeNil()) 3029 appwithInputOutputDependsOn := &v1beta1.Application{ 3030 TypeMeta: metav1.TypeMeta{ 3031 Kind: "Application", 3032 APIVersion: "core.oam.dev/v1beta1", 3033 }, 3034 ObjectMeta: metav1.ObjectMeta{ 3035 Name: "app-with-input-output-depends-on", 3036 Namespace: "app-with-input-output-depends-on", 3037 }, 3038 Spec: v1beta1.ApplicationSpec{ 3039 Components: []common.ApplicationComponent{ 3040 { 3041 Name: "myweb1", 3042 Type: "worker-with-health", 3043 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 3044 DependsOn: []string{"myweb2"}, 3045 Inputs: workflowv1alpha1.StepInputs{ 3046 { 3047 From: "message", 3048 ParameterKey: "properties.enemies", 3049 }, 3050 { 3051 From: "message", 3052 ParameterKey: "properties.lives", 3053 }, 3054 }, 3055 }, 3056 { 3057 Name: "myweb2", 3058 Type: "worker-with-health", 3059 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)}, 3060 Outputs: workflowv1alpha1.StepOutputs{ 3061 {Name: "message", ValueFrom: "output.status.conditions[0].message+\",\"+outputs.gameconfig.data.lives"}, 3062 }, 3063 }, 3064 }, 3065 }, 3066 } 3067 3068 Expect(k8sClient.Create(context.Background(), appwithInputOutputDependsOn)).Should(BeNil()) 3069 appKey := types.NamespacedName{Namespace: ns.Name, Name: appwithInputOutputDependsOn.Name} 3070 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 3071 3072 expDeployment := &v1.Deployment{} 3073 web1Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb1"} 3074 web2Key := types.NamespacedName{Namespace: ns.Name, Name: "myweb2"} 3075 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(util.NotFoundMatcher{}) 3076 3077 checkApp := &v1beta1.Application{} 3078 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 3079 Expect(k8sClient.Get(ctx, web2Key, expDeployment)).Should(BeNil()) 3080 3081 expDeployment.Status.Replicas = 1 3082 expDeployment.Status.ReadyReplicas = 1 3083 expDeployment.Status.Conditions = []v1.DeploymentCondition{{ 3084 Message: "hello", 3085 }} 3086 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 3087 3088 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 3089 3090 expDeployment = &v1.Deployment{} 3091 Expect(k8sClient.Get(ctx, web1Key, expDeployment)).Should(BeNil()) 3092 expDeployment.Status.Replicas = 1 3093 expDeployment.Status.ReadyReplicas = 1 3094 Expect(k8sClient.Status().Update(ctx, expDeployment)).Should(BeNil()) 3095 3096 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 3097 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 3098 checkApp = &v1beta1.Application{} 3099 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 3100 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunning)) 3101 3102 checkCM := &corev1.ConfigMap{} 3103 cmKey := types.NamespacedName{ 3104 Name: "myweb1game-config", 3105 Namespace: ns.Name, 3106 } 3107 Expect(k8sClient.Get(ctx, cmKey, checkCM)).Should(BeNil()) 3108 Expect(checkCM.Data["enemies"]).Should(BeEquivalentTo("hello,i am lives")) 3109 Expect(checkCM.Data["lives"]).Should(BeEquivalentTo("hello,i am lives")) 3110 }) 3111 3112 It("test application applied resource in workflow step status", func() { 3113 ns := corev1.Namespace{ 3114 ObjectMeta: metav1.ObjectMeta{ 3115 Name: "app-applied-resources", 3116 }, 3117 } 3118 Expect(k8sClient.Create(context.Background(), &ns)).Should(BeNil()) 3119 3120 webComponentDef := &v1beta1.ComponentDefinition{} 3121 hCDefJson, _ := yaml.YAMLToJSON([]byte(componentDefYaml)) 3122 Expect(json.Unmarshal(hCDefJson, webComponentDef)).Should(BeNil()) 3123 webComponentDef.Name = "web-worker" 3124 webComponentDef.Namespace = "app-applied-resources" 3125 Expect(k8sClient.Create(ctx, webComponentDef)).Should(BeNil()) 3126 3127 app := &v1beta1.Application{ 3128 TypeMeta: metav1.TypeMeta{ 3129 Kind: "Application", 3130 APIVersion: "core.oam.dev/v1beta1", 3131 }, 3132 ObjectMeta: metav1.ObjectMeta{ 3133 Name: "app-applied-resources", 3134 Namespace: "app-applied-resources", 3135 }, 3136 Spec: v1beta1.ApplicationSpec{ 3137 Components: []common.ApplicationComponent{ 3138 { 3139 Name: "myweb1", 3140 Type: "web-worker", 3141 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 3142 }, 3143 { 3144 Name: "myweb2", 3145 Type: "web-worker", 3146 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 3147 }, 3148 }, 3149 }, 3150 } 3151 Expect(k8sClient.Create(context.Background(), app)).Should(BeNil()) 3152 appKey := types.NamespacedName{Namespace: ns.Name, Name: app.Name} 3153 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 3154 checkApp := &v1beta1.Application{} 3155 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 3156 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunning)) 3157 Expect(checkApp.Status.AppliedResources).Should(BeEquivalentTo([]common.ClusterObjectReference{ 3158 { 3159 Cluster: "", 3160 Creator: common.WorkflowResourceCreator, 3161 ObjectReference: corev1.ObjectReference{Kind: "Deployment", 3162 Namespace: "app-applied-resources", 3163 Name: "myweb1", 3164 APIVersion: "apps/v1", 3165 }, 3166 }, 3167 { 3168 Cluster: "", 3169 Creator: common.WorkflowResourceCreator, 3170 ObjectReference: corev1.ObjectReference{Kind: "Deployment", 3171 Namespace: "app-applied-resources", 3172 Name: "myweb2", 3173 APIVersion: "apps/v1", 3174 }, 3175 }, 3176 })) 3177 3178 // make error 3179 checkApp.Spec.Components[0].Properties = nil 3180 Expect(k8sClient.Update(context.Background(), checkApp)).Should(BeNil()) 3181 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 3182 checkApp = &v1beta1.Application{} 3183 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 3184 Expect(checkApp.Status.AppliedResources).Should(BeEquivalentTo([]common.ClusterObjectReference{ 3185 { 3186 Cluster: "", 3187 Creator: common.WorkflowResourceCreator, 3188 ObjectReference: corev1.ObjectReference{Kind: "Deployment", 3189 Namespace: "app-applied-resources", 3190 Name: "myweb2", 3191 APIVersion: "apps/v1", 3192 }, 3193 }, 3194 })) 3195 Expect(checkApp.Status.Conditions[len(checkApp.Status.Conditions)-1].Type).Should(BeEquivalentTo("Render")) 3196 3197 checkApp.Spec.Components[0] = common.ApplicationComponent{ 3198 Name: "myweb-1", 3199 Type: "web-worker", 3200 Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)}, 3201 } 3202 Expect(k8sClient.Update(context.Background(), checkApp)).Should(BeNil()) 3203 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 3204 checkApp = &v1beta1.Application{} 3205 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil()) 3206 Expect(checkApp.Status.Phase).Should(BeEquivalentTo(common.ApplicationRunning)) 3207 Expect(checkApp.Status.AppliedResources).Should(BeEquivalentTo([]common.ClusterObjectReference{ 3208 { 3209 Cluster: "", 3210 Creator: common.WorkflowResourceCreator, 3211 ObjectReference: corev1.ObjectReference{Kind: "Deployment", 3212 Namespace: "app-applied-resources", 3213 Name: "myweb-1", 3214 APIVersion: "apps/v1", 3215 }, 3216 }, 3217 { 3218 Cluster: "", 3219 Creator: common.WorkflowResourceCreator, 3220 ObjectReference: corev1.ObjectReference{Kind: "Deployment", 3221 Namespace: "app-applied-resources", 3222 Name: "myweb2", 3223 APIVersion: "apps/v1", 3224 }, 3225 }, 3226 })) 3227 }) 3228 3229 It("app apply resource in parallel", func() { 3230 wfDef := &v1beta1.WorkflowStepDefinition{} 3231 wfDefJson, _ := yaml.YAMLToJSON([]byte(applyInParallelWorkflowDefinitionYaml)) 3232 Expect(json.Unmarshal(wfDefJson, wfDef)).Should(BeNil()) 3233 Expect(k8sClient.Create(ctx, wfDef.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) 3234 3235 ns := &corev1.Namespace{ 3236 ObjectMeta: metav1.ObjectMeta{ 3237 Name: "vela-test-apply-in-parallel", 3238 }, 3239 } 3240 app := appwithNoTrait.DeepCopy() 3241 app.Name = "vela-test-app" 3242 app.SetNamespace(ns.Name) 3243 app.Spec.Workflow = &v1beta1.Workflow{ 3244 Steps: []workflowv1alpha1.WorkflowStep{{ 3245 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 3246 Name: "apply-in-parallel", 3247 Type: "apply-test", 3248 Properties: &runtime.RawExtension{Raw: []byte(`{"parallelism": 20}`)}, 3249 }, 3250 }}, 3251 } 3252 Expect(k8sClient.Create(ctx, ns)).Should(BeNil()) 3253 Expect(k8sClient.Create(ctx, app)).Should(BeNil()) 3254 appKey := client.ObjectKey{ 3255 Name: app.Name, 3256 Namespace: app.Namespace, 3257 } 3258 _, err := testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 3259 Expect(err).Should(BeNil()) 3260 3261 deployList := new(v1.DeploymentList) 3262 Expect(k8sClient.List(ctx, deployList, client.InNamespace(app.Namespace))).Should(BeNil()) 3263 Expect(len(deployList.Items)).Should(Equal(20)) 3264 3265 checkApp := new(v1beta1.Application) 3266 Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(Succeed()) 3267 rt := new(v1beta1.ResourceTracker) 3268 expectRTName := fmt.Sprintf("%s-%s", checkApp.Status.LatestRevision.Name, checkApp.GetNamespace()) 3269 Eventually(func() error { 3270 return k8sClient.Get(ctx, client.ObjectKey{Name: expectRTName}, rt) 3271 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 3272 3273 Expect(len(rt.Spec.ManagedResources)).Should(Equal(20)) 3274 }) 3275 3276 It("test controller requirement", func() { 3277 3278 ns := corev1.Namespace{ 3279 ObjectMeta: metav1.ObjectMeta{ 3280 Name: "test-controller-requirement", 3281 }, 3282 } 3283 Expect(k8sClient.Create(context.Background(), &ns)).Should(BeNil()) 3284 3285 appWithoutCtrlReq := appwithNoTrait.DeepCopy() 3286 appWithoutCtrlReq.SetNamespace(ns.Name) 3287 appWithoutCtrlReq.SetName("app-no-ctrl-req") 3288 Expect(k8sClient.Create(context.Background(), appWithoutCtrlReq)).Should(BeNil()) 3289 3290 appWithCtrlReqV1 := appwithNoTrait.DeepCopy() 3291 appWithCtrlReqV1.SetNamespace(ns.Name) 3292 appWithCtrlReqV1.SetName("app-with-ctrl-v1") 3293 appWithCtrlReqV1.Annotations = map[string]string{ 3294 oam.AnnotationControllerRequirement: "v1", 3295 } 3296 Expect(k8sClient.Create(context.Background(), appWithCtrlReqV1)).Should(BeNil()) 3297 3298 appWithCtrlReqV2 := appwithNoTrait.DeepCopy() 3299 appWithCtrlReqV2.SetNamespace(ns.Name) 3300 appWithCtrlReqV2.SetName("app-with-ctrl-v2") 3301 appWithCtrlReqV2.Annotations = map[string]string{ 3302 oam.AnnotationControllerRequirement: "v2", 3303 } 3304 Expect(k8sClient.Create(context.Background(), appWithCtrlReqV2)).Should(BeNil()) 3305 3306 v1OREmptyReconciler := *reconciler 3307 v1OREmptyReconciler.ignoreAppNoCtrlReq = false 3308 v1OREmptyReconciler.controllerVersion = "v1" 3309 3310 v2OnlyReconciler := *reconciler 3311 v2OnlyReconciler.ignoreAppNoCtrlReq = true 3312 v2OnlyReconciler.controllerVersion = "v2" 3313 3314 check := func(r reconcile.Reconciler, app *v1beta1.Application, do bool) { 3315 testutil.ReconcileOnceAfterFinalizer(r, reconcile.Request{NamespacedName: client.ObjectKey{ 3316 Name: app.Name, 3317 Namespace: app.Namespace, 3318 }}) 3319 checkApp := &v1beta1.Application{} 3320 Expect(k8sClient.Get(context.Background(), client.ObjectKey{ 3321 Name: app.Name, 3322 Namespace: app.Namespace, 3323 }, checkApp)).Should(BeNil()) 3324 3325 if do { 3326 Expect(checkApp.Annotations[oam.AnnotationKubeVelaVersion]).ShouldNot(BeEmpty()) 3327 } else { 3328 if checkApp.Annotations == nil { 3329 return 3330 } 3331 Expect(checkApp.Annotations[oam.AnnotationKubeVelaVersion]).Should(BeEmpty()) 3332 } 3333 } 3334 3335 check(&v2OnlyReconciler, appWithoutCtrlReq, false) 3336 check(&v2OnlyReconciler, appWithCtrlReqV1, false) 3337 check(&v1OREmptyReconciler, appWithCtrlReqV2, false) 3338 3339 check(&v1OREmptyReconciler, appWithoutCtrlReq, true) 3340 check(&v1OREmptyReconciler, appWithCtrlReqV1, true) 3341 check(&v2OnlyReconciler, appWithCtrlReqV2, true) 3342 }) 3343 3344 It("app with env and storage will create application", func() { 3345 3346 ns := &corev1.Namespace{ 3347 ObjectMeta: metav1.ObjectMeta{ 3348 Name: "vela-test-with-env-storage", 3349 }, 3350 } 3351 Expect(k8sClient.Create(ctx, ns)).Should(BeNil()) 3352 3353 appWithStorage.SetNamespace(ns.Name) 3354 app := appWithStorage.DeepCopy() 3355 Expect(k8sClient.Create(ctx, app)).Should(BeNil()) 3356 3357 appKey := client.ObjectKey{ 3358 Name: app.Name, 3359 Namespace: app.Namespace, 3360 } 3361 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 3362 3363 By("Check App running successfully") 3364 curApp := &v1beta1.Application{} 3365 Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil()) 3366 Expect(curApp.Status.Phase).Should(Equal(common.ApplicationRunning)) 3367 3368 appRevision := &v1beta1.ApplicationRevision{} 3369 Expect(k8sClient.Get(ctx, client.ObjectKey{ 3370 Namespace: app.Namespace, 3371 Name: curApp.Status.LatestRevision.Name, 3372 }, appRevision)).Should(BeNil()) 3373 3374 By("Check affiliated resource tracker is created") 3375 expectRTName := fmt.Sprintf("%s-%s", appRevision.GetName(), appRevision.GetNamespace()) 3376 Eventually(func() error { 3377 return k8sClient.Get(ctx, client.ObjectKey{Name: expectRTName}, &v1beta1.ResourceTracker{}) 3378 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 3379 3380 By("Check AppRevision Created with the expected workload spec") 3381 appRev := &v1beta1.ApplicationRevision{} 3382 Eventually(func() error { 3383 return k8sClient.Get(ctx, client.ObjectKey{Name: app.Name + "-v1", Namespace: app.GetNamespace()}, appRev) 3384 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 3385 3386 Expect(k8sClient.Delete(ctx, app)).Should(BeNil()) 3387 }) 3388 3389 It("test application with trait-storage-secret-mountPath will be optional ", func() { 3390 3391 ns := &corev1.Namespace{ 3392 ObjectMeta: metav1.ObjectMeta{ 3393 Name: "vela-test-with-mountpath", 3394 }, 3395 } 3396 Expect(k8sClient.Create(ctx, ns)).Should(BeNil()) 3397 3398 appWithMountPath.SetNamespace(ns.Name) 3399 app := appWithMountPath.DeepCopy() 3400 Expect(k8sClient.Create(ctx, app)).Should(BeNil()) 3401 3402 appKey := client.ObjectKey{ 3403 Name: app.Name, 3404 Namespace: app.Namespace, 3405 } 3406 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 3407 3408 By("Check App running successfully") 3409 curApp := &v1beta1.Application{} 3410 Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil()) 3411 Expect(curApp.Status.Phase).Should(Equal(common.ApplicationRunning)) 3412 3413 appRevision := &v1beta1.ApplicationRevision{} 3414 Expect(k8sClient.Get(ctx, client.ObjectKey{ 3415 Namespace: app.Namespace, 3416 Name: curApp.Status.LatestRevision.Name, 3417 }, appRevision)).Should(BeNil()) 3418 3419 By("Check affiliated resource tracker is created") 3420 expectRTName := fmt.Sprintf("%s-%s", appRevision.GetName(), appRevision.GetNamespace()) 3421 Eventually(func() error { 3422 return k8sClient.Get(ctx, client.ObjectKey{Name: expectRTName}, &v1beta1.ResourceTracker{}) 3423 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 3424 3425 By("Check AppRevision Created with the expected workload spec") 3426 appRev := &v1beta1.ApplicationRevision{} 3427 Eventually(func() error { 3428 return k8sClient.Get(ctx, client.ObjectKey{Name: app.Name + "-v1", Namespace: app.GetNamespace()}, appRev) 3429 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 3430 3431 By("Check secret Created with the expected trait-storage spec") 3432 secret := &corev1.Secret{} 3433 Expect(k8sClient.Get(ctx, client.ObjectKey{ 3434 Namespace: ns.Name, 3435 Name: app.Spec.Components[0].Name + "-secret", 3436 }, secret)).Should(BeNil()) 3437 3438 By("Check configMap Created with the expected trait-storage spec") 3439 cm := &corev1.ConfigMap{} 3440 Expect(k8sClient.Get(ctx, client.ObjectKey{ 3441 Namespace: ns.Name, 3442 Name: app.Spec.Components[0].Name + "-cm", 3443 }, cm)).Should(BeNil()) 3444 3445 Expect(k8sClient.Delete(ctx, cm)).Should(BeNil()) 3446 Expect(k8sClient.Delete(ctx, secret)).Should(BeNil()) 3447 Expect(k8sClient.Delete(ctx, app)).Should(BeNil()) 3448 }) 3449 3450 It("test application with trait-gateway support https protocol", func() { 3451 3452 ns := &corev1.Namespace{ 3453 ObjectMeta: metav1.ObjectMeta{ 3454 Name: "vela-test-with-trait-gateway-https", 3455 }, 3456 } 3457 Expect(k8sClient.Create(ctx, ns)).Should(BeNil()) 3458 3459 appWithHttpsGateway.SetNamespace(ns.Name) 3460 app := appWithHttpsGateway.DeepCopy() 3461 Expect(k8sClient.Create(ctx, app)).Should(BeNil()) 3462 3463 appKey := client.ObjectKey{ 3464 Name: app.Name, 3465 Namespace: app.Namespace, 3466 } 3467 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 3468 3469 By("Check App running successfully") 3470 curApp := &v1beta1.Application{} 3471 Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil()) 3472 Expect(curApp.Status.Phase).Should(Equal(common.ApplicationRunning)) 3473 3474 appRevision := &v1beta1.ApplicationRevision{} 3475 Expect(k8sClient.Get(ctx, client.ObjectKey{ 3476 Namespace: app.Namespace, 3477 Name: curApp.Status.LatestRevision.Name, 3478 }, appRevision)).Should(BeNil()) 3479 3480 By("Check affiliated resource tracker is created") 3481 expectRTName := fmt.Sprintf("%s-%s", appRevision.GetName(), appRevision.GetNamespace()) 3482 Eventually(func() error { 3483 return k8sClient.Get(ctx, client.ObjectKey{Name: expectRTName}, &v1beta1.ResourceTracker{}) 3484 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 3485 3486 By("Check AppRevision Created with the expected workload spec") 3487 appRev := &v1beta1.ApplicationRevision{} 3488 Eventually(func() error { 3489 return k8sClient.Get(ctx, client.ObjectKey{Name: app.Name + "-v1", Namespace: app.GetNamespace()}, appRev) 3490 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 3491 3492 By("Check ingress Created with the expected trait-gateway spec") 3493 ingress := &networkingv1.Ingress{} 3494 Expect(k8sClient.Get(ctx, client.ObjectKey{ 3495 Namespace: ns.Name, 3496 Name: app.Spec.Components[0].Name, 3497 }, ingress)).Should(BeNil()) 3498 Expect(len(ingress.Spec.TLS) > 0).Should(BeTrue()) 3499 Expect(ingress.Spec.TLS[0].SecretName).ShouldNot(BeNil()) 3500 Expect(ingress.Spec.TLS[0].SecretName).ShouldNot(BeEmpty()) 3501 Expect(len(ingress.Spec.TLS[0].Hosts) > 0).Should(BeTrue()) 3502 Expect(ingress.Spec.TLS[0].Hosts[0]).ShouldNot(BeEmpty()) 3503 Expect(k8sClient.Delete(ctx, ingress)).Should(BeNil()) 3504 Expect(k8sClient.Delete(ctx, app)).Should(BeNil()) 3505 }) 3506 3507 It("test application with multi-mountToEnv will create application", func() { 3508 3509 ns := &corev1.Namespace{ 3510 ObjectMeta: metav1.ObjectMeta{ 3511 Name: "app-with-mount-to-envs", 3512 }, 3513 } 3514 Expect(k8sClient.Create(ctx, ns)).Should(BeNil()) 3515 3516 appWithMountToEnvs.SetNamespace(ns.Name) 3517 app := appWithMountToEnvs.DeepCopy() 3518 Expect(k8sClient.Create(ctx, app)).Should(BeNil()) 3519 3520 appKey := client.ObjectKey{ 3521 Name: app.Name, 3522 Namespace: app.Namespace, 3523 } 3524 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 3525 3526 By("Check App running successfully") 3527 curApp := &v1beta1.Application{} 3528 Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil()) 3529 Expect(curApp.Status.Phase).Should(Equal(common.ApplicationRunning)) 3530 3531 appRevision := &v1beta1.ApplicationRevision{} 3532 Expect(k8sClient.Get(ctx, client.ObjectKey{ 3533 Namespace: app.Namespace, 3534 Name: curApp.Status.LatestRevision.Name, 3535 }, appRevision)).Should(BeNil()) 3536 By("Check affiliated resource tracker is created") 3537 expectRTName := fmt.Sprintf("%s-%s", appRevision.GetName(), appRevision.GetNamespace()) 3538 Eventually(func() error { 3539 return k8sClient.Get(ctx, client.ObjectKey{Name: expectRTName}, &v1beta1.ResourceTracker{}) 3540 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 3541 3542 By("Check AppRevision Created with the expected workload spec") 3543 appRev := &v1beta1.ApplicationRevision{} 3544 Eventually(func() error { 3545 return k8sClient.Get(ctx, client.ObjectKey{Name: app.Name + "-v1", Namespace: app.GetNamespace()}, appRev) 3546 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 3547 3548 By("Check secret Created with the expected trait-storage spec") 3549 secret := &corev1.Secret{} 3550 Expect(k8sClient.Get(ctx, client.ObjectKey{ 3551 Namespace: ns.Name, 3552 Name: app.Spec.Components[0].Name + "-secret", 3553 }, secret)).Should(BeNil()) 3554 3555 By("Check configMap Created with the expected trait-storage spec") 3556 cm := &corev1.ConfigMap{} 3557 Expect(k8sClient.Get(ctx, client.ObjectKey{ 3558 Namespace: ns.Name, 3559 Name: app.Spec.Components[0].Name + "-cm", 3560 }, cm)).Should(BeNil()) 3561 3562 Expect(k8sClient.Delete(ctx, cm)).Should(BeNil()) 3563 Expect(k8sClient.Delete(ctx, secret)).Should(BeNil()) 3564 Expect(k8sClient.Delete(ctx, app)).Should(BeNil()) 3565 }) 3566 3567 It("app with debug policy", func() { 3568 app := &v1beta1.Application{ 3569 TypeMeta: metav1.TypeMeta{ 3570 Kind: "Application", 3571 APIVersion: "core.oam.dev/v1beta1", 3572 }, 3573 ObjectMeta: metav1.ObjectMeta{ 3574 Name: "app-debug", 3575 Namespace: "default", 3576 }, 3577 Spec: v1beta1.ApplicationSpec{ 3578 Components: []common.ApplicationComponent{ 3579 { 3580 Name: "myworker", 3581 Type: "worker", 3582 Properties: &runtime.RawExtension{Raw: []byte("{\"cmd\":[\"sleep\",\"1000\"],\"image\":\"busybox\",\"env\":[{\"name\":\"firstKey\",\"value\":\"firstValue\"}]}")}, 3583 }, 3584 { 3585 Name: "myworker2", 3586 Type: "worker", 3587 Properties: &runtime.RawExtension{Raw: []byte("{\"cmd\":[\"sleep\",\"1000\"],\"image\":\"busybox\",\"env\":[{\"name\":\"firstKey\",\"value\":\"firstValue\"}]}")}, 3588 }, 3589 }, 3590 Policies: []v1beta1.AppPolicy{ 3591 { 3592 Type: "debug", 3593 Name: "debug", 3594 }, 3595 }, 3596 Workflow: &v1beta1.Workflow{ 3597 Steps: []workflowv1alpha1.WorkflowStep{ 3598 { 3599 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 3600 Name: "step1", 3601 Type: "apply-component", 3602 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myworker"}`)}, 3603 }, 3604 }, 3605 { 3606 WorkflowStepBase: workflowv1alpha1.WorkflowStepBase{ 3607 Name: "step2", 3608 Type: "step-group", 3609 }, 3610 SubSteps: []workflowv1alpha1.WorkflowStepBase{ 3611 { 3612 Name: "step2-sub1", 3613 Type: "apply-component", 3614 Properties: &runtime.RawExtension{Raw: []byte(`{"component":"myworker2"}`)}, 3615 }, 3616 }, 3617 }, 3618 }, 3619 }, 3620 }, 3621 } 3622 Expect(k8sClient.Create(ctx, app)).Should(BeNil()) 3623 3624 appKey := client.ObjectKey{ 3625 Name: app.Name, 3626 Namespace: app.Namespace, 3627 } 3628 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 3629 3630 By("Check App running successfully") 3631 curApp := &v1beta1.Application{} 3632 Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil()) 3633 Expect(curApp.Status.Phase).Should(Equal(common.ApplicationRunning)) 3634 3635 By("Check debug Config Map is created") 3636 debugCM := &corev1.ConfigMap{} 3637 Expect(k8sClient.Get(ctx, types.NamespacedName{ 3638 Name: debug.GenerateContextName(app.Name, curApp.Status.Workflow.Steps[0].ID, string(app.UID)), 3639 Namespace: "default", 3640 }, debugCM)).Should(BeNil()) 3641 Expect(k8sClient.Get(ctx, types.NamespacedName{ 3642 Name: debug.GenerateContextName(app.Name, curApp.Status.Workflow.Steps[1].SubStepsStatus[0].ID, string(app.UID)), 3643 Namespace: "default", 3644 }, debugCM)).Should(BeNil()) 3645 3646 By("Update the application to update the debug Config Map") 3647 app.Spec.Components[0].Properties = &runtime.RawExtension{Raw: []byte("{\"cmd\":[\"sleep\",\"1000\"],\"image\":\"busybox\",\"env\":[{\"name\":\"firstKey\",\"value\":\"updateValue\"}]}")} 3648 testutil.ReconcileOnce(reconciler, reconcile.Request{NamespacedName: appKey}) 3649 updatedCM := &corev1.ConfigMap{} 3650 Expect(k8sClient.Get(ctx, types.NamespacedName{ 3651 Name: debug.GenerateContextName(app.Name, curApp.Status.Workflow.Steps[0].ID, string(app.UID)), 3652 Namespace: "default", 3653 }, updatedCM)).Should(BeNil()) 3654 3655 Expect(k8sClient.Delete(ctx, app)).Should(BeNil()) 3656 }) 3657 3658 It("test application with controlPlaneOnly trait ", func() { 3659 3660 ns := &corev1.Namespace{ 3661 ObjectMeta: metav1.ObjectMeta{ 3662 Name: "vela-test-with-controlplaneonly", 3663 }, 3664 } 3665 Expect(k8sClient.Create(ctx, ns)).Should(BeNil()) 3666 3667 appWithControlPlaneOnly.SetNamespace(ns.Name) 3668 app := appWithControlPlaneOnly.DeepCopy() 3669 Expect(k8sClient.Create(ctx, app)).Should(BeNil()) 3670 3671 appKey := client.ObjectKey{ 3672 Name: app.Name, 3673 Namespace: app.Namespace, 3674 } 3675 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 3676 3677 By("Check App running successfully") 3678 curApp := &v1beta1.Application{} 3679 Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil()) 3680 Expect(curApp.Status.Phase).Should(Equal(common.ApplicationRunning)) 3681 3682 appRevision := &v1beta1.ApplicationRevision{} 3683 Expect(k8sClient.Get(ctx, client.ObjectKey{ 3684 Namespace: app.Namespace, 3685 Name: curApp.Status.LatestRevision.Name, 3686 }, appRevision)).Should(BeNil()) 3687 3688 By("Check affiliated resource tracker is created") 3689 expectRTName := fmt.Sprintf("%s-%s", appRevision.GetName(), appRevision.GetNamespace()) 3690 Eventually(func() error { 3691 return k8sClient.Get(ctx, client.ObjectKey{Name: expectRTName}, &v1beta1.ResourceTracker{}) 3692 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 3693 3694 By("Check AppRevision Created with the expected workload spec") 3695 appRev := &v1beta1.ApplicationRevision{} 3696 Eventually(func() error { 3697 return k8sClient.Get(ctx, client.ObjectKey{Name: app.Name + "-v1", Namespace: app.GetNamespace()}, appRev) 3698 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 3699 3700 By("Check secret Created with the expected trait-storage spec") 3701 hpa := &autoscalingv1.HorizontalPodAutoscaler{} 3702 Expect(k8sClient.Get(ctx, client.ObjectKey{ 3703 Namespace: app.GetNamespace(), 3704 Name: app.Spec.Components[0].Name, 3705 }, hpa)).Should(BeNil()) 3706 3707 Expect(k8sClient.Delete(ctx, hpa)).Should(BeNil()) 3708 Expect(k8sClient.Delete(ctx, app)).Should(BeNil()) 3709 }) 3710 3711 It("test application with apply-once policy ", func() { 3712 ns := &corev1.Namespace{ 3713 ObjectMeta: metav1.ObjectMeta{ 3714 Name: "vela-test-with-apply-once", 3715 }, 3716 } 3717 Expect(k8sClient.Create(ctx, ns)).Should(BeNil()) 3718 appWithApplyOnce.SetNamespace(ns.Name) 3719 app := appWithApplyOnce.DeepCopy() 3720 Expect(k8sClient.Create(ctx, app)).Should(BeNil()) 3721 3722 appKey := client.ObjectKey{ 3723 Name: app.Name, 3724 Namespace: app.Namespace, 3725 } 3726 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 3727 3728 By("Check App running successfully") 3729 curApp := &v1beta1.Application{} 3730 Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil()) 3731 Expect(curApp.Status.Phase).Should(Equal(common.ApplicationRunning)) 3732 3733 appRevision := &v1beta1.ApplicationRevision{} 3734 Expect(k8sClient.Get(ctx, client.ObjectKey{ 3735 Namespace: app.Namespace, 3736 Name: curApp.Status.LatestRevision.Name, 3737 }, appRevision)).Should(BeNil()) 3738 3739 By("Check affiliated resource tracker is created") 3740 expectRTName := fmt.Sprintf("%s-%s", appRevision.GetName(), appRevision.GetNamespace()) 3741 Eventually(func() error { 3742 return k8sClient.Get(ctx, client.ObjectKey{Name: expectRTName}, &v1beta1.ResourceTracker{}) 3743 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 3744 3745 By("Check AppRevision Created with the expected workload spec") 3746 appRev := &v1beta1.ApplicationRevision{} 3747 Eventually(func() error { 3748 return k8sClient.Get(ctx, client.ObjectKey{Name: app.Name + "-v1", Namespace: app.GetNamespace()}, appRev) 3749 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 3750 3751 By("Check secret Created with the expected trait-storage spec") 3752 deployment := &v1.Deployment{} 3753 Expect(k8sClient.Get(ctx, client.ObjectKey{ 3754 Namespace: app.GetNamespace(), 3755 Name: app.Spec.Components[0].Name, 3756 }, deployment)).Should(BeNil()) 3757 targetReplicas := int32(5) 3758 deployment.Spec.Replicas = &targetReplicas 3759 Expect(k8sClient.Update(ctx, deployment)).Should(BeNil()) 3760 3761 newDeployment := &v1.Deployment{} 3762 Expect(k8sClient.Get(ctx, client.ObjectKey{ 3763 Namespace: app.GetNamespace(), 3764 Name: app.Spec.Components[0].Name, 3765 }, newDeployment)).Should(BeNil()) 3766 Expect(*newDeployment.Spec.Replicas).Should(Equal(targetReplicas)) 3767 Expect(k8sClient.Delete(ctx, newDeployment)).Should(BeNil()) 3768 Expect(k8sClient.Delete(ctx, app)).Should(BeNil()) 3769 }) 3770 3771 It("test application with healthProbe which use https", func() { 3772 ns := &corev1.Namespace{ 3773 ObjectMeta: metav1.ObjectMeta{ 3774 Name: "vela-test-with-httpshealthprobe", 3775 }, 3776 } 3777 Expect(k8sClient.Create(ctx, ns)).Should(BeNil()) 3778 3779 appWithHttpsHealthProbe.SetNamespace(ns.Name) 3780 app := appWithHttpsHealthProbe.DeepCopy() 3781 Expect(k8sClient.Create(ctx, app)).Should(BeNil()) 3782 3783 appKey := client.ObjectKey{ 3784 Name: app.Name, 3785 Namespace: app.Namespace, 3786 } 3787 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 3788 3789 By("Check App running successfully") 3790 curApp := &v1beta1.Application{} 3791 Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil()) 3792 Expect(curApp.Status.Phase).Should(Equal(common.ApplicationRunning)) 3793 3794 appRevision := &v1beta1.ApplicationRevision{} 3795 Expect(k8sClient.Get(ctx, client.ObjectKey{ 3796 Namespace: app.Namespace, 3797 Name: curApp.Status.LatestRevision.Name, 3798 }, appRevision)).Should(BeNil()) 3799 3800 By("Check affiliated resource tracker is created") 3801 expectRTName := fmt.Sprintf("%s-%s", appRevision.GetName(), appRevision.GetNamespace()) 3802 Eventually(func() error { 3803 return k8sClient.Get(ctx, client.ObjectKey{Name: expectRTName}, &v1beta1.ResourceTracker{}) 3804 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 3805 3806 By("Check AppRevision Created with the expected workload spec") 3807 appRev := &v1beta1.ApplicationRevision{} 3808 Eventually(func() error { 3809 return k8sClient.Get(ctx, client.ObjectKey{Name: app.Name + "-v1", Namespace: app.GetNamespace()}, appRev) 3810 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 3811 3812 Expect(k8sClient.Delete(ctx, app)).Should(BeNil()) 3813 }) 3814 3815 It("test application with pod affinity will create application", func() { 3816 3817 ns := &corev1.Namespace{ 3818 ObjectMeta: metav1.ObjectMeta{ 3819 Name: "app-with-affinity", 3820 }, 3821 } 3822 Expect(k8sClient.Create(ctx, ns)).Should(BeNil()) 3823 3824 appWithAffinity.SetNamespace(ns.Name) 3825 app := appWithAffinity.DeepCopy() 3826 Expect(k8sClient.Create(ctx, app)).Should(BeNil()) 3827 3828 appKey := client.ObjectKey{ 3829 Name: app.Name, 3830 Namespace: app.Namespace, 3831 } 3832 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 3833 3834 By("Check App running successfully") 3835 curApp := &v1beta1.Application{} 3836 Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil()) 3837 Expect(curApp.Status.Phase).Should(Equal(common.ApplicationRunning)) 3838 3839 appRevision := &v1beta1.ApplicationRevision{} 3840 Expect(k8sClient.Get(ctx, client.ObjectKey{ 3841 Namespace: app.Namespace, 3842 Name: curApp.Status.LatestRevision.Name, 3843 }, appRevision)).Should(BeNil()) 3844 By("Check affiliated resource tracker is created") 3845 expectRTName := fmt.Sprintf("%s-%s", appRevision.GetName(), appRevision.GetNamespace()) 3846 Eventually(func() error { 3847 return k8sClient.Get(ctx, client.ObjectKey{Name: expectRTName}, &v1beta1.ResourceTracker{}) 3848 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 3849 3850 By("Check AppRevision Created with the expected workload spec") 3851 appRev := &v1beta1.ApplicationRevision{} 3852 Eventually(func() error { 3853 return k8sClient.Get(ctx, client.ObjectKey{Name: app.Name + "-v1", Namespace: app.GetNamespace()}, appRev) 3854 }, 10*time.Second, 500*time.Millisecond).Should(Succeed()) 3855 3856 Expect(k8sClient.Delete(ctx, app)).Should(BeNil()) 3857 }) 3858 3859 It("test cue panic", func() { 3860 ns := &corev1.Namespace{ 3861 ObjectMeta: metav1.ObjectMeta{ 3862 Name: "cue-panic", 3863 }, 3864 } 3865 Expect(k8sClient.Create(ctx, ns)).Should(BeNil()) 3866 3867 appWithMountToEnvs.SetNamespace(ns.Name) 3868 app := appWithMountToEnvs.DeepCopy() 3869 app.Spec.Components[0].Traits = []common.ApplicationTrait{ 3870 { 3871 Type: "panic", 3872 Properties: &runtime.RawExtension{Raw: []byte("{\"configMap\": [{\"name\": \"myweb-cm\"}]}")}, 3873 }, 3874 } 3875 Expect(k8sClient.Create(ctx, app)).Should(BeNil()) 3876 3877 appKey := client.ObjectKey{ 3878 Name: app.Name, 3879 Namespace: app.Namespace, 3880 } 3881 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 3882 3883 curApp := &v1beta1.Application{} 3884 Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil()) 3885 // should not panic anymore, refer to https://github.com/cue-lang/cue/issues/1828 3886 // Expect(curApp.Status.Phase).Should(Equal(common.ApplicationWorkflowFailed)) 3887 Expect(curApp.Status.Phase).Should(Equal(common.ApplicationRunning)) 3888 }) 3889 3890 It("test application with healthy and PreDispatch trait", func() { 3891 defer featuregatetesting.SetFeatureGateDuringTest(&testing.T{}, utilfeature.DefaultFeatureGate, features.MultiStageComponentApply, true)() 3892 By("create the new namespace") 3893 ns := &corev1.Namespace{ 3894 ObjectMeta: metav1.ObjectMeta{ 3895 Name: "app-with-pre-dispatch-healthy", 3896 }, 3897 } 3898 Expect(k8sClient.Create(ctx, ns)).Should(BeNil()) 3899 3900 appWithPreDispatch := appwithNoTrait.DeepCopy() 3901 appWithPreDispatch.Spec.Components[0].Name = "comp-with-pre-dispatch-trait" 3902 appWithPreDispatch.Spec.Components[0].Traits = []common.ApplicationTrait{ 3903 { 3904 Type: "storage-pre-dispatch", 3905 Properties: &runtime.RawExtension{Raw: []byte("{\"configMap\":[{\"name\":\"pre-dispatch-cm\",\"mountPath\":\"/test/mount/cm\",\"data\":{\"firstKey\":\"firstValue\"}}]}")}, 3906 }, 3907 } 3908 appWithPreDispatch.Name = "app-with-pre-dispatch" 3909 appWithPreDispatch.SetNamespace(ns.Name) 3910 Expect(k8sClient.Create(ctx, appWithPreDispatch)).Should(BeNil()) 3911 3912 appKey := client.ObjectKey{ 3913 Name: appWithPreDispatch.Name, 3914 Namespace: appWithPreDispatch.Namespace, 3915 } 3916 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 3917 3918 By("Check App running successfully") 3919 curApp := &v1beta1.Application{} 3920 Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil()) 3921 Expect(curApp.Status.Phase).Should(Equal(common.ApplicationRunning)) 3922 3923 By("Check Manifests Created") 3924 expConfigMap := &corev1.ConfigMap{} 3925 Expect(k8sClient.Get(ctx, types.NamespacedName{ 3926 Name: "pre-dispatch-cm", 3927 Namespace: appWithPreDispatch.Namespace, 3928 }, expConfigMap)).Should(BeNil()) 3929 3930 expDeployment := &v1.Deployment{} 3931 Expect(k8sClient.Get(ctx, client.ObjectKey{ 3932 Name: appWithPreDispatch.Spec.Components[0].Name, 3933 Namespace: appWithPreDispatch.Namespace, 3934 }, expDeployment)).Should(BeNil()) 3935 3936 Expect(k8sClient.Delete(ctx, appWithPreDispatch)).Should(BeNil()) 3937 Expect(k8sClient.Delete(ctx, ns)).Should(BeNil()) 3938 }) 3939 3940 It("test application with unhealthy and PreDispatch trait", func() { 3941 defer featuregatetesting.SetFeatureGateDuringTest(&testing.T{}, utilfeature.DefaultFeatureGate, features.MultiStageComponentApply, true)() 3942 By("create the new namespace") 3943 ns := &corev1.Namespace{ 3944 ObjectMeta: metav1.ObjectMeta{ 3945 Name: "app-with-pre-dispatch-unhealthy", 3946 }, 3947 } 3948 Expect(k8sClient.Create(ctx, ns)).Should(BeNil()) 3949 3950 appWithPreDispatch := appwithNoTrait.DeepCopy() 3951 appWithPreDispatch.Spec.Components[0].Name = "comp-with-pre-dispatch-trait" 3952 appWithPreDispatch.Spec.Components[0].Traits = []common.ApplicationTrait{ 3953 { 3954 Type: "storage-pre-dispatch-unhealthy", 3955 Properties: &runtime.RawExtension{Raw: []byte("{\"configMap\":[{\"name\":\"pre-dispatch-unhealthy-cm\",\"mountPath\":\"/test/mount/cm\",\"data\":{\"firstKey\":\"firstValue\"}}]}")}, 3956 }, 3957 } 3958 appWithPreDispatch.Name = "app-with-pre-dispatch-unhealthy" 3959 appWithPreDispatch.SetNamespace(ns.Name) 3960 Expect(k8sClient.Create(ctx, appWithPreDispatch)).Should(BeNil()) 3961 3962 appKey := client.ObjectKey{ 3963 Name: appWithPreDispatch.Name, 3964 Namespace: appWithPreDispatch.Namespace, 3965 } 3966 testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey}) 3967 3968 By("Check the Application status") 3969 curApp := &v1beta1.Application{} 3970 Expect(k8sClient.Get(ctx, appKey, curApp)).Should(BeNil()) 3971 Expect(curApp.Status.Phase).Should(Equal(common.ApplicationRunningWorkflow)) 3972 3973 By("Check Manifests Created") 3974 expConfigMap := &corev1.ConfigMap{} 3975 Expect(k8sClient.Get(ctx, types.NamespacedName{ 3976 Name: "pre-dispatch-unhealthy-cm", 3977 Namespace: appWithPreDispatch.Namespace, 3978 }, expConfigMap)).Should(BeNil()) 3979 3980 expDeployment := &v1.Deployment{} 3981 Expect(k8sClient.Get(ctx, client.ObjectKey{ 3982 Name: appWithPreDispatch.Spec.Components[0].Name, 3983 Namespace: appWithPreDispatch.Namespace, 3984 }, expDeployment)).Should(util.NotFoundMatcher{}) 3985 3986 Expect(k8sClient.Delete(ctx, appWithPreDispatch)).Should(BeNil()) 3987 Expect(k8sClient.Delete(ctx, ns)).Should(BeNil()) 3988 }) 3989 }) 3990 3991 const ( 3992 componentDefYaml = ` 3993 apiVersion: core.oam.dev/v1beta1 3994 kind: ComponentDefinition 3995 metadata: 3996 name: worker 3997 namespace: vela-system 3998 annotations: 3999 definition.oam.dev/description: "Long-running scalable backend worker without network endpoint" 4000 spec: 4001 workload: 4002 definition: 4003 apiVersion: apps/v1 4004 kind: Deployment 4005 extension: 4006 template: | 4007 output: { 4008 apiVersion: "apps/v1" 4009 kind: "Deployment" 4010 spec: { 4011 selector: matchLabels: { 4012 "app.oam.dev/component": context.name 4013 } 4014 template: { 4015 metadata: labels: { 4016 "app.oam.dev/component": context.name 4017 } 4018 4019 spec: { 4020 containers: [{ 4021 name: context.name 4022 image: parameter.image 4023 4024 if parameter["cmd"] != _|_ { 4025 command: parameter.cmd 4026 } 4027 }] 4028 } 4029 } 4030 4031 selector: 4032 matchLabels: 4033 "app.oam.dev/component": context.name 4034 } 4035 } 4036 4037 parameter: { 4038 // +usage=Which image would you like to use for your service 4039 // +short=i 4040 image: string 4041 4042 cmd?: [...string] 4043 } 4044 ` 4045 4046 unhealthyComponentDefYaml = ` 4047 apiVersion: core.oam.dev/v1beta1 4048 kind: ComponentDefinition 4049 metadata: 4050 name: unhealthy-worker 4051 namespace: vela-system 4052 annotations: 4053 definition.oam.dev/description: "Long-running scalable backend worker without network endpoint" 4054 spec: 4055 workload: 4056 definition: 4057 apiVersion: apps/v1 4058 kind: Deployment 4059 extension: 4060 template: | 4061 output: { 4062 apiVersion: "apps/v1" 4063 kind: "Deployment" 4064 metadata: { 4065 annotations: { 4066 if context["config"] != _|_ { 4067 for _, v in context.config { 4068 "\(v.name)" : v.value 4069 } 4070 } 4071 } 4072 } 4073 spec: { 4074 selector: matchLabels: { 4075 "app.oam.dev/component": context.name 4076 } 4077 template: { 4078 metadata: labels: { 4079 "app.oam.dev/component": context.name 4080 } 4081 4082 spec: { 4083 containers: [{ 4084 name: context.name 4085 image: parameter.image 4086 4087 if parameter["cmd"] != _|_ { 4088 command: parameter.cmd 4089 } 4090 }] 4091 } 4092 } 4093 4094 selector: 4095 matchLabels: 4096 "app.oam.dev/component": context.name 4097 } 4098 } 4099 4100 parameter: { 4101 // +usage=Which image would you like to use for your service 4102 // +short=i 4103 image: string 4104 cmd?: [...string] 4105 } 4106 status: 4107 healthPolicy: |- 4108 isHealth: false 4109 ` 4110 4111 wDImportYaml = ` 4112 apiVersion: core.oam.dev/v1beta1 4113 kind: WorkloadDefinition 4114 metadata: 4115 name: worker-import 4116 namespace: vela-system 4117 annotations: 4118 definition.oam.dev/description: "Long-running scalable backend worker without network endpoint" 4119 spec: 4120 definitionRef: 4121 name: deployments.apps 4122 extension: 4123 template: | 4124 import ( 4125 "k8s.io/apps/v1" 4126 appsv1 "kube/apps/v1" 4127 ) 4128 output: v1.#Deployment & appsv1.#Deployment & { 4129 metadata: { 4130 annotations: { 4131 if context["config"] != _|_ { 4132 for _, v in context.config { 4133 "\(v.name)" : v.value 4134 } 4135 } 4136 } 4137 } 4138 spec: { 4139 selector: matchLabels: { 4140 "app.oam.dev/component": context.name 4141 } 4142 template: { 4143 metadata: labels: { 4144 "app.oam.dev/component": context.name 4145 } 4146 4147 spec: { 4148 containers: [{ 4149 name: context.name 4150 image: parameter.image 4151 4152 if parameter["cmd"] != _|_ { 4153 command: parameter.cmd 4154 } 4155 }] 4156 } 4157 } 4158 4159 selector: 4160 matchLabels: 4161 "app.oam.dev/component": context.name 4162 } 4163 } 4164 4165 parameter: { 4166 // +usage=Which image would you like to use for your service 4167 // +short=i 4168 image: string 4169 4170 cmd?: [...string] 4171 } 4172 ` 4173 4174 tdImportedYaml = `apiVersion: core.oam.dev/v1beta1 4175 kind: TraitDefinition 4176 metadata: 4177 name: ingress-import 4178 namespace: vela-system 4179 spec: 4180 appliesToWorkloads: 4181 - "*" 4182 schematic: 4183 cue: 4184 template: | 4185 import ( 4186 kubev1 "k8s.io/core/v1" 4187 network "k8s.io/networking/v1" 4188 ) 4189 4190 parameter: { 4191 domain: string 4192 http: [string]: int 4193 } 4194 4195 outputs: { 4196 service: kubev1.#Service 4197 ingress: network.#Ingress 4198 } 4199 4200 // trait template can have multiple outputs in one trait 4201 outputs: service: { 4202 metadata: 4203 name: context.name 4204 spec: { 4205 selector: 4206 "app.oam.dev/component": context.name 4207 ports: [ 4208 for k, v in parameter.http { 4209 port: v 4210 targetPort: v 4211 }, 4212 ] 4213 } 4214 } 4215 4216 outputs: ingress: { 4217 metadata: 4218 name: context.name 4219 spec: { 4220 rules: [{ 4221 host: parameter.domain 4222 http: { 4223 paths: [ 4224 for k, v in parameter.http { 4225 path: k 4226 backend: { 4227 serviceName: context.name 4228 servicePort: v 4229 } 4230 }, 4231 ] 4232 } 4233 }] 4234 } 4235 }` 4236 4237 webComponentDefYaml = `apiVersion: core.oam.dev/v1beta1 4238 kind: ComponentDefinition 4239 metadata: 4240 name: webserver 4241 namespace: vela-system 4242 annotations: 4243 definition.oam.dev/description: "webserver was composed by deployment and service" 4244 spec: 4245 workload: 4246 definition: 4247 apiVersion: apps/v1 4248 kind: Deployment 4249 extension: 4250 template: | 4251 output: { 4252 apiVersion: "apps/v1" 4253 kind: "Deployment" 4254 spec: { 4255 selector: matchLabels: { 4256 "app.oam.dev/component": context.name 4257 } 4258 template: { 4259 metadata: labels: { 4260 "app.oam.dev/component": context.name 4261 } 4262 spec: { 4263 containers: [{ 4264 name: context.name 4265 image: parameter.image 4266 4267 if parameter["cmd"] != _|_ { 4268 command: parameter.cmd 4269 } 4270 4271 if parameter["env"] != _|_ { 4272 env: parameter.env 4273 } 4274 4275 if context["config"] != _|_ { 4276 env: context.config 4277 } 4278 4279 ports: [{ 4280 containerPort: parameter.port 4281 }] 4282 4283 if parameter["cpu"] != _|_ { 4284 resources: { 4285 limits: 4286 cpu: parameter.cpu 4287 requests: 4288 cpu: parameter.cpu 4289 } 4290 } 4291 }] 4292 } 4293 } 4294 } 4295 } 4296 // workload can have extra object composition by using 'outputs' keyword 4297 outputs: service: { 4298 apiVersion: "v1" 4299 kind: "Service" 4300 spec: { 4301 selector: { 4302 "app.oam.dev/component": context.name 4303 } 4304 ports: [ 4305 { 4306 port: parameter.port 4307 targetPort: parameter.port 4308 }, 4309 ] 4310 } 4311 } 4312 parameter: { 4313 image: string 4314 cmd?: [...string] 4315 port: *80 | int 4316 env?: [...{ 4317 name: string 4318 value?: string 4319 valueFrom?: { 4320 secretKeyRef: { 4321 name: string 4322 key: string 4323 } 4324 } 4325 }] 4326 cpu?: string 4327 } 4328 4329 ` 4330 componentDefWithHealthYaml = ` 4331 apiVersion: core.oam.dev/v1beta1 4332 kind: ComponentDefinition 4333 metadata: 4334 name: worker 4335 namespace: vela-system 4336 annotations: 4337 definition.oam.dev/description: "Long-running scalable backend worker without network endpoint" 4338 spec: 4339 workload: 4340 definition: 4341 apiVersion: apps/v1 4342 kind: Deployment 4343 extension: 4344 healthPolicy: | 4345 isHealth: context.output.status.readyReplicas == context.output.status.replicas 4346 template: | 4347 output: { 4348 apiVersion: "apps/v1" 4349 kind: "Deployment" 4350 metadata: { 4351 annotations: { 4352 if context["config"] != _|_ { 4353 for _, v in context.config { 4354 "\(v.name)" : v.value 4355 } 4356 } 4357 } 4358 } 4359 spec: { 4360 selector: matchLabels: { 4361 "app.oam.dev/component": context.name 4362 } 4363 template: { 4364 metadata: labels: { 4365 "app.oam.dev/component": context.name 4366 } 4367 4368 spec: { 4369 containers: [{ 4370 name: context.name 4371 image: parameter.image 4372 4373 if parameter["cmd"] != _|_ { 4374 command: parameter.cmd 4375 } 4376 }] 4377 } 4378 } 4379 4380 selector: 4381 matchLabels: 4382 "app.oam.dev/component": context.name 4383 } 4384 } 4385 4386 parameter: { 4387 // +usage=Which image would you like to use for your service 4388 // +short=i 4389 image: string 4390 cmd?: [...string] 4391 } 4392 ` 4393 4394 cdDefWithHealthStatusYaml = `apiVersion: core.oam.dev/v1beta1 4395 kind: ComponentDefinition 4396 metadata: 4397 name: nworker 4398 namespace: vela-system 4399 annotations: 4400 definition.oam.dev/description: "Describes long-running, scalable, containerized services that running at backend. They do NOT have network endpoint to receive external network traffic." 4401 spec: 4402 workload: 4403 definition: 4404 apiVersion: apps/v1 4405 kind: Deployment 4406 status: 4407 healthPolicy: | 4408 isHealth: (context.output.status.readyReplicas > 0) && (context.output.status.readyReplicas == context.output.status.replicas) 4409 customStatus: |- 4410 message: "type: " + context.output.spec.template.spec.containers[0].image + ",\t enemies:" + context.outputs.gameconfig.data.enemies 4411 schematic: 4412 cue: 4413 template: | 4414 output: { 4415 apiVersion: "apps/v1" 4416 kind: "Deployment" 4417 spec: { 4418 selector: matchLabels: { 4419 "app.oam.dev/component": context.name 4420 } 4421 4422 template: { 4423 metadata: labels: { 4424 "app.oam.dev/component": context.name 4425 } 4426 4427 spec: { 4428 containers: [{ 4429 name: context.name 4430 image: parameter.image 4431 envFrom: [{ 4432 configMapRef: name: context.name + "game-config" 4433 }] 4434 if parameter["cmd"] != _|_ { 4435 command: parameter.cmd 4436 } 4437 }] 4438 } 4439 } 4440 } 4441 } 4442 4443 outputs: gameconfig: { 4444 apiVersion: "v1" 4445 kind: "ConfigMap" 4446 metadata: { 4447 name: context.name + "game-config" 4448 } 4449 data: { 4450 enemies: parameter.enemies 4451 lives: parameter.lives 4452 } 4453 } 4454 4455 parameter: { 4456 // +usage=Which image would you like to use for your service 4457 // +short=i 4458 image: string 4459 // +usage=Commands to run in the container 4460 cmd?: [...string] 4461 lives: string 4462 enemies: string 4463 } 4464 ` 4465 workloadDefYaml = ` 4466 apiVersion: core.oam.dev/v1beta1 4467 kind: WorkloadDefinition 4468 metadata: 4469 name: task 4470 namespace: vela-system 4471 annotations: 4472 definition.oam.dev/description: "Describes jobs that run code or a script to completion." 4473 spec: 4474 definitionRef: 4475 name: jobs.batch 4476 schematic: 4477 cue: 4478 template: | 4479 output: { 4480 apiVersion: "batch/v1" 4481 kind: "Job" 4482 spec: { 4483 parallelism: parameter.count 4484 completions: parameter.count 4485 template: spec: { 4486 restartPolicy: parameter.restart 4487 containers: [{ 4488 name: context.name 4489 image: parameter.image 4490 4491 if parameter["cmd"] != _|_ { 4492 command: parameter.cmd 4493 } 4494 }] 4495 } 4496 } 4497 } 4498 parameter: { 4499 // +usage=specify number of tasks to run in parallel 4500 // +short=c 4501 count: *1 | int 4502 4503 // +usage=Which image would you like to use for your service 4504 // +short=i 4505 image: string 4506 4507 // +usage=Define the job restart policy, the value can only be Never or OnFailure. By default, it's Never. 4508 restart: *"Never" | string 4509 4510 // +usage=Commands to run in the container 4511 cmd?: [...string] 4512 } 4513 ` 4514 4515 k8sObjectsComponentDefinitionYaml = ` 4516 apiVersion: core.oam.dev/v1beta1 4517 kind: ComponentDefinition 4518 metadata: 4519 annotations: 4520 definition.oam.dev/description: K8s-objects allow users to specify raw K8s objects in properties 4521 name: k8s-objects 4522 namespace: vela-system 4523 spec: 4524 schematic: 4525 cue: 4526 template: | 4527 output: parameter.objects[0] 4528 outputs: { 4529 for i, v in parameter.objects { 4530 if i > 0 { 4531 "objects-\(i)": v 4532 } 4533 } 4534 } 4535 parameter: objects: [...{}] 4536 ` 4537 applyInParallelWorkflowDefinitionYaml = ` 4538 apiVersion: core.oam.dev/v1beta1 4539 kind: WorkflowStepDefinition 4540 metadata: 4541 name: apply-test 4542 namespace: vela-system 4543 spec: 4544 schematic: 4545 cue: 4546 template: | 4547 import ( 4548 "vela/op" 4549 "list" 4550 ) 4551 4552 components: op.#LoadInOrder & {} 4553 targetComponent: components.value[0] 4554 resources: op.#RenderComponent & { 4555 value: targetComponent 4556 } 4557 workload: resources.output 4558 arr: list.Range(0, parameter.parallelism, 1) 4559 patchWorkloads: op.#Steps & { 4560 for idx in arr { 4561 "\(idx)": op.#PatchK8sObject & { 4562 value: workload 4563 patch: { 4564 // +patchStrategy=retainKeys 4565 metadata: name: "\(targetComponent.name)-\(idx)" 4566 } 4567 } 4568 } 4569 } 4570 workloads: [ for patchResult in patchWorkloads {patchResult.result}] 4571 apply: op.#ApplyInParallel & { 4572 value: workloads 4573 } 4574 parameter: parallelism: int 4575 4576 ` 4577 ) 4578 4579 func Test_isHealthy(t *testing.T) { 4580 tests := []struct { 4581 name string 4582 services []common.ApplicationComponentStatus 4583 want bool 4584 }{ 4585 { 4586 name: "test service unhealthy", 4587 services: []common.ApplicationComponentStatus{ 4588 { 4589 Name: "test", 4590 Healthy: false, 4591 }, 4592 }, 4593 want: false, 4594 }, 4595 { 4596 name: "test trait unhealthy", 4597 services: []common.ApplicationComponentStatus{ 4598 { 4599 Name: "test", 4600 Healthy: true, 4601 Traits: []common.ApplicationTraitStatus{ 4602 { 4603 Type: "expose", 4604 Healthy: false, 4605 }, 4606 { 4607 Type: "scaler", 4608 Healthy: true, 4609 }, 4610 }, 4611 }, 4612 }, 4613 want: false, 4614 }, 4615 { 4616 name: "test service and trait all healthy", 4617 services: []common.ApplicationComponentStatus{ 4618 { 4619 Name: "test", 4620 Healthy: true, 4621 Traits: []common.ApplicationTraitStatus{ 4622 { 4623 Type: "expose", 4624 Healthy: true, 4625 }, 4626 { 4627 Type: "scaler", 4628 Healthy: true, 4629 }, 4630 }, 4631 }, 4632 }, 4633 want: true, 4634 }, 4635 } 4636 for _, tt := range tests { 4637 t.Run(tt.name, func(t *testing.T) { 4638 if got := isHealthy(tt.services); got != tt.want { 4639 t.Errorf("isHealthy() = %v, want %v", got, tt.want) 4640 } 4641 }) 4642 } 4643 } 4644 4645 func Test_setVelaVersion(t *testing.T) { 4646 tests := []struct { 4647 name string 4648 app *v1beta1.Application 4649 validate func(app *v1beta1.Application) bool 4650 }{ 4651 { 4652 name: "test no vela version should add", 4653 app: &v1beta1.Application{}, 4654 validate: func(app *v1beta1.Application) bool { 4655 return app.Annotations != nil && app.Annotations[oam.AnnotationKubeVelaVersion] == version.VelaVersion 4656 }, 4657 }, 4658 { 4659 name: "test has vela version should not add", 4660 app: &v1beta1.Application{ 4661 ObjectMeta: metav1.ObjectMeta{ 4662 Annotations: map[string]string{ 4663 oam.AnnotationKubeVelaVersion: version.VelaVersion, 4664 }, 4665 }, 4666 }, 4667 validate: func(app *v1beta1.Application) bool { 4668 return app.Annotations != nil && app.Annotations[oam.AnnotationKubeVelaVersion] == version.VelaVersion 4669 }, 4670 }, 4671 } 4672 for _, tt := range tests { 4673 t.Run(tt.name, func(t *testing.T) { 4674 setVelaVersion(tt.app) 4675 if tt.validate != nil && !tt.validate(tt.app) { 4676 t.Errorf("setVelaVersion() failed") 4677 } 4678 }) 4679 } 4680 }