Skip to Content
GuideAPI ReferencePodMutation

PodMutation API Reference

PodMutation is a cluster-scoped custom resource that selects workloads and rewrites scheduling-related Pod fields during admission.

apiVersion: agent.cloudpilot.ai/v1alpha1 kind: PodMutation

The short name is pm:

kubectl get pm

Processing model

PodMutation follows this order for each newly created Pod:

  1. Evaluate every enabled PodMutation against the original incoming Pod.
  2. Sort matching resources by ascending spec.priority, then by resource name.
  3. For each matching resource, apply mutationRemove before mutationAdd.
  4. Append CloudPilot-managed Spot, NonSpot, and Diversity affinity.

All resources are matched before mutation begins. A field added by one PodMutation therefore cannot cause another PodMutation to match.

PodMutation does not update running Pods in place. Recreate or roll out a workload after creating or changing a rule. The webhook refreshes its PodMutation cache every 30 seconds.

Spec fields

FieldTypeRequiredDefaultDescription
enablebooleanNotrueEnables or disables the resource.
priorityintegerNo0Controls execution order. Lower values run first. Rules with the same priority are ordered by name.
workloadSelectorobjectYesSelects workloads and Pods. At least one selector field must be non-empty.
mutationRemoveobjectNoRemoves fields before mutationAdd is applied.
mutationAddobjectNoAdds or replaces supported Pod fields.

workloadSelector

Selector dimensions are ANDed together. Within one list field, values are ORed unless stated otherwise.

FieldTypeMatching behavior
namespacesstring arrayThe workload namespace must equal one listed value.
namesstring arrayThe workload name must equal one listed value.
kindsstring arrayThe workload kind must be Deployment, StatefulSet, Job, or CronJob and equal one listed value.
nodeSelectorMatchmap of string to stringThe Pod nodeSelector must contain every configured key-value pair.
nodeAffinityMatchnode affinity objectThe original Pod node affinity must contain a matching required and/or preferred term.
tolerationMatchtoleration arrayAt least one Pod toleration must exactly match one configured key, operator, value, and effect tuple.

DaemonSet-owned Pods are not mutated.

nodeAffinityMatch

nodeAffinityMatch uses the Kubernetes node affinity YAML shape, restricted to matchExpressions.

Required matching preserves Kubernetes term semantics:

  • nodeSelectorTerms are ORed.
  • Requirements inside one term are ANDed.
  • The selector term may be a subset of the Pod term.
  • A requirement must have the same key and operator.
  • Values are compared as a set.

Preferred matching requires the same weight and a matching preference term. If both required and preferred selectors are configured, both must match.

matchFields is not supported and is rejected by the API server.

mutationRemove

FieldTypeBehavior
nodeSelectorKeysstring arrayRemoves the listed keys from spec.nodeSelector.
tolerationKeysstring arrayRemoves tolerations whose key is listed.
nodeAffinityobjectRemoves node affinity requirements by key or removes the complete node affinity.

mutationRemove.nodeAffinity

Exactly one of these fields must be configured:

FieldTypeBehavior
keysstring arrayRemoves matching keys from required and preferred matchExpressions and from incoming Pod matchFields. Maximum 64 keys.
allbooleanWhen true, removes the complete incoming nodeAffinity.

If key removal empties a preferred term, that term is removed. If it empties a required OR term, the complete required selector is cleared so the webhook does not emit an empty term that Kubernetes treats as matching no nodes.

all: true affects only nodeAffinity; it preserves podAffinity and podAntiAffinity.

mutationAdd

FieldTypeBehavior
annotationsmap of string to stringAdds annotations and overwrites duplicate keys.
labelsmap of string to stringAdds labels and overwrites duplicate keys.
nodeSelectormap of string to stringAdds node selector entries and overwrites duplicate keys.
tolerationstoleration arrayAdds tolerations and deduplicates them by key, operator, value, and effect.
nodeAffinitynode affinity objectLogically combines required affinity and appends preferred affinity.

Adding required node affinity performs logical AND with the current required selector. Because each side may contain OR terms, the webhook creates the Cartesian combination of both term sets, then canonicalizes duplicate and redundant terms. Preferred terms are appended and deduplicated.

Adding a requirement does not implicitly replace another requirement that uses the same key. Use explicit remove-then-add when replacement is required.

Node affinity schema

Both nodeAffinityMatch and mutationAdd.nodeAffinity support these fields:

requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: example.com/node-pool operator: In values: - pool-a preferredDuringSchedulingIgnoredDuringExecution: - weight: 50 preference: matchExpressions: - key: topology.kubernetes.io/zone operator: In values: - us-east-1a

Requirement operators

Operatorvalues requirement
InOne or more values.
NotInOne or more values.
ExistsNo values.
DoesNotExistNo values.
GtExactly one integer value.
LtExactly one integer value.

Keys and values must use valid Kubernetes label syntax. Preferred weights must be between 1 and 100.

Reserved CloudPilot-managed affinity

The following keys are managed by CloudPilot AI and cannot be used in nodeAffinityMatch or mutationAdd.nodeAffinity, or removed through mutationRemove.nodeAffinity.keys:

  • karpenter.sh/capacity-type
  • eks.amazonaws.com/capacityType
  • cloud.google.com/gke-provisioning
  • node.kubernetes.io/instance-type

CloudPilot-managed affinity is added after customer PodMutation rules. It is therefore still present after mutationRemove.nodeAffinity.all: true.

Safety limits

The CRD and webhook bound affinity expansion during admission:

  • maximum 64 configured required terms;
  • maximum 64 configured preferred terms;
  • maximum 64 configured expressions in one term;
  • maximum 64 values in one expression;
  • maximum 64 required or preferred terms after multiple mutations are combined;
  • maximum 64 requirements in one combined required term;
  • maximum 4,096 intermediate required terms before canonicalization;
  • maximum 256 raw required terms, or 256 requirements in one raw term, on the incoming Pod.

If matching rules exceed a runtime combination limit, Pod admission is rejected with an error instead of emitting an oversized or semantically broadened affinity expression.

Complete example

apiVersion: agent.cloudpilot.ai/v1alpha1 kind: PodMutation metadata: name: migrate-legacy-pool spec: enable: true priority: 100 workloadSelector: namespaces: - production kinds: - Deployment - StatefulSet nodeAffinityMatch: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: example.com/node-pool operator: In values: - legacy mutationRemove: nodeAffinity: keys: - example.com/node-pool mutationAdd: annotations: example.com/migration: merged-pool nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: example.com/node-pool operator: In values: - merged

Wait for the webhook cache to refresh, then roll out the selected workloads so their replacement Pods pass through admission.

For a migration walkthrough, see Use PodMutation During a NodePool Merge.

Last updated on