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 }