Version: v1.2

CUE Components

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

First, generate ComponentDefinition scaffolds via vela def init with existed YAML file.

The YAML file:

  1. apiVersion: "apps/v1"
  2. kind: "Deployment"
  3. spec:
  4. selector:
  5. matchLabels:
  6. "app.oam.dev/component": "name"
  7. template:
  8. metadata:
  9. labels:
  10. "app.oam.dev/component": "name"
  11. spec:
  12. containers:
  13. - name: "name"
  14. image: "image"

Generate ComponentDefinition based on the YAML file:

  1. vela def init stateless -t component --template-yaml ./stateless.yaml -o stateless.cue

It generates a file:

  1. $ cat stateless.cue
  2. stateless: {
  3. annotations: {}
  4. attributes: workload: definition: {
  5. apiVersion: "<change me> apps/v1"
  6. kind: "<change me> Deployment"
  7. }
  8. description: ""
  9. labels: {}
  10. type: "component"
  11. }
  12. template: {
  13. output: {
  14. spec: {
  15. selector: matchLabels: "app.oam.dev/component": "name"
  16. template: {
  17. metadata: labels: "app.oam.dev/component": "name"
  18. spec: containers: [{
  19. name: "name"
  20. image: "image"
  21. }]
  22. }
  23. }
  24. apiVersion: "apps/v1"
  25. kind: "Deployment"
  26. }
  27. outputs: {}
  28. parameters: {}
  29. }

In detail:

  • .spec.workload is required to indicate the workload type of this component.
  • .spec.schematic.cue.template is a CUE template, specifically:
    • The output filed defines the template for the abstraction.
    • The parameter filed defines the template parameters, i.e. the configurable properties exposed in the Applicationabstraction (and JSON schema will be automatically generated based on them).

Add parameters in this auto-generated custom component file :

  1. stateless: {
  2. annotations: {}
  3. attributes: workload: definition: {
  4. apiVersion: "<change me> apps/v1"
  5. kind: "<change me> Deployment"
  6. }
  7. description: ""
  8. labels: {}
  9. type: "component"
  10. }
  11. template: {
  12. output: {
  13. spec: {
  14. selector: matchLabels: "app.oam.dev/component": parameter.name
  15. template: {
  16. metadata: labels: "app.oam.dev/component": parameter.name
  17. spec: containers: [{
  18. name: parameter.name
  19. image: parameter.image
  20. }]
  21. }
  22. }
  23. apiVersion: "apps/v1"
  24. kind: "Deployment"
  25. }
  26. outputs: {}
  27. parameters: {
  28. name: string
  29. image: string
  30. }
  31. }

You can use vela def vet to validate the format:

  1. $ vela def vet stateless.cue
  2. Validation succeed.

Declare another component named task which is an abstraction for run-to-completion workload.

  1. vela def init task -t component -o task.cue

It generates a file:

  1. $ cat task.cue
  2. task: {
  3. annotations: {}
  4. attributes: workload: definition: {
  5. apiVersion: "<change me> apps/v1"
  6. kind: "<change me> Deployment"
  7. }
  8. description: ""
  9. labels: {}
  10. type: "component"
  11. }
  12. template: {
  13. output: {}
  14. parameter: {}
  15. }

Edit the generated component file:

  1. task: {
  2. annotations: {}
  3. attributes: workload: definition: {
  4. apiVersion: "batch/v1"
  5. kind: "Job"
  6. }
  7. description: ""
  8. labels: {}
  9. type: "component"
  10. }
  11. template: {
  12. output: {
  13. apiVersion: "batch/v1"
  14. kind: "Job"
  15. spec: {
  16. parallelism: parameter.count
  17. completions: parameter.count
  18. template: spec: {
  19. restartPolicy: parameter.restart
  20. containers: [{
  21. image: parameter.image
  22. if parameter["cmd"] != _|_ {
  23. command: parameter.cmd
  24. }
  25. }]
  26. }
  27. }
  28. }
  29. parameter: {
  30. count: *1 | int
  31. image: string
  32. restart: *"Never" | string
  33. cmd?: [...string]
  34. }
  35. }

Apply above ComponentDefinition files to your Kubernetes cluster:

  1. $ vela def apply stateless.cue
  2. ComponentDefinition stateless created in namespace vela-system.
  3. $ vela def apply task.cue
  4. ComponentDefinition task created in namespace vela-system.

Declare an Application

