func (p *v2Puller) pullV2Repository(ref reference.Named) (err error) { var refs []reference.Named taggedName := p.repoInfo.LocalName if tagged, isTagged := ref.(reference.Tagged); isTagged { taggedName, err = reference.WithTag(p.repoInfo.LocalName, tagged.Tag()) if err != nil { return err } refs = []reference.Named{taggedName} } else if digested, isDigested := ref.(reference.Digested); isDigested { taggedName, err = reference.WithDigest(p.repoInfo.LocalName, digested.Digest()) if err != nil { return err } refs = []reference.Named{taggedName} } else { manSvc, err := p.repo.Manifests(context.Background()) if err != nil { return err } tags, err := manSvc.Tags() if err != nil { return err } // This probably becomes a lot nicer after the manifest // refactor... for _, tag := range tags { tagRef, err := reference.WithTag(p.repoInfo.LocalName, tag) if err != nil { return err } refs = append(refs, tagRef) } } var layersDownloaded bool for _, pullRef := range refs { // pulledNew is true if either new layers were downloaded OR if existing images were newly tagged // TODO(tiborvass): should we change the name of `layersDownload`? What about message in WriteStatus? pulledNew, err := p.pullV2Tag(p.config.OutStream, pullRef) if err != nil { return err } layersDownloaded = layersDownloaded || pulledNew } writeStatus(taggedName.String(), p.config.OutStream, p.sf, layersDownloaded) return nil }
func (imh *imageManifestHandler) convertSchema2Manifest(schema2Manifest *schema2.DeserializedManifest) (distribution.Manifest, error) { targetDescriptor := schema2Manifest.Target() blobs := imh.Repository.Blobs(imh) configJSON, err := blobs.Get(imh, targetDescriptor.Digest) if err != nil { imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithDetail(err)) return nil, err } ref := imh.Repository.Named() if imh.Tag != "" { ref, err = reference.WithTag(ref, imh.Tag) if err != nil { imh.Errors = append(imh.Errors, v2.ErrorCodeTagInvalid.WithDetail(err)) return nil, err } } builder := schema1.NewConfigManifestBuilder(imh.Repository.Blobs(imh), imh.Context.App.trustKey, ref, configJSON) for _, d := range schema2Manifest.References() { if err := builder.AppendReference(d); err != nil { imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithDetail(err)) return nil, err } } manifest, err := builder.Build(imh) if err != nil { imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithDetail(err)) return nil, err } imh.Digest = digest.FromBytes(manifest.(*schema1.SignedManifest).Canonical) return manifest, nil }
// Get issues a HEAD request for a Manifest against its named endpoint in order // to construct a descriptor for the tag. If the registry doesn't support HEADing // a manifest, fallback to GET. func (t *tags) Get(ctx context.Context, tag string) (distribution.Descriptor, error) { ref, err := reference.WithTag(t.name, tag) if err != nil { return distribution.Descriptor{}, err } u, err := t.ub.BuildManifestURL(ref) if err != nil { return distribution.Descriptor{}, err } var attempts int resp, err := t.client.Head(u) check: if err != nil { return distribution.Descriptor{}, err } switch { case resp.StatusCode >= 200 && resp.StatusCode < 400: return descriptorFromResponse(resp) case resp.StatusCode == http.StatusMethodNotAllowed: resp, err = t.client.Get(u) attempts++ if attempts > 1 { return distribution.Descriptor{}, err } goto check default: return distribution.Descriptor{}, HandleErrorResponse(resp) } }
// WithTag combines the name from "name" and the tag from "tag" to form a // reference incorporating both the name and the tag. func WithTag(name Named, tag string) (NamedTagged, error) { r, err := distreference.WithTag(name, tag) if err != nil { return nil, err } return &taggedRef{namedRef{r}}, nil }
// sanitizeRepoAndTags parses the raw "t" parameter received from the client // to a slice of repoAndTag. // It also validates each repoName and tag. func sanitizeRepoAndTags(names []string) ([]reference.Named, error) { var ( repoAndTags []reference.Named // This map is used for deduplicating the "-t" paramter. uniqNames = make(map[string]struct{}) ) for _, repo := range names { if repo == "" { continue } ref, err := reference.ParseNamed(repo) if err != nil { return nil, err } if _, isDigested := ref.(reference.Digested); isDigested { return nil, errors.New("build tag cannot be a digest") } if _, isTagged := ref.(reference.Tagged); !isTagged { ref, err = reference.WithTag(ref, tagpkg.DefaultTag) } nameWithTag := ref.String() if _, exists := uniqNames[nameWithTag]; !exists { uniqNames[nameWithTag] = struct{}{} repoAndTags = append(repoAndTags, ref) } } return repoAndTags, nil }
// CmdPull pulls an image or a repository from the registry. // // Usage: docker pull [OPTIONS] IMAGENAME[:TAG|@DIGEST] func (cli *DockerCli) CmdPull(args ...string) error { cmd := Cli.Subcmd("pull", []string{"NAME[:TAG|@DIGEST]"}, Cli.DockerCommands["pull"].Description, true) allTags := cmd.Bool([]string{"a", "-all-tags"}, false, "Download all tagged images in the repository") addTrustedFlags(cmd, true) cmd.Require(flag.Exact, 1) cmd.ParseFlags(args, true) remote := cmd.Arg(0) distributionRef, err := reference.ParseNamed(remote) if err != nil { return err } var tag string switch x := distributionRef.(type) { case reference.Digested: if *allTags { return errTagCantBeUsed } tag = x.Digest().String() case reference.Tagged: if *allTags { return errTagCantBeUsed } tag = x.Tag() default: if !*allTags { tag = tagpkg.DefaultTag distributionRef, err = reference.WithTag(distributionRef, tag) if err != nil { return err } fmt.Fprintf(cli.out, "Using default tag: %s\n", tag) } } ref := registry.ParseReference(tag) // Resolve the Repository name from fqn to RepositoryInfo repoInfo, err := registry.ParseRepositoryInfo(distributionRef) if err != nil { return err } if isTrusted() && !ref.HasDigest() { // Check if tag is digest authConfig := registry.ResolveAuthConfig(cli.configFile, repoInfo.Index) return cli.trustedPull(repoInfo, ref, authConfig) } v := url.Values{} v.Set("fromImage", distributionRef.String()) _, _, err = cli.clientRequestAttemptLogin("POST", "/images/create?"+v.Encode(), nil, cli.out, repoInfo.Index, "pull") return err }
func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer) error { legacyLoadedMap := make(map[string]image.ID) dirs, err := ioutil.ReadDir(tmpDir) if err != nil { return err } // every dir represents an image for _, d := range dirs { if d.IsDir() { if err := l.legacyLoadImage(d.Name(), tmpDir, legacyLoadedMap); err != nil { return err } } } // load tags from repositories file repositoriesPath, err := safePath(tmpDir, legacyRepositoriesFileName) if err != nil { return err } repositoriesFile, err := os.Open(repositoriesPath) if err != nil { if !os.IsNotExist(err) { return err } return repositoriesFile.Close() } defer repositoriesFile.Close() repositories := make(map[string]map[string]string) if err := json.NewDecoder(repositoriesFile).Decode(&repositories); err != nil { return err } for name, tagMap := range repositories { for tag, oldID := range tagMap { imgID, ok := legacyLoadedMap[oldID] if !ok { return fmt.Errorf("invalid target ID: %v", oldID) } named, err := reference.WithName(name) if err != nil { return err } ref, err := reference.WithTag(named, tag) if err != nil { return err } l.setLoadedTag(ref, imgID, outStream) } } return nil }
func (s *router) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { metaHeaders := map[string][]string{} for k, v := range r.Header { if strings.HasPrefix(k, "X-Meta-") { metaHeaders[k] = v } } if err := httputils.ParseForm(r); err != nil { return err } authConfig := &cliconfig.AuthConfig{} authEncoded := r.Header.Get("X-Registry-Auth") if authEncoded != "" { // the new format is to handle the authConfig as a header authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil { // to increase compatibility to existing api it is defaulting to be empty authConfig = &cliconfig.AuthConfig{} } } else { // the old format is supported for compatibility if there was no authConfig header if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil { return fmt.Errorf("Bad parameters and missing X-Registry-Auth: %v", err) } } ref, err := reference.ParseNamed(vars["name"]) if err != nil { return err } tag := r.Form.Get("tag") if tag != "" { // Push by digest is not supported, so only tags are supported. ref, err = reference.WithTag(ref, tag) if err != nil { return err } } output := ioutils.NewWriteFlusher(w) defer output.Close() w.Header().Set("Content-Type", "application/json") if err := s.daemon.PushImage(ref, metaHeaders, authConfig, output); err != nil { if !output.Flushed() { return err } sf := streamformatter.NewJSONStreamFormatter() output.Write(sf.FormatError(err)) } return nil }
func defaultTagIfNameOnly(ref reference.Named) reference.Named { switch ref.(type) { case reference.Tagged: return ref case reference.Digested: return ref default: // Should never fail ref, _ = reference.WithTag(ref, DefaultTag) return ref } }
// Put puts a manifest. A tag can be specified using an options parameter which uses some shared state to hold the // tag name in order to build the correct upload URL. This state is written and read under a lock. func (ms *manifests) Put(ctx context.Context, m distribution.Manifest, options ...distribution.ManifestServiceOption) (digest.Digest, error) { ref := ms.name for _, option := range options { if opt, ok := option.(withTagOption); ok { var err error ref, err = reference.WithTag(ref, opt.tag) if err != nil { return "", err } } else { err := option.Apply(ms) if err != nil { return "", err } } } manifestURL, err := ms.ub.BuildManifestURL(ref) if err != nil { return "", err } mediaType, p, err := m.Payload() if err != nil { return "", err } putRequest, err := http.NewRequest("PUT", manifestURL, bytes.NewReader(p)) if err != nil { return "", err } putRequest.Header.Set("Content-Type", mediaType) resp, err := ms.client.Do(putRequest) if err != nil { return "", err } defer resp.Body.Close() if SuccessStatus(resp.StatusCode) { dgstHeader := resp.Header.Get("Docker-Content-Digest") dgst, err := digest.ParseDigest(dgstHeader) if err != nil { return "", err } return dgst, nil } return "", HandleErrorResponse(resp) }
func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.RepositoryData, img *registry.ImgData, layersDownloaded *bool) error { if img.Tag == "" { logrus.Debugf("Image (id: %s) present in this repository but untagged, skipping", img.ID) return nil } localNameRef, err := reference.WithTag(p.repoInfo.LocalName, img.Tag) if err != nil { retErr := fmt.Errorf("Image (id: %s) has invalid tag: %s", img.ID, img.Tag) logrus.Debug(retErr.Error()) return retErr } if err := v1.ValidateID(img.ID); err != nil { return err } progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s", img.Tag, p.repoInfo.CanonicalName.Name()) success := false var lastErr error for _, ep := range p.repoInfo.Index.Mirrors { ep += "v1/" progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, p.repoInfo.CanonicalName.Name(), ep)) if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil { // Don't report errors when pulling from mirrors. logrus.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, p.repoInfo.CanonicalName.Name(), ep, err) continue } success = true break } if !success { for _, ep := range repoData.Endpoints { progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s, endpoint: %s", img.Tag, p.repoInfo.CanonicalName.Name(), ep) if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil { // It's not ideal that only the last error is returned, it would be better to concatenate the errors. // As the error is also given to the output stream the user will see the error. lastErr = err progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Error pulling image (%s) from %s, endpoint: %s, %s", img.Tag, p.repoInfo.CanonicalName.Name(), ep, err) continue } success = true break } } if !success { err := fmt.Errorf("Error pulling image (%s) from %s, %v", img.Tag, p.repoInfo.CanonicalName.Name(), lastErr) progress.Update(p.config.ProgressOutput, stringid.TruncateID(img.ID), err.Error()) return err } progress.Update(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Download complete") return nil }
// Get issues a HEAD request for a Manifest against its named endpoint in order // to construct a descriptor for the tag. If the registry doesn't support HEADing // a manifest, fallback to GET. func (t *tags) Get(ctx context.Context, tag string) (distribution.Descriptor, error) { ref, err := reference.WithTag(t.name, tag) if err != nil { return distribution.Descriptor{}, err } u, err := t.ub.BuildManifestURL(ref) if err != nil { return distribution.Descriptor{}, err } req, err := http.NewRequest("HEAD", u, nil) if err != nil { return distribution.Descriptor{}, err } for _, t := range distribution.ManifestMediaTypes() { req.Header.Add("Accept", t) } var attempts int resp, err := t.client.Do(req) check: if err != nil { return distribution.Descriptor{}, err } defer resp.Body.Close() switch { case resp.StatusCode >= 200 && resp.StatusCode < 400: return descriptorFromResponse(resp) case resp.StatusCode == http.StatusMethodNotAllowed: req, err = http.NewRequest("GET", u, nil) if err != nil { return distribution.Descriptor{}, err } for _, t := range distribution.ManifestMediaTypes() { req.Header.Add("Accept", t) } resp, err = t.client.Do(req) attempts++ if attempts > 1 { return distribution.Descriptor{}, err } goto check default: return distribution.Descriptor{}, HandleErrorResponse(resp) } }
// Get issues a HEAD request for a Manifest against its named endpoint in order // to construct a descriptor for the tag. If the registry doesn't support HEADing // a manifest, fallback to GET. func (t *tags) Get(ctx context.Context, tag string) (distribution.Descriptor, error) { ref, err := reference.WithTag(t.name, tag) if err != nil { return distribution.Descriptor{}, err } u, err := t.ub.BuildManifestURL(ref) if err != nil { return distribution.Descriptor{}, err } newRequest := func(method string) (*http.Response, error) { req, err := http.NewRequest(method, u, nil) if err != nil { return nil, err } for _, t := range distribution.ManifestMediaTypes() { req.Header.Add("Accept", t) } resp, err := t.client.Do(req) return resp, err } resp, err := newRequest("HEAD") if err != nil { return distribution.Descriptor{}, err } defer resp.Body.Close() switch { case resp.StatusCode >= 200 && resp.StatusCode < 400: return descriptorFromResponse(resp) default: // if the response is an error - there will be no body to decode. // Issue a GET request: // - for data from a server that does not handle HEAD // - to get error details in case of a failure resp, err = newRequest("GET") if err != nil { return distribution.Descriptor{}, err } defer resp.Body.Close() if resp.StatusCode >= 200 && resp.StatusCode < 400 { return descriptorFromResponse(resp) } return distribution.Descriptor{}, HandleErrorResponse(resp) } }
// applyDefaultImageTag parses a docker image string, if it doesn't contain any tag or digest, // a default tag will be applied. func applyDefaultImageTag(image string) (string, error) { named, err := dockerref.ParseNamed(image) if err != nil { return "", fmt.Errorf("couldn't parse image reference %q: %v", image, err) } _, isTagged := named.(dockerref.Tagged) _, isDigested := named.(dockerref.Digested) if !isTagged && !isDigested { named, err := dockerref.WithTag(named, parsers.DefaultImageTag) if err != nil { return "", fmt.Errorf("failed to apply default image tag %q: %v", image, err) } image = named.String() } return image, nil }
// NormalizeLocalReference transforms a reference to use a normalized LocalName // for the name poriton. Passes through the reference without transformation on // error. func NormalizeLocalReference(ref reference.Named) reference.Named { localName := NormalizeLocalName(ref) if tagged, isTagged := ref.(reference.Tagged); isTagged { newRef, err := reference.WithTag(localName, tagged.Tag()) if err != nil { return ref } return newRef } else if digested, isDigested := ref.(reference.Digested); isDigested { newRef, err := reference.WithDigest(localName, digested.Digest()) if err != nil { return ref } return newRef } return localName }
// removeImageRef attempts to parse and remove the given image reference from // this daemon's store of repository tag/digest references. The given // repositoryRef must not be an image ID but a repository name followed by an // optional tag or digest reference. If tag or digest is omitted, the default // tag is used. Returns the resolved image reference and an error. func (daemon *Daemon) removeImageRef(ref reference.Named) (reference.Named, error) { switch ref.(type) { case reference.Tagged: case reference.Digested: default: var err error ref, err = reference.WithTag(ref, tagpkg.DefaultTag) if err != nil { return nil, err } } // Ignore the boolean value returned, as far as we're concerned, this // is an idempotent operation and it's okay if the reference didn't // exist in the first place. _, err := daemon.tagStore.Delete(ref) return ref, err }
// Warmup implements Registry func (nc *NameCache) Warmup(r string) error { ref, err := reference.ParseNamed(r) if err != nil { return errors.Errorf("%v for %v", err, r) } ts, err := nc.RegistryClient.AllTags(r) if err != nil { return errors.Wrap(err, "warming up") } for _, t := range ts { Log.Debug.Printf("Harvested tag: %v for repo: %v", t, r) in, err := reference.WithTag(ref, t) Log.Vomit.Print(in, err) if err == nil { a := NewBuildArtifact(in.String(), strpairs{}) nc.GetSourceID(a) //pull it into the cache... } } return nil }
func (s *router) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } repo := r.Form.Get("repo") tag := r.Form.Get("tag") newTag, err := reference.WithName(repo) if err != nil { return err } if tag != "" { if newTag, err = reference.WithTag(newTag, tag); err != nil { return err } } if err := s.daemon.TagImage(newTag, vars["name"]); err != nil { return err } w.WriteHeader(http.StatusCreated) return nil }
func updateName(rn reference.Named, name string) (ref reference.Named, err error) { nr, err := reference.ParseNamed(name) if err != nil { return } //log.Printf("updateName: %#v %#v", rn, nr) switch r := rn.(type) { default: return nil, fmt.Errorf("Image name has neither tag nor digest (%T)", rn) case reference.Digested: ref, err = reference.WithDigest(nr, r.Digest()) case reference.Tagged: ref, err = reference.WithTag(nr, r.Tag()) case reference.Named: ref = nr } return }
// Pull tells Docker to pull image referenced by `name`. func (d Docker) Pull(name string) (*image.Image, error) { ref, err := reference.ParseNamed(name) if err != nil { return nil, err } switch ref.(type) { case reference.Tagged: case reference.Digested: default: ref, err = reference.WithTag(ref, "latest") if err != nil { return nil, err } } pullRegistryAuth := &cliconfig.AuthConfig{} if len(d.AuthConfigs) > 0 { // The request came with a full auth config file, we prefer to use that repoInfo, err := d.Daemon.RegistryService.ResolveRepository(ref) if err != nil { return nil, err } resolvedConfig := registry.ResolveAuthConfig( &cliconfig.ConfigFile{AuthConfigs: d.AuthConfigs}, repoInfo.Index, ) pullRegistryAuth = &resolvedConfig } if err := d.Daemon.PullImage(ref, nil, pullRegistryAuth, ioutils.NopWriteCloser(d.OutOld)); err != nil { return nil, err } return d.Daemon.GetImage(name) }
func TestReferenceBuilder(t *testing.T) { pk, err := libtrust.GenerateECP256PrivateKey() if err != nil { t.Fatalf("unexpected error generating private key: %v", err) } r1 := Reference{ Digest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Size: 1, History: History{V1Compatibility: "{\"a\" : 1 }"}, } r2 := Reference{ Digest: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", Size: 2, History: History{V1Compatibility: "{\"\a\" : 2 }"}, } handCrafted := makeSignedManifest(t, pk, []Reference{r1, r2}) ref, err := reference.ParseNamed(handCrafted.Manifest.Name) if err != nil { t.Fatalf("could not parse reference: %v", err) } ref, err = reference.WithTag(ref, handCrafted.Manifest.Tag) if err != nil { t.Fatalf("could not add tag: %v", err) } b := NewReferenceManifestBuilder(pk, ref, handCrafted.Manifest.Architecture) _, err = b.Build(context.Background()) if err == nil { t.Fatal("Expected error building zero length manifest") } err = b.AppendReference(r1) if err != nil { t.Fatal(err) } err = b.AppendReference(r2) if err != nil { t.Fatal(err) } refs := b.References() if len(refs) != 2 { t.Fatalf("Unexpected reference count : %d != %d", 2, len(refs)) } // Ensure ordering if refs[0].Digest != r2.Digest { t.Fatalf("Unexpected reference : %v", refs[0]) } m, err := b.Build(context.Background()) if err != nil { t.Fatal(err) } built, ok := m.(*SignedManifest) if !ok { t.Fatalf("unexpected type from Build() : %T", built) } d1 := digest.FromBytes(built.Canonical) d2 := digest.FromBytes(handCrafted.Canonical) if d1 != d2 { t.Errorf("mismatching canonical JSON") } }
func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, imageID image.ID) error { logrus.Debugf("Pushing repository: %s", ref.String()) img, err := p.config.ImageStore.Get(imageID) if err != nil { return fmt.Errorf("could not find image from tag %s: %v", ref.String(), err) } var l layer.Layer topLayerID := img.RootFS.ChainID() if topLayerID == "" { l = layer.EmptyLayer } else { l, err = p.config.LayerStore.Get(topLayerID) if err != nil { return fmt.Errorf("failed to get top layer from image: %v", err) } defer layer.ReleaseAndLog(p.config.LayerStore, l) } var descriptors []xfer.UploadDescriptor descriptorTemplate := v2PushDescriptor{ v2MetadataService: p.v2MetadataService, repoInfo: p.repoInfo, repo: p.repo, pushState: &p.pushState, } // Loop bounds condition is to avoid pushing the base layer on Windows. for i := 0; i < len(img.RootFS.DiffIDs); i++ { descriptor := descriptorTemplate descriptor.layer = l descriptors = append(descriptors, &descriptor) l = l.Parent() } if err := p.config.UploadManager.Upload(ctx, descriptors, p.config.ProgressOutput); err != nil { return err } // Try schema2 first builder := schema2.NewManifestBuilder(p.repo.Blobs(ctx), img.RawJSON()) manifest, err := manifestFromBuilder(ctx, builder, descriptors) if err != nil { return err } manSvc, err := p.repo.Manifests(ctx) if err != nil { return err } putOptions := []distribution.ManifestServiceOption{client.WithTag(ref.Tag())} if _, err = manSvc.Put(ctx, manifest, putOptions...); err != nil { logrus.Warnf("failed to upload schema2 manifest: %v - falling back to schema1", err) manifestRef, err := distreference.WithTag(p.repo.Named(), ref.Tag()) if err != nil { return err } builder = schema1.NewConfigManifestBuilder(p.repo.Blobs(ctx), p.config.TrustKey, manifestRef, img.RawJSON()) manifest, err = manifestFromBuilder(ctx, builder, descriptors) if err != nil { return err } if _, err = manSvc.Put(ctx, manifest, putOptions...); err != nil { return err } } var canonicalManifest []byte switch v := manifest.(type) { case *schema1.SignedManifest: canonicalManifest = v.Canonical case *schema2.DeserializedManifest: _, canonicalManifest, err = v.Payload() if err != nil { return err } } manifestDigest := digest.FromBytes(canonicalManifest) progress.Messagef(p.config.ProgressOutput, "", "%s: digest: %s size: %d", ref.Tag(), manifestDigest, len(canonicalManifest)) // Signal digest to the trust client so it can sign the // push, if appropriate. progress.Aux(p.config.ProgressOutput, PushResult{Tag: ref.Tag(), Digest: manifestDigest, Size: len(canonicalManifest)}) return nil }
func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...distribution.ManifestServiceOption) (distribution.Manifest, error) { var ( digestOrTag string ref reference.Named err error contentDgst *digest.Digest ) for _, option := range options { if opt, ok := option.(distribution.WithTagOption); ok { digestOrTag = opt.Tag ref, err = reference.WithTag(ms.name, opt.Tag) if err != nil { return nil, err } } else if opt, ok := option.(contentDigestOption); ok { contentDgst = opt.digest } else { err := option.Apply(ms) if err != nil { return nil, err } } } if digestOrTag == "" { digestOrTag = dgst.String() ref, err = reference.WithDigest(ms.name, dgst) if err != nil { return nil, err } } u, err := ms.ub.BuildManifestURL(ref) if err != nil { return nil, err } req, err := http.NewRequest("GET", u, nil) if err != nil { return nil, err } for _, t := range distribution.ManifestMediaTypes() { req.Header.Add("Accept", t) } if _, ok := ms.etags[digestOrTag]; ok { req.Header.Set("If-None-Match", ms.etags[digestOrTag]) } resp, err := ms.client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode == http.StatusNotModified { return nil, distribution.ErrManifestNotModified } else if SuccessStatus(resp.StatusCode) { if contentDgst != nil { dgst, err := digest.ParseDigest(resp.Header.Get("Docker-Content-Digest")) if err == nil { *contentDgst = dgst } } mt := resp.Header.Get("Content-Type") body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } m, _, err := distribution.UnmarshalManifest(mt, body) if err != nil { return nil, err } return m, nil } return nil, HandleErrorResponse(resp) }
// Commit creates a new filesystem image from the current state of a container. // The image can optionally be tagged into a repository. func (daemon *Daemon) Commit(name string, c *ContainerCommitConfig) (string, error) { container, err := daemon.Get(name) if err != nil { return "", err } // It is not possible to commit a running container on Windows if runtime.GOOS == "windows" && container.IsRunning() { return "", fmt.Errorf("Windows does not support commit of a running container") } if c.Pause && !container.isPaused() { daemon.containerPause(container) defer daemon.containerUnpause(container) } if c.MergeConfigs { if err := runconfig.Merge(c.Config, container.Config); err != nil { return "", err } } rwTar, err := daemon.exportContainerRw(container) if err != nil { return "", err } defer func() { if rwTar != nil { rwTar.Close() } }() var history []image.History rootFS := image.NewRootFS() if container.ImageID != "" { img, err := daemon.imageStore.Get(container.ImageID) if err != nil { return "", err } history = img.History rootFS = img.RootFS } l, err := daemon.layerStore.Register(rwTar, rootFS.ChainID()) if err != nil { return "", err } defer layer.ReleaseAndLog(daemon.layerStore, l) h := image.History{ Author: c.Author, Created: time.Now().UTC(), CreatedBy: strings.Join(container.Config.Cmd.Slice(), " "), Comment: c.Comment, EmptyLayer: true, } if diffID := l.DiffID(); layer.DigestSHA256EmptyTar != diffID { h.EmptyLayer = false rootFS.Append(diffID) } history = append(history, h) config, err := json.Marshal(&image.Image{ V1Image: image.V1Image{ DockerVersion: dockerversion.Version, Config: c.Config, Architecture: runtime.GOARCH, OS: runtime.GOOS, Container: container.ID, ContainerConfig: *container.Config, Author: c.Author, Created: h.Created, }, RootFS: rootFS, History: history, }) if err != nil { return "", err } id, err := daemon.imageStore.Create(config) if err != nil { return "", err } if container.ImageID != "" { if err := daemon.imageStore.SetParent(id, container.ImageID); err != nil { return "", err } } if c.Repo != "" { newTag, err := reference.WithName(c.Repo) // todo: should move this to API layer if err != nil { return "", err } if c.Tag != "" { if newTag, err = reference.WithTag(newTag, c.Tag); err != nil { return "", err } } if err := daemon.TagImage(newTag, id.String(), true); err != nil { return "", err } } daemon.LogContainerEvent(container, "commit") return id.String(), nil }
// withDefaultTag adds a default tag to a reference if it only has a repo name. func withDefaultTag(ref reference.Named) reference.Named { if isNameOnly(ref) { ref, _ = reference.WithTag(ref, DefaultTag) } return ref }
func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id digest.Digest) error { logrus.Debugf("Pushing repository: %s", ref.String()) imgConfig, err := p.config.ImageStore.Get(id) if err != nil { return fmt.Errorf("could not find image from tag %s: %v", ref.String(), err) } rootfs, err := p.config.ImageStore.RootFSFromConfig(imgConfig) if err != nil { return fmt.Errorf("unable to get rootfs for image %s: %s", ref.String(), err) } l, err := p.config.LayerStore.Get(rootfs.ChainID()) if err != nil { return fmt.Errorf("failed to get top layer from image: %v", err) } defer l.Release() hmacKey, err := metadata.ComputeV2MetadataHMACKey(p.config.AuthConfig) if err != nil { return fmt.Errorf("failed to compute hmac key of auth config: %v", err) } var descriptors []xfer.UploadDescriptor descriptorTemplate := v2PushDescriptor{ v2MetadataService: p.v2MetadataService, hmacKey: hmacKey, repoInfo: p.repoInfo, ref: p.ref, repo: p.repo, pushState: &p.pushState, } // Loop bounds condition is to avoid pushing the base layer on Windows. for i := 0; i < len(rootfs.DiffIDs); i++ { descriptor := descriptorTemplate descriptor.layer = l descriptor.checkedDigests = make(map[digest.Digest]struct{}) descriptors = append(descriptors, &descriptor) l = l.Parent() } if err := p.config.UploadManager.Upload(ctx, descriptors, p.config.ProgressOutput); err != nil { return err } // Try schema2 first builder := schema2.NewManifestBuilder(p.repo.Blobs(ctx), p.config.ConfigMediaType, imgConfig) manifest, err := manifestFromBuilder(ctx, builder, descriptors) if err != nil { return err } manSvc, err := p.repo.Manifests(ctx) if err != nil { return err } putOptions := []distribution.ManifestServiceOption{distribution.WithTag(ref.Tag())} if _, err = manSvc.Put(ctx, manifest, putOptions...); err != nil { if runtime.GOOS == "windows" || p.config.TrustKey == nil || p.config.RequireSchema2 { logrus.Warnf("failed to upload schema2 manifest: %v", err) return err } logrus.Warnf("failed to upload schema2 manifest: %v - falling back to schema1", err) manifestRef, err := distreference.WithTag(p.repo.Named(), ref.Tag()) if err != nil { return err } builder = schema1.NewConfigManifestBuilder(p.repo.Blobs(ctx), p.config.TrustKey, manifestRef, imgConfig) manifest, err = manifestFromBuilder(ctx, builder, descriptors) if err != nil { return err } if _, err = manSvc.Put(ctx, manifest, putOptions...); err != nil { return err } } var canonicalManifest []byte switch v := manifest.(type) { case *schema1.SignedManifest: canonicalManifest = v.Canonical case *schema2.DeserializedManifest: _, canonicalManifest, err = v.Payload() if err != nil { return err } } manifestDigest := digest.FromBytes(canonicalManifest) progress.Messagef(p.config.ProgressOutput, "", "%s: digest: %s size: %d", ref.Tag(), manifestDigest, len(canonicalManifest)) if err := addDigestReference(p.config.ReferenceStore, ref, manifestDigest, id); err != nil { return err } // Signal digest to the trust client so it can sign the // push, if appropriate. progress.Aux(p.config.ProgressOutput, PushResult{Tag: ref.Tag(), Digest: manifestDigest, Size: len(canonicalManifest)}) return nil }
func restoreCustomImage(driver graphdriver.Driver, is image.Store, ls layer.Store, ts tag.Store) error { if wd, ok := driver.(*windows.Driver); ok { imageInfos, err := wd.GetCustomImageInfos() if err != nil { return err } // Convert imageData to valid image configuration for i := range imageInfos { name := strings.ToLower(imageInfos[i].Name) type registrar interface { RegisterDiffID(graphID string, size int64) (layer.Layer, error) } r, ok := ls.(registrar) if !ok { return errors.New("Layerstore doesn't support RegisterDiffID") } if _, err := r.RegisterDiffID(imageInfos[i].ID, imageInfos[i].Size); err != nil { return err } // layer is intentionally not released rootFS := image.NewRootFS() rootFS.BaseLayer = filepath.Base(imageInfos[i].Path) // Create history for base layer config, err := json.Marshal(&image.Image{ V1Image: image.V1Image{ DockerVersion: dockerversion.Version, Architecture: runtime.GOARCH, OS: runtime.GOOS, Created: imageInfos[i].CreatedTime, }, RootFS: rootFS, History: []image.History{}, }) named, err := reference.ParseNamed(name) if err != nil { return err } ref, err := reference.WithTag(named, imageInfos[i].Version) if err != nil { return err } id, err := is.Create(config) if err != nil { return err } if err := ts.AddTag(ref, id, true); err != nil { return err } logrus.Debugf("Registered base layer %s as %s", ref, id) } } return nil }
func (l *tarexporter) parseNames(names []string) (map[image.ID]*imageDescriptor, error) { imgDescr := make(map[image.ID]*imageDescriptor) addAssoc := func(id image.ID, ref reference.Named) { if _, ok := imgDescr[id]; !ok { imgDescr[id] = &imageDescriptor{} } if ref != nil { var tagged reference.NamedTagged if _, ok := ref.(reference.Digested); ok { return } var ok bool if tagged, ok = ref.(reference.NamedTagged); !ok { var err error if tagged, err = reference.WithTag(ref, tag.DefaultTag); err != nil { return } } for _, t := range imgDescr[id].refs { if tagged.String() == t.String() { return } } imgDescr[id].refs = append(imgDescr[id].refs, tagged) } } for _, name := range names { ref, err := reference.ParseNamed(name) if err != nil { return nil, err } ref = registry.NormalizeLocalReference(ref) if ref.Name() == string(digest.Canonical) { imgID, err := l.is.Search(name) if err != nil { return nil, err } addAssoc(imgID, nil) continue } if _, ok := ref.(reference.Digested); !ok { if _, ok := ref.(reference.NamedTagged); !ok { assocs := l.ts.ReferencesByName(ref) for _, assoc := range assocs { addAssoc(assoc.ImageID, assoc.Ref) } if len(assocs) == 0 { imgID, err := l.is.Search(name) if err != nil { return nil, err } addAssoc(imgID, nil) } continue } } var imgID image.ID if imgID, err = l.ts.Get(ref); err != nil { return nil, err } addAssoc(imgID, ref) } return imgDescr, nil }
func TestTokenPassThru(t *testing.T) { authConfig := &types.AuthConfig{ RegistryToken: "mysecrettoken", } gotToken := false handler := func(w http.ResponseWriter, r *http.Request) { if strings.Contains(r.Header.Get("Authorization"), authConfig.RegistryToken) { logrus.Debug("Detected registry token in auth header") gotToken = true } if r.RequestURI == "/v2/" { w.Header().Set("WWW-Authenticate", `Bearer realm="foorealm"`) w.WriteHeader(401) } } ts := httptest.NewServer(http.HandlerFunc(handler)) defer ts.Close() tmp, err := utils.TestDirectory("") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmp) endpoint := registry.APIEndpoint{ Mirror: false, URL: ts.URL, Version: 2, Official: false, TrimHostname: false, TLSConfig: nil, //VersionHeader: "verheader", Versions: []auth.APIVersion{ { Type: "registry", Version: "2", }, }, } n, _ := reference.ParseNamed("testremotename") repoInfo := ®istry.RepositoryInfo{ Index: ®istrytypes.IndexInfo{ Name: "testrepo", Mirrors: nil, Secure: false, Official: false, }, RemoteName: n, LocalName: n, CanonicalName: n, Official: false, } imagePullConfig := &ImagePullConfig{ MetaHeaders: http.Header{}, AuthConfig: authConfig, } puller, err := newPuller(endpoint, repoInfo, imagePullConfig) if err != nil { t.Fatal(err) } p := puller.(*v2Puller) p.repo, err = NewV2Repository(p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull") if err != nil { t.Fatal(err) } logrus.Debug("About to pull") // We expect it to fail, since we haven't mock'd the full registry exchange in our handler above tag, _ := reference.WithTag(n, "tag_goes_here") _ = p.pullV2Repository(context.Background(), tag) if !gotToken { t.Fatal("Failed to receive registry token") } }
func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registry.Reference, authConfig types.AuthConfig, requestPrivilege lib.RequestPrivilegeFunc) error { var refs []target notaryRepo, err := cli.getNotaryRepository(repoInfo, authConfig) if err != nil { fmt.Fprintf(cli.out, "Error establishing connection to trust repository: %s\n", err) return err } if ref.String() == "" { // List all targets targets, err := notaryRepo.ListTargets() if err != nil { return notaryError(err) } for _, tgt := range targets { t, err := convertTarget(*tgt) if err != nil { fmt.Fprintf(cli.out, "Skipping target for %q\n", repoInfo.LocalName) continue } refs = append(refs, t) } } else { t, err := notaryRepo.GetTargetByName(ref.String()) if err != nil { return notaryError(err) } r, err := convertTarget(*t) if err != nil { return err } refs = append(refs, r) } for i, r := range refs { displayTag := r.reference.String() if displayTag != "" { displayTag = ":" + displayTag } fmt.Fprintf(cli.out, "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), repoInfo.LocalName, displayTag, r.digest) if err := cli.imagePullPrivileged(authConfig, repoInfo.LocalName.Name(), r.digest.String(), requestPrivilege); err != nil { return err } // If reference is not trusted, tag by trusted reference if !r.reference.HasDigest() { tagged, err := reference.WithTag(repoInfo.LocalName, r.reference.String()) if err != nil { return err } trustedRef, err := reference.WithDigest(repoInfo.LocalName, r.digest) if err := cli.tagTrusted(trustedRef, tagged); err != nil { return err } } } return nil }