CEL Architecture Placement in the Multi-Architecture Tuning Operator

Author: Pranita Turrey/IBM, Paul Bastide/IBM, Punith Kenchappa/IBM

Audience: Cluster administrators responsible for workload placement on heterogeneous OpenShift clusters.

This Blog explains how to configure and operate Common Expression Language (CEL) Architecture Placement using the Multi-Architecture Tuning Operator (MTO). It covers the concepts you need to understand, the configuration you need to create, and the outcomes you can expect. Implementation internals, detailed field references, and product-specific examples are collected in appendices at the end.



1. Why CEL Architecture Placement

The Multi-Architecture Tuning Operator (MTO) automates architecture-aware Pod scheduling on heterogeneous clusters. By default, MTO inspects a Pod’s container images and sets node affinity so the Pod runs only on nodes whose CPU architecture supports those images. This is called image-based placement.

Image-based placement covers the common case well. It has a structural limitation, though: when an image is built for multiple architectures, inspection alone cannot choose between them. If your cluster has both amd64 and ppc64le nodes and a workload’s image supports both, image-based placement will declare both eligible—and the scheduler will place the Pod wherever capacity is available.

CEL Architecture Placement closes this gap. By writing a small set of rules in a PodPlacementConfig, you can match Pods by their existing labels, annotations, or name patterns and assign them a specific architecture. The result:

  • Workloads land where they belong—on the right architecture for performance, licensing, or operational policy—without touching a single Deployment manifest.
  • Migrations are incremental—move a component at a time by flipping a label, not by editing dozens of YAML files.
  • Multi-architecture compute environments stay organized—different components in the same namespace can have distinct placement rules enforced automatically.

Prerequisite: A ClusterPodPlacementConfig named cluster must exist before any PodPlacementConfig can be created. The admission webhook enforces this.

Business value

CEL Architecture Placement enables teams to enforce architecture placement policy without modifying application manifests. Placement is expressed declaratively, version-controlled alongside infrastructure configuration, and applied consistently across all Pods in a namespace from the moment of creation.

This has direct operational value:

  • Reduced toil during migrations: moving a component to a new architecture requires only a label change and a rule update—not Deployment restructuring.
  • Auditable policy: placement decisions are inspectable in the PodPlacementConfig resource, not buried in Deployment YAML across dozens of namespaces.

When to use CEL Architecture Placement

Use CEL Architecture Placement when:

  • Your cluster has nodes of multiple architectures and you need workloads to land on specific ones.
  • Multi-arch images make image-based placement insufficient (MTO cannot choose between equally-supported architectures).
  • You want to enforce placement policy at the cluster level without requiring changes to application team manifests.
  • You are migrating workloads incrementally from one architecture to another.
  • You need different components in the same namespace to run on different architectures.

Continue using image-based placement (MTO’s default) when:

  • All workloads in a namespace should simply run on architectures their images support, with no explicit routing policy.
  • You have no preference between architectures and want the scheduler to decide.

2. Core MTO Concepts

This section defines the terms used throughout this document. No prior knowledge of MTO is required.

TermMeaning
Multi-Architecture Tuning Operator (MTO)A Kubernetes operator that automates architecture-aware Pod scheduling on clusters with nodes of multiple CPU architectures (e.g., amd64 and ppc64le).
ClusterPodPlacementConfigA cluster-scoped resource that activates MTO for the cluster. Required before any namespace-level configuration can be created. Only one exists, named cluster.
PodPlacementConfigA namespace-scoped resource that configures placement rules for Pods in that namespace. Multiple resources can coexist; they are evaluated in priority order.
CEL Architecture PlacementThe placement mode described in this document. You write CEL rules inside a PodPlacementConfig to explicitly assign Pods to architectures based on their metadata.
Image-based placementMTO’s default mode: inspect container image manifests to determine which architectures the Pod supports. CEL placement bypasses this entirely for matched Pods.
fallbackArchitecturesThe architectures applied when CEL placement is enabled but no rule matches. Required field. Think of it as “everything else runs here.”
Scheduling gateA mechanism that holds a Pod in a pending state until MTO has finished evaluating it and applied the correct architecture constraint. Once MTO is done, the gate is removed and the scheduler takes over. Administrators observe this as a brief pending state on new Pods; it is expected behavior.

Note: CEL placement sets required (hard) node affinity. The NodeAffinityScoring plugin sets preferred (soft) node affinity. Both can coexist in the same PodPlacementConfig without conflict.


3. How CEL Placement Works — Operational View

