Releases: sourcehawk/operator-component-framework
v0.9.1
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 addedconcepts.ObservationRecorderand dispatched to it via a runtime type assertion against the registered resource. Every primitive'sResourcewrapper 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 tosha256("{}")regardless of cluster contents) reproduced against v0.9.0 for every primitive inpkg/primitives. Each primitive (21 typed plus 4 unstructured variants) now forwardsRecordObservationexplicitly, mirroring howExtractDataandGuardStatusare forwarded.
Added
- Per-primitive compile-time interface assertions: Each primitive's new
observation_test.godeclaresvar _ 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.Resourcevia the public builder withWithDataExtractor, fetches a populated cluster Secret through a fake client, callsRecordObservationandExtractData, 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
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):
readResourcefetched the cluster object into a deep copy that was then discarded, andBaseResource.ExtractDatare-deep-copiedDesiredObject, 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.ObservationRecorderinterface (#115): New interface withRecordObservation(observed client.Object) error.BaseResourceimplements it automatically; primitives inpkg/primitives/pick this up with no changes. Custom resource wrappers built on the generic layer should forwardRecordObservationto their base when read-only data extraction is needed.ResourceOptions.BlockOnAbsenceandResourceOptionsBuilder.BlockOnAbsence()(#114): Opt-in for read-only resources. When set, aNotFoundfrom the cluster is recorded as a blocked status with reasonwaiting 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 theResourceOptionstable and a new entry in theResourceOptionsBuildermethods table forBlockOnAbsence. The reconciliation lifecycle now documents theRecordObservationstep that runs afterGetand before data extraction for read-only resources.docs/custom-resource.md: The typical-methods table and example resource wrapper listRecordObservationalongsideExtractData, with a note explaining when forwarding is required.WithDataExtractorGoDoc clarified for managed versus read-only flows.
v0.8.0
What's Changed
- align guidelines with the framework principles by @sourcehawk in #104
- Add logo to README for visual enhancement by @sourcehawk in #105
- improve opening of the README by @sourcehawk in #106
- add note about the framework's intentions by @sourcehawk in #107
- remove duplication by @sourcehawk in #108
- add mermaid diagram by @sourcehawk in #109
- update owner status outside of component reconciliation by @sourcehawk in #110
Full Changelog: v0.7.1...v0.8.0
v0.7.1
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
ContainerNotNamedandContainersNotNamedselectors: 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.EnableDebugLoggingfield onExampleAppSpec: 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.DebugLoggingMutationexample: A new boolean-gated mutation that targets a specific container by name, demonstrating how registration order interacts with backward compat renames.test-examplesMakefile target: Example tests (golden files, mutation unit tests) now run as part ofmake allvia a dedicatedtest-examplestarget.
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
BackwardCompatnaming 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
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
SuppressGraceInconsistencyWarningresource option: A new field onResourceOptions(and correspondingResourceOptionsBuildermethod) 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.mdon preferring stable extracted values for guard conditions to avoid unintentional re-blocking after resource creation.
Documentation
- Added blog post link to README and guidelines: The Missing Layers in Your Kubernetes Operator.
- Updated AI instructions with missing documentation and package references, E2E testing guidance, and a self-maintenance callout.
v0.6.0
This release adds component-level prerequisites (initialization barriers) and a golden file testing utility for snapshot-testing primitive output.
Added
- Component prerequisites: A new
Prerequisiteinterface andWithPrerequisitebuilder method allow components to declare initialization barriers that must be satisfied before any resources are reconciled. IncludesDependsOnfor expressing inter-component ordering via status conditions. PrerequisiteStatusandPrerequisiteResult: Status types (Met,NotMet) and result struct for prerequisite check outcomes.PrerequisiteNotMetcondition reason: Components blocked by an unsatisfied prerequisite report this condition reason with the prerequisite's explanation.golden.AssertYAMLandgolden.CompareYAML: Generic snapshot testing helpers that serialize a primitive'sPreviewObjectoutput to YAML and compare against a golden file. Supports-updateflag integration and optionalWithSchemefor resolvingTypeMeta.PreviewObjecton all primitives: Every built-in primitive (21 typed + 4 unstructured) now exposesPreviewObject()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
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
WrapGuardandWrapExtractorinstead of inline nil-check and closure wrapping in theirWithGuardandWithDataExtractormethods.
Docs
- Added
Guardableto the custom resource and unstructured primitive documentation. - Rewrote the custom resource mutator example to properly demonstrate
NextFeatureand per-feature plan scoping. - Updated the custom resource builder example to use value-type callbacks with
WrapGuardandWrapExtractor.
v0.5.0
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)toWhen(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
truthsrenamed torequiredTruthsto matchVersionGateinternals.
Dependencies
- Updated
k8s.io/utilsdigest to 28399d8.
v0.4.0
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 toBlocked, 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
Guardableinterface andGuardStatustype inpkg/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
- New
- 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/checkoutto v6.
v0.3.0
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
ResourceOptionsBuilderfor 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 viaWithTruth(), marking resources asAuxiliary()orReadOnly(), and aResourceOptionsFor()convenience function for the common single-gate case.- Compatibility tests and documentation (
docs/compatibility.md) covering controller-runtime v0.22+ support.