Repository navigation
✨ Enrich ObservedPhase with Status, CompletedAt, Message, and PhaseStatus #3044
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -555,12 +555,13 @@ type ClusterObjectSetStatus struct { | |
| // ready, meaning it had successfully rolled out and all of its objects passed | ||
| // their probes. It is set once and is immutable thereafter. | ||
| // | ||
| // +kubebuilder:validation:XValidation:rule="self == oldSelf || oldSelf == null",message="completedAt is immutable" | ||
| // +kubebuilder:validation:XValidation:rule="self == oldSelf || oldSelf == null",message="completedAt is immutable, once set" | ||
| // +optional | ||
| CompletedAt metav1.Time `json:"completedAt,omitempty,omitzero"` | ||
| CompletedAt metav1.Time `json:"completedAt,omitzero"` | ||
| } | ||
|
|
||
| // ObservedPhase records the observed content digest of a resolved phase. | ||
| // +kubebuilder:validation:XValidation:rule="!has(oldSelf.completedAt) || has(self.completedAt)",message="completedAt cannot be removed once set" | ||
| type ObservedPhase struct { | ||
| // name is the phase name matching a phase in spec.phases. | ||
| // | ||
|
|
@@ -570,6 +571,28 @@ type ObservedPhase struct { | |
| // +kubebuilder:validation:XValidation:rule=`!format.dns1123Label().validate(self).hasValue()`,message="the value must consist of only lowercase alphanumeric characters and hyphens, and must start and end with an alphanumeric character." | ||
| Name string `json:"name"` | ||
|
|
||
| // status is the current state of this phase in the rollout. Must be one of | ||
| // InvalidSpecification, Pending, Reconciling, | ||
| // Stabilizing, Ready, Unknown, Superseded, Archiving, or Archived. | ||
| // +required | ||
| Status PhaseStatus `json:"status"` | ||
|
|
||
| // completedAt is the timestamp when this phase first became Ready. | ||
| // Set once and never modified. Nil means the phase has never been | ||
| // Ready. | ||
| // +optional | ||
| // +kubebuilder:validation:XValidation:rule="self == oldSelf || oldSelf == null",message="completedAt is immutable, once set" | ||
| CompletedAt metav1.Time `json:"completedAt,omitzero"` | ||
|
|
||
| // message is a phase-level message providing context about the | ||
| // current status (e.g. a validation error for InvalidSpecification | ||
| // phases, or a summary for Pending phases). At most 1024 characters; | ||
| // longer messages are truncated by the controller. | ||
| // +kubebuilder:validation:MinLength=1 | ||
| // +kubebuilder:validation:MaxLength=1024 | ||
| // +optional | ||
| Message string `json:"message,omitzero"` | ||
|
|
||
| // digest is the digest of the phase's resolved object content | ||
| // at first successful resolution, in the format "<algorithm>:<hex>". | ||
| // | ||
|
|
@@ -581,10 +604,14 @@ type ObservedPhase struct { | |
| Digest string `json:"digest"` | ||
|
|
||
| // objectCounts reports the number of objects in this phase by state. | ||
| // It is omitted until counts have been computed; when present, all count | ||
| // fields are reported (including zeros). | ||
| // +optional | ||
| ObjectCounts ObjectCounts `json:"objectCounts,omitzero"` | ||
|
|
||
| // lastTransitionTime is the last time the phase transitioned from one status to another. | ||
| // +required | ||
| // +kubebuilder:validation:Type=string | ||
| // +kubebuilder:validation:Format=date-time | ||
| LastTransitionTime metav1.Time `json:"lastTransitionTime,omitzero"` | ||
| } | ||
|
|
||
| // ObjectCounts reports the number of objects in a ClusterObjectSet or phase by state. | ||
|
|
@@ -596,16 +623,16 @@ type ObjectCounts struct { | |
| Total int64 `json:"total"` | ||
|
|
||
| // present is the number of objects in this phase that exist on the | ||
| // cluster. During reconcile, this counts objects that the controller | ||
| // has found or created. During teardown, this decrements toward zero | ||
| // as objects are deleted. | ||
| // cluster and are owned by this object set, or another in the same group. | ||
| // During reconcile, this counts objects that the controller has found or | ||
| // created. During teardown, this decrements toward zero as objects are deleted. | ||
| // +required | ||
| // +kubebuilder:validation:Minimum:=0 | ||
| // +kubebuilder:validation:Maximum:=1000 | ||
| Present int64 `json:"present"` | ||
|
|
||
| // synced is the number of objects in this phase whose cluster state | ||
| // matches the desired state. | ||
| // matches the desired state. Set to zero during teardown. | ||
| // +required | ||
| // +kubebuilder:validation:Minimum:=0 | ||
| // +kubebuilder:validation:Maximum:=1000 | ||
|
|
@@ -619,6 +646,52 @@ type ObjectCounts struct { | |
| Ready int64 `json:"ready"` | ||
| } | ||
|
|
||
| // PhaseStatus describes the current state of a phase in the rollout. | ||
| // | ||
| // +kubebuilder:validation:Enum=InvalidSpecification;Pending;Reconciling;Stabilizing;Ready;Unknown;Superseded;Archiving;Archived | ||
| type PhaseStatus string | ||
|
|
||
| const ( | ||
| // PhaseStatusInvalidSpecification indicates the phase failed preflight validation | ||
| // in a permanent way (e.g. cross-phase duplication). | ||
| PhaseStatusInvalidSpecification PhaseStatus = "InvalidSpecification" | ||
|
|
||
| // PhaseStatusPending indicates the phase is not being actively | ||
| // reconciled, but the controller has checked and some objects do not | ||
| // match the desired state. | ||
| PhaseStatusPending PhaseStatus = "Pending" | ||
|
|
||
| // PhaseStatusReconciling indicates the controller is actively applying | ||
| // objects in this phase and not all objects are synced yet. | ||
| PhaseStatusReconciling PhaseStatus = "Reconciling" | ||
|
|
||
| // PhaseStatusStabilizing indicates all objects in this phase are | ||
| // synced but some assertions are not yet passing. The controller is | ||
| // not actively writing; it is waiting for other controllers or | ||
| // external actions to bring the objects into the expected state. | ||
| PhaseStatusStabilizing PhaseStatus = "Stabilizing" | ||
|
|
||
| // PhaseStatusReady indicates all objects in this phase are synced | ||
| // and pass their assertions. | ||
| PhaseStatusReady PhaseStatus = "Ready" | ||
|
|
||
| // PhaseStatusUnknown indicates this phase was not evaluated during | ||
| // the most recent reconcile. | ||
| PhaseStatusUnknown PhaseStatus = "Unknown" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we're currently not setting this status in the code
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here's where I used it in orb-operator: https://github.com/joelanford/orb-operator/blob/76c7c2316efdacf9a3eeb8a68719df9781dacf1e/internal/status/cos/status.go#L163-L186 Can't remember if I make any specific decisions on this, or if this is what fell out of the LLM and it seemed fine. One thing that occurs to me looking at the orb-operator code is that maybe we should specifically try to evaluate all phases even if some other phases are blocked by preflight validation failures?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we do. The only thing is we ignore validation failures if other phases have them? or aren't yet complete. The reason is those dry-run operations can fail because, e.g. the namespace that's supposed to be created in a previous phase hasn't been created yet.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd say we could use Unknown for now as the initial status when we derive the digest of the phase. Then, if there's a revision validation error, or some other precondition not met, it will come up. |
||
|
|
||
| // PhaseStatusSuperseded indicates all objects in this phase have been | ||
| // adopted by a newer revision. | ||
| PhaseStatusSuperseded PhaseStatus = "Superseded" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently, in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So you're saying that in a given phase we may have both objects that have been superseded by the subsequent revision and some that have been archived instead? If we aren't avoiding the use of
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point @dtfranz. The assumption here is that there is no use to differentiate between "Superseded" and "Archived", as once the objects are either taken over or deleted it changes nothing for the Phase: seems clear that the assumption holds to me, making it explicit to double check.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean, for example, rev-1.phase-1 has objects {a, b, c, d} and rev-2.phase-1 has objects {a, b, c} rev-1.phase-1 will never has a HasTransitioned() == true because object d isn't present in rev-2. So, I'm wondering whether a phase is superseded once any of its objects is now owned by a subsequent revision.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we can stick to the current semantic - but this is something we'll probably want to revisit
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To me, And if we need a present tense phase, then Archiving/Archived is different. That is about cleaning up any leftover objects after everything shared has been handed over.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Re: superseded versus archived
So we definitely need the COS controller to distinguish between those two things. It is totally possible for a fully superseded COS to remain active indefinitely.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah - superseded really should be what you said. The problem is that there's no way for the COS to know when that's happened (unless its also trolling throw the statuses of the later revisions). Feels like a bit of extra complexity for now. But, maybe it wouldn't be too bad 🤔 |
||
|
|
||
| // PhaseStatusArchiving indicates the controller is actively deleting | ||
| // objects in this phase. | ||
| PhaseStatusArchiving PhaseStatus = "Archiving" | ||
|
|
||
| // PhaseStatusArchived indicates all objects in this phase have | ||
| // been deleted from the cluster. | ||
| PhaseStatusArchived PhaseStatus = "Archived" | ||
| ) | ||
|
|
||
| // TODO(COD): Update the archival description below when archival decisions move | ||
| // to ClusterObjectDeployment reconciliation. | ||
|
|
||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.