Skip to main content

Component Types

A ComponentType is the single most important thing a platform engineer authors. It is the template a developer picks when they create a component — and by picking it, they accept the workload shape, the configurable parameters, the Kubernetes resources that get generated, and the builders they're allowed to use. Get your ComponentTypes right and developers self-serve safely without ever touching raw Kubernetes; the ComponentType is the golden path.

note

DevsPortal is built on the Control Plane Operator engine. ComponentType, ClusterComponentType, the openchoreo.dev/v1alpha1 API group, and the occ CLI are unchanged engine internals. Examples target the Fedshi instance.

Portal first, CLI second

Author ComponentTypes from the portal's Create… templates — they open the in-portal YAML editor, pre-filled with the right kind and skeleton. The occ/YAML form below is for scripting, review, and GitOps. Authoring a ComponentType is genuinely schema and CEL work, so there's no no-YAML wizard — the portal just gives you the editor and validation.

Where you author them

In the portal: open Create… in the left nav and, under Platform Resources, pick the ClusterComponentType template ("Define a cluster-wide reusable deployment pattern for how components are deployed across all namespaces"). It opens the in-portal YAML editor seeded with the ClusterComponentType kind, where you fill in the schema and resource templates described below. For a namespace-scoped ComponentType, scroll the same Create… page to its template.

The portal Create page, with the ClusterComponentType template under Platform Resources

Or with the occ CLI: write the manifest and apply it — see Managing them with the CLI below.

Why you author them

The platform ships with default ComponentTypes for common shapes — backend services, web apps, scheduled jobs. In most organizations those are a starting point, not the finish line. You author your own (or override the defaults) to:

  • Bake in your standards — sane resource defaults, security contexts, labels, the gateway wiring your org uses.
  • Add patterns the defaults don't cover — an internal-only gRPC service, a worker with a queue, a proxy.
  • Enforce policy by construction — a developer literally cannot deploy something off-menu; they pick from what you published, and what they configure is validated against your schema.

The developer keeps a simple Component model and never sees a Deployment, Service, or HTTPRoute. You keep control of how every one of those is generated.

ComponentType vs ClusterComponentType

A ClusterComponentType is the cluster-scoped variant. They share the same spec; only scope differs. The platform's defaults ship as ClusterComponentTypes so every organization namespace sees them without duplication. Author a namespace-scoped ComponentType only when one org needs to customize or override a type for itself.

warning

A ClusterComponentType is cluster-scoped, so its manifest must not set metadata.namespace. If you copy from a namespace-scoped example, delete the namespace field or the resource will fail validation.

The anatomy of a ComponentType

A ComponentType has four parts. Understanding the split between the two schemas is the key to authoring good ones.

PartFieldWhat it does
Workload kindworkloadTypeThe primary Kubernetes resource: deployment, statefulset, cronjob, job, or proxy
Parameter schemaparameters.openAPIV3SchemaWhat a developer sets once per release — same in every environment (replicas, ports)
Environment schemaenvironmentConfigs.openAPIV3SchemaWhat can be overridden per environment via a binding (CPU/memory, scaling)
Resource templatesresourcesThe Kubernetes manifests generated from the above, via CEL
Allowed workflowsallowedWorkflowsWhich build Workflows developers may use
Allowed traitsallowedTraitsWhich Traits may be attached

Parameters vs environment configs

This distinction is what makes "build once, promote everywhere" work:

  • parameters are captured in the immutable ComponentRelease and applied identically wherever that release is deployed. Replica count, container port, image pull policy. To change a parameter, you create a new release.
  • environmentConfigs are overridden per environment through a ReleaseBinding. CPU and memory requests, scaling limits — the things that should differ between dev and prod while the release snapshot itself stays byte-for-byte identical.

Author generous limits in prod and tight ones in dev without a separate build, because that difference lives in the binding, not the release.

Both schemas use standard OpenAPI v3 JSON Schema (openAPIV3Schema), so you get defaults, enums, minimum/maximum, and required for free — and the schema is self-documenting, which is what the console's Create Component wizard renders fields from.

A representative example

A trimmed web-service ClusterComponentType: a Deployment, a Service, and an externally-routed HTTPRoute generated per external endpoint.

apiVersion: openchoreo.dev/v1alpha1
kind: ClusterComponentType
metadata:
name: web-service
spec:
workloadType: deployment

# Capabilities developers may attach
allowedTraits:
- kind: ClusterTrait
name: autoscaler
- kind: ClusterTrait
name: persistent-volume

# Builders developers may use (see Workflows)
allowedWorkflows:
- kind: ClusterWorkflow
name: dockerfile-builder
- kind: ClusterWorkflow
name: gcp-buildpacks-builder

