github.com/oam-dev/kubevela@v1.9.11/references/cli/revision_test.go (about)

     1  /*
     2   Copyright 2022 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 cli
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"fmt"
    23  	"os"
    24  	"strings"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/kubevela/pkg/util/compression"
    29  	"github.com/stretchr/testify/assert"
    30  
    31  	"github.com/oam-dev/kubevela/apis/types"
    32  
    33  	. "github.com/onsi/ginkgo/v2"
    34  	. "github.com/onsi/gomega"
    35  	v1 "k8s.io/api/core/v1"
    36  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    37  	"sigs.k8s.io/yaml"
    38  
    39  	common2 "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
    40  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    41  	"github.com/oam-dev/kubevela/pkg/oam"
    42  	"github.com/oam-dev/kubevela/pkg/utils/common"
    43  )
    44  
    45  var compDef string = `apiVersion: core.oam.dev/v1beta1
    46  kind: ComponentDefinition
    47  metadata:
    48    annotations:
    49      definition.oam.dev/description: Describes long-running, scalable, containerized
    50        services that have a stable network endpoint to receive external network traffic
    51        from customers.
    52      meta.helm.sh/release-name: kubevela
    53      meta.helm.sh/release-namespace: vela-system
    54    creationTimestamp: null
    55    labels:
    56      app.kubernetes.io/managed-by: Helm
    57    name: webservice
    58    namespace: vela-system
    59  spec:
    60    schematic:
    61      cue:
    62        template: "import (\n\t\"strconv\"\n)\n\nmountsArray: {\n\tpvc: *[\n\t\tfor
    63          v in parameter.volumeMounts.pvc {\n\t\t\t{\n\t\t\t\tmountPath: v.mountPath\n\t\t\t\tif
    64          v.subPath != _|_ {\n\t\t\t\t\tsubPath: v.subPath\n\t\t\t\t}\n\t\t\t\tname:
    65          v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\tconfigMap: *[\n\t\t\tfor v in parameter.volumeMounts.configMap
    66          {\n\t\t\t{\n\t\t\t\tmountPath: v.mountPath\n\t\t\t\tif v.subPath != _|_ {\n\t\t\t\t\tsubPath:
    67          v.subPath\n\t\t\t\t}\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\tsecret:
    68          *[\n\t\tfor v in parameter.volumeMounts.secret {\n\t\t\t{\n\t\t\t\tmountPath:
    69          v.mountPath\n\t\t\t\tif v.subPath != _|_ {\n\t\t\t\t\tsubPath: v.subPath\n\t\t\t\t}\n\t\t\t\tname:
    70          v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\temptyDir: *[\n\t\t\tfor v in parameter.volumeMounts.emptyDir
    71          {\n\t\t\t{\n\t\t\t\tmountPath: v.mountPath\n\t\t\t\tif v.subPath != _|_ {\n\t\t\t\t\tsubPath:
    72          v.subPath\n\t\t\t\t}\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\thostPath:
    73          *[\n\t\t\tfor v in parameter.volumeMounts.hostPath {\n\t\t\t{\n\t\t\t\tmountPath:
    74          v.mountPath\n\t\t\t\tif v.subPath != _|_ {\n\t\t\t\t\tsubPath: v.subPath\n\t\t\t\t}\n\t\t\t\tname:
    75          v.name\n\t\t\t}\n\t\t},\n\t] | []\n}\nvolumesArray: {\n\tpvc: *[\n\t\tfor
    76          v in parameter.volumeMounts.pvc {\n\t\t\t{\n\t\t\t\tname: v.name\n\t\t\t\tpersistentVolumeClaim:
    77          claimName: v.claimName\n\t\t\t}\n\t\t},\n\t] | []\n\n\tconfigMap: *[\n\t\t\tfor
    78          v in parameter.volumeMounts.configMap {\n\t\t\t{\n\t\t\t\tname: v.name\n\t\t\t\tconfigMap:
    79          {\n\t\t\t\t\tdefaultMode: v.defaultMode\n\t\t\t\t\tname:        v.cmName\n\t\t\t\t\tif
    80          v.items != _|_ {\n\t\t\t\t\t\titems: v.items\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t]
    81          | []\n\n\tsecret: *[\n\t\tfor v in parameter.volumeMounts.secret {\n\t\t\t{\n\t\t\t\tname:
    82          v.name\n\t\t\t\tsecret: {\n\t\t\t\t\tdefaultMode: v.defaultMode\n\t\t\t\t\tsecretName:
    83          \ v.secretName\n\t\t\t\t\tif v.items != _|_ {\n\t\t\t\t\t\titems: v.items\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t]
    84          | []\n\n\temptyDir: *[\n\t\t\tfor v in parameter.volumeMounts.emptyDir {\n\t\t\t{\n\t\t\t\tname:
    85          v.name\n\t\t\t\temptyDir: medium: v.medium\n\t\t\t}\n\t\t},\n\t] | []\n\n\thostPath:
    86          *[\n\t\t\tfor v in parameter.volumeMounts.hostPath {\n\t\t\t{\n\t\t\t\tname:
    87          v.name\n\t\t\t\thostPath: path: v.path\n\t\t\t}\n\t\t},\n\t] | []\n}\nvolumesList:
    88          volumesArray.pvc + volumesArray.configMap + volumesArray.secret + volumesArray.emptyDir
    89          + volumesArray.hostPath\ndeDupVolumesArray: [\n\tfor val in [\n\t\tfor i,
    90          vi in volumesList {\n\t\t\tfor j, vj in volumesList if j < i && vi.name ==
    91          vj.name {\n\t\t\t\t_ignore: true\n\t\t\t}\n\t\t\tvi\n\t\t},\n\t] if val._ignore
    92          == _|_ {\n\t\tval\n\t},\n]\noutput: {\n\tapiVersion: \"apps/v1\"\n\tkind:
    93          \      \"Deployment\"\n\tspec: {\n\t\tselector: matchLabels: \"app.oam.dev/component\":
    94          context.name\n\n\t\ttemplate: {\n\t\t\tmetadata: {\n\t\t\t\tlabels: {\n\t\t\t\t\tif
    95          parameter.labels != _|_ {\n\t\t\t\t\t\tparameter.labels\n\t\t\t\t\t}\n\t\t\t\t\tif
    96          parameter.addRevisionLabel {\n\t\t\t\t\t\t\"app.oam.dev/revision\": context.revision\n\t\t\t\t\t}\n\t\t\t\t\t\"app.oam.dev/name\":
    97          \     context.appName\n\t\t\t\t\t\"app.oam.dev/component\": context.name\n\t\t\t\t}\n\t\t\t\tif
    98          parameter.annotations != _|_ {\n\t\t\t\t\tannotations: parameter.annotations\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tspec:
    99          {\n\t\t\t\tcontainers: [{\n\t\t\t\t\tname:  context.name\n\t\t\t\t\timage:
   100          parameter.image\n\t\t\t\t\tif parameter[\"port\"] != _|_ && parameter[\"ports\"]
   101          == _|_ {\n\t\t\t\t\t\tports: [{\n\t\t\t\t\t\t\tcontainerPort: parameter.port\n\t\t\t\t\t\t}]\n\t\t\t\t\t}\n\t\t\t\t\tif
   102          parameter[\"ports\"] != _|_ {\n\t\t\t\t\t\tports: [ for v in parameter.ports
   103          {\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcontainerPort: v.port\n\t\t\t\t\t\t\t\tprotocol:
   104          \     v.protocol\n\t\t\t\t\t\t\t\tif v.name != _|_ {\n\t\t\t\t\t\t\t\t\tname:
   105          v.name\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif v.name == _|_ {\n\t\t\t\t\t\t\t\t\tname:
   106          \"port-\" + strconv.FormatInt(v.port, 10)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}]\n\t\t\t\t\t}\n\n\t\t\t\t\tif
   107          parameter[\"imagePullPolicy\"] != _|_ {\n\t\t\t\t\t\timagePullPolicy: parameter.imagePullPolicy\n\t\t\t\t\t}\n\n\t\t\t\t\tif
   108          parameter[\"cmd\"] != _|_ {\n\t\t\t\t\t\tcommand: parameter.cmd\n\t\t\t\t\t}\n\n\t\t\t\t\tif
   109          parameter[\"env\"] != _|_ {\n\t\t\t\t\t\tenv: parameter.env\n\t\t\t\t\t}\n\n\t\t\t\t\tif
   110          context[\"config\"] != _|_ {\n\t\t\t\t\t\tenv: context.config\n\t\t\t\t\t}\n\n\t\t\t\t\tif
   111          parameter[\"cpu\"] != _|_ {\n\t\t\t\t\t\tresources: {\n\t\t\t\t\t\t\tlimits:
   112          cpu:   parameter.cpu\n\t\t\t\t\t\t\trequests: cpu: parameter.cpu\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif
   113          parameter[\"memory\"] != _|_ {\n\t\t\t\t\t\tresources: {\n\t\t\t\t\t\t\tlimits:
   114          memory:   parameter.memory\n\t\t\t\t\t\t\trequests: memory: parameter.memory\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif
   115          parameter[\"volumes\"] != _|_ && parameter[\"volumeMounts\"] == _|_ {\n\t\t\t\t\t\tvolumeMounts:
   116          [ for v in parameter.volumes {\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmountPath:
   117          v.mountPath\n\t\t\t\t\t\t\t\tname:      v.name\n\t\t\t\t\t\t\t}}]\n\t\t\t\t\t}\n\n\t\t\t\t\tif
   118          parameter[\"volumeMounts\"] != _|_ {\n\t\t\t\t\t\tvolumeMounts: mountsArray.pvc
   119          + mountsArray.configMap + mountsArray.secret + mountsArray.emptyDir + mountsArray.hostPath\n\t\t\t\t\t}\n\n\t\t\t\t\tif
   120          parameter[\"livenessProbe\"] != _|_ {\n\t\t\t\t\t\tlivenessProbe: parameter.livenessProbe\n\t\t\t\t\t}\n\n\t\t\t\t\tif
   121          parameter[\"readinessProbe\"] != _|_ {\n\t\t\t\t\t\treadinessProbe: parameter.readinessProbe\n\t\t\t\t\t}\n\n\t\t\t\t}]\n\n\t\t\t\tif
   122          parameter[\"hostAliases\"] != _|_ {\n\t\t\t\t\t// +patchKey=ip\n\t\t\t\t\thostAliases:
   123          parameter.hostAliases\n\t\t\t\t}\n\n\t\t\t\tif parameter[\"imagePullSecrets\"]
   124          != _|_ {\n\t\t\t\t\timagePullSecrets: [ for v in parameter.imagePullSecrets
   125          {\n\t\t\t\t\t\tname: v\n\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t}\n\n\t\t\t\tif
   126          parameter[\"volumes\"] != _|_ && parameter[\"volumeMounts\"] == _|_ {\n\t\t\t\t\tvolumes:
   127          [ for v in parameter.volumes {\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tname: v.name\n\t\t\t\t\t\t\tif
   128          v.type == \"pvc\" {\n\t\t\t\t\t\t\t\tpersistentVolumeClaim: claimName: v.claimName\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif
   129          v.type == \"configMap\" {\n\t\t\t\t\t\t\t\tconfigMap: {\n\t\t\t\t\t\t\t\t\tdefaultMode:
   130          v.defaultMode\n\t\t\t\t\t\t\t\t\tname:        v.cmName\n\t\t\t\t\t\t\t\t\tif
   131          v.items != _|_ {\n\t\t\t\t\t\t\t\t\t\titems: v.items\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif
   132          v.type == \"secret\" {\n\t\t\t\t\t\t\t\tsecret: {\n\t\t\t\t\t\t\t\t\tdefaultMode:
   133          v.defaultMode\n\t\t\t\t\t\t\t\t\tsecretName:  v.secretName\n\t\t\t\t\t\t\t\t\tif
   134          v.items != _|_ {\n\t\t\t\t\t\t\t\t\t\titems: v.items\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif
   135          v.type == \"emptyDir\" {\n\t\t\t\t\t\t\t\temptyDir: medium: v.medium\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}]\n\t\t\t\t}\n\n\t\t\t\tif
   136          parameter[\"volumeMounts\"] != _|_ {\n\t\t\t\t\tvolumes: deDupVolumesArray\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\nexposePorts:
   137          [\n\tfor v in parameter.ports if v.expose == true {\n\t\tport:       v.port\n\t\ttargetPort:
   138          v.port\n\t\tif v.name != _|_ {\n\t\t\tname: v.name\n\t\t}\n\t\tif v.name ==
   139          _|_ {\n\t\t\tname: \"port-\" + strconv.FormatInt(v.port, 10)\n\t\t}\n\t},\n]\noutputs:
   140          {\n\tif len(exposePorts) != 0 {\n\t\twebserviceExpose: {\n\t\t\tapiVersion:
   141          \"v1\"\n\t\t\tkind:       \"Service\"\n\t\t\tmetadata: name: context.name\n\t\t\tspec:
   142          {\n\t\t\t\tselector: \"app.oam.dev/component\": context.name\n\t\t\t\tports:
   143          exposePorts\n\t\t\t\ttype:  parameter.exposeType\n\t\t\t}\n\t\t}\n\t}\n}\nparameter:
   144          {\n\t// +usage=Specify the labels in the workload\n\tlabels?: [string]: string\n\n\t//
   145          +usage=Specify the annotations in the workload\n\tannotations?: [string]:
   146          string\n\n\t// +usage=Which image would you like to use for your service\n\t//
   147          +short=i\n\timage: string\n\n\t// +usage=Specify image pull policy for your
   148          service\n\timagePullPolicy?: \"Always\" | \"Never\" | \"IfNotPresent\"\n\n\t//
   149          +usage=Specify image pull secrets for your service\n\timagePullSecrets?: [...string]\n\n\t//
   150          +ignore\n\t// +usage=Deprecated field, please use ports instead\n\t// +short=p\n\tport?:
   151          int\n\n\t// +usage=Which ports do you want customer traffic sent to, defaults
   152          to 80\n\tports?: [...{\n\t\t// +usage=Number of port to expose on the pod's
   153          IP address\n\t\tport: int\n\t\t// +usage=Name of the port\n\t\tname?: string\n\t\t//
   154          +usage=Protocol for port. Must be UDP, TCP, or SCTP\n\t\tprotocol: *\"TCP\"
   155          | \"UDP\" | \"SCTP\"\n\t\t// +usage=Specify if the port should be exposed\n\t\texpose:
   156          *false | bool\n\t}]\n\n\t// +ignore\n\t// +usage=Specify what kind of Service
   157          you want. options: \"ClusterIP\", \"NodePort\", \"LoadBalancer\"\n\texposeType:
   158          *\"ClusterIP\" | \"NodePort\" | \"LoadBalancer\"\n\n\t// +ignore\n\t// +usage=If
   159          addRevisionLabel is true, the revision label will be added to the underlying
   160          pods\n\taddRevisionLabel: *false | bool\n\n\t// +usage=Commands to run in
   161          the container\n\tcmd?: [...string]\n\n\t// +usage=Define arguments by using
   162          environment variables\n\tenv?: [...{\n\t\t// +usage=Environment variable name\n\t\tname:
   163          string\n\t\t// +usage=The value of the environment variable\n\t\tvalue?: string\n\t\t//
   164          +usage=Specifies a source the value of this var should come from\n\t\tvalueFrom?:
   165          {\n\t\t\t// +usage=Selects a key of a secret in the pod's namespace\n\t\t\tsecretKeyRef?:
   166          {\n\t\t\t\t// +usage=The name of the secret in the pod's namespace to select
   167          from\n\t\t\t\tname: string\n\t\t\t\t// +usage=The key of the secret to select
   168          from. Must be a valid secret key\n\t\t\t\tkey: string\n\t\t\t}\n\t\t\t// +usage=Selects
   169          a key of a config map in the pod's namespace\n\t\t\tconfigMapKeyRef?: {\n\t\t\t\t//
   170          +usage=The name of the config map in the pod's namespace to select from\n\t\t\t\tname:
   171          string\n\t\t\t\t// +usage=The key of the config map to select from. Must be
   172          a valid secret key\n\t\t\t\tkey: string\n\t\t\t}\n\t\t}\n\t}]\n\n\t// +usage=Number
   173          of CPU units for the service\n\tcpu?: string\n\n\t// +usage=Specifies the
   174          attributes of the memory resource required for the container.\n\tmemory?:
   175          string\n\n\tvolumeMounts?: {\n\t\t// +usage=Mount PVC type volume\n\t\tpvc?:
   176          [...{\n\t\t\tname:      string\n\t\t\tmountPath: string\n\t\t\tsubPath?:  string\n\t\t\t//
   177          +usage=The name of the PVC\n\t\t\tclaimName: string\n\t\t}]\n\t\t// +usage=Mount
   178          ConfigMap type volume\n\t\tconfigMap?: [...{\n\t\t\tname:        string\n\t\t\tmountPath:
   179          \  string\n\t\t\tsubPath?:    string\n\t\t\tdefaultMode: *420 | int\n\t\t\tcmName:
   180          \     string\n\t\t\titems?: [...{\n\t\t\t\tkey:  string\n\t\t\t\tpath: string\n\t\t\t\tmode:
   181          *511 | int\n\t\t\t}]\n\t\t}]\n\t\t// +usage=Mount Secret type volume\n\t\tsecret?:
   182          [...{\n\t\t\tname:        string\n\t\t\tmountPath:   string\n\t\t\tsubPath?:
   183          \   string\n\t\t\tdefaultMode: *420 | int\n\t\t\tsecretName:  string\n\t\t\titems?:
   184          [...{\n\t\t\t\tkey:  string\n\t\t\t\tpath: string\n\t\t\t\tmode: *511 | int\n\t\t\t}]\n\t\t}]\n\t\t//
   185          +usage=Mount EmptyDir type volume\n\t\temptyDir?: [...{\n\t\t\tname:      string\n\t\t\tmountPath:
   186          string\n\t\t\tsubPath?:  string\n\t\t\tmedium:    *\"\" | \"Memory\"\n\t\t}]\n\t\t//
   187          +usage=Mount HostPath type volume\n\t\thostPath?: [...{\n\t\t\tname:      string\n\t\t\tmountPath:
   188          string\n\t\t\tsubPath?:  string\n\t\t\tpath:      string\n\t\t}]\n\t}\n\n\t//
   189          +usage=Deprecated field, use volumeMounts instead.\n\tvolumes?: [...{\n\t\tname:
   190          \     string\n\t\tmountPath: string\n\t\t// +usage=Specify volume type, options:
   191          \"pvc\",\"configMap\",\"secret\",\"emptyDir\"\n\t\ttype: \"pvc\" | \"configMap\"
   192          | \"secret\" | \"emptyDir\"\n\t\tif type == \"pvc\" {\n\t\t\tclaimName: string\n\t\t}\n\t\tif
   193          type == \"configMap\" {\n\t\t\tdefaultMode: *420 | int\n\t\t\tcmName:      string\n\t\t\titems?:
   194          [...{\n\t\t\t\tkey:  string\n\t\t\t\tpath: string\n\t\t\t\tmode: *511 | int\n\t\t\t}]\n\t\t}\n\t\tif
   195          type == \"secret\" {\n\t\t\tdefaultMode: *420 | int\n\t\t\tsecretName:  string\n\t\t\titems?:
   196          [...{\n\t\t\t\tkey:  string\n\t\t\t\tpath: string\n\t\t\t\tmode: *511 | int\n\t\t\t}]\n\t\t}\n\t\tif
   197          type == \"emptyDir\" {\n\t\t\tmedium: *\"\" | \"Memory\"\n\t\t}\n\t}]\n\n\t//
   198          +usage=Instructions for assessing whether the container is alive.\n\tlivenessProbe?:
   199          #HealthProbe\n\n\t// +usage=Instructions for assessing whether the container
   200          is in a suitable state to serve traffic.\n\treadinessProbe?: #HealthProbe\n\n\t//
   201          +usage=Specify the hostAliases to add\n\thostAliases?: [...{\n\t\tip: string\n\t\thostnames:
   202          [...string]\n\t}]\n}\n#HealthProbe: {\n\n\t// +usage=Instructions for assessing
   203          container health by executing a command. Either this attribute or the httpGet
   204          attribute or the tcpSocket attribute MUST be specified. This attribute is
   205          mutually exclusive with both the httpGet attribute and the tcpSocket attribute.\n\texec?:
   206          {\n\t\t// +usage=A command to be executed inside the container to assess its
   207          health. Each space delimited token of the command is a separate array element.
   208          Commands exiting 0 are considered to be successful probes, whilst all other
   209          exit codes are considered failures.\n\t\tcommand: [...string]\n\t}\n\n\t//
   210          +usage=Instructions for assessing container health by executing an HTTP GET
   211          request. Either this attribute or the exec attribute or the tcpSocket attribute
   212          MUST be specified. This attribute is mutually exclusive with both the exec
   213          attribute and the tcpSocket attribute.\n\thttpGet?: {\n\t\t// +usage=The endpoint,
   214          relative to the port, to which the HTTP GET request should be directed.\n\t\tpath:
   215          string\n\t\t// +usage=The TCP socket within the container to which the HTTP
   216          GET request should be directed.\n\t\tport:    int\n\t\thost?:   string\n\t\tscheme?:
   217          *\"HTTP\" | string\n\t\thttpHeaders?: [...{\n\t\t\tname:  string\n\t\t\tvalue:
   218          string\n\t\t}]\n\t}\n\n\t// +usage=Instructions for assessing container health
   219          by probing a TCP socket. Either this attribute or the exec attribute or the
   220          httpGet attribute MUST be specified. This attribute is mutually exclusive
   221          with both the exec attribute and the httpGet attribute.\n\ttcpSocket?: {\n\t\t//
   222          +usage=The TCP socket within the container that should be probed to assess
   223          container health.\n\t\tport: int\n\t}\n\n\t// +usage=Number of seconds after
   224          the container is started before the first probe is initiated.\n\tinitialDelaySeconds:
   225          *0 | int\n\n\t// +usage=How often, in seconds, to execute the probe.\n\tperiodSeconds:
   226          *10 | int\n\n\t// +usage=Number of seconds after which the probe times out.\n\ttimeoutSeconds:
   227          *1 | int\n\n\t// +usage=Minimum consecutive successes for the probe to be
   228          considered successful after having failed.\n\tsuccessThreshold: *1 | int\n\n\t//
   229          +usage=Number of consecutive failures required to determine the container
   230          is not alive (liveness probe) or not ready (readiness probe).\n\tfailureThreshold:
   231          *3 | int\n}\n"
   232    status:
   233      customStatus: "ready: {\n\treadyReplicas: *0 | int\n} & {\n\tif context.output.status.readyReplicas
   234        != _|_ {\n\t\treadyReplicas: context.output.status.readyReplicas\n\t}\n}\nmessage:
   235        \"Ready:\\(ready.readyReplicas)/\\(context.output.spec.replicas)\""
   236      healthPolicy: "ready: {\n\tupdatedReplicas:    *0 | int\n\treadyReplicas:      *0
   237        | int\n\treplicas:           *0 | int\n\tobservedGeneration: *0 | int\n} & {\n\tif
   238        context.output.status.updatedReplicas != _|_ {\n\t\tupdatedReplicas: context.output.status.updatedReplicas\n\t}\n\tif
   239        context.output.status.readyReplicas != _|_ {\n\t\treadyReplicas: context.output.status.readyReplicas\n\t}\n\tif
   240        context.output.status.replicas != _|_ {\n\t\treplicas: context.output.status.replicas\n\t}\n\tif
   241        context.output.status.observedGeneration != _|_ {\n\t\tobservedGeneration: context.output.status.observedGeneration\n\t}\n}\nisHealth:
   242        (context.output.spec.replicas == ready.readyReplicas) && (context.output.spec.replicas
   243        == ready.updatedReplicas) && (context.output.spec.replicas == ready.replicas)
   244        && (ready.observedGeneration == context.output.metadata.generation || ready.observedGeneration
   245        > context.output.metadata.generation)"
   246    workload:
   247      definition:
   248        apiVersion: apps/v1
   249        kind: Deployment
   250      type: deployments.apps
   251  status: {}
   252  `
   253  
   254  var firstVelaAppRev string = `
   255  apiVersion: core.oam.dev/v1beta1
   256  kind: ApplicationRevision
   257  metadata:
   258    annotations:
   259      oam.dev/kubevela-version: v1.5.2
   260    generation: 1
   261    labels:
   262      app.oam.dev/app-revision-hash: 1c3d847600ac0514
   263      app.oam.dev/name: first-vela-app
   264    name: first-vela-app-v1
   265    namespace: vela-system
   266  spec:
   267    application:
   268      apiVersion: core.oam.dev/v1beta1
   269      kind: Application
   270      metadata:
   271        annotations:
   272        finalizers:
   273        - app.oam.dev/resource-tracker-finalizer
   274        name: first-vela-app
   275        namespace: vela-system
   276      spec:
   277        components:
   278        - name: express-server
   279          properties:
   280            image: oamdev/hello-world
   281            ports:
   282            - expose: true
   283              port: 8000
   284          traits:
   285          - properties:
   286              replicas: 1
   287            type: scaler
   288          type: webservice
   289      status: {}
   290    componentDefinitions:
   291      webservice:
   292        apiVersion: core.oam.dev/v1beta1
   293        kind: ComponentDefinition
   294        metadata:
   295          annotations:
   296            definition.oam.dev/description: Describes long-running, scalable, containerized
   297              services that have a stable network endpoint to receive external network
   298              traffic from customers.
   299            meta.helm.sh/release-name: kubevela
   300            meta.helm.sh/release-namespace: vela-system
   301          labels:
   302            app.kubernetes.io/managed-by: Helm
   303          name: webservice
   304          namespace: vela-system
   305        spec:
   306          schematic:
   307            cue:
   308              template: "import (\n\t\"strconv\"\n)\n\nmountsArray: {\n\tpvc: *[\n\t\tfor
   309                v in parameter.volumeMounts.pvc {\n\t\t\t{\n\t\t\t\tmountPath: v.mountPath\n\t\t\t\tif
   310                v.subPath != _|_ {\n\t\t\t\t\tsubPath: v.subPath\n\t\t\t\t}\n\t\t\t\tname:
   311                v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\tconfigMap: *[\n\t\t\tfor v in
   312                parameter.volumeMounts.configMap {\n\t\t\t{\n\t\t\t\tmountPath: v.mountPath\n\t\t\t\tif
   313                v.subPath != _|_ {\n\t\t\t\t\tsubPath: v.subPath\n\t\t\t\t}\n\t\t\t\tname:
   314                v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\tsecret: *[\n\t\tfor v in parameter.volumeMounts.secret
   315                {\n\t\t\t{\n\t\t\t\tmountPath: v.mountPath\n\t\t\t\tif v.subPath !=
   316                _|_ {\n\t\t\t\t\tsubPath: v.subPath\n\t\t\t\t}\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t]
   317                | []\n\n\temptyDir: *[\n\t\t\tfor v in parameter.volumeMounts.emptyDir
   318                {\n\t\t\t{\n\t\t\t\tmountPath: v.mountPath\n\t\t\t\tif v.subPath !=
   319                _|_ {\n\t\t\t\t\tsubPath: v.subPath\n\t\t\t\t}\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t]
   320                | []\n\n\thostPath: *[\n\t\t\tfor v in parameter.volumeMounts.hostPath
   321                {\n\t\t\t{\n\t\t\t\tmountPath: v.mountPath\n\t\t\t\tif v.subPath !=
   322                _|_ {\n\t\t\t\t\tsubPath: v.subPath\n\t\t\t\t}\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t]
   323                | []\n}\nvolumesArray: {\n\tpvc: *[\n\t\tfor v in parameter.volumeMounts.pvc
   324                {\n\t\t\t{\n\t\t\t\tname: v.name\n\t\t\t\tpersistentVolumeClaim: claimName:
   325                v.claimName\n\t\t\t}\n\t\t},\n\t] | []\n\n\tconfigMap: *[\n\t\t\tfor
   326                v in parameter.volumeMounts.configMap {\n\t\t\t{\n\t\t\t\tname: v.name\n\t\t\t\tconfigMap:
   327                {\n\t\t\t\t\tdefaultMode: v.defaultMode\n\t\t\t\t\tname:        v.cmName\n\t\t\t\t\tif
   328                v.items != _|_ {\n\t\t\t\t\t\titems: v.items\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t]
   329                | []\n\n\tsecret: *[\n\t\tfor v in parameter.volumeMounts.secret {\n\t\t\t{\n\t\t\t\tname:
   330                v.name\n\t\t\t\tsecret: {\n\t\t\t\t\tdefaultMode: v.defaultMode\n\t\t\t\t\tsecretName:
   331                \ v.secretName\n\t\t\t\t\tif v.items != _|_ {\n\t\t\t\t\t\titems: v.items\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t]
   332                | []\n\n\temptyDir: *[\n\t\t\tfor v in parameter.volumeMounts.emptyDir
   333                {\n\t\t\t{\n\t\t\t\tname: v.name\n\t\t\t\temptyDir: medium: v.medium\n\t\t\t}\n\t\t},\n\t]
   334                | []\n\n\thostPath: *[\n\t\t\tfor v in parameter.volumeMounts.hostPath
   335                {\n\t\t\t{\n\t\t\t\tname: v.name\n\t\t\t\thostPath: path: v.path\n\t\t\t}\n\t\t},\n\t]
   336                | []\n}\nvolumesList: volumesArray.pvc + volumesArray.configMap + volumesArray.secret
   337                + volumesArray.emptyDir + volumesArray.hostPath\ndeDupVolumesArray:
   338                [\n\tfor val in [\n\t\tfor i, vi in volumesList {\n\t\t\tfor j, vj in
   339                volumesList if j < i && vi.name == vj.name {\n\t\t\t\t_ignore: true\n\t\t\t}\n\t\t\tvi\n\t\t},\n\t]
   340                if val._ignore == _|_ {\n\t\tval\n\t},\n]\noutput: {\n\tapiVersion:
   341                \"apps/v1\"\n\tkind:       \"Deployment\"\n\tspec: {\n\t\tselector:
   342                matchLabels: \"app.oam.dev/component\": context.name\n\n\t\ttemplate:
   343                {\n\t\t\tmetadata: {\n\t\t\t\tlabels: {\n\t\t\t\t\tif parameter.labels
   344                != _|_ {\n\t\t\t\t\t\tparameter.labels\n\t\t\t\t\t}\n\t\t\t\t\tif parameter.addRevisionLabel
   345                {\n\t\t\t\t\t\t\"app.oam.dev/revision\": context.revision\n\t\t\t\t\t}\n\t\t\t\t\t\"app.oam.dev/name\":
   346                \     context.appName\n\t\t\t\t\t\"app.oam.dev/component\": context.name\n\t\t\t\t}\n\t\t\t\tif
   347                parameter.annotations != _|_ {\n\t\t\t\t\tannotations: parameter.annotations\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tspec:
   348                {\n\t\t\t\tcontainers: [{\n\t\t\t\t\tname:  context.name\n\t\t\t\t\timage:
   349                parameter.image\n\t\t\t\t\tif parameter[\"port\"] != _|_ && parameter[\"ports\"]
   350                == _|_ {\n\t\t\t\t\t\tports: [{\n\t\t\t\t\t\t\tcontainerPort: parameter.port\n\t\t\t\t\t\t}]\n\t\t\t\t\t}\n\t\t\t\t\tif
   351                parameter[\"ports\"] != _|_ {\n\t\t\t\t\t\tports: [ for v in parameter.ports
   352                {\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcontainerPort: v.port\n\t\t\t\t\t\t\t\tprotocol:
   353                \     v.protocol\n\t\t\t\t\t\t\t\tif v.name != _|_ {\n\t\t\t\t\t\t\t\t\tname:
   354                v.name\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif v.name == _|_ {\n\t\t\t\t\t\t\t\t\tname:
   355                \"port-\" + strconv.FormatInt(v.port, 10)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}]\n\t\t\t\t\t}\n\n\t\t\t\t\tif
   356                parameter[\"imagePullPolicy\"] != _|_ {\n\t\t\t\t\t\timagePullPolicy:
   357                parameter.imagePullPolicy\n\t\t\t\t\t}\n\n\t\t\t\t\tif parameter[\"cmd\"]
   358                != _|_ {\n\t\t\t\t\t\tcommand: parameter.cmd\n\t\t\t\t\t}\n\n\t\t\t\t\tif
   359                parameter[\"env\"] != _|_ {\n\t\t\t\t\t\tenv: parameter.env\n\t\t\t\t\t}\n\n\t\t\t\t\tif
   360                context[\"config\"] != _|_ {\n\t\t\t\t\t\tenv: context.config\n\t\t\t\t\t}\n\n\t\t\t\t\tif
   361                parameter[\"cpu\"] != _|_ {\n\t\t\t\t\t\tresources: {\n\t\t\t\t\t\t\tlimits:
   362                cpu:   parameter.cpu\n\t\t\t\t\t\t\trequests: cpu: parameter.cpu\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif
   363                parameter[\"memory\"] != _|_ {\n\t\t\t\t\t\tresources: {\n\t\t\t\t\t\t\tlimits:
   364                memory:   parameter.memory\n\t\t\t\t\t\t\trequests: memory: parameter.memory\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif
   365                parameter[\"volumes\"] != _|_ && parameter[\"volumeMounts\"] == _|_
   366                {\n\t\t\t\t\t\tvolumeMounts: [ for v in parameter.volumes {\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmountPath:
   367                v.mountPath\n\t\t\t\t\t\t\t\tname:      v.name\n\t\t\t\t\t\t\t}}]\n\t\t\t\t\t}\n\n\t\t\t\t\tif
   368                parameter[\"volumeMounts\"] != _|_ {\n\t\t\t\t\t\tvolumeMounts: mountsArray.pvc
   369                + mountsArray.configMap + mountsArray.secret + mountsArray.emptyDir
   370                + mountsArray.hostPath\n\t\t\t\t\t}\n\n\t\t\t\t\tif parameter[\"livenessProbe\"]
   371                != _|_ {\n\t\t\t\t\t\tlivenessProbe: parameter.livenessProbe\n\t\t\t\t\t}\n\n\t\t\t\t\tif
   372                parameter[\"readinessProbe\"] != _|_ {\n\t\t\t\t\t\treadinessProbe:
   373                parameter.readinessProbe\n\t\t\t\t\t}\n\n\t\t\t\t}]\n\n\t\t\t\tif parameter[\"hostAliases\"]
   374                != _|_ {\n\t\t\t\t\t// +patchKey=ip\n\t\t\t\t\thostAliases: parameter.hostAliases\n\t\t\t\t}\n\n\t\t\t\tif
   375                parameter[\"imagePullSecrets\"] != _|_ {\n\t\t\t\t\timagePullSecrets:
   376                [ for v in parameter.imagePullSecrets {\n\t\t\t\t\t\tname: v\n\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t}\n\n\t\t\t\tif
   377                parameter[\"volumes\"] != _|_ && parameter[\"volumeMounts\"] == _|_
   378                {\n\t\t\t\t\tvolumes: [ for v in parameter.volumes {\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tname:
   379                v.name\n\t\t\t\t\t\t\tif v.type == \"pvc\" {\n\t\t\t\t\t\t\t\tpersistentVolumeClaim:
   380                claimName: v.claimName\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif v.type ==
   381                \"configMap\" {\n\t\t\t\t\t\t\t\tconfigMap: {\n\t\t\t\t\t\t\t\t\tdefaultMode:
   382                v.defaultMode\n\t\t\t\t\t\t\t\t\tname:        v.cmName\n\t\t\t\t\t\t\t\t\tif
   383                v.items != _|_ {\n\t\t\t\t\t\t\t\t\t\titems: v.items\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif
   384                v.type == \"secret\" {\n\t\t\t\t\t\t\t\tsecret: {\n\t\t\t\t\t\t\t\t\tdefaultMode:
   385                v.defaultMode\n\t\t\t\t\t\t\t\t\tsecretName:  v.secretName\n\t\t\t\t\t\t\t\t\tif
   386                v.items != _|_ {\n\t\t\t\t\t\t\t\t\t\titems: v.items\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif
   387                v.type == \"emptyDir\" {\n\t\t\t\t\t\t\t\temptyDir: medium: v.medium\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}]\n\t\t\t\t}\n\n\t\t\t\tif
   388                parameter[\"volumeMounts\"] != _|_ {\n\t\t\t\t\tvolumes: deDupVolumesArray\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\nexposePorts:
   389                [\n\tfor v in parameter.ports if v.expose == true {\n\t\tport:       v.port\n\t\ttargetPort:
   390                v.port\n\t\tif v.name != _|_ {\n\t\t\tname: v.name\n\t\t}\n\t\tif v.name
   391                == _|_ {\n\t\t\tname: \"port-\" + strconv.FormatInt(v.port, 10)\n\t\t}\n\t},\n]\noutputs:
   392                {\n\tif len(exposePorts) != 0 {\n\t\twebserviceExpose: {\n\t\t\tapiVersion:
   393                \"v1\"\n\t\t\tkind:       \"Service\"\n\t\t\tmetadata: name: context.name\n\t\t\tspec:
   394                {\n\t\t\t\tselector: \"app.oam.dev/component\": context.name\n\t\t\t\tports:
   395                exposePorts\n\t\t\t\ttype:  parameter.exposeType\n\t\t\t}\n\t\t}\n\t}\n}\nparameter:
   396                {\n\t// +usage=Specify the labels in the workload\n\tlabels?: [string]:
   397                string\n\n\t// +usage=Specify the annotations in the workload\n\tannotations?:
   398                [string]: string\n\n\t// +usage=Which image would you like to use for
   399                your service\n\t// +short=i\n\timage: string\n\n\t// +usage=Specify
   400                image pull policy for your service\n\timagePullPolicy?: \"Always\" |
   401                \"Never\" | \"IfNotPresent\"\n\n\t// +usage=Specify image pull secrets
   402                for your service\n\timagePullSecrets?: [...string]\n\n\t// +ignore\n\t//
   403                +usage=Deprecated field, please use ports instead\n\t// +short=p\n\tport?:
   404                int\n\n\t// +usage=Which ports do you want customer traffic sent to,
   405                defaults to 80\n\tports?: [...{\n\t\t// +usage=Number of port to expose
   406                on the pod's IP address\n\t\tport: int\n\t\t// +usage=Name of the port\n\t\tname?:
   407                string\n\t\t// +usage=Protocol for port. Must be UDP, TCP, or SCTP\n\t\tprotocol:
   408                *\"TCP\" | \"UDP\" | \"SCTP\"\n\t\t// +usage=Specify if the port should
   409                be exposed\n\t\texpose: *false | bool\n\t}]\n\n\t// +ignore\n\t// +usage=Specify
   410                what kind of Service you want. options: \"ClusterIP\", \"NodePort\",
   411                \"LoadBalancer\"\n\texposeType: *\"ClusterIP\" | \"NodePort\" | \"LoadBalancer\"\n\n\t//
   412                +ignore\n\t// +usage=If addRevisionLabel is true, the revision label
   413                will be added to the underlying pods\n\taddRevisionLabel: *false | bool\n\n\t//
   414                +usage=Commands to run in the container\n\tcmd?: [...string]\n\n\t//
   415                +usage=Define arguments by using environment variables\n\tenv?: [...{\n\t\t//
   416                +usage=Environment variable name\n\t\tname: string\n\t\t// +usage=The
   417                value of the environment variable\n\t\tvalue?: string\n\t\t// +usage=Specifies
   418                a source the value of this var should come from\n\t\tvalueFrom?: {\n\t\t\t//
   419                +usage=Selects a key of a secret in the pod's namespace\n\t\t\tsecretKeyRef?:
   420                {\n\t\t\t\t// +usage=The name of the secret in the pod's namespace to
   421                select from\n\t\t\t\tname: string\n\t\t\t\t// +usage=The key of the
   422                secret to select from. Must be a valid secret key\n\t\t\t\tkey: string\n\t\t\t}\n\t\t\t//
   423                +usage=Selects a key of a config map in the pod's namespace\n\t\t\tconfigMapKeyRef?:
   424                {\n\t\t\t\t// +usage=The name of the config map in the pod's namespace
   425                to select from\n\t\t\t\tname: string\n\t\t\t\t// +usage=The key of the
   426                config map to select from. Must be a valid secret key\n\t\t\t\tkey:
   427                string\n\t\t\t}\n\t\t}\n\t}]\n\n\t// +usage=Number of CPU units for
   428                the service\n\tcpu?: string\n\n\t//
   429                +usage=Specifies the attributes of the memory resource required for
   430                the container.\n\tmemory?: string\n\n\tvolumeMounts?: {\n\t\t// +usage=Mount
   431                PVC type volume\n\t\tpvc?: [...{\n\t\t\tname:      string\n\t\t\tmountPath:
   432                string\n\t\t\tsubPath?:  string\n\t\t\t// +usage=The name of the PVC\n\t\t\tclaimName:
   433                string\n\t\t}]\n\t\t// +usage=Mount ConfigMap type volume\n\t\tconfigMap?:
   434                [...{\n\t\t\tname:        string\n\t\t\tmountPath:   string\n\t\t\tsubPath?:
   435                \   string\n\t\t\tdefaultMode: *420 | int\n\t\t\tcmName:      string\n\t\t\titems?:
   436                [...{\n\t\t\t\tkey:  string\n\t\t\t\tpath: string\n\t\t\t\tmode: *511
   437                | int\n\t\t\t}]\n\t\t}]\n\t\t// +usage=Mount Secret type volume\n\t\tsecret?:
   438                [...{\n\t\t\tname:        string\n\t\t\tmountPath:   string\n\t\t\tsubPath?:
   439                \   string\n\t\t\tdefaultMode: *420 | int\n\t\t\tsecretName:  string\n\t\t\titems?:
   440                [...{\n\t\t\t\tkey:  string\n\t\t\t\tpath: string\n\t\t\t\tmode: *511
   441                | int\n\t\t\t}]\n\t\t}]\n\t\t// +usage=Mount EmptyDir type volume\n\t\temptyDir?:
   442                [...{\n\t\t\tname:      string\n\t\t\tmountPath: string\n\t\t\tsubPath?:
   443                \ string\n\t\t\tmedium:    *\"\" | \"Memory\"\n\t\t}]\n\t\t// +usage=Mount
   444                HostPath type volume\n\t\thostPath?: [...{\n\t\t\tname:      string\n\t\t\tmountPath:
   445                string\n\t\t\tsubPath?:  string\n\t\t\tpath:      string\n\t\t}]\n\t}\n\n\t//
   446                +usage=Deprecated field, use volumeMounts instead.\n\tvolumes?: [...{\n\t\tname:
   447                \     string\n\t\tmountPath: string\n\t\t// +usage=Specify volume type,
   448                options: \"pvc\",\"configMap\",\"secret\",\"emptyDir\"\n\t\ttype: \"pvc\"
   449                | \"configMap\" | \"secret\" | \"emptyDir\"\n\t\tif type == \"pvc\"
   450                {\n\t\t\tclaimName: string\n\t\t}\n\t\tif type == \"configMap\" {\n\t\t\tdefaultMode:
   451                *420 | int\n\t\t\tcmName:      string\n\t\t\titems?: [...{\n\t\t\t\tkey:
   452                \ string\n\t\t\t\tpath: string\n\t\t\t\tmode: *511 | int\n\t\t\t}]\n\t\t}\n\t\tif
   453                type == \"secret\" {\n\t\t\tdefaultMode: *420 | int\n\t\t\tsecretName:
   454                \ string\n\t\t\titems?: [...{\n\t\t\t\tkey:  string\n\t\t\t\tpath: string\n\t\t\t\tmode:
   455                *511 | int\n\t\t\t}]\n\t\t}\n\t\tif type == \"emptyDir\" {\n\t\t\tmedium:
   456                *\"\" | \"Memory\"\n\t\t}\n\t}]\n\n\t// +usage=Instructions for assessing
   457                whether the container is alive.\n\tlivenessProbe?: #HealthProbe\n\n\t//
   458                +usage=Instructions for assessing whether the container is in a suitable
   459                state to serve traffic.\n\treadinessProbe?: #HealthProbe\n\n\t// +usage=Specify
   460                the hostAliases to add\n\thostAliases?: [...{\n\t\tip: string\n\t\thostnames:
   461                [...string]\n\t}]\n}\n#HealthProbe: {\n\n\t// +usage=Instructions for
   462                assessing container health by executing a command. Either this attribute
   463                or the httpGet attribute or the tcpSocket attribute MUST be specified.
   464                This attribute is mutually exclusive with both the httpGet attribute
   465                and the tcpSocket attribute.\n\texec?: {\n\t\t// +usage=A command to
   466                be executed inside the container to assess its health. Each space delimited
   467                token of the command is a separate array element. Commands exiting 0
   468                are considered to be successful probes, whilst all other exit codes
   469                are considered failures.\n\t\tcommand: [...string]\n\t}\n\n\t// +usage=Instructions
   470                for assessing container health by executing an HTTP GET request. Either
   471                this attribute or the exec attribute or the tcpSocket attribute MUST
   472                be specified. This attribute is mutually exclusive with both the exec
   473                attribute and the tcpSocket attribute.\n\thttpGet?: {\n\t\t// +usage=The
   474                endpoint, relative to the port, to which the HTTP GET request should
   475                be directed.\n\t\tpath: string\n\t\t// +usage=The TCP socket within
   476                the container to which the HTTP GET request should be directed.\n\t\tport:
   477                \   int\n\t\thost?:   string\n\t\tscheme?: *\"HTTP\" | string\n\t\thttpHeaders?:
   478                [...{\n\t\t\tname:  string\n\t\t\tvalue: string\n\t\t}]\n\t}\n\n\t//
   479                +usage=Instructions for assessing container health by probing a TCP
   480                socket. Either this attribute or the exec attribute or the httpGet attribute
   481                MUST be specified. This attribute is mutually exclusive with both the
   482                exec attribute and the httpGet attribute.\n\ttcpSocket?: {\n\t\t// +usage=The
   483                TCP socket within the container that should be probed to assess container
   484                health.\n\t\tport: int\n\t}\n\n\t// +usage=Number of seconds after the
   485                container is started before the first probe is initiated.\n\tinitialDelaySeconds:
   486                *0 | int\n\n\t// +usage=How often, in seconds, to execute the probe.\n\tperiodSeconds:
   487                *10 | int\n\n\t// +usage=Number of seconds after which the probe times
   488                out.\n\ttimeoutSeconds: *1 | int\n\n\t// +usage=Minimum consecutive
   489                successes for the probe to be considered successful after having failed.\n\tsuccessThreshold:
   490                *1 | int\n\n\t// +usage=Number of consecutive failures required to determine
   491                the container is not alive (liveness probe) or not ready (readiness
   492                probe).\n\tfailureThreshold: *3 | int\n}\n"
   493          status:
   494            customStatus: "ready: {\n\treadyReplicas: *0 | int\n} & {\n\tif context.output.status.readyReplicas
   495              != _|_ {\n\t\treadyReplicas: context.output.status.readyReplicas\n\t}\n}\nmessage:
   496              \"Ready:\\(ready.readyReplicas)/\\(context.output.spec.replicas)\""
   497            healthPolicy: "ready: {\n\tupdatedReplicas:    *0 | int\n\treadyReplicas:
   498              \     *0 | int\n\treplicas:           *0 | int\n\tobservedGeneration:
   499              *0 | int\n} & {\n\tif context.output.status.updatedReplicas != _|_ {\n\t\tupdatedReplicas:
   500              context.output.status.updatedReplicas\n\t}\n\tif context.output.status.readyReplicas
   501              != _|_ {\n\t\treadyReplicas: context.output.status.readyReplicas\n\t}\n\tif
   502              context.output.status.replicas != _|_ {\n\t\treplicas: context.output.status.replicas\n\t}\n\tif
   503              context.output.status.observedGeneration != _|_ {\n\t\tobservedGeneration:
   504              context.output.status.observedGeneration\n\t}\n}\nisHealth: (context.output.spec.replicas
   505              == ready.readyReplicas) && (context.output.spec.replicas == ready.updatedReplicas)
   506              && (context.output.spec.replicas == ready.replicas) && (ready.observedGeneration
   507              == context.output.metadata.generation || ready.observedGeneration > context.output.metadata.generation)"
   508          workload:
   509            definition:
   510              apiVersion: apps/v1
   511              kind: Deployment
   512            type: deployments.apps
   513        status: {}
   514  
   515  status: {}
   516  `
   517  
   518  var _ = Describe("Test getRevision", func() {
   519  
   520  	var (
   521  		ctx       context.Context
   522  		arg       common.Args
   523  		name      string
   524  		namespace string
   525  		format    string
   526  		out       *bytes.Buffer
   527  		def       string
   528  	)
   529  
   530  	BeforeEach(func() {
   531  		// delete application and view if exist
   532  		app := v1beta1.ApplicationRevision{}
   533  		Expect(yaml.Unmarshal([]byte(firstVelaAppRev), &app)).Should(BeNil())
   534  		_ = k8sClient.Delete(context.TODO(), &app)
   535  		_ = k8sClient.Delete(context.TODO(), &v1.ConfigMap{
   536  			ObjectMeta: metav1.ObjectMeta{
   537  				Name:      revisionView,
   538  				Namespace: types.DefaultKubeVelaNS,
   539  			}})
   540  
   541  		// prepare args
   542  		ctx = context.Background()
   543  		format = ""
   544  		out = &bytes.Buffer{}
   545  		arg = common.Args{}
   546  		arg.SetConfig(cfg)
   547  		arg.SetClient(k8sClient)
   548  		name = "first-vela-app-v1"
   549  		namespace = types.DefaultKubeVelaNS
   550  		def = ""
   551  	})
   552  
   553  	It("Test no pre-defined view", func() {
   554  		err := getRevision(ctx, arg, format, out, name, namespace, def)
   555  		Expect(err).ToNot(Succeed())
   556  		Expect(err.Error()).To(ContainSubstring(fmt.Sprintf("Unable to get application revision %s in namespace %s", name, namespace)))
   557  	})
   558  
   559  	It("Test with no application revision existing", func() {
   560  
   561  		// setup view
   562  		setupView()
   563  
   564  		err := getRevision(ctx, arg, format, out, name, namespace, def)
   565  		Expect(err).To(Succeed())
   566  		Expect(out.String()).To(Equal(fmt.Sprintf("No such application revision %s in namespace %s", name, namespace)))
   567  	})
   568  
   569  	It("Test normal case with default output", func() {
   570  
   571  		// setup view
   572  		setupView()
   573  
   574  		// setup application
   575  		app := v1beta1.ApplicationRevision{}
   576  		Expect(yaml.Unmarshal([]byte(firstVelaAppRev), &app)).Should(BeNil())
   577  		Expect(k8sClient.Create(context.TODO(), &app)).Should(BeNil())
   578  
   579  		Expect(getRevision(ctx, arg, format, out, name, namespace, def)).To(Succeed())
   580  		table := newUITable().AddRow("NAME", "PUBLISH_VERSION", "SUCCEEDED", "HASH", "BEGIN_TIME", "STATUS", "SIZE")
   581  		table.AddRow("first-vela-app-v1", "", "false", "1c3d847600ac0514", "", "NotStart", "")
   582  		Expect(strings.ReplaceAll(out.String(), " ", "")).To(ContainSubstring(strings.ReplaceAll(table.String(), " ", "")))
   583  	})
   584  
   585  	It("Test normal case with yaml format", func() {
   586  
   587  		// setup view
   588  		setupView()
   589  
   590  		// setup application
   591  		app := v1beta1.ApplicationRevision{}
   592  		Expect(yaml.Unmarshal([]byte(firstVelaAppRev), &app)).Should(BeNil())
   593  		Expect(k8sClient.Create(context.TODO(), &app)).Should(BeNil())
   594  
   595  		// override args
   596  		format = "yaml"
   597  
   598  		Expect(getRevision(ctx, arg, format, out, name, namespace, def)).To(Succeed())
   599  		Expect(out.String()).Should(SatisfyAll(
   600  			ContainSubstring("app.oam.dev/name: first-vela-app"),
   601  			ContainSubstring("name: first-vela-app-v1"),
   602  			ContainSubstring("- name: express-server"),
   603  			ContainSubstring("succeeded: false"),
   604  		))
   605  	})
   606  
   607  	It("Test normal case with returning definition", func() {
   608  
   609  		// setup view
   610  		setupView()
   611  
   612  		// setup application
   613  		app := v1beta1.ApplicationRevision{}
   614  		Expect(yaml.Unmarshal([]byte(firstVelaAppRev), &app)).Should(BeNil())
   615  		Expect(k8sClient.Create(context.TODO(), &app)).Should(BeNil())
   616  
   617  		// override args
   618  		def = "webservice"
   619  
   620  		Expect(getRevision(ctx, arg, format, out, name, namespace, def)).To(Succeed())
   621  		Expect(out.String()).Should(Equal(compDef))
   622  	})
   623  
   624  	It("Test normal case with returning unknown definition", func() {
   625  
   626  		// setup view
   627  		setupView()
   628  
   629  		// setup application
   630  		app := v1beta1.ApplicationRevision{}
   631  		Expect(yaml.Unmarshal([]byte(firstVelaAppRev), &app)).Should(BeNil())
   632  		Expect(k8sClient.Create(context.TODO(), &app)).Should(BeNil())
   633  
   634  		// prepare args
   635  		def = "webservice1"
   636  
   637  		Expect(getRevision(ctx, arg, format, out, name, namespace, def)).To(Succeed())
   638  		Expect(out.String()).Should(Equal(fmt.Sprintf("No such definition %s", def)))
   639  	})
   640  })
   641  
   642  func TestPrintApprev(t *testing.T) {
   643  
   644  	tiFormat := "2006-01-02T15:04:05.000Z"
   645  	tiStr := "2022-08-12T11:45:26.371Z"
   646  	ti, err := time.Parse(tiFormat, tiStr)
   647  	assert.Nil(t, err)
   648  
   649  	cases := map[string]struct {
   650  		out    *bytes.Buffer
   651  		apprev v1beta1.ApplicationRevision
   652  		exp    string
   653  	}{
   654  		"NotStart": {out: &bytes.Buffer{}, apprev: v1beta1.ApplicationRevision{
   655  			ObjectMeta: metav1.ObjectMeta{
   656  				Name:      "test-apprev0",
   657  				Namespace: "dev",
   658  			},
   659  			Spec:   v1beta1.ApplicationRevisionSpec{},
   660  			Status: v1beta1.ApplicationRevisionStatus{},
   661  		}, exp: tableOut("test-apprev0", "", "false", "", "", "NotStart"),
   662  		},
   663  		"Succeeded": {out: &bytes.Buffer{}, apprev: v1beta1.ApplicationRevision{
   664  			ObjectMeta: metav1.ObjectMeta{
   665  				Name:      "test-apprev1",
   666  				Namespace: "dev1",
   667  				Labels: map[string]string{
   668  					oam.LabelAppRevisionHash: "1111231adfdf",
   669  				},
   670  			},
   671  			Spec: v1beta1.ApplicationRevisionSpec{
   672  				ApplicationRevisionCompressibleFields: v1beta1.ApplicationRevisionCompressibleFields{
   673  					Application: v1beta1.Application{
   674  						ObjectMeta: metav1.ObjectMeta{
   675  							Name:      "test-app1",
   676  							Namespace: "dev2",
   677  						},
   678  					},
   679  				},
   680  			},
   681  			Status: v1beta1.ApplicationRevisionStatus{
   682  				Workflow: &common2.WorkflowStatus{
   683  					StartTime: metav1.Time{
   684  						Time: ti,
   685  					},
   686  				},
   687  				Succeeded: true,
   688  			},
   689  		}, exp: tableOut("test-apprev1", "", "true", "1111231adfdf", "2022-08-12 11:45:26", "Succeeded")},
   690  		"Failed": {out: &bytes.Buffer{}, apprev: v1beta1.ApplicationRevision{
   691  			ObjectMeta: metav1.ObjectMeta{
   692  				Name:      "test-apprev2",
   693  				Namespace: "dev2",
   694  			},
   695  			Spec: v1beta1.ApplicationRevisionSpec{},
   696  			Status: v1beta1.ApplicationRevisionStatus{
   697  				Workflow: &common2.WorkflowStatus{
   698  					StartTime: metav1.Time{
   699  						Time: ti,
   700  					},
   701  					Terminated: true,
   702  				},
   703  			},
   704  		}, exp: tableOut("test-apprev2", "", "false", "", "2022-08-12 11:45:26", "Failed"),
   705  		},
   706  		"Executing or Failed": {out: &bytes.Buffer{}, apprev: v1beta1.ApplicationRevision{
   707  			ObjectMeta: metav1.ObjectMeta{
   708  				Name:      "test-apprev3",
   709  				Namespace: "dev3",
   710  			},
   711  			Spec: v1beta1.ApplicationRevisionSpec{},
   712  			Status: v1beta1.ApplicationRevisionStatus{
   713  				Workflow: &common2.WorkflowStatus{
   714  					StartTime: metav1.Time{
   715  						Time: ti,
   716  					},
   717  				},
   718  			},
   719  		}, exp: tableOut("test-apprev3", "", "false", "", "2022-08-12 11:45:26", "Executing or Failed"),
   720  		},
   721  		"Compressed": {out: &bytes.Buffer{}, apprev: v1beta1.ApplicationRevision{
   722  			ObjectMeta: metav1.ObjectMeta{
   723  				Name:      "test-apprev3",
   724  				Namespace: "dev3",
   725  			},
   726  			Spec: v1beta1.ApplicationRevisionSpec{
   727  				Compression: v1beta1.ApplicationRevisionCompression{
   728  					CompressedText: compression.CompressedText{
   729  						Type: "zstd",
   730  					},
   731  				},
   732  			},
   733  			Status: v1beta1.ApplicationRevisionStatus{
   734  				Workflow: &common2.WorkflowStatus{
   735  					StartTime: metav1.Time{
   736  						Time: ti,
   737  					},
   738  				},
   739  			},
   740  		}, exp: tableOut("test-apprev3", "", "false", "", "2022-08-12 11:45:26", "Executing or Failed"),
   741  		},
   742  	}
   743  
   744  	for name, tc := range cases {
   745  		t.Run(name, func(t *testing.T) {
   746  			printApprevs(tc.out, []v1beta1.ApplicationRevision{tc.apprev})
   747  			assert.Contains(t, strings.ReplaceAll(tc.out.String(), " ", ""), strings.ReplaceAll(tc.out.String(), " ", ""))
   748  			if tc.apprev.Spec.Compression.Type != compression.Uncompressed {
   749  				assert.Contains(t, tc.out.String(), "Compressed")
   750  			}
   751  		})
   752  	}
   753  }
   754  
   755  func tableOut(name, pv, s, hash, bt, status string) string {
   756  	table := newUITable().AddRow("NAME", "PUBLISH_VERSION", "SUCCEEDED", "HASH", "BEGIN_TIME", "STATUS", "SIZE")
   757  	table.AddRow(name, pv, s, hash, bt, status)
   758  
   759  	return table.String()
   760  }
   761  
   762  func setupView() {
   763  	viewContent, err := os.ReadFile("../../charts/vela-core/templates/velaql/application-revision.yaml")
   764  	Expect(err).Should(BeNil())
   765  	viewContent = bytes.ReplaceAll(viewContent, []byte("{{ include \"systemDefinitionNamespace\" . }}"), []byte(types.DefaultKubeVelaNS))
   766  	cm := &v1.ConfigMap{}
   767  	Expect(yaml.Unmarshal(viewContent, cm)).Should(BeNil())
   768  	Expect(k8sClient.Create(context.TODO(), cm)).Should(BeNil())
   769  }