imgregistry

package
v1.0.34 Latest Latest
Warning

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

Go to latest
Published: Aug 7, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package imgregistry is a minimal Docker Registry v2 HTTP client used to browse the fleet's image registry and resolve tags/digests for the upgrade-picker. Deliberately not named "registry" — the codebase already has jobs.Registry (job-kind registry) and a same-named package here would collide in every import block needing both.

Index

Constants

View Source
const TagsCacheTTL = 5 * time.Minute

TagsCacheTTL bounds how long a CachingClient serves a repo's Tags() result before re-resolving it. Registry contents only change on push (a human or CI action), never spontaneously, so a few minutes of staleness is harmless — an operator who just pushed a new tag and immediately checks the UI sees it within this window, not never. 5 minutes is picked to make the common case (browsing the list, clicking into a couple of repos, going back) free after the first view, while still bounding how long a since-deleted tag would linger in the UI.

Variables

View Source
var ErrNotFound = errors.New("not found")

ErrNotFound wraps a registry's genuine 404 response (NAME_UNKNOWN / MANIFEST_UNKNOWN) — the registry answered and said this repo/tag doesn't exist.

View Source
var ErrUnreachable = errors.New("registry unreachable")

ErrUnreachable wraps a transport failure, a non-404 non-2xx response, or a response body this client cannot decode — anything that means "we could not get a straight answer from the registry," as distinct from ErrNotFound ("the registry answered and said no"). Callers must not collapse the two: an operator told "not found" for a registry that is simply down gets the exact misleading signal this package exists to prevent.

Functions

func ValidRef

func ValidRef(ref string) bool

ValidRef reports whether ref is an acceptable manifest reference: either a tag (tagRe) or a content digest (digestRe). Callers must validate ref before passing it to Client.Manifest, which interpolates it unescaped into a registry request path — an unvalidated ref (e.g. containing "../" or "/") would let a caller steer that request to an arbitrary path on the registry host.

func ValidRepoName

func ValidRepoName(repo string) bool

ValidRepoName reports whether repo is an acceptable Docker Registry v2 repository name: one or more "/"-separated components, each matching repoComponentRe. Registry v2 repo names legally contain "/" (e.g. "iotready/engine"), unlike the DNS-label-style names (host/template/slug) validated elsewhere by render.ValidName — so this validator is intentionally separate, not a reuse of that one. Shared by internal/api and internal/ui so the two edges cannot drift into accepting different repo shapes.

Types

type Auth

type Auth struct {
	Mode     string // "none" (default) or "basic"
	Username string
	Password string
}

Auth configures how HTTPClient authenticates to the registry.

type CachingClient added in v1.0.33

type CachingClient struct {
	// contains filtered or unexported fields
}

CachingClient wraps a Client and caches its Tags() results for ttl, since Tags is the one call expensive enough to matter — for the fleet's "engine" repo, ~1000 manifest GETs plus 737 config-blob GETs, ~21s on a cold cache. The list page (internal/ui/handlers_registry.go) fires this once per catalog repo, concurrently, on every single page view — without a cache that's the full per-repo cost paid ~26 times on every visit. With it, only the first visit (or the first visit after ttl) pays that cost; every revisit inside the window is instant.

It is a decorator, not a change to HTTPClient itself: HTTPClient stays a pure client with no caching semantics of its own, the cache is independently testable against a fake inner Client, and a caller that wants uncached behavior (e.g. a future admin "force refresh" action, or a test) can simply hold the inner Client instead.

Catalog, TagCount, and Manifest are passed straight through uncached: Catalog and TagCount are already cheap (a single, unresolved tags/catalog list call), and Manifest is one lookup for one digest — caching it would buy little while adding another thing that could go stale.

Only a successful Tags() result is cached (see Tags below) — a transient registry blip must not poison every viewer's list page for the rest of the TTL.

func NewCachingClient added in v1.0.33

func NewCachingClient(inner Client, ttl time.Duration) *CachingClient