When a Pod is created in a namespace where a PodPlacementConfig with the CEL placement plugin enabled exists, MTO intercepts it and applies an architecture constraint before the Pod reaches the scheduler. From an administrator’s perspective, the key outcomes are:

  • Architecture is enforced automatically. You do not need to modify Deployment manifests. Where a CEL expression matches, its configured architecture is applied; if no rule matches, fallbackArchitectures is used.
  • Pods briefly appear pending. MTO holds each Pod briefly with a scheduling gate while evaluating rules and applying the constraint. This is expected behavior.
  • The result is required node affinity. The Pod’s spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution is set to the target architecture. The scheduler will only place the Pod on a node matching that architecture.
  • Existing architecture constraints are overridden. Any prior kubernetes.io/arch node selector or architecture affinity on the Pod is replaced by the CEL-derived value. Non-architecture constraints (zone, OS, pod affinity, preferred affinity) are left untouched.

Decision flow

No

Yes

Rule matches

No rule matches

All rules error

CEL constraint applies

None applies

Pod submitted

Applicable PodPlacementConfig?

Image-based detection / cluster fallback

Evaluate rules in order

Apply architecture

Apply fallbackArchitectures

Try lower-priority PodPlacementConfig

For full precedence rules—including multiple PodPlacementConfig resources, image-based detection, and cluster-level fallback—see Section 7.

For details on how MTO implements this internally (admission webhook vs. reconciliation loop, immutability constraints), see Appendix C.


4. Configuring CEL Architecture Placement

Step 1: Enable MTO for the cluster

A ClusterPodPlacementConfig named cluster must exist. If it does not already exist in your cluster:

apiVersion: multiarch.openshift.io/v1beta1
kind: ClusterPodPlacementConfig
metadata:
  name: cluster
spec: {}
oc apply -f cluster-config.yaml

Step 2: Create a PodPlacementConfig in your namespace

A minimal PodPlacementConfig with CEL placement enabled:

apiVersion: multiarch.openshift.io/v1beta1
kind: PodPlacementConfig
metadata:
  name: my-placement-rules
  namespace: my-namespace
spec:
  priority: 10
  plugins:
    celArchitecturePlacement:
      enabled: true
      fallbackArchitectures:
        - s390x             # applied when no rule matches; required
      rules:
        - name: postgres-on-ppc64le
          expression: |
            'app.kubernetes.io/component' in self.metadata.labels &&
            self.metadata.labels['app.kubernetes.io/component'] == 'database'
          architectures:
            - ppc64le

Key points:

  • fallbackArchitectures is required. Set it to the architecture that should apply to all Pods that do not match a specific rule.
  • priority must be unique within the namespace. Values range from 0 to 255. Higher values are evaluated first.
  • labelSelector is optional. When omitted, the config applies to every Pod in the namespace.
  • rules is optional. A config with no rules applies fallbackArchitectures to every matched Pod—useful for pinning an entire namespace to a single architecture.

labelSelector scopes which Pods this PodPlacementConfig considers at all. It is a standard Kubernetes label selector evaluated against the Pod’s own labels. When set, only Pods whose labels match are subject to this config’s rules and fallback. Omit it to apply the config to all Pods in the namespace. See the advanced configuration reference for examples combining labelSelector with CEL rules.

For a complete field reference, see the advanced configuration reference.


5. Writing Maintainable CEL Rules

Each rule’s expression is evaluated against a restricted view of the Pod. The self variable exposes the Pod’s metadata only:

CEL pathTypeTypical use
self.metadata.namestringMatch a specific, known Pod name
self.metadata.generateNamestringMatch Pods from a named Deployment or StatefulSet
self.metadata.namespacestringNamespace of the Pod
self.metadata.labelsmap<string, string>Match by workload label
self.metadata.annotationsmap<string, string>Match by operator-set annotation

self.spec and self.status are not available. Expressions that reference them are rejected when the PodPlacementConfig is saved, not at runtime.

Syntax examples

Match Pods created by a Deployment named worker (controller-managed Pods use generateName):

self.metadata.generateName.startsWith('worker-')

Match by a simple label value:

has(self.metadata.labels.tier) && self.metadata.labels.tier == 'backend'

Match by a label key containing dots or slashes (use bracket notation):

'app.kubernetes.io/component' in self.metadata.labels &&
self.metadata.labels['app.kubernetes.io/component'] == 'database'

Match by annotation (useful for operator-managed workloads):

'config.company.io/placement' in self.metadata.annotations &&
self.metadata.annotations['config.company.io/placement'] == 'ppc64le'

