From 922c9d905d843b1719b7ddaa67274d2656dbb016 Mon Sep 17 00:00:00 2001 From: "Per G. da Silva" Date: Wed, 11 Nov 2026 13:57:48 +0100 Subject: [PATCH] Document ClusterObjectDeployment rollout concepts Add a dedicated COD concept page covering desired content, revision management and ownership, status, rollout deadlines, and history retention. Keep COS concepts focused on resource readiness and completion, link the standalone tutorial to COD semantics, and correct stale tutorial fields and ClusterExtension integration guidance. This documentation describes the spec-level deadline API introduced by operator-framework/operator-controller#3147 and must merge after that PR. The branch is based directly on main, not stacked on the API change. Signed-off-by: Per G. da Silva --- .../clusterobjectdeployment.md | 7 + .../draft/concepts/clusterobjectdeployment.md | 269 ++++++++++++++++++ docs/draft/concepts/clusterobjectsets.md | 80 +----- .../manage-clusterobjectdeployments.md | 94 +++--- 4 files changed, 315 insertions(+), 135 deletions(-) create mode 100644 docs/draft/concepts/clusterobjectdeployment.md diff --git a/docs/designs/object-controller/clusterobjectdeployment.md b/docs/designs/object-controller/clusterobjectdeployment.md index 686ae59497..435f3df1a9 100644 --- a/docs/designs/object-controller/clusterobjectdeployment.md +++ b/docs/designs/object-controller/clusterobjectdeployment.md @@ -1,5 +1,12 @@ # ClusterObjectDeployment contract +> **Historical proposal:** The deadline semantics below describe an earlier design, +> not the current implementation. COD now enforces a total rollout budget from COS +> creation, excludes time waiting for creation, and recalculates that budget when +> the deadline changes—even after timeout. COS no longer exposes a deadline field. +> See [Progress deadlines and recovery](../../draft/concepts/clusterobjectdeployment.md#progress-deadlines-and-recovery) +> for the current behavior. + ClusterObjectDeployment (COD) is a mutable orchestration resource that creates ClusterObjectSet (COS) snapshots and observes their rollout. This proposal defines the template, revision lifecycle, and status contract for COD and COS. diff --git a/docs/draft/concepts/clusterobjectdeployment.md b/docs/draft/concepts/clusterobjectdeployment.md new file mode 100644 index 0000000000..03229a789c --- /dev/null +++ b/docs/draft/concepts/clusterobjectdeployment.md @@ -0,0 +1,269 @@ +# ClusterObjectDeployments + +A `ClusterObjectDeployment` (COD) is an experimental, cluster-scoped API that +manages mutable desired content through immutable +[ClusterObjectSet](clusterobjectsets.md) (COS) revisions. Update the deployment's +template to change what should be deployed; the controller creates a new revision +rather than modifying an existing revision's content. + +For a hands-on walkthrough, see +[Manage standalone ClusterObjectDeployments](../tutorials/manage-clusterobjectdeployments.md). +For field definitions, see the +[COD API reference](../../api-reference/olmv1-api-reference.md#clusterobjectdeployment). + +## Responsibilities + +COD and COS separate rollout orchestration from resource reconciliation: + +| ClusterObjectDeployment | ClusterObjectSet | +| --- | --- | +| Stores the mutable desired template | Records the content of one revision | +| Creates and owns revisions | Applies objects in ordered phases | +| Enforces rollout deadlines | Evaluates readiness and records first completion | +| Reports deployment availability and progress | Reports resource reconciliation through `Ready` | +| Prunes excess archived history | Handles object ownership handoff and teardown | + +Both APIs are reconciled by the experimental object-controller. They can be used +without ClusterExtension or ClusterCatalog resources. See +[Controller deployment](clusterobjectsets.md#controller-deployment) for standalone +installation details. + +## Desired content and rollout policy + +A COD has three main spec fields: + +- `spec.template` contains the desired revision content and revision metadata. +- `spec.progressDeadlineMinutes` configures the rollout budget independently of + revision content. +- `spec.revisionHistoryLimit` controls how much archived history to retain. + +For example, this deployment allows ten minutes for a revision to roll out and +retains two archived revisions: + +```yaml +apiVersion: olm.operatorframework.io/v1 +kind: ClusterObjectDeployment +metadata: + name: my-app +spec: + progressDeadlineMinutes: 10 + revisionHistoryLimit: 2 + template: + metadata: + labels: + app.kubernetes.io/name: my-app + spec: + collisionProtection: Prevent + phases: + - name: configuration + objects: + - type: InlineManifest + inlineManifest: + apiVersion: v1 + kind: ConfigMap + metadata: + name: my-app-settings + namespace: default + data: + message: Hello from revision A +``` + +The template's `spec` contains phases, optional custom progression probes, and +collision protection, with the same semantics as the corresponding COS fields. +Objects can be embedded as `inlineManifest` or stored in immutable Secrets and +referenced with `secretRef`. The controller assigns each revision's group, +revision number, lifecycle state, and controller owner reference; callers cannot +set these through the template. + +`spec.template.metadata` contains labels and annotations propagated to each COS, +not to its managed objects. Set a managed object's metadata in its own manifest. +The reserved `olm.operatorframework.io/template-hash` label is always set by the +controller to identify the template that produced a revision. + +## Revision lifecycle + +The COD's name identifies its COS group. Names must be 1–52 characters long, +start with a lowercase letter, contain only lowercase letters, digits or hyphens, +and end with a letter or digit. + +When a new revision is needed, the controller: + +1. Finds the highest revision number in the group, including archived and + foreign-owned revisions. +2. Allocates the next number (`1` for an empty group) and creates an owned, Active + COS named `-`. +3. Observes that revision's rollout while older revisions can remain Active. +4. Reports completion once the new COS records `status.completedAt`. + +The COS controller handles ownership handoff between revisions and archives +superseded revisions once handoff completes. The COD controller prunes excess +archived history after teardown finishes. + +Revision numbers range from 1 to 9999999999. Once that range is exhausted, the +controller reports an error rather than allocating another revision. + +### Which edits create revisions? + +The controller compares the desired template with the latest owned, active, +non-deleting revision's template identity. A different template, or the absence +of an active revision, causes a new revision to be created, subject to the +[ownership rules](#revision-ownership). + +| Edit | Result | +| --- | --- | +| Objects, Secret references, phases, probes, or collision protection in `spec.template.spec` | A new revision | +| Phase, object, or probe order | A new revision | +| Labels or annotations in `spec.template.metadata` | A new revision | +| `spec.progressDeadlineMinutes` | The existing revision is retained; its rollout budget changes | +| `spec.revisionHistoryLimit` | Retention changes; the existing revision is retained | +| Root COD labels or annotations | The existing revision is retained | + +Reapplying an unchanged template retains the current active revision. Restoring +an earlier template after a different one has been deployed creates a new, +higher-numbered revision; it does not reactivate archived history. For example, +A → B → A produces revisions 1, 2, and 3. + +### Revision ownership + +Group membership determines revision ordering; controller owner references +determine which revisions a COD manages. Sharing a group does not by itself make +a COS owned by the deployment. + +The controller can adopt an ownerless, non-deleting group member only when it +carries the `apps.kubernetes.io/managed-by=object-controller` label. It does not +replace another controller's owner reference. A stale reference to an older UID +of the same deployment is left for Kubernetes garbage collection rather than +rewritten. + +If the highest active, non-deleting group member is controlled by another +controller, the COD reports `Progressing=False/OwnerConflict` and creates no new +revision. If that group member is ownerless but lacks the managed-by label, the +COD reports `Progressing=False/UnmanagedRevision`. Resolve the foreign ownership, +remove the blocking revision, or restore a missing managed-by label only when the +revision was actually created by object-controller. A stale owner reference to an +older UID also blocks new revisions until garbage collection removes it, without +a distinct condition reason. + +`OwnerConflict` concerns ownership of the COS itself; `RevisionObjectCollision` +concerns ownership of Kubernetes objects within a revision. Ownership checks take +precedence over rollout completion and deadline checks. Availability still +reflects only owned active revisions, so an older ready revision can keep the COD +available during a conflict. + +## Status conditions + +COD exposes `Available` and `Progressing`. Its child COSs expose `Ready` and +record first rollout completion in `status.completedAt`. + +### Available + +Availability describes current health across owned, active, non-deleting +revisions, not just the latest revision: + +| Status | Reason | Meaning | +| --- | --- | --- | +| True | `Available` | At least one owned active revision is Ready | +| False | `NoActiveRevision` | No owned active revision exists | +| False | `Unavailable` | Owned active revisions exist, but none are Ready | + +An older ready revision can keep `Available=True` while its successor rolls out, +is stalled, or has exceeded its rollout deadline. + +### Progressing + +Progress describes the desired rollout, including failures that prevent it from +starting or completing: + +| Status | Reason | Meaning | +| --- | --- | --- | +| True | `NewClusterObjectSetProgressing` | Waiting to create or adopt revisions, or the desired revision is rolling out | +| True | `RevisionPreconditionsNotMet` | The desired revision's preconditions are unmet; reconciliation keeps retrying | +| False | `NewClusterObjectSetProgressed` | The desired revision completed rollout at least once | +| False | `ProgressDeadlineExceeded` | An incomplete, still-retrying revision exceeded its rollout budget | +| False | `InvalidSpecification` | Template labels or annotations are invalid; no revision is created until corrected | +| False | `OwnerConflict` | The highest active group member is controlled by another controller | +| False | `UnmanagedRevision` | The highest active group member is ownerless and lacks the object-controller managed-by label | +| False | `RevisionObjectCollision` | The desired revision cannot take ownership of one or more managed objects | +| False | `RevisionInvalidSpecification` | The desired revision's rendered objects are invalid | + +For revision-specific failures, the COD's condition message names the COS to +inspect. Its `Ready` condition provides the specific cause. +`RevisionInvalidSpecification` concerns the revision's rendered objects, while +`InvalidSpecification` concerns the COD's `spec.template.metadata`. + +`RevisionObjectCollision` and `RevisionInvalidSpecification` are hard stalls: +they require user action and take precedence over `ProgressDeadlineExceeded`. +`RevisionPreconditionsNotMet` is a retrying state and remains subject to the +rollout deadline. + +Completion is historical. Once the desired COS records `status.completedAt`, +later health regressions can affect `Available` but do not restart that +revision's rollout or its deadline. A new template starts a new rollout. + +## Progress deadlines and recovery + +`spec.progressDeadlineMinutes` supplies one budget for the whole revision. The +supported range is 1–720 minutes. Omit the field to disable deadline enforcement. +The deadline belongs to the COD; COSs do not carry a deadline field. + +Timing starts at the COS's creation timestamp. Failed or delayed creation +attempts consume no budget. Completing phases, polling, retries, and controller +restarts do not reset the timer. A five-phase revision with a ten-minute deadline +has ten minutes total, not ten minutes per phase. Each new revision receives a +fresh budget. + +Changing the deadline does not create a revision or reset its start time. The new +budget applies to the latest active revision and is still measured from that +COS's original creation timestamp: + +- Extending the budget beyond elapsed rollout time can clear an existing + `ProgressDeadlineExceeded` condition without new rollout progress. +- Shortening the budget below elapsed rollout time can cause immediate expiry. +- Removing the field disables enforcement, including after expiry. + +The COD schedules its own expiry check even if the COS emits no further events. +When an incomplete, retrying revision exceeds the budget, the COD reports +`Progressing=False/ProgressDeadlineExceeded`. The COS's `Ready` condition retains +its resource reconciliation status; for example, it can still report +`PreconditionsNotMet`. Hard stalls and revision ownership conflicts retain their +more actionable condition reasons rather than being replaced by a timeout. + +Reconciliation continues after expiry. A revision that later completes records +`status.completedAt`, and the COD reports +`Progressing=False/NewClusterObjectSetProgressed`. Completing only some phases +does not clear an expired deadline. Once completion is recorded, the deadline no +longer applies to that revision, even if it later becomes unhealthy. + +## Revision history and deletion + +`spec.revisionHistoryLimit` controls how many archived revisions the COD retains. +The default is 5, the supported range is 0–50, and 0 retains no archived history. +Active revisions never count against the limit and are not pruned by it. + +Only owned archived revisions that have finished teardown are eligible for +pruning. The controller deletes the oldest eligible revisions beyond the limit; +revisions still tearing down are left until cleanup completes. Foreign-owned +group members do not count toward the limit and are not pruned by the COD. + +Deleting a COD normally cascades to its owned COSs and their managed objects. +Deleting with orphan propagation leaves the revisions in their existing group; +it does not change revision membership or ownership handoff behavior. + +## Relationship with ClusterExtension + +When `BoxcutterRuntime` is enabled, operator-controller resolves and unpacks a +ClusterExtension's bundle and applies a COD with the same name. The +ClusterExtension owns the COD, the COD owns its COS revisions, and each COS owns +the Kubernetes objects it manages. + +The COD's name also becomes the COS group. Revision lookup uses that group, not +labels or controller owner references. Operator-controller observes COD rollout +status to report the extension's installation and progress. + +For a ClusterExtension, COD `OwnerConflict` maps to `Progressing=True` with reason +`Retrying`, preserving the deployment's message about the conflicting revision +and its controller. A first install reports `Installed=False` with reason +`Failed`. During an upgrade, the previously installed bundle and its +`Installed=True` condition are preserved until the new rollout completes, with +`observedGeneration` updated to acknowledge the current spec. Reconciliation +resumes automatically when the ownership conflict clears. diff --git a/docs/draft/concepts/clusterobjectsets.md b/docs/draft/concepts/clusterobjectsets.md index 4456ba28ed..6498c16d04 100644 --- a/docs/draft/concepts/clusterobjectsets.md +++ b/docs/draft/concepts/clusterobjectsets.md @@ -19,8 +19,10 @@ Each ClusterObjectSet has: ClusterObjectSets can be used by any controller or system that needs to manage the rollout of a set of Kubernetes resources in a controlled, phased manner. Within OLM, when `BoxcutterRuntime` is enabled, operator-controller creates and updates a ClusterObjectDeployment (COD) for each ClusterExtension. The COD controller creates the immutable ClusterObjectSet revisions that deploy and upgrade the extension. -For a standalone walkthrough that manages mutable desired content through COS -revisions, see [Manage standalone ClusterObjectDeployments](../tutorials/manage-clusterobjectdeployments.md). +For deployment-level revision management and rollout policy, see +[ClusterObjectDeployments](clusterobjectdeployment.md). For a standalone walkthrough +that manages mutable desired content through COS revisions, see +[Manage standalone ClusterObjectDeployments](../tutorials/manage-clusterobjectdeployments.md). The `group` value must be 1–52 characters long, start with a lowercase letter, contain only lowercase letters, digits or hyphens, and end with a letter or digit. @@ -247,81 +249,13 @@ Indicates whether all objects in the revision have been successfully rolled out On a revision's first reconciliation the `Ready` condition is initialized to `Unknown`/`Pending`, so it is always present. A transient internal error (e.g. an API read failure) leaves the `Ready` condition unchanged and is retried with backoff; it is not reported as a distinct reason (while the revision is still `Pending`, it stays `Unknown`/`Pending`). -### Progress deadline - -The progress deadline is a ClusterObjectDeployment (COD) concept: the time allowed -for the deployment to progress to its desired new revision. Configure it through -`spec.template.spec.progressDeadlineMinutes`. The COD enforces this budget itself; -the ClusterObjectSets it creates do not carry a deadline. Time spent waiting for -the ClusterObjectSet to be created is not counted, and each new revision receives -its own budget, measured from the revision's creation timestamp until its first -successful rollout (recorded in `status.completedAt`). Completing a phase does not -reset the timer. - -If the desired revision is still incomplete when that budget expires, the COD -reports `Progressing=False` with reason `ProgressDeadlineExceeded`. A five-phase -revision with a ten-minute deadline has ten minutes total, even if four phases -have completed. The COD schedules a deadline check even if the COS emits no -further events. - -The COS's `Ready` condition continues to report readiness failures, such as -`ObjectsNotReady`, `PreconditionsNotMet`, or `InvalidSpecification`. The COD checks -elapsed time independently of the COS's readiness condition. - -Omitting the field disables the deadline. Reconciliation continues after a -timeout, so a revision can still complete later. Once `status.completedAt` is -recorded, the COD reports `Progressing=False` with reason -`NewClusterObjectSetProgressed`. Later health regressions do not restart deadline -timing. The COD can remain `Available=True` if an older active revision is still -ready. - -### ClusterObjectDeployment Progressing - -A ClusterObjectDeployment (COD) reports rollout progress and ownership conflicts -on its `Progressing` condition. While a rollout is incomplete, it also reflects -*why* its head active revision is not yet `Ready`, so you can tell a rollout that -is merely in progress from one that is stalled on something you must fix. -For revision failures, inspect the named ClusterObjectSet for the specific cause. - -| Status | Reason | Meaning | What to do | -| --- | --- | --- | --- | -| True | `NewClusterObjectSetProgressing` | The head revision is rolling out (`Pending`/`ObjectsNotReady`) | Wait | -| True | `RevisionPreconditionsNotMet` | The head revision's precondition is not yet met (e.g. a mutable/invalid referenced Secret, or RBAC/dependency CRDs not yet propagated); it keeps retrying | Satisfy the precondition (grant RBAC, install the dependency, fix the Secret); it recovers automatically | -| False | `RevisionObjectCollision` | The head revision's objects are owned by another controller or already exist; it will not roll out without intervention | Resolve ownership of the conflicting objects | -| False | `RevisionInvalidSpecification` | The head revision's own objects are invalid | Correct the template so the rendered objects are valid | -| False | `ProgressDeadlineExceeded` | A still-retrying revision exceeded its deadline (see [Progress deadline](#progress-deadline)) | See above | -| False | `InvalidSpecification` | The deployment's own `spec.template.metadata` is invalid, so no revision is created | Fix the template labels/annotations | -| False | `OwnerConflict` | The highest active, non-deleting revision in the deployment's group is controlled by another controller; no new revision is created | Resolve the revision's controller ownership, or archive or delete the conflicting revision | -| False | `NewClusterObjectSetProgressed` | The head revision completed its rollout | None | - -`OwnerConflict` concerns ownership of the ClusterObjectSet itself, while -`RevisionObjectCollision` concerns ownership of objects within a revision. -`OwnerConflict` takes precedence even if an owned revision has completed or -exceeded its progress deadline. Availability still depends only on owned active -revisions; a previous ready revision can keep the COD `Available=True` during the -conflict. - -For a ClusterExtension, `OwnerConflict` maps to `Progressing=True` with reason -`Retrying`, preserving the deployment's message about the conflicting revision -and its controller. A first install reports `Installed=False` with reason -`Failed`. During an upgrade, the previously installed bundle and its `Installed=True` -condition are preserved until the new rollout completes, with `observedGeneration` -updated to acknowledge the current spec. Reconciliation resumes automatically when -the ownership conflict clears. - -`RevisionObjectCollision` and `RevisionInvalidSpecification` are *hard stalls*: -they will not resolve on their own, so they report `Progressing=False` and take -precedence over `ProgressDeadlineExceeded` (the actionable cause is more useful -than "the clock ran out"). `RevisionPreconditionsNotMet` keeps retrying, so it -stays `Progressing=True` and the progress deadline still governs it. Note that -`RevisionInvalidSpecification` (the revision's rendered objects) is distinct from -the deployment's own `InvalidSpecification` (invalid `spec.template.metadata`, -reported before any revision is created). - ### Completion (`status.completedAt`) The first time a revision becomes ready, the controller stamps `status.completedAt` with the current time. This timestamp is set once and never changes, marking that this revision successfully rolled out at least once — it persists even if the revision later becomes not ready. The ClusterExtension controller uses it to determine the installed revision. +For deployment-level rollout policy and conditions, see +[ClusterObjectDeployments](clusterobjectdeployment.md#status-conditions). + ## How operator-controller uses ClusterObjectSets With `BoxcutterRuntime` enabled, ClusterObjectSets serve as the deployment mechanism for `ClusterExtensions`. The operator-controller is one consumer of the ClusterObjectSet API. diff --git a/docs/draft/tutorials/manage-clusterobjectdeployments.md b/docs/draft/tutorials/manage-clusterobjectdeployments.md index 7c932ef23b..764a0789fc 100644 --- a/docs/draft/tutorials/manage-clusterobjectdeployments.md +++ b/docs/draft/tutorials/manage-clusterobjectdeployments.md @@ -92,7 +92,7 @@ Change the ConfigMap message to B: ```sh kubectl patch clusterobjectdeployment cod-example --type=json \ - -p='[{"op":"replace","path":"/spec/template/spec/phases/0/objects/0/object/data/message","value":"Hello from revision B"}]' + -p='[{"op":"replace","path":"/spec/template/spec/phases/0/objects/0/inlineManifest/data/message","value":"Hello from revision B"}]' kubectl wait --for=create clusterobjectset/cod-example-2 --timeout=180s kubectl wait --for=condition=Ready clusterobjectset/cod-example-2 --timeout=180s kubectl -n cod-example get configmap cod-example-settings \ @@ -119,44 +119,22 @@ The message returns to `Hello from revision A` through revision 3. Restoring an earlier template creates a new, higher revision; an archived revision cannot become Active again. Reapplying an unchanged template keeps the current revision. -### Which edits create revisions? - -| Edit | Result | -| --- | --- | -| Objects, references, phase/object order, probes, or collision protection in `spec.template.spec` | A new revision | -| Labels or annotations in `spec.template.metadata` | A new revision | -| `spec.template.spec.progressDeadlineMinutes` | A new revision, because the deadline is part of the template | -| Root COD labels or annotations | The current revision is retained | -| `spec.revisionHistoryLimit` | Retention changes; the current revision is retained | - -Template labels and annotations are placed on each generated COS. Set metadata -for a managed object in that object's manifest. The controller assigns COS group, -revision, lifecycle state, ownership, and the reserved -`olm.operatorframework.io/template-hash` label. +Changes to `spec.template`, including its labels and annotations, create a new +revision. Changes to rollout policy, such as `spec.progressDeadlineMinutes` or +`spec.revisionHistoryLimit`, do not. See +[Which edits create revisions?](../concepts/clusterobjectdeployment.md#which-edits-create-revisions) +for the complete rules. ## Read status and diagnose failures The COD exposes `Available` and `Progressing`. A child COS exposes `Ready` with the details of its resource reconciliation. -| COD condition | Status | Reason | Meaning | -| --- | --- | --- | --- | -| `Available` | `True` | `Available` | At least one active, non-deleting COS is Ready | -| `Available` | `False` | `NoActiveRevision` | No active, non-deleting COS exists yet | -| `Available` | `False` | `Unavailable` | Active revisions exist, but none are Ready | -| `Progressing` | `True` | `NewClusterObjectSetProgressing` | Waiting to create the desired revision, or rolling out while the COS reports `Pending` or `ObjectsNotReady` | -| `Progressing` | `True` | `RevisionPreconditionsNotMet` | The desired revision's preconditions are unmet, such as a mutable referenced Secret; reconciliation keeps retrying | -| `Progressing` | `False` | `NewClusterObjectSetProgressed` | The desired revision completed rollout at least once | -| `Progressing` | `False` | `ProgressDeadlineExceeded` | An incomplete, still-retrying revision exceeded its rollout budget | -| `Progressing` | `False` | `InvalidSpecification` | Template labels or annotations are invalid; correct them to allow creation | -| `Progressing` | `False` | `RevisionObjectCollision` | The desired revision's objects are owned by another controller or already exist; resolve the conflicting ownership | -| `Progressing` | `False` | `RevisionInvalidSpecification` | The desired revision's objects are invalid; correct the COD template | - -For revision-specific reasons, the COD's condition message names the latest -active COS. Inspect that COS's `Ready` condition for the specific cause. -`RevisionInvalidSpecification` concerns the revision's objects, while -`InvalidSpecification` concerns the COD's `spec.template.metadata` and prevents -creation of a new revision. +For revision-specific failures, the COD's condition message names the COS to +inspect. See [COD status conditions](../concepts/clusterobjectdeployment.md#status-conditions) +for the available reasons and +[Revision ownership](../concepts/clusterobjectdeployment.md#revision-ownership) +for conflicts that prevent a new rollout. Inspect the parent, the current child, and the controller logs: @@ -179,30 +157,21 @@ and let reconciliation retry. Change the COD template when the desired object content needs to change. See [COS status conditions](../concepts/clusterobjectsets.md#status-conditions) for the child reasons. -### Deadlines and recovery - -`spec.template.spec.progressDeadlineMinutes` supplies one budget for the whole -revision. The sample uses 10 minutes, the minimum; the maximum is 720 minutes. -Omit the field to disable deadline enforcement. +### Adjust the rollout deadline -Timing starts at COS creation. Failed or delayed creation attempts consume no -budget. Completing phases, polling, retries, and controller restarts preserve the -same deadline. A five-phase revision with a ten-minute deadline has ten minutes -total. Each new revision receives a fresh budget. +The sample configures a ten-minute rollout budget with +`spec.progressDeadlineMinutes`. To change that budget without creating another +revision, patch the COD directly: -The COD schedules its own expiry check even when the COS emits no further events. -The deadline still applies while preconditions are unmet: after expiry, the COD -reports `Progressing=False/ProgressDeadlineExceeded` while the COS retains its -readiness failure. `RevisionObjectCollision` and `RevisionInvalidSpecification` -require user action and take precedence over `ProgressDeadlineExceeded`, even -after expiry. The legacy deadline field remains available on directly created -COSs; COD enforces its budget independently. +```sh +kubectl patch clusterobjectdeployment cod-example --type=merge \ + -p='{"spec":{"progressDeadlineMinutes":20}}' +``` -Reconciliation continues after expiry. Completion of the entire revision records -`COS.status.completedAt` and recovers the COD to -`Progressing=False/NewClusterObjectSetProgressed`. Completing only some phases -does not clear an expired deadline. Once completion is recorded, later health regressions -can affect availability but never restart that revision's rollout timer. +The new budget is measured from the current COS's original creation timestamp, +not from the time of the edit. See +[Progress deadlines and recovery](../concepts/clusterobjectdeployment.md#progress-deadlines-and-recovery) +for timing, timeout precedence, and recovery behavior. ## Inspect history and referenced content @@ -211,26 +180,27 @@ objects that were removed from the desired content. COD prunes excess archived history after teardown completes. Active revisions are excluded from the limit. The sample retains two archived revisions with `spec.revisionHistoryLimit: 2`. -The default is 5, the supported range is 0–50, and 0 retains no archived revisions. -After A → B → A, revisions 1 and 2 can remain as archived history while revision 3 -is Active: +See [Revision history and deletion](../concepts/clusterobjectdeployment.md#revision-history-and-deletion) +for retention rules. After A → B → A, revisions 1 and 2 can remain as archived +history while revision 3 is Active: ```sh kubectl get clusterobjectsets --field-selector spec.group=cod-example \ -o custom-columns=NAME:.metadata.name,REVISION:.spec.revision,STATE:.spec.lifecycleState ``` -For larger content, each template object can use the existing COS `ref` mechanism +For larger content, each template object can use the COS `secretRef` mechanism to read a JSON manifest from an immutable Secret. Create the referenced Secret before applying the COD; it must remain available for every retained revision that references it. Changing the intended content requires a new Secret reference in the template. See [Object storage](../concepts/clusterobjectsets.md#object-storage-inline-vs-references). Standalone callers manage the lifetime of these content Secrets. -ClusterExtension integration is separate work tracked by -[OPRUN-4744](https://redhat.atlassian.net/browse/OPRUN-4744). The current -ClusterExtension path still creates COSs directly; this standalone workflow uses -object-controller without bundle resolution or catalog components. +With `BoxcutterRuntime` enabled, operator-controller manages CODs for +ClusterExtensions. This standalone workflow uses object-controller without bundle +resolution or catalog components. See +[Relationship with ClusterExtension](../concepts/clusterobjectdeployment.md#relationship-with-clusterextension) +for the integration. For all fields, see the [COD API reference](../../api-reference/olmv1-api-reference.md#clusterobjectdeployment).