Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apis/v1/cocoonset_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
)

// CocoonSetSpec defines the desired state of a CocoonSet.
// +kubebuilder:validation:XValidation:rule="!has(self.hibernatePolicy) || self.hibernatePolicy != 'release' || ((!has(self.agent.replicas) || self.agent.replicas == 0) && (!has(self.toolboxes) || size(self.toolboxes) == 0))",message="hibernatePolicy=release requires agent.replicas=0 and no toolboxes"
type CocoonSetSpec struct {
// +optional
Suspend bool `json:"suspend,omitempty"`
Expand All @@ -15,6 +16,13 @@ type CocoonSetSpec struct {
// +kubebuilder:default=always
SnapshotPolicy SnapshotPolicy `json:"snapshotPolicy,omitempty"`

// HibernatePolicy keeps (retain) or frees (release) the VM's scheduling
// seat while suspended; release requires a main-only set until sub-agent/
// toolbox restore intent is extended.
// +optional
// +kubebuilder:default=retain
HibernatePolicy HibernatePolicy `json:"hibernatePolicy,omitempty"`

// +optional
// +kubebuilder:default=default
NodePool string `json:"nodePool,omitempty"`
Expand Down
16 changes: 16 additions & 0 deletions apis/v1/crds/cocoonset.cocoonstack.io_cocoonsets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,16 @@ spec:
required:
- image
type: object
hibernatePolicy:
default: retain
description: |-
HibernatePolicy keeps (retain) or frees (release) the VM's scheduling
seat while suspended; release requires a main-only set until sub-agent/
toolbox restore intent is extended.
enum:
- retain
- release
type: string
nodeName:
description: |-
NodeName pins the VM to a node (cross-node migrate). Empty = let the
Expand Down Expand Up @@ -421,6 +431,11 @@ spec:
required:
- agent
type: object
x-kubernetes-validations:
- message: hibernatePolicy=release requires agent.replicas=0 and no toolboxes
rule: '!has(self.hibernatePolicy) || self.hibernatePolicy != ''release''
|| ((!has(self.agent.replicas) || self.agent.replicas == 0) && (!has(self.toolboxes)
|| size(self.toolboxes) == 0))'
status:
description: CocoonSetStatus represents the observed state of a CocoonSet.
properties:
Expand Down Expand Up @@ -530,6 +545,7 @@ spec:
- Scaling
- Suspending
- Suspended
- Waking
- Migrating
- Failed
type: string
Expand Down
31 changes: 24 additions & 7 deletions apis/v1/enums.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,15 @@ const (
SnapshotPolicyMainOnly SnapshotPolicy = "main-only"
SnapshotPolicyNever SnapshotPolicy = "never"

HibernatePolicyRetain HibernatePolicy = "retain"
HibernatePolicyRelease HibernatePolicy = "release"

CocoonSetPhasePending CocoonSetPhase = "Pending"
CocoonSetPhaseRunning CocoonSetPhase = "Running"
CocoonSetPhaseScaling CocoonSetPhase = "Scaling"
CocoonSetPhaseSuspending CocoonSetPhase = "Suspending"
CocoonSetPhaseSuspended CocoonSetPhase = "Suspended"
CocoonSetPhaseWaking CocoonSetPhase = "Waking"
CocoonSetPhaseMigrating CocoonSetPhase = "Migrating"
CocoonSetPhaseFailed CocoonSetPhase = "Failed"

Expand All @@ -40,12 +44,13 @@ const (
)

var (
agentModeValid = []AgentMode{AgentModeClone, AgentModeRun}
toolboxModeValid = []ToolboxMode{ToolboxModeRun, ToolboxModeClone, ToolboxModeStatic}
osTypeValid = []OSType{OSLinux, OSWindows, OSAndroid, OSMacos}
snapshotPolicyValid = []SnapshotPolicy{SnapshotPolicyAlways, SnapshotPolicyMainOnly, SnapshotPolicyNever}
connTypeValid = []ConnType{ConnTypeSSH, ConnTypeRDP, ConnTypeVNC, ConnTypeADB}
backendValid = []Backend{BackendCloudHypervisor, BackendFirecracker}
agentModeValid = []AgentMode{AgentModeClone, AgentModeRun}
toolboxModeValid = []ToolboxMode{ToolboxModeRun, ToolboxModeClone, ToolboxModeStatic}
osTypeValid = []OSType{OSLinux, OSWindows, OSAndroid, OSMacos}
snapshotPolicyValid = []SnapshotPolicy{SnapshotPolicyAlways, SnapshotPolicyMainOnly, SnapshotPolicyNever}
hibernatePolicyValid = []HibernatePolicy{HibernatePolicyRetain, HibernatePolicyRelease}
connTypeValid = []ConnType{ConnTypeSSH, ConnTypeRDP, ConnTypeVNC, ConnTypeADB}
backendValid = []Backend{BackendCloudHypervisor, BackendFirecracker}
)

// AgentMode defines the mode of an agent VM.
Expand Down Expand Up @@ -88,8 +93,20 @@ func (p SnapshotPolicy) IsValid() bool { return slices.Contains(snapshotPolicyVa
// Default returns p when set, otherwise SnapshotPolicyAlways.
func (p SnapshotPolicy) Default() SnapshotPolicy { return cmp.Or(p, SnapshotPolicyAlways) }

// HibernatePolicy selects the scheduling-seat semantics of a suspended
// CocoonSet: retain keeps the placeholder pod on its node, release frees the
// seat once the hibernate snapshot is registry-verified (wake reschedules).
// +kubebuilder:validation:Enum=retain;release
type HibernatePolicy string

// IsValid reports whether p is a recognized HibernatePolicy value.
func (p HibernatePolicy) IsValid() bool { return slices.Contains(hibernatePolicyValid, p) }

// Default returns p when set, otherwise HibernatePolicyRetain.
func (p HibernatePolicy) Default() HibernatePolicy { return cmp.Or(p, HibernatePolicyRetain) }

// CocoonSetPhase represents the lifecycle phase of a CocoonSet.
// +kubebuilder:validation:Enum=Pending;Running;Scaling;Suspending;Suspended;Migrating;Failed
// +kubebuilder:validation:Enum=Pending;Running;Scaling;Suspending;Suspended;Waking;Migrating;Failed
type CocoonSetPhase string

// ConnType is the connection protocol advertised for a VM. Empty
Expand Down
25 changes: 25 additions & 0 deletions apis/v1/enums_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,23 @@ func TestSnapshotPolicyIsValid(t *testing.T) {
}
}

func TestHibernatePolicyIsValid(t *testing.T) {
cases := []struct {
in HibernatePolicy
want bool
}{
{HibernatePolicyRetain, true},
{HibernatePolicyRelease, true},
{"", false},
{"relase", false},
}
for _, c := range cases {
if got := c.in.IsValid(); got != c.want {
t.Errorf("HibernatePolicy(%q).IsValid() = %v, want %v", c.in, got, c.want)
}
}
}

func TestConnTypeIsValid(t *testing.T) {
cases := []struct {
in ConnType
Expand Down Expand Up @@ -142,6 +159,14 @@ func TestEnumDefaults(t *testing.T) {
t.Errorf("set value should pass through, got %q", got)
}
})
t.Run("HibernatePolicy", func(t *testing.T) {
if got := HibernatePolicy("").Default(); got != HibernatePolicyRetain {
t.Errorf("empty default = %q, want %q", got, HibernatePolicyRetain)
}
if got := HibernatePolicyRelease.Default(); got != HibernatePolicyRelease {
t.Errorf("set value should pass through, got %q", got)
}
})
t.Run("Backend", func(t *testing.T) {
if got := Backend("").Default(); got != BackendCloudHypervisor {
t.Errorf("empty default = %q, want %q", got, BackendCloudHypervisor)
Expand Down
3 changes: 3 additions & 0 deletions meta/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ const (
AnnotationForcePull = "cocoonset.cocoonstack.io/force-pull"
// AnnotationCocoonSetGeneration carries the CocoonSet generation stamped at scheduling time.
AnnotationCocoonSetGeneration = "cocoonset.cocoonstack.io/generation"
// AnnotationHibernatedOnNode records the main agent's node at release-policy
// suspend; wake uses it as a preferred-affinity hint for a warm restore.
AnnotationHibernatedOnNode = "cocoonset.cocoonstack.io/hibernated-on-node"

// AnnotationVMID carries the runtime VM identifier vk-cocoon assigns after creation.
AnnotationVMID = "vm.cocoonstack.io/id"
Expand Down