Catch-all rule (matches every Pod — useful as a final rule to make fallback intent explicit):

true

Label access: dot vs. bracket notation

Key typeExistence checkValue check
Simple alphanumeric (tier, app)has(self.metadata.labels.tier)self.metadata.labels.tier == 'value'
Contains dots or slashes (app.kubernetes.io/component)'key' in self.metadata.labelsself.metadata.labels['key'] == 'value'

Missing keys are treated as a non-match. MTO handles missing-key evaluation errors as a non-match, allowing evaluation to continue to the next rule or fallbackArchitectures. You can safely check for a label’s presence before reading its value.

What is rejected at save time

Expressions that do not compile or violate the allowed schema are rejected when the PodPlacementConfig is saved. They can never reach runtime.

ProblemExample
Non-boolean resultself.metadata.name (returns a string)
Syntax errorself.metadata.name == (incomplete)
Unavailable fieldself.spec.nodeName == 'worker-1'
Typo in field pathself.metadata.labells.app

Maintainability note

CEL expressions are concise and powerful, but they can become difficult to read as rules accumulate. Some practices that help:

  • Give each rule a descriptive name that explains its intent, not just its mechanics.
  • Use the labelSelector on the PodPlacementConfig to narrow scope before writing rules—it reduces the surface area each rule needs to cover.
  • Prefer label-based matching over generateName-based matching where possible; labels are more stable and easier to reason about over time.
  • Keep the number of rules in a single config small. If rules are growing unwieldy, consider whether separate PodPlacementConfig resources with distinct priorities would be clearer.

A follow-up article on CEL expression maintainability, testing patterns, and advanced rule organization is forthcoming.


6. Rule Evaluation and Fallback Behavior

This section is the single authoritative reference for how rules are evaluated and what happens when no rule matches.

Within a single PodPlacementConfig

Rules are evaluated in list order. The first rule that evaluates to true wins; remaining rules are not evaluated. If no rule matches, fallbackArchitectures is applied.

If a rule’s expression fails at runtime (unexpected state, transient error), that rule is treated as a non-match and evaluation moves to the next rule. A single broken rule does not block the others.

Important exception: if every rule in a PodPlacementConfig fails at runtime, the entire config is skipped and its fallback is not applied. Lower-priority configs or image-based detection may then handle the Pod. See Section 8.

Across multiple PodPlacementConfigs

When more than one PodPlacementConfig applies to a Pod, they are evaluated in descending priority order; the first config that successfully applies a constraint wins. For the full precedence rules—including the priority cascade, runtime error behaviour, and interaction with image-based detection—see Section 7.

Fallback behavior

fallbackArchitectures is applied when the plugin is enabled and no rule matches—including when no rules are configured. It is a required field.

Recommended pattern: set fallbackArchitectures to the architecture that the majority of Pods in the namespace should use. Write explicit rules only for the exceptions. For example: if 90% of workloads should run on amd64 and a small subset must run on ppc64le, set fallbackArchitectures: [amd64] and write rules only for the ppc64le workloads.


7. Precedence and Interaction with Other Placement Methods

This section is the single authoritative reference for how CEL placement interacts with MTO’s image-based path, fallback architectures, and other placement constraints.

CEL placement takes precedence over image-based placement. When the celArchitecturePlacement plugin is enabled and applies to a Pod—whether via a rule match or the fallback—image-based detection is bypassed entirely for that Pod. MTO does not inspect container images and does not consult the cluster-level fallbackArchitecture from ClusterPodPlacementConfig.

Image-based placement only runs when no matching PodPlacementConfig applies a CEL constraint:

ConditionArchitecture source
CEL plugin enabled; rule matchesCEL rule’s architectures
CEL plugin enabled; no rule matchesfallbackArchitectures
CEL plugin disabledImage-based detection (or cluster fallback)
CEL plugin enabled; all rules error; no lower-priority config appliesImage-based detection (or cluster fallback)
No matching PodPlacementConfig existsImage-based detection (or cluster fallback)

Existing architecture constraints on the Pod are overridden. When the plugin applies, any kubernetes.io/arch entry in nodeSelector and any architecture term in required node affinity is removed and replaced with the CEL-derived value.

Other constraints are preserved. Zone affinity, OS selectors, pod affinity, and preferred affinity (NodeAffinityScoring) are left untouched. CEL placement and NodeAffinityScoring can coexist in the same PodPlacementConfig without conflict.

Priority cascade

Yes

