Skip to content

Commit c31f73a

Browse files
authored
Merge pull request moby#29001 from darrenstahlmsft/WindowsOnLinux
Block pulling Windows images on non-Windows daemons
2 parents bd81a16 + d553040 commit c31f73a

File tree

3 files changed

+55
-30
lines changed

3 files changed

+55
-30
lines changed

distribution/config.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,12 @@ func (s *imageConfigStore) RootFSFromConfig(c []byte) (*image.RootFS, error) {
142142
return nil, err
143143
}
144144

145-
// fail immediately on windows
145+
// fail immediately on Windows when downloading a non-Windows image
146+
// and vice versa
146147
if runtime.GOOS == "windows" && unmarshalledConfig.OS == "linux" {
147148
return nil, fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
149+
} else if runtime.GOOS != "windows" && unmarshalledConfig.OS == "windows" {
150+
return nil, fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
148151
}
149152

150153
return unmarshalledConfig.RootFS, nil

distribution/pull_v2.go

+44-29
Original file line numberDiff line numberDiff line change
@@ -534,15 +534,18 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
534534
}
535535

536536
configChan := make(chan []byte, 1)
537-
errChan := make(chan error, 1)
537+
configErrChan := make(chan error, 1)
538+
layerErrChan := make(chan error, 1)
539+
downloadsDone := make(chan struct{})
538540
var cancel func()
539541
ctx, cancel = context.WithCancel(ctx)
542+
defer cancel()
540543

541544
// Pull the image config
542545
go func() {
543546
configJSON, err := p.pullSchema2Config(ctx, target.Digest)
544547
if err != nil {
545-
errChan <- ImageConfigPullError{Err: err}
548+
configErrChan <- ImageConfigPullError{Err: err}
546549
cancel()
547550
return
548551
}
@@ -553,6 +556,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
553556
configJSON []byte // raw serialized image config
554557
downloadedRootFS *image.RootFS // rootFS from registered layers
555558
configRootFS *image.RootFS // rootFS from configuration
559+
release func() // release resources from rootFS download
556560
)
557561

558562
// https://github.com/docker/docker/issues/24766 - Err on the side of caution,
@@ -564,7 +568,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
564568
// check to block Windows images being pulled on Linux is implemented, it
565569
// may be necessary to perform the same type of serialisation.
566570
if runtime.GOOS == "windows" {
567-
configJSON, configRootFS, err = receiveConfig(p.config.ImageStore, configChan, errChan)
571+
configJSON, configRootFS, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
568572
if err != nil {
569573
return "", "", err
570574
}
@@ -575,41 +579,52 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
575579
}
576580

577581
if p.config.DownloadManager != nil {
578-
downloadRootFS := *image.NewRootFS()
579-
rootFS, release, err := p.config.DownloadManager.Download(ctx, downloadRootFS, descriptors, p.config.ProgressOutput)
580-
if err != nil {
581-
if configJSON != nil {
582-
// Already received the config
583-
return "", "", err
584-
}
585-
select {
586-
case err = <-errChan:
587-
return "", "", err
588-
default:
589-
cancel()
590-
select {
591-
case <-configChan:
592-
case <-errChan:
593-
}
594-
return "", "", err
582+
go func() {
583+
var (
584+
err error
585+
rootFS image.RootFS
586+
)
587+
downloadRootFS := *image.NewRootFS()
588+
rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, descriptors, p.config.ProgressOutput)
589+
if err != nil {
590+
// Intentionally do not cancel the config download here
591+
// as the error from config download (if there is one)
592+
// is more interesting than the layer download error
593+
layerErrChan <- err
594+
return
595595
}
596-
}
597-
if release != nil {
598-
defer release()
599-
}
600596

601-
downloadedRootFS = &rootFS
597+
downloadedRootFS = &rootFS
598+
close(downloadsDone)
599+
}()
600+
} else {
601+
// We have nothing to download
602+
close(downloadsDone)
602603
}
603604

604605
if configJSON == nil {
605-
configJSON, configRootFS, err = receiveConfig(p.config.ImageStore, configChan, errChan)
606+
configJSON, configRootFS, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
607+
if err == nil && configRootFS == nil {
608+
err = errRootFSInvalid
609+
}
606610
if err != nil {
611+
cancel()
612+
select {
613+
case <-downloadsDone:
614+
case <-layerErrChan:
615+
}
607616
return "", "", err
608617
}
618+
}
609619

610-
if configRootFS == nil {
611-
return "", "", errRootFSInvalid
612-
}
620+
select {
621+
case <-downloadsDone:
622+
case err = <-layerErrChan:
623+
return "", "", err
624+
}
625+
626+
if release != nil {
627+
defer release()
613628
}
614629

615630
if downloadedRootFS != nil {

integration-cli/docker_cli_pull_test.go

+7
Original file line numberDiff line numberDiff line change
@@ -272,3 +272,10 @@ func (s *DockerSuite) TestPullLinuxImageFailsOnWindows(c *check.C) {
272272
_, _, err := dockerCmdWithError("pull", "ubuntu")
273273
c.Assert(err.Error(), checker.Contains, "cannot be used on this platform")
274274
}
275+
276+
// Regression test for https://github.com/docker/docker/issues/28892
277+
func (s *DockerSuite) TestPullWindowsImageFailsOnLinux(c *check.C) {
278+
testRequires(c, DaemonIsLinux, Network)
279+
_, _, err := dockerCmdWithError("pull", "microsoft/nanoserver")
280+
c.Assert(err.Error(), checker.Contains, "cannot be used on this platform")
281+
}

0 commit comments

Comments
 (0)