# Set once per release — identical in every environment
parameters:
openAPIV3Schema:
type: object
properties:
replicas:
type: integer
default: 1
minimum: 1

# Overridable per environment via ReleaseBinding
environmentConfigs:
openAPIV3Schema:
type: object
properties:
resources:
type: object
properties:
cpu:
type: string
default: "100m"
memory:
type: string
default: "256Mi"

resources:
# Primary workload — id must match workloadType
- id: deployment
template:
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${metadata.componentName}
namespace: ${metadata.namespace}
labels: ${metadata.labels}
spec:
replicas: ${parameters.replicas}
selector:
matchLabels: ${metadata.podSelectors}
template:
metadata:
labels: ${metadata.podSelectors}
spec:
containers:
- name: main
image: ${workload.container.image}
resources:
requests:
cpu: ${environmentConfigs.resources.cpu}
memory: ${environmentConfigs.resources.memory}

- id: service
template:
apiVersion: v1
kind: Service
metadata:
name: ${metadata.componentName}
namespace: ${metadata.namespace}
spec:
selector: ${metadata.podSelectors}
ports: ${workload.toServicePorts()}

# One HTTPRoute per external endpoint — note forEach + the visibility filter
- id: httproute-external
forEach: '${workload.endpoints.transformList(name, ep, ("external" in ep.visibility && ep.type in ["HTTP", "GraphQL", "Websocket"]) ? [name] : []).flatten()}'
var: endpoint
template:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: ${oc_generate_name(metadata.componentName, endpoint)}
namespace: ${metadata.namespace}
spec:
parentRefs:
- name: ${gateway.ingress.external.name}
namespace: ${gateway.ingress.external.namespace}
rules:
- backendRefs:
- name: ${metadata.componentName}
port: ${workload.endpoints[endpoint].port}

Three template mechanisms are doing the work here, and they're the heart of authoring:

  • CEL interpolation${parameters.replicas}, ${workload.container.image} pull values from the developer's parameters, the workload, and platform-injected metadata/gateway context into the manifest.
  • forEach + var — generate N resources from a list (here, one HTTPRoute per matching endpoint). The expression filters by endpoint visibility, so an internal endpoint never gets an external route. This is how a ComponentType turns a developer's declared endpoints into the right networking automatically.
  • includeWhen (not shown) — a boolean CEL expression that conditionally emits a resource (e.g. only create a Certificate when TLS is enabled).

Built-in helpers like oc_generate_name(), oc_merge(), and oc_dns_label(), plus the full metadata/parameters/workload/gateway context, are documented in the CEL reference linked below.

Validation rules

Beyond JSON-Schema validation of individual fields, a ComponentType can carry CEL-based validation rules in a validations section for cross-field, semantic checks — "if HTTPS is enabled, a certificate ref is required", "max replicas must exceed min replicas". These run when a developer's configuration is evaluated and surface as clear errors, so a misconfigured component is rejected up front rather than producing a broken deployment. The field-level syntax is in the upstream validation-rules reference.

How developers consume it

Once published, a developer references your ComponentType and supplies parameter values that conform to your schema:

apiVersion: openchoreo.dev/v1alpha1
kind: Component
metadata:
name: cart-api
namespace: fedshi
spec:
owner:
projectName: checkout
componentType:
kind: ClusterComponentType
name: deployment/web-service
parameters:
replicas: 2
traits:
- name: autoscaler
kind: ClusterTrait
instanceName: hpa
parameters:
maxReplicas: 5

A few consequences of your design land here:

  • The name is workloadType/name (deployment/web-service), and developers must set kind: ClusterComponentType explicitly to use the platform defaults.
  • They can only attach Traits you listed in allowedTraits, and only build with Workflows you listed in allowedWorkflows — a component that references a disallowed workflow stops in a WorkflowNotAllowed state (see governance and guardrails).
  • Per-environment overrides go in the binding (componentTypeEnvironmentConfigs), against your environmentConfigs schema — which is exactly how the same release runs lean in dev and generous in prod.

The developer side of this is documented in projects and components.

Managing them with the CLI

occ clustercomponenttype list
occ clustercomponenttype get web-service
occ componenttype list -n fedshi
occ apply -f web-service.yaml

Deep reference

The field-level syntax — the full openAPIV3Schema features, every CEL context variable and built-in function, forEach/includeWhen/var semantics, and validation rules — is documented upstream:

What's next

  • Traits — the composable capabilities your ComponentType allows.
  • Workflows — the builders your allowedWorkflows references.
  • Governance and guardrails — how the ComponentType menu becomes a guardrail.