Fix races accessing d.poolKey and d.tmpFile when pullV2Tag returns

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
This commit is contained in:
Aaron Lehmann 2015-08-31 16:43:44 -07:00
Родитель 23e68679f0
Коммит 64faec8269
1 изменённых файлов: 33 добавлений и 39 удалений

Просмотреть файл

@ -110,7 +110,6 @@ type downloadInfo struct {
layer distribution.ReadSeekCloser layer distribution.ReadSeekCloser
size int64 size int64
err chan error err chan error
out io.Writer // Download progress is written here.
poolKey string poolKey string
broadcaster *progressreader.Broadcaster broadcaster *progressreader.Broadcaster
} }
@ -122,22 +121,6 @@ func (errVerification) Error() string { return "verification failed" }
func (p *v2Puller) download(di *downloadInfo) { func (p *v2Puller) download(di *downloadInfo) {
logrus.Debugf("pulling blob %q to %s", di.digest, di.img.ID) logrus.Debugf("pulling blob %q to %s", di.digest, di.img.ID)
di.poolKey = "layer:" + di.img.ID
broadcaster, found := p.poolAdd("pull", di.poolKey)
broadcaster.Add(di.out)
di.broadcaster = broadcaster
if found {
di.err <- nil
return
}
tmpFile, err := ioutil.TempFile("", "GetImageBlob")
if err != nil {
di.err <- err
return
}
di.tmpFile = tmpFile
blobs := p.repo.Blobs(context.Background()) blobs := p.repo.Blobs(context.Background())
desc, err := blobs.Stat(context.Background(), di.digest) desc, err := blobs.Stat(context.Background(), di.digest)
@ -164,16 +147,16 @@ func (p *v2Puller) download(di *downloadInfo) {
reader := progressreader.New(progressreader.Config{ reader := progressreader.New(progressreader.Config{
In: ioutil.NopCloser(io.TeeReader(layerDownload, verifier)), In: ioutil.NopCloser(io.TeeReader(layerDownload, verifier)),
Out: broadcaster, Out: di.broadcaster,
Formatter: p.sf, Formatter: p.sf,
Size: di.size, Size: di.size,
NewLines: false, NewLines: false,
ID: stringid.TruncateID(di.img.ID), ID: stringid.TruncateID(di.img.ID),
Action: "Downloading", Action: "Downloading",
}) })
io.Copy(tmpFile, reader) io.Copy(di.tmpFile, reader)
broadcaster.Write(p.sf.FormatProgress(stringid.TruncateID(di.img.ID), "Verifying Checksum", nil)) di.broadcaster.Write(p.sf.FormatProgress(stringid.TruncateID(di.img.ID), "Verifying Checksum", nil))
if !verifier.Verified() { if !verifier.Verified() {
err = fmt.Errorf("filesystem layer verification failed for digest %s", di.digest) err = fmt.Errorf("filesystem layer verification failed for digest %s", di.digest)
@ -182,9 +165,9 @@ func (p *v2Puller) download(di *downloadInfo) {
return return
} }
broadcaster.Write(p.sf.FormatProgress(stringid.TruncateID(di.img.ID), "Download complete", nil)) di.broadcaster.Write(p.sf.FormatProgress(stringid.TruncateID(di.img.ID), "Download complete", nil))
logrus.Debugf("Downloaded %s to tempfile %s", di.img.ID, tmpFile.Name()) logrus.Debugf("Downloaded %s to tempfile %s", di.img.ID, di.tmpFile.Name())
di.layer = layerDownload di.layer = layerDownload
di.err <- nil di.err <- nil
@ -244,6 +227,16 @@ func (p *v2Puller) pullV2Tag(out io.Writer, tag, taggedName string) (verified bo
var layerIDs []string var layerIDs []string
defer func() { defer func() {
p.graph.Release(p.sessionID, layerIDs...) p.graph.Release(p.sessionID, layerIDs...)
for _, d := range downloads {
p.poolRemoveWithError("pull", d.poolKey, err)
if d.tmpFile != nil {
d.tmpFile.Close()
if err := os.RemoveAll(d.tmpFile.Name()); err != nil {
logrus.Errorf("Failed to remove temp file: %s", d.tmpFile.Name())
}
}
}
}() }()
for i := len(manifest.FSLayers) - 1; i >= 0; i-- { for i := len(manifest.FSLayers) - 1; i >= 0; i-- {
@ -264,30 +257,31 @@ func (p *v2Puller) pullV2Tag(out io.Writer, tag, taggedName string) (verified bo
out.Write(p.sf.FormatProgress(stringid.TruncateID(img.ID), "Pulling fs layer", nil)) out.Write(p.sf.FormatProgress(stringid.TruncateID(img.ID), "Pulling fs layer", nil))
d := &downloadInfo{ d := &downloadInfo{
img: img, img: img,
digest: manifest.FSLayers[i].BlobSum, poolKey: "layer:" + img.ID,
digest: manifest.FSLayers[i].BlobSum,
// TODO: seems like this chan buffer solved hanging problem in go1.5, // TODO: seems like this chan buffer solved hanging problem in go1.5,
// this can indicate some deeper problem that somehow we never take // this can indicate some deeper problem that somehow we never take
// error from channel in loop below // error from channel in loop below
err: make(chan error, 1), err: make(chan error, 1),
out: pipeWriter,
} }
tmpFile, err := ioutil.TempFile("", "GetImageBlob")
if err != nil {
return false, err
}
d.tmpFile = tmpFile
downloads = append(downloads, d) downloads = append(downloads, d)
go p.download(d) broadcaster, found := p.poolAdd("pull", d.poolKey)
} broadcaster.Add(pipeWriter)
d.broadcaster = broadcaster
// run clean for all downloads to prevent leftovers if found {
for _, d := range downloads { d.err <- nil
defer func(d *downloadInfo) { } else {
p.poolRemoveWithError("pull", d.poolKey, err) go p.download(d)
if d.tmpFile != nil { }
d.tmpFile.Close()
if err := os.RemoveAll(d.tmpFile.Name()); err != nil {
logrus.Errorf("Failed to remove temp file: %s", d.tmpFile.Name())
}
}
}(d)
} }
var tagUpdated bool var tagUpdated bool