Browse code

LCOW: Pass platform through into layer store

Signed-off-by: John Howard <jhoward@microsoft.com>

John Howard authored on 2017/04/26 08:45:42
Showing 18 changed files
... ...
@@ -160,6 +160,7 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
160 160
 	}()
161 161
 
162 162
 	var parent *image.Image
163
+	os := runtime.GOOS
163 164
 	if container.ImageID == "" {
164 165
 		parent = new(image.Image)
165 166
 		parent.RootFS = image.NewRootFS()
... ...
@@ -168,9 +169,13 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
168 168
 		if err != nil {
169 169
 			return "", err
170 170
 		}
171
+		// To support LCOW, Windows needs to pass the platform in when registering the layer in the store
172
+		if runtime.GOOS == "windows" {
173
+			os = parent.OS
174
+		}
171 175
 	}
172 176
 
173
-	l, err := daemon.layerStore.Register(rwTar, parent.RootFS.ChainID())
177
+	l, err := daemon.layerStore.Register(rwTar, parent.RootFS.ChainID(), layer.Platform(os))
174 178
 	if err != nil {
175 179
 		return "", err
176 180
 	}
... ...
@@ -3,6 +3,7 @@ package daemon
3 3
 import (
4 4
 	"encoding/json"
5 5
 	"fmt"
6
+	"runtime"
6 7
 	"sort"
7 8
 	"time"
8 9
 
... ...
@@ -272,7 +273,13 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) {
272 272
 	}
273 273
 	defer ts.Close()
274 274
 
275
-	newL, err := daemon.layerStore.Register(ts, parentChainID)
275
+	// To support LCOW, Windows needs to pass the platform into the store when registering the layer.
276
+	platform := layer.Platform("")
277
+	if runtime.GOOS == "windows" {
278
+		platform = l.Platform()
279
+	}
280
+
281
+	newL, err := daemon.layerStore.Register(ts, parentChainID, platform)
276 282
 	if err != nil {
277 283
 		return "", errors.Wrap(err, "error registering layer")
278 284
 	}
... ...
@@ -86,7 +86,12 @@ func (daemon *Daemon) ImportImage(src string, repository, tag string, msg string
86 86
 		return err
87 87
 	}
88 88
 	// TODO: support windows baselayer?
89
-	l, err := daemon.layerStore.Register(inflatedLayerData, "")
89
+	// TODO: LCOW support @jhowardmsft. For now, pass in a null platform when
90
+	//       registering the layer. Windows doesn't currently support import,
91
+	//       but for Linux images, there's no reason it couldn't. However it
92
+	//       would need another CLI flag as there's no meta-data indicating
93
+	//       the OS of the thing being imported.
94
+	l, err := daemon.layerStore.Register(inflatedLayerData, "", "")
90 95
 	if err != nil {
91 96
 		return err
92 97
 	}
... ...
@@ -83,7 +83,7 @@ type ImagePushConfig struct {
83 83
 type ImageConfigStore interface {
84 84
 	Put([]byte) (digest.Digest, error)
85 85
 	Get(digest.Digest) ([]byte, error)
86
-	RootFSFromConfig([]byte) (*image.RootFS, error)
86
+	RootFSAndPlatformFromConfig([]byte) (*image.RootFS, layer.Platform, error)
87 87
 }
88 88
 
89 89
 // PushLayerProvider provides layers to be pushed by ChainID.
... ...
@@ -109,7 +109,7 @@ type RootFSDownloadManager interface {
109 109
 	// returns the final rootfs.
110 110
 	// Given progress output to track download progress
111 111
 	// Returns function to release download resources
112
-	Download(ctx context.Context, initialRootFS image.RootFS, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error)
112
+	Download(ctx context.Context, initialRootFS image.RootFS, platform layer.Platform, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error)
113 113
 }
114 114
 
115 115
 type imageConfigStore struct {
... ...
@@ -137,21 +137,25 @@ func (s *imageConfigStore) Get(d digest.Digest) ([]byte, error) {
137 137
 	return img.RawJSON(), nil
138 138
 }
139 139
 
140
-func (s *imageConfigStore) RootFSFromConfig(c []byte) (*image.RootFS, error) {
140
+func (s *imageConfigStore) RootFSAndPlatformFromConfig(c []byte) (*image.RootFS, layer.Platform, error) {
141 141
 	var unmarshalledConfig image.Image
142 142
 	if err := json.Unmarshal(c, &unmarshalledConfig); err != nil {
143
-		return nil, err
143
+		return nil, "", err
144 144
 	}
145 145
 
146 146
 	// fail immediately on Windows when downloading a non-Windows image
147 147
 	// and vice versa. Exception on Windows if Linux Containers are enabled.
148 148
 	if runtime.GOOS == "windows" && unmarshalledConfig.OS == "linux" && !system.LCOWSupported() {
149
-		return nil, fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
149
+		return nil, "", fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
150 150
 	} else if runtime.GOOS != "windows" && unmarshalledConfig.OS == "windows" {
151
-		return nil, fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
151
+		return nil, "", fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
152 152
 	}
153 153
 
154
-	return unmarshalledConfig.RootFS, nil
154
+	platform := ""
155
+	if runtime.GOOS == "windows" {
156
+		platform = unmarshalledConfig.OS
157
+	}
158
+	return unmarshalledConfig.RootFS, layer.Platform(platform), nil
155 159
 }
156 160
 
157 161
 type storeLayerProvider struct {
... ...
@@ -232,7 +232,7 @@ func (p *v1Puller) pullImage(ctx context.Context, v1ID, endpoint string, localNa
232 232
 	}
233 233
 
234 234
 	rootFS := image.NewRootFS()
235
-	resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, descriptors, p.config.ProgressOutput)
235
+	resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, "", descriptors, p.config.ProgressOutput)
236 236
 	if err != nil {
237 237
 		return err
238 238
 	}
... ...
@@ -486,7 +486,7 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Named, unverif
486 486
 		descriptors = append(descriptors, layerDescriptor)
487 487
 	}
488 488
 
489
-	resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, descriptors, p.config.ProgressOutput)
489
+	resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, "", descriptors, p.config.ProgressOutput)
490 490
 	if err != nil {
491 491
 		return "", "", err
492 492
 	}
