snapshot

package
v0.1.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 10, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package snapshot pushes and pulls cocoon VM snapshots as OCI artifacts.

A snapshot in epoch is an OCI 1.1 image manifest with artifactType `application/vnd.cocoonstack.snapshot.v1+json`. The manifest carries:

  • A config blob (mediaType vnd.cocoonstack.snapshot.config.v1+json) containing structured cocoon VM metadata (cpu, memory, image ref, ...).
  • One layer blob per file inside `cocoon snapshot export -o -` tar output, with the OCI standard `org.opencontainers.image.title` annotation set to the original filename so the puller can reassemble the tar and feed it to `cocoon snapshot import`.
  • Optional `cocoonstack.snapshot.baseimage` annotation when the operator supplies one with `epoch push --base-image <ref>`.

epoch never reads cocoon's filesystem directly. Push streams the snapshot from `cocoon snapshot export <name> -o -` stdout; pull writes back via `cocoon snapshot import <name>` stdin. The cocoon binary is the only point of contact between epoch and cocoon's local storage layout.

Index

Constants

View Source
const (
	// CocoonBinaryEnv lets users override the cocoon binary used by epoch's
	// snapshot push/pull (defaults to looking up `cocoon` on $PATH).
	CocoonBinaryEnv = "EPOCH_COCOON_BINARY"
)

Variables

This section is empty.

Functions

func FetchSnapshotConfig

func FetchSnapshotConfig(ctx context.Context, dl Downloader, name string, desc manifest.Descriptor) (*manifest.SnapshotConfig, error)

FetchSnapshotConfig downloads and parses the snapshot config blob referenced by the given descriptor. Exported so the in-process tag-detail API can reuse it via the same Downloader adapter that snapshot.StreamParsed already uses, instead of duplicating the parse/stream/decode dance.

Returns an error when desc.MediaType is not manifest.MediaTypeSnapshotConfig — that should never happen for a tag the catalog has classified as a snapshot, so callers can treat it as corrupt state and surface it.

func ResolveCocoonBinary

func ResolveCocoonBinary(envValue string) (string, error)

ResolveCocoonBinary picks the cocoon binary path from $EPOCH_COCOON_BINARY, falling back to looking up `cocoon` on $PATH. Returns an error if neither is reachable.

func Stream

func Stream(ctx context.Context, raw []byte, dl Downloader, opts StreamOptions) error

