--- title: Component Definition --- In this section, we will introduce how to use [CUE](../cue/basic.md) to customize components via `ComponentDefinition`. Make sure you've learned the basic knowledge about [Definition Concept](../../getting-started/definition.md) and [how to manage definition](../cue/definition-edit.md). ## Declare `ComponentDefinition` First, generate `ComponentDefinition` scaffolds via `vela def init` with existed YAML file. The YAML file: ```yaml title="stateless.yaml" apiVersion: "apps/v1" kind: "Deployment" spec: selector: matchLabels: "app.oam.dev/component": "name" template: metadata: labels: "app.oam.dev/component": "name" spec: containers: - name: "name" image: "image" ``` Generate `ComponentDefinition` based on the YAML file: ```shell vela def init stateless -t component --template-yaml ./stateless.yaml -o stateless.cue ``` It generates a file: ```cue title="stateless.cue" stateless: { annotations: {} attributes: workload: definition: { apiVersion: " apps/v1" kind: " 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` 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 : ```cue 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: ```shell vela def vet stateless.cue ```
expected output ``` Validation succeed. ```
Apply above `ComponentDefinition` to your Kubernetes cluster to make it work: ```shell 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. ```shell vela def init task -t component -o task.cue ``` It generates a file: ```cue // $ cat task.cue task: { annotations: {} attributes: workload: definition: { apiVersion: " apps/v1" kind: " Deployment" } description: "" labels: {} type: "component" } template: { output: {} parameter: {} } ``` Edit the generated component file: ```cue 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: ```shell $ 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: ```yaml apiVersion: core.oam.dev/v1beta1 kind: Application metadata: name: website spec: components: - name: hello type: stateless properties: image: oamdev/hello-world name: mysvc - name: countdown type: task properties: image: centos:7 cmd: - "bin/bash" - "-c" - "for i in 9 8 7 6 5 4 3 2 1 ; do echo $i ; done" ```
Learn Details 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. ```yaml 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](../debug/dry-run.md) to show what the yaml results will be rendered for debugging. ## CUE `Context` for runtime information 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`). ```cue 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: ```cue 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](#full-available-context-in-component) 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](../../tutorials/helm.md), 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: ```cue output: {