No: all rules error



Yes

No: no lower-priority config applies

Pod submitted

Highest-priority applicable PodPlacementConfig

CEL constraint successfully applied?

Architecture applied

Next lower-priority applicable PodPlacementConfig

CEL constraint successfully applied?

Image-based detection / cluster fallback


8. Error Handling

MTO combines configuration-time validation with runtime error handling to isolate expression errors and allow rule evaluation to continue where possible.

Runtime and admission error handling

BehaviorHow it is enforced
Invalid expressions rejected at configuration timeExpressions are compiled against the allowed schema when the PodPlacementConfig is saved. Invalid, non-boolean, or schema-violating expressions are rejected immediately.
Runtime rule errorThat rule is skipped; evaluation continues with the next rule. If every rule fails, the entire config is skipped (fallback not applied) and lower-priority configs or image-based detection handle the Pod. See Section 7 for the full cascade.

Configuration-time errors

These errors are caught when the PodPlacementConfig is saved, not at runtime:

ProblemError message
Invalid CEL syntaxinvalid CEL expression in rule "…": CEL compilation error: Syntax error …
Non-boolean expressioninvalid CEL expression in rule "…": CEL expression must return a boolean, got …
Reference to self.spec.* or self.status.*invalid CEL expression in rule "…": … field 'spec'/'status' …
Typo in a metadata fieldCompilation fails against the typed schema

Operational note on broad rules: a rule expression: true with no labelSelector on the config matches every Pod in the namespace. This may be intentional (a namespace-wide architecture pin), but confirm the intended scope using the labelSelector on the PodPlacementConfig to avoid unintended placement. CEL placement controls where a Pod is scheduled but does not verify that the Pod’s images support that architecture. If you assign a workload to an architecture its image does not support, the Pod can fail to pull its image. Always verify image compatibility before applying new placement rules in production. Use oc image info <image> or podman manifest inspect <image> to check multi-arch support.

Account for improperly specified Pods. Pods that are missing expected labels or annotations will not match label- or annotation-based rules and will fall through to fallbackArchitectures. Ensure your fallback is appropriate for unlabelled or misconfigured Pods, and consider adding a monitoring check for Pods landing on the fallback architecture unexpectedly.


9. End-to-End Administrator Example

This example shows the complete flow for the most common operational pattern: default most workloads to one architecture, and explicitly route a small set of workloads to another.

This is a general pattern that any administrator can adapt. For a product-specific example using this same pattern in an IBM Cloud Pak for Data / Watsonx.data namespace, see Appendix B.

Scenario

A platform team runs a multi-architecture amd64/ppc64le/s390x cluster. The analytics-ns namespace hosts several workloads:

  • An analytics engine (component: engine) that must run on ppc64le for performance.
  • A monitoring agent (app: monitoring) that must run on s390x via an explicit rule.
  • A frontend UI and everything else defaults to amd64 via fallback.

All workloads use multi-arch images that technically support multiple architectures. Image inspection alone cannot enforce this placement policy.

Step 1 — Ensure a ClusterPodPlacementConfig exists

apiVersion: multiarch.openshift.io/v1beta1
kind: ClusterPodPlacementConfig
metadata:
  name: cluster
spec: {}
oc apply -f cluster-config.yaml

Step 2 — Create the PodPlacementConfig

apiVersion: multiarch.openshift.io/v1beta1
kind: PodPlacementConfig
metadata:
  name: two-tier-placement
  namespace: analytics-ns
spec:
  priority: 10
  plugins:
    celArchitecturePlacement:
      enabled: true
      fallbackArchitectures:
        - amd64              # everything that doesn't match a rule lands here
      rules:
        - name: engine-on-ppc64le
          expression: |
            'component' in self.metadata.labels &&
            self.metadata.labels['component'] == 'engine'
          architectures:
            - ppc64le
        - name: monitoring-agent-on-s390x
          expression: |
            'app' in self.metadata.labels &&
            self.metadata.labels['app'] == 'monitoring'
          architectures:
            - s390x
oc apply -f two-tier-placement.yaml
oc get podplacementconfig -n analytics-ns

Step 3 — Deploy workloads and observe placement

Analytics engine Pod (component: engine):

apiVersion: v1
kind: Pod
metadata:
  name: analytics-engine
  namespace: analytics-ns
  labels:
    component: engine
spec:
  containers:
  - name: engine
    image: quay.io/example/analytics-engine:latest

Rule engine-on-ppc64le matches → ppc64le is applied.

Frontend Pod (component: frontend):

