Skip to main content
Version: Next

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

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

fieldName: "type | default=value | required=true | enum=val1,val2"

Example:

schema:
parameters:
volumeName: "string | required=true"
mountPath: "string | required=true"
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.

FieldTypeRequiredDescription
templateobjectYesKubernetes resource template with CEL expressions

CEL expressions in trait templates have access to:

  • metadata.* - Component metadata
  • trait.* - Trait parameters and instance name
  • parameters.* - Component parameters
  • environment.* - Environment information

TraitPatch

Defines modifications to existing resources generated by the ComponentType.

FieldTypeRequiredDescription
forEachstringNoCEL expression for iterating over a list
varstringNoVariable name for forEach iterations (required if forEach set)
targetPatchTargetYesSpecifies which resource to patch
operations[JSONPatchOperation]YesList of JSONPatch operations to apply

PatchTarget

Specifies which Kubernetes resource to modify.

FieldTypeRequiredDescription
groupstringNoAPI group (empty 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, mergeShallow
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
  • mergeShallow: OpenChoreo extension for overlaying map keys

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 | required=true"
mountPath: "string | required=true"
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: ${trait.parameters.storageClass}
resources:
requests:
storage: ${trait.parameters.size}

patches:
- target:
group: apps
version: v1
kind: Deployment
operations:
- op: add
path: /spec/template/spec/containers/[?(@.name=='${trait.parameters.containerName}')]/volumeMounts/-
value:
name: ${trait.parameters.volumeName}
mountPath: ${trait.parameters.mountPath}
- op: add
path: /spec/template/spec/volumes/-
value:
name: ${trait.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: ${trait.parameters.sidecarImage}
volumeMounts:
- name: logs
mountPath: ${trait.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: mergeShallow
path: /spec/template/spec/containers/[?(@.name=='main')]/resources
value:
limits:
cpu: ${trait.parameters.cpuLimit}
memory: ${trait.parameters.memoryLimit}

Multi-Container Trait with forEach

apiVersion: openchoreo.dev/v1alpha1
kind: Trait
metadata:
name: multi-volume
namespace: default
spec:
schema:
parameters:
mounts: "array<object> | required=true"

patches:
- target:
group: apps
version: v1
kind: Deployment
forEach: ${trait.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 ComponentDeployment:

apiVersion: openchoreo.dev/v1alpha1
kind: ComponentDeployment
metadata:
name: my-service-production
spec:
componentName: my-service
environment: production

traitOverrides:
- instanceName: data-storage
parameters:
size: 100Gi
storageClass: production-ssd

Best Practices

  1. Single Responsibility: Each trait should address one cross-cutting concern
  2. Naming: Use descriptive names that indicate the capability being added
  3. Parameters: Provide sensible defaults for all non-required parameters
  4. Target Specificity: Use where clauses when needed to avoid unintended modifications
  5. Testing: Test traits with different ComponentTypes to ensure compatibility
  6. Documentation: Document which ComponentTypes a trait is designed to work with
  7. Idempotency: Ensure traits can be safely applied multiple times

Trait vs ComponentType

AspectComponentTypeTrait
PurposeDefines core deployment patternAdds cross-cutting concerns
ScopeOne per componentMultiple can be attached to one component
ResourcesCreates primary workload resourcesCreates supplementary resources or patches existing ones
ModificationDetermines base resource structureModifies or extends ComponentType resources
ExamplesService, WebApp, ScheduledTaskPersistent Storage, Logging, Security