Skip to content

Commit

Permalink
copy: wait for progress bars before cleaning up
Browse files Browse the repository at this point in the history
Wait for all progress bars to finish before cleaning up.  In case of
errors, clean up immediately.  Waiting for all bars closes a race
condition where a bar might not be rendered before being cleaned up.

Fixes: containers#1013
Signed-off-by: Valentin Rothberg <[email protected]>
  • Loading branch information
vrothberg committed Aug 17, 2020
1 parent dd428be commit 8c68dc9
Showing 1 changed file with 13 additions and 7 deletions.
20 changes: 13 additions & 7 deletions copy/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,7 @@ func isTTY(w io.Writer) bool {
}

// copyLayers copies layers from ic.src/ic.c.rawSource to dest, using and updating ic.manifestUpdates if necessary and ic.canModifyManifest.
func (ic *imageCopier) copyLayers(ctx context.Context) error {
func (ic *imageCopier) copyLayers(ctx context.Context) (finalErr error) {
srcInfos := ic.src.LayerInfos()
numLayers := len(srcInfos)
updatedSrcInfos, err := ic.src.LayerInfosForCopy(ctx)
Expand Down Expand Up @@ -852,7 +852,7 @@ func (ic *imageCopier) copyLayers(ctx context.Context) error {
defer func() {
// Wait for all layers to be copied. progressCleanup() must not be called while any of the copyLayerHelpers interact with the progressPool.
copyGroup.Wait()
progressCleanup()
progressCleanup(finalErr == nil)
}()

for i, srcLayer := range srcInfos {
Expand Down Expand Up @@ -951,13 +951,17 @@ func (ic *imageCopier) copyUpdatedConfigAndManifest(ctx context.Context, instanc
}

// newProgressPool creates a *mpb.Progress and a cleanup function.
// The caller must eventually call the returned cleanup function after the pool will no longer be updated.
func (c *copier) newProgressPool(ctx context.Context) (*mpb.Progress, func()) {
// The caller must eventually call the returned cleanup function after the pool
// will no longer be updated. The `wait` argument of the cleanup function
// controls whether we wait until all progress bars have finished.
func (c *copier) newProgressPool(ctx context.Context) (*mpb.Progress, func(wait bool)) {
ctx, cancel := context.WithCancel(ctx)
pool := mpb.NewWithContext(ctx, mpb.WithWidth(40), mpb.WithOutput(c.progressOutput))
return pool, func() {
if wait {
pool.Wait()
}
cancel()
pool.Wait()
}
}

Expand Down Expand Up @@ -1007,7 +1011,7 @@ func (c *copier) createProgressBar(pool *mpb.Progress, info types.BlobInfo, kind
}

// copyConfig copies config.json, if any, from src to dest.
func (c *copier) copyConfig(ctx context.Context, src types.Image) error {
func (c *copier) copyConfig(ctx context.Context, src types.Image) (finalErr error) {
srcInfo := src.ConfigInfo()
if srcInfo.Digest != "" {
configBlob, err := src.ConfigBlob(ctx)
Expand All @@ -1017,7 +1021,9 @@ func (c *copier) copyConfig(ctx context.Context, src types.Image) error {

destInfo, err := func() (types.BlobInfo, error) { // A scope for defer
progressPool, progressCleanup := c.newProgressPool(ctx)
defer progressCleanup()
defer func(){
progressCleanup(finalErr == nil)
}()
bar := c.createProgressBar(progressPool, srcInfo, "config", "done")
destInfo, err := c.copyBlobFromStream(ctx, bytes.NewReader(configBlob), srcInfo, nil, false, true, false, bar)
if err != nil {
Expand Down

0 comments on commit 8c68dc9

Please sign in to comment.