mirror of
https://github.com/kubevela/kubevela.git
synced 2026-08-27 16:17:34 +00:00
Merge pull request #1027 from wonderflow/doc
add more docs about apllication and CUE
This commit is contained in:
@@ -13,7 +13,7 @@ spec:
|
||||
name: manualscalertraits.core.oam.dev
|
||||
workloadRefPath: spec.workloadRef
|
||||
template: |
|
||||
output: {
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
|
||||
@@ -5,6 +5,13 @@
|
||||
|
||||
- Platform Team Guide
|
||||
- [What is KubeVela?](/en/platform-engineers/overview.md)
|
||||
- Designing Abstraction
|
||||
- [Application CRD](/en/application.md)
|
||||
- CUE
|
||||
- [CUE Basic](/en/cue/basic.md)
|
||||
- [Workload Type](/en/cue/workload-type.md)
|
||||
- [Trait](/en/cue/trait.md)
|
||||
- [Status](/en/cue/status.md)
|
||||
- Register Capability Modules
|
||||
- [Workload Type](/en/platform-engineers/workload-type.md)
|
||||
- [Trait](/en/platform-engineers/trait.md)
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
# Application CRD
|
||||
|
||||
Application CRD describes the components and configurations of an application deployment.
|
||||
It captures all of the definitions of an application in a single object and acts as a declarative anchor to
|
||||
avoid configuration-drift.
|
||||
|
||||
Application CRD provides an abstraction layer on top of infrastructure (e.g. Kubernetes) capabilities to
|
||||
simplify APIs by hiding low level details. For instance, it enables developers to model the "web service" workload
|
||||
without defining detailed `Deployment` + `Service` combo each time, or claim the auto-scaling requirements
|
||||
without referring to the underlying [KEDA](https://keda.sh/) ScaleObject.
|
||||
|
||||
## Spec of an Application
|
||||
|
||||
The following is an example of an Application, it will create a backend service with workload type worker,
|
||||
and a frontend service with workload type webservice.
|
||||
The frontend service will have a sidecar and auto scale policy trait.
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: Application
|
||||
metadata:
|
||||
name: website
|
||||
spec:
|
||||
components:
|
||||
- name: backend
|
||||
type: worker
|
||||
settings:
|
||||
image: busybox
|
||||
cmd:
|
||||
- sleep
|
||||
- '1000'
|
||||
- name: frontend
|
||||
type: webservice
|
||||
settings:
|
||||
image: nginx
|
||||
traits:
|
||||
- name: autoscaler
|
||||
properties:
|
||||
min: 1
|
||||
max: 10
|
||||
- name: sidecar
|
||||
properties:
|
||||
name: "sidecar-test"
|
||||
image: "fluentd"
|
||||
```
|
||||
|
||||
Let's explain more details of the Application spec with the example:
|
||||
|
||||
1. An application named `website` is created, indicated by the `.metadata.name` field.
|
||||
2. The application created two different components, indicated by the `.spec.components` field.
|
||||
3. The name of the first component is `backend`, indicated by the `.spec.components[0].name` field.
|
||||
4. The workload type of the first component is `worker`, indicated by the `.spec.components[0].type` field.
|
||||
The workload type refer to a WorkloadDefinition object named `worker` which will contain the schema of the component.
|
||||
5. The `settings` field of the first component is a schema free field, the schema depends on the workload type. In this
|
||||
example, it has two fields `image` and `cmd` which is defined in the `parameter` of the `.spec.template` field of the `worker` like below.
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: worker
|
||||
spec:
|
||||
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
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
image: string
|
||||
cmd?: [...string]
|
||||
}
|
||||
```
|
||||
6. The second component named `frontend` has a type `webservice`, the schema of the `settings` field is defined by the `webservice`
|
||||
workload type.
|
||||
7. The second component has two traits, indicated by the `.spec.components[0].traits` field.
|
||||
8. The first trait in the second component named `autoscaler`, indicated by the `.spec.components[0].traits[0].name` field.
|
||||
The trait name refer to a TraitDefinition object named `autoscaler` which will define the trait schema.
|
||||
9. The `properties` field in the first trait in the second component is also a schema free field, its schema defends on the trait.
|
||||
10. The second trait in the second component named `sidecar` follows the same rule, refer to a TraitDefinition named `scaler` which will
|
||||
define the schema of the `properties` field.
|
||||
|
||||
|
||||
## Application Object and K8s Resources
|
||||
|
||||
As you can see in the above example, the relationships of all these concepts are:
|
||||
|
||||
* An application object composed by multiple components.
|
||||
* A component in the application composed by name, workload and multiple traits.
|
||||
* A workload composed by type and settings. The type refer to a WorkloadDefinition object which is the template of the workload type.
|
||||
The settings are parameter values which can fill with the template and generate the K8s resource.
|
||||
* A trait composed by name and properties. The name refer to a TraitDefinition object which is the template of the trait.
|
||||
The properties are parameter values which can fill with the template and generate the auxiliary K8s resources.
|
||||
|
||||
Beside the relationships, there are some standard contracts between application object and K8s resourcs.
|
||||
|
||||
* The K8s resource generated by the workload will be tagged the following KubeVela system labels:
|
||||
- `workload.oam.dev/type=<workload type name>` represents the name of the WorkloadDefinition object referred.
|
||||
In the example above, it is `worker` to the first component.
|
||||
- `app.oam.dev/name=<app name>` represents the name of the application. In the example above, it is `website`.
|
||||
- `app.oam.dev/component=<component name>` represents the name of the component in the application. In the example above,
|
||||
it is `backend` to the first component.
|
||||
- `trait.oam.dev/resource=<resource name of the auxiliary object>` represents the auxiliary K8s object resource name.
|
||||
In the example above, we don't have this kind of resource, so this label could be empty.
|
||||
It's an advanced usage for composed K8s resources in a workload type.
|
||||
- revision will also be added, this part is still under development with revision mechanism.
|
||||
|
||||
* The K8s resource generated by the trait will be tagged the following KubeVela system labels:
|
||||
- `trait.oam.dev/type=<trait name>` represents the name of the TraitDefinition object referred.
|
||||
In the example above, it is `autoscaler` to the first trait in the second component.
|
||||
- `app.oam.dev/name=<app name>` represents the name of the application. In the example above, it is `website`.
|
||||
- `app.oam.dev/component=<component name>` represents the name of the component in the application. In the example above,
|
||||
it is `backend` to the first component.
|
||||
- `trait.oam.dev/resource=<resource name of the auxiliary object>` represents the auxiliary K8s object resource name.
|
||||
Look at the example below, the `scaler` in the template inside the outputs is the resource name of this trait.
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: scaler
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- webservice
|
||||
- worker
|
||||
definitionRef:
|
||||
name: manualscalertraits.core.oam.dev
|
||||
workloadRefPath: spec.workloadRef
|
||||
template: |
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
replicaCount: parameter.replicas
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
//+usage=Replicas of the workload
|
||||
replicas: *1 | int
|
||||
}
|
||||
```
|
||||
- revision will also be added, this part is still under development with revision mechanism.
|
||||
|
||||
We will introduce more about the CUE template in the `CUE` section.
|
||||
+39
-42
@@ -37,15 +37,15 @@ spec:
|
||||
type: webservice
|
||||
settings:
|
||||
image: nginx
|
||||
traits:
|
||||
- name: autoscaler
|
||||
properties:
|
||||
min: 1
|
||||
max: 10
|
||||
- name: sidecar
|
||||
properties:
|
||||
name: "sidecar-test"
|
||||
image: "fluentd"
|
||||
traits:
|
||||
- name: autoscaler
|
||||
properties:
|
||||
min: 1
|
||||
max: 10
|
||||
- name: sidecar
|
||||
properties:
|
||||
name: "sidecar-test"
|
||||
image: "fluentd"
|
||||
```
|
||||
|
||||
### Workload Type
|
||||
@@ -74,42 +74,39 @@ metadata:
|
||||
spec:
|
||||
definitionRef:
|
||||
name: deployments.apps
|
||||
extension:
|
||||
template: |
|
||||
output: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
spec: {
|
||||
selector: matchLabels: {
|
||||
"app.oam.dev/component": context.name
|
||||
}
|
||||
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
|
||||
|
||||
template: {
|
||||
metadata: labels: {
|
||||
"app.oam.dev/component": context.name
|
||||
}
|
||||
if parameter["cmd"] != _|_ {
|
||||
command: parameter.cmd
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spec: {
|
||||
containers: [{
|
||||
name: context.name
|
||||
image: parameter.image
|
||||
parameter: {
|
||||
// +usage=Which image would you like to use for your service
|
||||
// +short=i
|
||||
image: string
|
||||
|
||||
if parameter["cmd"] != _|_ {
|
||||
command: parameter.cmd
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parameter: {
|
||||
// +usage=Which image would you like to use for your service
|
||||
// +short=i
|
||||
image: string
|
||||
|
||||
cmd?: [...string]
|
||||
}
|
||||
cmd?: [...string]
|
||||
}
|
||||
```
|
||||
|
||||
Once this definition is applied to the cluster, the end users will be able to claim a component with workload type of `worker`, and fill in the properties as below:
|
||||
|
||||
@@ -0,0 +1,401 @@
|
||||
# CUE Basic
|
||||
|
||||
KubeVela use [CUE](https://cuelang.org/) as its template DSL. With the help of CUE, we can define simple but powerful
|
||||
template in Definition Objects and build abstraction for applications.
|
||||
|
||||
## Why CUE ?
|
||||
|
||||
Why does KubeVela choose CUE? [Points of Cedric Charly](https://blog.cedriccharly.com/post/20191109-the-configuration-complexity-curse/) speaks well,
|
||||
let me conclude here.
|
||||
|
||||
* **CUE is designed for large scale configuration.**
|
||||
CUE deliberately opts for the graph unification model used in computational linguistics instead of the traditional
|
||||
inheritance model. The graph model can help KubeVela to build a clear view of resources' relationships and dependency.
|
||||
For large scale infrastructure, that will be a complex, tightly interconnected graph of
|
||||
resources that describes an organization's entire computing environment. In this case, CUE has the ability to understand a
|
||||
configuration worked on by engineers across a whole company and to safely change a value that modifies thousands of
|
||||
objects in a configuration.
|
||||
* **CUE supports first-class code generation and automation.**
|
||||
A design goal of CUE is to have code that is straightfoward for humans to write, but is also simple for machines to
|
||||
generate. This goal highly consistent with KubeVela which wants to offer abstractions to bridge the gap between concepts
|
||||
used by app developers and kubernetes. CUE can integrate with existing tools and workflows naturally while other tools
|
||||
would have to build complex custom solutions. CUE can generate Kubernetes definitions from Go code and OpenAPI schemas
|
||||
and immediately work with resources directly or build higher level libraries.
|
||||
* **CUE integrates very well with Go.**
|
||||
KubeVela is built with GO just like most projects of the while Kubernetes system. CUE is also implemented in and
|
||||
exposes a rich client API in Go. KubeVela integrates with CUE as its core library and works as a Kubernetes CRD controller.
|
||||
With the help of CUE, KubeVela can easily handle data constraint problems.
|
||||
|
||||
If you want to go deeper I recommend reading [The Logic of CUE](https://cuelang.org/docs/concepts/logic/)
|
||||
to understand the theoretical foundation and what makes CUE different from other configuration languages.
|
||||
|
||||
## CUE in KubeVela
|
||||
|
||||
Let's go back to discuss how does CUE be used in KubeVela. As you know, KubeVela helps platform builder to build abstraction
|
||||
from Kubernetes resources to an application. We will use a [K8s Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/)
|
||||
as example to explain how was it implemented.
|
||||
|
||||
In KubeVela we usually build a WorkloadDefinition to generate K8s Deployment as it's workload-like resources.
|
||||
A complete WorkloadDefinition example like below:
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: mydeploy
|
||||
spec:
|
||||
definitionRef:
|
||||
name: deployments.apps
|
||||
template: |
|
||||
parameter: {
|
||||
name: string
|
||||
image: string
|
||||
}
|
||||
output: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
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
|
||||
}]
|
||||
}}}
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the `template` field is totally CUE, it contains two keywords, `output` and `parameter`.
|
||||
The `output` defines what will be rendering out by the template. The `parameter` defines the input parameters which can
|
||||
be part of the application.
|
||||
|
||||
Let's try to write the CUE template step by step.
|
||||
|
||||
As you see, below is a Deployment YAML, most of the fields can be hidden and only expose `image` and `env` field for end user.
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
meadata:
|
||||
name: mytest
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: mytest
|
||||
env:
|
||||
- name: a
|
||||
value: b
|
||||
image: nginx:v1
|
||||
metadata:
|
||||
labels:
|
||||
app.oam.dev/component: mytest
|
||||
selector:
|
||||
matchLabels:
|
||||
app.oam.dev/component: mytest
|
||||
```
|
||||
|
||||
The first step is to convert the YAML to JSON and put the whole json object into the `output` keyword field.
|
||||
CUE is a superset of JSON: any valid JSON file is a valid CUE file. It provides some conveniences such as you can omit
|
||||
some quotes from field names without special characters.
|
||||
|
||||
Here is the converted result:
|
||||
|
||||
```cue
|
||||
output: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
metadata: name: "mytest"
|
||||
spec: {
|
||||
selector: matchLabels: {
|
||||
"app.oam.dev/component": "mytest"
|
||||
}
|
||||
template: {
|
||||
metadata: labels: {
|
||||
"app.oam.dev/component": "mytest"
|
||||
}
|
||||
spec: {
|
||||
containers: [{
|
||||
name: "mytest"
|
||||
image: "nginx:v1"
|
||||
env: [{name:"a",value:"b"}]
|
||||
}]
|
||||
}}}
|
||||
}
|
||||
```
|
||||
|
||||
Here are all conveniences add by CUE as a superset of JSON:
|
||||
|
||||
* C-style comments,
|
||||
* quotes may be omitted from field names without special characters,
|
||||
* commas at the end of fields are optional,
|
||||
* comma after last element in list is allowed,
|
||||
* outer curly braces are optional.
|
||||
|
||||
After that we add `parameter` keyword into the template, and use it as a variable reference, this is basic CUE grammar.
|
||||
|
||||
Fields of keyword `parameter` will be detected by KubeVela and be exposed to users using in application.
|
||||
|
||||
```cue
|
||||
parameter: {
|
||||
name: string
|
||||
image: string
|
||||
}
|
||||
output: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
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
|
||||
}]
|
||||
}}}
|
||||
}
|
||||
```
|
||||
|
||||
Finally, you can put the whole CUE template into the `template` field of WorkloadDefinition object. That's all you need
|
||||
to know to create a basic KubeVela capability.
|
||||
|
||||
## More Advanced Usage of CUE Grammar in Definition
|
||||
|
||||
In this section, we will introduce some more advanced CUE grammar to use in KubeVela.
|
||||
|
||||
### Structural parameter
|
||||
|
||||
If you have some complex type of parameters in your template, and want to define a struct or embed struct as parameters,
|
||||
then you could use structural parameter.
|
||||
|
||||
1. Define a struct type in, it includes a struct, a string and an integer.
|
||||
```
|
||||
#Config: {
|
||||
name: string
|
||||
value: int
|
||||
other: {
|
||||
key: string
|
||||
value: string
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. Use the struct defined in the `parameter` keyword, and use it as an array list.
|
||||
```
|
||||
parameter: {
|
||||
name: string
|
||||
image: string
|
||||
configSingle: #Config
|
||||
config: [...#Config]
|
||||
}
|
||||
```
|
||||
|
||||
3. In `output` keyword, it's referenced the same way with other normal field s.
|
||||
```
|
||||
output: {
|
||||
...
|
||||
spec: {
|
||||
containers: [{
|
||||
name: parameter.name
|
||||
image: parameter.image
|
||||
env: parameter.config
|
||||
}]
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
4. The structural field `config` can be easily used in Application like below:
|
||||
```
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: Application
|
||||
metadata:
|
||||
name: website
|
||||
spec:
|
||||
components:
|
||||
- name: backend
|
||||
type: mydeploy
|
||||
settings:
|
||||
image: crccheck/hello-world
|
||||
name: mysvc
|
||||
config:
|
||||
- name: a
|
||||
value: 1
|
||||
other:
|
||||
key: mykey
|
||||
value: myvalue
|
||||
```
|
||||
|
||||
### Conditional Parameter
|
||||
|
||||
Conditional parameter can be used to decide template condition logic.
|
||||
Below is an example that when `useENV=true`, it will render env section, otherwise, it will not.
|
||||
|
||||
```
|
||||
parameter: {
|
||||
name: string
|
||||
image: string
|
||||
useENV: bool
|
||||
}
|
||||
output: {
|
||||
...
|
||||
spec: {
|
||||
containers: [{
|
||||
name: parameter.name
|
||||
image: parameter.image
|
||||
if parameter.useENV == true {
|
||||
env: [{name: "my-env", value: "my-value"}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Optional Parameter and Default Value
|
||||
|
||||
Optional parameter can be optional, that usually works with conditional logic. If some field does not exit, the CUE
|
||||
grammar is `if _variable_ != _|_`, the example is like below:
|
||||
|
||||
```
|
||||
parameter: {
|
||||
name: string
|
||||
image: string
|
||||
config?: [...#Config]
|
||||
}
|
||||
output: {
|
||||
...
|
||||
spec: {
|
||||
containers: [{
|
||||
name: parameter.name
|
||||
image: parameter.image
|
||||
if parameter.config != _|_ {
|
||||
config: parameter.config
|
||||
}
|
||||
}]
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Default Value is marked with a `*` prefix. It's used like
|
||||
|
||||
```
|
||||
parameter: {
|
||||
name: string
|
||||
image: *"nginx:v1" | string
|
||||
port: *80 | int
|
||||
number: *123.4 | float
|
||||
}
|
||||
output: {
|
||||
...
|
||||
spec: {
|
||||
containers: [{
|
||||
name: parameter.name
|
||||
image: parameter.image
|
||||
}]
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
So if a parameter field is neither a parameter with default value nor a conditional field, it's a required value.
|
||||
|
||||
### Loop
|
||||
|
||||
#### Loop for map type
|
||||
|
||||
```cue
|
||||
parameter: {
|
||||
name: string
|
||||
image: string
|
||||
env: [string]: string
|
||||
}
|
||||
output: {
|
||||
spec: {
|
||||
containers: [{
|
||||
name: parameter.name
|
||||
image: parameter.image
|
||||
env: [
|
||||
for k, v in parameter.env {
|
||||
name: k
|
||||
value: v
|
||||
},
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Loop for slice
|
||||
|
||||
```cue
|
||||
parameter: {
|
||||
name: string
|
||||
image: string
|
||||
env: [...{name:string,value:string}]
|
||||
}
|
||||
output: {
|
||||
...
|
||||
spec: {
|
||||
containers: [{
|
||||
name: parameter.name
|
||||
image: parameter.image
|
||||
env: [
|
||||
for _, v in parameter.env {
|
||||
name: v.name
|
||||
value: v.value
|
||||
},
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Import CUE internal packages
|
||||
|
||||
CUE has [lots of internal packages](https://pkg.go.dev/cuelang.org/go@v0.2.2/pkg) which also can be used in KubeVela.
|
||||
|
||||
Below is an example that use `strings.Join` to concat string list to one string.
|
||||
|
||||
```cue
|
||||
import ("strings")
|
||||
|
||||
parameter: {
|
||||
outputs: [{ip: "1.1.1.1", hostname: "xxx.com"}, {ip: "2.2.2.2", hostname: "yyy.com"}]
|
||||
}
|
||||
output: {
|
||||
spec: {
|
||||
if len(parameter.outputs) > 0 {
|
||||
_x: [ for _, v in parameter.outputs {
|
||||
"\(v.ip) \(v.hostname)"
|
||||
}]
|
||||
message: "Visiting URL: " + strings.Join(_x, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
# Status Loop Back
|
||||
|
||||
In the previous sections, we use CUE as template to render K8s resources. After an application deployed, the status
|
||||
loop back can also use CUE.
|
||||
|
||||
KubeVela use CUE to define health check and custom status message for an application in workload type and trait.
|
||||
|
||||
## Health Check
|
||||
|
||||
The spec of health check is `spec.status.healthPolicy`, they are the same for both Workload Type and Trait.
|
||||
|
||||
If not defined, the health result will always be `true`.
|
||||
|
||||
The keyword in CUE is `isHealth`, the result of CUE expression must be `bool` type.
|
||||
Application CRD controller will evaluate the CUE expression periodically until it
|
||||
becomes healthy. Every time the controller will get all the K8s resources and fill them into the context field.
|
||||
|
||||
So the context will contain following information:
|
||||
|
||||
```cue
|
||||
context:{
|
||||
name: <component name>
|
||||
appName: <app name>
|
||||
output: <K8s workload resource>
|
||||
outputs: {
|
||||
<resource1>: <K8s trait resource1>
|
||||
<resource2>: <K8s trait resource2>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Trait will not have the `context.ouput`, other fields are the same.
|
||||
|
||||
The example of health check likes below:
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
spec:
|
||||
status:
|
||||
healthPolicy: |
|
||||
isHealth: (context.output.status.readyReplicas > 0) && (context.output.status.readyReplicas == context.output.status.replicas)
|
||||
...
|
||||
```
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
spec:
|
||||
status:
|
||||
healthPolicy: |
|
||||
isHealth: len(context.outputs.service.spec.clusterIP) > 0
|
||||
...
|
||||
```
|
||||
|
||||
Refer to [this doc](https://github.com/oam-dev/kubevela/blob/master/config/samples/app-with-status/template.yaml) for the complete example.
|
||||
|
||||
The health check result will be recorded into the Application CRD resource.
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: Application
|
||||
spec:
|
||||
components:
|
||||
- name: myweb
|
||||
settings:
|
||||
cmd:
|
||||
- sleep
|
||||
- "1000"
|
||||
enemies: alien
|
||||
image: busybox
|
||||
lives: "3"
|
||||
traits:
|
||||
- name: ingress
|
||||
properties:
|
||||
domain: www.example.com
|
||||
http:
|
||||
/: 80
|
||||
type: worker
|
||||
status:
|
||||
...
|
||||
services:
|
||||
- healthy: true
|
||||
message: "type: busybox,\t enemies:alien"
|
||||
name: myweb
|
||||
traits:
|
||||
- healthy: true
|
||||
message: 'Visiting URL: www.example.com, IP: 47.111.233.220'
|
||||
type: ingress
|
||||
status: running
|
||||
```
|
||||
|
||||
## Custom Status
|
||||
|
||||
The spec of custom status is `spec.status.customStatus`, they are the same for both Workload Type and Trait.
|
||||
|
||||
The keyword in CUE is `message`, the result of CUE expression must be `string` type.
|
||||
|
||||
The custom status has the same mechanism with health check.
|
||||
Application CRD controller will evaluate the CUE expression after the health check succeed.
|
||||
|
||||
The context will contain following information:
|
||||
|
||||
```cue
|
||||
context:{
|
||||
name: <component name>
|
||||
appName: <app name>
|
||||
output: <K8s workload resource>
|
||||
outputs: {
|
||||
<resource1>: <K8s trait resource1>
|
||||
<resource2>: <K8s trait resource2>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Trait will not have the `context.ouput`, other fields are the same.
|
||||
|
||||
|
||||
Refer to [this doc](https://github.com/oam-dev/kubevela/blob/master/config/samples/app-with-status/template.yaml) for the complete example.
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
spec:
|
||||
status:
|
||||
customStatus: |-
|
||||
message: "type: " + context.output.spec.template.spec.containers[0].image + ",\t enemies:" + context.outputs.gameconfig.data.enemies
|
||||
...
|
||||
```
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
spec:
|
||||
status:
|
||||
customStatus: |-
|
||||
message: "type: "+ context.outputs.service.spec.type +",\t clusterIP:"+ context.outputs.service.spec.clusterIP+",\t ports:"+ "\(context.outputs.service.spec.ports[0].port)"+",\t domain"+context.outputs.ingress.spec.rules[0].host
|
||||
...
|
||||
```
|
||||
@@ -1,17 +1,17 @@
|
||||
# Using CUE to Extend Trait in advanced way
|
||||
|
||||
> WARNINIG: you are now reading a platform builder/administrator oriented documentation.
|
||||
In this section we will introduce how to define a Trait with CUE template.
|
||||
|
||||
In the following tutorial, you will learn how to add a trait in a more advanced way without writing any CRD controller.
|
||||
In general, the advanced way can help you build abstraction by composition or decomposition.
|
||||
|
||||
## Trait generate multiple resources
|
||||
|
||||
With the help of CUE template, we can combine multiple K8s resources into one trait.
|
||||
## Basic Usage
|
||||
|
||||
You can use the keyword `outputs` to create multiple K8s objects. The format MUST be `outputs:<unique-name>:<k8s-object>`.
|
||||
Defining a Trait with CUE template is a little different from Workload Type.
|
||||
The different part is a trait must use `outputs` keyword instead of `output` to define resources that will be generated.
|
||||
|
||||
Let's look at an example, assume you hope to make a combo for K8s service and ingress, naming it as `ingress`.
|
||||
With the help of CUE template, it is very nature to compose multiple K8s resources in one trait.
|
||||
|
||||
The format MUST be `outputs:<unique-name>:<k8s-object>`.
|
||||
|
||||
Below is an example that make a combo for K8s service and ingress, naming this trait as `ingress`.
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
@@ -66,13 +66,7 @@ spec:
|
||||
}
|
||||
```
|
||||
|
||||
Apply this newly defined TraitDefinition into our system:
|
||||
|
||||
```shell script
|
||||
kubectl apply -f https://raw.githubusercontent.com/oam-dev/kubevela/master/docs/examples/registry/ingress.yaml
|
||||
```
|
||||
|
||||
You can check it by using the application object like below:
|
||||
It can be used in the application object like below:
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
@@ -82,8 +76,6 @@ metadata:
|
||||
spec:
|
||||
components:
|
||||
- name: express-server
|
||||
scopes:
|
||||
healthscopes.core.oam.dev: testapp-default-health
|
||||
settings:
|
||||
cmd:
|
||||
- node
|
||||
@@ -99,13 +91,8 @@ spec:
|
||||
type: webservice
|
||||
```
|
||||
|
||||
Apply it:
|
||||
|
||||
```shell script
|
||||
kubectl apply -f https://raw.githubusercontent.com/oam-dev/kubevela/master/docs/examples/advanced-cue/app1.yaml
|
||||
```
|
||||
|
||||
Then you will see the deployment behind webservice along with the K8s service and ingress behind the ingress trait created.
|
||||
After the application deployed, you will see the deployment(belong to webservice) along with the K8s service and
|
||||
ingress(belong to ingress trait) created.
|
||||
|
||||
### Generate multiple resources by using for loop
|
||||
|
||||
@@ -145,14 +132,7 @@ spec:
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Apply this newly defined TraitDefinition into our system:
|
||||
|
||||
```shell script
|
||||
kubectl apply -f https://raw.githubusercontent.com/oam-dev/kubevela/master/docs/examples/registry/for-loop.yaml
|
||||
```
|
||||
|
||||
Use the newly created trait like below:
|
||||
The usage of this trait could be:
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
@@ -164,11 +144,7 @@ spec:
|
||||
- name: express-server
|
||||
type: webservice
|
||||
settings:
|
||||
cmd:
|
||||
- node
|
||||
- server.js
|
||||
image: oamdev/testapp:v1
|
||||
port: 8080
|
||||
...
|
||||
traits:
|
||||
- name: expose
|
||||
properties:
|
||||
@@ -177,25 +153,15 @@ spec:
|
||||
myservice2: 8081
|
||||
```
|
||||
|
||||
Apply it:
|
||||
|
||||
```shell script
|
||||
kubectl apply -f https://raw.githubusercontent.com/oam-dev/kubevela/master/docs/examples/advanced-cue/app2.yaml
|
||||
```
|
||||
|
||||
Then you will see the deployment behind webservice along with two K8s services created.
|
||||
|
||||
|
||||
## Patch Trait
|
||||
|
||||
For the purpose of separate of concerns, we usually won't do decomposition some fields out as trait from the underlying workload.
|
||||
For the purpose of separate of concerns, we usually want to decompose some fields out as trait from the underlying workload.
|
||||
|
||||
For example the [webservice workload] is implemented by K8s Deployment, but the workload doesn't care about the `replicas` field.
|
||||
In this case, you can write a [ManualScalerTrait](https://github.com/oam-dev/kubevela/tree/master/pkg/controller/core.oam.dev/v1alpha2/core/traits/manualscalertrait)
|
||||
CRD controller to control the `replicas` field after the deployment created.
|
||||
|
||||
But now, you are more encouraged to use patch trait in KubeVela. With the help of patch trait, you don't need to write CRD
|
||||
controller for this case anymore.
|
||||
For example the [webservice workload] is implemented by K8s Deployment, but the workload doesn't care about the node affinity related fields.
|
||||
|
||||
Users should be able to run this app without node affinity and they can add it as a trait later when they want.
|
||||
This is exactly what patch trait do.
|
||||
With the help of CUE template, you don't need to write a CRD controller for this case.
|
||||
|
||||
The keyword is `patch`, object describe after the keyword will be patched into the workload.
|
||||
|
||||
@@ -206,8 +172,8 @@ apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: "Manually scale the app"
|
||||
name: scaler
|
||||
definition.oam.dev/description: "affinity specify node affinity and toleration"
|
||||
name: node-affinity
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- webservice
|
||||
@@ -215,33 +181,82 @@ spec:
|
||||
extension:
|
||||
template: |-
|
||||
patch: {
|
||||
spec: replicas: parameter.replicas
|
||||
spec: template: spec: {
|
||||
if parameter.affinity != _|_ {
|
||||
affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: [{
|
||||
matchExpressions: [
|
||||
for k, v in parameter.affinity {
|
||||
key: k
|
||||
operator: "In"
|
||||
values: v
|
||||
},
|
||||
]}]
|
||||
}
|
||||
if parameter.tolerations != _|_ {
|
||||
tolerations: [
|
||||
for k, v in parameter.tolerations {
|
||||
effect: "NoSchedule"
|
||||
key: k
|
||||
operator: "Equal"
|
||||
value: v
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parameter: {
|
||||
replicas: *1 | int
|
||||
affinity?: [string]: [...string]
|
||||
tolerations?: [string]: string
|
||||
}
|
||||
```
|
||||
|
||||
The patch trait rely on the workload object will always match the structure `spec.replicas` and the type of the field.
|
||||
So we usually use it with the field `appliesToWorkloads` which can limit the trait can only be used by these specified workloads.
|
||||
You can use it like:
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: Application
|
||||
metadata:
|
||||
name: testapp
|
||||
spec:
|
||||
components:
|
||||
- name: express-server
|
||||
type: webservice
|
||||
settings:
|
||||
image: oamdev/testapp:v1
|
||||
traits:
|
||||
- name: "node-affinity"
|
||||
properties:
|
||||
affinity:
|
||||
server-owner: ["owner1","owner2"]
|
||||
resource-pool: ["pool1","pool2","pool3"]
|
||||
tolerations:
|
||||
resource-pool: "broken-pool1"
|
||||
server-owner: "old-owner"
|
||||
```
|
||||
|
||||
The patch trait rely on the workload object will always match the structure `spec.template.spec.affinity` and
|
||||
the type of these fields.
|
||||
So we usually use it with the field `appliesToWorkloads` which can limit the trait only to be used
|
||||
by these specified workloads.
|
||||
|
||||
By default, the patch implemented in KubeVela relies on the CUE merge operation. It has these constraints:
|
||||
|
||||
* New field will be added only when the schema doesn't conflict with each other, and the value not finalized.
|
||||
|
||||
For example, if the workload already define the `spec.replicas` is `5`, then the patch trait replicas value `1` will fail to patch.
|
||||
For example, if a field already has a final value `replicas=5`, then the patch trait will conflict when patches `replicas=1`.
|
||||
It only works when `replica` is not finalized before patch.
|
||||
|
||||
* Array list in the patch will be merged into the workload by the order of index, you need to use strategy patch.
|
||||
|
||||
|
||||
### Strategy Patch Trait
|
||||
|
||||
`strategy patch` is a special patch logic for patching array list supported in KubeVela, it's not native CUElang feature,
|
||||
so you need to write annotation for using it.
|
||||
`strategy patch` is a special patch logic for patching array list supported **only** in KubeVela,
|
||||
it's not native CUE feature, so you need to write annotation for using it.
|
||||
|
||||
The annotation keyword is `//+patchKey=<key_name>`.
|
||||
The annotation grammar is `//+patchKey=<key_name>`.
|
||||
|
||||
By adding this annotation, merging logic of two array list will not follow the CUE rule, instead of that, it will
|
||||
By adding this annotation, merging logic of two lists will not follow the CUE rule, instead of that, it will
|
||||
regard the element type of the array list will always be object, and compare the object field with the specified key name.
|
||||
If the value of the key name equal, then the patch data will merge into that, if no equal found, the patch will append into the array list.
|
||||
|
||||
@@ -277,7 +292,7 @@ with same name, it will be a sidecar container append into the `spec.template.sp
|
||||
|
||||
### Patch works with output
|
||||
|
||||
Patch can also work with output, if patch and output both exist in one trait, the patch part will execute first and then
|
||||
Patch can also work with outputs, if patch and outputs both exist in one trait, the patch part will execute first and then
|
||||
the output object will be rendered out.
|
||||
|
||||
```yaml
|
||||
@@ -296,7 +311,7 @@ spec:
|
||||
extension:
|
||||
template: |-
|
||||
patch: {spec: template: metadata: labels: app: context.name}
|
||||
output: {
|
||||
outputs: service: {
|
||||
apiVersion: "v1"
|
||||
kind: "Service"
|
||||
metadata: name: context.name
|
||||
@@ -624,74 +639,3 @@ spec:
|
||||
appMountPath: "/usr/share/nginx/html"
|
||||
initMountPath: "/work-dir"
|
||||
```
|
||||
|
||||
### Node affinity and anti-affinity
|
||||
|
||||
Node affinity and anti-affinity is also common trait:
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: "affinity specify node affinity and toleration"
|
||||
name: node-affinity
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- webservice
|
||||
- worker
|
||||
extension:
|
||||
template: |-
|
||||
patch: {
|
||||
spec: template: spec: {
|
||||
if parameter.affinity != _|_ {
|
||||
affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: [{
|
||||
matchExpressions: [
|
||||
for k, v in parameter.affinity {
|
||||
key: k
|
||||
operator: "In"
|
||||
values: v
|
||||
},
|
||||
]}]
|
||||
}
|
||||
if parameter.tolerations != _|_ {
|
||||
tolerations: [
|
||||
for k, v in parameter.tolerations {
|
||||
effect: "NoSchedule"
|
||||
key: k
|
||||
operator: "Equal"
|
||||
value: v
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parameter: {
|
||||
affinity?: [string]: [...string]
|
||||
tolerations?: [string]: string
|
||||
}
|
||||
```
|
||||
|
||||
You can use it like:
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: Application
|
||||
metadata:
|
||||
name: testapp
|
||||
spec:
|
||||
components:
|
||||
- name: express-server
|
||||
type: webservice
|
||||
settings:
|
||||
image: oamdev/testapp:v1
|
||||
traits:
|
||||
- name: "node-affinity"
|
||||
properties:
|
||||
affinity:
|
||||
server-owner: ["owner1","owner2"]
|
||||
resource-pool: ["pool1","pool2","pool3"]
|
||||
tolerations:
|
||||
resource-pool: "broken-pool1"
|
||||
server-owner: "old-owner"
|
||||
```
|
||||
@@ -0,0 +1,227 @@
|
||||
# Workload Type with CUE
|
||||
|
||||
In the [CUE basic section](./basic.md), we have explained how CUE works as template of Workload Type and Trait.
|
||||
In this section, we will introduce more details about workload type.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
The very basic usage of CUE in workload is to extend a K8s Resource as a workload type(WorkloadDefinition).
|
||||
|
||||
A K8s Deployment as a workload:
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: worker
|
||||
spec:
|
||||
definitionRef:
|
||||
name: deployments.apps
|
||||
template: |
|
||||
parameter: {
|
||||
name: string
|
||||
image: string
|
||||
}
|
||||
output: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
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
|
||||
}]
|
||||
}}}
|
||||
}
|
||||
```
|
||||
|
||||
A K8s Job as workload:
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: task
|
||||
annotations:
|
||||
definition.oam.dev/description: "Describes jobs that run code or a script to completion."
|
||||
spec:
|
||||
definitionRef:
|
||||
name: jobs.batch
|
||||
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]
|
||||
}
|
||||
```
|
||||
|
||||
Other resources are the same, you can define all K8s resources include CRD in this way.
|
||||
|
||||
## Context
|
||||
|
||||
When you want to reference the runtime instance name for an app, you can use the `conext` keyword instead of define a new parameter.
|
||||
|
||||
KubeVela will provide a `context` struct including app name(`context.appName`) and component name(`context.name`).
|
||||
|
||||
```cue
|
||||
context: {
|
||||
appName: string
|
||||
name: string
|
||||
}
|
||||
```
|
||||
|
||||
Values of the context will be injected automatically when an application is deploying.
|
||||
So you can reference the context variable to use this information.
|
||||
|
||||
```yaml
|
||||
parameter: {
|
||||
image: string
|
||||
}
|
||||
output: {
|
||||
...
|
||||
spec: {
|
||||
containers: [{
|
||||
name: context.name
|
||||
image: parameter.image
|
||||
}]
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## Composition
|
||||
|
||||
A workload type can contain multiple K8s resources, for example, a webserver workload type may be composed by
|
||||
K8s Deployment and Service.
|
||||
|
||||
The main workload resource MUST be defined in keyword `output` while the auxiliary workload resources MUST be defined
|
||||
in keyword `outputs` with a resource name inside.
|
||||
|
||||
The format MUST be `outputs:<unique-name>:<k8s-object>`.
|
||||
|
||||
In the underlying OAM model, the `output` resource will become the `workload` object while the `outputs` resources will
|
||||
become traits.
|
||||
|
||||
Below is the example:
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: WorkloadDefinition
|
||||
metadata:
|
||||
name: webserver
|
||||
annotations:
|
||||
definition.oam.dev/description: "webserver was composed by deployment and service"
|
||||
spec:
|
||||
definitionRef:
|
||||
name: deployments.apps
|
||||
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
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// workload can have extra object composition by using 'outputs' keyword
|
||||
outputs: service: {
|
||||
apiVersion: "v1"
|
||||
kind: "Service"
|
||||
spec: {
|
||||
selector: {
|
||||
"app.oam.dev/component": context.name
|
||||
}
|
||||
ports: [
|
||||
{
|
||||
port: parameter.port
|
||||
targetPort: parameter.port
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
image: string
|
||||
cmd?: [...string]
|
||||
port: *80 | int
|
||||
env?: [...{
|
||||
name: string
|
||||
value?: string
|
||||
valueFrom?: {
|
||||
secretKeyRef: {
|
||||
name: string
|
||||
key: string
|
||||
}
|
||||
}
|
||||
}]
|
||||
cpu?: string
|
||||
}
|
||||
```
|
||||
|
||||
The main workload inside the `output` keyword is a K8s Deployment. The auxiliary resources inside the `outputs` field
|
||||
is a K8s service, the resource name in the CUE template is `service` after the `outputs` keyword.
|
||||
|
||||
The resource name will also be labeled on the K8s resource when it is deployed. In this example, the K8s Service will have
|
||||
a label(`trait.oam.dev/resource=service`).
|
||||
|
||||
@@ -22,7 +22,7 @@ spec:
|
||||
template: |
|
||||
import "strconv"
|
||||
|
||||
output: {
|
||||
outputs: autoscaler: {
|
||||
apiVersion: "standard.oam.dev/v1alpha1"
|
||||
kind: "Autoscaler"
|
||||
spec: {
|
||||
|
||||
@@ -22,7 +22,7 @@ spec:
|
||||
url: https://prometheus-community.github.io/helm-charts
|
||||
version: 9.4.4
|
||||
template: |-
|
||||
output: {
|
||||
outputs: metrics: {
|
||||
apiVersion: "standard.oam.dev/v1alpha1"
|
||||
kind: "MetricsTrait"
|
||||
spec: {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: "Manually scale the app"
|
||||
name: patch-scaler
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- webservice
|
||||
- worker
|
||||
extension:
|
||||
template: |-
|
||||
patch: {
|
||||
spec: replicas: parameter.replicas
|
||||
}
|
||||
parameter: {
|
||||
replicas: *1 | int
|
||||
}
|
||||
@@ -21,7 +21,7 @@ spec:
|
||||
url: https://oam.dev/flagger/archives/
|
||||
version: 1.1.0
|
||||
template: |-
|
||||
output: {
|
||||
outputs: canary: {
|
||||
apiVersion: "flagger.app/v1beta1"
|
||||
kind: "Canary"
|
||||
spec: {
|
||||
|
||||
@@ -19,7 +19,7 @@ spec:
|
||||
url: https://kubernetes-charts.storage.googleapis.com/
|
||||
version: 1.41.2
|
||||
template: |
|
||||
output: {
|
||||
outputs: route: {
|
||||
apiVersion: "standard.oam.dev/v1alpha1"
|
||||
kind: "Route"
|
||||
spec: {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
output: {
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
|
||||
Reference in New Issue
Block a user