github.com/oam-dev/kubevela@v1.9.11/e2e/plugin/plugin_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 plugin
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"time"
    23  
    24  	. "github.com/onsi/ginkgo/v2"
    25  	. "github.com/onsi/gomega"
    26  	"sigs.k8s.io/controller-runtime/pkg/client"
    27  
    28  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    29  	"github.com/oam-dev/kubevela/e2e"
    30  )
    31  
    32  var _ = Describe("Test Kubectl Plugin", func() {
    33  	namespace := "default"
    34  	componentDefName := "test-webservice"
    35  	traitDefName := "test-ingress"
    36  
    37  	Context("Test kubectl vela dry-run", func() {
    38  		It("Test dry-run application use definitions which applied to the cluster", func() {
    39  			By("check definitions which application used whether applied to the cluster")
    40  			var cd v1beta1.ComponentDefinition
    41  			Eventually(func() error {
    42  				err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentDefName}, &cd)
    43  				return err
    44  			}, 5*time.Second, time.Second).Should(BeNil())
    45  
    46  			var td v1beta1.TraitDefinition
    47  			Eventually(func() error {
    48  				err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: traitDefName}, &td)
    49  				return err
    50  			}, 5*time.Second, time.Second).Should(BeNil())
    51  
    52  			By("dry-run application")
    53  			err := os.WriteFile("dry-run-app.yaml", []byte(application), 0644)
    54  			Expect(err).NotTo(HaveOccurred())
    55  			Eventually(func() string {
    56  				output, _ := e2e.Exec("kubectl-vela dry-run -f dry-run-app.yaml -n default -x default")
    57  				return output
    58  			}, 10*time.Second, time.Second).Should(ContainSubstring(dryRunResult))
    59  		})
    60  
    61  		It("Test dry-run application use definitions in local", func() {
    62  			Eventually(func() string {
    63  				output, _ := e2e.Exec("kubectl-vela dry-run -f dry-run-app.yaml -n default -d definitions")
    64  				return output
    65  			}, 10*time.Second, time.Second).Should(ContainSubstring(dryRunResult))
    66  		})
    67  	})
    68  
    69  	Context("Test kubectl vela live-diff", func() {
    70  		applicationName := "test-vela-app"
    71  
    72  		It("Test live-diff application use definition which applied to the cluster", func() {
    73  			By("check definitions which application used whether applied to the cluster")
    74  			var cd v1beta1.ComponentDefinition
    75  			Eventually(func() error {
    76  				err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentDefName}, &cd)
    77  				return err
    78  			}, 5*time.Second).Should(BeNil())
    79  
    80  			var td v1beta1.TraitDefinition
    81  			Eventually(func() error {
    82  				err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: traitDefName}, &td)
    83  				return err
    84  			}, 5*time.Second).Should(BeNil())
    85  
    86  			By("get appRevision")
    87  			var appRev v1beta1.ApplicationRevision
    88  			var appRevName = fmt.Sprintf("%s-v1", applicationName)
    89  			Eventually(func() error {
    90  				err := k8sClient.Get(ctx, client.ObjectKey{Namespace: "default", Name: appRevName}, &appRev)
    91  				return err
    92  			}, 5*time.Second).Should(BeNil())
    93  
    94  			Eventually(func() bool {
    95  				var tempApp v1beta1.Application
    96  				_ = k8sClient.Get(ctx, client.ObjectKey{Namespace: "default", Name: app.Name}, &tempApp)
    97  				return tempApp.Status.LatestRevision != nil
    98  			}, 20*time.Second, time.Second).Should(BeTrue())
    99  
   100  			By("live-diff application")
   101  			err := os.WriteFile("live-diff-app.yaml", []byte(newApplication), 0644)
   102  			Expect(err).NotTo(HaveOccurred())
   103  			output, err := e2e.Exec("kubectl-vela live-diff -f live-diff-app.yaml")
   104  			Expect(err).NotTo(HaveOccurred())
   105  			Expect(output).Should(ContainSubstring(livediffResult))
   106  		})
   107  
   108  		It("Test dry-run application use definitions in local", func() {
   109  			Eventually(func() bool {
   110  				var tempApp v1beta1.Application
   111  				_ = k8sClient.Get(ctx, client.ObjectKey{Namespace: "default", Name: app.Name}, &tempApp)
   112  				return tempApp.Status.LatestRevision != nil
   113  			}, 20*time.Second, time.Second).Should(BeTrue())
   114  
   115  			output, err := e2e.Exec("kubectl-vela live-diff -f live-diff-app.yaml -d definitions")
   116  			Expect(err).NotTo(HaveOccurred())
   117  			Expect(output).Should(ContainSubstring(livediffResult))
   118  		})
   119  	})
   120  
   121  	Context("Test kubectl vela show", func() {
   122  		It("Test show componentDefinition reference", func() {
   123  			cdName := "test-show-task"
   124  			output, err := e2e.Exec(fmt.Sprintf("kubectl-vela show %s -n default", cdName))
   125  			Expect(err).NotTo(HaveOccurred())
   126  			Expect(output).Should(ContainSubstring(showCdResult))
   127  		})
   128  		It("Test show traitDefinition reference", func() {
   129  			tdName := "test-sidecar"
   130  			output, err := e2e.Exec(fmt.Sprintf("kubectl-vela show %s -n default", tdName))
   131  			Expect(err).NotTo(HaveOccurred())
   132  			Expect(output).Should(ContainSubstring(showTdResult))
   133  		})
   134  		It("Test show traitDefinition def with cue single map parameter", func() {
   135  			tdName := "annotations"
   136  			output, err := e2e.Exec(fmt.Sprintf("kubectl-vela show %s -n default", tdName))
   137  			Expect(err).NotTo(HaveOccurred())
   138  			Expect(output).Should(ContainSubstring("map[string]:(null|string)"))
   139  		})
   140  		It("Test show webservice def with cue ignore annotation ", func() {
   141  			tdName := "webservice"
   142  			output, err := e2e.Exec(fmt.Sprintf("kubectl-vela show %s -n default", tdName))
   143  			Expect(err).NotTo(HaveOccurred())
   144  			Expect(output).ShouldNot(ContainSubstring("addRevisionLabel"))
   145  		})
   146  		It("Test show webservice def with cue ignore annotation ", func() {
   147  			tdName := "mywebservice"
   148  			output, err := e2e.Exec(fmt.Sprintf("kubectl-vela show %s -n default", tdName))
   149  			Expect(err).NotTo(HaveOccurred())
   150  			Expect(output).ShouldNot(ContainSubstring("addRevisionLabel"))
   151  			Expect(output).ShouldNot(ContainSubstring("mySecretKey"))
   152  		})
   153  	})
   154  })
   155  
   156  var application = `
   157  apiVersion: core.oam.dev/v1beta1
   158  kind: Application
   159  metadata:
   160    name: test-vela-app
   161    namespace: default
   162  spec:
   163    components:
   164      - name: express-server
   165        type: test-webservice
   166        properties:
   167          image: crccheck/hello-world
   168          port: 80
   169        traits:
   170          - type: test-ingress
   171            properties:
   172              domain: testsvc.example.com
   173              http:
   174                "/": 80
   175  `
   176  
   177  var newApplication = `
   178  apiVersion: core.oam.dev/v1beta1
   179  kind: Application
   180  metadata:
   181    name: test-vela-app
   182    namespace: default
   183  spec:
   184    components:
   185      - name: new-express-server
   186        type: test-webservice
   187        properties:
   188          image: crccheck/hello-world
   189          port: 5000
   190          cpu: "0.5"
   191        traits:
   192          - type: test-ingress
   193            properties:
   194              domain: new-testsvc.example.com
   195              http:
   196                "/": 8080
   197  `
   198  
   199  var componentDef = `
   200  apiVersion: core.oam.dev/v1beta1
   201  kind: ComponentDefinition
   202  metadata:
   203    name: test-webservice
   204    namespace: default
   205    annotations:
   206      definition.oam.dev/description: "Describes long-running, scalable, containerized services that have a stable network endpoint to receive external network traffic from customers."
   207  spec:
   208    workload:
   209      definition:
   210        apiVersion: apps/v1
   211        kind: Deployment
   212    schematic:
   213      cue:
   214        template: |
   215          output: {
   216          	apiVersion: "apps/v1"
   217          	kind:       "Deployment"
   218          	spec: {
   219          		selector: matchLabels: {
   220          			"app.oam.dev/component": context.name
   221          		}
   222          
   223          		template: {
   224          			metadata: labels: {
   225          				"app.oam.dev/component": context.name
   226          				if parameter.addRevisionLabel {
   227          					"app.oam.dev/appRevision": context.appRevision
   228          				}
   229          			}
   230          
   231          			spec: {
   232          				containers: [{
   233          					name:  context.name
   234          					image: parameter.image
   235          
   236          					if parameter["cmd"] != _|_ {
   237          						command: parameter.cmd
   238          					}
   239          
   240          					if parameter["env"] != _|_ {
   241          						env: parameter.env
   242          					}
   243          
   244          					if context["config"] != _|_ {
   245          						env: context.config
   246          					}
   247          
   248          					ports: [{
   249          						containerPort: parameter.port
   250          					}]
   251          
   252          					if parameter["cpu"] != _|_ {
   253          						resources: {
   254          							limits:
   255          								cpu: parameter.cpu
   256          							requests:
   257          								cpu: parameter.cpu
   258          						}
   259          					}
   260          
   261          					if parameter["volumes"] != _|_ {
   262          						volumeMounts: [ for v in parameter.volumes {
   263          							{
   264          								mountPath: v.mountPath
   265          								name:      v.name
   266          							}}]
   267          					}
   268          				}]
   269          
   270          			if parameter["volumes"] != _|_ {
   271          				volumes: [ for v in parameter.volumes {
   272          					{
   273          						name: v.name
   274          						if v.type == "pvc" {
   275          							persistentVolumeClaim: {
   276          								claimName: v.claimName
   277          							}
   278          						}
   279          						if v.type == "configMap" {
   280          							configMap: {
   281          								defaultMode: v.defaultMode
   282          								name:        v.cmName
   283          								if v.items != _|_ {
   284          									items: v.items
   285          								}
   286          							}
   287          						}
   288          						if v.type == "secret" {
   289          							secret: {
   290          								defaultMode: v.defaultMode
   291          								secretName:  v.secretName
   292          								if v.items != _|_ {
   293          									items: v.items
   294          								}
   295          							}
   296          						}
   297          						if v.type == "emptyDir" {
   298          							emptyDir: {
   299          								medium: v.medium
   300          							}
   301          						}
   302          					}}]
   303          			}
   304          		}
   305          		}
   306          	}
   307          }
   308          parameter: {
   309          	// +usage=Which image would you like to use for your service
   310          	// +short=i
   311          	image: string
   312          
   313          	// +usage=Commands to run in the container
   314          	cmd?: [...string]
   315          
   316          	// +usage=Which port do you want customer traffic sent to
   317          	// +short=p
   318          	port: *80 | int
   319          	// +usage=Define arguments by using environment variables
   320          	env?: [...{
   321          		// +usage=Environment variable name
   322          		name: string
   323          		// +usage=The value of the environment variable
   324          		value?: string
   325          		// +usage=Specifies a source the value of this var should come from
   326          		valueFrom?: {
   327          			// +usage=Selects a key of a secret in the pod's namespace
   328          			secretKeyRef: {
   329          				// +usage=The name of the secret in the pod's namespace to select from
   330          				name: string
   331          				// +usage=The key of the secret to select from. Must be a valid secret key
   332          				key: string
   333          			}
   334          		}
   335          	}]
   336  
   337          	cpu?: string
   338  
   339          	// If addRevisionLabel is true, the appRevision label will be added to the underlying pods
   340          	addRevisionLabel: *false | bool
   341  
   342          	// +usage=Declare volumes and volumeMounts
   343          	volumes?: [...{
   344          		name:      string
   345          		mountPath: string
   346          		// +usage=Specify volume type, options: "pvc","configMap","secret","emptyDir"
   347          		type: "pvc" | "configMap" | "secret" | "emptyDir"
   348          		if type == "pvc" {
   349          			claimName: string
   350          		}
   351          		if type == "configMap" {
   352          			defaultMode: *420 | int
   353          			cmName:      string
   354          			items?: [...{
   355          				key:  string
   356          				path: string
   357          				mode: *511 | int
   358          			}]
   359          		}
   360          		if type == "secret" {
   361          			defaultMode: *420 | int
   362          			secretName:  string
   363          			items?: [...{
   364          				key:  string
   365          				path: string
   366          				mode: *511 | int
   367          			}]
   368          		}
   369          		if type == "emptyDir" {
   370          			medium: *"" | "Memory"
   371          		}
   372          	}]
   373          }
   374  
   375  `
   376  
   377  var traitDef = `
   378  apiVersion: core.oam.dev/v1beta1
   379  kind: TraitDefinition
   380  metadata:
   381    annotations:
   382      definition.oam.dev/description: "Configures K8s ingress and service to enable web traffic for your service.
   383      Please use route trait in cap center for advanced usage."
   384    name: test-ingress
   385    namespace: default
   386  spec:
   387    status:
   388      customStatus: |-
   389        if len(context.outputs.ingress.status.loadBalancer.ingress) > 0 {
   390        	message: "Visiting URL: " + context.outputs.ingress.spec.rules[0].host + ", IP: " + context.outputs.ingress.status.loadBalancer.ingress[0].ip
   391        }
   392        if len(context.outputs.ingress.status.loadBalancer.ingress) == 0 {
   393        	message: "No loadBalancer found, visiting by using 'vela port-forward " + context.appName + " --route'\n"
   394        }
   395      healthPolicy: |
   396        isHealth: len(context.outputs.service.spec.clusterIP) > 0
   397    appliesToWorkloads:
   398      - deployments.apps
   399    podDisruptive: false
   400    schematic:
   401      cue:
   402        template: |
   403          parameter: {
   404          	domain: string
   405          	http: [string]: int
   406          }
   407          
   408          // trait template can have multiple outputs in one trait
   409          outputs: service: {
   410          	apiVersion: "v1"
   411          	kind:       "Service"
   412          	metadata:
   413          		name: context.name
   414          	spec: {
   415          		selector: {
   416          			"app.oam.dev/component": context.name
   417          		}
   418          		ports: [
   419          			for k, v in parameter.http {
   420          				port:       v
   421          				targetPort: v
   422          			},
   423          		]
   424          	}
   425          }
   426          
   427          outputs: ingress: {
   428          	apiVersion: "networking.k8s.io/v1beta1"
   429          	kind:       "Ingress"
   430          	metadata:
   431          		name: context.name
   432          	spec: {
   433          		rules: [{
   434          			host: parameter.domain
   435          			http: {
   436          				paths: [
   437          					for k, v in parameter.http {
   438          						path: k
   439          						backend: {
   440          							serviceName: context.name
   441          							servicePort: v
   442          						}
   443          					},
   444          				]
   445          			}
   446          		}]
   447          	}
   448          }
   449          
   450  `
   451  
   452  var traitDefWithKube = `
   453  apiVersion: core.oam.dev/v1beta1
   454  kind: TraitDefinition
   455  metadata:
   456    name: service-kube
   457    namespace: default
   458  spec:
   459    appliesToWorkloads:
   460      - webservice
   461      - worker
   462      - backend
   463    podDisruptive: true
   464    schematic:
   465      kube:
   466        template:
   467          apiVersion: v1
   468          kind: Service
   469          metadata:
   470            name: my-service
   471          spec:
   472            ports:
   473              - protocol: TCP
   474                port: 80
   475                targetPort: 9376
   476        parameters:
   477          - name: targetPort
   478            required: true
   479            type: number
   480            fieldPaths:
   481              - "spec.template.spec.ports[0].targetPort"
   482            description: "target port num for service provider."
   483  `
   484  
   485  var componentWithDeepCue = `
   486  # Test for deeper parameter in cue Template
   487  apiVersion: core.oam.dev/v1beta1
   488  kind: ComponentDefinition
   489  metadata:
   490   name: mywebservice
   491   namespace: default
   492   annotations:
   493     definition.oam.dev/description: "Describes long-running, scalable, containerized services that have a stable network endpoint to receive external network traffic from customers."
   494  spec:
   495   workload:
   496     definition:
   497       apiVersion: apps/v1
   498       kind: Deployment
   499   schematic:
   500     cue:
   501       template: |
   502         output: {
   503         	apiVersion: "apps/v1"
   504         	kind:       "Deployment"
   505         	spec: {
   506         		selector: matchLabels: {
   507         			"app.oam.dev/component": context.name
   508         		}
   509  
   510         		template: {
   511         			metadata: labels: {
   512         				"app.oam.dev/component": context.name
   513         				if parameter.addRevisionLabel {
   514         					"app.oam.dev/appRevision": context.appRevision
   515         				}
   516         			}
   517  
   518         			spec: {
   519         				containers: [{
   520         					name:  context.name
   521         					image: parameter.image
   522  
   523         					if parameter["env"] != _|_ {
   524         						env: parameter.env
   525         					}
   526         				}]
   527         		    }
   528         	    }
   529             }
   530         }
   531         parameter: {
   532         	// +usage=Which image would you like to use for your service
   533         	// +short=i
   534         	image: string
   535  
   536         	// +ignore
   537         	// +usage=If addRevisionLabel is true, the appRevision label will be added to the underlying pods
   538         	addRevisionLabel: *false | bool
   539  
   540         	// +usage=Define arguments by using environment variables
   541         	env?: [...{
   542         		// +usage=Environment variable name
   543         		name: string
   544         		// +usage=The value of the environment variable
   545         		value?: string
   546         		// +usage=Specifies a source the value of this var should come from
   547         		valueFrom?: {
   548         			// +usage=Selects a key of a secret in the pod's namespace
   549         			secretKeyRef: {
   550         				// +usage=The name of the secret in the pod's namespace to select from
   551         				name: string
   552                         // +ignore
   553         				// +usage=The key of the secret to select from. Must be a valid secret key
   554         				mySecretKey: string
   555         			}
   556         		}
   557         	}]
   558         }
   559  `
   560  
   561  var dryRunResult = `---
   562  # Application(test-vela-app) -- Component(express-server) 
   563  ---
   564  
   565  apiVersion: apps/v1
   566  kind: Deployment
   567  metadata:
   568    annotations: {}
   569    labels:
   570      app.oam.dev/appRevision: ""
   571      app.oam.dev/component: express-server
   572      app.oam.dev/name: test-vela-app
   573      app.oam.dev/namespace: default
   574      app.oam.dev/resourceType: WORKLOAD
   575      workload.oam.dev/type: test-webservice
   576    name: express-server
   577    namespace: default
   578  spec:
   579    selector:
   580      matchLabels:
   581        app.oam.dev/component: express-server
   582    template:
   583      metadata:
   584        labels:
   585          app.oam.dev/component: express-server
   586      spec:
   587        containers:
   588        - image: crccheck/hello-world
   589          name: express-server
   590          ports:
   591          - containerPort: 80
   592  
   593  ---
   594  ## From the trait test-ingress 
   595  apiVersion: v1
   596  kind: Service
   597  metadata:
   598    annotations: {}
   599    labels:
   600      app.oam.dev/appRevision: ""
   601      app.oam.dev/component: express-server
   602      app.oam.dev/name: test-vela-app
   603      app.oam.dev/namespace: default
   604      app.oam.dev/resourceType: TRAIT
   605      trait.oam.dev/resource: service
   606      trait.oam.dev/type: test-ingress
   607    name: express-server
   608    namespace: default
   609  spec:
   610    ports:
   611    - port: 80
   612      targetPort: 80
   613    selector:
   614      app.oam.dev/component: express-server
   615  
   616  ---
   617  ## From the trait test-ingress 
   618  apiVersion: networking.k8s.io/v1beta1
   619  kind: Ingress
   620  metadata:
   621    annotations: {}
   622    labels:
   623      app.oam.dev/appRevision: ""
   624      app.oam.dev/component: express-server
   625      app.oam.dev/name: test-vela-app
   626      app.oam.dev/namespace: default
   627      app.oam.dev/resourceType: TRAIT
   628      trait.oam.dev/resource: ingress
   629      trait.oam.dev/type: test-ingress
   630    name: express-server
   631    namespace: default
   632  spec:
   633    rules:
   634    - host: testsvc.example.com
   635      http:
   636        paths:
   637        - backend:
   638            serviceName: express-server
   639            servicePort: 80
   640          path: /
   641  
   642  ---
   643  `
   644  
   645  var livediffResult = `Application (test-vela-app) has been modified(*)
   646    apiVersion: core.oam.dev/v1beta1
   647    kind: Application
   648    metadata:
   649      creationTimestamp: null
   650  -   finalizers:
   651  -   - app.oam.dev/resource-tracker-finalizer
   652      name: test-vela-app
   653      namespace: default
   654    spec:
   655      components:
   656  -   - name: express-server
   657  +   - name: new-express-server
   658        properties:
   659  +       cpu: "0.5"
   660          image: crccheck/hello-world
   661  -       port: 80
   662  +       port: 5000
   663        traits:
   664        - properties:
   665  -         domain: testsvc.example.com
   666  +         domain: new-testsvc.example.com
   667            http:
   668  -           /: 80
   669  +           /: 8080
   670          type: test-ingress
   671        type: test-webservice
   672    status: {}
   673    
   674  * Component (express-server) has been removed(-)
   675  - apiVersion: apps/v1
   676  - kind: Deployment
   677  - metadata:
   678  -   labels:
   679  -     app.oam.dev/component: express-server
   680  -     app.oam.dev/name: test-vela-app
   681  -     app.oam.dev/namespace: default
   682  -     app.oam.dev/resourceType: WORKLOAD
   683  -     workload.oam.dev/type: test-webservice
   684  -   name: express-server
   685  -   namespace: default
   686  - spec:
   687  -   selector:
   688  -     matchLabels:
   689  -       app.oam.dev/component: express-server
   690  -   template:
   691  -     metadata:
   692  -       labels:
   693  -         app.oam.dev/component: express-server
   694  -     spec:
   695  -       containers:
   696  -       - image: crccheck/hello-world
   697  -         name: express-server
   698  -         ports:
   699  -         - containerPort: 80
   700    
   701  * Component (express-server) / Trait (test-ingress/service) has been removed(-)
   702  - apiVersion: v1
   703  - kind: Service
   704  - metadata:
   705  -   labels:
   706  -     app.oam.dev/component: express-server
   707  -     app.oam.dev/name: test-vela-app
   708  -     app.oam.dev/namespace: default
   709  -     app.oam.dev/resourceType: TRAIT
   710  -     trait.oam.dev/resource: service
   711  -     trait.oam.dev/type: test-ingress
   712  -   name: express-server
   713  -   namespace: default
   714  - spec:
   715  -   ports:
   716  -   - port: 80
   717  -     targetPort: 80
   718  -   selector:
   719  -     app.oam.dev/component: express-server
   720    
   721  * Component (express-server) / Trait (test-ingress/ingress) has been removed(-)
   722  - apiVersion: networking.k8s.io/v1beta1
   723  - kind: Ingress
   724  - metadata:
   725  -   labels:
   726  -     app.oam.dev/component: express-server
   727  -     app.oam.dev/name: test-vela-app
   728  -     app.oam.dev/namespace: default
   729  -     app.oam.dev/resourceType: TRAIT
   730  -     trait.oam.dev/resource: ingress
   731  -     trait.oam.dev/type: test-ingress
   732  -   name: express-server
   733  -   namespace: default
   734  - spec:
   735  -   rules:
   736  -   - host: testsvc.example.com
   737  -     http:
   738  -       paths:
   739  -       - backend:
   740  -           serviceName: express-server
   741  -           servicePort: 80
   742  -         path: /
   743    
   744  * Component (new-express-server) has been added(+)
   745  + apiVersion: apps/v1
   746  + kind: Deployment
   747  + metadata:
   748  +   labels:
   749  +     app.oam.dev/component: new-express-server
   750  +     app.oam.dev/name: test-vela-app
   751  +     app.oam.dev/namespace: default
   752  +     app.oam.dev/resourceType: WORKLOAD
   753  +     workload.oam.dev/type: test-webservice
   754  +   name: new-express-server
   755  +   namespace: default
   756  + spec:
   757  +   selector:
   758  +     matchLabels:
   759  +       app.oam.dev/component: new-express-server
   760  +   template:
   761  +     metadata:
   762  +       labels:
   763  +         app.oam.dev/component: new-express-server
   764  +     spec:
   765  +       containers:
   766  +       - image: crccheck/hello-world
   767  +         name: new-express-server
   768  +         ports:
   769  +         - containerPort: 5000
   770  +         resources:
   771  +           limits:
   772  +             cpu: "0.5"
   773  +           requests:
   774  +             cpu: "0.5"
   775    
   776  * Component (new-express-server) / Trait (test-ingress/service) has been added(+)
   777  + apiVersion: v1
   778  + kind: Service
   779  + metadata:
   780  +   labels:
   781  +     app.oam.dev/component: new-express-server
   782  +     app.oam.dev/name: test-vela-app
   783  +     app.oam.dev/namespace: default
   784  +     app.oam.dev/resourceType: TRAIT
   785  +     trait.oam.dev/resource: service
   786  +     trait.oam.dev/type: test-ingress
   787  +   name: new-express-server
   788  +   namespace: default
   789  + spec:
   790  +   ports:
   791  +   - port: 8080
   792  +     targetPort: 8080
   793  +   selector:
   794  +     app.oam.dev/component: new-express-server
   795    
   796  * Component (new-express-server) / Trait (test-ingress/ingress) has been added(+)
   797  + apiVersion: networking.k8s.io/v1beta1
   798  + kind: Ingress
   799  + metadata:
   800  +   labels:
   801  +     app.oam.dev/component: new-express-server
   802  +     app.oam.dev/name: test-vela-app
   803  +     app.oam.dev/namespace: default
   804  +     app.oam.dev/resourceType: TRAIT
   805  +     trait.oam.dev/resource: ingress
   806  +     trait.oam.dev/type: test-ingress
   807  +   name: new-express-server
   808  +   namespace: default
   809  + spec:
   810  +   rules:
   811  +   - host: new-testsvc.example.com
   812  +     http:
   813  +       paths:
   814  +       - backend:
   815  +           serviceName: new-express-server
   816  +           servicePort: 8080
   817  +         path: /
   818  `
   819  
   820  var testShowComponentDef = `
   821  apiVersion: core.oam.dev/v1beta1
   822  kind: ComponentDefinition
   823  metadata:
   824    name: test-show-task
   825    namespace: vela-system
   826  spec:
   827    workload:
   828      definition:
   829        apiVersion: batch/v1
   830        kind: Job
   831    schematic:
   832      cue:
   833        template: |
   834          output: {
   835          	apiVersion: "batch/v1"
   836          	kind:       "Job"
   837          	spec: {
   838          		parallelism: parameter.count
   839          		completions: parameter.count
   840          		template: spec: {
   841          			restartPolicy: parameter.restart
   842          			containers: [{
   843          				name:  context.name
   844          				image: parameter.image
   845          
   846          				if parameter["cmd"] != _|_ {
   847          					command: parameter.cmd
   848          				}
   849          			}]
   850          		}
   851          	}
   852          }
   853          parameter: {
   854          	// +usage=specify number of tasks to run in parallel
   855          	// +short=c
   856          	count: *1 | int
   857          
   858          	// +usage=Which image would you like to use for your service
   859          	// +short=i
   860          	image: string
   861          
   862          	// +usage=Define the job restart policy, the value can only be Never or OnFailure. By default, it's Never.
   863          	restart: *"Never" | string
   864          
   865          	// +usage=Commands to run in the container
   866          	cmd?: [...string]
   867          }
   868  `
   869  
   870  var testShowTraitDef = `
   871  apiVersion: core.oam.dev/v1beta1
   872  kind: TraitDefinition
   873  metadata:
   874    name: test-sidecar
   875    namespace: vela-system
   876  spec:
   877    appliesToWorkloads:
   878      - deployments.apps
   879    schematic:
   880      cue:
   881        template: |-
   882          patch: {
   883          	// +patchKey=name
   884          	spec: template: spec: containers: [parameter]
   885          }
   886          parameter: {
   887          	name:  string
   888          	image: string
   889          	command?: [...string]
   890          }
   891  `
   892  
   893  var showCdResult = `# Specification
   894  +---------+--------------------------------------------------------------------------------------------------+----------+----------+---------+
   895  |  NAME   |                                           DESCRIPTION                                            |   TYPE   | REQUIRED | DEFAULT |
   896  +---------+--------------------------------------------------------------------------------------------------+----------+----------+---------+
   897  | count   | specify number of tasks to run in parallel.                                                      | int      | false    |       1 |
   898  | image   | Which image would you like to use for your service.                                              | string   | true     |         |
   899  | restart | Define the job restart policy, the value can only be Never or OnFailure. By default, it's Never. | string   | false    | Never   |
   900  | cmd     | Commands to run in the container.                                                                | []string | false    |         |
   901  +---------+--------------------------------------------------------------------------------------------------+----------+----------+---------+
   902  
   903  
   904  `
   905  
   906  var showTdResult = `# Specification
   907  +---------+-------------+----------+----------+---------+
   908  |  NAME   | DESCRIPTION |   TYPE   | REQUIRED | DEFAULT |
   909  +---------+-------------+----------+----------+---------+
   910  | name    |             | string   | true     |         |
   911  | image   |             | string   | true     |         |
   912  | command |             | []string | false    |         |
   913  +---------+-------------+----------+----------+---------+
   914  
   915  
   916  `