Documentation
¶
Overview ¶
Package registry implements Zattera's embedded OCI image registry (spec §3.5). T-31 covers the content-addressed blob store and the OCI push protocol (blob uploads); manifests, tags, pull, auth and GC arrive in T-32.
Blob storage is content-addressed by digest, so identical layers — shared across the architectures of a multi-arch image, or across repos — are stored exactly once. Nothing here is keyed by repository or platform; the push path is media-type agnostic (an image index is just bytes at PUT time, its validation is T-32's job).
Index ¶
- Constants
- Variables
- func RemotePlatforms(ctx context.Context, imageRef string) ([]string, error)
- func SplitRepoRef(path string) (repo, ref string)
- type AuthFunc
- type Authenticator
- type BlobStore
- type Fetcher
- type Handler
- type Manifests
- func (m *Manifests) Close() error
- func (m *Manifests) DeleteManifest(repo, digest string) error
- func (m *Manifests) GetManifest(repo, ref string) (body []byte, mediaType, digest string, err error)
- func (m *Manifests) Platforms(repo, ref string) ([]string, error)
- func (m *Manifests) PutManifest(repo, ref, contentType string, body []byte) (string, error)
- func (m *Manifests) Resolve(repo, ref string) (string, error)
- func (m *Manifests) ResolveManifest(repo, ref, platform string) (digest, mediaType string, err error)
- func (m *Manifests) Tags(repo string) ([]string, error)
- func (m *Manifests) UntagAndSweep(repo, tag string) error
- type Peer
- type PeerCredentials
- type PeerSource
- type PeerSourceFunc
- type Registry
- type Upload
- type Uploads
- func (m *Uploads) Append(id string, r io.Reader, start int64) (int64, error)
- func (m *Uploads) Cancel(id string) error
- func (m *Uploads) Finalize(id, expected string, trailing io.Reader) (string, error)
- func (m *Uploads) Get(id string) (*Upload, bool)
- func (m *Uploads) Ingest(r io.Reader, expected string) (string, error)
- func (m *Uploads) Reap() int
- func (m *Uploads) Start() (*Upload, error)
Constants ¶
const ( MediaTypeOCIManifest = "application/vnd.oci.image.manifest.v1+json" MediaTypeOCIIndex = "application/vnd.oci.image.index.v1+json" MediaTypeDockerManifest = "application/vnd.docker.distribution.manifest.v2+json" MediaTypeDockerList = "application/vnd.docker.distribution.manifest.list.v2+json" )
OCI / Docker media types we accept and produce. Manifest lists and image indexes are the multi-arch descriptors; a single-arch image is a plain manifest.
Variables ¶
var ( ErrBlobUnknown = errors.New("registry: blob unknown") ErrUploadUnknown = errors.New("registry: upload unknown") ErrDigestInvalid = errors.New("registry: invalid digest") ErrDigestMismatch = errors.New("registry: digest mismatch") ErrRangeNotSatisfiable = errors.New("registry: range not satisfiable") )
Sentinel errors returned by the store and upload manager. The HTTP layer maps these onto OCI error codes.
var ( ErrManifestUnknown = errors.New("registry: manifest unknown") ErrManifestInvalid = errors.New("registry: manifest invalid") ErrManifestBlobUnkn = errors.New("registry: referenced blob unknown") ErrManifestChildUnkn = errors.New("registry: referenced child manifest unknown") )
Manifest-layer sentinel errors, mapped to OCI error codes by the HTTP layer.
Functions ¶
func RemotePlatforms ¶
RemotePlatforms best-effort inspects an EXTERNAL image reference (docker hub, ghcr, …) and returns the OCI platforms it can run on: an index/manifest list's child platforms, or the single platform read from a plain manifest's config. It speaks anonymous distribution-API auth (bearer token challenge). Callers treat ANY error as "unknown" — a deploy must never fail over manifest inspection (T-88).
func SplitRepoRef ¶
SplitRepoRef splits a host-less image path ("proj/app:tag", "proj/app@sha256:…") into repository and tag-or-digest ("latest" when bare).
Types ¶
type AuthFunc ¶
AuthFunc adapts a plain function to the Authenticator interface.
func (AuthFunc) Authenticate ¶
Authenticate implements Authenticator.
type Authenticator ¶
type Authenticator interface {
// Authenticate reports whether the username/password pair is valid.
Authenticate(username, password string) bool
}
Authenticator verifies registry HTTP basic-auth credentials. The registry package stays decoupled from cluster state: the daemon supplies an implementation backed by node registry credentials (KV registry/creds/<id>, created at join in T-17) and user personal access tokens (zpat_… as the password). A nil Authenticator disables auth (dev / tests).
type BlobStore ¶
type BlobStore struct {
// contains filtered or unexported fields
}
BlobStore keeps immutable, digest-addressed blobs on disk. Layout:
<root>/blobs/sha256/<first2>/<hex> committed blobs <root>/uploads/<id> in-progress upload temp files
Writes go to a temp file, get digest-verified, then atomically rename into place — a crash mid-upload can never surface as a corrupt blob.
func NewBlobStore ¶
NewBlobStore prepares the blob and upload directories under root (which is typically <data-dir>/registry).
func (*BlobStore) Delete ¶
Delete removes a blob by digest. A missing blob is not an error (GC calls this idempotently).
func (*BlobStore) Open ¶
Open returns a read handle to a committed blob. ErrBlobUnknown if absent. The caller closes the returned file.
func (*BlobStore) Write ¶
Write streams r into the blob store, computing its digest as it goes, and commits it under that digest. Used for content whose digest we do not know in advance (manifests and image indexes, which are addressed by the sha256 of their own bytes). Returns the resulting digest and byte size.
type Fetcher ¶
type Fetcher struct {
// contains filtered or unexported fields
}
Fetcher pulls blobs and manifests from peer control nodes on demand.
func NewFetcher ¶
func NewFetcher(peers PeerSource, creds PeerCredentials, client *http.Client) *Fetcher
NewFetcher builds a pull-through fetcher. A nil PeerSource (or one that returns no peers) makes every method a no-op miss, which is exactly what a single-node cluster wants.
func (*Fetcher) FetchBlob ¶
FetchBlob fetches dgst from the first peer that has it and commits it to store. It returns nil when the blob is now present locally. Callers re-read from the local store afterwards: committing first and serving from disk keeps the write path atomic (BlobStore.Write digest-verifies and renames) and avoids half-serving a blob whose transfer failed midway.
func (*Fetcher) FetchManifest ¶
func (f *Fetcher) FetchManifest(ctx context.Context, man *Manifests, store *BlobStore, repo, ref string) error
FetchManifest fetches a manifest from a peer and registers it locally so the tag index and refcount graph stay correct (a manifest is a blob plus bookkeeping — copying only the bytes would leave GC blind to its children).
ref may be a tag or a digest. A tag is resolved on the peer first and the local tag is bound only to the digest the peer actually served, so a stale or racing tag on one node cannot silently repoint another node's tag.
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler serves the OCI distribution API: the version probe and blob upload flow (T-31) plus manifests, tags, pull and delete (T-32). A nil manifests store leaves the manifest routes disabled (the T-31-only surface); a nil Authenticator disables auth.
func NewHandler ¶
NewHandler wires the blob/upload push surface (T-31) with no manifest store or auth. Use newHandler (or registry.New) for the full T-32 surface.
type Manifests ¶
type Manifests struct {
// contains filtered or unexported fields
}
Manifests stores image manifests and indexes (as content-addressed blobs) plus the tag index and a reference-counted object graph for GC.
func NewManifests ¶
NewManifests opens (creating if needed) the registry metadata db under dir and prepares its buckets.
func (*Manifests) DeleteManifest ¶
DeleteManifest removes a manifest by digest from repo: it untags every tag in the repo pointing at it and releases those references (cascading GC). Absent manifests return ErrManifestUnknown.
func (*Manifests) GetManifest ¶
func (m *Manifests) GetManifest(repo, ref string) (body []byte, mediaType, digest string, err error)
GetManifest returns the stored bytes, media type and digest for a reference.
func (*Manifests) Platforms ¶
Platforms lists the OCI platform strings a reference can run on: an index's child platforms, or a single-element list for a plain manifest read from its config blob (empty when unknown). Used by T-88 to populate Release.platforms.
func (*Manifests) PutManifest ¶
PutManifest validates and stores a manifest or image index under repo, then (when ref is a tag) points the tag at it. Returns the manifest digest.
Validation: an image manifest requires its config and every layer blob to already exist; an index requires every referenced child manifest to already exist in this repo (children are pushed before the index in OCI push order).
func (*Manifests) Resolve ¶
Resolve turns a tag or digest reference into a concrete manifest digest that exists in repo. ErrManifestUnknown when absent.
func (*Manifests) ResolveManifest ¶
func (m *Manifests) ResolveManifest(repo, ref, platform string) (digest, mediaType string, err error)
ResolveManifest maps (repo, ref, platform) to the concrete manifest digest a client should pull for that platform: for an index it returns the matching child; for a plain manifest (or empty platform) it returns the reference itself. This lets the control plane learn a release's arch without a docker client (T-88).
func (*Manifests) UntagAndSweep ¶
UntagAndSweep removes a tag and reclaims anything it solely kept alive. This is the retention hook (T-38): drop an old release's image tag and let GC free its blobs.
type Peer ¶
type Peer struct {
// NodeID identifies the peer (logging only).
NodeID string
// BaseURL is its registry root, e.g. "https://10.90.0.2:5000".
BaseURL string
}
Peer is one other control node's registry endpoint.
type PeerCredentials ¶
PeerCredentials is the identity a node presents to a peer's registry. In production this is the node's own "node-<id>" credential (already minted at join and validated by the registry authenticator) over the mesh with the cluster CA — never anonymous, never a user PAT.
type PeerSource ¶
type PeerSource interface {
Peers() []Peer
}
PeerSource lists the OTHER control-node registries this node may fetch from. It is resolved per call from cluster state (never a static list) so it follows joins, removals and cordons without restarts. Returning nil disables pull-through — which is the single-node and dev case.
type PeerSourceFunc ¶
type PeerSourceFunc func() []Peer
PeerSourceFunc adapts a plain function to PeerSource.
type Registry ¶
type Registry struct {
Blobs *BlobStore
Uploads *Uploads
Manifests *Manifests
// contains filtered or unexported fields
}
Registry bundles the embedded OCI registry's storage (blobs, upload sessions, manifests) behind a single http.Handler mounted on :5000 by the daemon on control nodes.
func New ¶
New opens the registry rooted at dir (typically <data-dir>/registry). auth may be nil to disable authentication (dev / tests).
func (*Registry) EnablePullThrough ¶
func (rg *Registry) EnablePullThrough(peers PeerSource, creds PeerCredentials, client *http.Client)
EnablePullThrough makes this registry fetch blobs and manifests it lacks from peer control nodes (T-101). Safe to leave unset: without it a miss is simply a miss, which is the single-node and dev behaviour.
func (*Registry) ReapUploads ¶
ReapUploads expires idle upload sessions; call it periodically from a janitor.
type Upload ¶
Upload is one in-progress, resumable blob upload. Bytes are streamed to a temp file while a running sha256 is maintained, so finalize can verify the client-declared digest without re-reading the file. Uploads are sequential: a PATCH must continue exactly where the previous chunk ended.
type Uploads ¶
type Uploads struct {
// contains filtered or unexported fields
}
Uploads tracks live upload sessions and owns their lifecycle against the backing BlobStore.
func NewUploads ¶
NewUploads constructs the upload manager. The clock drives session expiry.
func (*Uploads) Append ¶
Append writes r to the session's temp file. When start >= 0 it is a declared Content-Range start and MUST equal the session's current offset (uploads are strictly sequential); start < 0 means "append at the current offset" (a monolithic PATCH with no Content-Range). Returns the new offset.
func (*Uploads) Finalize ¶
Finalize appends any trailing bytes, verifies the running digest against the client-declared expected digest, then commits the blob. On success the session is closed and removed. On a digest mismatch the session is discarded (its temp file deleted) so a bad upload can never linger or corrupt a blob.
func (*Uploads) Ingest ¶
Ingest performs a one-shot monolithic upload (POST ?digest= or a PUT with the full body): start → append → finalize in a single call.