... ...
@@ -556,10 +556,11 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
556 556
 	}()
557 557
 
558 558
 	var (
559
-		configJSON       []byte        // raw serialized image config
560
-		downloadedRootFS *image.RootFS // rootFS from registered layers
561
-		configRootFS     *image.RootFS // rootFS from configuration
562
-		release          func()        // release resources from rootFS download
559
+		configJSON       []byte         // raw serialized image config
560
+		downloadedRootFS *image.RootFS  // rootFS from registered layers
561
+		configRootFS     *image.RootFS  // rootFS from configuration
562
+		release          func()         // release resources from rootFS download
563
+		platform         layer.Platform // for LCOW when registering downloaded layers
563 564
 	)
564 565
 
565 566
 	// https://github.com/docker/docker/issues/24766 - Err on the side of caution,
... ...
@@ -571,7 +572,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
571 571
 	// check to block Windows images being pulled on Linux is implemented, it
572 572
 	// may be necessary to perform the same type of serialisation.
573 573
 	if runtime.GOOS == "windows" {
574
-		configJSON, configRootFS, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
574
+		configJSON, configRootFS, platform, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
575 575
 		if err != nil {
576 576
 			return "", "", err
577 577
 		}
... ...
@@ -598,7 +599,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
598 598
 				rootFS image.RootFS
599 599
 			)
600 600
 			downloadRootFS := *image.NewRootFS()
601
-			rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, descriptors, p.config.ProgressOutput)
601
+			rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, platform, descriptors, p.config.ProgressOutput)
602 602
 			if err != nil {
603 603
 				// Intentionally do not cancel the config download here
604 604
 				// as the error from config download (if there is one)
... ...
@@ -616,7 +617,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
616 616
 	}
617 617
 
618 618
 	if configJSON == nil {
619
-		configJSON, configRootFS, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
619
+		configJSON, configRootFS, _, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
620 620
 		if err == nil && configRootFS == nil {
621 621
 			err = errRootFSInvalid
622 622
 		}
... ...
@@ -663,16 +664,16 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
663 663
 	return imageID, manifestDigest, nil
664 664
 }
665 665
 
666
-func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan error) ([]byte, *image.RootFS, error) {
666
+func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan error) ([]byte, *image.RootFS, layer.Platform, error) {
667 667
 	select {
668 668
 	case configJSON := <-configChan:
669
-		rootfs, err := s.RootFSFromConfig(configJSON)
669
+		rootfs, platform, err := s.RootFSAndPlatformFromConfig(configJSON)
670 670
 		if err != nil {
671
-			return nil, nil, err
671
+			return nil, nil, "", err
672 672
 		}
673
-		return configJSON, rootfs, nil
673
+		return configJSON, rootfs, platform, nil
674 674
 	case err := <-errChan:
675
-		return nil, nil, err
675
+		return nil, nil, "", err
676 676
 		// Don't need a case for ctx.Done in the select because cancellation
677 677
 		// will trigger an error in p.pullSchema2ImageConfig.
678 678
 	}
... ...
@@ -118,7 +118,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id
118 118
 		return fmt.Errorf("could not find image from tag %s: %v", reference.FamiliarString(ref), err)
119 119
 	}
