-
Notifications
You must be signed in to change notification settings - Fork 2
feat(meta): add lifecycle-state annotation contract #1
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
Merged
Merged
Changes from 4 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
4ab341d
feat(meta): add lifecycle-state annotation contract
tonicmuroq 76d22b1
refactor(meta): tighten lifecycle contract and close test gaps
CMGS 01ff91e
docs: document lifecycle-state contract and PatchCocoonSetGeneration
CMGS 9a593eb
fix(k8s): make PatchCocoonSetGeneration nil-pod tolerant
CMGS 89871c9
refactor(meta): rename LifecycleStatus.PatchPayload to Annotations
CMGS File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| package meta | ||
|
|
||
| import ( | ||
| "strconv" | ||
|
|
||
| corev1 "k8s.io/api/core/v1" | ||
| ) | ||
|
|
||
| // LifecycleState is the typed contract for the lifecycle-state annotation | ||
| // vk-cocoon publishes on a Pod. | ||
| type LifecycleState string | ||
|
|
||
| const ( | ||
| LifecycleStateCreating LifecycleState = "creating" | ||
| LifecycleStateReady LifecycleState = "ready" | ||
| LifecycleStateHibernating LifecycleState = "hibernating" | ||
| LifecycleStateHibernated LifecycleState = "hibernated" | ||
| LifecycleStateFailed LifecycleState = "failed" | ||
| ) | ||
|
|
||
| // IsTerminal reports whether s is a state a client would wait for. | ||
| func (s LifecycleState) IsTerminal() bool { | ||
| switch s { | ||
| case LifecycleStateReady, LifecycleStateHibernated, LifecycleStateFailed: | ||
| return true | ||
| } | ||
| return false | ||
| } | ||
|
|
||
| // LifecycleStatus is the full triple (state, observed-generation, message). | ||
| // PatchPayload is the source of truth for what gets written; Apply | ||
| // consumes the same payload in-memory and Snapshot derives a comparison | ||
| // key from the same fields. | ||
| type LifecycleStatus struct { | ||
| State LifecycleState | ||
| ObservedGeneration int64 | ||
| Message string | ||
| } | ||
|
|
||
| // PatchPayload returns the strategic-merge value map. nil entries | ||
| // instruct the apiserver to delete the key. | ||
|
CMGS marked this conversation as resolved.
Outdated
|
||
| func (s LifecycleStatus) PatchPayload() map[string]any { | ||
| annos := map[string]any{ | ||
| AnnotationLifecycleState: string(s.State), | ||
| AnnotationLifecycleObservedGeneration: strconv.FormatInt(s.ObservedGeneration, 10), | ||
| } | ||
| if s.Message == "" { | ||
| annos[AnnotationLifecycleStateMessage] = nil | ||
| } else { | ||
| annos[AnnotationLifecycleStateMessage] = s.Message | ||
| } | ||
| return annos | ||
| } | ||
|
|
||
| // Apply writes PatchPayload into the pod's annotations, deleting keys | ||
| // whose payload value is nil. Empty message clears the annotation so a | ||
| // stale failure reason cannot tail into the next lifecycle. | ||
| func (s LifecycleStatus) Apply(pod *corev1.Pod) { | ||
| if pod == nil { | ||
| return | ||
| } | ||
| a := ensurePodAnnotations(pod) | ||
| for key, val := range s.PatchPayload() { | ||
| if val == nil { | ||
| delete(a, key) | ||
| continue | ||
| } | ||
| a[key] = val.(string) | ||
| } | ||
| } | ||
|
|
||
| // Snapshot returns a stable comparison key. NUL separator avoids | ||
| // collisions with arbitrary message contents. | ||
| func (s LifecycleStatus) Snapshot() string { | ||
| return string(s.State) + "\x00" + strconv.FormatInt(s.ObservedGeneration, 10) + "\x00" + s.Message | ||
| } | ||
|
|
||
| // ReadLifecycleStatus reads the triple from pod annotations. | ||
| func ReadLifecycleStatus(pod *corev1.Pod) LifecycleStatus { | ||
| if pod == nil { | ||
| return LifecycleStatus{} | ||
| } | ||
| return LifecycleStatus{ | ||
| State: LifecycleState(pod.Annotations[AnnotationLifecycleState]), | ||
| ObservedGeneration: ReadLifecycleObservedGeneration(pod), | ||
| Message: pod.Annotations[AnnotationLifecycleStateMessage], | ||
| } | ||
| } | ||
|
|
||
| // ReadLifecycleState reads the lifecycle-state annotation, "" when missing. | ||
| func ReadLifecycleState(pod *corev1.Pod) LifecycleState { | ||
| if pod == nil { | ||
| return "" | ||
| } | ||
| return LifecycleState(pod.Annotations[AnnotationLifecycleState]) | ||
| } | ||
|
|
||
| // ReadLifecycleObservedGeneration reads the observed-generation annotation. | ||
| // Missing or unparseable returns 0 — callers treat it as "not observed yet". | ||
| func ReadLifecycleObservedGeneration(pod *corev1.Pod) int64 { | ||
| return readInt64Annotation(pod, AnnotationLifecycleObservedGeneration) | ||
| } | ||
|
|
||
| // ReadCocoonSetGeneration reads the CocoonSet generation stamped by | ||
| // cocoon-operator. vk-cocoon writes it back as observed-generation — | ||
| // counter-based completion is not subject to wallclock skew. | ||
| func ReadCocoonSetGeneration(pod *corev1.Pod) int64 { | ||
| return readInt64Annotation(pod, AnnotationCocoonSetGeneration) | ||
| } | ||
|
|
||
| // StampCocoonSetGeneration writes the CocoonSet generation onto the pod. | ||
| func StampCocoonSetGeneration(pod *corev1.Pod, generation int64) { | ||
| if pod == nil { | ||
| return | ||
| } | ||
| a := ensurePodAnnotations(pod) | ||
| a[AnnotationCocoonSetGeneration] = strconv.FormatInt(generation, 10) | ||
| } | ||
|
|
||
| // readInt64Annotation parses an int64-valued annotation, returning 0 | ||
| // when missing or unparseable. | ||
| func readInt64Annotation(pod *corev1.Pod, key string) int64 { | ||
| if pod == nil { | ||
| return 0 | ||
| } | ||
| raw := pod.Annotations[key] | ||
| if raw == "" { | ||
| return 0 | ||
| } | ||
| n, err := strconv.ParseInt(raw, 10, 64) | ||
| if err != nil { | ||
| return 0 | ||
| } | ||
| return n | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.