Shared Go packages for cocoonstack services.
apis/v1-- typed CocoonSet and CocoonHibernation CRD Go types and generated CRD YAML manifestsmeta-- shared CRD identifiers, annotation/label/toleration keys, VM naming helpers, the typedVMSpec/VMRuntime/HibernateStateannotation contract, and pod-state helpers (IsPodReady,IsPodTerminal,IsContainerRunning,IsWindowsPod,PodKey,PodNodePool) every cocoon component sharesk8s-- Kubernetes client config bootstrap with the standard kubeconfig fallback chain, merge-patch helpers, env/duration/sleep helpers (EnvOrDefault,EnvDuration,EnvBool,SleepCtx), unstructured decoder, and TLS helpers (LoadOrGenerateCert,GenerateSelfSignedCert,DetectNodeIP)k8s/admission-- shared admission-webhook scaffolding (Allow/Denyresponses,Decode/Serverequest loop, RFC 6902JSONPatchOp+EscapeJSONPointerhelpers) used bycocoon-webhookand reusable by any future cocoonstack admission handlerlog-- common log setup for cocoonstack binaries usingprojecteru2/core/log
This repository keeps cross-project contracts in one place instead of re-exporting them from cocoon-operator. cocoon-operator, cocoon-webhook, and vk-cocoon all consume the same package set directly.
go get github.com/cocoonstack/cocoon-common@latestgit clone https://github.com/cocoonstack/cocoon-common.git
cd cocoon-common
make buildTyped Go definitions for the cocoonset.cocoonstack.io/v1 API
group, plus the generated CRD YAML manifests under
apis/v1/crds/. The package ships:
CocoonSet-- declarative spec for an agent cluster (main + sub-agents + toolboxes)CocoonHibernation-- per-pod hibernate / wake request
Downstream operators import these via go list -m and copy the CRD
YAML into their own kustomize tree (see make import-crds in
cocoon-operator). Regenerate via make generate manifests after any
type change.
Use meta for:
- Cocoon annotation, label, and CRD identifier constants
- stable VM naming helpers
- slot extraction and role inference
- toolbox connection type detection
All identifiers live under two cocoonstack.io subdomains:
| Prefix | Used for | Examples |
|---|---|---|
cocoonset.cocoonstack.io/ |
CocoonSet CRD group, Pod selector labels, and declarative fields mirrored from a CocoonSet spec onto a managed Pod | cocoonset.cocoonstack.io/v1, name, role, slot, mode, image, os, storage, snapshot-policy, network, managed |
vm.cocoonstack.io/ |
Runtime state observed about the VM backing a Pod | id, name, ip, vnc-port, hibernate, fork-from |
For typed annotation access, prefer the meta.VMSpec / meta.VMRuntime / meta.HibernateState wrappers over raw map manipulation:
// operator side: build the spec contract for vk-cocoon to consume.
// Managed=true means vk-cocoon owns this VM's lifecycle (Clone / Run /
// Remove); Managed=false tells vk-cocoon to adopt a pre-assigned VM
// (static toolboxes) using the VMRuntime hints below.
spec := meta.VMSpec{
VMName: "vk-prod-demo-0",
Image: "ghcr.io/cocoonstack/cocoon/ubuntu:24.04",
Mode: string(v1.AgentModeClone),
OS: string(v1.OSLinux),
SnapshotPolicy: string(v1.SnapshotPolicyAlways),
Managed: true,
}
spec.Apply(pod)
// vk-cocoon side: read the spec back and write the runtime contract.
// VMRuntime.VNCPort is intentionally asymmetric — cloud-hypervisor
// does not expose a VNC server, so vk-cocoon leaves it zero for
// every VM it brings up itself. Static (Managed=false) toolboxes
// carry a pre-seeded VMRuntime{VMID, IP, VNCPort} written by the
// operator from the CocoonSet spec at pod-build time.
runtime := meta.VMRuntime{VMID: vmID, IP: ip}
runtime.Apply(pod)
// hibernate / wake
meta.HibernateState(true).Apply(pod)Two snapshot tag constants anchor the cross-component contract:
meta.HibernateSnapshotTag("hibernate") — the OCI tag vk-cocoon pushes a hibernation snapshot under, and the tag the operator probes to detect that a hibernation has completed.meta.DefaultSnapshotTag("latest") — the tag vk-cocoon publishes routine (non-hibernate) VM snapshots under at pod-delete time, and the tag cocoon-operator garbage-collects when a CocoonSet is deleted.
meta.ShouldSnapshotVM(spec) is the single shared decoder for the
SnapshotPolicy / slot-index decision. vk-cocoon consults it on
the producer side (should I push this VM?) and cocoon-operator on
the GC side (should I delete this tag?) so the two cannot drift —
under main-only both sides agree only slot-0 is touched.
Use k8s.LoadConfig() to resolve cluster configuration from:
KUBECONFIG~/.kube/config- in-cluster config
Other helpers in this package:
k8s.EnvOrDefault,k8s.EnvDuration,k8s.EnvBool-- lenient env-var parsing that falls back to the supplied default on unset / malformed values.k8s.SleepCtx(ctx, d)-- context-aware sleep; returnsfalsewhen the context fires first so callers can exit retry loops without a secondselect.k8s.LoadOrGenerateCert/k8s.GenerateSelfSignedCert/k8s.DetectNodeIP-- TLS bring-up helpers used byvk-cocoonand reusable by any cocoonstack HTTP server that needs a dev-time self-signed fallback.k8s.StatusMergePatch/k8s.AnnotationsMergePatch-- merge-patch builders used by reconcilers that prefer the JSON merge-patch encoding overclient.MergeFrom.k8s.PatchStatus[T]-- genericclient.MergeFrompatch for the/statussubresource; captures the pre-mutation snapshot via the kubebuilder-generated typedDeepCopy()so callers skip the boilerplate.k8s.PatchHibernateState-- pod-level hibernate annotation patch that short-circuits when the pod already carries the desired state, safe to call unconditionally in a reconcile loop.k8s.NewReadyCondition/k8s.ConditionTypeReady-- canonicalReadycondition constructor shared across every cocoon CRD status block, leavingLastTransitionTimezero soapimeta.SetStatusConditionpreserves the existing timestamp on no-op updates.k8s.DecodeUnstructured[T]-- generic unstructured-to-typed converter.
Shared admission-webhook scaffolding. Example:
import commonadmission "github.com/cocoonstack/cocoon-common/k8s/admission"
mux.HandleFunc("/mutate", func(w http.ResponseWriter, r *http.Request) {
commonadmission.Serve(w, r, 0 /* default max body */, func(ctx context.Context, rev *admissionv1.AdmissionReview) *admissionv1.AdmissionResponse {
// ... your handler logic ...
return commonadmission.Allow()
})
})commonadmission.JSONPatchOp, commonadmission.MarshalPatch, and commonadmission.EscapeJSONPointer cover the RFC 6902 patch flow for mutating webhooks.
Use log.Setup(ctx, envVar) to initialize the shared logger from an environment variable, defaulting to info.
make build # build all packages
make test # run tests with coverage
make lint # run golangci-lint on linux + darwin
make fmt # format code
make generate # regenerate deepcopy methods for api types
make manifests # regenerate CRD YAML manifests for api types
make all # full pipeline: deps + generate + manifests + fmt + lint + test + build
make help # show all targetsAfter any change to apis/v1/*_types.go, run make generate manifests and commit the regenerated zz_generated.deepcopy.go and apis/v1/crds/*.yaml. CI rejects PRs that forget this step.
| Project | Role |
|---|---|
| cocoon-operator | CocoonSet and Hibernation controllers |
| cocoon-webhook | Admission webhook for sticky scheduling |
| epoch | Snapshot registry and storage backend |
| vk-cocoon | Virtual kubelet provider |