120 120
 
121
-	rootfs, err := p.config.ImageStore.RootFSFromConfig(imgConfig)
121
+	rootfs, _, err := p.config.ImageStore.RootFSAndPlatformFromConfig(imgConfig)
122 122
 	if err != nil {
123 123
 		return fmt.Errorf("unable to get rootfs for image %s: %s", reference.FamiliarString(ref), err)
124 124
 	}
... ...
@@ -94,7 +94,7 @@ type DownloadDescriptorWithRegistered interface {
94 94
 // Download method is called to get the layer tar data. Layers are then
95 95
 // registered in the appropriate order.  The caller must call the returned
96 96
 // release function once it is done with the returned RootFS object.
97
-func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS image.RootFS, layers []DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) {
97
+func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS image.RootFS, platform layer.Platform, layers []DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) {
98 98
 	var (
99 99
 		topLayer       layer.Layer
100 100
 		topDownload    *downloadTransfer
... ...
@@ -140,7 +140,7 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima
140 140
 		// the stack? If so, avoid downloading it more than once.
141 141
 		var topDownloadUncasted Transfer
142 142
 		if existingDownload, ok := downloadsByKey[key]; ok {
143
-			xferFunc := ldm.makeDownloadFuncFromDownload(descriptor, existingDownload, topDownload)
143
+			xferFunc := ldm.makeDownloadFuncFromDownload(descriptor, existingDownload, topDownload, platform)
144 144
 			defer topDownload.Transfer.Release(watcher)
145 145
 			topDownloadUncasted, watcher = ldm.tm.Transfer(transferKey, xferFunc, progressOutput)
146 146
 			topDownload = topDownloadUncasted.(*downloadTransfer)
... ...
@@ -152,10 +152,10 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima
152 152
 
153 153
 		var xferFunc DoFunc
154 154
 		if topDownload != nil {
155
-			xferFunc = ldm.makeDownloadFunc(descriptor, "", topDownload)
155
+			xferFunc = ldm.makeDownloadFunc(descriptor, "", topDownload, platform)
156 156
 			defer topDownload.Transfer.Release(watcher)
157 157
 		} else {
158
-			xferFunc = ldm.makeDownloadFunc(descriptor, rootFS.ChainID(), nil)
158
+			xferFunc = ldm.makeDownloadFunc(descriptor, rootFS.ChainID(), nil, platform)
159 159
 		}
160 160
 		topDownloadUncasted, watcher = ldm.tm.Transfer(transferKey, xferFunc, progressOutput)
161 161
 		topDownload = topDownloadUncasted.(*downloadTransfer)
... ...
@@ -212,7 +212,7 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima
212 212
 // complete before the registration step, and registers the downloaded data
213 213
 // on top of parentDownload's resulting layer. Otherwise, it registers the
214 214
 // layer on top of the ChainID given by parentLayer.
215
-func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor, parentLayer layer.ChainID, parentDownload *downloadTransfer) DoFunc {
215
+func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor, parentLayer layer.ChainID, parentDownload *downloadTransfer, platform layer.Platform) DoFunc {
216 216
 	return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer {
217 217
 		d := &downloadTransfer{
218 218
 			Transfer:   NewTransfer(),
... ...
@@ -335,9 +335,9 @@ func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor,
335 335
 				src = fs.Descriptor()
336 336
 			}
337 337
 			if ds, ok := d.layerStore.(layer.DescribableStore); ok {
338
-				d.layer, err = ds.RegisterWithDescriptor(inflatedLayerData, parentLayer, src)
338
+				d.layer, err = ds.RegisterWithDescriptor(inflatedLayerData, parentLayer, platform, src)
339 339
 			} else {
340
-				d.layer, err = d.layerStore.Register(inflatedLayerData, parentLayer)
340
+				d.layer, err = d.layerStore.Register(inflatedLayerData, parentLayer, platform)
341 341
 			}
342 342
 			if err != nil {
343 343
 				select {
... ...
@@ -376,7 +376,7 @@ func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor,
376 376
 // parentDownload. This function does not log progress output because it would
377 377
 // interfere with the progress reporting for sourceDownload, which has the same
378 378
 // Key.
379
-func (ldm *LayerDownloadManager) makeDownloadFuncFromDownload(descriptor DownloadDescriptor, sourceDownload *downloadTransfer, parentDownload *downloadTransfer) DoFunc {
379
+func (ldm *LayerDownloadManager) makeDownloadFuncFromDownload(descriptor DownloadDescriptor, sourceDownload *downloadTransfer, parentDownload *downloadTransfer, platform layer.Platform) DoFunc {
380 380
 	return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer {
381 381
 		d := &downloadTransfer{
382 382
 			Transfer:   NewTransfer(),
... ...
@@ -434,9 +434,9 @@ func (ldm *LayerDownloadManager) makeDownloadFuncFromDownload(descriptor Downloa
434 434
 				src = fs.Descriptor()
435 435
 			}
436 436
 			if ds, ok := d.layerStore.(layer.DescribableStore); ok {
437
-				d.layer, err = ds.RegisterWithDescriptor(layerReader, parentLayer, src)
437
+				d.layer, err = ds.RegisterWithDescriptor(layerReader, parentLayer, platform, src)
438 438
 			} else {
439
-				d.layer, err = d.layerStore.Register(layerReader, parentLayer)
439
+				d.layer, err = d.layerStore.Register(layerReader, parentLayer, platform)
440 440
 			}
441 441
 			if err != nil {
442 442
 				d.err = fmt.Errorf("failed to register layer: %v", err)
... ...
@@ -91,7 +91,7 @@ func (ls *mockLayerStore) Map() map[layer.ChainID]layer.Layer {
91 91
 	return layers
92 92
 }
93 93
 
94
-func (ls *mockLayerStore) Register(reader io.Reader, parentID layer.ChainID) (layer.Layer, error) {
94
+func (ls *mockLayerStore) Register(reader io.Reader, parentID layer.ChainID, platform layer.Platform) (layer.Layer, error) {
95 95
 	return ls.RegisterWithDescriptor(reader, parentID, distribution.Descriptor{})
96 96
 }
97 97
 
... ...
@@ -272,7 +272,9 @@ func TestSuccessfulDownload(t *testing.T) {
272 272
 	}
273 273
 
274 274
 	layerStore := &mockLayerStore{make(map[layer.ChainID]*mockLayer)}
275
-	ldm := NewLayerDownloadManager(layerStore, maxDownloadConcurrency, func(m *LayerDownloadManager) { m.waitDuration = time.Millisecond })
275
+	lsMap := make(map[string]layer.Store)
276
+	lsMap[runtime.GOOS] = layerStore
277
+	ldm := NewLayerDownloadManager(lsMap, maxDownloadConcurrency, func(m *LayerDownloadManager) { m.waitDuration = time.Millisecond })
276 278
 
277 279
 	progressChan := make(chan progress.Progress)
278 280
 	progressDone := make(chan struct{})
... ...
@@ -291,13 +293,13 @@ func TestSuccessfulDownload(t *testing.T) {
291 291
 	firstDescriptor := descriptors[0].(*mockDownloadDescriptor)
292 292
 
293 293
 	// Pre-register the first layer to simulate an already-existing layer
294
-	l, err := layerStore.Register(firstDescriptor.mockTarStream(), "")
294
+	l, err := layerStore.Register(firstDescriptor.mockTarStream(), "", layer.Platform(runtime.GOOS))
295 295
 	if err != nil {
296 296
 		t.Fatal(err)
297 297
 	}
298 298
 	firstDescriptor.diffID = l.DiffID()
299 299
 
300
-	rootFS, releaseFunc, err := ldm.Download(context.Background(), *image.NewRootFS(), descriptors, progress.ChanOutput(progressChan))
300
+	rootFS, releaseFunc, err := ldm.Download(context.Background(), *image.NewRootFS(), layer.Platform(runtime.GOOS), descriptors, progress.ChanOutput(progressChan))
301 301
 	if err != nil {
302 302
 		t.Fatalf("download error: %v", err)
303 303
 	}
... ...
@@ -333,7 +335,10 @@ func TestSuccessfulDownload(t *testing.T) {
333 333
 }
334 334
 
335 335
 func TestCancelledDownload(t *testing.T) {
336
-	ldm := NewLayerDownloadManager(&mockLayerStore{make(map[layer.ChainID]*mockLayer)}, maxDownloadConcurrency, func(m *LayerDownloadManager) { m.waitDuration = time.Millisecond })
336
+	layerStore := &mockLayerStore{make(map[layer.ChainID]*mockLayer)}
337
+	lsMap := make(map[string]layer.Store)
338
+	lsMap[runtime.GOOS] = layerStore
339
+	ldm := NewLayerDownloadManager(lsMap, maxDownloadConcurrency, func(m *LayerDownloadManager) { m.waitDuration = time.Millisecond })
337 340
 
338 341
 	progressChan := make(chan progress.Progress)
339 342
 	progressDone := make(chan struct{})
... ...
@@ -352,7 +357,7 @@ func TestCancelledDownload(t *testing.T) {
352 352
 	}()
353 353
 
354 354
 	descriptors := downloadDescriptors(nil)
355
-	_, _, err := ldm.Download(ctx, *image.NewRootFS(), descriptors, progress.ChanOutput(progressChan))
355
+	_, _, err := ldm.Download(ctx, *image.NewRootFS(), layer.Platform(runtime.GOOS), descriptors, progress.ChanOutput(progressChan))
356 356
 	if err != context.Canceled {
357 357
 		t.Fatal("expected download to be cancelled")
358 358
 	}
... ...
@@ -2,12 +2,14 @@ package tarexport
2 2
 
3 3
 import (
4 4
 	"encoding/json"
5
+	"errors"
5 6
 	"fmt"
6 7
 	"io"
7 8
 	"io/ioutil"
8 9
 	"os"
9 10
 	"path/filepath"
10 11
 	"reflect"
12
+	"runtime"
11 13
 
12 14
 	"github.com/Sirupsen/logrus"
13 15
 	"github.com/docker/distribution"
... ...
@@ -85,6 +87,17 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
85 85
 			return fmt.Errorf("invalid manifest, layers length mismatch: expected %d, got %d", expected, actual)
86 86
 		}
87 87
 
88
+		// On Windows, validate the platform, defaulting to windows if not present.
89
+		platform := layer.Platform(img.OS)
90
+		if runtime.GOOS == "windows" {
91
+			if platform == "" {
92
+				platform = "windows"
93
+			}
94
+			if (platform != "windows") && (platform != "linux") {
95
+				return fmt.Errorf("configuration for this image has an unsupported platform: %s", platform)
96
+			}
97
+		}
98
+
88 99
 		for i, diffID := range img.RootFS.DiffIDs {
89 100
 			layerPath, err := safePath(tmpDir, m.Layers[i])
90 101
 			if err != nil {
... ...
@@ -94,7 +107,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
94 94
 			r.Append(diffID)
95 95
 			newLayer, err := l.ls.Get(r.ChainID())
96 96
 			if err != nil {
97
-				newLayer, err = l.loadLayer(layerPath, rootFS, diffID.String(), m.LayerSources[diffID], progressOutput)
97
+				newLayer, err = l.loadLayer(layerPath, rootFS, diffID.String(), platform, m.LayerSources[diffID], progressOutput)
98 98
 				if err != nil {
99 99
 					return err
100 100
 				}
... ...
@@ -161,7 +174,7 @@ func (l *tarexporter) setParentID(id, parentID image.ID) error {
161 161
 	return l.is.SetParent(id, parentID)
162 162
 }
163 163
 
164
-func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, foreignSrc distribution.Descriptor, progressOutput progress.Output) (layer.Layer, error) {
164
+func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, platform layer.Platform, foreignSrc distribution.Descriptor, progressOutput progress.Output) (layer.Layer, error) {
165 165
 	// We use system.OpenSequential to use sequential file access on Windows, avoiding
166 166
 	// depleting the standby list. On Linux, this equates to a regular os.Open.
167 167
 	rawTar, err := system.OpenSequential(filename)
... ...
@@ -191,9 +204,9 @@ func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string,
191 191
 	defer inflatedLayerData.Close()
192 192
 
193 193
 	if ds, ok := l.ls.(layer.DescribableStore); ok {
194
-		return ds.RegisterWithDescriptor(inflatedLayerData, rootFS.ChainID(), foreignSrc)
194
+		return ds.RegisterWithDescriptor(inflatedLayerData, rootFS.ChainID(), platform, foreignSrc)
195 195
 	}
196
-	return l.ls.Register(inflatedLayerData, rootFS.ChainID())
196
+	return l.ls.Register(inflatedLayerData, rootFS.ChainID(), platform)
197 197
 }
198 198
 
199 199
 func (l *tarexporter) setLoadedTag(ref reference.NamedTagged, imgID digest.Digest, outStream io.Writer) error {
... ...
@@ -208,6 +221,10 @@ func (l *tarexporter) setLoadedTag(ref reference.NamedTagged, imgID digest.Diges
208 208
 }
209 209
 
210 210
 func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer, progressOutput progress.Output) error {
211
+	if runtime.GOOS == "windows" {
212
+		return errors.New("Windows does not support legacy loading of images")
213
+	}
214
+
211 215
 	legacyLoadedMap := make(map[string]image.ID)
212 216
 
213 217
 	dirs, err := ioutil.ReadDir(tmpDir)
... ...
@@ -312,7 +329,7 @@ func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[str
312 312
 	if err != nil {
313 313
 		return err
314 314
 	}
315
-	newLayer, err := l.loadLayer(layerPath, *rootFS, oldID, distribution.Descriptor{}, progressOutput)
315
+	newLayer, err := l.loadLayer(layerPath, *rootFS, oldID, "", distribution.Descriptor{}, progressOutput)
316 316
 	if err != nil {
317 317
 		return err
318 318
 	}
... ...
@@ -190,7 +190,7 @@ type CreateRWLayerOpts struct {
190 190
 // Store represents a backend for managing both
191 191
 // read-only and read-write layers.
192 192
 type Store interface {
193
-	Register(io.Reader, ChainID) (Layer, error)
193
+	Register(io.Reader, ChainID, Platform) (Layer, error)
194 194
 	Get(ChainID) (Layer, error)
195 195
 	Map() map[ChainID]Layer
196 196
 	Release(Layer) ([]Metadata, error)
... ...
@@ -208,7 +208,7 @@ type Store interface {
208 208
 // DescribableStore represents a layer store capable of storing
209 209
 // descriptors for layers.
210 210
 type DescribableStore interface {
211
-	RegisterWithDescriptor(io.Reader, ChainID, distribution.Descriptor) (Layer, error)
211
+	RegisterWithDescriptor(io.Reader, ChainID, Platform, distribution.Descriptor) (Layer, error)
212 212
 }
213 213
 
214 214
 // MetadataTransaction represents functions for setting layer metadata
... ...
@@ -253,11 +253,11 @@ func (ls *layerStore) applyTar(tx MetadataTransaction, ts io.Reader, parent stri
253 253
 	return nil
254 254
 }
255 255
 
256
-func (ls *layerStore) Register(ts io.Reader, parent ChainID) (Layer, error) {
257
-	return ls.registerWithDescriptor(ts, parent, distribution.Descriptor{})
256
+func (ls *layerStore) Register(ts io.Reader, parent ChainID, platform Platform) (Layer, error) {
257
+	return ls.registerWithDescriptor(ts, parent, platform, distribution.Descriptor{})
258 258
 }
259 259
 
260
-func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, descriptor distribution.Descriptor) (Layer, error) {
260
+func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, platform Platform, descriptor distribution.Descriptor) (Layer, error) {
261 261
 	// err is used to hold the error which will always trigger
262 262
 	// cleanup of creates sources but may not be an error returned
263 263
 	// to the caller (already exists).
... ...
@@ -292,6 +292,7 @@ func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, descr
292 292
 		layerStore:     ls,
293 293
 		references:     map[Layer]struct{}{},
294 294
 		descriptor:     descriptor,
295
+		platform:       platform,
295 296
 	}
296 297
 
297 298
 	if err = ls.driver.Create(layer.cacheID, pid, nil); err != nil {
... ...
@@ -394,7 +395,6 @@ func (ls *layerStore) deleteLayer(layer *roLayer, metadata *Metadata) error {
394 394
 	if err != nil {
395 395
 		return err
396 396
 	}
397
-
398 397
 	err = ls.store.Remove(layer.chainID)
399 398
 	if err != nil {
400 399
 		return err
... ...
@@ -524,7 +524,6 @@ func (ls *layerStore) CreateRWLayer(name string, parent ChainID, opts *CreateRWL
524 524
 	if err = ls.driver.CreateReadWrite(m.mountID, pid, createOpts); err != nil {
525 525
 		return nil, err
526 526
 	}
527
-
528 527
 	if err = ls.saveMount(m); err != nil {
529 528
 		return nil, err
530 529
 	}
... ...
@@ -6,6 +6,6 @@ import (
6 6
 	"github.com/docker/distribution"
7 7
 )
8 8
 
9
-func (ls *layerStore) RegisterWithDescriptor(ts io.Reader, parent ChainID, descriptor distribution.Descriptor) (Layer, error) {
10
-	return ls.registerWithDescriptor(ts, parent, descriptor)
9
+func (ls *layerStore) RegisterWithDescriptor(ts io.Reader, parent ChainID, platform Platform, descriptor distribution.Descriptor) (Layer, error) {
10
+	return ls.registerWithDescriptor(ts, parent, platform, descriptor)
11 11
 }
... ...
@@ -106,7 +106,7 @@ func createLayer(ls Store, parent ChainID, layerFunc layerInit) (Layer, error) {
106 106
 	}
107 107
 	defer ts.Close()
108 108
 
109
-	layer, err := ls.Register(ts, parent)
109
+	layer, err := ls.Register(ts, parent, Platform(runtime.GOOS))
110 110
 	if err != nil {
111 111
 		return nil, err
112 112
 	}
... ...
@@ -499,7 +499,7 @@ func TestTarStreamStability(t *testing.T) {
499 499
 		t.Fatal(err)
500 500
 	}
501 501
 
502
-	layer1, err := ls.Register(bytes.NewReader(tar1), "")
502
+	layer1, err := ls.Register(bytes.NewReader(tar1), "", Platform(runtime.GOOS))
503 503
 	if err != nil {
504 504
 		t.Fatal(err)
505 505
 	}
... ...
@@ -518,7 +518,7 @@ func TestTarStreamStability(t *testing.T) {
518 518
 		t.Fatal(err)
519 519
 	}
520 520
 
521
-	layer2, err := ls.Register(bytes.NewReader(tar2), layer1.ChainID())
521
+	layer2, err := ls.Register(bytes.NewReader(tar2), layer1.ChainID(), Platform(runtime.GOOS))
522 522
 	if err != nil {
523 523
 		t.Fatal(err)
524 524
 	}
... ...
@@ -686,12 +686,12 @@ func TestRegisterExistingLayer(t *testing.T) {
686 686
 		t.Fatal(err)
687 687
 	}
688 688
 
689
-	layer2a, err := ls.Register(bytes.NewReader(tar1), layer1.ChainID())
689
+	layer2a, err := ls.Register(bytes.NewReader(tar1), layer1.ChainID(), Platform(runtime.GOOS))
690 690
 	if err != nil {
691 691
 		t.Fatal(err)
692 692
 	}
693 693
 
694
-	layer2b, err := ls.Register(bytes.NewReader(tar1), layer1.ChainID())
694
+	layer2b, err := ls.Register(bytes.NewReader(tar1), layer1.ChainID(), Platform(runtime.GOOS))
695 695
 	if err != nil {
696 696
 		t.Fatal(err)
697 697
 	}
... ...
@@ -726,12 +726,12 @@ func TestTarStreamVerification(t *testing.T) {
726 726
 		t.Fatal(err)
727 727
 	}
728 728
 
729
-	layer1, err := ls.Register(bytes.NewReader(tar1), "")
729
+	layer1, err := ls.Register(bytes.NewReader(tar1), "", Platform(runtime.GOOS))
730 730
 	if err != nil {
731 731
 		t.Fatal(err)
732 732
 	}
733 733
 
734
-	layer2, err := ls.Register(bytes.NewReader(tar2), "")
734
+	layer2, err := ls.Register(bytes.NewReader(tar2), "", Platform(runtime.GOOS))
735 735
 	if err != nil {
736 736
 		t.Fatal(err)
737 737
 	}
... ...
@@ -110,14 +110,14 @@ func TestLayerMigration(t *testing.T) {
110 110
 		t.Fatal(err)
111 111
 	}
112 112
 
113
-	layer1b, err := ls.Register(bytes.NewReader(tar1), "")
113
+	layer1b, err := ls.Register(bytes.NewReader(tar1), "", Platform(runtime.GOOS))
114 114
 	if err != nil {
115 115
 		t.Fatal(err)
116 116
 	}
117 117
 
118 118
 	assertReferences(t, layer1a, layer1b)
119 119
 	// Attempt register, should be same
120
-	layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID())
120
+	layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID(), Platform(runtime.GOOS))
121 121
 	if err != nil {
122 122
 		t.Fatal(err)
123 123
 	}
... ...
@@ -238,7 +238,7 @@ func TestLayerMigrationNoTarsplit(t *testing.T) {
238 238
 		t.Fatal(err)
239 239
 	}
240 240
 
241
-	layer1b, err := ls.Register(bytes.NewReader(tar1), "")
241
+	layer1b, err := ls.Register(bytes.NewReader(tar1), "", Platform(runtime.GOOS))
242 242
 	if err != nil {
243 243
 		t.Fatal(err)
244 244
 	}
... ...
@@ -246,7 +246,7 @@ func TestLayerMigrationNoTarsplit(t *testing.T) {
246 246
 	assertReferences(t, layer1a, layer1b)
247 247
 
248 248
 	// Attempt register, should be same
249
-	layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID())
249
+	layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID(), Platform(runtime.GOOS))
250 250
 	if err != nil {
251 251
 		t.Fatal(err)
252 252
 	}
... ...
@@ -145,7 +145,7 @@ func (s *tempConfigStore) Get(d digest.Digest) ([]byte, error) {
145 145
 	return s.config, nil
146 146
 }
147 147
 
148
-func (s *tempConfigStore) RootFSFromConfig(c []byte) (*image.RootFS, error) {
148
+func (s *tempConfigStore) RootFSAndPlatformFromConfig(c []byte) (*image.RootFS, layer.Platform, error) {
149 149
 	return configToRootFS(c)
150 150
 }
151 151
 
... ...
@@ -525,7 +525,7 @@ func (s *pluginConfigStore) Get(d digest.Digest) ([]byte, error) {
525 525
 	return ioutil.ReadAll(rwc)
526 526
 }
527 527
 
528
-func (s *pluginConfigStore) RootFSFromConfig(c []byte) (*image.RootFS, error) {
528
+func (s *pluginConfigStore) RootFSAndPlatformFromConfig(c []byte) (*image.RootFS, layer.Platform, error) {
529 529
 	return configToRootFS(c)
530 530
 }
531 531
 
... ...
@@ -126,7 +126,8 @@ type downloadManager struct {
126 126
 	configDigest digest.Digest
127 127
 }
128 128
 
129
-func (dm *downloadManager) Download(ctx context.Context, initialRootFS image.RootFS, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) {
129
+func (dm *downloadManager) Download(ctx context.Context, initialRootFS image.RootFS, platform layer.Platform, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) {
130
+	// TODO @jhowardmsft LCOW: May need revisiting.
130 131
 	for _, l := range layers {
131 132
 		b, err := dm.blobStore.New()
132 133
 		if err != nil {
... ...
@@ -178,6 +179,6 @@ func (dm *downloadManager) Put(dt []byte) (digest.Digest, error) {
178 178
 func (dm *downloadManager) Get(d digest.Digest) ([]byte, error) {
179 179
 	return nil, fmt.Errorf("digest not found")
180 180
 }
181
-func (dm *downloadManager) RootFSFromConfig(c []byte) (*image.RootFS, error) {
181
+func (dm *downloadManager) RootFSAndPlatformFromConfig(c []byte) (*image.RootFS, layer.Platform, error) {
182 182
 	return configToRootFS(c)
183 183
 }
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"path/filepath"
9 9
 	"reflect"
10 10
 	"regexp"
11
+	"runtime"
11 12
 	"sort"
12 13
 	"strings"
13 14
 	"sync"
... ...
@@ -21,6 +22,7 @@ import (
21 21
 	"github.com/docker/docker/pkg/authorization"
22 22
 	"github.com/docker/docker/pkg/ioutils"
23 23
 	"github.com/docker/docker/pkg/mount"
24
+	"github.com/docker/docker/pkg/system"
24 25
 	"github.com/docker/docker/plugin/v2"
25 26
 	"github.com/docker/docker/registry"
26 27
 	"github.com/opencontainers/go-digest"
... ...
@@ -348,17 +350,22 @@ func isEqualPrivilege(a, b types.PluginPrivilege) bool {
348 348
 	return reflect.DeepEqual(a.Value, b.Value)
349 349
 }
350 350
 
351
-func configToRootFS(c []byte) (*image.RootFS, error) {
351
+func configToRootFS(c []byte) (*image.RootFS, layer.Platform, error) {
352
+	// TODO @jhowardmsft LCOW - Will need to revisit this. For now, calculate the platform.
353
+	platform := layer.Platform(runtime.GOOS)
354
+	if platform == "windows" && system.LCOWSupported() {
355
+		platform = "linux"
356
+	}
352 357
 	var pluginConfig types.PluginConfig
353 358
 	if err := json.Unmarshal(c, &pluginConfig); err != nil {
354
-		return nil, err
359
+		return nil, "", err
355 360
 	}
356 361
 	// validation for empty rootfs is in distribution code
357 362
 	if pluginConfig.Rootfs == nil {
358
-		return nil, nil
363
+		return nil, platform, nil
359 364
 	}
360 365
 
361
-	return rootFSFromPlugin(pluginConfig.Rootfs), nil
366
+	return rootFSFromPlugin(pluginConfig.Rootfs), platform, nil
362 367
 }
363 368
 
364 369
 func rootFSFromPlugin(pluginfs *types.PluginConfigRootfs) *image.RootFS {