The ComponentDefinition can be instantiated in Application abstraction as below:

  1. apiVersion: core.oam.dev/v1alpha2
  2. kind: Application
  3. metadata:
  4. name: website
  5. spec:
  6. components:
  7. - name: hello
  8. type: stateless
  9. properties:
  10. image: crccheck/hello-world
  11. name: mysvc
  12. - name: countdown
  13. type: task
  14. properties:
  15. image: centos:7
  16. cmd:
  17. - "bin/bash"
  18. - "-c"
  19. - "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.

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: backend
  5. ... # skip tons of metadata info
  6. spec:
  7. template:
  8. spec:
  9. containers:
  10. - name: mysvc
  11. image: crccheck/hello-world
  12. metadata:
  13. labels:
  14. app.oam.dev/component: mysvc
  15. selector:
  16. matchLabels:
  17. app.oam.dev/component: mysvc
  18. ---
  19. apiVersion: batch/v1
  20. kind: Job
  21. metadata:
  22. name: countdown
  23. ... # skip tons of metadata info
  24. spec:
  25. parallelism: 1
  26. completions: 1
  27. template:
  28. metadata:
  29. name: countdown
  30. spec:
  31. containers:
  32. - name: countdown
  33. image: 'centos:7'
  34. command:
  35. - bin/bash
  36. - '-c'
  37. - for i in 9 8 7 6 5 4 3 2 1 ; do echo $i ; done
  38. restartPolicy: 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).

  1. context: {
  2. appName: string
  3. name: string
  4. }

For example, let’s say you want to use the component name filled in by users as the container name in the workload instance:

  1. parameter: {
  2. image: string
  3. }
  4. output: {
  5. ...
  6. spec: {
  7. containers: [{
  8. name: context.name
  9. image: parameter.image
  10. }]
  11. }
  12. ...
  13. }

Note that context information are auto-injected before resources are applied to target cluster.

Full available information in CUE context

Context VariableDescription
context.appRevisionThe revision of the application
context.appRevisionNumThe revision number(int type) of the application, e.g., context.appRevisionNum will be 1 if context.appRevision is app-v1
context.appNameThe name of the application
context.nameThe name of the component of the application
context.namespaceThe namespace of the application
context.outputThe 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:

  1. outputs: <unique-name>:
  2. <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:

  1. webserver: {
  2. annotations: {}
  3. attributes: workload: definition: {
  4. apiVersion: "apps/v1"
  5. kind: "Deployment"
  6. }
  7. description: ""
  8. labels: {}
  9. type: "component"
  10. }
  11. template: {
  12. output: {
  13. apiVersion: "apps/v1"
  14. kind: "Deployment"
  15. spec: {
  16. selector: matchLabels: {
  17. "app.oam.dev/component": context.name
  18. }
  19. template: {
  20. metadata: labels: {
  21. "app.oam.dev/component": context.name
  22. }
  23. spec: {
  24. containers: [{
  25. name: context.name
  26. image: parameter.image
  27. if parameter["cmd"] != _|_ {
  28. command: parameter.cmd
  29. }
  30. if parameter["env"] != _|_ {
  31. env: parameter.env
  32. }
  33. if context["config"] != _|_ {
  34. env: context.config
  35. }
  36. ports: [{
  37. containerPort: parameter.port
  38. }]
  39. if parameter["cpu"] != _|_ {
  40. resources: {
  41. limits:
  42. cpu: parameter.cpu
  43. requests:
  44. cpu: parameter.cpu
  45. }
  46. }
  47. }]
  48. }
  49. }
  50. }
  51. }
  52. // an extra template
  53. outputs: service: {
  54. apiVersion: "v1"
  55. kind: "Service"
  56. spec: {
  57. selector: {
  58. "app.oam.dev/component": context.name
  59. }
  60. ports: [
  61. {
  62. port: parameter.port
  63. targetPort: parameter.port
  64. },
  65. ]
  66. }
  67. }
  68. parameter: {
  69. image: string
  70. cmd?: [...string]
  71. port: *80 | int
  72. env?: [...{
  73. name: string
  74. value?: string
  75. valueFrom?: {
  76. secretKeyRef: {
  77. name: string
  78. key: string
  79. }
  80. }
  81. }]
  82. cpu?: string
  83. }
  84. }

Apply to your Kubernetes cluster:

  1. $ vela def apply webserver.cue
  2. ComponentDefinition webserver created in namespace vela-system.

The user could now declare an Application with it:

  1. apiVersion: core.oam.dev/v1beta1
  2. kind: Application
  3. metadata:
  4. name: webserver-demo
  5. namespace: default
  6. spec:
  7. components:
  8. - name: hello-world
  9. type: webserver
  10. properties:
  11. image: crccheck/hello-world
  12. port: 8000
  13. env:
  14. - name: "foo"
  15. value: "bar"
  16. cpu: "100m"

It will generate and manage below API resources in target cluster:

  1. kubectl get deployment
  1. NAME READY UP-TO-DATE AVAILABLE AGE
  2. hello-world-v1 1/1 1 1 15s
  1. kubectl get svc
  1. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  2. hello-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.