|
@@ -533,15 +533,18 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
|
|
}
|
|
}
|
|
|
|
|
|
configChan := make(chan []byte, 1)
|
|
configChan := make(chan []byte, 1)
|
|
- errChan := make(chan error, 1)
|
|
|
|
|
|
+ configErrChan := make(chan error, 1)
|
|
|
|
+ layerErrChan := make(chan error, 1)
|
|
|
|
+ downloadsDone := make(chan struct{})
|
|
var cancel func()
|
|
var cancel func()
|
|
ctx, cancel = context.WithCancel(ctx)
|
|
ctx, cancel = context.WithCancel(ctx)
|
|
|
|
+ defer cancel()
|
|
|
|
|
|
// Pull the image config
|
|
// Pull the image config
|
|
go func() {
|
|
go func() {
|
|
configJSON, err := p.pullSchema2Config(ctx, target.Digest)
|
|
configJSON, err := p.pullSchema2Config(ctx, target.Digest)
|
|
if err != nil {
|
|
if err != nil {
|
|
- errChan <- ImageConfigPullError{Err: err}
|
|
|
|
|
|
+ configErrChan <- ImageConfigPullError{Err: err}
|
|
cancel()
|
|
cancel()
|
|
return
|
|
return
|
|
}
|
|
}
|
|
@@ -552,6 +555,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
|
|
configJSON []byte // raw serialized image config
|
|
configJSON []byte // raw serialized image config
|
|
downloadedRootFS *image.RootFS // rootFS from registered layers
|
|
downloadedRootFS *image.RootFS // rootFS from registered layers
|
|
configRootFS *image.RootFS // rootFS from configuration
|
|
configRootFS *image.RootFS // rootFS from configuration
|
|
|
|
+ release func() // release resources from rootFS download
|
|
)
|
|
)
|
|
|
|
|
|
// https://github.com/docker/docker/issues/24766 - Err on the side of caution,
|
|
// https://github.com/docker/docker/issues/24766 - Err on the side of caution,
|
|
@@ -563,7 +567,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
|
|
// check to block Windows images being pulled on Linux is implemented, it
|
|
// check to block Windows images being pulled on Linux is implemented, it
|
|
// may be necessary to perform the same type of serialisation.
|
|
// may be necessary to perform the same type of serialisation.
|
|
if runtime.GOOS == "windows" {
|
|
if runtime.GOOS == "windows" {
|
|
- configJSON, configRootFS, err = receiveConfig(p.config.ImageStore, configChan, errChan)
|
|
|
|
|
|
+ configJSON, configRootFS, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
|
|
if err != nil {
|
|
if err != nil {
|
|
return "", "", err
|
|
return "", "", err
|
|
}
|
|
}
|
|
@@ -574,41 +578,52 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
|
|
}
|
|
}
|
|
|
|
|
|
if p.config.DownloadManager != nil {
|
|
if p.config.DownloadManager != nil {
|
|
- downloadRootFS := *image.NewRootFS()
|
|
|
|
- rootFS, release, err := p.config.DownloadManager.Download(ctx, downloadRootFS, descriptors, p.config.ProgressOutput)
|
|
|
|
- if err != nil {
|
|
|
|
- if configJSON != nil {
|
|
|
|
- // Already received the config
|
|
|
|
- return "", "", err
|
|
|
|
- }
|
|
|
|
- select {
|
|
|
|
- case err = <-errChan:
|
|
|
|
- return "", "", err
|
|
|
|
- default:
|
|
|
|
- cancel()
|
|
|
|
- select {
|
|
|
|
- case <-configChan:
|
|
|
|
- case <-errChan:
|
|
|
|
- }
|
|
|
|
- return "", "", err
|
|
|
|
|
|
+ go func() {
|
|
|
|
+ var (
|
|
|
|
+ err error
|
|
|
|
+ rootFS image.RootFS
|
|
|
|
+ )
|
|
|
|
+ downloadRootFS := *image.NewRootFS()
|
|
|
|
+ rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, descriptors, p.config.ProgressOutput)
|
|
|
|
+ if err != nil {
|
|
|
|
+ // Intentionally do not cancel the config download here
|
|
|
|
+ // as the error from config download (if there is one)
|
|
|
|
+ // is more interesting than the layer download error
|
|
|
|
+ layerErrChan <- err
|
|
|
|
+ return
|
|
}
|
|
}
|
|
- }
|
|
|
|
- if release != nil {
|
|
|
|
- defer release()
|
|
|
|
- }
|
|
|
|
|
|
|
|
- downloadedRootFS = &rootFS
|
|
|
|
|
|
+ downloadedRootFS = &rootFS
|
|
|
|
+ close(downloadsDone)
|
|
|
|
+ }()
|
|
|
|
+ } else {
|
|
|
|
+ // We have nothing to download
|
|
|
|
+ close(downloadsDone)
|
|
}
|
|
}
|
|
|
|
|
|
if configJSON == nil {
|
|
if configJSON == nil {
|
|
- configJSON, configRootFS, err = receiveConfig(p.config.ImageStore, configChan, errChan)
|
|
|
|
|
|
+ configJSON, configRootFS, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
|
|
|
|
+ if err == nil && configRootFS == nil {
|
|
|
|
+ err = errRootFSInvalid
|
|
|
|
+ }
|
|
if err != nil {
|
|
if err != nil {
|
|
|
|
+ cancel()
|
|
|
|
+ select {
|
|
|
|
+ case <-downloadsDone:
|
|
|
|
+ case <-layerErrChan:
|
|
|
|
+ }
|
|
return "", "", err
|
|
return "", "", err
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
|
|
- if configRootFS == nil {
|
|
|
|
- return "", "", errRootFSInvalid
|
|
|
|
- }
|
|
|
|
|
|
+ select {
|
|
|
|
+ case <-downloadsDone:
|
|
|
|
+ case err = <-layerErrChan:
|
|
|
|
+ return "", "", err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if release != nil {
|
|
|
|
+ defer release()
|
|
}
|
|
}
|
|
|
|
|
|
if downloadedRootFS != nil {
|
|
if downloadedRootFS != nil {
|