Stream classifies the manifest as a snapshot, fetches its config blob, and writes the cocoon-import tar (snapshot.json envelope + every layer entry) to opts.Writer. It is the function shared between snapshot.Puller (which writes to cocoon's stdin) and epoch's server-side /dl/{name} handler (which writes to the HTTP response body).

Stream does not invoke cocoon — it only reassembles the tar. The caller is responsible for piping the bytes to wherever they should land.

func StreamParsed

func StreamParsed(ctx context.Context, m *manifest.OCIManifest, dl Downloader, opts StreamOptions) error

StreamParsed is the same as Stream but accepts an already-parsed manifest. Callers that have already classified and parsed the manifest (e.g. the /dl/ handler) use this to avoid a redundant JSON unmarshal.

Types

type CocoonRunner

type CocoonRunner interface {
	// Export runs `cocoon snapshot export <name> -o -` and returns a reader
	// for the resulting tar stream plus a wait function the caller invokes
	// once the stream is fully consumed.
	Export(ctx context.Context, name string) (io.ReadCloser, func() error, error)

	// Import runs `cocoon snapshot import <name>` (with optional --description)
	// and returns a writer for the tar stream plus a wait function. The caller
	// closes the writer to signal end-of-stream and then calls wait.
	Import(ctx context.Context, opts ImportOptions) (io.WriteCloser, func() error, error)
}

CocoonRunner abstracts how snapshot.Pusher and snapshot.Puller talk to the local `cocoon` CLI. The default implementation ExecCocoon runs `cocoon` as a subprocess; tests can substitute a fake.

type Downloader

type Downloader interface {
	GetManifest(ctx context.Context, name, tag string) ([]byte, string, error)
	GetBlob(ctx context.Context, name, digest string) (io.ReadCloser, error)
}

Downloader is the registry-side surface needed by Puller.

type ExecCocoon

type ExecCocoon struct {
	// Binary is the resolved cocoon binary path. Use [ResolveCocoonBinary]
	// to populate it from $EPOCH_COCOON_BINARY or $PATH.
	Binary string
	// Stderr receives the cocoon subprocess stderr. Defaults to os.Stderr
	// when nil.
	Stderr io.Writer
}

ExecCocoon is the default CocoonRunner backed by an actual `cocoon` binary on $PATH (or the path in $EPOCH_COCOON_BINARY).

func (*ExecCocoon) Export

func (e *ExecCocoon) Export(ctx context.Context, name string) (io.ReadCloser, func() error, error)

Export runs `cocoon snapshot export <name> -o -` and returns its stdout stream and a wait function.

func (*ExecCocoon) ImageImport

func (e *ExecCocoon) ImageImport(ctx context.Context, name string) (io.WriteCloser, func() error, error)

ImageImport runs `cocoon image import <name>` and returns its stdin pipe and a wait function. Used by the cloudimg package; lives here so the snapshot package owns the single `cocoon` exec helper.

func (*ExecCocoon) Import

func (e *ExecCocoon) Import(ctx context.Context, opts ImportOptions) (io.WriteCloser, func() error, error)

Import runs `cocoon snapshot import <name>` and returns its stdin pipe and a wait function. The caller writes the tar stream, then closes the pipe, then calls wait.

type ImportOptions

type ImportOptions struct {
	Name        string
	Description string
}

ImportOptions configures `cocoon snapshot import`.

type PullOptions

type PullOptions struct {
	// Name is the OCI repository name. Required.
	Name string
	// Tag is the OCI tag. Defaults to "latest".
	Tag string
	// LocalName overrides the cocoon-side snapshot name on import.
	// Empty means reuse Name.
	LocalName string
	// Description is forwarded to `cocoon snapshot import --description`.
	Description string
	// Progress receives one-line status updates. May be nil.
	Progress func(string)
}

PullOptions configures a snapshot pull.

type Puller

type Puller struct {
	Downloader Downloader
	Cocoon     CocoonRunner
}

Puller fetches a snapshot OCI artifact and pipes it into `cocoon snapshot import`.

func (*Puller) Pull

func (p *Puller) Pull(ctx context.Context, opts PullOptions) error

Pull downloads a snapshot artifact from the registry, reassembles its layers into a tar stream, and feeds it to `cocoon snapshot import`.

Errors during streaming close the cocoon stdin pipe so the import process observes EOF and exits cleanly; the original streaming error wins over the downstream wait error.

type PushOptions

type PushOptions struct {
	// Name is the OCI repository name. Required.
	Name string
	// Tag is the OCI tag. Defaults to "latest".
	Tag string
	// BaseImage is the optional cocoonstack.snapshot.baseimage annotation
	// value (an OCI ref like "ghcr.io/cocoonstack/cocoon/ubuntu:24.04").
	// When empty the annotation is omitted.
	BaseImage string
	// Source identifies the producer in the manifest annotations
	// (org.opencontainers.image.source). Optional.
	Source string
	// Revision is the org.opencontainers.image.revision annotation. Optional.
	Revision string
	// Progress receives one-line status updates. May be nil.
	Progress func(string)
}

PushOptions configures a snapshot push.

type PushResult

type PushResult struct {
	Name           string
	Tag            string
	ManifestDigest string // sha256:<hex>
	ManifestBytes  []byte
	TotalSize      int64
	LayerCount     int
}

PushResult summarizes a successful snapshot push.

type Pusher

type Pusher struct {
	Uploader Uploader
	Cocoon   CocoonRunner
}

Pusher uploads cocoon VM snapshots into an OCI registry.

func (*Pusher) Push

func (p *Pusher) Push(ctx context.Context, opts PushOptions) (*PushResult, error)

Push streams a snapshot from `cocoon snapshot export` and writes an OCI snapshot artifact to the registry.

The data flow is:

  1. start `cocoon snapshot export <name> -o -`
  2. read tar entries from its stdout, hashing each one to a temp file and uploading as a content-addressable blob
  3. translate the snapshot.json envelope into a snapshot config blob (mediaType vnd.cocoonstack.snapshot.config.v1+json) and upload it
  4. assemble an OCI image manifest with artifactType vnd.cocoonstack.snapshot.v1+json and PUT it under name:tag

type StreamOptions

type StreamOptions struct {
	// Name is the OCI repository name used to fetch layer blobs. Required.
	Name string
	// LocalName overrides the snapshot name written into the rebuilt
	// snapshot.json envelope. Empty falls back to Name so the importer
	// reuses the registry repository name.
	LocalName string
	// Writer is the destination for the cocoon-import-shaped tar stream.
	// Required.
	Writer io.Writer
	// Progress receives one-line status updates per layer. May be nil.
	Progress func(string)
}

StreamOptions configures Stream.

type Uploader

type Uploader interface {
	BlobExists(ctx context.Context, name, digest string) (bool, error)
	PutBlob(ctx context.Context, name, digest string, body io.Reader, size int64) error
	PutManifest(ctx context.Context, name, tag string, data []byte, contentType string) error
}

Uploader is the registry-side surface needed by Pusher. The HTTP `registryclient.Client` satisfies this interface; an in-process implementation could too.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL