// TrustedReference returns the canonical trusted reference for an image reference func TrustedReference(ctx context.Context, cli *command.DockerCli, ref reference.NamedTagged) (reference.Canonical, error) { repoInfo, err := registry.ParseRepositoryInfo(ref) if err != nil { return nil, err } // Resolve the Auth config relevant for this server authConfig := command.ResolveAuthConfig(ctx, cli, repoInfo.Index) notaryRepo, err := trust.GetNotaryRepository(cli, repoInfo, authConfig, "pull") if err != nil { fmt.Fprintf(cli.Out(), "Error establishing connection to trust repository: %s\n", err) return nil, err } t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole) if err != nil { return nil, err } // Only list tags in the top level targets role or the releases delegation role - ignore // all other delegation roles if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole { return nil, trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", ref.Tag())) } r, err := convertTarget(t.Target) if err != nil { return nil, err } return reference.WithDigest(reference.TrimNamed(ref), r.digest) }
// PullImage initiates a pull operation. image is the repository name to pull, and // tag may be either empty, or indicate a specific tag to pull. func (daemon *Daemon) PullImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { // Special case: "pull -a" may send an image name with a // trailing :. This is ugly, but let's not break API // compatibility. image = strings.TrimSuffix(image, ":") ref, err := reference.ParseNamed(image) if err != nil { return err } if tag != "" { // The "tag" could actually be a digest. var dgst digest.Digest dgst, err = digest.ParseDigest(tag) if err == nil { ref, err = reference.WithDigest(reference.TrimNamed(ref), dgst) } else { ref, err = reference.WithTag(ref, tag) } if err != nil { return err } } return daemon.pullImageWithReference(ctx, ref, metaHeaders, authConfig, outStream) }
// pullManifestList handles "manifest lists" which point to various // platform-specifc manifests. func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mfstList *manifestlist.DeserializedManifestList) (id digest.Digest, manifestListDigest digest.Digest, err error) { manifestListDigest, err = schema2ManifestDigest(ref, mfstList) if err != nil { return "", "", err } var manifestDigest digest.Digest for _, manifestDescriptor := range mfstList.Manifests { // TODO(aaronl): The manifest list spec supports optional // "features" and "variant" fields. These are not yet used. // Once they are, their values should be interpreted here. if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == runtime.GOOS { manifestDigest = manifestDescriptor.Digest break } } if manifestDigest == "" { return "", "", errors.New("no supported platform found in manifest list") } manSvc, err := p.repo.Manifests(ctx) if err != nil { return "", "", err } manifest, err := manSvc.Get(ctx, manifestDigest) if err != nil { return "", "", err } manifestRef, err := reference.WithDigest(reference.TrimNamed(ref), manifestDigest) if err != nil { return "", "", err } switch v := manifest.(type) { case *schema1.SignedManifest: id, _, err = p.pullSchema1(ctx, manifestRef, v) if err != nil { return "", "", err } case *schema2.DeserializedManifest: id, _, err = p.pullSchema2(ctx, manifestRef, v) if err != nil { return "", "", err } default: return "", "", errors.New("unsupported manifest format") } return id, manifestListDigest, err }
func addDigestReference(store reference.Store, ref reference.Named, dgst digest.Digest, id digest.Digest) error { dgstRef, err := reference.WithDigest(reference.TrimNamed(ref), dgst) if err != nil { return err } if oldTagID, err := store.Get(dgstRef); err == nil { if oldTagID != id { // Updating digests not supported by reference store logrus.Errorf("Image ID for digest %s changed from %s to %s, cannot update", dgst.String(), oldTagID, id) } return nil } else if err != reference.ErrDoesNotExist { return err } return store.AddDigest(dgstRef, id, true) }
// trustedPull handles content trust pulling of an image func trustedPull(ctx context.Context, cli *command.DockerCli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error { var refs []target notaryRepo, err := trust.GetNotaryRepository(cli, repoInfo, authConfig, "pull") if err != nil { fmt.Fprintf(cli.Out(), "Error establishing connection to trust repository: %s\n", err) return err } if tagged, isTagged := ref.(reference.NamedTagged); !isTagged { // List all targets targets, err := notaryRepo.ListTargets(trust.ReleasesRole, data.CanonicalTargetsRole) if err != nil { return trust.NotaryError(repoInfo.FullName(), err) } for _, tgt := range targets { t, err := convertTarget(tgt.Target) if err != nil { fmt.Fprintf(cli.Out(), "Skipping target for %q\n", repoInfo.Name()) continue } // Only list tags in the top level targets role or the releases delegation role - ignore // all other delegation roles if tgt.Role != trust.ReleasesRole && tgt.Role != data.CanonicalTargetsRole { continue } refs = append(refs, t) } if len(refs) == 0 { return trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trusted tags for %s", repoInfo.FullName())) } } else { t, err := notaryRepo.GetTargetByName(tagged.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole) if err != nil { return trust.NotaryError(repoInfo.FullName(), err) } // Only get the tag if it's in the top level targets role or the releases delegation role // ignore it if it's in any other delegation roles if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole { return trust.NotaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", tagged.Tag())) } logrus.Debugf("retrieving target for %s role\n", t.Role) r, err := convertTarget(t.Target) if err != nil { return err } refs = append(refs, r) } for i, r := range refs { displayTag := r.name if displayTag != "" { displayTag = ":" + displayTag } fmt.Fprintf(cli.Out(), "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), repoInfo.Name(), displayTag, r.digest) ref, err := reference.WithDigest(reference.TrimNamed(repoInfo), r.digest) if err != nil { return err } if err := imagePullPrivileged(ctx, cli, authConfig, ref.String(), requestPrivilege, false); err != nil { return err } tagged, err := reference.WithTag(repoInfo, r.name) if err != nil { return err } trustedRef, err := reference.WithDigest(reference.TrimNamed(repoInfo), r.digest) if err != nil { return err } if err := TagTrusted(ctx, cli, trustedRef, tagged); err != nil { return err } } return nil }
func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image.ID) error { migrationFile := filepath.Join(root, migrationTagsFileName) if _, err := os.Lstat(migrationFile); !os.IsNotExist(err) { return err } type repositories struct { Repositories map[string]map[string]string } var repos repositories f, err := os.Open(filepath.Join(root, repositoriesFilePrefixLegacy+driverName)) if err != nil { if os.IsNotExist(err) { return nil } return err } defer f.Close() if err := json.NewDecoder(f).Decode(&repos); err != nil { return err } for name, repo := range repos.Repositories { for tag, id := range repo { if strongID, exists := mappings[id]; exists { ref, err := reference.WithName(name) if err != nil { logrus.Errorf("migrate tags: invalid name %q, %q", name, err) continue } if dgst, err := digest.ParseDigest(tag); err == nil { canonical, err := reference.WithDigest(reference.TrimNamed(ref), dgst) if err != nil { logrus.Errorf("migrate tags: invalid digest %q, %q", dgst, err) continue } if err := rs.AddDigest(canonical, strongID.Digest(), false); err != nil { logrus.Errorf("can't migrate digest %q for %q, err: %q", ref.String(), strongID, err) } } else { tagRef, err := reference.WithTag(ref, tag) if err != nil { logrus.Errorf("migrate tags: invalid tag %q, %q", tag, err) continue } if err := rs.AddTag(tagRef, strongID.Digest(), false); err != nil { logrus.Errorf("can't migrate tag %q for %q, err: %q", ref.String(), strongID, err) } } logrus.Infof("migrated tag %s:%s to point to %s", name, tag, strongID) } } } mf, err := os.Create(migrationFile) if err != nil { return err } mf.Close() return nil }
func (pr *pluginRouter) pullPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return errors.Wrap(err, "failed to parse form") } var privileges types.PluginPrivileges dec := json.NewDecoder(r.Body) if err := dec.Decode(&privileges); err != nil { return errors.Wrap(err, "failed to parse privileges") } if dec.More() { return errors.New("invalid privileges") } metaHeaders, authConfig := parseHeaders(r.Header) ref, tag, err := parseRemoteRef(r.FormValue("remote")) if err != nil { return err } name := r.FormValue("name") if name == "" { if _, ok := ref.(reference.Canonical); ok { trimmed := reference.TrimNamed(ref) if tag != "" { nt, err := reference.WithTag(trimmed, tag) if err != nil { return err } name = nt.String() } else { name = reference.WithDefaultTag(trimmed).String() } } else { name = ref.String() } } else { localRef, err := reference.ParseNamed(name) if err != nil { return err } if _, ok := localRef.(reference.Canonical); ok { return errors.New("cannot use digest in plugin tag") } if distreference.IsNameOnly(localRef) { // TODO: log change in name to out stream name = reference.WithDefaultTag(localRef).String() } } w.Header().Set("Docker-Plugin-Name", name) w.Header().Set("Content-Type", "application/json") output := ioutils.NewWriteFlusher(w) if err := pr.backend.Pull(ctx, ref, name, metaHeaders, authConfig, privileges, output); err != nil { if !output.Flushed() { return err } output.Write(streamformatter.NewJSONStreamFormatter().FormatError(err)) } return nil }