Documentation
¶
Index ¶
- Constants
- Variables
- func BlobExists(imagesDir, digest string) bool
- func BlobPath(imagesDir, digest string) (string, error)
- func BlobSize(imagesDir, digest string) int64
- func BlobsRoot(imagesDir string) string
- func CacheLowerPath(imagesDir, manifestDigest string) (string, error)
- func CacheLowerSize(imagesDir, manifestDigest string) int64
- func DeleteBlob(imagesDir, digest string) error
- func DeleteTag(imagesDir, tag string) error
- func DeriveTagFromRef(ref string) string
- func DigestBytes(data []byte) string
- func DigestReader(r io.Reader) (string, int64, error)
- func EnsureOCILayout(imagesDir string) error
- func FindDigestBySourceRef(imagesDir, ref string) (digest string, ok bool)
- func HashFile(path string) (string, error)
- func HexDigest(digest string) string
- func IndexManifestDigests(imagesDir string) (map[string]bool, error)
- func IndexRemoveByDigest(imagesDir, digest string) error
- func InstallSyntheticImage(imagesDir, tagName, sourceRef string, ...) (string, error)
- func IsDockerRef(s string) bool
- func ListBlobs(imagesDir string) ([]string, error)
- func ListTags(imagesDir string) ([]string, error)
- func MergeLayers(ctx context.Context, imagesDir string, layerDigests []string, ...) error
- func MergeLayersFromManifest(ctx context.Context, imagesDir, manifestDigest, outTarPath string) error
- func MintRootfsErofs(ctx context.Context, opts MintErofsOptions) (string, error)
- func OpenBlob(imagesDir, digest string) (*os.File, error)
- func PlatformOCI(s string) (*v1.Platform, error)
- func PushFromOCILayout(ctx context.Context, opts PushOptions) error
- func ReadBlob(imagesDir, digest string) ([]byte, error)
- func RefIndexDeleteByDigest(imagesDir, digest string)
- func RefIndexGet(imagesDir, ref string) (digest string, ok bool)
- func RefIndexPut(imagesDir, ref, digest string) error
- func RefIndexReverse(imagesDir string) map[string]string
- func Resolve(imagesDir, tag, expectedRef string) string
- func ResolveTag(imagesDir, tag string) (digest, rootfsPath string, err error)
- func SetTag(imagesDir, tag, digest string) error
- func ShortDigest(digest string) string
- func SyntheticDigestFromBytes(content []byte) string
- func SyntheticDigestFromString(s string) string
- func TagPath(imagesDir, tag string) (string, error)
- func TagsRoot(imagesDir string) string
- func TryAcquireFileLock(path string) (release func(), held bool, err error)
- func TryAcquireFileLockBlocking(path string) (func(), error)
- func ValidateImageName(name string) error
- func WriteBlob(imagesDir, expectedDigest string, data []byte) (string, error)
- func WriteBlobFromFile(imagesDir, srcPath string, consume bool) (digest, blobPath string, err error)
- func WriteIndex(imagesDir string, idx *OCIIndex) error
- type ConvertOptions
- type ConvertResult
- type Descriptor
- type EnsureResult
- type ImageConfig
- type ImageInfo
- type LayerInfo
- type Manager
- func (m *Manager) DeleteImage(ident string) error
- func (m *Manager) EnsureImage(ctx context.Context, ref ResolvedRef, progress ProgressFunc) (EnsureResult, error)
- func (m *Manager) ImageHistory(tagOrDigest string) ([]LayerInfo, error)
- func (m *Manager) InspectImage(tagOrDigest string) (*ImageInfo, *OCIManifest, error)
- func (m *Manager) ListImages() ([]ImageInfo, error)
- func (m *Manager) LoadImage(in io.Reader) ([]string, error)
- func (m *Manager) PruneImages(dryRun bool) ([]ImageInfo, error)
- func (m *Manager) PullImage(ctx context.Context, dockerRef, tag, platform string, withLayers bool, ...) (string, error)
- func (m *Manager) PushImage(ctx context.Context, tagOrDigest, dstRef string, progress ProgressFunc) error
- func (m *Manager) ResolveImageBlobs(manifestDigest string) (manifest *OCIManifest, kernelPath, initrdPath string, err error)
- func (m *Manager) ResolveManifestLower(ctx context.Context, manifestDigest string) (string, error)
- func (m *Manager) SaveImage(tagOrDigest string, out io.Writer) error
- func (m *Manager) TagImage(srcTagOrDigest, newTag string) error
- type MintErofsOptions
- type OCIConfig
- type OCIConfigConfig
- type OCIHistory
- type OCIIndex
- type OCILayoutMarker
- type OCIManifest
- type OCIRootFS
- type ProgressEvent
- type ProgressFunc
- type PullOptions
- type PullPolicy
- type PullResult
- type PushOptions
- type RefKind
- type RefScanner
- type Reference
- type ResolvedRef
- type Tag
Constants ¶
const ( MediaTypeOCIIndex = "application/vnd.oci.image.index.v1+json" MediaTypeOCIManifest = "application/vnd.oci.image.manifest.v1+json" MediaTypeOCIConfig = "application/vnd.oci.image.config.v1+json" MediaTypeOCILayer = "application/vnd.oci.image.layer.v1.tar+gzip" // MediaTypeShedKernel and MediaTypeShedInitrd are shed-specific blob // types carried alongside the standard rootfs layer. Foreign OCI // tools see them as generic blobs and skip them; shed reads them as // the kernel/initrd to boot the VM. MediaTypeShedKernel = "application/vnd.shed.kernel" MediaTypeShedInitrd = "application/vnd.shed.initrd" )
OCI media types used by shed. We standardize on the OCI variants (application/vnd.oci.*) rather than the legacy Docker variants — both reference implementations interoperate, but OCI is the spec.
const ( // AnnotationVariant marks the variant of a shed image // (e.g. "base", "extensions", "full"). Display-only. AnnotationVariant = "io.shed.variant" // AnnotationKernelDigest names the blob digest of the kernel // associated with this image, when shed extracted one. AnnotationKernelDigest = "io.shed.kernel.digest" // AnnotationInitrdDigest names the blob digest of the initrd // associated with this image, when shed extracted one. AnnotationInitrdDigest = "io.shed.initrd.digest" // AnnotationRootfsErofsDigest names the blob digest of the // flattened rootfs erofs that shed minted at image-build time // (using a pinned mkfs.erofs from the shed-build-tools image). // When present, the host skips local mkfs.erofs entirely and // mounts this blob directly as the read-only lower; when absent, // the host rejects the image (no on-host fallback in the v0.5.2+ // layout — see internal/vmimage/manager.go for the reject path). AnnotationRootfsErofsDigest = "io.shed.rootfs.erofs.digest" // AnnotationSchemaVersion records shed's own manifest schema version // alongside OCI's schemaVersion (always 2 for OCI manifests). Lets // us bump shed semantics independently of OCI. AnnotationSchemaVersion = "io.shed.schema-version" // AnnotationSourceRef preserves the registry ref the image was // pulled from or built against. Useful for cache freshness checks. AnnotationSourceRef = "io.shed.source-ref" // AnnotationRootfsLogicalSize records the logical (sparse) byte // size of the derived rootfs ext4. Display-only. AnnotationRootfsLogicalSize = "io.shed.rootfs.logical-size" )
Shed-specific manifest annotations. These live alongside standard OCI annotations (org.opencontainers.image.*) in the manifest's annotations map and on per-descriptor annotations.
const ( BlobStatusDownloading = "downloading" // a byte tick or the initial 0-byte start BlobStatusExists = "exists" // blob already present in the store BlobStatusDone = "done" // fully fetched + verified (Current == Total) )
Blob status values for ProgressEvent.Status when Kind=="blob". These string values cross the wire (the backend forwards Status verbatim), so they are the single source of truth for both sides.
const CacheLowerExt = ".erofs"
CacheLowerExt is the file extension used by v0.5.1's derived lower-image cache. Kept so `PruneImages` can recognize and sweep stale entries during the post-upgrade window.
const CurrentOCILayoutVersion = "1.0.0"
CurrentOCILayoutVersion is the OCI image-layout version shed writes.
const DefaultPlatform = "linux/arm64"
DefaultPlatform is the Docker platform used for VZ images (Apple Silicon).
const DigestPrefix = "sha256:"
DigestPrefix is the algorithm prefix for image digests. Always "sha256:".
const FirecrackerPlatform = "linux/amd64"
FirecrackerPlatform is the Docker platform used for Firecracker images (x86_64 Linux).
const MaxLayers = 16
MaxLayers caps the number of layers a shed image manifest may list. Bounds both the Firecracker drive count (1 upper + N lowers ≤ 17, well under FC's 26-drive limit) and overlayfs lowerdir= argument length (each layer adds a path; PAGE_SIZE ~4096 caps the total).
const ShedSchemaVersion = "1"
ShedSchemaVersion is the current shed-specific manifest schema version. Bumped when the annotation contract changes.
Variables ¶
var ( // ErrBlobNotFound is returned when a digest is not present in the store. ErrBlobNotFound = errors.New("blob not found") // ErrTagNotFound is returned when a tag does not exist. ErrTagNotFound = errors.New("tag not found") // ErrInvalidDigest is returned when a digest string is malformed. ErrInvalidDigest = errors.New("invalid digest") // ErrInvalidTag is returned when a tag name is unsafe. ErrInvalidTag = errors.New("invalid tag name") // ErrLegacyBundledBlob is returned when a blob path is occupied by a // v0.4.x bundled directory layout (a directory holding manifest.json, // kernel, initrd, rootfs.ext4) instead of the v0.5.0+ flat-file OCI // blob. The caller should surface the wrapped message so the user // knows to wipe the legacy store. See ocilayout.go ReadBlob for the // detection step. ErrLegacyBundledBlob = errors.New("legacy v0.4.x bundled blob layout") )
Sentinel errors for blob/tag operations.
var ( // ErrImageNotFound is returned when a tag does not exist. ErrImageNotFound = errors.New("image not found") // ErrImageInUse is returned when trying to delete an image referenced // by config or an existing shed. ErrImageInUse = errors.New("image is in use") // ErrLayersMissing is returned when an operation needs layer blobs that // aren't present locally — e.g. pushing an image that was pulled // boot-only. Re-pull with --with-layers to recover. ErrLayersMissing = errors.New("image layers not present locally") )
Sentinel errors for image operations. Backend wrappers map these to config sentinel errors (e.g., config.ErrImageNotFoundSentinel).
var ErrPullDisabled = errors.New("image not present locally and pull_policy=never")
ErrPullDisabled is returned when pull_policy=never and the configured ref is not present in the local store. Callers should surface this as a client-actionable error (4xx), not an internal failure.
Functions ¶
func BlobExists ¶
BlobExists reports whether a blob is present in the store.
func BlobPath ¶
BlobPath returns the on-disk path of a blob keyed by digest. digest must be of the form "sha256:<hex>".
func CacheLowerPath ¶
CacheLowerPath returns the on-disk path a v0.5.1 install would have used for a manifest's flattened lower. manifestDigest must be of the form "sha256:<hex>". v0.5.2+ never writes to this path.
func CacheLowerSize ¶
CacheLowerSize returns the on-disk size of the legacy cache file for a manifest, or 0 if absent (the common case post-v0.5.2). Reports actual allocated blocks (st_blocks × 512), not the sparse file logical length, so `shed image ls` SIZE reads true on-disk usage during the upgrade window.
func DeleteBlob ¶
DeleteBlob removes a blob from the store. Caller is responsible for refcount checks. Returns ErrBlobNotFound if missing.
func DeriveTagFromRef ¶ added in v0.6.0
DeriveTagFromRef reduces a Docker ref to a short, filesystem-safe cosmetic name (e.g. ghcr.io/charliek/shed-vz-full:v0.5.9 -> "full"). It is NOT a unique identity — multiple ref versions collapse to the same name — so it is only used as a lock-adjacent label. Resolution identity is the full ref via the ref-index.
func DigestBytes ¶
DigestBytes computes the sha256 digest of an in-memory byte slice, formatted as "sha256:<hex>".
func DigestReader ¶
DigestReader streams a reader through sha256, returning the digest of the consumed bytes. Reader is fully drained.
func EnsureOCILayout ¶
EnsureOCILayout creates the OCI image-layout markers at imagesDir if they don't already exist. Idempotent.
func FindDigestBySourceRef ¶ added in v0.6.0
FindDigestBySourceRef scans installed manifests for one whose io.shed.source-ref equals ref, returning its digest. This is a READ-ONLY, O(N-manifests) fallback for the cold paths (rm, prune protection) where a ref may not have a sidecar entry yet (e.g. images present from before the ref-index existed). It deliberately does NOT write the sidecar: the create hot path resolves sidecar-only, so a manifest left behind by `shed image rm` (blob persists until prune, Docker model) must not be silently re-cached for create — `rm` then `create` should re-pull.
func HashFile ¶
HashFile computes the sha256 digest of a file's contents. Returns a digest string of the form "sha256:<hex>".
func HexDigest ¶
HexDigest returns the hex portion of a digest, panicking on malformed input. For test use only.
func IndexManifestDigests ¶
IndexManifestDigests reads index.json and returns the set of manifest digests recorded there. This is the cheap way to discover which blobs are manifests without probe-reading every blob.
Returns an empty set if index.json is missing or unreadable — callers should treat that as "no known manifests" and may fall back to a more expensive scan if they require completeness.
func IndexRemoveByDigest ¶
IndexRemoveByDigest drops a manifest entry from index.json. Called by PruneImages after deleting a manifest blob so foreign tools don't see a stale descriptor pointing at a missing blob.
func InstallSyntheticImage ¶
func InstallSyntheticImage(imagesDir, tagName, sourceRef string, rootfsContent, kernelContent, initrdContent []byte) (string, error)
InstallSyntheticImage writes a faux OCI image into imagesDir using the provided rootfsContent bytes as the layer content. Tags the resulting manifest at tagName.
The synthetic image consists of:
- one layer blob (the raw rootfsContent — not actually gzipped, but stored at a sha256-keyed blob path so the OCI shape is correct)
- one config blob (minimal OCI image config)
- one manifest blob with the standard annotations
- optional kernel + initrd blobs referenced by manifest annotations
- a tag pointing at the manifest digest
Returns the manifest digest. Used by tests that previously called InstallBlob with synthetic content.
func IsDockerRef ¶
IsDockerRef returns true if s is a Docker image reference rather than a filesystem path.
func MergeLayers ¶
func MergeLayers(ctx context.Context, imagesDir string, layerDigests []string, outTarPath string) error
MergeLayers is the manifest-less variant of MergeLayersFromManifest used during image build, where we have the layer digests in hand but the shed-annotated manifest hasn't been minted yet. layerDigests must be in OCI order (lowest at index 0).
func MergeLayersFromManifest ¶
func MergeLayersFromManifest(ctx context.Context, imagesDir, manifestDigest, outTarPath string) error
MergeLayersFromManifest writes a flattened tar of the manifest's layers to outTarPath. Returns an error if the manifest is missing, has zero layers, or any layer blob fails to decode.
func MintRootfsErofs ¶
func MintRootfsErofs(ctx context.Context, opts MintErofsOptions) (string, error)
MintRootfsErofs flattens layerDigests, runs mkfs.erofs inside the build-tools container, and installs the resulting erofs as a content-addressed blob in imagesDir. Returns the blob digest (sha256:...) — caller stamps this into the manifest's io.shed.rootfs.erofs.digest annotation.
The mkfs.erofs invocation pins:
- `-b 4096`: erofs block size. Host page sizes vary (4 KiB on Linux/amd64, 16 KiB on Apple Silicon); fixing the block size keeps the same erofs mountable in both contexts. The guest kernel expects 4 KiB.
- `-z lz4`: lz4 compression. Random-read friendly, ~50% on-disk reduction vs raw, decompression is cheap enough that the boot path doesn't measurably slow down.
- `-E force-inode-compact`: 32-byte inodes (vs the default 64-byte extended layout). Saves disk for image rootfs's thousands of files. The 1.7.x writer bug that motivated this whole change was an interaction between this flag and big pcluster headers; mkfs.erofs 1.8+ (which ships in shed-build-tools) fixes it.
- `-T 0`: zeroes the per-inode mtime field. Without this the erofs digest would vary by clock skew on the build host, breaking content addressing for byte-for-byte identical rootfs content.
Changing any of these flags changes the produced digest. The shed-build-tools image is versioned in lockstep with shed-server so a flag change rides a known release.
func PlatformOCI ¶
PlatformOCI parses a Docker-style platform string ("linux/arm64") into a go-containerregistry v1.Platform. Returns an error for malformed input (missing os/arch, extra components, or empty tokens).
func PushFromOCILayout ¶
func PushFromOCILayout(ctx context.Context, opts PushOptions) error
PushFromOCILayout uploads the on-disk manifest + config + layers to the destination registry. Layer bytes are streamed straight from the OCI store so the registry digests are preserved end-to-end (the byte-perfect push guarantee). Shed-specific kernel/initrd blobs are also uploaded when the manifest carries the corresponding annotations, so other shed instances can pull them back without re-extracting from rootfs.
func RefIndexDeleteByDigest ¶ added in v0.6.0
func RefIndexDeleteByDigest(imagesDir, digest string)
RefIndexDeleteByDigest removes every ref-index entry pointing at digest. Called by prune/rm after a manifest blob is deleted so a later resolve can't hit a dangling entry. Best-effort: scan errors are logged, not fatal.
func RefIndexGet ¶ added in v0.6.0
RefIndexGet returns the cached manifest digest for ref as a VALIDATED hit: the sidecar entry exists, its stored ref matches the lookup ref (guarding against a sha256 key collision), and the manifest blob is still present. On any validation failure it removes the stale entry and reports a miss (ok=false), so the caller's pull_policy then governs.
Identity here is the ref the image was PULLED BY (the index key), NOT the manifest's io.shed.source-ref annotation — those legitimately differ when the configured ref is a mutable tag (:latest), a digest pin, or a mirror host, while the annotation records the immutable publish ref. Validating against the annotation would delete the entry EnsureImage just wrote.
Blob-completeness beyond the manifest digest (erofs/kernel/initrd) is validated by resolveManifestLower at the call site.
func RefIndexPut ¶ added in v0.6.0
RefIndexPut records ref -> manifest digest in the sidecar ref-index. It is the final commit step of a successful pull/build: callers MUST invoke it only after the manifest, config, layers, kernel/initrd/erofs, and index.json are durable, so a crash can never leave the index pointing at an incomplete digest. The write itself is atomic (temp+rename).
func RefIndexReverse ¶ added in v0.6.0
RefIndexReverse returns a digest -> ref map built from all sidecar entries (first entry wins when several refs share a digest). Read-only; used by `ls`/`inspect` so an image is displayed by the ref it was pulled by, not the manifest's publish-time io.shed.source-ref (which differs for mutable tags, digest pins, and mirrors).
func Resolve ¶
Resolve looks up a tag and returns the path to the prebuilt rootfs erofs blob referenced by the manifest's io.shed.rootfs.erofs.digest annotation, when the tag exists, the manifest is installed, and (when expectedRef is non-empty) the source-ref annotation matches expectedRef. Returns "" otherwise (cache miss, manifest missing annotation, or expectedRef mismatch).
func ResolveTag ¶
ResolveTag looks up a tag and returns its manifest digest plus the path to the manifest's prebuilt rootfs erofs blob. Returns ErrTagNotFound or ErrBlobNotFound on miss; returns a clear error when the manifest lacks the v0.5.2+ io.shed.rootfs.erofs.digest annotation.
func SetTag ¶
SetTag atomically points <tag> at <digest>. The digest does not need to exist (caller's responsibility — useful for record-only flows like recording the original ref of a snapshot whose blob has been pruned).
func ShortDigest ¶
ShortDigest returns the first 12 hex chars of a digest for display.
func SyntheticDigestFromBytes ¶
SyntheticDigestFromBytes returns a sha256 digest of the given content formatted as "sha256:<hex>". Used by tests that need a deterministic digest without writing to disk.
func SyntheticDigestFromString ¶
SyntheticDigestFromString is a convenience wrapper for tests using string literals to generate stable digests.
func TryAcquireFileLock ¶
TryAcquireFileLock attempts a non-blocking exclusive flock on path. Returns held=true with a no-op release if the lock file does not exist.
func TryAcquireFileLockBlocking ¶
TryAcquireFileLockBlocking takes an exclusive flock on path and blocks until it's available. Use this from tests that need to simulate a live conversion holding the lock.
func ValidateImageName ¶
ValidateImageName validates that an image name is safe for filesystem operations.
func WriteBlob ¶
WriteBlob installs `data` as a blob in the store. The provided expectedDigest is verified against sha256(data). Returns the blob path. Idempotent: if the blob is already present, returns success without rewriting.
func WriteBlobFromFile ¶
func WriteBlobFromFile(imagesDir, srcPath string, consume bool) (digest, blobPath string, err error)
WriteBlobFromFile installs the contents of srcPath as a blob in the store. The STAGED bytes are hashed (not the source) so a concurrent writer to srcPath can't end up with the staged content stored under a stale digest. If consume is true, srcPath is renamed into the staging path (saving a copy); otherwise it is copied and srcPath is preserved.
func WriteIndex ¶
WriteIndex atomically writes the top-level OCI index.
Types ¶
type ConvertOptions ¶
type ConvertOptions struct {
// OCIArchivePath is the path to an OCI image-layout tar produced
// by `docker buildx build --output type=oci,dest=...`. Required.
OCIArchivePath string
// DockerRef is the image reference being converted (e.g.
// ghcr.io/.../shed-vz-full:v1.0.0). Recorded verbatim in the
// io.shed.source-ref manifest annotation so the server's
// resolveImage cache-hit check (compares the annotation to the
// configured ref) matches on subsequent pulls. Not used to fetch
// content — that's the OCI archive's job.
DockerRef string
// Name is the variant name (e.g. "full"); recorded as io.shed.variant.
Name string
// ImagesDir is the OCI image-layout root; blobs are written under
// {ImagesDir}/blobs/sha256/.
ImagesDir string
// Platform is the Docker platform (e.g., "linux/arm64"). Defaults to DefaultPlatform.
Platform string
// ExtractKernel controls whether the kernel should be extracted and
// written as a shed-typed blob referenced by the manifest annotation.
ExtractKernel bool
// NeedsInitrd controls whether an initrd should be extracted alongside
// the kernel. Only consulted when ExtractKernel is true. When set,
// either InitrdSourcePath must be provided OR the Ubuntu-style
// /boot/initrd.img-* will be extracted from the image (a fallback
// useful for ad-hoc Dockerfiles; not appropriate for shed images
// that need the shed-overlay initramfs to assemble overlayfs at boot).
NeedsInitrd bool
// InitrdSourcePath, when set, points at a pre-built initrd file (on
// the host) that shed should install as the image's boot initrd
// instead of extracting one from the rootfs. The shed build flow
// builds the shed-overlay initramfs separately via build-initramfs.sh
// and passes the path here so the resulting image boots through
// shed's overlay assembly path rather than Ubuntu's regular initrd.
InitrdSourcePath string
// BuildToolsRef, when set, triggers MintRootfsErofs after the
// layer blobs are installed: a docker container running mkfs.erofs
// from this image flattens the layers and produces the
// io.shed.rootfs.erofs.digest blob. The shed publish workflow pins
// this to ghcr.io/charliek/shed-build-tools:<current shed tag>;
// local `shed image build` runs default to the same pin via
// cmd/shed/image.go's BuildToolsRef flag (see --build-tools-version).
// When empty, no erofs is minted — the resulting image will be
// rejected by v0.5.2+ servers at EnsureImage. Empty is only
// appropriate for tests or for images that will be re-processed
// by a later mint pass.
BuildToolsRef string
}
ConvertOptions configures an OCI-archive ingestion.
OCIArchivePath is the only input mode — Convert reads the OCI image-layout tar, copies its layer + config blobs into the local OCI image-layout, extracts kernel/initrd, mints the rootfs erofs when BuildToolsRef is set, and writes a shed-annotated manifest. The docker image's layer structure is preserved across variants (extensions and full share base's layers in the on-disk store).
type ConvertResult ¶
type ConvertResult struct {
// ManifestDigest is the OCI image manifest digest. Used as the
// image identity in the tag store.
ManifestDigest string
// ConfigDigest is the OCI image config digest.
ConfigDigest string
// LayerDigests lists the OCI layer descriptor digests (one per
// layer). Phase 1 always produces exactly one entry; layered
// conversions land in later phases.
LayerDigests []string
// KernelDigest names the shed-typed kernel blob (if extracted).
KernelDigest string
// InitrdDigest names the shed-typed initrd blob (if extracted).
InitrdDigest string
// RootfsErofsDigest names the prebuilt rootfs erofs blob (only
// populated when ConvertOptions.BuildToolsRef was non-empty).
RootfsErofsDigest string
// RootfsLogicalSize records the uncompressed tar size in bytes.
RootfsLogicalSize int64
}
ConvertResult holds the digests produced by a successful conversion.
func Convert ¶
func Convert(ctx context.Context, opts ConvertOptions) (*ConvertResult, error)
Convert dispatches between two input modes:
- OCIArchivePath: ingest a buildx OCI tar, preserving its layer structure. Shared layers (extensions FROM base) end up referenced by both manifests with identical digests in the store, so disk usage scales with unique-deltas rather than full-rootfs-per-tag.
- DockerRef: flatten the named local docker daemon image to a single layer via docker create + docker export. Kept for the pull-fallback case where a ref is in the daemon but not on a registry.
The caller advances the tag (SetTag) after Convert returns; this keeps Convert pure with respect to tag indirection.
type Descriptor ¶
type Descriptor struct {
MediaType string `json:"mediaType"`
Digest string `json:"digest"`
Size int64 `json:"size"`
Annotations map[string]string `json:"annotations,omitempty"`
}
Descriptor is the OCI content-descriptor pointing at a blob by digest.
type EnsureResult ¶
type EnsureResult struct {
Path string // path to the cached lower image (manifest-digest-keyed erofs)
Digest string // OCI manifest digest
}
EnsureResult is what EnsureImage returns: the path to the content-addressed lower image for the resolved manifest, and the manifest digest itself.
type ImageConfig ¶
type ImageConfig interface {
GetDefaultImage() string // ref (or path) for new sheds when no --image
GetImageAliases() map[string]string // alias -> ref convenience map
GetPullPolicy() string // "missing" (default) | "always" | "never"
GetImagesDir() string
GetPlatform() string // "linux/arm64" or "linux/amd64"
GetExtractKernel() bool // true for both VZ and Firecracker
GetNeedsInitrd() bool // true for VZ, false for Firecracker
GetPullConcurrency() int // max concurrent blob downloads per pull (>=1)
}
ImageConfig provides the configuration data needed by image management operations. Both VZConfig and FirecrackerConfig implement this interface.
type ImageInfo ¶
type ImageInfo struct {
Name string // tag name (or "<dangling>" for unreferenced blobs)
Digest string // "sha256:..." digest of the underlying OCI manifest
Tag string // tag name (same as Name for tagged images, empty for dangling)
Path string // path to the first layer's cached ext4 (single-layer compat)
DockerRef string // OCI manifest annotation io.shed.source-ref
SizeBytes int64 // sum of layer descriptor sizes + cached ext4 bytes
UniqueBytes int64 // bytes attributable to layers only this manifest references
Source string // "config", "user", or "dangling"
Cached bool // manifest blob is installed
InUse bool // protected by a shed or snapshot reference
Alias string // friendly image_aliases key (empty for user/dangling)
IsDefault bool // this image's ref is the configured default_image
BootOnly bool // pulled without layer tarballs (boots, can't push without --with-layers)
}
ImageInfo describes an image known to the blob store, addressed by tag. Keep in sync with config.ImageInfo (field-by-field copy in backend wrappers).
type LayerInfo ¶
type LayerInfo struct {
Index int `json:"index"`
Digest string `json:"digest"`
Size int64 `json:"size"`
MediaType string `json:"media_type"`
CreatedAt time.Time `json:"created_at,omitempty"`
CreatedBy string `json:"created_by,omitempty"`
Comment string `json:"comment,omitempty"`
Variant string `json:"variant,omitempty"`
EmptyLayer bool `json:"empty_layer,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
}
LayerInfo describes one layer in an image's history.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager handles image lifecycle: ensure, list, delete, prune. All file locking happens inside the Manager.
func NewManager ¶
func NewManager(cfg ImageConfig, scanner RefScanner) *Manager
NewManager creates a new image Manager with the given configuration.
func (*Manager) DeleteImage ¶
DeleteImage removes an image's addressability — every cosmetic tag and the ref-index entry pointing at the resolved manifest. Following the Docker model, the underlying manifest blob is NOT removed; call PruneImages to garbage-collect once nothing references it. ident may be a Docker ref, a digest, or a cosmetic tag label.
Hard-blocks only when the manifest is pinned by a live shed or snapshot. (Warning when the target is the configured default_image is a CLI concern.)
func (*Manager) EnsureImage ¶
func (m *Manager) EnsureImage(ctx context.Context, ref ResolvedRef, progress ProgressFunc) (EnsureResult, error)
EnsureImage ensures an image is available locally. For Docker refs, pulls + converts + writes OCI blobs + materializes ext4 cache, then advances the tag. For local-path refs, returns the path directly (legacy escape hatch).
func (*Manager) ImageHistory ¶
ImageHistory loads the manifest + config for tagOrDigest and returns per-layer history in display order (layer[0] = base, layer[N-1] = top).
func (*Manager) InspectImage ¶
func (m *Manager) InspectImage(tagOrDigest string) (*ImageInfo, *OCIManifest, error)
InspectImage returns full details for a tag or digest.
func (*Manager) ListImages ¶
ListImages returns ImageInfo entries for every known tag plus dangling blobs (installed manifests not referenced by any tag). UniqueBytes and SharedBytes are computed by attributing each layer's on-disk cost to its referencing manifests.
func (*Manager) LoadImage ¶
LoadImage reads a tar produced by SaveImage (or any compatible OCI image-layout-tar) and ingests it into the local store. Returns the list of manifest digests added.
Behavior:
- Every blob in the tar is content-verified against its filename digest before being written.
- Manifests listed in the input index.json are recorded in the local index. ref-name annotations are translated to local tags (if not already taken).
- Layer ext4 files are NOT materialized — call EnsureImage afterwards to populate the cache.
func (*Manager) PruneImages ¶
PruneImages removes blobs unreferenced by any shed/snapshot.
Reachability: a blob is "live" iff it is a manifest pinned by a shed or snapshot (via metadata.LowerDigest, which is the OCI manifest digest), or it is reachable from a live manifest (its config, layers, kernel, initrd, and any cached ext4 for those layers).
All other blobs are candidates for prune. Cached ext4 files for orphaned layers are also evicted. Dangling tag files (pointing at a no-longer-present manifest) are dropped.
func (*Manager) PullImage ¶
func (m *Manager) PullImage(ctx context.Context, dockerRef, tag, platform string, withLayers bool, progress ProgressFunc) (string, error)
PullImage pulls a registry reference straight to the OCI layout (no Docker daemon required) and advances the named tag. Defaults to the backend's native platform when platform is empty.
func (*Manager) PushImage ¶
func (m *Manager) PushImage(ctx context.Context, tagOrDigest, dstRef string, progress ProgressFunc) error
PushImage uploads the manifest currently held by tagOrDigest to a destination registry ref. Byte-perfect: the on-disk tar.gz layer blobs are streamed straight from the OCI store.
func (*Manager) ResolveImageBlobs ¶
func (m *Manager) ResolveImageBlobs(manifestDigest string) (manifest *OCIManifest, kernelPath, initrdPath string, err error)
ResolveImageBlobs returns the manifest and config for an installed image, plus its kernel/initrd blob paths if the manifest advertises them via shed annotations.
func (*Manager) ResolveManifestLower ¶
ResolveManifestLower returns the path of the single content-addressed lower-image (flattened, all-layers-merged erofs) for the manifest, materializing it from the layer blobs if it isn't already cached. Used by VM start to attach one read-only block device.
func (*Manager) SaveImage ¶
SaveImage writes the OCI subtree reachable from tagOrDigest as a tar stream to out. The stream contains:
oci-layout index.json (one entry pointing at the saved manifest) blobs/sha256/<hex> for manifest, config, every layer, and kernel/initrd
Foreign tools that accept "OCI image layout in a tar" (crane, skopeo, oras) can consume the stream directly.
type MintErofsOptions ¶
type MintErofsOptions struct {
// ImagesDir is the OCI store the layer blobs are already
// installed in and where the new erofs blob will be written.
ImagesDir string
// LayerDigests are the OCI manifest's layer digests in OCI order
// (lowest at index 0). MergeLayers flattens them into a single
// tarball before feeding mkfs.erofs.
LayerDigests []string
// BuildToolsRef is the OCI image carrying mkfs.erofs that will
// run inside `docker run --rm ...`. Pin to the same tag as the
// shed-server version being released — see
// docs/reference/build-tools.md for the versioning model.
BuildToolsRef string
// DockerBinary defaults to "docker"; tests / unusual hosts
// override (e.g. "podman" with a compat wrapper).
DockerBinary string
}
MintErofsOptions controls what MintRootfsErofs invokes on the publishing host.
type OCIConfig ¶
type OCIConfig struct {
Architecture string `json:"architecture"` // "arm64" or "amd64"
OS string `json:"os"` // "linux"
Created string `json:"created,omitempty"`
Author string `json:"author,omitempty"`
RootFS OCIRootFS `json:"rootfs"`
History []OCIHistory `json:"history,omitempty"`
Config OCIConfigConfig `json:"config,omitempty"`
}
OCIConfig is the OCI image config. Shed populates only the fields it uses; the rest are omitted via omitempty so foreign tools see a well-formed config with no surprises.
func LoadConfigByDigest ¶
LoadConfigByDigest reads + parses an OCI image config blob.
func ParseConfig ¶
ParseConfig decodes an OCI image config blob.
func (*OCIConfig) MarshalIndent ¶
MarshalIndent emits OCI-compliant JSON with stable indentation.
type OCIConfigConfig ¶
type OCIConfigConfig struct {
Env []string `json:"Env,omitempty"`
Entrypoint []string `json:"Entrypoint,omitempty"`
Cmd []string `json:"Cmd,omitempty"`
WorkingDir string `json:"WorkingDir,omitempty"`
}
OCIConfigConfig is the runtime config block. Shed doesn't run images like a container, so we leave most fields empty.
type OCIHistory ¶
type OCIHistory struct {
Created string `json:"created,omitempty"`
CreatedBy string `json:"created_by,omitempty"`
Comment string `json:"comment,omitempty"`
EmptyLayer bool `json:"empty_layer,omitempty"`
}
OCIHistory is the per-layer history entry, optional but conventional.
type OCIIndex ¶
type OCIIndex struct {
SchemaVersion int `json:"schemaVersion"` // always 2
MediaType string `json:"mediaType,omitempty"`
Manifests []Descriptor `json:"manifests"`
Annotations map[string]string `json:"annotations,omitempty"`
}
OCIIndex is the top-level image index stored at {imagesDir}/index.json. Shed lists every installed manifest here with a ref-name annotation (org.opencontainers.image.ref.name) so foreign tools can enumerate the store with `crane catalog dir:<imagesDir>`.
func ParseIndex ¶
ParseIndex decodes an OCI index blob.
func (*OCIIndex) MarshalIndent ¶
MarshalIndent emits OCI-compliant JSON with stable indentation.
type OCILayoutMarker ¶
type OCILayoutMarker struct {
ImageLayoutVersion string `json:"imageLayoutVersion"`
}
OCILayoutMarker is the content of the {imagesDir}/oci-layout file.
type OCIManifest ¶
type OCIManifest struct {
SchemaVersion int `json:"schemaVersion"` // always 2
MediaType string `json:"mediaType,omitempty"`
Config Descriptor `json:"config"`
Layers []Descriptor `json:"layers"`
Annotations map[string]string `json:"annotations,omitempty"`
}
OCIManifest is an OCI image manifest. The shed-on-disk shape is the standard OCI shape; consumers can inspect it via `crane manifest`.
func LoadManifestByDigest ¶
func LoadManifestByDigest(imagesDir, manifestDigest string) (*OCIManifest, error)
LoadManifestByDigest reads + parses an OCI manifest blob.
func ParseManifest ¶
func ParseManifest(data []byte) (*OCIManifest, error)
ParseManifest decodes an OCI manifest blob.
func (*OCIManifest) MarshalIndent ¶
func (m *OCIManifest) MarshalIndent() ([]byte, error)
MarshalIndent emits OCI-compliant JSON with stable indentation.
func (*OCIManifest) ShedInitrdDigest ¶
func (m *OCIManifest) ShedInitrdDigest() string
ShedInitrdDigest returns the initrd blob digest carried in annotations, or "" if no initrd is associated with this image.
func (*OCIManifest) ShedKernelDigest ¶
func (m *OCIManifest) ShedKernelDigest() string
ShedKernelDigest returns the kernel blob digest carried in annotations, or "" if no kernel is associated with this image.
func (*OCIManifest) ShedRootfsErofsDigest ¶
func (m *OCIManifest) ShedRootfsErofsDigest() string
ShedRootfsErofsDigest returns the prebuilt rootfs erofs blob digest carried in annotations, or "" if the image was built before v0.5.2 (when this annotation was introduced). Callers that depend on the blob being present should fail with a clear "image too old" error rather than fall back to local mkfs.erofs — see manager.go.
func (*OCIManifest) ShedSourceRef ¶
func (m *OCIManifest) ShedSourceRef() string
ShedSourceRef returns the manifest's recorded source ref annotation, or "" if unset.
func (*OCIManifest) ShedVariant ¶
func (m *OCIManifest) ShedVariant() string
ShedVariant returns the manifest's recorded variant annotation, or "" if unset.
type OCIRootFS ¶
type OCIRootFS struct {
Type string `json:"type"` // "layers"
DiffIDs []string `json:"diff_ids"` // sha256 digests of uncompressed layer tars
}
OCIRootFS is the rootfs section of the OCI image config.
type ProgressEvent ¶ added in v0.6.2
type ProgressEvent struct {
Stage string // phase/stage label (e.g. "image"); advances the PhaseTimer
Message string // human-readable status line; always set for emitted events
// Structured per-blob byte progress. Zero on plain status events.
Kind string // "" plain status; "blob" per-blob byte progress
ID string // full digest (blob events); renderer keys on this
Status string // BlobStatus* (blob events)
Current int64 // bytes fetched so far (blob events)
Total int64 // total bytes — compressed descriptor size (blob events)
}
ProgressEvent is a progress update from a long-running image operation (pull / push / ensure). Stage + Message are the plain status fields used since the beginning. The Kind=="blob" fields carry structured per-blob byte progress for the `shed` CLI's live renderer; line-mode and older clients ignore them and render Message.
Backends translate this to backend.ProgressEvent at the API boundary (vmimage must not import backend), so the wire shape is owned by the backend package — this struct is the in-process carrier.
func (ProgressEvent) IsBlob ¶ added in v0.6.2
func (e ProgressEvent) IsBlob() bool
IsBlob reports whether e carries structured per-blob byte progress. Plain line-oriented consumers (logs, the bulk pull-images command) use this to skip byte-tick events they would otherwise spam.
type ProgressFunc ¶
type ProgressFunc func(ProgressEvent)
ProgressFunc receives progress events during long-running operations.
type PullOptions ¶
type PullOptions struct {
// Ref is the registry reference (e.g. "ghcr.io/charliek/shed-vz-full:v1").
Ref string
// ImagesDir is the OCI image-layout root that receives the blobs.
ImagesDir string
// TagName is the shed tag to advance to the new manifest digest.
// If empty, no tag is set — useful for previewing pulls.
TagName string
// Platform selects the per-platform manifest from a multi-arch
// image index. Defaults to the backend's native platform via
// the caller; pass an empty string to use go-containerregistry's
// default-platform resolver.
Platform string
// Insecure permits plain-HTTP transport (typical for local test
// registries like `registry:2` on localhost:5000).
Insecure bool
// AuthKeychain is the credential helper chain. nil falls back to
// authn.DefaultKeychain which reads ~/.docker/config.json.
AuthKeychain authn.Keychain
// ExtractKernel, when true, asks the puller to extract a kernel
// from the layer rootfs if the manifest has no
// io.shed.kernel.digest annotation. ExtractKernel + a manifest
// without the annotation triggers an in-Go tar walk that fishes
// out /boot/vmlinuz-* or /boot/vmlinux.
ExtractKernel bool
// NeedsInitrd is the initrd analog of ExtractKernel.
NeedsInitrd bool
// Progress receives stage messages during the pull.
Progress ProgressFunc
// Concurrency caps how many blobs download in parallel. <=1 means
// serial. Callers should pass cfg.GetPullConcurrency().
Concurrency int
// SkipLayers pulls "boot-only": config + kernel + initrd + erofs +
// manifest, but NOT the layer tarballs (the host boots from the erofs
// and never reads layers). Requires a v0.5.2+ image whose kernel/initrd/
// erofs come from manifest annotations; rejected otherwise. The skipped
// layers are re-fetchable later via a full pull (`--with-layers`), which
// is required before `shed image push` of a boot-only image.
SkipLayers bool
}
PullOptions configures a registry-direct image pull.
type PullPolicy ¶ added in v0.6.0
type PullPolicy string
PullPolicy controls how EnsureImage reconciles a configured Docker ref against the local content-addressed store at create time. It mirrors the Docker/Podman vocabulary so existing operator intuition transfers.
const ( // PullMissing uses the cached ref if present, pulling only when absent. // This is the default and keeps the create hot path O(1) and offline. PullMissing PullPolicy = "missing" // PullAlways always contacts the registry and pulls, bypassing the cache. PullAlways PullPolicy = "always" // PullNever uses the cached ref if present and errors on a miss; it never // contacts the registry. PullNever PullPolicy = "never" )
func ParsePullPolicy ¶ added in v0.6.0
func ParsePullPolicy(s string) (PullPolicy, error)
ParsePullPolicy validates a configured pull_policy string. An empty value defaults to PullMissing.
type PullResult ¶
type PullResult struct {
ManifestDigest string
ConfigDigest string
LayerDigests []string
KernelDigest string
InitrdDigest string
}
PullResult mirrors ConvertResult so callers can use either flow.
func PullToOCILayout ¶
func PullToOCILayout(ctx context.Context, opts PullOptions) (*PullResult, error)
PullToOCILayout fetches an image from a registry into the OCI image layout under opts.ImagesDir. The pulled manifest's digest is recorded; each layer + config blob is written under blobs/sha256/<digest>. If the manifest carries shed annotations for kernel/initrd, those blobs are also fetched. Otherwise — and when opts.ExtractKernel is set — the kernel/initrd are pulled out of the rootfs layer in Go.
After all blobs are written, the named tag (if any) is advanced and each layer is materialized into the derived ext4 cache.
type PushOptions ¶
type PushOptions struct {
// Ref is the destination registry reference.
Ref string
// ImagesDir is the source OCI layout.
ImagesDir string
// ManifestDigest is the source manifest digest to push (typically
// resolved from a tag by the caller).
ManifestDigest string
// Insecure permits plain-HTTP transport. Auto-detected for
// loopback hosts when left at its zero value (caller can also
// set explicitly).
Insecure bool
// AuthKeychain controls credential lookup. nil → DefaultKeychain.
AuthKeychain authn.Keychain
// Progress is invoked with stage updates.
Progress ProgressFunc
}
PushOptions configures a registry-direct image push.
type RefKind ¶
type RefKind string
RefKind classifies a reference protecting a blob from prune.
const ( // RefKindShed indicates a reference held by an existing shed instance. RefKindShed RefKind = "shed" // RefKindSnapshot indicates a reference held by a snapshot. RefKindSnapshot RefKind = "snapshot" // RefKindTag indicates a reference held by a tag. Tags ARE // protective from prune (changed in v0.5.8 — pre-v0.5.8 the prune // walker followed Docker's "tags don't protect" model, which made // `shed image pull X && shed image prune` delete the manifest // blob just pulled if no shed pinned it yet, leaving operators // with a tag pointing at a missing blob or, worse, silently // reverting to a stale locally-cached manifest). To delete a // blob a tag points at, the workflow is `shed image rm <tag>` // followed by `shed image prune`. See // docs/upgrades/v0.5.7-to-v0.5.8.md. RefKindTag RefKind = "tag" // RefKindPending indicates a reference held by an in-flight // `shed create`. The backend writes a `.creating` marker into the // instance directory between EnsureImage and meta.Save; the // refscanner emits a Pending ref for each fresh marker so prune // can't delete the blob the create is about to depend on. Fresh // for 1 h (see systemprune.InstanceCreatingMaxAge); stale markers // produce no reference and the underlying blob becomes prunable. RefKindPending RefKind = "pending-create" )
type RefScanner ¶
RefScanner scans the on-disk layout for references that point at blobs. Implementations live in the backend packages where the appropriate metadata directories are reachable.
Implementations MUST return shed and snapshot references (the protective refs that block prune). Tag refs are scanned by the Manager itself.
The `strict` parameter selects how malformed instance metadata is handled:
- strict=false (read paths — ListImages, InspectImage, DiskUsage): skip the broken instance with a warning, return the rest. A `shed list` or `shed system df` shouldn't fail because one instance has a corrupt JSON file somewhere.
- strict=true (PruneImages): fail closed. Returning a partial ref set from a destructive caller risks deleting a blob the broken-but-recoverable shed still pinned. The caller surfaces the error to the operator and tells them to fix or `rm -rf` the broken instance before pruning.
Sentinels for "the listing itself succeeded but a known-broken instance was skipped" are intentionally not part of the interface — implementations log a warning. Callers that want to inspect the skipped set should walk instances/* themselves.
type Reference ¶
type Reference struct {
// Digest is the blob digest the reference points at.
Digest string
// Kind is the kind of reference (shed, snapshot, tag).
Kind RefKind
// Name is the name of the referencing object — shed name, snapshot
// name, or tag name. Used for human-readable error messages.
Name string
}
Reference describes one thing that points at a blob digest.
func ProtectiveRefs ¶
ProtectiveRefs reports whether a digest has any protective reference — Shed, Snapshot, Pending (in-flight create), or Tag.
As of v0.5.8 tag references are protective: see RefKindTag's doc for the rationale. RefScanner implementations still don't emit tag refs (tags live in the central store the Manager owns, not in per-backend metadata), so the Manager merges them into the ref list at the caller sites that need protection (notably PruneImages).
type ResolvedRef ¶
type ResolvedRef struct {
Path string // set when the ext4 image already exists on disk
DockerRef string // set when the image needs to be pulled and converted
Name string // cosmetic label derived from the ref; not an identity key
Digest string // set when Path came from a tag in the blob store; preserved through to EnsureResult.Digest
// Policy governs cache-vs-pull for a DockerRef. Empty means PullMissing.
// Ignored for local-path refs.
Policy PullPolicy
}
ResolvedRef describes an image to ensure: either a local path or a Docker ref to pull.