NewCachingClient wraps inner in a Tags()-caching decorator with the given ttl (TagsCacheTTL in production; tests pass a short one to observe expiry).

func (*CachingClient) Catalog added in v1.0.33

func (c *CachingClient) Catalog(ctx context.Context) ([]string, error)

func (*CachingClient) Manifest added in v1.0.33

func (c *CachingClient) Manifest(ctx context.Context, repo, ref string) (Manifest, error)

func (*CachingClient) TagCount added in v1.0.33

func (c *CachingClient) TagCount(ctx context.Context, repo string) (int, error)

func (*CachingClient) Tags added in v1.0.33

func (c *CachingClient) Tags(ctx context.Context, repo string) ([]TagGroup, error)

Tags returns repo's cached TagGroups if present and not yet expired, otherwise resolves them via the inner Client and, only on success, stores the result. Safe for concurrent use — the list page fans this out across ~26 goroutines on a single page load.

type Client

type Client interface {
	Catalog(ctx context.Context) ([]string, error)
	Tags(ctx context.Context, repo string) ([]TagGroup, error)
	TagCount(ctx context.Context, repo string) (int, error)
	Manifest(ctx context.Context, repo, ref string) (Manifest, error)
}

Client is the read-only surface this package exposes.

type HTTPClient

type HTTPClient struct {
	BaseURL string // e.g. "http://100.64.0.23:5000", no trailing slash
	Auth    Auth
	HTTP    *http.Client
}

HTTPClient implements Client against a real Registry v2 server.

func NewHTTPClient

func NewHTTPClient(baseURL string, auth Auth) *HTTPClient

NewHTTPClient builds an HTTPClient with a sane default timeout.

func (*HTTPClient) Catalog

func (c *HTTPClient) Catalog(ctx context.Context) ([]string, error)

Catalog lists every repository in the registry, following the Link response header across pages rather than requesting one unbounded page.

func (*HTTPClient) Manifest

func (c *HTTPClient) Manifest(ctx context.Context, repo, ref string) (Manifest, error)

Manifest fetches a single manifest by tag or digest and returns its canonical digest (from Docker-Content-Digest) plus size/layer info.

func (*HTTPClient) TagCount added in v1.0.33

func (c *HTTPClient) TagCount(ctx context.Context, repo string) (int, error)

TagCount returns how many tags repo has, without resolving any of them. Deliberately NOT implemented as len(Tags(...)): Tags resolves every tag's manifest and every unique digest's config blob (~21s for the fleet's "engine" repo), while the repo-list page calls this once per repo on a single page load. One tags-list call — paginated, but nothing more — is the whole point.

Note this counts tags, not unique digests, so it can exceed len(Tags(...)) when several tags share a digest. That is the intended meaning for a "how many tags does this repo have" column.

func (*HTTPClient) Tags

func (c *HTTPClient) Tags(ctx context.Context, repo string) ([]TagGroup, error)

Tags lists every tag in repo, resolves each to its manifest digest, and groups tags that share a digest into one TagGroup. Each unique digest's config blob is fetched once (not once per tag) for its Created timestamp. Manifest and blob-created lookups both run with bounded concurrency (tagResolveConcurrency), not sequentially. Groups are sorted newest-Created-first.

type Layer

type Layer struct {
	Digest string
	Size   int64
}

Layer is one entry in a manifest's layer list.

type Manifest

type Manifest struct {
	Digest       string // from Docker-Content-Digest, the canonical content digest
	ConfigDigest string
	Layers       []Layer
	Size         int64 // config size + sum of all layer sizes
}

Manifest is the resolved shape of a single tag/digest reference.

type TagGroup

type TagGroup struct {
	Digest  string
	Tags    []string
	Created time.Time
	Size    int64
}

TagGroup is every tag that resolves to the same content digest, with the digest's creation time (from its config blob) and total size.

Jump to

Keyboard shortcuts

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