From 4dce280d96797b6a82e3eb54243e21bce7f55e5d Mon Sep 17 00:00:00 2001 From: Aaron Lehmann Date: Tue, 20 Oct 2015 16:05:21 -0700 Subject: [PATCH] Fix layer compression regression PR #15493 removed compression of layers when pushing them to a V2 registry. This this makes layer uploads larger than they should be. This commit restores the compression. It uses an io.Pipe to turn the gzip compressor output Writer into a Reader, so the ReadFrom method can be used on the BlobWriter (which is very important for avoiding many PATCH requests per layer). Fixes #17209 Fixes #17038 Signed-off-by: Aaron Lehmann --- graph/push_v2.go | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/graph/push_v2.go b/graph/push_v2.go index 70cb2e42a0..dd6de9016c 100644 --- a/graph/push_v2.go +++ b/graph/push_v2.go @@ -1,6 +1,7 @@ package graph import ( + "compress/gzip" "fmt" "io" "io/ioutil" @@ -236,11 +237,8 @@ func (p *v2Pusher) pushV2Image(bs distribution.BlobService, img *image.Image) (d } defer layerUpload.Close() - digester := digest.Canonical.New() - tee := io.TeeReader(arch, digester.Hash()) - reader := progressreader.New(progressreader.Config{ - In: ioutil.NopCloser(tee), // we'll take care of close here. + In: ioutil.NopCloser(arch), // we'll take care of close here. Out: out, Formatter: p.sf, @@ -254,8 +252,28 @@ func (p *v2Pusher) pushV2Image(bs distribution.BlobService, img *image.Image) (d Action: "Pushing", }) + digester := digest.Canonical.New() + // HACK: The MultiWriter doesn't write directly to layerUpload because + // we must make sure the ReadFrom is used, not Write. Using Write would + // send a PATCH request for every Write call. + pipeReader, pipeWriter := io.Pipe() + compressor := gzip.NewWriter(io.MultiWriter(pipeWriter, digester.Hash())) + + go func() { + _, err := io.Copy(compressor, reader) + if err == nil { + err = compressor.Close() + } + if err != nil { + pipeWriter.CloseWithError(err) + } else { + pipeWriter.Close() + } + }() + out.Write(p.sf.FormatProgress(stringid.TruncateID(img.ID), "Pushing", nil)) - nn, err := io.Copy(layerUpload, reader) + nn, err := layerUpload.ReadFrom(pipeReader) + pipeReader.Close() if err != nil { return "", err }