func (p *v2Pusher) pushLayerIfNecessary(out io.Writer, l layer.Layer) (digest.Digest, error) { logrus.Debugf("Pushing layer: %s", l.DiffID()) // Do we have any blobsums associated with this layer's DiffID? possibleBlobsums, err := p.blobSumService.GetBlobSums(l.DiffID()) if err == nil { dgst, exists, err := p.blobSumAlreadyExists(possibleBlobsums) if err != nil { out.Write(p.sf.FormatProgress(stringid.TruncateID(string(l.DiffID())), "Image push failed", nil)) return "", err } if exists { out.Write(p.sf.FormatProgress(stringid.TruncateID(string(l.DiffID())), "Layer already exists", nil)) return dgst, nil } } // if digest was empty or not saved, or if blob does not exist on the remote repository, // then push the blob. pushDigest, err := p.pushV2Layer(p.repo.Blobs(context.Background()), l) if err != nil { return "", err } // Cache mapping from this layer's DiffID to the blobsum if err := p.blobSumService.Add(l.DiffID(), pushDigest); err != nil { return "", err } return pushDigest, nil }
func (p *v2Pusher) pushV2Layer(bs distribution.BlobService, l layer.Layer) (digest.Digest, error) { out := p.config.OutStream displayID := stringid.TruncateID(string(l.DiffID())) out.Write(p.sf.FormatProgress(displayID, "Preparing", nil)) arch, err := l.TarStream() if err != nil { return "", err } // Send the layer layerUpload, err := bs.Create(context.Background()) if err != nil { return "", err } defer layerUpload.Close() // don't care if this fails; best effort size, _ := l.DiffSize() reader := progressreader.New(progressreader.Config{ In: ioutil.NopCloser(arch), // we'll take care of close here. Out: out, Formatter: p.sf, Size: size, NewLines: false, ID: displayID, Action: "Pushing", }) compressedReader := compress(reader) digester := digest.Canonical.New() tee := io.TeeReader(compressedReader, digester.Hash()) out.Write(p.sf.FormatProgress(displayID, "Pushing", nil)) nn, err := layerUpload.ReadFrom(tee) compressedReader.Close() if err != nil { return "", err } dgst := digester.Digest() if _, err := layerUpload.Commit(context.Background(), distribution.Descriptor{Digest: dgst}); err != nil { return "", err } logrus.Debugf("uploaded layer %s (%s), %d bytes", l.DiffID(), dgst, nn) out.Write(p.sf.FormatProgress(displayID, "Pushed", nil)) return dgst, nil }
func (p *v2Pusher) pushV2Tag(association tag.Association) error { ref := association.Ref logrus.Debugf("Pushing repository: %s", ref.String()) img, err := p.config.ImageStore.Get(association.ImageID) if err != nil { return fmt.Errorf("could not find image from tag %s: %v", ref.String(), err) } out := p.config.OutStream 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) } fsLayers := make(map[layer.DiffID]schema1.FSLayer) // Push empty layer if necessary for _, h := range img.History { if h.EmptyLayer { dgst, err := p.pushLayerIfNecessary(out, layer.EmptyLayer) if err != nil { return err } p.layersPushed[dgst] = true fsLayers[layer.EmptyLayer.DiffID()] = schema1.FSLayer{BlobSum: dgst} break } } for i := 0; i < len(img.RootFS.DiffIDs); i++ { dgst, err := p.pushLayerIfNecessary(out, l) if err != nil { return err } p.layersPushed[dgst] = true fsLayers[l.DiffID()] = schema1.FSLayer{BlobSum: dgst} l = l.Parent() } var tag string if tagged, isTagged := ref.(reference.Tagged); isTagged { tag = tagged.Tag() } m, err := CreateV2Manifest(p.repo.Name(), tag, img, fsLayers) if err != nil { return err } logrus.Infof("Signed manifest for %s using daemon's key: %s", ref.String(), p.config.TrustKey.KeyID()) signed, err := schema1.Sign(m, p.config.TrustKey) if err != nil { return err } manifestDigest, manifestSize, err := digestFromManifest(signed, p.repo.Name()) if err != nil { return err } if manifestDigest != "" { if tagged, isTagged := ref.(reference.Tagged); isTagged { // NOTE: do not change this format without first changing the trust client // code. This information is used to determine what was pushed and should be signed. out.Write(p.sf.FormatStatus("", "%s: digest: %s size: %d", tagged.Tag(), manifestDigest, manifestSize)) } } manSvc, err := p.repo.Manifests(context.Background()) if err != nil { return err } return manSvc.Put(signed) }