Skip to content

Add a keep-applied mutation clamp with a one-shot override annotation #211

Description

@sourcehawk

Problem

Some fields must never move backwards once applied: a storage size must not shrink, a running version must not silently downgrade. An ocf mutation cannot express this because mutations are pure functions of desired state with no view of the live object, so the clamp has to be hoisted into the controller before the components are built. Every consumer then also needs the escape hatch: an annotation that sanctions the otherwise refused change once and is deleted after it is honored.

konsole-is/camunda-operator carries four copies: internal/controller/camundacluster/storage.go (recordIgnoredShrink), internal/controller/elasticsearchcluster/controller.go (keepAppliedStorageSize), internal/controller/databaseserver/controller.go (keepAppliedStorageSize, keepAppliedWALSize, shared keepAppliedSize), and internal/controller/databaseserver/version.go (keepRunningVersion, the same shape over a version instead of a quantity). Three of the four redeclare the identical private event constants StorageShrinkIgnored and Resize. The override side exists too: AllowVersionDowngradeAnnotation is read, acted on, and then deleted in internal/controller/camundacluster/downgrade.go, and the stamp-what-was-applied annotations (RequestedStorageSizeAnnotation, BrokerVersionAnnotation) implement the same idea by hand.

Approach

Two composable pieces on the mutation side:

  • mutation.KeepApplied(path string, cmp func(desired, applied any) bool): at apply time, compare the desired value at path with the live applied value and keep the applied one when the comparator refuses the move, emitting a framework-owned event (reason and action declared once) naming the ignored change.
  • mutation.OneShotOverrideAnnotation(key string): when the annotation is present, permit the otherwise refused change for this pass and remove the annotation in the same reconcile so it cannot sanction a second change.

This needs the mutation pipeline to see the live object at clamp time, which is the actual framework change; the two helpers are thin once that exists.

Verification

  • The three storage-size copies in konsole-is/camunda-operator port to KeepApplied and the triplicated event constants collapse into the framework vocabulary.
  • The version downgrade flow ports to KeepApplied plus OneShotOverrideAnnotation with the same observable behavior: refused without the annotation, honored once with it, annotation gone afterwards.
  • A test covers: shrink refused with event, growth passes, override honors exactly one change, override annotation removed even when the change equals the applied value.

Context

Found while auditing konsole-is/camunda-operator for repeated hand-rolled patterns. Smallest change on the audit list and the most literal duplication, so a good first candidate if only one gets picked up.

Activity

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

Metadata

Metadata

Assignees

Labels

featureUser-observable capability or surface

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions