Signed-off-by: John Howard <jhoward@microsoft.com>
| ... | ... |
@@ -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 {
|