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:

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

    It generates a file:

    stateless.cue

    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": "name"
    15. template: {
    16. metadata: labels: "app.oam.dev/component": "name"
    17. spec: containers: [{
    18. name: "name"
    19. image: "image"
    20. }]
    21. }
    22. }
    23. apiVersion: "apps/v1"
    24. kind: "Deployment"
    25. }
    26. outputs: {}
    27. parameter: {}
    28. }

    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 and outputs 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 the Application (and schema will be automatically generated based on them for end users to learn this component).

    Add parameters in this auto-generated custom component file :

    1. stateless: {
    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. 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. parameter: {
    28. name: string
    29. image: string
    30. }
    31. }

    You can use vela def vet to validate the format:

    1. vela def vet stateless.cue

    expected output

    1. Validation succeed.

    Apply above ComponentDefinition to your Kubernetes cluster to make it work:

    1. vela def apply stateless.cue

    expected output

    1. ComponentDefinition stateless created in namespace vela-system.

    Then the end user can check the schema and use it in an application now:

    1. vela show stateless

    expected output

    1. # Specification
    2. +-------+-------------+--------+----------+---------+
    3. | NAME | DESCRIPTION | TYPE | REQUIRED | DEFAULT |
    4. +-------+-------------+--------+----------+---------+
    5. | name | | string | true | |
    6. | image | | string | true | |
    7. +-------+-------------+--------+----------+---------+

    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.

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

    It generates a file:

    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 to make it work:

    1. $ vela def apply task.cue
    2. 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:

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

    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: oamdev/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

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

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

    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.

    Component Definition - 图2tip

    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:

    1. output: {
    2. <template of main workload structural data>
    3. }
    4. outputs: {
    5. <unique-name>: {
    6. <template of auxiliary resource structural data>
    7. }
    8. }

    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

    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. spec: {
    56. selector: {
    57. "app.oam.dev/component": context.name
    58. }
    59. ports: [
    60. {
    61. port: parameter.port
    62. targetPort: parameter.port
    63. },
    64. ]
    65. }
    66. }
    67. parameter: {
    68. image: string
    69. port: *80 | int
    70. env?: [...{
    71. name: string
    72. value?: string
    73. valueFrom?: {
    74. secretKeyRef: {
    75. name: string
    76. key: string
    77. }
    78. }
    79. }]
    80. cpu?: string
    81. }
    82. }

    Apply to your Kubernetes cluster:

    1. vela def apply webserver.cue

    expected output

    1. ComponentDefinition webserver created in namespace vela-system.

    The user could now declare an Application with it:

    webserver-app.yaml

    Create this application by:

    1. vela up -f webserver-app.yaml
    1. vela status webserver-demo --tree --detail

    expected output

    1. CLUSTER NAMESPACE RESOURCE STATUS APPLY_TIME DETAIL
    2. local ─── default ─┬─ Service/hello-webserver-auxiliaryworkload-685d98b6d9 updated 2022-10-15 21:58:35 Type: ClusterIP
    3. Cluster-IP: 10.43.255.55
    4. External-IP: <none>
    5. Port(s): 8000/TCP
    6. Age: 66s
    7. └─ Deployment/hello-webserver updated 2022-10-15 21:58:35 Ready: 1/1 Up-to-date: 1
    8. 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:

    1. context:{
    2. name: <component name>
    3. appName: <app name>
    4. output: <K8s workload resource>
    5. outputs: {
    6. <resource1>: <K8s trait resource1>
    7. <resource2>: <K8s trait resource2>
    8. }
    9. }

    The example of health check likes below:

    1. webserver: {
    2. type: "component"
    3. ...
    4. attributes: {
    5. status: {
    6. healthPolicy: #"""
    7. isHealth: (context.output.status.readyReplicas > 0) && (context.output.status.readyReplicas == context.output.status.replicas)
    8. """#
    9. }
    10. }
    11. }

    You can also use the parameter defined in the template like:

    1. webserver: {
    2. type: "component"
    3. ...
    4. attributes: {
    5. status: {
    6. healthPolicy: #"""
    7. isHealth: (context.output.status.readyReplicas > 0) && (context.output.status.readyReplicas == parameter.replicas)
    8. """#
    9. }
    10. }
    11. template: {
    12. parameter: {
    13. replicas: int
    14. }
    15. ...
    16. }

    The health check result will be recorded into the corresponding component in .status.services of Application resource.

    1. apiVersion: core.oam.dev/v1beta1
    2. kind: Application
    3. status:
    4. ...
    5. services:
    6. - healthy: true
    7. name: myweb
    8. ...
    9. 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:

    1. webserver: {
    2. type: "component"
    3. ...
    4. attributes: {
    5. status: {
    6. customStatus: #"""
    7. ready: {
    8. readyReplicas: *0 | int
    9. } & {
    10. if context.output.status.readyReplicas != _|_ {
    11. readyReplicas: context.output.status.readyReplicas
    12. }
    13. }
    14. message: "Ready:\(ready.readyReplicas)/\(context.output.spec.replicas)"
    15. """#
    16. }
    17. }
    18. }

    The message will be recorded into the corresponding component in .status.services of Application resource like below.

    1. apiVersion: core.oam.dev/v1beta1
    2. kind: Application
    3. status:
    4. ...
    5. services:
    6. - healthy: false
    7. message: Ready:1/1
    8. name: express-server

    Please refer to this doc for more examples.

    Full available context in Component

    Context VariableDescriptionType
    context.clusterVersion.majorThe major version of the runtime Kubernetes cluster.string
    context.clusterVersion.gitVersionThe gitVersion of the runtime Kubernetes cluster.string
    context.clusterVersion.platformThe platform information of the runtime Kubernetes cluster.string
    context.clusterVersion.minorThe 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.

    1. outputs: ingress: {
    2. if context.clusterVersion.minor < 19 {
    3. apiVersion: "networking.k8s.io/v1beta1"
    4. }
    5. if context.clusterVersion.minor >= 19 {
    6. apiVersion: "networking.k8s.io/v1"
    7. }
    8. kind: "Ingress"
    9. }

    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:

    1. apiVersion: core.oam.dev/v1beta1
    2. kind: ComponentDefinition
    3. metadata:
    4. name: <ComponentDefinition name>
    5. annotations:
    6. definition.oam.dev/description: <Function description>
    7. spec:
    8. workload: # Workload Capability Indicator
    9. definition:
    10. apiVersion: <Kubernetes Workload resource group>
    11. kind: <Kubernetes Workload types>
    12. schematic: # Component description
    13. 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 .