Component Definition
First, generate ComponentDefinition
scaffolds via vela def init
with existed YAML file.
The YAML file:
stateless.yaml
Generate ComponentDefinition
based on the YAML file:
vela def init stateless -t component --template-yaml ./stateless.yaml -o stateless.cue
It generates a file:
stateless.cue
stateless: {
annotations: {}
attributes: workload: definition: {
apiVersion: "<change me> apps/v1"
kind: "<change me> Deployment"
}
description: ""
labels: {}
type: "component"
}
template: {
output: {
spec: {
selector: matchLabels: "app.oam.dev/component": "name"
template: {
metadata: labels: "app.oam.dev/component": "name"
spec: containers: [{
name: "name"
image: "image"
}]
}
}
apiVersion: "apps/v1"
kind: "Deployment"
}
outputs: {}
parameter: {}
}
In detail:
- The
stateless
is the name of component definition, it can be defined by yourself when initialize the component. stateless.attributes.workload
indicates the workload type of this component, it can help integrate with traits that apply to this kind of workload.template
is a CUE template, specifically:- The
output
andoutputs
fields define the resources that the component will be composed. - The
parameter
field defines the parameters of the component, i.e. the configurable properties exposed in theApplication
(and schema will be automatically generated based on them for end users to learn this component).
- The
Add parameters in this auto-generated custom component file :
stateless: {
annotations: {}
attributes: workload: definition: {
apiVersion: "apps/v1"
kind: "Deployment"
}
description: ""
labels: {}
type: "component"
}
template: {
output: {
spec: {
selector: matchLabels: "app.oam.dev/component": parameter.name
template: {
metadata: labels: "app.oam.dev/component": parameter.name
spec: containers: [{
name: parameter.name
image: parameter.image
}]
}
}
apiVersion: "apps/v1"
kind: "Deployment"
}
outputs: {}
parameter: {
name: string
image: string
}
}
You can use vela def vet
to validate the format:
vela def vet stateless.cue
expected output
Validation succeed.
Apply above ComponentDefinition
to your Kubernetes cluster to make it work:
vela def apply stateless.cue
expected output
ComponentDefinition stateless created in namespace vela-system.
Then the end user can check the schema and use it in an application now:
vela show stateless
expected output
# Specification
+-------+-------------+--------+----------+---------+
| NAME | DESCRIPTION | TYPE | REQUIRED | DEFAULT |
+-------+-------------+--------+----------+---------+
| name | | string | true | |
| image | | string | true | |
+-------+-------------+--------+----------+---------+
Declare another component named task
which is an abstraction for run-to-completion workload works the same.
Check the details for another example to define a task based component.
vela def init task -t component -o task.cue
It generates a file:
Edit the generated component file:
task: {
annotations: {}
attributes: workload: definition: {
apiVersion: "batch/v1"
kind: "Job"
}
description: ""
labels: {}
type: "component"
}
template: {
output: {
apiVersion: "batch/v1"
kind: "Job"
spec: {
parallelism: parameter.count
completions: parameter.count
template: spec: {
restartPolicy: parameter.restart
containers: [{
image: parameter.image
if parameter["cmd"] != _|_ {
command: parameter.cmd
}
}]
}
}
}
parameter: {
count: *1 | int
image: string
restart: *"Never" | string
cmd?: [...string]
}
}
Apply above ComponentDefinition
files to your Kubernetes cluster to make it work:
$ vela def apply task.cue
ComponentDefinition task created in namespace vela-system.
Now let’s use the stateless
and task
component type.
Declare an Application
using this component
The ComponentDefinition
can be instantiated in Application
abstraction as below:
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: website
spec:
components:
type: stateless
properties:
image: oamdev/hello-world
name: mysvc
- name: countdown
type: task
properties:
image: centos:7
- "bin/bash"
- "-c"
- "for i in 9 8 7 6 5 4 3 2 1 ; do echo $i ; done"
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/v1
kind: Deployment
metadata:
name: backend
... # skip tons of metadata info
spec:
template:
spec:
containers:
- name: mysvc
image: oamdev/hello-world
metadata:
labels:
app.oam.dev/component: mysvc
selector:
matchLabels:
app.oam.dev/component: mysvc
---
apiVersion: batch/v1
kind: Job
metadata:
name: countdown
... # skip tons of metadata info
spec:
parallelism: 1
completions: 1
template:
metadata:
name: countdown
spec:
containers:
- name: countdown
image: 'centos:7'
command:
- bin/bash
- '-c'
- for i in 9 8 7 6 5 4 3 2 1 ; do echo $i ; done
restartPolicy: Never
You can also use dry run to show what the yaml results will be rendered for debugging.
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: string
name: 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.name
image: parameter.image
}]
}
...
}
tip
Note that context
information are auto-injected before resources are applied to target cluster.
The list of all available context variables are listed at last of this doc.
Compose resources in one component
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.
tip
Compare to using Helm, this approach gives your more flexibility as you can control the abstraction any time and integrate with traits, workflows in KubeVela better.
KubeVela requires you to define the template of main workload in output
section, and leave all the other resource templates in outputs
section with format as below:
output: {
<template of main workload structural data>
}
outputs: {
<unique-name>: {
<template of auxiliary resource structural data>
}
}
note
The reason for this requirement is KubeVela needs to know it is currently rendering a workload so it could do some “magic” by traits such like patching annotations/labels or other data during it.
Below is the example for webserver
definition, let’s use a demo to show how to use it:
webserver.cue
webserver: {
annotations: {}
attributes: workload: definition: {
apiVersion: "apps/v1"
kind: "Deployment"
}
description: ""
labels: {}
type: "component"
}
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.name
image: parameter.image
if 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.cpu
requests:
cpu: parameter.cpu
}
}
}]
}
}
}
}
// an extra template
outputs: service: {
apiVersion: "v1"
spec: {
selector: {
"app.oam.dev/component": context.name
}
ports: [
{
port: parameter.port
targetPort: parameter.port
},
]
}
}
parameter: {
image: string
port: *80 | int
env?: [...{
name: string
value?: string
valueFrom?: {
secretKeyRef: {
name: string
key: string
}
}
}]
cpu?: string
}
}
Apply to your Kubernetes cluster:
vela def apply webserver.cue
expected output
ComponentDefinition webserver created in namespace vela-system.
The user could now declare an Application
with it:
webserver-app.yaml
Create this application by:
vela up -f webserver-app.yaml
vela status webserver-demo --tree --detail
expected output
CLUSTER NAMESPACE RESOURCE STATUS APPLY_TIME DETAIL
local ─── default ─┬─ Service/hello-webserver-auxiliaryworkload-685d98b6d9 updated 2022-10-15 21:58:35 Type: ClusterIP
│ Cluster-IP: 10.43.255.55
│ External-IP: <none>
│ Port(s): 8000/TCP
│ Age: 66s
└─ Deployment/hello-webserver updated 2022-10-15 21:58:35 Ready: 1/1 Up-to-date: 1
Available: 1 Age: 66s
You can also define health check policy and status message when a component deployed and tell the real status to end users.
The spec of health check is <component-type-name>.attributes.status.healthPolicy
.
If not defined, the health result will always be true
, which means it will be marked as healthy immediately after resources applied to Kubernetes. You can define a CUE expression in it to notify if the component is healthy or not.
The keyword is isHealth
, the result of CUE expression must be bool
type.
KubeVela runtime will evaluate the CUE expression periodically until it becomes healthy. Every time the controller will get all the Kubernetes resources and fill them into the context
variables.
So the context will contain following information:
context:{
name: <component name>
appName: <app name>
output: <K8s workload resource>
outputs: {
<resource1>: <K8s trait resource1>
<resource2>: <K8s trait resource2>
}
}
The example of health check likes below:
webserver: {
type: "component"
...
attributes: {
status: {
healthPolicy: #"""
isHealth: (context.output.status.readyReplicas > 0) && (context.output.status.readyReplicas == context.output.status.replicas)
"""#
}
}
}
You can also use the parameter
defined in the template like:
webserver: {
type: "component"
...
attributes: {
status: {
healthPolicy: #"""
isHealth: (context.output.status.readyReplicas > 0) && (context.output.status.readyReplicas == parameter.replicas)
"""#
}
}
template: {
parameter: {
replicas: int
}
...
}
The health check result will be recorded into the corresponding component in .status.services
of Application
resource.
apiVersion: core.oam.dev/v1beta1
kind: Application
status:
...
services:
- healthy: true
name: myweb
...
status: running
The spec of custom status is <component-type-name>.attributes.status.customStatus
, it shares the same mechanism with the health check.
The keyword in CUE expression is message
, the result must be string
type.
Application CRD controller will evaluate the CUE expression after the health check succeed.
The example of custom status likes below:
webserver: {
type: "component"
...
attributes: {
status: {
customStatus: #"""
ready: {
readyReplicas: *0 | int
} & {
if context.output.status.readyReplicas != _|_ {
readyReplicas: context.output.status.readyReplicas
}
}
message: "Ready:\(ready.readyReplicas)/\(context.output.spec.replicas)"
"""#
}
}
}
The message will be recorded into the corresponding component in .status.services
of Application
resource like below.
apiVersion: core.oam.dev/v1beta1
kind: Application
status:
...
services:
- healthy: false
message: Ready:1/1
name: express-server
Please refer to this doc for more examples.
Full available context
in Component
Context Variable | Description | Type |
---|---|---|
context.clusterVersion.major | The major version of the runtime Kubernetes cluster. | string |
context.clusterVersion.gitVersion | The gitVersion of the runtime Kubernetes cluster. | string |
context.clusterVersion.platform | The platform information of the runtime Kubernetes cluster. | string |
context.clusterVersion.minor | The minor version of the runtime Kubernetes cluster. | int |
The cluster version context info can be used for graceful upgrade of definition. For example, you can define different API according to the cluster version.
outputs: ingress: {
if context.clusterVersion.minor < 19 {
apiVersion: "networking.k8s.io/v1beta1"
}
if context.clusterVersion.minor >= 19 {
apiVersion: "networking.k8s.io/v1"
}
kind: "Ingress"
}
Or use string contain pattern for this usage:
KubeVela is fully programmable via CUE, while it leverage Kubernetes as control plane and align with the API in yaml. As a result, the CUE definition will be converted as Kubernetes API when applied into cluster.
The component definition will be in the following API format:
apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
name: <ComponentDefinition name>
annotations:
definition.oam.dev/description: <Function description>
spec:
workload: # Workload Capability Indicator
definition:
apiVersion: <Kubernetes Workload resource group>
kind: <Kubernetes Workload types>
schematic: # Component description
cue: # Details of components defined by CUE language
More examples to learn
You can check the following resources for more examples:
- Definitions defined in addons in the .