Skip to content

feat: export macOS VMs as OCI cloud images - #27

Closed
czmDeRepository wants to merge 5 commits into
cocoonstack:masterfrom
czmDeRepository:feat/oci-vm-export-pr
Closed

feat: export macOS VMs as OCI cloud images#27
czmDeRepository wants to merge 5 commits into
cocoonstack:masterfrom
czmDeRepository:feat/oci-vm-export-pr

Conversation

@czmDeRepository

@czmDeRepository czmDeRepository commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Summary

  • export a macOS VM root disk as a standalone OCI cloud-image artifact
  • add local cloud-image import/export for host-to-host transfer
  • expose the related image and vm export commands
  • preserve an existing output file when a local export fails
  • report remote publication explicitly when optional local retention fails

Lifecycle behavior

The destination is validated before the VM is stopped. A running VM is stopped under its lifecycle lock, flattened, and restarted before the registry upload begins, so upload latency does not extend downtime. A VM that was already stopped remains stopped.

Temporary files are synchronized before atomic replacement, and successful OCI publication is not misreported as a total failure if only the optional local retain step fails.

Merge ordering

#30 should land first. It makes a queued vm start idempotent when export has already restarted QEMU and restores a missing VNC proxy without restarting a healthy guest.

Validation

  • make lint
  • go test ./...

@CMGS CMGS left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stepping back: this verb inverts the stack's image model, and i don't think it should land in any form.

  • images here are immutable goldens minted OUTSIDE the runtime — cocoon-macos's come from the ci image pipeline (docs/image-pipeline.md: 'built entirely by CI'), cocoon's from docker-export → image import. the runtime consumes images; it doesn't produce them.
  • cocoon's image surface is list / import / pull / rm / inspect — deliberately no image export. what cocoon does have for moving vm-derived state between hosts is snapshot export / snapshot import (tar/dir, pairing with vm clone --from-dir). so both halves here break parity: vm export makes the runtime an image producer, image export adds a verb the base system chose not to have.
  • the mechanical value is thin anyway: image pull already consumes what plain oras push produces (pickQcow2Layer prefers the .qcow2-titled layer — 'what oras push writes'), so publishing a customized disk is a documented 4-liner:
cocoon-macos vm stop m1
qemu-img convert -O qcow2 -c $STATE/vms/m1/disk.qcow2 out.qcow2   # convert flattens the backing chain
cocoon-macos vm start m1 [--vnc ...]
oras push ghcr.io/team/macos-custom:v1 out.qcow2

no new artifact format, no lock orchestration, and the #30 merge-ordering dependency evaporates.

suggestion: close this and add the recipe to docs/images.md. if cross-host macos vm movement becomes a real need, the cocoon-parity shape is snapshot export/import, not minting goldens from vms.

(if some form survives anyway, the body's 'upload latency does not extend downtime' sidesteps that downtime = the whole qemu-img convert -c of a 15-30 GB chain inside the vm lock — see inline.)

Comment thread cmd/vm/export.go
}
}

return utils.RunQemuImg(ctx, "convert", "-p", "-f", "qcow2", "-O", "qcow2", "-c", r.Disk, tmpPath)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this runs inside the vm lock with the guest stopped — downtime is the full flatten+compress of the chain (minutes at 15-30 GB), not 'not the upload'. if the verb survives at all: freeze the delta instead — copy/reflink the overlay right after stop, restart immediately, convert the frozen copy outside the lock.

Comment thread cmd/image/oci.go
const pullConns = 8

const (
artifactTypeOSImage = "application/vnd.cocoonstack.os-image.v1+json"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

second artifact format for the same payload — image pull already consumes plain oras push output (pickQcow2Layer below: 'what oras push writes'). a custom artifactType/mediaType makes exported images the odd ones out next to the ci-published goldens.

Comment thread cmd/image/transfer.go
return nil
}

func (h *Handler) Export(cmd *cobra.Command, args []string) error {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cocoon's image surface has no export (list/import/pull/rm/inspect); vm-derived state moves via snapshot export/import there. no consumer, no counterpart — drop.

@CMGS CMGS closed this Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants