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  }