Skip to main content
Version: v0.16.x

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.

note

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>
note

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​

FieldTypeRequiredDefaultDescription
schemaTraitSchemaNo-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.

FieldTypeRequiredDefaultDescription
typesobjectNo-Reusable type definitions referenced in parameters
parametersobjectNo-Developer-facing configuration options
envOverridesobjectNo-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.

FieldTypeRequiredDefaultDescription
targetPlanestringNodataplaneTarget plane: dataplane or observabilityplane
includeWhenstringNo-CEL expression determining if resource should be created
forEachstringNo-CEL expression for generating multiple resources from list
varstringNo-Variable name for forEach iterations (required if forEach is set)
templateobjectYes-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):

FieldTypeDescription
metadata.namestringBase name for generated resources (e.g., my-service-dev-a1b2c3d4)
metadata.namespacestringTarget namespace for resources
metadata.componentNamespacestringTarget namespace of the component
metadata.componentNamestringName of the component
metadata.componentUIDstringUnique identifier of the component
metadata.projectNamestringName of the project
metadata.projectUIDstringUnique identifier of the project
metadata.environmentNamestringName of the environment (e.g., development, production)
metadata.environmentUIDstringUnique identifier of the environment
metadata.dataPlaneNamestringName of the data plane
metadata.dataPlaneUIDstringUnique identifier of the data plane
metadata.labelsmapCommon labels to add to all resources
metadata.annotationsmapCommon annotations to add to all resources
metadata.podSelectorsmapPlatform-injected selectors for pod identity
trait​

Trait-specific metadata:

FieldTypeDescription
trait.namestringName of the trait (e.g., persistent-volume)
trait.instanceNamestringUnique 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:

FieldTypeDescription
dataplane.secretStorestringName of the ClusterSecretStore for external secrets
dataplane.publicVirtualHoststringPublic virtual host for external access
Helper Functions​
FunctionDescription
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.

FieldTypeRequiredDefaultDescription
forEachstringNo-CEL expression for iterating over a list
varstringNo-Variable name for forEach iterations (required if forEach is set)
targetPatchTargetYes-Specifies which resource to patch
targetPlanestringNodataplaneTarget plane: dataplane or observabilityplane
operations[JSONPatchOperation]Yes-List of JSONPatch operations to apply

PatchTarget​

Specifies which Kubernetes resource to modify.

FieldTypeRequiredDescription
groupstringYesAPI group (e.g., apps, batch). Use empty string "" for core resources
versionstringYesAPI version (e.g., v1, v1beta1)
kindstringYesResource type (e.g., Deployment, StatefulSet)
wherestringNoCEL expression to filter which resources to patch

JSONPatchOperation​

Defines a modification using JSONPatch format (RFC 6902) with OpenChoreo extensions.

FieldTypeRequiredDescription
opstringYesOperation: add, replace, remove
pathstringYesJSON Pointer to the field (RFC 6901)
valueanyNoValue 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​

  1. Use for shared concerns: Define ClusterTraits for cross-cutting concerns shared across namespaces; use namespace-scoped Traits for namespace-specific concerns
  2. Single responsibility: Each trait should address one cross-cutting concern
  3. Naming: Use descriptive names that indicate the capability being added
  4. Parameters: Provide sensible defaults for all non-required parameters
  5. Target specificity: Use where clauses when needed to avoid unintended modifications
  6. Testing: Test ClusterTraits with different ComponentTypes to ensure compatibility
  7. Idempotency: Ensure traits can be safely applied multiple times

ClusterTrait vs Trait​

AspectTraitClusterTrait
ScopeNamespace-scopedCluster-scoped
AvailabilityOnly within its namespaceAcross all namespaces
Short namestrait, traitsctrait, ctraits
ValidationsSupports validations fieldDoes not support validations field
Used byComponentType or ClusterComponentTypeComponentType or ClusterComponentType
  • 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