ClusterTrait
A ClusterTrait is a cluster-scoped variant of Trait that defines reusable cross-cutting concerns available across namespaces. This enables platform engineers to define shared traits once β such as persistent storage, observability, or security policies β and allow Components in any namespace to reference them, eliminating duplication.
ClusterTraits share the same spec structure as Traits with the same schema, creates, and patches fields.
The only difference is scope: ClusterTraits are cluster-scoped (no namespace), while Traits are namespace-scoped.
Unlike namespace-scoped Traits, ClusterTraits do not support the validations field.
API Versionβ
openchoreo.dev/v1alpha1
Resource Definitionβ
Metadataβ
ClusterTraits are cluster-scoped resources (no namespace).
apiVersion: openchoreo.dev/v1alpha1
kind: ClusterTrait
metadata:
name: <clustertrait-name>
ClusterTrait manifests must not include metadata.namespace. If you are copying from a namespace-scoped
Trait example, remove the namespace field.
Short names: ctrait, ctraits
Spec Fieldsβ
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
schema | TraitSchema | No | - | Configurable parameters for this trait |
creates | [TraitCreate] | No | [] | New Kubernetes resources to create when this trait is applied |
patches | [TraitPatch] | No | [] | Modifications to the rendered resources produced by the ComponentType template |
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 |
includeWhen | string | No | - | CEL expression determining if resource should be created |
forEach | string | No | - | CEL expression for generating multiple resources from list |
var | string | No | - | Variable name for forEach iterations (required if forEach is set) |
template | object | Yes | - | Kubernetes resource template with CEL expressions |
CEL Context Variables for Createsβ
CEL expressions in trait create 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.componentNamespace | string | Target namespace of the component |
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β
Trait instance parameters from Component.spec.traits[].parameters with schema defaults applied. Use for static configuration that doesn't change across environments.
envOverridesβ
Environment-specific overrides from ReleaseBinding.spec.traitOverrides[instanceName] with schema defaults applied. Use for values that vary per environment.
dataplaneβ
Data plane configuration:
| Field | Type | Description |
|---|---|---|
dataplane.secretStore | string | Name of the ClusterSecretStore for external secrets |
dataplane.publicVirtualHost | string | Public virtual host for external access |
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 is 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 ClusterTraitβ
apiVersion: openchoreo.dev/v1alpha1
kind: ClusterTrait
metadata:
name: persistent-volume
spec:
schema:
parameters:
volumeName: "string | required=true"
mountPath: "string | required=true"
containerName: "string | default=app"
envOverrides:
size: "string | default=10Gi"
storageClass: "string | default=standard"
creates:
- targetPlane: dataplane
template:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ${metadata.name}-${parameters.volumeName}
namespace: ${metadata.namespace}
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: ${envOverrides.size}
storageClassName: ${envOverrides.storageClass}
patches:
- target:
group: apps
version: v1
kind: Deployment
targetPlane: dataplane
operations:
- op: add
path: /spec/template/spec/volumes/-
value:
name: ${parameters.volumeName}
persistentVolumeClaim:
claimName: ${metadata.name}-${parameters.volumeName}
- op: add
path: /spec/template/spec/containers/[?(@.name=='${parameters.containerName}')]/volumeMounts/-
value:
name: ${parameters.volumeName}
mountPath: ${parameters.mountPath}
Sidecar Container ClusterTraitβ
apiVersion: openchoreo.dev/v1alpha1
kind: ClusterTrait
metadata:
name: logging-sidecar
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 ClusterTraitβ
apiVersion: openchoreo.dev/v1alpha1
kind: ClusterTrait
metadata:
name: resource-limits
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: ${envOverrides.cpuLimit}
- op: add
path: /spec/template/spec/containers[?(@.name=='main')]/resources/limits/memory
value: ${envOverrides.memoryLimit}
Multi-Volume ClusterTrait with forEachβ
apiVersion: openchoreo.dev/v1alpha1
kind: ClusterTrait
metadata:
name: multi-volume
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 ClusterTraits to components using kind: ClusterTrait in the Component specification:
apiVersion: openchoreo.dev/v1alpha1
kind: Component
metadata:
name: my-service
namespace: default
spec:
componentType:
kind: ClusterComponentType
name: deployment/service
traits:
- name: persistent-volume
kind: ClusterTrait
instanceName: data-storage
parameters:
volumeName: data
mountPath: /var/data
containerName: app
Platform engineers can set trait envOverrides 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:
data-storage: # keyed by instanceName
size: 100Gi
storageClass: production-ssd
Best Practicesβ
- Use for shared concerns: Define ClusterTraits for cross-cutting concerns shared across namespaces; use namespace-scoped Traits for namespace-specific concerns
- 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 ClusterTraits with different ComponentTypes to ensure compatibility
- Idempotency: Ensure traits can be safely applied multiple times
ClusterTrait vs Traitβ
| Aspect | Trait | ClusterTrait |
|---|---|---|
| Scope | Namespace-scoped | Cluster-scoped |
| Availability | Only within its namespace | Across all namespaces |
| Short names | trait, traits | ctrait, ctraits |
| Validations | Supports validations field | Does not support validations field |
| Used by | ComponentType or ClusterComponentType | ComponentType or ClusterComponentType |
Related Resourcesβ
- Trait - Namespace-scoped variant of ClusterTrait
- ComponentType - Defines the base deployment pattern that traits modify
- ClusterComponentType - Cluster-scoped variant of ComponentType (can only use ClusterTraits)
- Component - Attaches traits to components
- ReleaseBinding - Binds a ComponentRelease to an environment with trait parameter overrides