cocoon-common

API types

apis/v1 holds the typed Go definitions for the cocoonset.cocoonstack.io/v1 API group and the CRD YAML generated from them, under apis/v1/crds/.

Import path: github.com/cocoonstack/cocoon-common/apis/v1.

Resources

Kind Short names Purpose
CocoonSet cs, cocoonsets Declarative agent cluster: one main agent, agent.replicas sub-agents, and any number of toolbox VMs
CocoonHibernation ch, cocoonhibernations Per-pod hibernate / wake request

Both carry a status subresource and a Ready condition built by k8s.NewReadyCondition.

CocoonSetSpec

Field Default Notes
suspend false Suspends the whole set
snapshotPolicy always always / main-only / never
hibernatePolicy retain retain keeps the scheduling seat while suspended; release frees it once the hibernate snapshot is registry-verified
nodePool default Selected via the cocoonstack.io/pool node label
snapshotCompatibilityClass unset Certified guest-visible CPU ABI; rendered as a hard node selector
nodeName unset Pins the set to one node (cross-node migrate); empty leaves placement to the scheduler
agent required AgentSpec — image, replicas, mode, plus the shared VMOptions
toolboxes unset []ToolboxSpec — named auxiliary VMs

VMOptions is inlined into both AgentSpec and ToolboxSpec: os, backend, connType, network, forcePull, noDirectIO, probePort, storage, resources.

Toolbox names contain 1–63 characters and match ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$. The webhook additionally rejects duplicate or purely numeric names. Static toolboxes require staticIP and staticVMID at admission and are unmanaged; image is required for run/clone toolboxes.

Enum defaults

Spec enum types are strings with a +kubebuilder:validation:Enum marker and an IsValid() predicate. Types with defaults also provide Default() to resolve the empty string; a non-empty value is returned unchanged, so this method does not replace validation.

Type Values Default()
AgentMode clone, run clone
ToolboxMode run, clone, static run
OSType linux, windows, android, macos linux
SnapshotPolicy always, main-only, never always
HibernatePolicy retain, release retain
Backend cloud-hypervisor, firecracker cloud-hypervisor
ConnType ssh, rdp, vnc, adb none — empty infers it from the VNC port, then the OS
HibernationDesire Hibernate, Wake none

CocoonSetPhase and CocoonHibernationPhase are status-only enums with no IsValid() — the controller is their only writer.

For managed VMs, Backend: firecracker requires mode: run, boots a kernel directly, and accepts only OCI VM images. The webhook rejects clone mode, cloudimg URLs, and Windows guests. Static toolboxes skip backend/image checks because vk-cocoon does not create their VMs.

CEL rules inside the CRDs

Two rules ship in the generated CocoonSet CRD, so they hold for any client that never touches the Go types:

- 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))'
- message: snapshotCompatibilityClass is immutable once set
  rule: '!has(oldSelf.snapshotCompatibilityClass)
    || (has(self.snapshotCompatibilityClass)
    && self.snapshotCompatibilityClass == oldSelf.snapshotCompatibilityClass)'

Seat release is restricted to a main-only set until sub-agent and toolbox restore intent is defined. The compatibility class is immutable because changing it would invalidate every memory snapshot the set has already published.

CocoonHibernation adds one: spec.podRef is immutable.

Regeneration

zz_generated.deepcopy.go and apis/v1/crds/*.yaml are generated by controller-gen, pinned through hack/tools.go so no separate binary download is needed:

make generate    # deepcopy methods
make manifests   # CRD YAML

Run both after any change to apis/v1/*_types.go and commit the result. The lint workflow re-runs them and fails on a dirty tree, so a forgotten regeneration is caught in CI rather than in a downstream repo.

Field godoc becomes the CRD description and is what kubectl explain prints — it is API documentation, not a code comment, and removing it changes the generated YAML.

Downstream consumption

Consumers pin a release or commit in go.mod; changing cocoon-common alone does not update their binaries. A consumer must update its dependency to include a fix before rebuilding with it. A workspace can resolve a newer sibling checkout without changing that pin.

cocoon-operator also copies the CRD YAML into its kustomize tree with make import-crds. This reads the resolved module directory, so use GOWORK=off make import-crds to copy from the committed dependency version. Commit the copied config/crd/bases/*.yaml along with the dependency update; a Go module update alone does not update the manifests deployed to a cluster.