apiVersion: v1
kind: Pod
metadata:
  name: analytics-ui
  namespace: analytics-ns
  labels:
    component: frontend
spec:
  containers:
  - name: ui
    image: quay.io/example/analytics-ui:latest

No rule matches → fallback amd64 is applied.

Monitoring agent (app: monitoring):

apiVersion: v1
kind: Pod
metadata:
  name: monitoring-agent
  namespace: analytics-ns
  labels:
    app: monitoring
spec:
  containers:
  - name: agent
    image: quay.io/example/monitoring:latest

Rule monitoring-agent-on-s390x matches → s390x is applied.

Step 4 — Validate the placement

After MTO processes each Pod, verify the resulting node affinity:

oc get pod analytics-engine -n analytics-ns -o jsonpath='{.spec.affinity}'

Expected output for analytics-engine (ppc64le via rule match):

spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/arch
            operator: In
            values:
            - ppc64le

For analytics-ui, the same structure will show values: [amd64] (via fallback). For monitoring-agent, it will show values: [s390x] (via rule match).

What to check:

  • Each Pod’s spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution reflects the expected architecture.
  • Pods are in Running state (not stuck in Pending beyond the brief scheduling gate window).
  • Pod events do not show image pull errors (which would indicate an architecture/image mismatch).

10. Conclusion

CEL Architecture Placement gives cluster administrators direct, policy-driven control over workload placement in heterogeneous clusters—without requiring application teams to modify their Deployment manifests.

For operations teams, this reduces toil during architecture migrations and provides clear, auditable placement policy expressed as code. A single PodPlacementConfig can govern an entire namespace, routing each workload to the right architecture automatically.

For platform engineers, this makes multi-architecture compute composable: multiple PodPlacementConfig resources can coexist in a namespace with distinct priorities, allowing team-level and platform-level placement policies to stack cleanly.

Adopt CEL Architecture Placement when you need explicit, policy-driven control over which architectures workloads land on—especially in multi-architecture compute namespaces where image-based detection alone is insufficient. Start with the default-to-one-architecture-with-exceptions pattern, validate in a non-production namespace first, and extend incrementally.


Appendix A: Placement Strategies and Design Patterns

Recommended adoption patterns

Before applying placement rules in production, follow these steps:

  1. Start in a non-production namespace. Apply your PodPlacementConfig and verify that rules match the intended Pods before rolling out to production.
  2. Default to one architecture; route exceptions explicitly. Set fallbackArchitectures to the majority architecture and write rules only for the minority. This is the most maintainable long-term pattern. See Pattern 1 below.
  3. Use labels as the placement control plane. Instead of writing rules that match on Pod name or generateName, have your CI/CD pipeline apply a placement label (e.g., arch-placement: ppc64le) to the Pod template. Rules then stay stable as workload names change. See Pattern 3 and Pattern 4 for annotation- and name-based alternatives.
  4. Layer configs by concern. Use multiple PodPlacementConfig resources with distinct priorities to separate platform-level policy (high priority) from team-level exceptions (lower priority).

Pattern 1: Default to one architecture; explicitly route exceptions

The most common pattern. Set fallbackArchitectures to the majority architecture and write rules only for workloads that need something different. Rules are evaluated top-to-bottom; the first match wins. Everything else falls through to the fallback.

fallbackArchitectures:
  - amd64
rules:
  - name: high-memory-workload-on-ppc64le
    expression: |
      'workload-class' in self.metadata.labels &&
      self.metadata.labels['workload-class'] == 'high-memory'
    architectures:
      - ppc64le

Effect: every Pod in the namespace lands on amd64 unless it carries workload-class: high-memory, in which case it lands on ppc64le. This is also the pattern used in the Watsonx.data example (Appendix B).

Pattern 2: Incremental architecture migration using a label gate

A team migrating workloads from amd64 to ppc64le adds a label (migration-ready: "true") to each component as it is validated on the new architecture. No Deployment manifest restructuring is required.

fallbackArchitectures:
  - amd64
rules:
  - name: migrated-workloads-on-ppc64le
    expression: |
      'migration-ready' in self.metadata.labels &&
      self.metadata.labels['migration-ready'] == 'true'
    architectures:
      - ppc64le

To move a component, apply the label to its Pod template. To roll back, remove the label. The PodPlacementConfig does not change.

Pattern 3: Placement via annotation for operator-managed workloads

When Pods are created by operators that set annotations but not labels, use annotation-based matching:

