Files
package-updater/internal/agent/updater/resolve.go

49 lines
1.5 KiB
Go

package updater
import (
"context"
"fmt"
"github.com/docker/docker/api/types/registry"
"github.com/shcizo/package-updater/internal/imageref"
)
// DistributionClient is the subset of the Docker SDK the Resolver needs.
// *client.Client satisfies it.
type DistributionClient interface {
DistributionInspect(ctx context.Context, imageRef, encodedRegistryAuth string) (registry.DistributionInspect, error)
}
// Resolver turns "repo:tag" into "repo:tag@digest" by asking the local
// daemon, which contacts the registry.
type Resolver struct {
cli DistributionClient
auth *RegistryAuth
}
// NewResolver returns a Resolver. auth may be nil (anonymous access).
func NewResolver(cli DistributionClient, auth *RegistryAuth) *Resolver {
return &Resolver{cli: cli, auth: auth}
}
// Resolve returns ref pinned to the digest its tag currently points at. It
// fails for a ref without a tag or with a digest already.
func (r *Resolver) Resolve(ctx context.Context, ref string) (string, error) {
p := imageref.Parse(ref)
switch {
case p.Digest != "":
return "", fmt.Errorf("image %q already has a digest", ref)
case p.Tag == "":
return "", fmt.Errorf("image %q has no tag to resolve", ref)
}
info, err := r.cli.DistributionInspect(ctx, ref, r.auth.For(p.Repo))
if err != nil {
return "", fmt.Errorf("resolve %s: %w", ref, err)
}
if info.Descriptor.Digest == "" {
return "", fmt.Errorf("resolve %s: registry returned no digest", ref)
}
return ref + "@" + info.Descriptor.Digest.String(), nil
}