feat: implement pod primitive#28
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new pod Kubernetes resource primitive to the operator-component-framework, following the existing primitive patterns (builder/resource wrappers, field applicator + flavors, mutation planning via a typed mutator, default status/suspension handlers), along with docs and an example demonstrating usage.
Changes:
- Introduces
pkg/primitives/pod/with builder, resource wrapper, mutator, flavors, and default lifecycle handlers. - Adds unit tests for the pod mutator/handlers/flavors/builder.
- Adds
docs/primitives/pod.mdand a runnableexamples/pod-primitive/showcasing feature mutations and flavors.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/primitives/pod/resource.go | Pod workload resource wrapper + pod-specific default field applicator. |
| pkg/primitives/pod/builder.go | Fluent builder wiring defaults, mutations, flavors, handlers, extractors. |
| pkg/primitives/pod/mutator.go | Plan-and-apply mutator for pod metadata/spec/containers (incl. init containers). |
| pkg/primitives/pod/handlers.go | Default converge/grace/suspend handlers for Pod lifecycle integration. |
| pkg/primitives/pod/flavors.go | Pod-specific field application flavors (labels/annotations preservation). |
| pkg/primitives/pod/builder_test.go | Builder validation and option wiring tests. |
| pkg/primitives/pod/mutator_test.go | Mutator behavior tests (ordering, presence, selector snapshots, nil-safety). |
| pkg/primitives/pod/handlers_test.go | Default handler behavior tests. |
| pkg/primitives/pod/flavors_test.go | Flavor ordering and behavior tests. |
| docs/primitives/pod.md | User-facing documentation for the new pod primitive. |
| examples/pod-primitive/** | End-to-end example using the pod primitive with feature mutations and flavors. |
Claude Review Cycle 1 CompleteAddressed:
Intentionally ignored:
<!-- claude-review-cycle --> |
Claude Review Cycle 1 CompleteAddressed:
Intentionally ignored: <!-- claude-review-cycle --> |
Claude Review Cycle 1 CompleteAddressed:
Intentionally ignored:
|
| ## Capabilities | ||
|
|
||
| | Capability | Detail | | ||
| |-----------------------|-------------------------------------------------------------------------------------------------| | ||
| | **Health tracking** | Monitors pod phase and container statuses; reports `Healthy`, `Creating`, `Updating`, or `Failing` | | ||
| | **Graceful rollouts** | Detects degraded or down states via grace status handler | | ||
| | **Suspension** | Deletes the pod (pods cannot be paused); reports `Suspended` | | ||
| | **Mutation pipeline** | Typed editors for metadata, pod spec, and containers | | ||
| | **Flavors** | Preserves externally-managed fields (labels, annotations) | | ||
|
|
There was a problem hiding this comment.
The capabilities table uses || at the start of each row, which renders as an extra empty column and breaks standard Markdown table formatting. Switch to single leading | for the header/separator/rows so it renders correctly on GitHub.
…and flavors Implements the Pod primitive for managing Kubernetes Pod objects within the operator-component-framework. The Pod follows the Workload lifecycle pattern with pod-specific behavior: immutable spec preservation via DefaultFieldApplicator, deletion-based suspension, and direct PodSpec/Container mutation (no pod template indirection). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tions Demonstrates pod lifecycle management including version-gated mutations, tracing sidecar injection, field preservation flavors, and deletion-based suspension. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Handle PodFailed and PodSucceeded phases explicitly in DefaultConvergingStatusHandler instead of falling through to "pending" - Check container readiness in DefaultGraceStatusHandler so healthy running pods are not incorrectly reported as degraded - Clone label/annotation maps in DefaultFieldApplicator to prevent shared mutable references between desired and current pods - Fix "custom customFieldApplicator" doc comment typo in Resource.Mutate - Clarify Mutation type comment to document nil-feature semantics - Fix findEnv test helper to return pointer to slice element, not copy - Add Kubernetes immutability note for container presence operations in pod.md documentation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Exercises the Resource wrapper integration including: identity format, Object deep-copy semantics, Mutate pipeline with mutations and feature ordering, disabled feature skipping, default and custom handler wiring for converging status, grace status, suspension status, delete-on-suspend, suspend mutation, data extraction, custom field applicator, and default field applicator metadata-only propagation for existing pods. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix DefaultConvergingStatusHandler reporting Healthy when ContainerStatuses is empty; now returns Creating to match DefaultGraceStatusHandler behavior - Fix misleading "Pod is being recreated" reason to "Pod is being updated" - Rewrite example mutations to only mutate metadata, avoiding immutable Pod spec fields (containers, env, args) - Fix Pod immutability docs: env/args/resources are immutable, not mutable - Fix WithCustomFieldApplicator doc example to deep-copy maps - Assert Build() errors in flavors_test.go instead of ignoring them Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix inaccurate comments and documentation: - README: describe actual label mutations instead of sidecars/env vars - mutations.go: clarify Kubernetes allows some in-place spec updates (e.g. images) - handlers.go: fix GoDoc to say "updated" instead of "recreated" - resources/pod.go: remove misleading "overwritten by VersionFeature" comment Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tests Address Copilot review feedback: convert all raw testing.T assertion calls to testify helpers for consistent test style and better failure output across the pod primitive test suite. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Initialize plans slice directly and set active pointer instead of calling beginFeature(), preventing an empty feature plan when the generic mutator helper also calls beginFeature(). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| func (m *Mutator) EditObjectMetadata(edit func(*editors.ObjectMetaEditor) error) { | ||
| if edit == nil { | ||
| return | ||
| } | ||
| m.active.podMetadataEdits = append(m.active.podMetadataEdits, edit) | ||
| } |
There was a problem hiding this comment.
m.active is dereferenced without ensuring BeginFeature() has been called, which will panic (nil pointer deref) if a caller uses NewMutator directly and forgets BeginFeature. Consider making registration methods robust by ensuring an active plan exists (e.g., create an implicit plan when m.active == nil, or fail fast with a clear panic/error via a dedicated internal guard). This avoids controller crashes from a simple usage mistake.
| type containerPresenceOp struct { | ||
| name string | ||
| container *corev1.Container // nil for remove | ||
| } |
There was a problem hiding this comment.
containerPresenceOp stores a pointer to a corev1.Container, but presence ops ultimately copy the container value into the slice. Storing the container by value (plus an explicit remove bool) makes intent clearer, avoids pointer-escape/heap allocation for &container in EnsureContainer/EnsureInitContainer, and reduces the chance of subtle bugs if this struct evolves.
| return concepts.GraceStatusWithReason{ | ||
| Status: concepts.GraceStatusDown, | ||
| Reason: "Pod is not running", |
There was a problem hiding this comment.
For non-running phases, the reason is always "Pod is not running", which loses important debugging context (e.g., Pending vs Failed vs Unknown). Consider including the phase (and optionally the message/reason from pod conditions when available) in the returned Reason, while keeping GraceStatusDown unchanged.
| return concepts.GraceStatusWithReason{ | |
| Status: concepts.GraceStatusDown, | |
| Reason: "Pod is not running", | |
| // Provide more detailed context for non-running phases while keeping the status as GraceStatusDown. | |
| reason := "Pod phase is " + string(pod.Status.Phase) | |
| // Optionally include the first non-true condition's reason and message, if available. | |
| for _, cond := range pod.Status.Conditions { | |
| if cond.Status == corev1.ConditionTrue { | |
| continue | |
| } | |
| if cond.Reason != "" { | |
| reason += " (" + cond.Reason | |
| if cond.Message != "" { | |
| reason += ": " + cond.Message | |
| } | |
| reason += ")" | |
| } else if cond.Message != "" { | |
| reason += " (" + cond.Message + ")" | |
| } | |
| // Only use the first relevant condition for brevity. | |
| break | |
| } | |
| return concepts.GraceStatusWithReason{ | |
| Status: concepts.GraceStatusDown, | |
| Reason: reason, |
- Add requireActive() guard to mutator registration methods to fail fast with a clear message instead of nil pointer dereference - Change containerPresenceOp to store container by value with explicit remove bool instead of using nil pointer as sentinel - Include pod phase in grace status reason for better debugging context Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude Review Cycle 1 CompleteAddressed:
Intentionally ignored:
<!-- claude-review-cycle --> |
| // It uses a "plan-and-apply" pattern: mutations are recorded first, and then | ||
| // applied to the Pod in a single controlled pass when Apply() is called. | ||
| // | ||
| // This approach ensures that mutations are applied consistently and minimises |
There was a problem hiding this comment.
Spelling consistency: elsewhere in the repo (e.g. deployment mutator comment) this sentence uses American English (“minimizes”). Consider changing “minimises” to “minimizes” for consistency in GoDoc/comments.
| // This approach ensures that mutations are applied consistently and minimises | |
| // This approach ensures that mutations are applied consistently and minimizes |
…ments Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude Review Cycle 1 CompleteAddressed:
Intentionally ignored: <!-- claude-review-cycle --> |
Implements the
podKubernetes resource primitive following the pattern established by the existingConfigMapandDeploymentprimitives.Summary
podprimitive package underpkg/primitives/pod/Checklist