Trait
A Trait represents a cross-cutting concern that can be attached to components to add additional capabilities like persistent storage, observability, security policies, or service mesh integration. Traits modify or extend the resources generated by ComponentTypes without requiring changes to the ComponentType itself.
API Versionβ
openchoreo.dev/v1alpha1
Resource Definitionβ
Metadataβ
Traits are namespace-scoped resources typically created in an Organization's namespace to be available for components in that organization.
apiVersion: openchoreo.dev/v1alpha1
kind: Trait
metadata:
name: <trait-name>
namespace: <org-namespace> # Organization namespace
Spec Fieldsβ
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
schema | TraitSchema | No | - | Configurable parameters for this trait |
creates | [TraitCreate] | No | [] | New Kubernetes resources to create |
patches | [TraitPatch] | No | [] | Modifications to existing ComponentType resources |
TraitSchemaβ
Defines the configurable parameters that developers can set when attaching this trait to a component.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
types | object | No | - | Reusable type definitions referenced in parameters |
parameters | object | No | - | Developer-facing configuration options |
envOverrides | object | No | - | Parameters that can be overridden per environment |
Parameter Schema Syntaxβ
Uses the same inline schema syntax as ComponentType: a single pipe after the type, then space-separated constraints:
fieldName: "type | default=value enum=val1,val2"
Example:
schema:
parameters:
volumeName: "string"
mountPath: "string"
containerName: "string | default=app"
envOverrides:
size: "string | default=10Gi"
storageClass: "string | default=standard"
TraitCreateβ
Defines a new Kubernetes resource to be created when the trait is applied.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
targetPlane | string | No | dataplane | Target plane: dataplane or observabilityplane |
template | object | Yes | - | Kubernetes resource template with CEL expressions |
CEL expressions in trait templates have access to the following context variables:
metadataβ
Platform-computed metadata for resource generation (same as ComponentType):
| Field | Type | Description |
|---|---|---|
metadata.name | string | Base name for generated resources (e.g., my-service-dev-a1b2c3d4) |
metadata.namespace | string | Target namespace for resources |
metadata.componentName | string | Name of the component |
metadata.componentUID | string | Unique identifier of the component |
metadata.projectName | string | Name of the project |
metadata.projectUID | string | Unique identifier of the project |
metadata.environmentName | string | Name of the environment (e.g., development, production) |
metadata.environmentUID | string | Unique identifier of the environment |
metadata.dataPlaneName | string | Name of the data plane |
metadata.dataPlaneUID | string | Unique identifier of the data plane |
metadata.labels | map | Common labels to add to all resources |
metadata.annotations | map | Common annotations to add to all resources |
metadata.podSelectors | map | Platform-injected selectors for pod identity |
traitβ
Trait-specific metadata:
| Field | Type | Description |
|---|---|---|
trait.name | string | Name of the trait (e.g., persistent-volume) |
trait.instanceName | string | Unique instance name within the component (e.g., data-storage) |
parametersβ
Merged trait instance parameters with schema defaults applied. Fields depend on the Trait schema definition.
Helper Functionsβ
| Function | Description |
|---|---|
oc_generate_name(args...) | Generate valid Kubernetes names with hash suffix for uniqueness |
oc_hash(string) | Generate 8-character FNV-32a hash from input string |
oc_merge(map1, map2, ...) | Shallow merge maps (later maps override earlier ones) |
oc_omit() | Remove field/key from output when used in conditional expressions |
TraitPatchβ
Defines modifications to existing resources generated by the ComponentType.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
forEach | string | No | - | CEL expression for iterating over a list |
var | string | No | - | Variable name for forEach iterations (required if forEach set) |
target | PatchTarget | Yes | - | Specifies which resource to patch |
targetPlane | string | No | dataplane | Target plane: dataplane or observabilityplane |
operations | [JSONPatchOperation] | Yes | - | List of JSONPatch operations to apply |
PatchTargetβ
Specifies which Kubernetes resource to modify.
| Field | Type | Required | Description |
|---|---|---|---|
group | string | Yes | API group (e.g., apps, batch). Use empty string "" for core resources |
version | string | Yes | API version (e.g., v1, v1beta1) |
kind | string | Yes | Resource type (e.g., Deployment, StatefulSet) |
where | string | No | CEL expression to filter which resources to patch |
JSONPatchOperationβ
Defines a modification using JSONPatch format (RFC 6902) with OpenChoreo extensions.
| Field | Type | Required | Description |
|---|---|---|---|
op | string | Yes | Operation: add, replace, remove |
path | string | Yes | JSON Pointer to the field (RFC 6901) |
value | any | No | Value to set (not used for remove) |
Supported Operationsβ
- add: Add a new field or array element
- replace: Replace an existing field value
- remove: Delete a field
Path Syntaxβ
Supports array filters for targeting specific elements:
/spec/containers[?(@.name=='app')]/volumeMounts/-
Examplesβ
Persistent Volume Traitβ
apiVersion: openchoreo.dev/v1alpha1
kind: Trait
metadata:
name: persistent-volume
namespace: default
spec:
schema:
parameters:
volumeName: "string"
mountPath: "string"
containerName: "string | default=app"
envOverrides:
size: "string | default=10Gi"
storageClass: "string | default=standard"
creates:
- template:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ${metadata.name}-${trait.instanceName}
namespace: ${metadata.namespace}
spec:
accessModes:
- ReadWriteOnce
storageClassName: ${parameters.storageClass}
resources:
requests:
storage: ${parameters.size}
patches:
- target:
group: apps
version: v1
kind: Deployment
operations:
- op: add
path: /spec/template/spec/containers[?(@.name=='${parameters.containerName}')]/volumeMounts/-
value:
name: ${parameters.volumeName}
mountPath: ${parameters.mountPath}
- op: add
path: /spec/template/spec/volumes/-
value:
name: ${parameters.volumeName}
persistentVolumeClaim:
claimName: ${metadata.name}-${trait.instanceName}
Sidecar Container Traitβ
apiVersion: openchoreo.dev/v1alpha1
kind: Trait
metadata:
name: logging-sidecar
namespace: default
spec:
schema:
parameters:
logPath: "string | default=/var/log/app"
sidecarImage: "string | default=fluent/fluent-bit:latest"
patches:
- target:
group: apps
version: v1
kind: Deployment
operations:
- op: add
path: /spec/template/spec/containers/-
value:
name: log-collector
image: ${parameters.sidecarImage}
volumeMounts:
- name: logs
mountPath: ${parameters.logPath}
- op: add
path: /spec/template/spec/volumes/-
value:
name: logs
emptyDir: {}
Resource Limits Traitβ
apiVersion: openchoreo.dev/v1alpha1
kind: Trait
metadata:
name: resource-limits
namespace: default
spec:
schema:
envOverrides:
cpuLimit: "string | default=1000m"
memoryLimit: "string | default=512Mi"
patches:
- target:
group: apps
version: v1
kind: Deployment
operations:
- op: add
path: /spec/template/spec/containers[?(@.name=='main')]/resources/limits/cpu
value: ${parameters.cpuLimit}
- op: add
path: /spec/template/spec/containers[?(@.name=='main')]/resources/limits/memory
value: ${parameters.memoryLimit}
Multi-Container Trait with forEachβ
apiVersion: openchoreo.dev/v1alpha1
kind: Trait
metadata:
name: multi-volume
namespace: default
spec:
schema:
types:
Mount:
name: "string"
path: "string"
parameters:
mounts: "[]Mount"
patches:
- target:
group: apps
version: v1
kind: Deployment
forEach: ${parameters.mounts}
var: mount
operations:
- op: add
path: /spec/template/spec/volumes/-
value:
name: ${mount.name}
emptyDir: {}
- op: add
path: /spec/template/spec/containers[?(@.name=='app')]/volumeMounts/-
value:
name: ${mount.name}
mountPath: ${mount.path}
Usageβ
Developers attach traits to components in the Component specification:
apiVersion: openchoreo.dev/v1alpha1
kind: Component
metadata:
name: my-service
spec:
componentType: deployment/service
traits:
- name: persistent-volume
instanceName: data-storage
parameters:
volumeName: data
mountPath: /var/data
containerName: app
envOverrides:
size: 20Gi
storageClass: fast-ssd
Platform engineers can also override trait parameters in ReleaseBinding:
apiVersion: openchoreo.dev/v1alpha1
kind: ReleaseBinding
metadata:
name: my-service-production
namespace: default
spec:
environment: production
owner:
componentName: my-service
projectName: default
traitOverrides:
- instanceName: data-storage
parameters:
size: 100Gi
storageClass: production-ssd
Best Practicesβ
- Single Responsibility: Each trait should address one cross-cutting concern
- Naming: Use descriptive names that indicate the capability being added
- Parameters: Provide sensible defaults for all non-required parameters
- Target Specificity: Use
whereclauses when needed to avoid unintended modifications - Testing: Test traits with different ComponentTypes to ensure compatibility
- Documentation: Document which ComponentTypes a trait is designed to work with
- Idempotency: Ensure traits can be safely applied multiple times
Trait vs ComponentTypeβ
| Aspect | ComponentType | Trait |
|---|---|---|
| Purpose | Defines core deployment pattern | Adds cross-cutting concerns |
| Scope | One per component | Multiple can be attached to one component |
| Resources | Creates primary workload resources | Creates supplementary resources or patches existing ones |
| Modification | Determines base resource structure | Modifies or extends ComponentType resources |
| Examples | Service, WebApp, ScheduledTask | Persistent Storage, Logging, Security |
Related Resourcesβ
- ComponentType - Defines the base deployment pattern
- Component - Attaches traits to components
- ReleaseBinding - Binds a ComponentRelease to an environment with trait parameter overrides