Skip to content

feat: implement networkpolicy primitive#31

Merged
sourcehawk merged 31 commits into
mainfrom
feature/networkpolicy-primitive
Mar 25, 2026
Merged

feat: implement networkpolicy primitive#31
sourcehawk merged 31 commits into
mainfrom
feature/networkpolicy-primitive

Conversation

@sourcehawk
Copy link
Copy Markdown
Owner

@sourcehawk sourcehawk commented Mar 22, 2026

Implements the networkpolicy Kubernetes resource primitive following the pattern established by the existing ConfigMap and Deployment primitives.

Summary

  • Adds networkpolicy primitive package under pkg/primitives/networkpolicy/
  • Implements required lifecycle interfaces
  • Includes editors, mutator, flavors, and builder
  • Adds NetworkPolicySpecEditor in shared pkg/mutation/editors/ package
  • Updates shared docs/primitives.md with networkpolicy entry and adds docs/primitives/networkpolicy.md

Checklist

  • Compiles cleanly
  • Tests pass
  • Follows naming conventions in CONTEXT.md
  • Cross-cutting changes documented (shared editor, shared docs)

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new networkpolicy primitive to the operator-component-framework, providing a static resource wrapper + builder/mutator pipeline (with typed spec editor support), along with documentation and a runnable example demonstrating feature-gated composition of NetworkPolicy rules.

Changes:

  • Introduces pkg/primitives/networkpolicy with Resource, Builder, Mutator, and field-application flavors.
  • Adds a shared NetworkPolicySpecEditor under pkg/mutation/editors with unit tests.
  • Adds documentation (docs/primitives/networkpolicy.md) and a complete example (examples/networkpolicy-primitive/).

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
pkg/primitives/networkpolicy/resource.go Static resource wrapper + default field applicator for NetworkPolicy.
pkg/primitives/networkpolicy/builder.go Fluent builder for configuring mutations, flavors, and extractors.
pkg/primitives/networkpolicy/mutator.go Plan-and-apply mutator with feature boundaries + typed edit hooks.
pkg/primitives/networkpolicy/flavors.go NetworkPolicy-specific wrappers around generic “preserve current” flavors.
pkg/primitives/networkpolicy/builder_test.go Builder validation and option-wiring tests.
pkg/primitives/networkpolicy/mutator_test.go Mutator behavior/order/error-propagation tests.
pkg/primitives/networkpolicy/flavors_test.go Flavor behavior + integration tests via Resource.Mutate.
pkg/mutation/editors/networkpolicyspec.go New shared typed editor for NetworkPolicySpec.
pkg/mutation/editors/networkpolicyspec_test.go Unit tests for NetworkPolicySpecEditor.
docs/primitives/networkpolicy.md New primitive documentation (builder usage, mutations, ordering, flavors).
examples/networkpolicy-primitive/app/controller.go Example controller wiring the primitive into a component.
examples/networkpolicy-primitive/features/mutations.go Example feature mutations for ingress/egress + version label.
examples/networkpolicy-primitive/resources/networkpolicy.go Example resource factory assembling the primitive configuration.
examples/networkpolicy-primitive/main.go Runnable demo using a fake client and multiple reconciliation passes.
examples/networkpolicy-primitive/README.md Example overview and run instructions.

Comment thread docs/primitives/networkpolicy.md
Comment thread pkg/mutation/editors/networkpolicyspec.go
Comment thread pkg/primitives/networkpolicy/resource.go Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Comment thread pkg/primitives/networkpolicy/resource.go Outdated
Comment thread docs/primitives/networkpolicy.md Outdated
Comment thread docs/primitives/networkpolicy.md Outdated
@sourcehawk
Copy link
Copy Markdown
Owner Author

Claude Review Cycle 1 Complete

Addressed:

  • Fixed docs/primitives/networkpolicy.md line 44: Removed incorrect claim that DefaultFieldApplicator preserves ResourceVersion. Aligned wording with the configmap primitive docs and actual code behavior.

Intentionally ignored:

  • resource.go line 15 (preserve server-managed metadata in DefaultFieldApplicator): All primitives in this codebase (configmap, deployment, networkpolicy) use the identical *current = *desired.DeepCopy() pattern without preserving ResourceVersion/UID/etc. This is a deliberate codebase convention — server-managed metadata is handled at the framework/controller-runtime layer, not in the field applicator. Changing only networkpolicy would break consistency.
  • docs/primitives/networkpolicy.md line 13 (double pipes in markdown table): Inspected the table — it uses single | separators throughout. No double pipes exist; this comment appears to be a false positive.

Copilot AI review requested due to automatic review settings March 22, 2026 19:52
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 4 comments.

Comment thread docs/primitives/networkpolicy.md
Comment thread examples/networkpolicy-primitive/features/mutations.go
Comment thread pkg/primitives/networkpolicy/mutator.go Outdated
Comment thread docs/primitives/networkpolicy.md Outdated
Comment thread docs/primitives/networkpolicy.md Outdated
Copilot AI review requested due to automatic review settings March 22, 2026 21:59
@sourcehawk
Copy link
Copy Markdown
Owner Author

approved

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 4 comments.

Comment thread docs/primitives/networkpolicy.md Outdated
Comment thread pkg/primitives/networkpolicy/resource.go Outdated
Comment thread pkg/mutation/editors/networkpolicyspec.go
Comment thread pkg/primitives/networkpolicy/resource.go
Copilot AI review requested due to automatic review settings March 23, 2026 03:00
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Comment thread pkg/primitives/networkpolicy/resource.go Outdated
Comment thread pkg/mutation/editors/networkpolicyspec.go Outdated
@sourcehawk
Copy link
Copy Markdown
Owner Author

Claude Review Cycle 1 Complete

Addressed:

  • resource.go:29 — Changed "workload or integration primitive" to "workload or task primitive" to align with configmap primitive wording.
  • networkpolicyspec.go:78 — Replaced "EgressRules" with ".Egress field (egress rules)" and prefixed type with networkingv1.PolicyTypeEgress for clarity.

Intentionally ignored:
None

<!-- claude-review-cycle -->

Copilot AI review requested due to automatic review settings March 23, 2026 16:08
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.

Comment thread pkg/primitives/networkpolicy/mutator.go Outdated
Comment thread docs/primitives/networkpolicy.md Outdated
Comment thread docs/primitives/networkpolicy.md
Copilot AI review requested due to automatic review settings March 23, 2026 16:58
@sourcehawk
Copy link
Copy Markdown
Owner Author

Claude Review Cycle 1 Complete

Addressed:

  • Mutator GoDoc (mutator.go:25): Added feature boundary documentation and editors.ObjectMutator interface mention, aligned with configmap/deployment mutator conventions.
  • Primitives index (docs/primitives.md): Added networkpolicy to the Built-in Primitives table and NetworkPolicySpecEditor to the Mutation Editors table for discoverability.

Intentionally ignored:

  • Markdown table formatting (networkpolicy.md:13): The table already uses single leading pipes and renders correctly. The suggested change is cosmetic whitespace only with no rendering difference.

<!-- claude-review-cycle -->

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Comment thread docs/primitives/networkpolicy.md Outdated
Comment thread docs/primitives.md Outdated
sourcehawk and others added 4 commits March 23, 2026 20:16
Implements the NetworkPolicy static primitive following the configmap
reference pattern. Includes NetworkPolicySpecEditor tests, typed mutator
with plan-and-apply pattern, and PreserveCurrentLabels/Annotations flavors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Demonstrates HTTP ingress, boolean-gated metrics ingress, DNS egress,
version labels, and PreserveCurrentLabels flavor. The DefaultFieldApplicator
preserves ResourceVersion across reconcile cycles.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The NetworkPolicySpecEditor was referenced by tests and the networkpolicy
primitive but was never committed, causing build failures in CI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Comment on lines +48 to +50
// 4. Preserve labels added by external controllers.
builder.WithFieldApplicationFlavor(networkpolicy.PreserveCurrentLabels)

Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Builder currently has no WithFieldApplicationFlavor method, and the networkpolicy package doesn't define PreserveCurrentLabels. As written, this example won't compile with go build ./examples/... (Makefile build-examples). Either remove this call or add the missing builder API and flavor implementation in pkg/primitives/networkpolicy (matching whatever mechanism other primitives use).

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +10
- **Metadata Mutations**: Setting version labels on the NetworkPolicy via `EditObjectMetadata`.
- **Field Flavors**: Preserving labels managed by external controllers using `PreserveCurrentLabels`.
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This README claims the example uses PreserveCurrentLabels, but there is no such symbol in pkg/primitives/networkpolicy, and the example code currently references a non-existent WithFieldApplicationFlavor API. Please update the README to match the actual, compilable example (or implement the missing flavor API/symbols).

Suggested change
- **Metadata Mutations**: Setting version labels on the NetworkPolicy via `EditObjectMetadata`.
- **Field Flavors**: Preserving labels managed by external controllers using `PreserveCurrentLabels`.
- **Metadata Mutations**: Setting version labels on the NetworkPolicy via metadata editors.
- **Label Coexistence**: Demonstrating how label updates from this component can coexist with labels managed by other controllers.