rules:
  - name: annotated-ppc64le-workloads
    expression: |
      'platform.company.io/arch-preference' in self.metadata.annotations &&
      self.metadata.annotations['platform.company.io/arch-preference'] == 'ppc64le'
    architectures:
      - ppc64le

Pattern 4: Matching controller-managed Pods by name prefix

Pods created by a Deployment named my-service will have generateName: my-service-. Use generateName to match them reliably when label-based matching is not available:

rules:
  - name: my-service-on-ppc64le
    expression: self.metadata.generateName.startsWith('my-service-')
    architectures:
      - ppc64le

Prefer label-based matching where possible. generateName matching is sensitive to Deployment rename operations and may require rule updates if workload names change.

CEL expression best practices, testing patterns, and maintainability guidance are covered in a forthcoming follow-up article.


Appendix B: Watsonx.data / cpd-instance Example

This appendix documents a validated configuration for a namespace hosting IBM Cloud Pak for Data (CPD) and Watsonx.data workloads on a multi-architecture amd64/ppc64le cluster. It is provided as a reference for the general pattern described in Appendix A, Pattern 1: a namespace where a small number of components are qualified on ppc64le and everything else defaults to amd64.

Applicability

This configuration applies to any platform product where:

  • Most workloads can run on the default architecture (amd64).
  • A specific, known set of components has been tested and qualified on a secondary architecture (ppc64le).
  • Placement rules should be scoped to the product’s namespace to avoid cross-namespace interference.

Configuration

apiVersion: multiarch.openshift.io/v1beta1
kind: PodPlacementConfig
metadata:
  name: cpd-cel-placement
  namespace: cpd-instance
spec:
  priority: 200
  # No labelSelector — this config applies to all pods in cpd-instance.
  # The CEL rules below determine per-workload architecture.
  plugins:
    celArchitecturePlacement:
      enabled: true
      fallbackArchitectures:
        - amd64              # all unmatched workloads run on amd64
      rules:
        - name: ibm-nginx-on-ppc64le
          expression: |
            'component' in self.metadata.labels &&
            self.metadata.labels['component'] == 'ibm-nginx'
          architectures:
            - ppc64le
        - name: runtime-manager-api-on-ppc64le
          expression: |
            'component' in self.metadata.labels &&
            self.metadata.labels['component'] == 'runtime-manager-api'
          architectures:
            - ppc64le
        - name: cpd-mgmt-on-ppc64le
          expression: |
            'component' in self.metadata.labels &&
            self.metadata.labels['component'] == 'cpd-mgmt'
          architectures:
            - ppc64le

How it works

The config has no labelSelector, so it applies to every Pod in cpd-instance. Three rules match the specific CPD components that are qualified on ppc64le; all other Pods fall through to the amd64 fallback.

Pod created in cpd-instance
      │
      ├── component == 'ibm-nginx'           → ppc64le
      ├── component == 'runtime-manager-api' → ppc64le
      ├── component == 'cpd-mgmt'            → ppc64le
      └── (anything else)                    → amd64 (fallback)

Deploying

oc apply -f cpd-cel-placement.yaml
oc get podplacementconfig -n cpd-instance

Image compatibility

This config assigns ppc64le to specific components. Before applying it in production, confirm that every container image used by those components (ibm-nginx, runtime-manager-api, cpd-mgmt) includes a ppc64le manifest. Assigning a workload to ppc64le when its image has no ppc64le layer can cause the Pod to fail to pull its image.

oc image info <image>
# or
podman manifest inspect <image>

Appendix C: Advanced Configuration Reference

The detailed configuration reference — including the complete PodPlacementConfig field table, the full self variable schema, internal implementation details, and labelSelector usage patterns — has been moved to a dedicated document:

CEL Architecture Placement: Advanced Configuration Reference


Appendix D: Testing, Validation Results, and Lessons Learned

The CEL architecture placement feature is covered by unit tests, admission webhook tests, and controller integration tests (using envtest).

Test coverage includes:

  • CEL compilation and runtime evaluation for all documented expression patterns
  • Admission-time rejection of invalid, non-boolean, and schema-violating expressions
  • Rule ordering and first-match semantics
  • Fallback behavior when no rule matches
  • Priority-based selection across multiple PodPlacementConfig resources
  • Removal of existing architecture constraints from nodeSelector and required node affinity
  • Preservation of non-architecture constraints
  • Idempotent re-reconciliation
  • Coexistence with NodeAffinityScoring
  • The all-errored-rules bypass behavior (config skipped entirely if every rule fails at runtime)

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *