Skip to content

Releases: sourcehawk/operator-component-framework

v0.9.1

24 May 17:38
d42ea51

Choose a tag to compare

This release fixes a regression in v0.9.0 where the read-only data-extraction fix shipped in that version was inert for every primitive in pkg/primitives.

Fixed

  • Primitive resource wrappers now forward RecordObservation (#118): The v0.9.0 fix for #115 added concepts.ObservationRecorder and dispatched to it via a runtime type assertion against the registered resource. Every primitive's Resource wrapper holds its underlying generic resource behind an unexported named field rather than embedding it, so methods are not promoted onto the wrapper. The type assertion therefore failed for every primitive, the fetched object was never recorded, and read-only data extractors continued to see the inert base used to build the resource. The original #115 scenario (Secret rotation hash equal to sha256("{}") regardless of cluster contents) reproduced against v0.9.0 for every primitive in pkg/primitives. Each primitive (21 typed plus 4 unstructured variants) now forwards RecordObservation explicitly, mirroring how ExtractData and GuardStatus are forwarded.

Added

  • Per-primitive compile-time interface assertions: Each primitive's new observation_test.go declares var _ concepts.ObservationRecorder = (*Resource)(nil). A primitive that forgets to forward the method fails to build instead of regressing silently. The same pattern can be extended to lock in any future framework-side type-assertion dispatch.
  • End-to-end regression test for secret (pkg/primitives/secret/observation_test.go): Builds a real *secret.Resource via the public builder with WithDataExtractor, fetches a populated cluster Secret through a fake client, calls RecordObservation and ExtractData, and asserts the extractor observes the live data. Catches both forwarder-missing and forwarder-broken regressions for the canonical user-reported scenario.

v0.9.0

24 May 16:17
94185a2

Choose a tag to compare

This release fixes a bug in read-only resource data extraction and adds an opt-in for treating absent read-only resources as guard-blocked instead of erroring.

Fixed

  • Read-only resource extractors now observe the fetched cluster object (#115): readResource fetched the cluster object into a deep copy that was then discarded, and BaseResource.ExtractData re-deep-copied DesiredObject, so extractors on read-only resources only ever saw the inert base used to build the resource. The framework now records the fetched object back onto the resource before extraction runs, so closures capturing values from read-only Secrets or ConfigMaps see real cluster state.

Added

  • concepts.ObservationRecorder interface (#115): New interface with RecordObservation(observed client.Object) error. BaseResource implements it automatically; primitives in pkg/primitives/ pick this up with no changes. Custom resource wrappers built on the generic layer should forward RecordObservation to their base when read-only data extraction is needed.
  • ResourceOptions.BlockOnAbsence and ResourceOptionsBuilder.BlockOnAbsence() (#114): Opt-in for read-only resources. When set, a NotFound from the cluster is recorded as a blocked status with reason waiting for <resource> and short-circuits the remaining resources for the reconcile, instead of returning an error and triggering controller-runtime's exponential backoff. Intended for consumers that have a watch on the referenced resource type so that re-enqueue happens through the watch rather than the backoff loop. The flag has no effect on managed resources.

Documentation

  • docs/component.md: New row in the ResourceOptions table and a new entry in the ResourceOptionsBuilder methods table for BlockOnAbsence. The reconciliation lifecycle now documents the RecordObservation step that runs after Get and before data extraction for read-only resources.
  • docs/custom-resource.md: The typical-methods table and example resource wrapper list RecordObservation alongside ExtractData, with a note explaining when forwarding is required.
  • WithDataExtractor GoDoc clarified for managed versus read-only flows.

v0.8.0

13 May 11:06
7e4f93d

Choose a tag to compare

What's Changed

Full Changelog: v0.7.1...v0.8.0

v0.7.1

01 Apr 01:47
8d45db4

Choose a tag to compare

This release replaces the repetitive per-primitive examples with focused, high-value examples that demonstrate real operator patterns. It also adds mutation ordering guidance, backward compatibility naming conventions, and exclusion-based container selectors.

Changed

  • Examples replaced with pattern-focused examples: Removed 22 per-primitive boilerplate examples and replaced them with 5 examples that each demonstrate a specific operator pattern: mutations and gating, data extraction and guards, component prerequisites, custom resources, and grace inconsistency handling. Each example includes golden file tests.
  • Backward compat mutation naming convention: Legacy mutations are now named BackwardCompat<version><what> (e.g., BackwardCompatV1Container) so the pattern is immediately recognizable when scanning a builder chain.

Added

  • ContainerNotNamed and ContainersNotNamed selectors: New container selectors that match all containers except those with the given name(s). Useful when a mutation should apply to everything except a known sidecar or infrastructure container.
  • EnableDebugLogging field on ExampleAppSpec: Shared example owner spec now includes a debug logging toggle, used by the mutations-and-gating example to demonstrate mutation ordering with container name dependencies.
  • DebugLoggingMutation example: A new boolean-gated mutation that targets a specific container by name, demonstrating how registration order interacts with backward compat renames.
  • test-examples Makefile target: Example tests (golden files, mutation unit tests) now run as part of make all via a dedicated test-examples target.

Documentation

  • Mutation ordering and container name dependencies: New guidelines section covering how to handle mutations that depend on container names that vary across versions, with rules for broad selectors vs. registration ordering.
  • Revert mutations vs. forward mutations: Expanded reasoning in the "Represent Desired State" guideline explaining why baseline-as-latest with revert mutations is easier to maintain than baseline-as-original with forward mutations.
  • Backward compat naming and multi-version ordering: Guidance on the BackwardCompat naming convention, chained revert ordering (newest first), and the alternative non-overlapping gate approach with its tradeoffs.
  • Tracing sidecar GoDoc fix: Corrected comment that incorrectly said JAEGER_AGENT_HOST is set on "the application container" when it applies to all containers.

v0.7.0

31 Mar 20:04
122b7b1

Choose a tag to compare

This release fixes grace status handling across all primitives, adds per-resource configuration for grace inconsistency warnings, and improves documentation.

Fixed

  • Grace status handlers now return Healthy when appropriate: Deployment, StatefulSet, ReplicaSet, and DaemonSet grace handlers previously never returned GraceStatusHealthy, always reporting Degraded or Down even when all replicas were ready. They now compare ready replicas against the desired count and return Healthy when they match.
  • CronJob operational handler no longer reports false Pending: The default handler now always returns Operational, since a CronJob is a passive scheduler that is functioning correctly once it exists. Previously it reported Pending until the first execution, which could conflict with the grace handler.
  • Grace/convergence consistency enforced: Grace handlers use == (not >=) when comparing replica counts against desired, preventing grace from returning Healthy for states where convergence reports non-healthy (e.g., during scale-down).

Added

  • SuppressGraceInconsistencyWarning resource option: A new field on ResourceOptions (and corresponding ResourceOptionsBuilder method) allows suppressing the per-resource warning log emitted when a grace handler returns Healthy while the convergence handler returns non-healthy. Use this when the inconsistency is intentional due to a custom handler override.
  • Per-resource grace inconsistency logging: The component now logs each inconsistent resource individually with its identity and convergence reason, rather than a single aggregate warning.
  • Grace/convergence consistency documentation: The custom resource implementation guide (docs/custom-resource.md) now documents the consistency invariant between convergence and grace handlers, with illustrative tables showing correct and incorrect behavior.
  • Guidelines: stable guard values: New guidance in docs/guidelines.md on preferring stable extracted values for guard conditions to avoid unintentional re-blocking after resource creation.

Documentation

v0.6.0

30 Mar 20:25
4a7ee00

Choose a tag to compare

This release adds component-level prerequisites (initialization barriers) and a golden file testing utility for snapshot-testing primitive output.

Added

  • Component prerequisites: A new Prerequisite interface and WithPrerequisite builder method allow components to declare initialization barriers that must be satisfied before any resources are reconciled. Includes DependsOn for expressing inter-component ordering via status conditions.
  • PrerequisiteStatus and PrerequisiteResult: Status types (Met, NotMet) and result struct for prerequisite check outcomes.
  • PrerequisiteNotMet condition reason: Components blocked by an unsatisfied prerequisite report this condition reason with the prerequisite's explanation.
  • golden.AssertYAML and golden.CompareYAML: Generic snapshot testing helpers that serialize a primitive's PreviewObject output to YAML and compare against a golden file. Supports -update flag integration and optional WithScheme for resolving TypeMeta.
  • PreviewObject on all primitives: Every built-in primitive (21 typed + 4 unstructured) now exposes PreviewObject() for rendering post-mutation state without a live cluster.
  • Guidelines documentation (docs/guidelines.md): Covers testing strategies, version-aware mutation patterns, and golden file workflow.

Changed

  • Existing e2e primitive tests refactored to use shared assertion helpers from e2e/framework.

v0.5.1

30 Mar 11:43
53c6da3

Choose a tag to compare

This release adds WrapGuard and WrapExtractor generic helpers that eliminate repetitive pointer-to-value callback wrapping across all primitive builders.

Added

  • generic.WrapGuard: Converts a value-receiver guard callback into a pointer-receiver callback for the generic builder layer. Handles nil passthrough.
  • generic.WrapExtractor: Converts a value-receiver data extractor callback into a pointer-receiver callback for the generic builder layer. Handles nil passthrough.

Changed

  • All 25 primitive builders (21 typed + 4 unstructured) now use WrapGuard and WrapExtractor instead of inline nil-check and closure wrapping in their WithGuard and WithDataExtractor methods.

Docs

  • Added Guardable to the custom resource and unstructured primitive documentation.
  • Rewrote the custom resource mutator example to properly demonstrate NextFeature and per-feature plan scoping.
  • Updated the custom resource builder example to use value-type callbacks with WrapGuard and WrapExtractor.

v0.5.0

30 Mar 10:58
e869e18

Choose a tag to compare

This release renames the WithTruth method on ResourceOptionsBuilder to When, aligning its API with VersionGate.When() for consistency across the framework.

Changed

  • ResourceOptionsBuilder: Renamed WithTruth(bool) to When(bool). The semantics are unchanged: all conditions must be true for the resource to be created, evaluated with AND logic alongside any configured feature gate.
  • Internal field truths renamed to requiredTruths to match VersionGate internals.

Dependencies

  • Updated k8s.io/utils digest to 28399d8.

v0.4.0

30 Mar 02:37
bbb3d03

Choose a tag to compare

This release introduces guards, a precondition system for controlling resource reconciliation order within a component.

Added

  • Guards for gating resource application on preconditions. Each resource primitive can declare a guard via WithGuard() on its builder. When a guard evaluates to Blocked, the resource is skipped and all resources registered after it in the component are also skipped. The blocked reason is surfaced in the component's status condition. Guards are not evaluated during suspension, so a component can always be fully deactivated regardless of guard state.
    • New Guardable interface and GuardStatus type in pkg/component/concepts/
    • WithGuard() builder method available on all primitive categories (static, workload, task, integration)
    • Guard-blocked resources always participate in status aggregation regardless of participation mode
  • Ordered resource reconciliation. Resources are now reconciled sequentially in registration order, which is required for guards to work correctly and provides deterministic reconciliation behavior.

Changed

  • CI: Updated actions/checkout to v6.

v0.3.0

29 Mar 21:31
5ff5aeb

Choose a tag to compare

This release builds on the v0.2.0 initial release with naming improvements to the feature system and a new builder for gating resource lifecycle at the component level.

Breaking changes

The feature package has been renamed for clarity and consistency with Go naming conventions:

Before After
feature.MutationFeature feature.Gate
feature.ResourceFeature feature.VersionGate
feature.NewResourceFeature(...) feature.NewVersionGate(...)

Migration: These are direct renames with no behavioral changes. A find-and-replace is sufficient.

Added

  • ResourceOptionsBuilder for declaratively gating resource lifecycle based on feature state. When a gate is disabled, the resource is deleted from the cluster; when enabled, it is created normally. Supports composing gates with boolean conditions via WithTruth(), marking resources as Auxiliary() or ReadOnly(), and a ResourceOptionsFor() convenience function for the common single-gate case.
  • Compatibility tests and documentation (docs/compatibility.md) covering controller-runtime v0.22+ support.