Skip to content

Commit f7217e2

Browse files
pedjakclaude
andcommitted
Ensure COS phase immutability for referenced object approach
ClusterObjectSet phases are immutable by design, but when objects are stored in external Secrets via refs, the Secret content could be changed by deleting and recreating the Secret with the same name. This enforces phase immutability for the referenced object approach by verifying that referenced Secrets are marked immutable and that their content hashes have not changed since first resolution. On first successful reconciliation, SHA-256 hashes of referenced Secret data are recorded in .status.observedObjectContainers. On subsequent reconciles, the hashes are compared and reconciliation is blocked with Progressing=False/Reason=Blocked if any mismatch is detected. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 8bd971b commit f7217e2

File tree

14 files changed

+779
-13
lines changed

14 files changed

+779
-13
lines changed

api/v1/clusterobjectset_types.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,30 @@ type ClusterObjectSetStatus struct {
510510
// +listMapKey=type
511511
// +optional
512512
Conditions []metav1.Condition `json:"conditions,omitempty"`
513+
514+
// observedObjectContainers records the content hashes of referenced Secrets
515+
// at first successful resolution. This is used to detect if a referenced
516+
// Secret was deleted and recreated with different content.
517+
//
518+
// +listType=map
519+
// +listMapKey=name
520+
// +optional
521+
ObservedObjectContainers []ObservedObjectContainer `json:"observedObjectContainers,omitempty"`
522+
}
523+
524+
// ObservedObjectContainer records the observed state of a referenced Secret
525+
// used as an object source.
526+
type ObservedObjectContainer struct {
527+
// name identifies the referenced Secret in "namespace/name" format.
528+
//
529+
// +required
530+
Name string `json:"name"`
531+
532+
// hash is the hex-encoded SHA-256 hash of the Secret's .data field
533+
// at first successful resolution.
534+
//
535+
// +required
536+
Hash string `json:"hash"`
513537
}
514538

515539
// +genclient

api/v1/zz_generated.deepcopy.go

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

applyconfigurations/api/v1/clusterobjectsetstatus.go

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

applyconfigurations/api/v1/observedobjectcontainer.go

Lines changed: 53 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

applyconfigurations/utils.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/draft/concepts/large-bundle-support.md

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,18 @@ Recommended conventions:
142142
1. **Secret type**: Secrets should use the dedicated type
143143
`olm.operatorframework.io/object-data` to distinguish them from user-created
144144
Secrets and enable easy identification. The system always sets this type on
145-
Secrets it creates. The reconciler does not enforce the type when resolving
146-
refs — Secrets with any type are accepted — but producers should set it for
147-
consistency.
148-
149-
2. **Immutability**: Secrets should set `immutable: true`. Because COS phases
150-
are immutable, the content backing a ref should not change after creation.
151-
Mutable referenced Secrets are not rejected, but modifying them after the
152-
COS is created leads to undefined behavior.
145+
Secrets it creates. The reconciler does not enforce the Secret type when
146+
resolving refs, but it does enforce that referenced Secrets have
147+
`immutable: true` set and that their content has not changed since first
148+
resolution.
149+
150+
2. **Immutability**: Secrets must set `immutable: true`. The reconciler verifies
151+
that all referenced Secrets have `immutable: true` set before proceeding.
152+
Mutable referenced Secrets are rejected and reconciliation is blocked with
153+
`Progressing=False, Reason=Blocked`. Additionally, the reconciler records
154+
content hashes of referenced Secrets on first resolution and blocks
155+
reconciliation if the content changes (e.g., if a Secret is deleted and
156+
recreated with the same name but different data).
153157

154158
3. **Owner references**: Referenced Secrets should carry an ownerReference to
155159
the COS so that Kubernetes garbage collection removes them when the COS is
@@ -388,6 +392,13 @@ Key properties:
388392
389393
### COS reconciler behavior
390394
395+
Before resolving individual object refs, the reconciler verifies all referenced
396+
Secrets: each must have `immutable: true` set, and its content hash must match
397+
the hash recorded in `.status.observedObjectContainers` (if present). If any Secret
398+
fails verification, reconciliation is blocked with `Progressing=False,
399+
Reason=Blocked`. On first successful verification, content hashes are persisted
400+
to status for future comparisons.
401+
391402
When processing a COS phase:
392403
- For each object entry in the phase:
393404
- If `object` is set, use it directly (current behavior, unchanged).

helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterobjectsets.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,33 @@ spec:
621621
x-kubernetes-list-map-keys:
622622
- type
623623
x-kubernetes-list-type: map
624+
observedObjectContainers:
625+
description: |-
626+
observedObjectContainers records the content hashes of referenced Secrets
627+
at first successful resolution. This is used to detect if a referenced
628+
Secret was deleted and recreated with different content.
629+
items:
630+
description: |-
631+
ObservedObjectContainer records the observed state of a referenced Secret
632+
used as an object source.
633+
properties:
634+
hash:
635+
description: |-
636+
hash is the hex-encoded SHA-256 hash of the Secret's .data field
637+
at first successful resolution.
638+
type: string
639+
name:
640+
description: name identifies the referenced Secret in "namespace/name"
641+
format.
642+
type: string
643+
required:
644+
- hash
645+
- name
646+
type: object
647+
type: array
648+
x-kubernetes-list-map-keys:
649+
- name
650+
x-kubernetes-list-type: map
624651
type: object
625652
type: object
626653
served: true

0 commit comments

Comments
 (0)