github.com/oam-dev/kubevela@v1.9.11/pkg/controller/core.oam.dev/v1beta1/application/generator_test.go (about)

     1  /*Copyright 2021 The KubeVela Authors.
     2  
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7      http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package application
    17  
    18  import (
    19  	"context"
    20  	"encoding/json"
    21  	"strconv"
    22  	"time"
    23  
    24  	. "github.com/onsi/ginkgo/v2"
    25  	. "github.com/onsi/gomega"
    26  	corev1 "k8s.io/api/core/v1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/runtime"
    29  	"sigs.k8s.io/yaml"
    30  
    31  	monitorContext "github.com/kubevela/pkg/monitor/context"
    32  	workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
    33  
    34  	"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
    35  	oamcore "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    36  	"github.com/oam-dev/kubevela/pkg/oam/util"
    37  )
    38  
    39  var _ = Describe("Test Application workflow generator", func() {
    40  	var namespaceName string
    41  	var ns corev1.Namespace
    42  	var ctx context.Context
    43  
    44  	BeforeEach(func() {
    45  		namespaceName = "generate-test-" + strconv.Itoa(time.Now().Second()) + "-" + strconv.Itoa(time.Now().Nanosecond())
    46  		ctx = context.WithValue(context.TODO(), util.AppDefinitionNamespace, namespaceName)
    47  		ns = corev1.Namespace{
    48  			ObjectMeta: metav1.ObjectMeta{
    49  				Name: namespaceName,
    50  			},
    51  		}
    52  		By("Create the Namespace for test")
    53  		Expect(k8sClient.Create(ctx, &ns)).Should(Succeed())
    54  
    55  		healthComponentDef := &oamcore.ComponentDefinition{}
    56  		hCDefJson, _ := yaml.YAMLToJSON([]byte(cdDefWithHealthStatusYaml))
    57  		Expect(json.Unmarshal(hCDefJson, healthComponentDef)).Should(BeNil())
    58  		healthComponentDef.Name = "worker-with-health"
    59  		healthComponentDef.Namespace = namespaceName
    60  
    61  		By("Create the Component Definition for test")
    62  		Expect(k8sClient.Create(ctx, healthComponentDef)).Should(Succeed())
    63  	})
    64  
    65  	AfterEach(func() {
    66  		By("[TEST] Clean up resources after an integration test")
    67  		Expect(k8sClient.Delete(context.TODO(), &ns)).Should(Succeed())
    68  	})
    69  
    70  	It("Test generate application workflow with inputs and outputs", func() {
    71  		app := &oamcore.Application{
    72  			TypeMeta: metav1.TypeMeta{
    73  				Kind:       "Application",
    74  				APIVersion: "core.oam.dev/v1beta1",
    75  			},
    76  			ObjectMeta: metav1.ObjectMeta{
    77  				Name:      "app-with-input-output",
    78  				Namespace: namespaceName,
    79  			},
    80  			Spec: oamcore.ApplicationSpec{
    81  				Components: []common.ApplicationComponent{
    82  					{
    83  						Name:       "myweb1",
    84  						Type:       "worker-with-health",
    85  						Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
    86  						Inputs: workflowv1alpha1.StepInputs{
    87  							{
    88  								From:         "message",
    89  								ParameterKey: "properties.enemies",
    90  							},
    91  							{
    92  								From:         "message",
    93  								ParameterKey: "properties.lives",
    94  							},
    95  						},
    96  					},
    97  					{
    98  						Name:       "myweb2",
    99  						Type:       "worker-with-health",
   100  						Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)},
   101  						Outputs: workflowv1alpha1.StepOutputs{
   102  							{
   103  								Name:      "message",
   104  								ValueFrom: "output.status.conditions[0].message+\",\"+outputs.gameconfig.data.lives",
   105  							},
   106  						},
   107  					},
   108  				},
   109  			},
   110  		}
   111  		af, err := appParser.GenerateAppFile(ctx, app)
   112  		Expect(err).Should(BeNil())
   113  		_, err = af.GeneratePolicyManifests(context.Background())
   114  		Expect(err).Should(BeNil())
   115  
   116  		handler, err := NewAppHandler(ctx, reconciler, app)
   117  		Expect(err).Should(Succeed())
   118  
   119  		logCtx := monitorContext.NewTraceContext(ctx, "")
   120  		handler.currentAppRev = &oamcore.ApplicationRevision{}
   121  		handler.CheckWorkflowRestart(logCtx, app)
   122  
   123  		_, taskRunner, err := handler.GenerateApplicationSteps(logCtx, app, appParser, af)
   124  		Expect(err).To(BeNil())
   125  		Expect(len(taskRunner)).Should(BeEquivalentTo(2))
   126  		Expect(taskRunner[0].Name()).Should(BeEquivalentTo("myweb1"))
   127  		Expect(taskRunner[1].Name()).Should(BeEquivalentTo("myweb2"))
   128  	})
   129  
   130  	It("Test generate application workflow without inputs and outputs", func() {
   131  		app := &oamcore.Application{
   132  			TypeMeta: metav1.TypeMeta{
   133  				Kind:       "Application",
   134  				APIVersion: "core.oam.dev/v1beta1",
   135  			},
   136  			ObjectMeta: metav1.ObjectMeta{
   137  				Name:      "app-without-input-output",
   138  				Namespace: namespaceName,
   139  			},
   140  			Spec: oamcore.ApplicationSpec{
   141  				Components: []common.ApplicationComponent{
   142  					{
   143  						Name:       "myweb1",
   144  						Type:       "worker-with-health",
   145  						Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
   146  					},
   147  					{
   148  						Name:       "myweb2",
   149  						Type:       "worker-with-health",
   150  						Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)},
   151  					},
   152  				},
   153  			},
   154  		}
   155  		af, err := appParser.GenerateAppFile(ctx, app)
   156  		Expect(err).Should(BeNil())
   157  		_, err = af.GeneratePolicyManifests(context.Background())
   158  		Expect(err).Should(BeNil())
   159  
   160  		handler, err := NewAppHandler(ctx, reconciler, app)
   161  		Expect(err).Should(Succeed())
   162  
   163  		logCtx := monitorContext.NewTraceContext(ctx, "")
   164  		handler.currentAppRev = &oamcore.ApplicationRevision{}
   165  		handler.CheckWorkflowRestart(logCtx, app)
   166  		_, taskRunner, err := handler.GenerateApplicationSteps(logCtx, app, appParser, af)
   167  		Expect(err).To(BeNil())
   168  		Expect(len(taskRunner)).Should(BeEquivalentTo(2))
   169  		Expect(taskRunner[0].Name()).Should(BeEquivalentTo("myweb1"))
   170  		Expect(taskRunner[1].Name()).Should(BeEquivalentTo("myweb2"))
   171  	})
   172  
   173  	It("Test generate application workflow with dependsOn", func() {
   174  		app := &oamcore.Application{
   175  			TypeMeta: metav1.TypeMeta{
   176  				Kind:       "Application",
   177  				APIVersion: "core.oam.dev/v1beta1",
   178  			},
   179  			ObjectMeta: metav1.ObjectMeta{
   180  				Name:      "app-with-input-output",
   181  				Namespace: namespaceName,
   182  			},
   183  			Spec: oamcore.ApplicationSpec{
   184  				Components: []common.ApplicationComponent{
   185  					{
   186  						Name:       "myweb1",
   187  						Type:       "worker-with-health",
   188  						Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
   189  					},
   190  					{
   191  						Name:       "myweb2",
   192  						Type:       "worker-with-health",
   193  						DependsOn:  []string{"myweb1"},
   194  						Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)},
   195  					},
   196  				},
   197  			},
   198  		}
   199  		af, err := appParser.GenerateAppFile(ctx, app)
   200  		Expect(err).Should(BeNil())
   201  
   202  		handler, err := NewAppHandler(ctx, reconciler, app)
   203  		Expect(err).Should(Succeed())
   204  
   205  		logCtx := monitorContext.NewTraceContext(ctx, "")
   206  		handler.currentAppRev = &oamcore.ApplicationRevision{}
   207  		handler.CheckWorkflowRestart(logCtx, app)
   208  		_, taskRunner, err := handler.GenerateApplicationSteps(logCtx, app, appParser, af)
   209  		Expect(err).To(BeNil())
   210  		Expect(len(taskRunner)).Should(BeEquivalentTo(2))
   211  		Expect(taskRunner[0].Name()).Should(BeEquivalentTo("myweb1"))
   212  		Expect(taskRunner[1].Name()).Should(BeEquivalentTo("myweb2"))
   213  	})
   214  
   215  	It("Test generate application workflow with invalid dependsOn", func() {
   216  		app := &oamcore.Application{
   217  			TypeMeta: metav1.TypeMeta{
   218  				Kind:       "Application",
   219  				APIVersion: "core.oam.dev/v1beta1",
   220  			},
   221  			ObjectMeta: metav1.ObjectMeta{
   222  				Name:      "app-with-input-output",
   223  				Namespace: namespaceName,
   224  			},
   225  			Spec: oamcore.ApplicationSpec{
   226  				Components: []common.ApplicationComponent{
   227  					{
   228  						Name:       "myweb1",
   229  						Type:       "worker-with-health",
   230  						Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
   231  					},
   232  					{
   233  						Name:       "myweb2",
   234  						Type:       "worker-with-health",
   235  						DependsOn:  []string{"myweb0"},
   236  						Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)},
   237  					},
   238  				},
   239  			},
   240  		}
   241  		af, err := appParser.GenerateAppFile(ctx, app)
   242  		Expect(err).Should(BeNil())
   243  
   244  		handler, err := NewAppHandler(ctx, reconciler, app)
   245  		Expect(err).Should(Succeed())
   246  
   247  		logCtx := monitorContext.NewTraceContext(ctx, "")
   248  		handler.currentAppRev = &oamcore.ApplicationRevision{}
   249  		handler.CheckWorkflowRestart(logCtx, app)
   250  		_, _, err = handler.GenerateApplicationSteps(logCtx, app, appParser, af)
   251  		Expect(err).NotTo(BeNil())
   252  	})
   253  
   254  	It("Test generate application workflow with multiple invalid dependsOn", func() {
   255  		app := &oamcore.Application{
   256  			TypeMeta: metav1.TypeMeta{
   257  				Kind:       "Application",
   258  				APIVersion: "core.oam.dev/v1beta1",
   259  			},
   260  			ObjectMeta: metav1.ObjectMeta{
   261  				Name:      "app-with-input-output",
   262  				Namespace: namespaceName,
   263  			},
   264  			Spec: oamcore.ApplicationSpec{
   265  				Components: []common.ApplicationComponent{
   266  					{
   267  						Name:       "myweb1",
   268  						Type:       "worker-with-health",
   269  						Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
   270  					},
   271  					{
   272  						Name:       "myweb2",
   273  						Type:       "worker-with-health",
   274  						DependsOn:  []string{"myweb1", "myweb0", "myweb3"},
   275  						Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)},
   276  					},
   277  				},
   278  			},
   279  		}
   280  		af, err := appParser.GenerateAppFile(ctx, app)
   281  		Expect(err).Should(BeNil())
   282  
   283  		handler, err := NewAppHandler(ctx, reconciler, app)
   284  		Expect(err).Should(Succeed())
   285  
   286  		logCtx := monitorContext.NewTraceContext(ctx, "")
   287  		handler.currentAppRev = &oamcore.ApplicationRevision{}
   288  		handler.CheckWorkflowRestart(logCtx, app)
   289  		_, _, err = handler.GenerateApplicationSteps(logCtx, app, appParser, af)
   290  		Expect(err).NotTo(BeNil())
   291  	})
   292  })