Version: v1.0
How-to
In this section, it will introduce how to use CUE to declare app components via ComponentDefinition.
Before reading this part, please make sure you’ve learned the Definition CRD in KubeVela.
Declare ComponentDefinition
Here is a CUE based ComponentDefinition example which provides a abstraction for stateless workload type:
apiVersion: core.oam.dev/v1beta1kind: ComponentDefinitionmetadata:name: statelessspec:workload:definition:apiVersion: apps/v1kind: Deploymentschematic:cue:template: |parameter: {name: stringimage: string}output: {apiVersion: "apps/v1"kind: "Deployment"spec: {selector: matchLabels: {"app.oam.dev/component": parameter.name}template: {metadata: labels: {"app.oam.dev/component": parameter.name}spec: {containers: [{name: parameter.nameimage: parameter.image}]}}}}
In detail:
.spec.workloadis required to indicate the workload type of this component..spec.schematic.cue.templateis a CUE template, specifically:- The
outputfiled defines the template for the abstraction. - The
parameterfiled defines the template parameters, i.e. the configurable properties exposed in theApplicationabstraction (and JSON schema will be automatically generated based on them).
- The
Let’s declare another component named task, i.e. an abstraction for run-to-completion workload.
apiVersion: core.oam.dev/v1beta1kind: ComponentDefinitionmetadata:name: taskannotations:definition.oam.dev/description: "Describes jobs that run code or a script to completion."spec:workload:definition:apiVersion: batch/v1kind: Jobschematic:cue:template: |output: {apiVersion: "batch/v1"kind: "Job"spec: {parallelism: parameter.countcompletions: parameter.counttemplate: spec: {restartPolicy: parameter.restartcontainers: [{image: parameter.imageif parameter["cmd"] != _|_ {command: parameter.cmd}}]}}}parameter: {count: *1 | intimage: stringrestart: *"Never" | stringcmd?: [...string]}
Save above ComponentDefinition objects to files and install them to your Kubernetes cluster by $ kubectl apply -f stateless-def.yaml task-def.yaml
Declare an Application
The ComponentDefinition can be instantiated in Application abstraction as below:
apiVersion: core.oam.dev/v1alpha2kind: Applicationmetadata:name: websitespec:components:- name: hellotype: statelessproperties:image: crccheck/hello-worldname: mysvc- name: countdowntype: taskproperties:image: centos:7cmd:- "bin/bash"- "-c"- "for i in 9 8 7 6 5 4 3 2 1 ; do echo $i ; done"
Under The Hood
Above application resource will generate and manage following Kubernetes resources in your target cluster based on the output in CUE template and user input in Application properties.
apiVersion: apps/v1kind: Deploymentmetadata:name: backend... # skip tons of metadata infospec:template:spec:containers:- name: mysvcimage: crccheck/hello-worldmetadata:labels:app.oam.dev/component: mysvcselector:matchLabels:app.oam.dev/component: mysvc---apiVersion: batch/v1kind: Jobmetadata:name: countdown... # skip tons of metadata infospec:parallelism: 1completions: 1template:metadata:name: countdownspec:containers:- name: countdownimage: 'centos:7'command:- bin/bash- '-c'- for i in 9 8 7 6 5 4 3 2 1 ; do echo $i ; donerestartPolicy: Never
CUE Context
KubeVela allows you to reference the runtime information of your application via context keyword.
The most widely used context is application name(context.appName) component name(context.name).
context: {appName: stringname: string}
For example, let’s say you want to use the component name filled in by users as the container name in the workload instance:
parameter: {image: string}output: {...spec: {containers: [{name: context.nameimage: parameter.image}]}...}
Note that
contextinformation are auto-injected before resources are applied to target cluster.
Full available information in CUE context
| Context Variable | Description |
|---|---|
context.appRevision | The revision of the application |
context.appName | The name of the application |
context.name | The name of the component of the application |
context.namespace | The namespace of the application |
context.output | The rendered workload API resource of the component, this usually used in trait |
context.outputs.<resourceName> | The rendered trait API resource of the component, this usually used in trait |
Composition
It’s common that a component definition is composed by multiple API resources, for example, a webserver component that is composed by a Deployment and a Service. CUE is a great solution to achieve this in simplified primitives.
Another approach to do composition in KubeVela of course is using Helm.
How-to
KubeVela requires you to define the template of workload type in output section, and leave all the other resource templates in outputs section with format as below:
outputs: <unique-name>:<full template data>
The reason for this requirement is KubeVela needs to know it is currently rendering a workload so it could do some “magic” like patching annotations/labels or other data during it.
Below is the example for webserver definition:
apiVersion: core.oam.dev/v1beta1kind: ComponentDefinitionmetadata:name: webserverannotations:definition.oam.dev/description: "webserver is a combo of Deployment + Service"spec:workload:definition:apiVersion: apps/v1kind: Deploymentschematic:cue:template: |output: {apiVersion: "apps/v1"kind: "Deployment"spec: {selector: matchLabels: {"app.oam.dev/component": context.name}template: {metadata: labels: {"app.oam.dev/component": context.name}spec: {containers: [{name: context.nameimage: parameter.imageif parameter["cmd"] != _|_ {command: parameter.cmd}if parameter["env"] != _|_ {env: parameter.env}if context["config"] != _|_ {env: context.config}ports: [{containerPort: parameter.port}]if parameter["cpu"] != _|_ {resources: {limits:cpu: parameter.cpurequests:cpu: parameter.cpu}}}]}}}}// an extra templateoutputs: service: {apiVersion: "v1"kind: "Service"spec: {selector: {"app.oam.dev/component": context.name}ports: [{port: parameter.porttargetPort: parameter.port},]}}parameter: {image: stringcmd?: [...string]port: *80 | intenv?: [...{name: stringvalue?: stringvalueFrom?: {secretKeyRef: {name: stringkey: string}}}]cpu?: string}
The user could now declare an Application with it:
apiVersion: core.oam.dev/v1beta1kind: Applicationmetadata:name: webserver-demonamespace: defaultspec:components:- name: hello-worldtype: webserverproperties:image: crccheck/hello-worldport: 8000env:- name: "foo"value: "bar"cpu: "100m"
It will generate and manage below API resources in target cluster:
$ kubectl get deploymentNAME READY UP-TO-DATE AVAILABLE AGEhello-world-v1 1/1 1 1 15s$ kubectl get svcNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEhello-world-trait-7bdcff98f7 ClusterIP <your ip> <none> 8000/TCP 32s
What’s Next
Please check the Learning CUE documentation about why we support CUE as first-class templating solution and more details about using CUE efficiently.