Copilot uses AI. Check for mistakes.
sourcehawk and others added 2 commits March 25, 2026 15:46
The networkpolicy example still referenced WithFieldApplicationFlavor
and PreserveCurrentLabels which were removed in the prior refactor.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The README still referenced PreserveCurrentLabels and field flavors
which were removed in a prior refactor. Updated to match the current
example code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 25, 2026 16:02
@sourcehawk
Copy link
Copy Markdown
Owner Author

Claude Review Cycle 1 Complete

Addressed:

  • README.md line 10: Updated bullet points to remove references to PreserveCurrentLabels and WithFieldApplicationFlavor API, replacing with accurate descriptions of the current example behavior.

Intentionally ignored:

  • networkpolicy.go line 50: The code no longer references WithFieldApplicationFlavor or PreserveCurrentLabels — this was already fixed in commit 73b4abf ("fix: remove references to deleted field applicators in example"). The review comment appears stale.

<!-- claude-review-cycle -->

@sourcehawk sourcehawk requested review from Copilot and removed request for Copilot March 25, 2026 16:03
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Comment on lines +61 to +66
func (m *Mutator) EditObjectMetadata(edit func(*editors.ObjectMetaEditor) error) {
if edit == nil {
return
}
m.active.metadataEdits = append(m.active.metadataEdits, edit)
}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EditObjectMetadata / EditNetworkPolicySpec dereference m.active without ensuring a feature plan has been started, which will panic if BeginFeature() hasn’t been called (e.g., if Mutator is ever used outside the generic framework wiring). Consider making these methods resilient by auto-starting a plan when m.active == nil (or returning a clear error—though that would require changing the API). Auto-starting keeps the current public method signatures and avoids a runtime nil-pointer dereference.

Copilot uses AI. Check for mistakes.
Comment on lines +77 to +82
func (m *Mutator) EditNetworkPolicySpec(edit func(*editors.NetworkPolicySpecEditor) error) {
if edit == nil {
return
}
m.active.specEdits = append(m.active.specEdits, edit)
}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EditObjectMetadata / EditNetworkPolicySpec dereference m.active without ensuring a feature plan has been started, which will panic if BeginFeature() hasn’t been called (e.g., if Mutator is ever used outside the generic framework wiring). Consider making these methods resilient by auto-starting a plan when m.active == nil (or returning a clear error—though that would require changing the API). Auto-starting keeps the current public method signatures and avoids a runtime nil-pointer dereference.

Copilot uses AI. Check for mistakes.
Comment thread docs/primitives.md Outdated
Comment on lines +117 to +124
| Editor | Scope |
| ------------------------- | ----------------------------------------------------------------------- |
| `ContainerEditor` | Environment variables, arguments, resource limits, ports |
| `PodSpecEditor` | Volumes, tolerations, node selectors, service account, security context |
| `DeploymentSpecEditor` | Replicas, update strategy, label selectors |
| `ConfigMapDataEditor` | `.data` entries — set, remove, deep-merge YAML patches, raw access |
| `NetworkPolicySpecEditor` | Pod selector, ingress/egress rules, policy types |
| `ObjectMetaEditor` | Labels and annotations on any Kubernetes object |
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tables use || at the start of each row, which GitHub-flavored Markdown renders as an extra empty leading column (because the first cell is blank). Use standard table syntax with a single leading | (or no leading pipe) for correct rendering.

Copilot uses AI. Check for mistakes.
sourcehawk and others added 2 commits March 25, 2026 16:10
Add ensureActive() helper that auto-starts a feature plan when
EditObjectMetadata or EditNetworkPolicySpec is called without a
prior BeginFeature() call, preventing a nil-pointer dereference.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sourcehawk
Copy link
Copy Markdown
Owner Author

Claude Review Cycle 2 Complete

Addressed:

  • mutator.go L66, L82: Added ensureActive() helper that auto-starts a feature plan when EditObjectMetadata or EditNetworkPolicySpec is called without a prior BeginFeature() call, preventing nil-pointer dereference.

Intentionally ignored:

  • docs/primitives.md L124: The comment claims tables use || syntax, but inspection shows all tables already use correct single-| GFM syntax. This appears to reference an older version of the file that has since been fixed.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Comment on lines +39 to +48
// NewMutator creates a new Mutator for the given NetworkPolicy.
//
// It is typically used within a Feature's Mutation logic to express desired
// changes to the NetworkPolicy. BeginFeature must be called before registering
// any mutations.
func NewMutator(np *networkingv1.NetworkPolicy) *Mutator {
return &Mutator{
np: np,
}
}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc comment says BeginFeature must be called before registering any mutations, but the implementation auto-creates a plan when edits are registered (via ensureActive). Please either (a) update the comment to reflect the implicit feature plan behavior, or (b) remove the implicit behavior and enforce explicit BeginFeature (e.g., by making edit registration fail fast).

