Image management is handled by core.ImageManager which wraps BaseServer image operations with cloud-specific registry authentication and synchronization.
Docker CLI → HTTP Handler → s.self.ImagePull/Push/Tag/Remove
↓
ImageManager (cloud backends)
├── AuthProvider.GetToken() → cloud auth
├── FetchImageMetadata() → registry metadata
├── BaseServer.ImagePullWithMetadata() → store image
└── AuthProvider.OnPush/OnTag/OnRemove → sync to cloud
Docker backend bypasses ImageManager entirely — delegates to local Docker daemon.
Source: backends/core/image_manager.go
type ImageManager struct {
Base *BaseServer // in-memory store + base implementations
Auth AuthProvider // cloud auth + registry sync (nil = no cloud)
Logger zerolog.Logger
}Pull flow:
- Check if image reference matches a cloud registry (
Auth.IsCloudRegistry) - If yes, get cloud auth token (
Auth.GetToken) - Fetch real image metadata from registry (
FetchImageMetadata) — config, layers, sizes - Store image with real metadata in
BaseServer.Store - Return progress stream to client
Push flow:
- Resolve image in local store
- If cloud registry, call
Auth.OnPush(imageID, registry, repo, tag) - Return progress stream (real or error)
Tag flow:
- Delegate to
BaseServer.ImageTag - If cloud registry, call
Auth.OnTag(imageID, registry, repo, newTag)
Remove flow:
- Collect cloud references before removal
- Delegate to
BaseServer.ImageRemove - Call
Auth.OnRemove(registry, repo, tags)for each cloud reference
type AuthProvider interface {
GetToken(registry string) (string, error)
IsCloudRegistry(registry string) bool
OnPush(imageID, registry, repo, tag string) error
OnTag(imageID, registry, repo, newTag string) error
OnRemove(registry, repo string, tags []string) error
}All On* methods are non-fatal — they log warnings on failure and return errors that callers may ignore. This prevents cloud sync issues from breaking local operations.
type ECRAuthProvider struct {
ecr *ecr.Client
logger zerolog.Logger
ctx func() context.Context
}| Method | Implementation |
|---|---|
GetToken |
ecr.GetAuthorizationToken() → "Basic {base64}" |
IsCloudRegistry |
*.dkr.ecr.*.amazonaws.com pattern match |
OnPush |
ecr.CreateRepository() + ecr.PutImage() with OCI manifest |
OnTag |
ecr.CreateRepository() + ecr.PutImage() with new tag |
OnRemove |
ecr.BatchDeleteImage() for all tags |
Used by: ECS, Lambda
type ARAuthProvider struct {
ctx func() context.Context
logger zerolog.Logger
}| Method | Implementation |
|---|---|
GetToken |
google.FindDefaultCredentials() → "Bearer {token}" |
IsCloudRegistry |
*.gcr.io, *-docker.pkg.dev |
OnPush |
core.OCIPush() — OCI registry v2 API |
OnTag |
core.OCIPush() with new tag |
OnRemove |
DELETE /v2/{repo}/manifests/{tag} (graceful 404/405) |
Used by: Cloud Run, Cloud Run Functions
type ACRAuthProvider struct {
Logger zerolog.Logger
}| Method | Implementation |
|---|---|
GetToken |
azidentity.NewDefaultAzureCredential() → "Bearer {token}" |
IsCloudRegistry |
*.azurecr.io suffix |
OnPush |
core.OCIPush() — OCI registry v2 API |
OnTag |
GET source manifest → PUT with new tag |
OnRemove |
HEAD for digest → DELETE /v2/{repo}/manifests/{digest} |
Used by: ACA, Azure Functions
Source: backends/core/oci_push.go
Implements the OCI Distribution Spec v2 push protocol:
- Initiate upload —
POST /v2/{repo}/blobs/uploads/ - Upload blob —
PUT /v2/{repo}/blobs/uploads/{uuid}?digest={digest} - Put manifest —
PUT /v2/{repo}/manifests/{tag}
Used by GCP and Azure auth providers. ECR uses the ECR SDK directly (PutImage) instead of the OCI protocol.
Real registry-to-registry layer mirror. core.FetchLayerBlob
downloads each layer's compressed bytes during ImagePull, caches
them in Store.LayerContent[compressedDigest] keyed by the source
manifest digest, and OCIPush requires ManifestLayers and uses
each entry's compressed digest verbatim. No recompute, no empty-layer
fallback, no synthetic success.
Source: backends/core/registry.go
FetchImageMetadata(ref) fetches real image configuration from Docker v2 registries:
- Resolve registry, authenticate (anonymous or cloud token)
- Fetch manifest (
GET /v2/{repo}/manifests/{tag}) - Fetch config blob (
GET /v2/{repo}/blobs/{config_digest}) - Parse OCI image config → Cmd, Entrypoint, Env, ExposedPorts, WorkingDir, Labels
- Extract layer digests, sizes, history
- Cache result in-memory
When fetch fails (registry unreachable, auth error), FetchImageMetadata
returns the real underlying error — there is no synthetic-metadata
fallback. Real auth failures surface as the original error, never as a
fabricated metadata stub.
All 6 cloud backends initialize ImageManager identically:
s.images = &core.ImageManager{
Base: s.BaseServer,
Auth: cloudcommon.NewAuthProvider(clients, logger, s.ctx),
Logger: logger,
}Then delegate image methods:
func (s *Server) ImagePull(ref, auth string) (io.ReadCloser, error) {
return s.images.Pull(ref, auth)
}
func (s *Server) ImagePush(name, tag, auth string) (io.ReadCloser, error) {
return s.images.Push(name, tag, auth)
}
// ... etc for Tag, Remove, Load, Build