62 lines
1.9 KiB
Go
62 lines
1.9 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. A ref that already has a digest is
|
|
// passed through.
|
|
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. A ref
|
|
// that already has a digest ("repo:tag@digest" or "repo@digest") is returned
|
|
// unchanged without asking the daemon. It fails for a ref with neither tag nor
|
|
// digest.
|
|
func (r *Resolver) Resolve(ctx context.Context, ref string) (string, error) {
|
|
p := imageref.Parse(ref)
|
|
switch {
|
|
case p.Digest != "":
|
|
return ref, nil
|
|
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
|
|
}
|
|
|
|
// resolveNote is the job-output line that says which ref is deployed:
|
|
// "resolved" when pinned came from resolving a tag, "pinned" when ref already
|
|
// carried its digest.
|
|
func resolveNote(ref, pinned string) string {
|
|
if pinned == ref {
|
|
return "pinned " + pinned
|
|
}
|
|
return "resolved " + pinned
|
|
}
|