Copilot uses AI. Check for mistakes.
Comment on lines +60 to +67
func (b *Builder) WithDataExtractor(extractor func(networkingv1.NetworkPolicy) error) *Builder {
if extractor != nil {
b.base.WithDataExtractor(func(np *networkingv1.NetworkPolicy) error {
return extractor(*np)
})
}
return b
}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing *np by value does not fully protect the underlying object from mutation because NetworkPolicy contains reference types (maps/slices) that remain shared across a shallow struct copy. To keep the 'extractor receives a copy' contract robust, pass a deep-copied value (e.g., copy := *np.DeepCopy()) to the extractor.

Copilot uses AI. Check for mistakes.
res, err := NewBuilder(desired).
WithMutation(Mutation{
Name: "test-mutation",
Feature: feature.NewResourceFeature("v1", nil).When(true),
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests are using feature.NewResourceFeature(\"v1\", nil) even though the examples elsewhere pass semantic versions (e.g., 1.2.3). If NewResourceFeature expects semver, \"v1\" is likely invalid and could make the test brittle if feature evaluation becomes stricter. Prefer a semver-like value (e.g., \"1.0.0\") or omit the feature gate entirely for 'always on' behavior in this test.

Suggested change
Feature: feature.NewResourceFeature("v1", nil).When(true),
Feature: feature.NewResourceFeature("1.0.0", nil).When(true),

Copilot uses AI. Check for mistakes.
The doc comment incorrectly stated that BeginFeature must be called
before registering mutations, but ensureActive() auto-creates a plan.
Updated to accurately describe the implicit behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sourcehawk
Copy link
Copy Markdown
Owner Author

Claude Review Cycle 1 Complete

Addressed:

  • mutator.go:48 — Updated NewMutator doc comment to accurately describe the implicit feature plan behavior (ensureActive auto-creates a plan when edits are registered without an explicit BeginFeature call).

Intentionally ignored:

  • builder.go:67 (DeepCopy in WithDataExtractor) — The deep copy already happens upstream in BaseResource.ExtractData(), which calls DeepCopyObject() before passing the object to extractors. The *np dereference in the wrapper just converts the pointer to a value on the already-deep-copied object. Adding another DeepCopy here would be redundant.
  • resource_test.go:72 (semver version strings) — NewResourceFeature performs no validation on the version string, and with nil constraints + .When(true/false) the version value is irrelevant. The deployment primitive tests use the same "v1" pattern; changing only networkpolicy tests would create inconsistency across the codebase.

<!-- claude-review-cycle -->

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Comment on lines +3 to +6
The `networkpolicy` primitive is the framework's built-in static abstraction for managing Kubernetes `NetworkPolicy`
resources. It integrates with the component lifecycle and provides a structured mutation API for managing pod selectors,
ingress rules, egress rules, and policy types.

Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description mentions that the networkpolicy primitive includes “flavors”, but this primitive package/docs don’t define any flavor helpers or builder options related to field-application flavors. Please either add the intended flavor support or update the PR description/docs to avoid advertising functionality that isn’t present.

Copilot uses AI. Check for mistakes.
})
require.NoError(t, m.Apply())
require.Len(t, np.Spec.Ingress, 1)
assert.Equal(t, &port8080, np.Spec.Ingress[0].Ports[0].Port)
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this test, the assertion compares pointer identity of the Port field (&port8080). That makes the test brittle to harmless implementation changes (e.g., if the mutator/editor later deep-copies ports). Prefer asserting on the port value (e.g., IntVal/StrVal) instead of pointer equality.

Suggested change
assert.Equal(t, &port8080, np.Spec.Ingress[0].Ports[0].Port)
assert.Equal(t, port8080.IntVal, np.Spec.Ingress[0].Ports[0].Port.IntVal)

Copilot uses AI. Check for mistakes.
… test

Compare port IntVal instead of pointer identity to make the test
resilient to implementation changes like deep-copying.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sourcehawk
Copy link
Copy Markdown
Owner Author

Claude Review Cycle 1 Complete

Addressed:

  • mutator_test.go line 151: Changed pointer identity assertion (&port8080) to value assertion (port8080.IntVal) to make the test resilient to deep-copy implementation changes.

Intentionally ignored:

  • docs/primitives/networkpolicy.md line 6 (flavors comment): The docs do not mention or advertise flavors — the word "flavor" does not appear anywhere in the document. Prior commits (0968d4f, 73b4abf) already removed flavor/applicator references from the project. The comment appears to be based on the PR description rather than the actual doc content; no doc change is needed.

<!-- claude-review-cycle -->

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants