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:

  1. apiVersion: core.oam.dev/v1beta1
  2. kind: ComponentDefinition
  3. metadata:
  4. name: stateless
  5. spec:
  6. workload:
  7. definition:
  8. apiVersion: apps/v1
  9. kind: Deployment
  10. schematic:
  11. cue:
  12. template: |
  13. parameter: {
  14. name: string
  15. image: string
  16. }
  17. output: {
  18. apiVersion: "apps/v1"
  19. kind: "Deployment"
  20. spec: {
  21. selector: matchLabels: {
  22. "app.oam.dev/component": parameter.name
  23. }
  24. template: {
  25. metadata: labels: {
  26. "app.oam.dev/component": parameter.name
  27. }
  28. spec: {
  29. containers: [{
  30. name: parameter.name
  31. image: parameter.image
  32. }]
  33. }
  34. }
  35. }
  36. }

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).

Let’s declare another component named task, i.e. an abstraction for run-to-completion workload.

  1. apiVersion: core.oam.dev/v1beta1
  2. kind: ComponentDefinition
  3. metadata:
  4. name: task
  5. annotations:
  6. definition.oam.dev/description: "Describes jobs that run code or a script to completion."
  7. spec:
  8. workload:
  9. definition:
  10. apiVersion: batch/v1
  11. kind: Job
  12. schematic:
  13. cue:
  14. template: |
  15. output: {
  16. apiVersion: "batch/v1"
  17. kind: "Job"
  18. spec: {
  19. parallelism: parameter.count
  20. completions: parameter.count
  21. template: spec: {
  22. restartPolicy: parameter.restart
  23. containers: [{
  24. image: parameter.image
  25. if parameter["cmd"] != _|_ {
  26. command: parameter.cmd
  27. }
  28. }]
  29. }
  30. }
  31. }
  32. parameter: {
  33. count: *1 | int
  34. image: string
  35. restart: *"Never" | string
  36. cmd?: [...string]
  37. }

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:

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

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