diff --git a/components/engine/api/server/backend/build/backend.go b/components/engine/api/server/backend/build/backend.go index f93fba93b7..a702d04a40 100644 --- a/components/engine/api/server/backend/build/backend.go +++ b/components/engine/api/server/backend/build/backend.go @@ -17,7 +17,7 @@ import ( // ImageComponent provides an interface for working with images type ImageComponent interface { SquashImage(from string, to string) (string, error) - TagImageWithReference(image.ID, string, reference.Named) error + TagImageWithReference(image.ID, reference.Named) error } // Builder defines interface for running a build diff --git a/components/engine/api/server/backend/build/tag.go b/components/engine/api/server/backend/build/tag.go index 7bd5dcdeb2..5c3918a3e1 100644 --- a/components/engine/api/server/backend/build/tag.go +++ b/components/engine/api/server/backend/build/tag.go @@ -3,11 +3,9 @@ package build import ( "fmt" "io" - "runtime" "github.com/docker/distribution/reference" "github.com/docker/docker/image" - "github.com/docker/docker/pkg/system" "github.com/pkg/errors" ) @@ -35,12 +33,7 @@ func NewTagger(backend ImageComponent, stdout io.Writer, names []string) (*Tagge // TagImages creates image tags for the imageID func (bt *Tagger) TagImages(imageID image.ID) error { for _, rt := range bt.repoAndTags { - // TODO @jhowardmsft LCOW support. Will need revisiting. - platform := runtime.GOOS - if system.LCOWSupported() { - platform = "linux" - } - if err := bt.imageComponent.TagImageWithReference(imageID, platform, rt); err != nil { + if err := bt.imageComponent.TagImageWithReference(imageID, rt); err != nil { return err } fmt.Fprintf(bt.stdout, "Successfully tagged %s\n", reference.FamiliarString(rt)) diff --git a/components/engine/builder/builder.go b/components/engine/builder/builder.go index 5a3e2cd8cd..b7d5edea0a 100644 --- a/components/engine/builder/builder.go +++ b/components/engine/builder/builder.go @@ -44,7 +44,7 @@ type Backend interface { // ContainerCreateWorkdir creates the workdir ContainerCreateWorkdir(containerID string) error - CreateImage(config []byte, parent string, platform string) (Image, error) + CreateImage(config []byte, parent string) (Image, error) ImageCacheBuilder } @@ -79,7 +79,7 @@ type Result struct { // ImageCacheBuilder represents a generator for stateful image cache. type ImageCacheBuilder interface { // MakeImageCache creates a stateful image cache. - MakeImageCache(cacheFrom []string, platform string) ImageCache + MakeImageCache(cacheFrom []string) ImageCache } // ImageCache abstracts an image cache. @@ -102,6 +102,6 @@ type Image interface { type ReleaseableLayer interface { Release() error Mount() (containerfs.ContainerFS, error) - Commit(platform string) (ReleaseableLayer, error) + Commit() (ReleaseableLayer, error) DiffID() layer.DiffID } diff --git a/components/engine/builder/dockerfile/builder.go b/components/engine/builder/dockerfile/builder.go index 0354b2d3f9..374f301ac5 100644 --- a/components/engine/builder/dockerfile/builder.go +++ b/components/engine/builder/dockerfile/builder.go @@ -123,7 +123,7 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) ( PathCache: bm.pathCache, IDMappings: bm.idMappings, } - return newBuilder(ctx, builderOptions, os).build(source, dockerfile) + return newBuilder(ctx, builderOptions).build(source, dockerfile) } func (bm *BuildManager) initializeClientSession(ctx context.Context, cancel func(), options *types.ImageBuildOptions) (builder.Source, error) { @@ -190,7 +190,7 @@ type Builder struct { } // newBuilder creates a new Dockerfile builder from an optional dockerfile and a Options. -func newBuilder(clientCtx context.Context, options builderOptions, os string) *Builder { +func newBuilder(clientCtx context.Context, options builderOptions) *Builder { config := options.Options if config == nil { config = new(types.ImageBuildOptions) @@ -207,7 +207,7 @@ func newBuilder(clientCtx context.Context, options builderOptions, os string) *B idMappings: options.IDMappings, imageSources: newImageSources(clientCtx, options), pathCache: options.PathCache, - imageProber: newImageProber(options.Backend, config.CacheFrom, os, config.NoCache), + imageProber: newImageProber(options.Backend, config.CacheFrom, config.NoCache), containerManager: newContainerManager(options.Backend), } @@ -357,7 +357,10 @@ func addNodesForLabelOption(dockerfile *parser.Node, labels map[string]string) { // coming from the query parameter of the same name. // // TODO: Remove? -func BuildFromConfig(config *container.Config, changes []string) (*container.Config, error) { +func BuildFromConfig(config *container.Config, changes []string, os string) (*container.Config, error) { + if !system.IsOSSupported(os) { + return nil, errdefs.InvalidParameter(system.ErrNotSupportedOperatingSystem) + } if len(changes) == 0 { return config, nil } @@ -367,14 +370,9 @@ func BuildFromConfig(config *container.Config, changes []string) (*container.Con return nil, errdefs.InvalidParameter(err) } - os := runtime.GOOS - if dockerfile.OS != "" { - os = dockerfile.OS - } - b := newBuilder(context.Background(), builderOptions{ Options: &types.ImageBuildOptions{NoCache: true}, - }, os) + }) // ensure that the commands are valid for _, n := range dockerfile.AST.Children { @@ -400,6 +398,7 @@ func BuildFromConfig(config *container.Config, changes []string) (*container.Con // We make mutations to the configuration, ensure we have a copy dispatchRequest.state.runConfig = copyRunConfig(config) dispatchRequest.state.imageID = config.Image + dispatchRequest.state.operatingSystem = os for _, cmd := range commands { err := dispatch(dispatchRequest, cmd) if err != nil { diff --git a/components/engine/builder/dockerfile/containerbackend.go b/components/engine/builder/dockerfile/containerbackend.go index add0a876df..523cc5076a 100644 --- a/components/engine/builder/dockerfile/containerbackend.go +++ b/components/engine/builder/dockerfile/containerbackend.go @@ -28,7 +28,7 @@ func newContainerManager(docker builder.ExecBackend) *containerManager { } // Create a container -func (c *containerManager) Create(runConfig *container.Config, hostConfig *container.HostConfig, platform string) (container.ContainerCreateCreatedBody, error) { +func (c *containerManager) Create(runConfig *container.Config, hostConfig *container.HostConfig) (container.ContainerCreateCreatedBody, error) { container, err := c.backend.ContainerCreate(types.ContainerCreateConfig{ Config: runConfig, HostConfig: hostConfig, diff --git a/components/engine/builder/dockerfile/dispatchers.go b/components/engine/builder/dockerfile/dispatchers.go index 1cf275d8c4..4239e3ccc7 100644 --- a/components/engine/builder/dockerfile/dispatchers.go +++ b/components/engine/builder/dockerfile/dispatchers.go @@ -156,7 +156,9 @@ func initializeStage(d dispatchRequest, cmd *instructions.Stage) error { return err } state := d.state - state.beginStage(cmd.Name, image) + if err := state.beginStage(cmd.Name, image); err != nil { + return err + } if len(state.runConfig.OnBuild) > 0 { triggers := state.runConfig.OnBuild state.runConfig.OnBuild = nil @@ -261,8 +263,8 @@ func dispatchOnbuild(d dispatchRequest, c *instructions.OnbuildCommand) error { func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error { runConfig := d.state.runConfig var err error - optionsOS := system.ParsePlatform(d.builder.options.Platform).OS - runConfig.WorkingDir, err = normalizeWorkdir(optionsOS, runConfig.WorkingDir, c.Path) + baseImageOS := system.ParsePlatform(d.state.operatingSystem).OS + runConfig.WorkingDir, err = normalizeWorkdir(baseImageOS, runConfig.WorkingDir, c.Path) if err != nil { return err } @@ -278,7 +280,7 @@ func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error { } comment := "WORKDIR " + runConfig.WorkingDir - runConfigWithCommentCmd := copyRunConfig(runConfig, withCmdCommentString(comment, optionsOS)) + runConfigWithCommentCmd := copyRunConfig(runConfig, withCmdCommentString(comment, baseImageOS)) containerID, err := d.builder.probeAndCreate(d.state, runConfigWithCommentCmd) if err != nil || containerID == "" { return err @@ -290,10 +292,10 @@ func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error { return d.builder.commitContainer(d.state, containerID, runConfigWithCommentCmd) } -func resolveCmdLine(cmd instructions.ShellDependantCmdLine, runConfig *container.Config, platform string) []string { +func resolveCmdLine(cmd instructions.ShellDependantCmdLine, runConfig *container.Config, os string) []string { result := cmd.CmdLine if cmd.PrependShell && result != nil { - result = append(getShell(runConfig, platform), result...) + result = append(getShell(runConfig, os), result...) } return result } @@ -309,10 +311,11 @@ func resolveCmdLine(cmd instructions.ShellDependantCmdLine, runConfig *container // RUN [ "echo", "hi" ] # echo hi // func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error { - + if !system.IsOSSupported(d.state.operatingSystem) { + return system.ErrNotSupportedOperatingSystem + } stateRunConfig := d.state.runConfig - optionsOS := system.ParsePlatform(d.builder.options.Platform).OS - cmdFromArgs := resolveCmdLine(c.ShellDependantCmdLine, stateRunConfig, optionsOS) + cmdFromArgs := resolveCmdLine(c.ShellDependantCmdLine, stateRunConfig, d.state.operatingSystem) buildArgs := d.state.buildArgs.FilterAllowed(stateRunConfig.Env) saveCmd := cmdFromArgs diff --git a/components/engine/builder/dockerfile/dispatchers_test.go b/components/engine/builder/dockerfile/dispatchers_test.go index dc9148bca5..29cecd8fcd 100644 --- a/components/engine/builder/dockerfile/dispatchers_test.go +++ b/components/engine/builder/dockerfile/dispatchers_test.go @@ -31,7 +31,7 @@ func newBuilderWithMockBackend() *Builder { Options: &types.ImageBuildOptions{Platform: runtime.GOOS}, Backend: mockBackend, }), - imageProber: newImageProber(mockBackend, nil, runtime.GOOS, false), + imageProber: newImageProber(mockBackend, nil, false), containerManager: newContainerManager(mockBackend), } return b @@ -427,10 +427,10 @@ func TestRunWithBuildArgs(t *testing.T) { } mockBackend := b.docker.(*MockBackend) - mockBackend.makeImageCacheFunc = func(_ []string, _ string) builder.ImageCache { + mockBackend.makeImageCacheFunc = func(_ []string) builder.ImageCache { return imageCache } - b.imageProber = newImageProber(mockBackend, nil, runtime.GOOS, false) + b.imageProber = newImageProber(mockBackend, nil, false) mockBackend.getImageFunc = func(_ string) (builder.Image, builder.ReleaseableLayer, error) { return &mockImage{ id: "abcdef", diff --git a/components/engine/builder/dockerfile/evaluator.go b/components/engine/builder/dockerfile/evaluator.go index cf3d4fd01e..f60869d7c4 100644 --- a/components/engine/builder/dockerfile/evaluator.go +++ b/components/engine/builder/dockerfile/evaluator.go @@ -21,6 +21,7 @@ package dockerfile import ( "reflect" + "runtime" "strconv" "strings" @@ -104,13 +105,14 @@ func dispatch(d dispatchRequest, cmd instructions.Command) (err error) { // dispatchState is a data object which is modified by dispatchers type dispatchState struct { - runConfig *container.Config - maintainer string - cmdSet bool - imageID string - baseImage builder.Image - stageName string - buildArgs *buildArgs + runConfig *container.Config + maintainer string + cmdSet bool + imageID string + baseImage builder.Image + stageName string + buildArgs *buildArgs + operatingSystem string } func newDispatchState(baseArgs *buildArgs) *dispatchState { @@ -210,9 +212,16 @@ func (s *dispatchState) hasFromImage() bool { return s.imageID != "" || (s.baseImage != nil && s.baseImage.ImageID() == "") } -func (s *dispatchState) beginStage(stageName string, image builder.Image) { +func (s *dispatchState) beginStage(stageName string, image builder.Image) error { s.stageName = stageName s.imageID = image.ImageID() + s.operatingSystem = image.OperatingSystem() + if s.operatingSystem == "" { // In case it isn't set + s.operatingSystem = runtime.GOOS + } + if !system.IsOSSupported(s.operatingSystem) { + return system.ErrNotSupportedOperatingSystem + } if image.RunConfig() != nil { // copy avoids referencing the same instance when 2 stages have the same base @@ -224,12 +233,13 @@ func (s *dispatchState) beginStage(stageName string, image builder.Image) { s.setDefaultPath() s.runConfig.OpenStdin = false s.runConfig.StdinOnce = false + return nil } // Add the default PATH to runConfig.ENV if one exists for the operating system and there // is no PATH set. Note that Windows containers on Windows won't have one as it's set by HCS func (s *dispatchState) setDefaultPath() { - defaultPath := system.DefaultPathEnv(s.baseImage.OperatingSystem()) + defaultPath := system.DefaultPathEnv(s.operatingSystem) if defaultPath == "" { return } diff --git a/components/engine/builder/dockerfile/imageprobe.go b/components/engine/builder/dockerfile/imageprobe.go index 239eb9e38f..1a6df69385 100644 --- a/components/engine/builder/dockerfile/imageprobe.go +++ b/components/engine/builder/dockerfile/imageprobe.go @@ -19,13 +19,13 @@ type imageProber struct { cacheBusted bool } -func newImageProber(cacheBuilder builder.ImageCacheBuilder, cacheFrom []string, platform string, noCache bool) ImageProber { +func newImageProber(cacheBuilder builder.ImageCacheBuilder, cacheFrom []string, noCache bool) ImageProber { if noCache { return &nopProber{} } reset := func() builder.ImageCache { - return cacheBuilder.MakeImageCache(cacheFrom, platform) + return cacheBuilder.MakeImageCache(cacheFrom) } return &imageProber{cache: reset(), reset: reset} } diff --git a/components/engine/builder/dockerfile/internals.go b/components/engine/builder/dockerfile/internals.go index adce7b9709..3d93b510a7 100644 --- a/components/engine/builder/dockerfile/internals.go +++ b/components/engine/builder/dockerfile/internals.go @@ -125,8 +125,7 @@ func (b *Builder) commitContainer(dispatchState *dispatchState, id string, conta } func (b *Builder) exportImage(state *dispatchState, imageMount *imageMount, runConfig *container.Config) error { - optionsPlatform := system.ParsePlatform(b.options.Platform) - newLayer, err := imageMount.Layer().Commit(optionsPlatform.OS) + newLayer, err := imageMount.Layer().Commit() if err != nil { return err } @@ -154,7 +153,7 @@ func (b *Builder) exportImage(state *dispatchState, imageMount *imageMount, runC return errors.Wrap(err, "failed to encode image config") } - exportedImage, err := b.docker.CreateImage(config, state.imageID, parentImage.OS) + exportedImage, err := b.docker.CreateImage(config, state.imageID) if err != nil { return errors.Wrapf(err, "failed to export image") } @@ -502,15 +501,13 @@ func (b *Builder) probeAndCreate(dispatchState *dispatchState, runConfig *contai } // Set a log config to override any default value set on the daemon hostConfig := &container.HostConfig{LogConfig: defaultLogConfig} - optionsPlatform := system.ParsePlatform(b.options.Platform) - container, err := b.containerManager.Create(runConfig, hostConfig, optionsPlatform.OS) + container, err := b.containerManager.Create(runConfig, hostConfig) return container.ID, err } func (b *Builder) create(runConfig *container.Config) (string, error) { hostConfig := hostConfigFromOptions(b.options) - optionsPlatform := system.ParsePlatform(b.options.Platform) - container, err := b.containerManager.Create(runConfig, hostConfig, optionsPlatform.OS) + container, err := b.containerManager.Create(runConfig, hostConfig) if err != nil { return "", err } diff --git a/components/engine/builder/dockerfile/mockbackend_test.go b/components/engine/builder/dockerfile/mockbackend_test.go index 986861cbbb..6c19f17ebc 100644 --- a/components/engine/builder/dockerfile/mockbackend_test.go +++ b/components/engine/builder/dockerfile/mockbackend_test.go @@ -20,7 +20,7 @@ type MockBackend struct { containerCreateFunc func(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error) commitFunc func(string, *backend.ContainerCommitConfig) (string, error) getImageFunc func(string) (builder.Image, builder.ReleaseableLayer, error) - makeImageCacheFunc func(cacheFrom []string, platform string) builder.ImageCache + makeImageCacheFunc func(cacheFrom []string) builder.ImageCache } func (m *MockBackend) ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool, attached chan struct{}) error { @@ -73,14 +73,14 @@ func (m *MockBackend) GetImageAndReleasableLayer(ctx context.Context, refOrID st return &mockImage{id: "theid"}, &mockLayer{}, nil } -func (m *MockBackend) MakeImageCache(cacheFrom []string, platform string) builder.ImageCache { +func (m *MockBackend) MakeImageCache(cacheFrom []string) builder.ImageCache { if m.makeImageCacheFunc != nil { - return m.makeImageCacheFunc(cacheFrom, platform) + return m.makeImageCacheFunc(cacheFrom) } return nil } -func (m *MockBackend) CreateImage(config []byte, parent string, platform string) (builder.Image, error) { +func (m *MockBackend) CreateImage(config []byte, parent string) (builder.Image, error) { return nil, nil } @@ -127,7 +127,7 @@ func (l *mockLayer) Mount() (containerfs.ContainerFS, error) { return containerfs.NewLocalContainerFS("mountPath"), nil } -func (l *mockLayer) Commit(string) (builder.ReleaseableLayer, error) { +func (l *mockLayer) Commit() (builder.ReleaseableLayer, error) { return nil, nil } diff --git a/components/engine/daemon/build.go b/components/engine/daemon/build.go index b8de3e76a9..b69ff897b3 100644 --- a/components/engine/daemon/build.go +++ b/components/engine/daemon/build.go @@ -2,7 +2,6 @@ package daemon import ( "io" - "runtime" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" @@ -13,6 +12,7 @@ import ( "github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/pkg/system" "github.com/docker/docker/registry" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -55,7 +55,7 @@ func (rl *releaseableLayer) Mount() (containerfs.ContainerFS, error) { return mountPath, nil } -func (rl *releaseableLayer) Commit(os string) (builder.ReleaseableLayer, error) { +func (rl *releaseableLayer) Commit() (builder.ReleaseableLayer, error) { var chainID layer.ChainID if rl.roLayer != nil { chainID = rl.roLayer.ChainID() @@ -67,7 +67,7 @@ func (rl *releaseableLayer) Commit(os string) (builder.ReleaseableLayer, error) } defer stream.Close() - newLayer, err := rl.layerStore.Register(stream, chainID, layer.OS(os)) + newLayer, err := rl.layerStore.Register(stream, chainID) if err != nil { return nil, err } @@ -142,7 +142,7 @@ func newReleasableLayerForImage(img *image.Image, layerStore layer.Store) (build } // TODO: could this use the regular daemon PullImage ? -func (daemon *Daemon) pullForBuilder(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer, platform string) (*image.Image, error) { +func (daemon *Daemon) pullForBuilder(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer, os string) (*image.Image, error) { ref, err := reference.ParseNormalizedNamed(name) if err != nil { return nil, err @@ -161,7 +161,7 @@ func (daemon *Daemon) pullForBuilder(ctx context.Context, name string, authConfi pullRegistryAuth = &resolvedConfig } - if err := daemon.pullImageWithReference(ctx, ref, platform, nil, pullRegistryAuth, output); err != nil { + if err := daemon.pullImageWithReference(ctx, ref, os, nil, pullRegistryAuth, output); err != nil { return nil, err } return daemon.GetImage(name) @@ -172,7 +172,10 @@ func (daemon *Daemon) pullForBuilder(ctx context.Context, name string, authConfi // leaking of layers. func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ReleaseableLayer, error) { if refOrID == "" { - layer, err := newReleasableLayerForImage(nil, daemon.stores[opts.OS].layerStore) + if !system.IsOSSupported(opts.OS) { + return nil, nil, system.ErrNotSupportedOperatingSystem + } + layer, err := newReleasableLayerForImage(nil, daemon.layerStores[opts.OS]) return nil, layer, err } @@ -183,7 +186,10 @@ func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID st } // TODO: shouldn't we error out if error is different from "not found" ? if image != nil { - layer, err := newReleasableLayerForImage(image, daemon.stores[opts.OS].layerStore) + if !system.IsOSSupported(image.OperatingSystem()) { + return nil, nil, system.ErrNotSupportedOperatingSystem + } + layer, err := newReleasableLayerForImage(image, daemon.layerStores[image.OperatingSystem()]) return image, layer, err } } @@ -192,29 +198,29 @@ func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID st if err != nil { return nil, nil, err } - layer, err := newReleasableLayerForImage(image, daemon.stores[opts.OS].layerStore) + if !system.IsOSSupported(image.OperatingSystem()) { + return nil, nil, system.ErrNotSupportedOperatingSystem + } + layer, err := newReleasableLayerForImage(image, daemon.layerStores[image.OperatingSystem()]) return image, layer, err } // CreateImage creates a new image by adding a config and ID to the image store. // This is similar to LoadImage() except that it receives JSON encoded bytes of // an image instead of a tar archive. -func (daemon *Daemon) CreateImage(config []byte, parent string, platform string) (builder.Image, error) { - if platform == "" { - platform = runtime.GOOS - } - id, err := daemon.stores[platform].imageStore.Create(config) +func (daemon *Daemon) CreateImage(config []byte, parent string) (builder.Image, error) { + id, err := daemon.imageStore.Create(config) if err != nil { return nil, errors.Wrapf(err, "failed to create image") } if parent != "" { - if err := daemon.stores[platform].imageStore.SetParent(id, image.ID(parent)); err != nil { + if err := daemon.imageStore.SetParent(id, image.ID(parent)); err != nil { return nil, errors.Wrapf(err, "failed to set parent %s", parent) } } - return daemon.stores[platform].imageStore.Get(id) + return daemon.imageStore.Get(id) } // IDMappings returns uid/gid mappings for the builder diff --git a/components/engine/daemon/cache.go b/components/engine/daemon/cache.go index 5ad68ec0af..ab3a249869 100644 --- a/components/engine/daemon/cache.go +++ b/components/engine/daemon/cache.go @@ -7,12 +7,12 @@ import ( ) // MakeImageCache creates a stateful image cache. -func (daemon *Daemon) MakeImageCache(sourceRefs []string, platform string) builder.ImageCache { +func (daemon *Daemon) MakeImageCache(sourceRefs []string) builder.ImageCache { if len(sourceRefs) == 0 { - return cache.NewLocal(daemon.stores[platform].imageStore) + return cache.NewLocal(daemon.imageStore) } - cache := cache.New(daemon.stores[platform].imageStore) + cache := cache.New(daemon.imageStore) for _, ref := range sourceRefs { img, err := daemon.GetImage(ref) diff --git a/components/engine/daemon/commit.go b/components/engine/daemon/commit.go index b2c969316b..de621168a9 100644 --- a/components/engine/daemon/commit.go +++ b/components/engine/daemon/commit.go @@ -17,6 +17,7 @@ import ( "github.com/docker/docker/image" "github.com/docker/docker/layer" "github.com/docker/docker/pkg/ioutils" + "github.com/docker/docker/pkg/system" "github.com/pkg/errors" ) @@ -149,12 +150,15 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str daemon.containerPause(container) defer daemon.containerUnpause(container) } + if !system.IsOSSupported(container.OS) { + return "", system.ErrNotSupportedOperatingSystem + } if c.MergeConfigs && c.Config == nil { c.Config = container.Config } - newConfig, err := dockerfile.BuildFromConfig(c.Config, c.Changes) + newConfig, err := dockerfile.BuildFromConfig(c.Config, c.Changes, container.OS) if err != nil { return "", err } @@ -180,17 +184,17 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str parent = new(image.Image) parent.RootFS = image.NewRootFS() } else { - parent, err = daemon.stores[container.OS].imageStore.Get(container.ImageID) + parent, err = daemon.imageStore.Get(container.ImageID) if err != nil { return "", err } } - l, err := daemon.stores[container.OS].layerStore.Register(rwTar, parent.RootFS.ChainID(), layer.OS(container.OS)) + l, err := daemon.layerStores[container.OS].Register(rwTar, parent.RootFS.ChainID()) if err != nil { return "", err } - defer layer.ReleaseAndLog(daemon.stores[container.OS].layerStore, l) + defer layer.ReleaseAndLog(daemon.layerStores[container.OS], l) containerConfig := c.ContainerConfig if containerConfig == nil { @@ -209,13 +213,13 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str return "", err } - id, err := daemon.stores[container.OS].imageStore.Create(config) + id, err := daemon.imageStore.Create(config) if err != nil { return "", err } if container.ImageID != "" { - if err := daemon.stores[container.OS].imageStore.SetParent(id, container.ImageID); err != nil { + if err := daemon.imageStore.SetParent(id, container.ImageID); err != nil { return "", err } } @@ -234,7 +238,7 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str return "", err } } - if err := daemon.TagImageWithReference(id, container.OS, newTag); err != nil { + if err := daemon.TagImageWithReference(id, newTag); err != nil { return "", err } imageRef = reference.FamiliarString(newTag) @@ -251,13 +255,14 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str } func (daemon *Daemon) exportContainerRw(container *container.Container) (arch io.ReadCloser, err error) { - rwlayer, err := daemon.stores[container.OS].layerStore.GetRWLayer(container.ID) + // Note: Indexing by OS is safe as only called from `Commit` which has already performed validation + rwlayer, err := daemon.layerStores[container.OS].GetRWLayer(container.ID) if err != nil { return nil, err } defer func() { if err != nil { - daemon.stores[container.OS].layerStore.ReleaseRWLayer(rwlayer) + daemon.layerStores[container.OS].ReleaseRWLayer(rwlayer) } }() @@ -278,7 +283,7 @@ func (daemon *Daemon) exportContainerRw(container *container.Container) (arch io return ioutils.NewReadCloserWrapper(archive, func() error { archive.Close() err = rwlayer.Unmount() - daemon.stores[container.OS].layerStore.ReleaseRWLayer(rwlayer) + daemon.layerStores[container.OS].ReleaseRWLayer(rwlayer) return err }), nil diff --git a/components/engine/daemon/create.go b/components/engine/daemon/create.go index 54ce12c239..4ba5ad9e27 100644 --- a/components/engine/daemon/create.go +++ b/components/engine/daemon/create.go @@ -257,7 +257,7 @@ func (daemon *Daemon) generateSecurityOpt(hostConfig *containertypes.HostConfig) func (daemon *Daemon) setRWLayer(container *container.Container) error { var layerID layer.ChainID if container.ImageID != "" { - img, err := daemon.stores[container.OS].imageStore.Get(container.ImageID) + img, err := daemon.imageStore.Get(container.ImageID) if err != nil { return err } @@ -270,7 +270,9 @@ func (daemon *Daemon) setRWLayer(container *container.Container) error { StorageOpt: container.HostConfig.StorageOpt, } - rwLayer, err := daemon.stores[container.OS].layerStore.CreateRWLayer(container.ID, layerID, rwLayerOpts) + // Indexing by OS is safe here as validation of OS has already been performed in create() (the only + // caller), and guaranteed non-nil + rwLayer, err := daemon.layerStores[container.OS].CreateRWLayer(container.ID, layerID, rwLayerOpts) if err != nil { return err } diff --git a/components/engine/daemon/daemon.go b/components/engine/daemon/daemon.go index 3378411fd6..16510316b5 100644 --- a/components/engine/daemon/daemon.go +++ b/components/engine/daemon/daemon.go @@ -69,50 +69,46 @@ var ( errSystemNotSupported = errors.New("the Docker daemon is not supported on this platform") ) -type daemonStore struct { - graphDriver string - imageRoot string - imageStore image.Store - layerStore layer.Store - distributionMetadataStore dmetadata.Store -} - // Daemon holds information about the Docker daemon. type Daemon struct { - ID string - repository string - containers container.Store - containersReplica container.ViewDB - execCommands *exec.Store - downloadManager *xfer.LayerDownloadManager - uploadManager *xfer.LayerUploadManager - trustKey libtrust.PrivateKey - idIndex *truncindex.TruncIndex - configStore *config.Config - statsCollector *stats.Collector - defaultLogConfig containertypes.LogConfig - RegistryService registry.Service - EventsService *events.Events - netController libnetwork.NetworkController - volumes *store.VolumeStore - discoveryWatcher discovery.Reloader - root string - seccompEnabled bool - apparmorEnabled bool - shutdown bool - idMappings *idtools.IDMappings - stores map[string]daemonStore // By container target platform - referenceStore refstore.Store - PluginStore *plugin.Store // todo: remove - pluginManager *plugin.Manager - linkIndex *linkIndex - containerd libcontainerd.Client - containerdRemote libcontainerd.Remote - defaultIsolation containertypes.Isolation // Default isolation mode on Windows - clusterProvider cluster.Provider - cluster Cluster - genericResources []swarm.GenericResource - metricsPluginListener net.Listener + ID string + repository string + containers container.Store + containersReplica container.ViewDB + execCommands *exec.Store + downloadManager *xfer.LayerDownloadManager + uploadManager *xfer.LayerUploadManager + trustKey libtrust.PrivateKey + idIndex *truncindex.TruncIndex + configStore *config.Config + statsCollector *stats.Collector + defaultLogConfig containertypes.LogConfig + RegistryService registry.Service + EventsService *events.Events + netController libnetwork.NetworkController + volumes *store.VolumeStore + discoveryWatcher discovery.Reloader + root string + seccompEnabled bool + apparmorEnabled bool + shutdown bool + idMappings *idtools.IDMappings + graphDrivers map[string]string // By operating system + referenceStore refstore.Store + imageStore image.Store + imageRoot string + layerStores map[string]layer.Store // By operating system + distributionMetadataStore dmetadata.Store + PluginStore *plugin.Store // todo: remove + pluginManager *plugin.Manager + linkIndex *linkIndex + containerd libcontainerd.Client + containerdRemote libcontainerd.Remote + defaultIsolation containertypes.Isolation // Default isolation mode on Windows + clusterProvider cluster.Provider + cluster Cluster + genericResources []swarm.GenericResource + metricsPluginListener net.Listener machineMemory uint64 @@ -159,11 +155,14 @@ func (daemon *Daemon) restore() error { logrus.Errorf("Failed to load container %v: %v", id, err) continue } - + if !system.IsOSSupported(container.OS) { + logrus.Errorf("Failed to load container %v: %s (%q)", id, system.ErrNotSupportedOperatingSystem, container.OS) + continue + } // Ignore the container if it does not support the current driver being used by the graph - currentDriverForContainerOS := daemon.stores[container.OS].graphDriver + currentDriverForContainerOS := daemon.graphDrivers[container.OS] if (container.Driver == "" && currentDriverForContainerOS == "aufs") || container.Driver == currentDriverForContainerOS { - rwlayer, err := daemon.stores[container.OS].layerStore.GetRWLayer(container.ID) + rwlayer, err := daemon.layerStores[container.OS].GetRWLayer(container.ID) if err != nil { logrus.Errorf("Failed to load container mount %v: %v", id, err) continue @@ -706,11 +705,12 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe // lcow. Unix platforms however run a single graphdriver for all containers, and it can // be set through an environment variable, a daemon start parameter, or chosen through // initialization of the layerstore through driver priority order for example. - d.stores = make(map[string]daemonStore) + d.graphDrivers = make(map[string]string) + d.layerStores = make(map[string]layer.Store) if runtime.GOOS == "windows" { - d.stores["windows"] = daemonStore{graphDriver: "windowsfilter"} + d.graphDrivers[runtime.GOOS] = "windowsfilter" if system.LCOWSupported() { - d.stores["linux"] = daemonStore{graphDriver: "lcow"} + d.graphDrivers["linux"] = "lcow" } } else { driverName := os.Getenv("DOCKER_DRIVER") @@ -719,7 +719,7 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe } else { logrus.Infof("Setting the storage driver from the $DOCKER_DRIVER environment variable (%s)", driverName) } - d.stores[runtime.GOOS] = daemonStore{graphDriver: driverName} // May still be empty. Layerstore init determines instead. + d.graphDrivers[runtime.GOOS] = driverName // May still be empty. Layerstore init determines instead. } d.RegistryService = registryService @@ -750,12 +750,11 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe return nil, errors.Wrap(err, "couldn't create plugin manager") } - var graphDrivers []string - for operatingSystem, ds := range d.stores { - ls, err := layer.NewStoreFromOptions(layer.StoreOptions{ - StorePath: config.Root, + for operatingSystem, gd := range d.graphDrivers { + d.layerStores[operatingSystem], err = layer.NewStoreFromOptions(layer.StoreOptions{ + Root: config.Root, MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"), - GraphDriver: ds.graphDriver, + GraphDriver: gd, GraphDriverOptions: config.GraphOptions, IDMappings: idMappings, PluginGetter: d.PluginStore, @@ -765,40 +764,37 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe if err != nil { return nil, err } - ds.graphDriver = ls.DriverName() // As layerstore may set the driver - ds.layerStore = ls - d.stores[operatingSystem] = ds - graphDrivers = append(graphDrivers, ls.DriverName()) } - // Configure and validate the kernels security support - if err := configureKernelSecuritySupport(config, graphDrivers); err != nil { + // As layerstore initialization may set the driver + for os := range d.graphDrivers { + d.graphDrivers[os] = d.layerStores[os].DriverName() + } + + // Configure and validate the kernels security support. Note this is a Linux/FreeBSD + // operation only, so it is safe to pass *just* the runtime OS graphdriver. + if err := configureKernelSecuritySupport(config, d.graphDrivers[runtime.GOOS]); err != nil { return nil, err } logrus.Debugf("Max Concurrent Downloads: %d", *config.MaxConcurrentDownloads) - lsMap := make(map[string]layer.Store) - for operatingSystem, ds := range d.stores { - lsMap[operatingSystem] = ds.layerStore - } - d.downloadManager = xfer.NewLayerDownloadManager(lsMap, *config.MaxConcurrentDownloads) + d.downloadManager = xfer.NewLayerDownloadManager(d.layerStores, *config.MaxConcurrentDownloads) logrus.Debugf("Max Concurrent Uploads: %d", *config.MaxConcurrentUploads) d.uploadManager = xfer.NewLayerUploadManager(*config.MaxConcurrentUploads) - for operatingSystem, ds := range d.stores { - imageRoot := filepath.Join(config.Root, "image", ds.graphDriver) - ifs, err := image.NewFSStoreBackend(filepath.Join(imageRoot, "imagedb")) - if err != nil { - return nil, err - } - var is image.Store - is, err = image.NewImageStore(ifs, operatingSystem, ds.layerStore) - if err != nil { - return nil, err - } - ds.imageRoot = imageRoot - ds.imageStore = is - d.stores[operatingSystem] = ds + d.imageRoot = filepath.Join(config.Root, "image", d.graphDrivers[runtime.GOOS]) + ifs, err := image.NewFSStoreBackend(filepath.Join(d.imageRoot, "imagedb")) + if err != nil { + return nil, err + } + + lgrMap := make(map[string]image.LayerGetReleaser) + for os, ls := range d.layerStores { + lgrMap[os] = ls + } + d.imageStore, err = image.NewImageStore(ifs, lgrMap) + if err != nil { + return nil, err } // Configure the volumes driver @@ -830,30 +826,25 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe // operating systems, the list of graphdrivers available isn't user configurable. // For backwards compatibility, we just put it under the windowsfilter // directory regardless. - refStoreLocation := filepath.Join(d.stores[runtime.GOOS].imageRoot, `repositories.json`) + refStoreLocation := filepath.Join(d.imageRoot, `repositories.json`) rs, err := refstore.NewReferenceStore(refStoreLocation) if err != nil { return nil, fmt.Errorf("Couldn't create reference store repository: %s", err) } d.referenceStore = rs - for platform, ds := range d.stores { - dms, err := dmetadata.NewFSMetadataStore(filepath.Join(ds.imageRoot, "distribution"), platform) - if err != nil { - return nil, err - } + d.distributionMetadataStore, err = dmetadata.NewFSMetadataStore(filepath.Join(d.imageRoot, "distribution")) + if err != nil { + return nil, err + } - ds.distributionMetadataStore = dms - d.stores[platform] = ds - - // No content-addressability migration on Windows as it never supported pre-CA - if runtime.GOOS != "windows" { - migrationStart := time.Now() - if err := v1.Migrate(config.Root, ds.graphDriver, ds.layerStore, ds.imageStore, rs, dms); err != nil { - logrus.Errorf("Graph migration failed: %q. Your old graph data was found to be too inconsistent for upgrading to content-addressable storage. Some of the old data was probably not upgraded. We recommend starting over with a clean storage directory if possible.", err) - } - logrus.Infof("Graph migration to content-addressability took %.2f seconds", time.Since(migrationStart).Seconds()) + // No content-addressability migration on Windows as it never supported pre-CA + if runtime.GOOS != "windows" { + migrationStart := time.Now() + if err := v1.Migrate(config.Root, d.graphDrivers[runtime.GOOS], d.layerStores[runtime.GOOS], d.imageStore, rs, d.distributionMetadataStore); err != nil { + logrus.Errorf("Graph migration failed: %q. Your old graph data was found to be too inconsistent for upgrading to content-addressable storage. Some of the old data was probably not upgraded. We recommend starting over with a clean storage directory if possible.", err) } + logrus.Infof("Graph migration to content-addressability took %.2f seconds", time.Since(migrationStart).Seconds()) } // Discovery is only enabled when the daemon is launched with an address to advertise. When @@ -922,13 +913,13 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe engineMemory.Set(float64(info.MemTotal)) gd := "" - for platform, ds := range d.stores { + for os, driver := range d.graphDrivers { if len(gd) > 0 { gd += ", " } - gd += ds.graphDriver - if len(d.stores) > 1 { - gd = fmt.Sprintf("%s (%s)", gd, platform) + gd += driver + if len(d.graphDrivers) > 1 { + gd = fmt.Sprintf("%s (%s)", gd, os) } } logrus.WithFields(logrus.Fields{ @@ -1009,7 +1000,7 @@ func (daemon *Daemon) Shutdown() error { logrus.Errorf("Stop container error: %v", err) return } - if mountid, err := daemon.stores[c.OS].layerStore.GetMountID(c.ID); err == nil { + if mountid, err := daemon.layerStores[c.OS].GetMountID(c.ID); err == nil { daemon.cleanupMountsByID(mountid) } logrus.Debugf("container stopped %s", c.ID) @@ -1022,10 +1013,10 @@ func (daemon *Daemon) Shutdown() error { } } - for platform, ds := range daemon.stores { - if ds.layerStore != nil { - if err := ds.layerStore.Cleanup(); err != nil { - logrus.Errorf("Error during layer Store.Cleanup(): %v %s", err, platform) + for os, ls := range daemon.layerStores { + if ls != nil { + if err := ls.Cleanup(); err != nil { + logrus.Errorf("Error during layer Store.Cleanup(): %v %s", err, os) } } } @@ -1107,8 +1098,8 @@ func (daemon *Daemon) Subnets() ([]net.IPNet, []net.IPNet) { } // GraphDriverName returns the name of the graph driver used by the layer.Store -func (daemon *Daemon) GraphDriverName(platform string) string { - return daemon.stores[platform].layerStore.DriverName() +func (daemon *Daemon) GraphDriverName(os string) string { + return daemon.layerStores[os].DriverName() } // prepareTempDir prepares and returns the default directory to use diff --git a/components/engine/daemon/daemon_unix.go b/components/engine/daemon/daemon_unix.go index 82e721fe61..bf8741096c 100644 --- a/components/engine/daemon/daemon_unix.go +++ b/components/engine/daemon/daemon_unix.go @@ -814,22 +814,14 @@ func overlaySupportsSelinux() (bool, error) { } // configureKernelSecuritySupport configures and validates security support for the kernel -func configureKernelSecuritySupport(config *config.Config, driverNames []string) error { +func configureKernelSecuritySupport(config *config.Config, driverName string) error { if config.EnableSelinuxSupport { if !selinuxEnabled() { logrus.Warn("Docker could not enable SELinux on the host system") return nil } - overlayFound := false - for _, d := range driverNames { - if d == "overlay" || d == "overlay2" { - overlayFound = true - break - } - } - - if overlayFound { + if driverName == "overlay" || driverName == "overlay2" { // If driver is overlay or overlay2, make sure kernel // supports selinux with overlay. supported, err := overlaySupportsSelinux() @@ -838,7 +830,7 @@ func configureKernelSecuritySupport(config *config.Config, driverNames []string) } if !supported { - logrus.Warnf("SELinux is not supported with the %v graph driver on this kernel", driverNames) + logrus.Warnf("SELinux is not supported with the %v graph driver on this kernel", driverName) } } } else { diff --git a/components/engine/daemon/daemon_windows.go b/components/engine/daemon/daemon_windows.go index 8582d4e580..77e12e908c 100644 --- a/components/engine/daemon/daemon_windows.go +++ b/components/engine/daemon/daemon_windows.go @@ -262,7 +262,7 @@ func ensureServicesInstalled(services []string) error { } // configureKernelSecuritySupport configures and validate security support for the kernel -func configureKernelSecuritySupport(config *config.Config, driverNames []string) error { +func configureKernelSecuritySupport(config *config.Config, driverName string) error { return nil } diff --git a/components/engine/daemon/delete.go b/components/engine/daemon/delete.go index 5fb9b9a1d9..f16d38beb3 100644 --- a/components/engine/daemon/delete.go +++ b/components/engine/daemon/delete.go @@ -94,6 +94,9 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo return fmt.Errorf("Could not kill running container %s, cannot remove - %v", container.ID, err) } } + if !system.IsOSSupported(container.OS) { + return fmt.Errorf("cannot remove %s: %s ", container.ID, system.ErrNotSupportedOperatingSystem) + } // stop collection of stats for the container regardless // if stats are currently getting collected. @@ -118,7 +121,7 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo // When container creation fails and `RWLayer` has not been created yet, we // do not call `ReleaseRWLayer` if container.RWLayer != nil { - metadata, err := daemon.stores[container.OS].layerStore.ReleaseRWLayer(container.RWLayer) + metadata, err := daemon.layerStores[container.OS].ReleaseRWLayer(container.RWLayer) layer.LogReleaseMetadata(metadata) if err != nil && err != layer.ErrMountDoesNotExist && !os.IsNotExist(errors.Cause(err)) { e := errors.Wrapf(err, "driver %q failed to remove root filesystem for %s", daemon.GraphDriverName(container.OS), container.ID) diff --git a/components/engine/daemon/disk_usage.go b/components/engine/daemon/disk_usage.go index 7142ef0781..c9b86604b1 100644 --- a/components/engine/daemon/disk_usage.go +++ b/components/engine/daemon/disk_usage.go @@ -15,12 +15,12 @@ import ( "github.com/sirupsen/logrus" ) -func (daemon *Daemon) getLayerRefs(platform string) map[layer.ChainID]int { - tmpImages := daemon.stores[platform].imageStore.Map() +func (daemon *Daemon) getLayerRefs() map[layer.ChainID]int { + tmpImages := daemon.imageStore.Map() layerRefs := map[layer.ChainID]int{} for id, img := range tmpImages { dgst := digest.Digest(id) - if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.stores[platform].imageStore.Children(id)) != 0 { + if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.imageStore.Children(id)) != 0 { continue } @@ -53,7 +53,6 @@ func (daemon *Daemon) SystemDiskUsage(ctx context.Context) (*types.DiskUsage, er } // Get all top images with extra attributes - // TODO @jhowardmsft LCOW. This may need revisiting allImages, err := daemon.Images(filters.NewArgs(), false, true) if err != nil { return nil, fmt.Errorf("failed to retrieve image list: %v", err) @@ -96,9 +95,9 @@ func (daemon *Daemon) SystemDiskUsage(ctx context.Context) (*types.DiskUsage, er // Get total layers size on disk var allLayersSize int64 - for platform := range daemon.stores { - layerRefs := daemon.getLayerRefs(platform) - allLayers := daemon.stores[platform].layerStore.Map() + layerRefs := daemon.getLayerRefs() + for _, ls := range daemon.layerStores { + allLayers := ls.Map() for _, l := range allLayers { select { case <-ctx.Done(): @@ -109,10 +108,10 @@ func (daemon *Daemon) SystemDiskUsage(ctx context.Context) (*types.DiskUsage, er if _, ok := layerRefs[l.ChainID()]; ok { allLayersSize += size } else { - logrus.Warnf("found leaked image layer %v platform %s", l.ChainID(), platform) + logrus.Warnf("found leaked image layer %v", l.ChainID()) } } else { - logrus.Warnf("failed to get diff size for layer %v %s", l.ChainID(), platform) + logrus.Warnf("failed to get diff size for layer %v", l.ChainID()) } } } diff --git a/components/engine/daemon/export.go b/components/engine/daemon/export.go index 66a0cca048..3a5af632d7 100644 --- a/components/engine/daemon/export.go +++ b/components/engine/daemon/export.go @@ -9,6 +9,7 @@ import ( "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/ioutils" + "github.com/docker/docker/pkg/system" ) // ContainerExport writes the contents of the container to the given @@ -47,13 +48,16 @@ func (daemon *Daemon) ContainerExport(name string, out io.Writer) error { } func (daemon *Daemon) containerExport(container *container.Container) (arch io.ReadCloser, err error) { - rwlayer, err := daemon.stores[container.OS].layerStore.GetRWLayer(container.ID) + if !system.IsOSSupported(container.OS) { + return nil, fmt.Errorf("cannot export %s: %s ", container.ID, system.ErrNotSupportedOperatingSystem) + } + rwlayer, err := daemon.layerStores[container.OS].GetRWLayer(container.ID) if err != nil { return nil, err } defer func() { if err != nil { - daemon.stores[container.OS].layerStore.ReleaseRWLayer(rwlayer) + daemon.layerStores[container.OS].ReleaseRWLayer(rwlayer) } }() @@ -74,7 +78,7 @@ func (daemon *Daemon) containerExport(container *container.Container) (arch io.R arch = ioutils.NewReadCloserWrapper(archive, func() error { err := archive.Close() rwlayer.Unmount() - daemon.stores[container.OS].layerStore.ReleaseRWLayer(rwlayer) + daemon.layerStores[container.OS].ReleaseRWLayer(rwlayer) return err }) daemon.LogContainerEvent(container, "export") diff --git a/components/engine/daemon/getsize_unix.go b/components/engine/daemon/getsize_unix.go index fff90f2756..deefcd9f95 100644 --- a/components/engine/daemon/getsize_unix.go +++ b/components/engine/daemon/getsize_unix.go @@ -15,12 +15,14 @@ func (daemon *Daemon) getSize(containerID string) (int64, int64) { err error ) - rwlayer, err := daemon.stores[runtime.GOOS].layerStore.GetRWLayer(containerID) + // Safe to index by runtime.GOOS as Unix hosts don't support multiple + // container operating systems. + rwlayer, err := daemon.layerStores[runtime.GOOS].GetRWLayer(containerID) if err != nil { logrus.Errorf("Failed to compute size of container rootfs %v: %v", containerID, err) return sizeRw, sizeRootfs } - defer daemon.stores[runtime.GOOS].layerStore.ReleaseRWLayer(rwlayer) + defer daemon.layerStores[runtime.GOOS].ReleaseRWLayer(rwlayer) sizeRw, err = rwlayer.Size() if err != nil { diff --git a/components/engine/daemon/graphdriver/windows/windows.go b/components/engine/daemon/graphdriver/windows/windows.go index f3d239d76a..3c05a01b05 100644 --- a/components/engine/daemon/graphdriver/windows/windows.go +++ b/components/engine/daemon/graphdriver/windows/windows.go @@ -931,8 +931,6 @@ func parseStorageOpt(storageOpt map[string]string) (*storageOptions, error) { return nil, err } options.size = uint64(size) - default: - return nil, fmt.Errorf("Unknown storage option: %s", key) } } return &options, nil diff --git a/components/engine/daemon/image.go b/components/engine/daemon/image.go index fdbd6e2be7..c2afa02f16 100644 --- a/components/engine/daemon/image.go +++ b/components/engine/daemon/image.go @@ -2,7 +2,6 @@ package daemon import ( "fmt" - "runtime" "github.com/docker/distribution/reference" "github.com/docker/docker/errdefs" @@ -38,32 +37,27 @@ func (daemon *Daemon) GetImageIDAndOS(refOrID string) (image.ID, string, error) return "", "", errImageDoesNotExist{ref} } id := image.IDFromDigest(digested.Digest()) - for platform := range daemon.stores { - if _, err = daemon.stores[platform].imageStore.Get(id); err == nil { - return id, platform, nil - } + if img, err := daemon.imageStore.Get(id); err == nil { + return id, img.OperatingSystem(), nil } return "", "", errImageDoesNotExist{ref} } if digest, err := daemon.referenceStore.Get(namedRef); err == nil { // Search the image stores to get the operating system, defaulting to host OS. - imageOS := runtime.GOOS id := image.IDFromDigest(digest) - for os := range daemon.stores { - if img, err := daemon.stores[os].imageStore.Get(id); err == nil { - imageOS = img.OperatingSystem() - break - } + if img, err := daemon.imageStore.Get(id); err == nil { + return id, img.OperatingSystem(), nil } - return id, imageOS, nil } // Search based on ID - for os := range daemon.stores { - if id, err := daemon.stores[os].imageStore.Search(refOrID); err == nil { - return id, os, nil + if id, err := daemon.imageStore.Search(refOrID); err == nil { + img, err := daemon.imageStore.Get(id) + if err != nil { + return "", "", errImageDoesNotExist{ref} } + return id, img.OperatingSystem(), nil } return "", "", errImageDoesNotExist{ref} @@ -71,9 +65,9 @@ func (daemon *Daemon) GetImageIDAndOS(refOrID string) (image.ID, string, error) // GetImage returns an image corresponding to the image referred to by refOrID. func (daemon *Daemon) GetImage(refOrID string) (*image.Image, error) { - imgID, os, err := daemon.GetImageIDAndOS(refOrID) + imgID, _, err := daemon.GetImageIDAndOS(refOrID) if err != nil { return nil, err } - return daemon.stores[os].imageStore.Get(imgID) + return daemon.imageStore.Get(imgID) } diff --git a/components/engine/daemon/image_delete.go b/components/engine/daemon/image_delete.go index 36ac2c7c68..c5dc578294 100644 --- a/components/engine/daemon/image_delete.go +++ b/components/engine/daemon/image_delete.go @@ -11,6 +11,7 @@ import ( "github.com/docker/docker/errdefs" "github.com/docker/docker/image" "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/pkg/system" "github.com/pkg/errors" ) @@ -66,10 +67,13 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I start := time.Now() records := []types.ImageDeleteResponseItem{} - imgID, os, err := daemon.GetImageIDAndOS(imageRef) + imgID, operatingSystem, err := daemon.GetImageIDAndOS(imageRef) if err != nil { return nil, err } + if !system.IsOSSupported(operatingSystem) { + return nil, errors.Errorf("unable to delete image: %q", system.ErrNotSupportedOperatingSystem) + } repoRefs := daemon.referenceStore.References(imgID.Digest()) @@ -95,7 +99,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I return nil, err } - parsedRef, err = daemon.removeImageRef(os, parsedRef) + parsedRef, err = daemon.removeImageRef(parsedRef) if err != nil { return nil, err } @@ -123,7 +127,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I remainingRefs := []reference.Named{} for _, repoRef := range repoRefs { if _, repoRefIsCanonical := repoRef.(reference.Canonical); repoRefIsCanonical && parsedRef.Name() == repoRef.Name() { - if _, err := daemon.removeImageRef(os, repoRef); err != nil { + if _, err := daemon.removeImageRef(repoRef); err != nil { return records, err } @@ -153,12 +157,12 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I if !force { c |= conflictSoft &^ conflictActiveReference } - if conflict := daemon.checkImageDeleteConflict(imgID, os, c); conflict != nil { + if conflict := daemon.checkImageDeleteConflict(imgID, c); conflict != nil { return nil, conflict } for _, repoRef := range repoRefs { - parsedRef, err := daemon.removeImageRef(os, repoRef) + parsedRef, err := daemon.removeImageRef(repoRef) if err != nil { return nil, err } @@ -171,7 +175,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I } } - if err := daemon.imageDeleteHelper(imgID, os, &records, force, prune, removedRepositoryRef); err != nil { + if err := daemon.imageDeleteHelper(imgID, &records, force, prune, removedRepositoryRef); err != nil { return nil, err } @@ -232,7 +236,7 @@ func (daemon *Daemon) getContainerUsingImage(imageID image.ID) *container.Contai // repositoryRef must not be an image ID but a repository name followed by an // optional tag or digest reference. If tag or digest is omitted, the default // tag is used. Returns the resolved image reference and an error. -func (daemon *Daemon) removeImageRef(platform string, ref reference.Named) (reference.Named, error) { +func (daemon *Daemon) removeImageRef(ref reference.Named) (reference.Named, error) { ref = reference.TagNameOnly(ref) // Ignore the boolean value returned, as far as we're concerned, this @@ -248,11 +252,11 @@ func (daemon *Daemon) removeImageRef(platform string, ref reference.Named) (refe // on the first encountered error. Removed references are logged to this // daemon's event service. An "Untagged" types.ImageDeleteResponseItem is added to the // given list of records. -func (daemon *Daemon) removeAllReferencesToImageID(imgID image.ID, platform string, records *[]types.ImageDeleteResponseItem) error { +func (daemon *Daemon) removeAllReferencesToImageID(imgID image.ID, records *[]types.ImageDeleteResponseItem) error { imageRefs := daemon.referenceStore.References(imgID.Digest()) for _, imageRef := range imageRefs { - parsedRef, err := daemon.removeImageRef(platform, imageRef) + parsedRef, err := daemon.removeImageRef(imageRef) if err != nil { return err } @@ -299,15 +303,15 @@ func (idc *imageDeleteConflict) Conflict() {} // conflict is encountered, it will be returned immediately without deleting // the image. If quiet is true, any encountered conflicts will be ignored and // the function will return nil immediately without deleting the image. -func (daemon *Daemon) imageDeleteHelper(imgID image.ID, platform string, records *[]types.ImageDeleteResponseItem, force, prune, quiet bool) error { +func (daemon *Daemon) imageDeleteHelper(imgID image.ID, records *[]types.ImageDeleteResponseItem, force, prune, quiet bool) error { // First, determine if this image has any conflicts. Ignore soft conflicts // if force is true. c := conflictHard if !force { c |= conflictSoft } - if conflict := daemon.checkImageDeleteConflict(imgID, platform, c); conflict != nil { - if quiet && (!daemon.imageIsDangling(imgID, platform) || conflict.used) { + if conflict := daemon.checkImageDeleteConflict(imgID, c); conflict != nil { + if quiet && (!daemon.imageIsDangling(imgID) || conflict.used) { // Ignore conflicts UNLESS the image is "dangling" or not being used in // which case we want the user to know. return nil @@ -318,18 +322,18 @@ func (daemon *Daemon) imageDeleteHelper(imgID image.ID, platform string, records return conflict } - parent, err := daemon.stores[platform].imageStore.GetParent(imgID) + parent, err := daemon.imageStore.GetParent(imgID) if err != nil { // There may be no parent parent = "" } // Delete all repository tag/digest references to this image. - if err := daemon.removeAllReferencesToImageID(imgID, platform, records); err != nil { + if err := daemon.removeAllReferencesToImageID(imgID, records); err != nil { return err } - removedLayers, err := daemon.stores[platform].imageStore.Delete(imgID) + removedLayers, err := daemon.imageStore.Delete(imgID) if err != nil { return err } @@ -349,7 +353,7 @@ func (daemon *Daemon) imageDeleteHelper(imgID image.ID, platform string, records // either running or stopped). // Do not force prunings, but do so quietly (stopping on any encountered // conflicts). - return daemon.imageDeleteHelper(parent, platform, records, false, true, true) + return daemon.imageDeleteHelper(parent, records, false, true, true) } // checkImageDeleteConflict determines whether there are any conflicts @@ -358,9 +362,9 @@ func (daemon *Daemon) imageDeleteHelper(imgID image.ID, platform string, records // using the image. A soft conflict is any tags/digest referencing the given // image or any stopped container using the image. If ignoreSoftConflicts is // true, this function will not check for soft conflict conditions. -func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, platform string, mask conflictType) *imageDeleteConflict { +func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, mask conflictType) *imageDeleteConflict { // Check if the image has any descendant images. - if mask&conflictDependentChild != 0 && len(daemon.stores[platform].imageStore.Children(imgID)) > 0 { + if mask&conflictDependentChild != 0 && len(daemon.imageStore.Children(imgID)) > 0 { return &imageDeleteConflict{ hard: true, imgID: imgID, @@ -411,6 +415,6 @@ func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, platform string, // imageIsDangling returns whether the given image is "dangling" which means // that there are no repository references to the given image and it has no // child images. -func (daemon *Daemon) imageIsDangling(imgID image.ID, platform string) bool { - return !(len(daemon.referenceStore.References(imgID.Digest())) > 0 || len(daemon.stores[platform].imageStore.Children(imgID)) > 0) +func (daemon *Daemon) imageIsDangling(imgID image.ID) bool { + return !(len(daemon.referenceStore.References(imgID.Digest())) > 0 || len(daemon.imageStore.Children(imgID)) > 0) } diff --git a/components/engine/daemon/image_exporter.go b/components/engine/daemon/image_exporter.go index ce9aa7a050..4b85b30e14 100644 --- a/components/engine/daemon/image_exporter.go +++ b/components/engine/daemon/image_exporter.go @@ -2,10 +2,8 @@ package daemon import ( "io" - "runtime" "github.com/docker/docker/image/tarexport" - "github.com/docker/docker/pkg/system" ) // ExportImage exports a list of images to the given output stream. The @@ -14,12 +12,7 @@ import ( // the same tag are exported. names is the set of tags to export, and // outStream is the writer which the images are written to. func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error { - // TODO @jhowardmsft LCOW. This will need revisiting later. - platform := runtime.GOOS - if system.LCOWSupported() { - platform = "linux" - } - imageExporter := tarexport.NewTarExporter(daemon.stores[platform].imageStore, daemon.stores[platform].layerStore, daemon.referenceStore, daemon) + imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStores, daemon.referenceStore, daemon) return imageExporter.Save(names, outStream) } @@ -27,11 +20,6 @@ func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error { // complement of ImageExport. The input stream is an uncompressed tar // ball containing images and metadata. func (daemon *Daemon) LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error { - // TODO @jhowardmsft LCOW. This will need revisiting later. - platform := runtime.GOOS - if system.LCOWSupported() { - platform = "linux" - } - imageExporter := tarexport.NewTarExporter(daemon.stores[platform].imageStore, daemon.stores[platform].layerStore, daemon.referenceStore, daemon) + imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStores, daemon.referenceStore, daemon) return imageExporter.Load(inTar, outStream, quiet) } diff --git a/components/engine/daemon/image_history.go b/components/engine/daemon/image_history.go index e7dd85cbab..9cb9efc3c0 100644 --- a/components/engine/daemon/image_history.go +++ b/components/engine/daemon/image_history.go @@ -2,7 +2,6 @@ package daemon import ( "fmt" - "runtime" "time" "github.com/docker/distribution/reference" @@ -19,12 +18,6 @@ func (daemon *Daemon) ImageHistory(name string) ([]*image.HistoryResponseItem, e return nil, err } - // If the image OS isn't set, assume it's the host OS - platform := img.OS - if platform == "" { - platform = runtime.GOOS - } - history := []*image.HistoryResponseItem{} layerCounter := 0 @@ -40,12 +33,12 @@ func (daemon *Daemon) ImageHistory(name string) ([]*image.HistoryResponseItem, e } rootFS.Append(img.RootFS.DiffIDs[layerCounter]) - l, err := daemon.stores[platform].layerStore.Get(rootFS.ChainID()) + l, err := daemon.layerStores[img.OperatingSystem()].Get(rootFS.ChainID()) if err != nil { return nil, err } layerSize, err = l.DiffSize() - layer.ReleaseAndLog(daemon.stores[platform].layerStore, l) + layer.ReleaseAndLog(daemon.layerStores[img.OperatingSystem()], l) if err != nil { return nil, err } diff --git a/components/engine/daemon/image_inspect.go b/components/engine/daemon/image_inspect.go index fefb93c6e1..6d76c0a947 100644 --- a/components/engine/daemon/image_inspect.go +++ b/components/engine/daemon/image_inspect.go @@ -1,12 +1,12 @@ package daemon import ( - "runtime" "time" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/layer" + "github.com/docker/docker/pkg/system" "github.com/pkg/errors" ) @@ -17,13 +17,9 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) { if err != nil { return nil, errors.Wrapf(err, "no such image: %s", name) } - - // If the image OS isn't set, assume it's the host OS - platform := img.OS - if platform == "" { - platform = runtime.GOOS + if !system.IsOSSupported(img.OperatingSystem()) { + return nil, system.ErrNotSupportedOperatingSystem } - refs := daemon.referenceStore.References(img.ID().Digest()) repoTags := []string{} repoDigests := []string{} @@ -40,11 +36,11 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) { var layerMetadata map[string]string layerID := img.RootFS.ChainID() if layerID != "" { - l, err := daemon.stores[platform].layerStore.Get(layerID) + l, err := daemon.layerStores[img.OperatingSystem()].Get(layerID) if err != nil { return nil, err } - defer layer.ReleaseAndLog(daemon.stores[platform].layerStore, l) + defer layer.ReleaseAndLog(daemon.layerStores[img.OperatingSystem()], l) size, err = l.Size() if err != nil { return nil, err @@ -61,7 +57,7 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) { comment = img.History[len(img.History)-1].Comment } - lastUpdated, err := daemon.stores[platform].imageStore.GetLastUpdated(img.ID()) + lastUpdated, err := daemon.imageStore.GetLastUpdated(img.ID()) if err != nil { return nil, err } @@ -79,7 +75,7 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) { Author: img.Author, Config: img.Config, Architecture: img.Architecture, - Os: platform, + Os: img.OperatingSystem(), OsVersion: img.OSVersion, Size: size, VirtualSize: size, // TODO: field unused, deprecate @@ -89,7 +85,7 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) { }, } - imageInspect.GraphDriver.Name = daemon.GraphDriverName(platform) + imageInspect.GraphDriver.Name = daemon.GraphDriverName(img.OperatingSystem()) imageInspect.GraphDriver.Data = layerMetadata return imageInspect, nil diff --git a/components/engine/daemon/image_pull.go b/components/engine/daemon/image_pull.go index 4944721f9f..318e3582d3 100644 --- a/components/engine/daemon/image_pull.go +++ b/components/engine/daemon/image_pull.go @@ -19,7 +19,7 @@ import ( // PullImage initiates a pull operation. image is the repository name to pull, and // tag may be either empty, or indicate a specific tag to pull. -func (daemon *Daemon) PullImage(ctx context.Context, image, tag, platform string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { +func (daemon *Daemon) PullImage(ctx context.Context, image, tag, os string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { // Special case: "pull -a" may send an image name with a // trailing :. This is ugly, but let's not break API // compatibility. @@ -44,10 +44,10 @@ func (daemon *Daemon) PullImage(ctx context.Context, image, tag, platform string } } - return daemon.pullImageWithReference(ctx, ref, platform, metaHeaders, authConfig, outStream) + return daemon.pullImageWithReference(ctx, ref, os, metaHeaders, authConfig, outStream) } -func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference.Named, platform string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { +func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference.Named, os string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { // Include a buffer so that slow client connections don't affect // transfer performance. progressChan := make(chan progress.Progress, 100) @@ -62,8 +62,8 @@ func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference. }() // Default to the host OS platform in case it hasn't been populated with an explicit value. - if platform == "" { - platform = runtime.GOOS + if os == "" { + os = runtime.GOOS } imagePullConfig := &distribution.ImagePullConfig{ @@ -73,13 +73,13 @@ func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference. ProgressOutput: progress.ChanOutput(progressChan), RegistryService: daemon.RegistryService, ImageEventLogger: daemon.LogImageEvent, - MetadataStore: daemon.stores[platform].distributionMetadataStore, - ImageStore: distribution.NewImageConfigStoreFromStore(daemon.stores[platform].imageStore), + MetadataStore: daemon.distributionMetadataStore, + ImageStore: distribution.NewImageConfigStoreFromStore(daemon.imageStore), ReferenceStore: daemon.referenceStore, }, DownloadManager: daemon.downloadManager, Schema2Types: distribution.ImageTypes, - Platform: platform, + OS: os, } err := distribution.Pull(ctx, ref, imagePullConfig) diff --git a/components/engine/daemon/image_push.go b/components/engine/daemon/image_push.go index b558073379..a98fe1fd1c 100644 --- a/components/engine/daemon/image_push.go +++ b/components/engine/daemon/image_push.go @@ -2,7 +2,6 @@ package daemon import ( "io" - "runtime" "github.com/docker/distribution/manifest/schema2" "github.com/docker/distribution/reference" @@ -10,7 +9,6 @@ import ( "github.com/docker/docker/distribution" progressutils "github.com/docker/docker/distribution/utils" "github.com/docker/docker/pkg/progress" - "github.com/docker/docker/pkg/system" "golang.org/x/net/context" ) @@ -41,12 +39,6 @@ func (daemon *Daemon) PushImage(ctx context.Context, image, tag string, metaHead close(writesDone) }() - // TODO @jhowardmsft LCOW Support. This will require revisiting. For now, hard-code. - platform := runtime.GOOS - if system.LCOWSupported() { - platform = "linux" - } - imagePushConfig := &distribution.ImagePushConfig{ Config: distribution.Config{ MetaHeaders: metaHeaders, @@ -54,12 +46,12 @@ func (daemon *Daemon) PushImage(ctx context.Context, image, tag string, metaHead ProgressOutput: progress.ChanOutput(progressChan), RegistryService: daemon.RegistryService, ImageEventLogger: daemon.LogImageEvent, - MetadataStore: daemon.stores[platform].distributionMetadataStore, - ImageStore: distribution.NewImageConfigStoreFromStore(daemon.stores[platform].imageStore), + MetadataStore: daemon.distributionMetadataStore, + ImageStore: distribution.NewImageConfigStoreFromStore(daemon.imageStore), ReferenceStore: daemon.referenceStore, }, ConfigMediaType: schema2.MediaTypeImageConfig, - LayerStore: distribution.NewLayerProviderFromStore(daemon.stores[platform].layerStore), + LayerStores: distribution.NewLayerProvidersFromStores(daemon.layerStores), TrustKey: daemon.trustKey, UploadManager: daemon.uploadManager, } diff --git a/components/engine/daemon/image_tag.go b/components/engine/daemon/image_tag.go index cfac6d6ac9..a955f7d970 100644 --- a/components/engine/daemon/image_tag.go +++ b/components/engine/daemon/image_tag.go @@ -8,7 +8,7 @@ import ( // TagImage creates the tag specified by newTag, pointing to the image named // imageName (alternatively, imageName can also be an image ID). func (daemon *Daemon) TagImage(imageName, repository, tag string) error { - imageID, os, err := daemon.GetImageIDAndOS(imageName) + imageID, _, err := daemon.GetImageIDAndOS(imageName) if err != nil { return err } @@ -23,16 +23,16 @@ func (daemon *Daemon) TagImage(imageName, repository, tag string) error { } } - return daemon.TagImageWithReference(imageID, os, newTag) + return daemon.TagImageWithReference(imageID, newTag) } // TagImageWithReference adds the given reference to the image ID provided. -func (daemon *Daemon) TagImageWithReference(imageID image.ID, os string, newTag reference.Named) error { +func (daemon *Daemon) TagImageWithReference(imageID image.ID, newTag reference.Named) error { if err := daemon.referenceStore.AddTag(newTag, imageID.Digest(), true); err != nil { return err } - if err := daemon.stores[os].imageStore.SetLastUpdated(imageID); err != nil { + if err := daemon.imageStore.SetLastUpdated(imageID); err != nil { return err } daemon.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), "tag") diff --git a/components/engine/daemon/images.go b/components/engine/daemon/images.go index 7cd2c628f6..5d388581f0 100644 --- a/components/engine/daemon/images.go +++ b/components/engine/daemon/images.go @@ -3,7 +3,6 @@ package daemon import ( "encoding/json" "fmt" - "runtime" "sort" "time" @@ -36,12 +35,7 @@ func (r byCreated) Less(i, j int) bool { return r[i].Created < r[j].Created } // Map returns a map of all images in the ImageStore func (daemon *Daemon) Map() map[image.ID]*image.Image { - // TODO @jhowardmsft LCOW. This can be removed when imagestores are coalesced - platform := runtime.GOOS - if system.LCOWSupported() { - platform = "linux" - } - return daemon.stores[platform].imageStore.Map() + return daemon.imageStore.Map() } // Images returns a filtered list of images. filterArgs is a JSON-encoded set @@ -50,13 +44,6 @@ func (daemon *Daemon) Map() map[image.ID]*image.Image { // named all controls whether all images in the graph are filtered, or just // the heads. func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error) { - - // TODO @jhowardmsft LCOW. This can be removed when imagestores are coalesced - platform := runtime.GOOS - if system.LCOWSupported() { - platform = "linux" - } - var ( allImages map[image.ID]*image.Image err error @@ -75,9 +62,9 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs } } if danglingOnly { - allImages = daemon.stores[platform].imageStore.Heads() + allImages = daemon.imageStore.Heads() } else { - allImages = daemon.stores[platform].imageStore.Map() + allImages = daemon.imageStore.Map() } var beforeFilter, sinceFilter *image.Image @@ -127,10 +114,17 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs } } + // Skip any images with an unsupported operating system to avoid a potential + // panic when indexing through the layerstore. Don't error as we want to list + // the other images. This should never happen, but here as a safety precaution. + if !system.IsOSSupported(img.OperatingSystem()) { + continue + } + layerID := img.RootFS.ChainID() var size int64 if layerID != "" { - l, err := daemon.stores[platform].layerStore.Get(layerID) + l, err := daemon.layerStores[img.OperatingSystem()].Get(layerID) if err != nil { // The layer may have been deleted between the call to `Map()` or // `Heads()` and the call to `Get()`, so we just ignore this error @@ -141,7 +135,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs } size, err = l.Size() - layer.ReleaseAndLog(daemon.stores[platform].layerStore, l) + layer.ReleaseAndLog(daemon.layerStores[img.OperatingSystem()], l) if err != nil { return nil, err } @@ -171,7 +165,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs } } if newImage.RepoDigests == nil && newImage.RepoTags == nil { - if all || len(daemon.stores[platform].imageStore.Children(id)) == 0 { + if all || len(daemon.imageStore.Children(id)) == 0 { if imageFilters.Contains("dangling") && !danglingOnly { //dangling=false case, so dangling image is not needed @@ -193,7 +187,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs // lazily init variables if imagesMap == nil { allContainers = daemon.List() - allLayers = daemon.stores[platform].layerStore.Map() + allLayers = daemon.layerStores[img.OperatingSystem()].Map() imagesMap = make(map[*image.Image]*types.ImageSummary) layerRefs = make(map[layer.ChainID]int) } @@ -261,19 +255,14 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) { img *image.Image err error ) - for _, ds := range daemon.stores { - if img, err = ds.imageStore.Get(image.ID(id)); err == nil { - break - } - } - if err != nil { + if img, err = daemon.imageStore.Get(image.ID(id)); err != nil { return "", err } var parentImg *image.Image var parentChainID layer.ChainID if len(parent) != 0 { - parentImg, err = daemon.stores[img.OperatingSystem()].imageStore.Get(image.ID(parent)) + parentImg, err = daemon.imageStore.Get(image.ID(parent)) if err != nil { return "", errors.Wrap(err, "error getting specified parent layer") } @@ -283,11 +272,11 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) { parentImg = &image.Image{RootFS: rootFS} } - l, err := daemon.stores[img.OperatingSystem()].layerStore.Get(img.RootFS.ChainID()) + l, err := daemon.layerStores[img.OperatingSystem()].Get(img.RootFS.ChainID()) if err != nil { return "", errors.Wrap(err, "error getting image layer") } - defer daemon.stores[img.OperatingSystem()].layerStore.Release(l) + defer daemon.layerStores[img.OperatingSystem()].Release(l) ts, err := l.TarStreamFrom(parentChainID) if err != nil { @@ -295,11 +284,11 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) { } defer ts.Close() - newL, err := daemon.stores[img.OperatingSystem()].layerStore.Register(ts, parentChainID, layer.OS(img.OperatingSystem())) + newL, err := daemon.layerStores[img.OperatingSystem()].Register(ts, parentChainID) if err != nil { return "", errors.Wrap(err, "error registering layer") } - defer daemon.stores[img.OperatingSystem()].layerStore.Release(newL) + defer daemon.layerStores[img.OperatingSystem()].Release(newL) newImage := *img newImage.RootFS = nil @@ -334,7 +323,7 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) { return "", errors.Wrap(err, "error marshalling image config") } - newImgID, err := daemon.stores[img.OperatingSystem()].imageStore.Create(b) + newImgID, err := daemon.imageStore.Create(b) if err != nil { return "", errors.Wrap(err, "error creating new image after squash") } diff --git a/components/engine/daemon/import.go b/components/engine/daemon/import.go index 3ac83a9fa9..bd4a3fdfca 100644 --- a/components/engine/daemon/import.go +++ b/components/engine/daemon/import.go @@ -57,7 +57,7 @@ func (daemon *Daemon) ImportImage(src string, repository, os string, tag string, } } - config, err := dockerfile.BuildFromConfig(&container.Config{}, changes) + config, err := dockerfile.BuildFromConfig(&container.Config{}, changes, os) if err != nil { return err } @@ -91,11 +91,11 @@ func (daemon *Daemon) ImportImage(src string, repository, os string, tag string, if err != nil { return err } - l, err := daemon.stores[os].layerStore.Register(inflatedLayerData, "", layer.OS(os)) + l, err := daemon.layerStores[os].Register(inflatedLayerData, "") if err != nil { return err } - defer layer.ReleaseAndLog(daemon.stores[os].layerStore, l) + defer layer.ReleaseAndLog(daemon.layerStores[os], l) created := time.Now().UTC() imgConfig, err := json.Marshal(&image.Image{ @@ -120,14 +120,14 @@ func (daemon *Daemon) ImportImage(src string, repository, os string, tag string, return err } - id, err := daemon.stores[os].imageStore.Create(imgConfig) + id, err := daemon.imageStore.Create(imgConfig) if err != nil { return err } // FIXME: connect with commit code and call refstore directly if newRef != nil { - if err := daemon.TagImageWithReference(id, os, newRef); err != nil { + if err := daemon.TagImageWithReference(id, newRef); err != nil { return err } } diff --git a/components/engine/daemon/info.go b/components/engine/daemon/info.go index bbb027ed54..3a28834014 100644 --- a/components/engine/daemon/info.go +++ b/components/engine/daemon/info.go @@ -78,32 +78,26 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) { securityOptions = append(securityOptions, "name=userns") } - imageCount := 0 + var ds [][2]string drivers := "" - for p, ds := range daemon.stores { - imageCount += len(ds.imageStore.Map()) - drivers += daemon.GraphDriverName(p) - if len(daemon.stores) > 1 { - drivers += fmt.Sprintf(" (%s) ", p) + for os, gd := range daemon.graphDrivers { + ds = append(ds, daemon.layerStores[os].DriverStatus()...) + drivers += gd + if len(daemon.graphDrivers) > 1 { + drivers += fmt.Sprintf(" (%s) ", os) } } - - // TODO @jhowardmsft LCOW support. For now, hard-code the platform shown for the driver status - p := runtime.GOOS - if system.LCOWSupported() { - p = "linux" - } - drivers = strings.TrimSpace(drivers) + v := &types.Info{ ID: daemon.ID, Containers: cRunning + cPaused + cStopped, ContainersRunning: cRunning, ContainersPaused: cPaused, ContainersStopped: cStopped, - Images: imageCount, + Images: len(daemon.imageStore.Map()), Driver: drivers, - DriverStatus: daemon.stores[p].layerStore.DriverStatus(), + DriverStatus: ds, Plugins: daemon.showPluginsInfo(), IPv4Forwarding: !sysInfo.IPv4ForwardingDisabled, BridgeNfIptables: !sysInfo.BridgeNFCallIPTablesDisabled, diff --git a/components/engine/daemon/list.go b/components/engine/daemon/list.go index 8598d25280..626de7767a 100644 --- a/components/engine/daemon/list.go +++ b/components/engine/daemon/list.go @@ -323,7 +323,7 @@ func (daemon *Daemon) foldFilter(view container.View, config *types.ContainerLis if psFilters.Contains("ancestor") { ancestorFilter = true psFilters.WalkValues("ancestor", func(ancestor string) error { - id, os, err := daemon.GetImageIDAndOS(ancestor) + id, _, err := daemon.GetImageIDAndOS(ancestor) if err != nil { logrus.Warnf("Error while looking up for image %v", ancestor) return nil @@ -333,7 +333,7 @@ func (daemon *Daemon) foldFilter(view container.View, config *types.ContainerLis return nil } // Then walk down the graph and put the imageIds in imagesFilter - populateImageFilterByParents(imagesFilter, id, daemon.stores[os].imageStore.Children) + populateImageFilterByParents(imagesFilter, id, daemon.imageStore.Children) return nil }) } diff --git a/components/engine/daemon/oci_windows.go b/components/engine/daemon/oci_windows.go index c7c94f327a..0799e5e01f 100644 --- a/components/engine/daemon/oci_windows.go +++ b/components/engine/daemon/oci_windows.go @@ -138,9 +138,12 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { max := len(img.RootFS.DiffIDs) for i := 1; i <= max; i++ { img.RootFS.DiffIDs = img.RootFS.DiffIDs[:i] - layerPath, err := layer.GetLayerPath(daemon.stores[c.OS].layerStore, img.RootFS.ChainID()) + if !system.IsOSSupported(img.OperatingSystem()) { + return nil, fmt.Errorf("cannot get layerpath for ImageID %s: %s ", img.RootFS.ChainID(), system.ErrNotSupportedOperatingSystem) + } + layerPath, err := layer.GetLayerPath(daemon.layerStores[img.OperatingSystem()], img.RootFS.ChainID()) if err != nil { - return nil, fmt.Errorf("failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.stores[c.OS].layerStore, img.RootFS.ChainID(), err) + return nil, fmt.Errorf("failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.layerStores[img.OperatingSystem()], img.RootFS.ChainID(), err) } // Reverse order, expecting parent most first s.Windows.LayerFolders = append([]string{layerPath}, s.Windows.LayerFolders...) @@ -210,15 +213,18 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { NetworkSharedContainerName: networkSharedContainerID, } - if img.OS == "windows" { + switch img.OS { + case "windows": if err := daemon.createSpecWindowsFields(c, &s, isHyperV); err != nil { return nil, err } - } else { - // TODO @jhowardmsft LCOW Support. Modify this check when running in dual-mode - if system.LCOWSupported() && img.OS == "linux" { - daemon.createSpecLinuxFields(c, &s) + case "linux": + if !system.LCOWSupported() { + return nil, fmt.Errorf("Linux containers on Windows are not supported") } + daemon.createSpecLinuxFields(c, &s) + default: + return nil, fmt.Errorf("Unsupported platform %q", img.OS) } return (*specs.Spec)(&s), nil diff --git a/components/engine/daemon/prune.go b/components/engine/daemon/prune.go index 1850752cf5..cf42f28728 100644 --- a/components/engine/daemon/prune.go +++ b/components/engine/daemon/prune.go @@ -3,7 +3,6 @@ package daemon import ( "fmt" "regexp" - "runtime" "sync/atomic" "time" @@ -14,7 +13,6 @@ import ( "github.com/docker/docker/image" "github.com/docker/docker/layer" "github.com/docker/docker/pkg/directory" - "github.com/docker/docker/pkg/system" "github.com/docker/docker/runconfig" "github.com/docker/docker/volume" "github.com/docker/libnetwork" @@ -162,12 +160,6 @@ func (daemon *Daemon) VolumesPrune(ctx context.Context, pruneFilters filters.Arg // ImagesPrune removes unused images func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error) { - // TODO @jhowardmsft LCOW Support: This will need revisiting later. - platform := runtime.GOOS - if system.LCOWSupported() { - platform = "linux" - } - if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) { return nil, errPruneRunning } @@ -197,9 +189,9 @@ func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args var allImages map[image.ID]*image.Image if danglingOnly { - allImages = daemon.stores[platform].imageStore.Heads() + allImages = daemon.imageStore.Heads() } else { - allImages = daemon.stores[platform].imageStore.Map() + allImages = daemon.imageStore.Map() } allContainers := daemon.List() imageRefs := map[string]bool{} @@ -213,7 +205,12 @@ func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args } // Filter intermediary images and get their unique size - allLayers := daemon.stores[platform].layerStore.Map() + allLayers := make(map[layer.ChainID]layer.Layer) + for _, ls := range daemon.layerStores { + for k, v := range ls.Map() { + allLayers[k] = v + } + } topImages := map[image.ID]*image.Image{} for id, img := range allImages { select { @@ -221,7 +218,7 @@ func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args return nil, ctx.Err() default: dgst := digest.Digest(id) - if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.stores[platform].imageStore.Children(id)) != 0 { + if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.imageStore.Children(id)) != 0 { continue } if !until.IsZero() && img.Created.After(until) { diff --git a/components/engine/daemon/start.go b/components/engine/daemon/start.go index 008fb050d4..d4e423d478 100644 --- a/components/engine/daemon/start.go +++ b/components/engine/daemon/start.go @@ -222,7 +222,7 @@ func (daemon *Daemon) Cleanup(container *container.Container) { if err := daemon.conditionalUnmountOnCleanup(container); err != nil { // FIXME: remove once reference counting for graphdrivers has been refactored // Ensure that all the mounts are gone - if mountid, err := daemon.stores[container.OS].layerStore.GetMountID(container.ID); err == nil { + if mountid, err := daemon.layerStores[container.OS].GetMountID(container.ID); err == nil { daemon.cleanupMountsByID(mountid) } } diff --git a/components/engine/distribution/config.go b/components/engine/distribution/config.go index 16d729c0ec..f42c9670ff 100644 --- a/components/engine/distribution/config.go +++ b/components/engine/distribution/config.go @@ -59,9 +59,9 @@ type ImagePullConfig struct { // Schema2Types is the valid schema2 configuration types allowed // by the pull operation. Schema2Types []string - // Platform is the requested platform of the image being pulled to ensure it can be validated - // when the host platform supports multiple image operating systems. - Platform string + // OS is the requested operating system of the image being pulled to ensure it can be validated + // when the host OS supports multiple image operating systems. + OS string } // ImagePushConfig stores push configuration. @@ -71,8 +71,8 @@ type ImagePushConfig struct { // ConfigMediaType is the configuration media type for // schema2 manifests. ConfigMediaType string - // LayerStore manages layers. - LayerStore PushLayerProvider + // LayerStores (indexed by operating system) manages layers. + LayerStores map[string]PushLayerProvider // TrustKey is the private key for legacy signatures. This is typically // an ephemeral key, since these signatures are no longer verified. TrustKey libtrust.PrivateKey @@ -86,7 +86,7 @@ type ImagePushConfig struct { type ImageConfigStore interface { Put([]byte) (digest.Digest, error) Get(digest.Digest) ([]byte, error) - RootFSAndOSFromConfig([]byte) (*image.RootFS, layer.OS, error) + RootFSAndOSFromConfig([]byte) (*image.RootFS, string, error) } // PushLayerProvider provides layers to be pushed by ChainID. @@ -112,7 +112,7 @@ type RootFSDownloadManager interface { // returns the final rootfs. // Given progress output to track download progress // Returns function to release download resources - Download(ctx context.Context, initialRootFS image.RootFS, os layer.OS, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) + Download(ctx context.Context, initialRootFS image.RootFS, os string, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) } type imageConfigStore struct { @@ -140,7 +140,7 @@ func (s *imageConfigStore) Get(d digest.Digest) ([]byte, error) { return img.RawJSON(), nil } -func (s *imageConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, layer.OS, error) { +func (s *imageConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, string, error) { var unmarshalledConfig image.Image if err := json.Unmarshal(c, &unmarshalledConfig); err != nil { return nil, "", err @@ -154,24 +154,29 @@ func (s *imageConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, layer return nil, "", fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS) } - os := "" - if runtime.GOOS == "windows" { - os = unmarshalledConfig.OS + os := unmarshalledConfig.OS + if os == "" { + os = runtime.GOOS } - return unmarshalledConfig.RootFS, layer.OS(os), nil + if !system.IsOSSupported(os) { + return nil, "", system.ErrNotSupportedOperatingSystem + } + return unmarshalledConfig.RootFS, os, nil } type storeLayerProvider struct { ls layer.Store } -// NewLayerProviderFromStore returns a layer provider backed by +// NewLayerProvidersFromStores returns layer providers backed by // an instance of LayerStore. Only getting layers as gzipped // tars is supported. -func NewLayerProviderFromStore(ls layer.Store) PushLayerProvider { - return &storeLayerProvider{ - ls: ls, +func NewLayerProvidersFromStores(lss map[string]layer.Store) map[string]PushLayerProvider { + plps := make(map[string]PushLayerProvider) + for os, ls := range lss { + plps[os] = &storeLayerProvider{ls: ls} } + return plps } func (p *storeLayerProvider) Get(lid layer.ChainID) (PushLayer, error) { diff --git a/components/engine/distribution/metadata/metadata.go b/components/engine/distribution/metadata/metadata.go index 3dae79555d..05ba4f817d 100644 --- a/components/engine/distribution/metadata/metadata.go +++ b/components/engine/distribution/metadata/metadata.go @@ -26,17 +26,15 @@ type Store interface { type FSMetadataStore struct { sync.RWMutex basePath string - platform string } // NewFSMetadataStore creates a new filesystem-based metadata store. -func NewFSMetadataStore(basePath, platform string) (*FSMetadataStore, error) { +func NewFSMetadataStore(basePath string) (*FSMetadataStore, error) { if err := os.MkdirAll(basePath, 0700); err != nil { return nil, err } return &FSMetadataStore{ basePath: basePath, - platform: platform, }, nil } diff --git a/components/engine/distribution/metadata/v1_id_service_test.go b/components/engine/distribution/metadata/v1_id_service_test.go index 957ed5d63a..337278613e 100644 --- a/components/engine/distribution/metadata/v1_id_service_test.go +++ b/components/engine/distribution/metadata/v1_id_service_test.go @@ -3,7 +3,6 @@ package metadata import ( "io/ioutil" "os" - "runtime" "testing" "github.com/docker/docker/layer" @@ -17,7 +16,7 @@ func TestV1IDService(t *testing.T) { } defer os.RemoveAll(tmpDir) - metadataStore, err := NewFSMetadataStore(tmpDir, runtime.GOOS) + metadataStore, err := NewFSMetadataStore(tmpDir) if err != nil { t.Fatalf("could not create metadata store: %v", err) } diff --git a/components/engine/distribution/metadata/v2_metadata_service_test.go b/components/engine/distribution/metadata/v2_metadata_service_test.go index b5d59b2297..8e3e4614c0 100644 --- a/components/engine/distribution/metadata/v2_metadata_service_test.go +++ b/components/engine/distribution/metadata/v2_metadata_service_test.go @@ -6,7 +6,6 @@ import ( "math/rand" "os" "reflect" - "runtime" "testing" "github.com/docker/docker/layer" @@ -20,7 +19,7 @@ func TestV2MetadataService(t *testing.T) { } defer os.RemoveAll(tmpDir) - metadataStore, err := NewFSMetadataStore(tmpDir, runtime.GOOS) + metadataStore, err := NewFSMetadataStore(tmpDir) if err != nil { t.Fatalf("could not create metadata store: %v", err) } diff --git a/components/engine/distribution/pull.go b/components/engine/distribution/pull.go index 446197cf2b..008ccfb5e8 100644 --- a/components/engine/distribution/pull.go +++ b/components/engine/distribution/pull.go @@ -21,7 +21,7 @@ type Puller interface { // Pull tries to pull the image referenced by `tag` // Pull returns an error if any, as well as a boolean that determines whether to retry Pull on the next configured endpoint. // - Pull(ctx context.Context, ref reference.Named, platform string) error + Pull(ctx context.Context, ref reference.Named, os string) error } // newPuller returns a Puller interface that will pull from either a v1 or v2 @@ -115,12 +115,12 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo continue } - // Make sure we default the platform if it hasn't been supplied - if imagePullConfig.Platform == "" { - imagePullConfig.Platform = runtime.GOOS + // Make sure we default the OS if it hasn't been supplied + if imagePullConfig.OS == "" { + imagePullConfig.OS = runtime.GOOS } - if err := puller.Pull(ctx, ref, imagePullConfig.Platform); err != nil { + if err := puller.Pull(ctx, ref, imagePullConfig.OS); err != nil { // Was this pull cancelled? If so, don't try to fall // back. fallback := false diff --git a/components/engine/distribution/pull_v1.go b/components/engine/distribution/pull_v1.go index 6f1c2eee0e..17e9a7cce2 100644 --- a/components/engine/distribution/pull_v1.go +++ b/components/engine/distribution/pull_v1.go @@ -36,7 +36,7 @@ type v1Puller struct { session *registry.Session } -func (p *v1Puller) Pull(ctx context.Context, ref reference.Named, platform string) error { +func (p *v1Puller) Pull(ctx context.Context, ref reference.Named, os string) error { if _, isCanonical := ref.(reference.Canonical); isCanonical { // Allowing fallback, because HTTPS v1 is before HTTP v2 return fallbackError{err: ErrNoSupport{Err: errors.New("Cannot pull by digest with v1 registry")}} diff --git a/components/engine/distribution/pull_v2.go b/components/engine/distribution/pull_v2.go index 35ff529b4f..0b3a3b0cd8 100644 --- a/components/engine/distribution/pull_v2.go +++ b/components/engine/distribution/pull_v2.go @@ -62,7 +62,7 @@ type v2Puller struct { confirmedV2 bool } -func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, platform string) (err error) { +func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, os string) (err error) { // TODO(tiborvass): was ReceiveTimeout p.repo, p.confirmedV2, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull") if err != nil { @@ -70,7 +70,7 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, platform strin return err } - if err = p.pullV2Repository(ctx, ref, platform); err != nil { + if err = p.pullV2Repository(ctx, ref, os); err != nil { if _, ok := err.(fallbackError); ok { return err } @@ -85,10 +85,10 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, platform strin return err } -func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, platform string) (err error) { +func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, os string) (err error) { var layersDownloaded bool if !reference.IsNameOnly(ref) { - layersDownloaded, err = p.pullV2Tag(ctx, ref, platform) + layersDownloaded, err = p.pullV2Tag(ctx, ref, os) if err != nil { return err } @@ -110,7 +110,7 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, pl if err != nil { return err } - pulledNew, err := p.pullV2Tag(ctx, tagRef, platform) + pulledNew, err := p.pullV2Tag(ctx, tagRef, os) if err != nil { // Since this is the pull-all-tags case, don't // allow an error pulling a particular tag to @@ -488,9 +488,9 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv descriptors = append(descriptors, layerDescriptor) } - // The v1 manifest itself doesn't directly contain a platform. However, + // The v1 manifest itself doesn't directly contain an OS. However, // the history does, but unfortunately that's a string, so search through - // all the history until hopefully we find one which indicates the os. + // all the history until hopefully we find one which indicates the OS. // supertest2014/nyan is an example of a registry image with schemav1. configOS := runtime.GOOS if system.LCOWSupported() { @@ -514,7 +514,7 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configOS, requestedOS) } - resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, layer.OS(configOS), descriptors, p.config.ProgressOutput) + resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, configOS, descriptors, p.config.ProgressOutput) if err != nil { return "", "", err } @@ -588,7 +588,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s downloadedRootFS *image.RootFS // rootFS from registered layers configRootFS *image.RootFS // rootFS from configuration release func() // release resources from rootFS download - configOS layer.OS // for LCOW when registering downloaded layers + configOS string // for LCOW when registering downloaded layers ) // https://github.com/docker/docker/issues/24766 - Err on the side of caution, @@ -615,7 +615,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s // Early bath if the requested OS doesn't match that of the configuration. // This avoids doing the download, only to potentially fail later. - if !strings.EqualFold(string(configOS), requestedOS) { + if !strings.EqualFold(configOS, requestedOS) { return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configOS, requestedOS) } @@ -633,7 +633,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s rootFS image.RootFS ) downloadRootFS := *image.NewRootFS() - rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, layer.OS(requestedOS), descriptors, p.config.ProgressOutput) + rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, requestedOS, 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) @@ -698,7 +698,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s return imageID, manifestDigest, nil } -func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan error) ([]byte, *image.RootFS, layer.OS, error) { +func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan error) ([]byte, *image.RootFS, string, error) { select { case configJSON := <-configChan: rootfs, os, err := s.RootFSAndOSFromConfig(configJSON) diff --git a/components/engine/distribution/push_v1.go b/components/engine/distribution/push_v1.go index dccc7b094f..6dbc110110 100644 --- a/components/engine/distribution/push_v1.go +++ b/components/engine/distribution/push_v1.go @@ -14,6 +14,7 @@ import ( "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/progress" "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/pkg/system" "github.com/docker/docker/registry" "github.com/opencontainers/go-digest" "github.com/sirupsen/logrus" @@ -210,7 +211,10 @@ func (p *v1Pusher) imageListForTag(imgID image.ID, dependenciesSeen map[layer.Ch topLayerID := img.RootFS.ChainID() - pl, err := p.config.LayerStore.Get(topLayerID) + if !system.IsOSSupported(img.OperatingSystem()) { + return nil, system.ErrNotSupportedOperatingSystem + } + pl, err := p.config.LayerStores[img.OperatingSystem()].Get(topLayerID) *referencedLayers = append(*referencedLayers, pl) if err != nil { return nil, fmt.Errorf("failed to get top layer from image: %v", err) diff --git a/components/engine/distribution/push_v2.go b/components/engine/distribution/push_v2.go index 2aecc183b0..90b3b60300 100644 --- a/components/engine/distribution/push_v2.go +++ b/components/engine/distribution/push_v2.go @@ -118,12 +118,12 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id return fmt.Errorf("could not find image from tag %s: %v", reference.FamiliarString(ref), err) } - rootfs, _, err := p.config.ImageStore.RootFSAndOSFromConfig(imgConfig) + rootfs, os, err := p.config.ImageStore.RootFSAndOSFromConfig(imgConfig) if err != nil { return fmt.Errorf("unable to get rootfs for image %s: %s", reference.FamiliarString(ref), err) } - l, err := p.config.LayerStore.Get(rootfs.ChainID()) + l, err := p.config.LayerStores[os].Get(rootfs.ChainID()) if err != nil { return fmt.Errorf("failed to get top layer from image: %v", err) } diff --git a/components/engine/distribution/xfer/download.go b/components/engine/distribution/xfer/download.go index 043119e320..1ccb91a14f 100644 --- a/components/engine/distribution/xfer/download.go +++ b/components/engine/distribution/xfer/download.go @@ -13,6 +13,7 @@ import ( "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/progress" + "github.com/docker/docker/pkg/system" "github.com/sirupsen/logrus" "golang.org/x/net/context" ) @@ -95,7 +96,7 @@ type DownloadDescriptorWithRegistered interface { // Download method is called to get the layer tar data. Layers are then // registered in the appropriate order. The caller must call the returned // release function once it is done with the returned RootFS object. -func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS image.RootFS, os layer.OS, layers []DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) { +func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS image.RootFS, os string, layers []DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) { var ( topLayer layer.Layer topDownload *downloadTransfer @@ -105,9 +106,13 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima downloadsByKey = make(map[string]*downloadTransfer) ) - // Assume that the operating system is the host OS if blank + // Assume that the operating system is the host OS if blank, and validate it + // to ensure we don't cause a panic by an invalid index into the layerstores. if os == "" { - os = layer.OS(runtime.GOOS) + os = runtime.GOOS + } + if !system.IsOSSupported(os) { + return image.RootFS{}, nil, system.ErrNotSupportedOperatingSystem } rootFS := initialRootFS @@ -121,20 +126,20 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima if err == nil { getRootFS := rootFS getRootFS.Append(diffID) - l, err := ldm.layerStores[string(os)].Get(getRootFS.ChainID()) + l, err := ldm.layerStores[os].Get(getRootFS.ChainID()) if err == nil { // Layer already exists. logrus.Debugf("Layer already exists: %s", descriptor.ID()) progress.Update(progressOutput, descriptor.ID(), "Already exists") if topLayer != nil { - layer.ReleaseAndLog(ldm.layerStores[string(os)], topLayer) + layer.ReleaseAndLog(ldm.layerStores[os], topLayer) } topLayer = l missingLayer = false rootFS.Append(diffID) // Register this repository as a source of this layer. withRegistered, hasRegistered := descriptor.(DownloadDescriptorWithRegistered) - if hasRegistered { + if hasRegistered { // As layerstore may set the driver withRegistered.Registered(diffID) } continue @@ -171,7 +176,7 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima if topDownload == nil { return rootFS, func() { if topLayer != nil { - layer.ReleaseAndLog(ldm.layerStores[string(os)], topLayer) + layer.ReleaseAndLog(ldm.layerStores[os], topLayer) } }, nil } @@ -182,7 +187,7 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima defer func() { if topLayer != nil { - layer.ReleaseAndLog(ldm.layerStores[string(os)], topLayer) + layer.ReleaseAndLog(ldm.layerStores[os], topLayer) } }() @@ -218,11 +223,11 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima // complete before the registration step, and registers the downloaded data // on top of parentDownload's resulting layer. Otherwise, it registers the // layer on top of the ChainID given by parentLayer. -func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor, parentLayer layer.ChainID, parentDownload *downloadTransfer, os layer.OS) DoFunc { +func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor, parentLayer layer.ChainID, parentDownload *downloadTransfer, os string) DoFunc { return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { d := &downloadTransfer{ Transfer: NewTransfer(), - layerStore: ldm.layerStores[string(os)], + layerStore: ldm.layerStores[os], } go func() { @@ -341,9 +346,9 @@ func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor, src = fs.Descriptor() } if ds, ok := d.layerStore.(layer.DescribableStore); ok { - d.layer, err = ds.RegisterWithDescriptor(inflatedLayerData, parentLayer, os, src) + d.layer, err = ds.RegisterWithDescriptor(inflatedLayerData, parentLayer, src) } else { - d.layer, err = d.layerStore.Register(inflatedLayerData, parentLayer, os) + d.layer, err = d.layerStore.Register(inflatedLayerData, parentLayer) } if err != nil { select { @@ -382,11 +387,11 @@ func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor, // parentDownload. This function does not log progress output because it would // interfere with the progress reporting for sourceDownload, which has the same // Key. -func (ldm *LayerDownloadManager) makeDownloadFuncFromDownload(descriptor DownloadDescriptor, sourceDownload *downloadTransfer, parentDownload *downloadTransfer, os layer.OS) DoFunc { +func (ldm *LayerDownloadManager) makeDownloadFuncFromDownload(descriptor DownloadDescriptor, sourceDownload *downloadTransfer, parentDownload *downloadTransfer, os string) DoFunc { return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { d := &downloadTransfer{ Transfer: NewTransfer(), - layerStore: ldm.layerStores[string(os)], + layerStore: ldm.layerStores[os], } go func() { @@ -440,9 +445,9 @@ func (ldm *LayerDownloadManager) makeDownloadFuncFromDownload(descriptor Downloa src = fs.Descriptor() } if ds, ok := d.layerStore.(layer.DescribableStore); ok { - d.layer, err = ds.RegisterWithDescriptor(layerReader, parentLayer, os, src) + d.layer, err = ds.RegisterWithDescriptor(layerReader, parentLayer, src) } else { - d.layer, err = d.layerStore.Register(layerReader, parentLayer, os) + d.layer, err = d.layerStore.Register(layerReader, parentLayer) } if err != nil { d.err = fmt.Errorf("failed to register layer: %v", err) diff --git a/components/engine/distribution/xfer/download_test.go b/components/engine/distribution/xfer/download_test.go index 8af27f6e71..0d86b658c1 100644 --- a/components/engine/distribution/xfer/download_test.go +++ b/components/engine/distribution/xfer/download_test.go @@ -26,7 +26,7 @@ type mockLayer struct { diffID layer.DiffID chainID layer.ChainID parent layer.Layer - os layer.OS + os string } func (ml *mockLayer) TarStream() (io.ReadCloser, error) { @@ -57,10 +57,6 @@ func (ml *mockLayer) DiffSize() (size int64, err error) { return 0, nil } -func (ml *mockLayer) OS() layer.OS { - return ml.os -} - func (ml *mockLayer) Metadata() (map[string]string, error) { return make(map[string]string), nil } @@ -91,7 +87,7 @@ func (ls *mockLayerStore) Map() map[layer.ChainID]layer.Layer { return layers } -func (ls *mockLayerStore) Register(reader io.Reader, parentID layer.ChainID, os layer.OS) (layer.Layer, error) { +func (ls *mockLayerStore) Register(reader io.Reader, parentID layer.ChainID) (layer.Layer, error) { return ls.RegisterWithDescriptor(reader, parentID, distribution.Descriptor{}) } @@ -293,13 +289,13 @@ func TestSuccessfulDownload(t *testing.T) { firstDescriptor := descriptors[0].(*mockDownloadDescriptor) // Pre-register the first layer to simulate an already-existing layer - l, err := layerStore.Register(firstDescriptor.mockTarStream(), "", layer.OS(runtime.GOOS)) + l, err := layerStore.Register(firstDescriptor.mockTarStream(), "") if err != nil { t.Fatal(err) } firstDescriptor.diffID = l.DiffID() - rootFS, releaseFunc, err := ldm.Download(context.Background(), *image.NewRootFS(), layer.OS(runtime.GOOS), descriptors, progress.ChanOutput(progressChan)) + rootFS, releaseFunc, err := ldm.Download(context.Background(), *image.NewRootFS(), runtime.GOOS, descriptors, progress.ChanOutput(progressChan)) if err != nil { t.Fatalf("download error: %v", err) } @@ -339,7 +335,6 @@ func TestCancelledDownload(t *testing.T) { lsMap := make(map[string]layer.Store) lsMap[runtime.GOOS] = layerStore ldm := NewLayerDownloadManager(lsMap, maxDownloadConcurrency, func(m *LayerDownloadManager) { m.waitDuration = time.Millisecond }) - progressChan := make(chan progress.Progress) progressDone := make(chan struct{}) @@ -357,7 +352,7 @@ func TestCancelledDownload(t *testing.T) { }() descriptors := downloadDescriptors(nil) - _, _, err := ldm.Download(ctx, *image.NewRootFS(), layer.OS(runtime.GOOS), descriptors, progress.ChanOutput(progressChan)) + _, _, err := ldm.Download(ctx, *image.NewRootFS(), runtime.GOOS, descriptors, progress.ChanOutput(progressChan)) if err != context.Canceled { t.Fatal("expected download to be cancelled") } diff --git a/components/engine/image/store.go b/components/engine/image/store.go index 04842857a1..60f499c9fe 100644 --- a/components/engine/image/store.go +++ b/components/engine/image/store.go @@ -3,7 +3,6 @@ package image import ( "encoding/json" "fmt" - "strings" "sync" "time" @@ -43,21 +42,19 @@ type imageMeta struct { type store struct { sync.RWMutex - ls LayerGetReleaser + lss map[string]LayerGetReleaser images map[ID]*imageMeta fs StoreBackend digestSet *digestset.Set - os string } -// NewImageStore returns new store object for given layer store -func NewImageStore(fs StoreBackend, os string, ls LayerGetReleaser) (Store, error) { +// NewImageStore returns new store object for given set of layer stores +func NewImageStore(fs StoreBackend, lss map[string]LayerGetReleaser) (Store, error) { is := &store{ - ls: ls, + lss: lss, images: make(map[ID]*imageMeta), fs: fs, digestSet: digestset.NewSet(), - os: os, } // load all current images and retain layers @@ -77,7 +74,10 @@ func (is *store) restore() error { } var l layer.Layer if chainID := img.RootFS.ChainID(); chainID != "" { - l, err = is.ls.Get(chainID) + if !system.IsOSSupported(img.OperatingSystem()) { + return system.ErrNotSupportedOperatingSystem + } + l, err = is.lss[img.OperatingSystem()].Get(chainID) if err != nil { return err } @@ -118,14 +118,6 @@ func (is *store) Create(config []byte) (ID, error) { return "", err } - // TODO @jhowardmsft - LCOW Support. This will need revisiting when coalescing the image stores. - // Integrity check - ensure we are creating something for the correct platform - if system.LCOWSupported() { - if strings.ToLower(img.OperatingSystem()) != strings.ToLower(is.os) { - return "", fmt.Errorf("cannot create entry for operating system %q in image store for operating system %q", img.OperatingSystem(), is.os) - } - } - // Must reject any config that references diffIDs from the history // which aren't among the rootfs layers. rootFSLayers := make(map[layer.DiffID]struct{}) @@ -160,7 +152,10 @@ func (is *store) Create(config []byte) (ID, error) { var l layer.Layer if layerID != "" { - l, err = is.ls.Get(layerID) + if !system.IsOSSupported(img.OperatingSystem()) { + return "", system.ErrNotSupportedOperatingSystem + } + l, err = is.lss[img.OperatingSystem()].Get(layerID) if err != nil { return "", errors.Wrapf(err, "failed to get layer %s", layerID) } @@ -229,6 +224,13 @@ func (is *store) Delete(id ID) ([]layer.Metadata, error) { if imageMeta == nil { return nil, fmt.Errorf("unrecognized image ID %s", id.String()) } + img, err := is.Get(id) + if err != nil { + return nil, fmt.Errorf("unrecognized image %s, %v", id.String(), err) + } + if !system.IsOSSupported(img.OperatingSystem()) { + return nil, fmt.Errorf("unsupported image operating system %q", img.OperatingSystem()) + } for id := range imageMeta.children { is.fs.DeleteMetadata(id.Digest(), "parent") } @@ -243,7 +245,7 @@ func (is *store) Delete(id ID) ([]layer.Metadata, error) { is.fs.Delete(id.Digest()) if imageMeta.layer != nil { - return is.ls.Release(imageMeta.layer) + return is.lss[img.OperatingSystem()].Release(imageMeta.layer) } return nil, nil } diff --git a/components/engine/image/store_test.go b/components/engine/image/store_test.go index 23a60a98fa..4fe3660478 100644 --- a/components/engine/image/store_test.go +++ b/components/engine/image/store_test.go @@ -26,7 +26,9 @@ func TestRestore(t *testing.T) { err = fs.SetMetadata(id2, "parent", []byte(id1)) assert.NoError(t, err) - is, err := NewImageStore(fs, runtime.GOOS, &mockLayerGetReleaser{}) + mlgrMap := make(map[string]LayerGetReleaser) + mlgrMap[runtime.GOOS] = &mockLayerGetReleaser{} + is, err := NewImageStore(fs, mlgrMap) assert.NoError(t, err) assert.Len(t, is.Map(), 2) @@ -143,7 +145,9 @@ func TestParentReset(t *testing.T) { func defaultImageStore(t *testing.T) (Store, func()) { fsBackend, cleanup := defaultFSStoreBackend(t) - store, err := NewImageStore(fsBackend, runtime.GOOS, &mockLayerGetReleaser{}) + mlgrMap := make(map[string]LayerGetReleaser) + mlgrMap[runtime.GOOS] = &mockLayerGetReleaser{} + store, err := NewImageStore(fsBackend, mlgrMap) assert.NoError(t, err) return store, cleanup diff --git a/components/engine/image/tarexport/load.go b/components/engine/image/tarexport/load.go index 81beaf4a71..a0cfc64203 100644 --- a/components/engine/image/tarexport/load.go +++ b/components/engine/image/tarexport/load.go @@ -90,11 +90,11 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool) } // On Windows, validate the platform, defaulting to windows if not present. - os := layer.OS(img.OS) + os := img.OS + if os == "" { + os = runtime.GOOS + } if runtime.GOOS == "windows" { - if os == "" { - os = "windows" - } if (os != "windows") && (os != "linux") { return fmt.Errorf("configuration for this image has an unsupported operating system: %s", os) } @@ -107,14 +107,14 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool) } r := rootFS r.Append(diffID) - newLayer, err := l.ls.Get(r.ChainID()) + newLayer, err := l.lss[os].Get(r.ChainID()) if err != nil { newLayer, err = l.loadLayer(layerPath, rootFS, diffID.String(), os, m.LayerSources[diffID], progressOutput) if err != nil { return err } } - defer layer.ReleaseAndLog(l.ls, newLayer) + defer layer.ReleaseAndLog(l.lss[os], newLayer) if expected, actual := diffID, newLayer.DiffID(); expected != actual { return fmt.Errorf("invalid diffID for layer %d: expected %q, got %q", i, expected, actual) } @@ -176,7 +176,7 @@ func (l *tarexporter) setParentID(id, parentID image.ID) error { return l.is.SetParent(id, parentID) } -func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, os layer.OS, foreignSrc distribution.Descriptor, progressOutput progress.Output) (layer.Layer, error) { +func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, os string, foreignSrc distribution.Descriptor, progressOutput progress.Output) (layer.Layer, error) { // We use system.OpenSequential to use sequential file access on Windows, avoiding // depleting the standby list. On Linux, this equates to a regular os.Open. rawTar, err := system.OpenSequential(filename) @@ -205,10 +205,10 @@ func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, } defer inflatedLayerData.Close() - if ds, ok := l.ls.(layer.DescribableStore); ok { - return ds.RegisterWithDescriptor(inflatedLayerData, rootFS.ChainID(), os, foreignSrc) + if ds, ok := l.lss[os].(layer.DescribableStore); ok { + return ds.RegisterWithDescriptor(inflatedLayerData, rootFS.ChainID(), foreignSrc) } - return l.ls.Register(inflatedLayerData, rootFS.ChainID(), os) + return l.lss[os].Register(inflatedLayerData, rootFS.ChainID()) } func (l *tarexporter) setLoadedTag(ref reference.Named, imgID digest.Digest, outStream io.Writer) error { @@ -302,6 +302,9 @@ func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[str if err := checkCompatibleOS(img.OS); err != nil { return err } + if img.OS == "" { + img.OS = runtime.GOOS + } var parentID image.ID if img.Parent != "" { @@ -335,7 +338,7 @@ func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[str if err != nil { return err } - newLayer, err := l.loadLayer(layerPath, *rootFS, oldID, "", distribution.Descriptor{}, progressOutput) + newLayer, err := l.loadLayer(layerPath, *rootFS, oldID, img.OS, distribution.Descriptor{}, progressOutput) if err != nil { return err } @@ -356,7 +359,7 @@ func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[str return err } - metadata, err := l.ls.Release(newLayer) + metadata, err := l.lss[img.OS].Release(newLayer) layer.LogReleaseMetadata(metadata) if err != nil { return err @@ -409,19 +412,18 @@ func checkValidParent(img, parent *image.Image) bool { return true } -func checkCompatibleOS(os string) error { - // TODO @jhowardmsft LCOW - revisit for simultaneous platforms - platform := runtime.GOOS - if system.LCOWSupported() { - platform = "linux" - } - // always compatible if the OS matches; also match an empty OS - if os == platform || os == "" { +func checkCompatibleOS(imageOS string) error { + // always compatible if the images OS matches the host OS; also match an empty image OS + if imageOS == runtime.GOOS || imageOS == "" { return nil } - // for compatibility, only fail if the image or runtime OS is Windows - if os == "windows" || platform == "windows" { - return fmt.Errorf("cannot load %s image on %s", os, platform) + // On non-Windows hosts, for compatibility, fail if the image is Windows. + if runtime.GOOS != "windows" && imageOS == "windows" { + return fmt.Errorf("cannot load %s image on %s", imageOS, runtime.GOOS) + } + // Finally, check the image OS is supported for the platform. + if err := system.ValidatePlatform(system.ParsePlatform(imageOS)); err != nil { + return fmt.Errorf("cannot load %s image on %s: %s", imageOS, runtime.GOOS, err) } return nil } diff --git a/components/engine/image/tarexport/save.go b/components/engine/image/tarexport/save.go index d304a54c34..15f12304ad 100644 --- a/components/engine/image/tarexport/save.go +++ b/components/engine/image/tarexport/save.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "os" "path/filepath" + "runtime" "time" "github.com/docker/distribution" @@ -153,7 +154,11 @@ func (l *tarexporter) takeLayerReference(id image.ID, imgDescr *imageDescriptor) if topLayerID == "" { return nil } - layer, err := l.ls.Get(topLayerID) + os := img.OS + if os == "" { + os = runtime.GOOS + } + layer, err := l.lss[os].Get(topLayerID) if err != nil { return err } @@ -165,7 +170,11 @@ func (l *tarexporter) takeLayerReference(id image.ID, imgDescr *imageDescriptor) func (l *tarexporter) releaseLayerReferences(imgDescr map[image.ID]*imageDescriptor) error { for _, descr := range imgDescr { if descr.layerRef != nil { - l.ls.Release(descr.layerRef) + os := descr.image.OS + if os == "" { + os = runtime.GOOS + } + l.lss[os].Release(descr.layerRef) } } return nil @@ -356,11 +365,15 @@ func (s *saveSession) saveLayer(id layer.ChainID, legacyImg image.V1Image, creat // serialize filesystem layerPath := filepath.Join(outDir, legacyLayerFileName) - l, err := s.ls.Get(id) + operatingSystem := legacyImg.OS + if operatingSystem == "" { + operatingSystem = runtime.GOOS + } + l, err := s.lss[operatingSystem].Get(id) if err != nil { return distribution.Descriptor{}, err } - defer layer.ReleaseAndLog(s.ls, l) + defer layer.ReleaseAndLog(s.lss[operatingSystem], l) if oldPath, exists := s.diffIDPaths[l.DiffID()]; exists { relPath, err := filepath.Rel(outDir, oldPath) diff --git a/components/engine/image/tarexport/tarexport.go b/components/engine/image/tarexport/tarexport.go index f7fab74f53..f12259dd10 100644 --- a/components/engine/image/tarexport/tarexport.go +++ b/components/engine/image/tarexport/tarexport.go @@ -25,7 +25,7 @@ type manifestItem struct { type tarexporter struct { is image.Store - ls layer.Store + lss map[string]layer.Store rs refstore.Store loggerImgEvent LogImageEvent } @@ -37,10 +37,10 @@ type LogImageEvent interface { } // NewTarExporter returns new Exporter for tar packages -func NewTarExporter(is image.Store, ls layer.Store, rs refstore.Store, loggerImgEvent LogImageEvent) image.Exporter { +func NewTarExporter(is image.Store, lss map[string]layer.Store, rs refstore.Store, loggerImgEvent LogImageEvent) image.Exporter { return &tarexporter{ is: is, - ls: ls, + lss: lss, rs: rs, loggerImgEvent: loggerImgEvent, } diff --git a/components/engine/layer/empty.go b/components/engine/layer/empty.go index 7924a56e3a..80f2c12439 100644 --- a/components/engine/layer/empty.go +++ b/components/engine/layer/empty.go @@ -55,10 +55,6 @@ func (el *emptyLayer) Metadata() (map[string]string, error) { return make(map[string]string), nil } -func (el *emptyLayer) OS() OS { - return "" -} - // IsEmpty returns true if the layer is an EmptyLayer func IsEmpty(diffID DiffID) bool { return diffID == DigestSHA256EmptyTar diff --git a/components/engine/layer/filestore_unix.go b/components/engine/layer/filestore_unix.go index 35e282ee45..898774bdca 100644 --- a/components/engine/layer/filestore_unix.go +++ b/components/engine/layer/filestore_unix.go @@ -2,12 +2,14 @@ package layer -// SetOS writes the "os" file to the layer filestore -func (fm *fileMetadataTransaction) SetOS(os OS) error { +import "runtime" + +// setOS writes the "os" file to the layer filestore +func (fm *fileMetadataTransaction) setOS(os string) error { return nil } -// GetOS reads the "os" file from the layer filestore -func (fms *fileMetadataStore) GetOS(layer ChainID) (OS, error) { - return "", nil +// getOS reads the "os" file from the layer filestore +func (fms *fileMetadataStore) getOS(layer ChainID) (string, error) { + return runtime.GOOS, nil } diff --git a/components/engine/layer/filestore_windows.go b/components/engine/layer/filestore_windows.go index 25daa2104c..199b0b7a5b 100644 --- a/components/engine/layer/filestore_windows.go +++ b/components/engine/layer/filestore_windows.go @@ -7,16 +7,16 @@ import ( "strings" ) -// SetOS writes the "os" file to the layer filestore -func (fm *fileMetadataTransaction) SetOS(os OS) error { +// setOS writes the "os" file to the layer filestore +func (fm *fileMetadataTransaction) setOS(os string) error { if os == "" { return nil } return fm.ws.WriteFile("os", []byte(os), 0644) } -// GetOS reads the "os" file from the layer filestore -func (fms *fileMetadataStore) GetOS(layer ChainID) (OS, error) { +// getOS reads the "os" file from the layer filestore +func (fms *fileMetadataStore) getOS(layer ChainID) (string, error) { contentBytes, err := ioutil.ReadFile(fms.getLayerFilename(layer, "os")) if err != nil { // For backwards compatibility, the os file may not exist. Default to "windows" if missing. @@ -31,5 +31,5 @@ func (fms *fileMetadataStore) GetOS(layer ChainID) (OS, error) { return "", fmt.Errorf("invalid operating system value: %s", content) } - return OS(content), nil + return content, nil } diff --git a/components/engine/layer/layer.go b/components/engine/layer/layer.go index 08fc789d99..2603ac5921 100644 --- a/components/engine/layer/layer.go +++ b/components/engine/layer/layer.go @@ -65,14 +65,6 @@ func (id ChainID) String() string { return string(id) } -// OS is the operating system of a layer -type OS string - -// String returns a string rendition of layers target operating system -func (id OS) String() string { - return string(id) -} - // DiffID is the hash of an individual layer tar. type DiffID digest.Digest @@ -108,9 +100,6 @@ type Layer interface { // Parent returns the next layer in the layer chain. Parent() Layer - // OS returns the operating system of the layer - OS() OS - // Size returns the size of the entire layer chain. The size // is calculated from the total size of all files in the layers. Size() (int64, error) @@ -191,7 +180,7 @@ type CreateRWLayerOpts struct { // Store represents a backend for managing both // read-only and read-write layers. type Store interface { - Register(io.Reader, ChainID, OS) (Layer, error) + Register(io.Reader, ChainID) (Layer, error) Get(ChainID) (Layer, error) Map() map[ChainID]Layer Release(Layer) ([]Metadata, error) @@ -209,7 +198,7 @@ type Store interface { // DescribableStore represents a layer store capable of storing // descriptors for layers. type DescribableStore interface { - RegisterWithDescriptor(io.Reader, ChainID, OS, distribution.Descriptor) (Layer, error) + RegisterWithDescriptor(io.Reader, ChainID, distribution.Descriptor) (Layer, error) } // MetadataTransaction represents functions for setting layer metadata @@ -220,7 +209,7 @@ type MetadataTransaction interface { SetDiffID(DiffID) error SetCacheID(string) error SetDescriptor(distribution.Descriptor) error - SetOS(OS) error + setOS(string) error TarSplitWriter(compressInput bool) (io.WriteCloser, error) Commit(ChainID) error @@ -241,7 +230,7 @@ type MetadataStore interface { GetDiffID(ChainID) (DiffID, error) GetCacheID(ChainID) (string, error) GetDescriptor(ChainID) (distribution.Descriptor, error) - GetOS(ChainID) (OS, error) + getOS(ChainID) (string, error) TarSplitReader(ChainID) (io.ReadCloser, error) SetMountID(string, string) error diff --git a/components/engine/layer/layer_store.go b/components/engine/layer/layer_store.go index 27abcdeec7..c6225386d0 100644 --- a/components/engine/layer/layer_store.go +++ b/components/engine/layer/layer_store.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "io/ioutil" - "strings" "sync" "github.com/docker/distribution" @@ -28,23 +27,21 @@ import ( const maxLayerDepth = 125 type layerStore struct { - store MetadataStore - driver graphdriver.Driver + store MetadataStore + driver graphdriver.Driver + useTarSplit bool layerMap map[ChainID]*roLayer layerL sync.Mutex mounts map[string]*mountedLayer mountL sync.Mutex - - useTarSplit bool - - os string + os string } // StoreOptions are the options used to create a new Store instance type StoreOptions struct { - StorePath string + Root string MetadataStorePathTemplate string GraphDriver string GraphDriverOptions []string @@ -57,7 +54,7 @@ type StoreOptions struct { // NewStoreFromOptions creates a new Store instance func NewStoreFromOptions(options StoreOptions) (Store, error) { driver, err := graphdriver.New(options.GraphDriver, options.PluginGetter, graphdriver.Options{ - Root: options.StorePath, + Root: options.Root, DriverOptions: options.GraphDriverOptions, UIDMaps: options.IDMappings.UIDs(), GIDMaps: options.IDMappings.GIDs(), @@ -66,7 +63,7 @@ func NewStoreFromOptions(options StoreOptions) (Store, error) { if err != nil { return nil, fmt.Errorf("error initializing graphdriver: %v", err) } - logrus.Debugf("Using graph driver %s", driver) + logrus.Debugf("Initialized graph driver %s", driver) fms, err := NewFSMetadataStore(fmt.Sprintf(options.MetadataStorePathTemplate, driver)) if err != nil { @@ -80,6 +77,9 @@ func NewStoreFromOptions(options StoreOptions) (Store, error) { // metadata store and graph driver. The metadata store will be used to restore // the Store. func NewStoreFromGraphDriver(store MetadataStore, driver graphdriver.Driver, os string) (Store, error) { + if !system.IsOSSupported(os) { + return nil, fmt.Errorf("failed to initialize layer store as operating system '%s' is not supported", os) + } caps := graphdriver.Capabilities{} if capDriver, ok := driver.(graphdriver.CapabilityDriver); ok { caps = capDriver.Capabilities() @@ -150,11 +150,15 @@ func (ls *layerStore) loadLayer(layer ChainID) (*roLayer, error) { return nil, fmt.Errorf("failed to get descriptor for %s: %s", layer, err) } - os, err := ls.store.GetOS(layer) + os, err := ls.store.getOS(layer) if err != nil { return nil, fmt.Errorf("failed to get operating system for %s: %s", layer, err) } + if os != ls.os { + return nil, fmt.Errorf("failed to load layer with os %s into layerstore for %s", os, ls.os) + } + cl = &roLayer{ chainID: layer, diffID: diff, @@ -163,7 +167,6 @@ func (ls *layerStore) loadLayer(layer ChainID) (*roLayer, error) { layerStore: ls, references: map[Layer]struct{}{}, descriptor: descriptor, - os: os, } if parent != "" { @@ -259,11 +262,11 @@ func (ls *layerStore) applyTar(tx MetadataTransaction, ts io.Reader, parent stri return nil } -func (ls *layerStore) Register(ts io.Reader, parent ChainID, os OS) (Layer, error) { - return ls.registerWithDescriptor(ts, parent, os, distribution.Descriptor{}) +func (ls *layerStore) Register(ts io.Reader, parent ChainID) (Layer, error) { + return ls.registerWithDescriptor(ts, parent, distribution.Descriptor{}) } -func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, os OS, descriptor distribution.Descriptor) (Layer, error) { +func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, descriptor distribution.Descriptor) (Layer, error) { // err is used to hold the error which will always trigger // cleanup of creates sources but may not be an error returned // to the caller (already exists). @@ -271,13 +274,6 @@ func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, os OS var pid string var p *roLayer - // Integrity check - ensure we are creating something for the correct operating system - if system.LCOWSupported() { - if strings.ToLower(ls.os) != strings.ToLower(string(os)) { - return nil, fmt.Errorf("cannot create entry for operating system %q in layer store for operating system %q", os, ls.os) - } - } - if string(parent) != "" { p = ls.get(parent) if p == nil { @@ -306,7 +302,6 @@ func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, os OS layerStore: ls, references: map[Layer]struct{}{}, descriptor: descriptor, - os: os, } if err = ls.driver.Create(layer.cacheID, pid, nil); err != nil { diff --git a/components/engine/layer/layer_store_windows.go b/components/engine/layer/layer_store_windows.go index 8e93119fd4..1276a912cc 100644 --- a/components/engine/layer/layer_store_windows.go +++ b/components/engine/layer/layer_store_windows.go @@ -6,6 +6,6 @@ import ( "github.com/docker/distribution" ) -func (ls *layerStore) RegisterWithDescriptor(ts io.Reader, parent ChainID, os OS, descriptor distribution.Descriptor) (Layer, error) { - return ls.registerWithDescriptor(ts, parent, os, descriptor) +func (ls *layerStore) RegisterWithDescriptor(ts io.Reader, parent ChainID, descriptor distribution.Descriptor) (Layer, error) { + return ls.registerWithDescriptor(ts, parent, descriptor) } diff --git a/components/engine/layer/layer_test.go b/components/engine/layer/layer_test.go index f682035ce1..edd9c9f4c2 100644 --- a/components/engine/layer/layer_test.go +++ b/components/engine/layer/layer_test.go @@ -108,7 +108,7 @@ func createLayer(ls Store, parent ChainID, layerFunc layerInit) (Layer, error) { } defer ts.Close() - layer, err := ls.Register(ts, parent, OS(runtime.GOOS)) + layer, err := ls.Register(ts, parent) if err != nil { return nil, err } @@ -498,7 +498,7 @@ func TestTarStreamStability(t *testing.T) { t.Fatal(err) } - layer1, err := ls.Register(bytes.NewReader(tar1), "", OS(runtime.GOOS)) + layer1, err := ls.Register(bytes.NewReader(tar1), "") if err != nil { t.Fatal(err) } @@ -517,7 +517,7 @@ func TestTarStreamStability(t *testing.T) { t.Fatal(err) } - layer2, err := ls.Register(bytes.NewReader(tar2), layer1.ChainID(), OS(runtime.GOOS)) + layer2, err := ls.Register(bytes.NewReader(tar2), layer1.ChainID()) if err != nil { t.Fatal(err) } @@ -685,12 +685,12 @@ func TestRegisterExistingLayer(t *testing.T) { t.Fatal(err) } - layer2a, err := ls.Register(bytes.NewReader(tar1), layer1.ChainID(), OS(runtime.GOOS)) + layer2a, err := ls.Register(bytes.NewReader(tar1), layer1.ChainID()) if err != nil { t.Fatal(err) } - layer2b, err := ls.Register(bytes.NewReader(tar1), layer1.ChainID(), OS(runtime.GOOS)) + layer2b, err := ls.Register(bytes.NewReader(tar1), layer1.ChainID()) if err != nil { t.Fatal(err) } @@ -725,12 +725,12 @@ func TestTarStreamVerification(t *testing.T) { t.Fatal(err) } - layer1, err := ls.Register(bytes.NewReader(tar1), "", OS(runtime.GOOS)) + layer1, err := ls.Register(bytes.NewReader(tar1), "") if err != nil { t.Fatal(err) } - layer2, err := ls.Register(bytes.NewReader(tar2), "", OS(runtime.GOOS)) + layer2, err := ls.Register(bytes.NewReader(tar2), "") if err != nil { t.Fatal(err) } diff --git a/components/engine/layer/layer_unix_test.go b/components/engine/layer/layer_unix_test.go index 9aa1afd597..c11f3becea 100644 --- a/components/engine/layer/layer_unix_test.go +++ b/components/engine/layer/layer_unix_test.go @@ -2,7 +2,9 @@ package layer -import "testing" +import ( + "testing" +) func graphDiffSize(ls Store, l Layer) (int64, error) { cl := getCachedLayer(l) diff --git a/components/engine/layer/layer_windows.go b/components/engine/layer/layer_windows.go index d02d4d0dda..c379c1b86e 100644 --- a/components/engine/layer/layer_windows.go +++ b/components/engine/layer/layer_windows.go @@ -28,7 +28,6 @@ func GetLayerPath(s Store, layer ChainID) (string, error) { if layerGetter, ok := ls.driver.(Getter); ok { return layerGetter.GetLayerPath(rl.cacheID) } - path, err := ls.driver.Get(rl.cacheID, "") if err != nil { return "", err diff --git a/components/engine/layer/migration.go b/components/engine/layer/migration.go index 9c0c2c94ed..21b43b73ee 100644 --- a/components/engine/layer/migration.go +++ b/components/engine/layer/migration.go @@ -16,7 +16,7 @@ import ( // CreateRWLayerByGraphID creates a RWLayer in the layer store using // the provided name with the given graphID. To get the RWLayer // after migration the layer may be retrieved by the given name. -func (ls *layerStore) CreateRWLayerByGraphID(name string, graphID string, parent ChainID) (err error) { +func (ls *layerStore) CreateRWLayerByGraphID(name, graphID string, parent ChainID) (err error) { ls.mountL.Lock() defer ls.mountL.Unlock() m, ok := ls.mounts[name] diff --git a/components/engine/layer/migration_test.go b/components/engine/layer/migration_test.go index cc63a014db..e2acd19c2c 100644 --- a/components/engine/layer/migration_test.go +++ b/components/engine/layer/migration_test.go @@ -110,14 +110,14 @@ func TestLayerMigration(t *testing.T) { t.Fatal(err) } - layer1b, err := ls.Register(bytes.NewReader(tar1), "", OS(runtime.GOOS)) + layer1b, err := ls.Register(bytes.NewReader(tar1), "") if err != nil { t.Fatal(err) } assertReferences(t, layer1a, layer1b) // Attempt register, should be same - layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID(), OS(runtime.GOOS)) + layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID()) if err != nil { t.Fatal(err) } @@ -238,7 +238,7 @@ func TestLayerMigrationNoTarsplit(t *testing.T) { t.Fatal(err) } - layer1b, err := ls.Register(bytes.NewReader(tar1), "", OS(runtime.GOOS)) + layer1b, err := ls.Register(bytes.NewReader(tar1), "") if err != nil { t.Fatal(err) } @@ -246,7 +246,7 @@ func TestLayerMigrationNoTarsplit(t *testing.T) { assertReferences(t, layer1a, layer1b) // Attempt register, should be same - layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID(), OS(runtime.GOOS)) + layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID()) if err != nil { t.Fatal(err) } diff --git a/components/engine/layer/ro_layer.go b/components/engine/layer/ro_layer.go index ca3e1189f3..29fa85362a 100644 --- a/components/engine/layer/ro_layer.go +++ b/components/engine/layer/ro_layer.go @@ -16,7 +16,6 @@ type roLayer struct { size int64 layerStore *layerStore descriptor distribution.Descriptor - os OS referenceCount int references map[Layer]struct{} @@ -143,7 +142,11 @@ func storeLayer(tx MetadataTransaction, layer *roLayer) error { return err } } - return tx.SetOS(layer.os) + if err := tx.setOS(layer.layerStore.os); err != nil { + return err + } + + return nil } func newVerifiedReadCloser(rc io.ReadCloser, dgst digest.Digest) (io.ReadCloser, error) { diff --git a/components/engine/layer/ro_layer_unix.go b/components/engine/layer/ro_layer_unix.go deleted file mode 100644 index f0c2003f34..0000000000 --- a/components/engine/layer/ro_layer_unix.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build !windows - -package layer - -func (rl *roLayer) OS() OS { - return "" -} diff --git a/components/engine/layer/ro_layer_windows.go b/components/engine/layer/ro_layer_windows.go index 1210f1028f..32bd7182a3 100644 --- a/components/engine/layer/ro_layer_windows.go +++ b/components/engine/layer/ro_layer_windows.go @@ -7,10 +7,3 @@ var _ distribution.Describable = &roLayer{} func (rl *roLayer) Descriptor() distribution.Descriptor { return rl.descriptor } - -func (rl *roLayer) OS() OS { - if rl.os == "" { - return "windows" - } - return rl.os -} diff --git a/components/engine/migrate/v1/migratev1_test.go b/components/engine/migrate/v1/migratev1_test.go index bab176fe3c..28b7df3f3d 100644 --- a/components/engine/migrate/v1/migratev1_test.go +++ b/components/engine/migrate/v1/migratev1_test.go @@ -87,14 +87,15 @@ func TestMigrateContainers(t *testing.T) { t.Fatal(err) } - ls := &mockMounter{} - ifs, err := image.NewFSStoreBackend(filepath.Join(tmpdir, "imagedb")) if err != nil { t.Fatal(err) } - is, err := image.NewImageStore(ifs, runtime.GOOS, ls) + ls := &mockMounter{} + mmMap := make(map[string]image.LayerGetReleaser) + mmMap[runtime.GOOS] = ls + is, err := image.NewImageStore(ifs, mmMap) if err != nil { t.Fatal(err) } @@ -165,19 +166,20 @@ func TestMigrateImages(t *testing.T) { t.Fatal(err) } - ls := &mockRegistrar{} - ifs, err := image.NewFSStoreBackend(filepath.Join(tmpdir, "imagedb")) if err != nil { t.Fatal(err) } - is, err := image.NewImageStore(ifs, runtime.GOOS, ls) + ls := &mockRegistrar{} + mrMap := make(map[string]image.LayerGetReleaser) + mrMap[runtime.GOOS] = ls + is, err := image.NewImageStore(ifs, mrMap) if err != nil { t.Fatal(err) } - ms, err := metadata.NewFSMetadataStore(filepath.Join(tmpdir, "distribution"), runtime.GOOS) + ms, err := metadata.NewFSMetadataStore(filepath.Join(tmpdir, "distribution")) if err != nil { t.Fatal(err) } @@ -430,10 +432,6 @@ func (l *mockLayer) DiffSize() (int64, error) { return 0, nil } -func (l *mockLayer) OS() layer.OS { - return "" -} - func (l *mockLayer) Metadata() (map[string]string, error) { return nil, nil } diff --git a/components/engine/pkg/system/errors.go b/components/engine/pkg/system/errors.go index 288318985e..1b5bc2c037 100644 --- a/components/engine/pkg/system/errors.go +++ b/components/engine/pkg/system/errors.go @@ -7,4 +7,7 @@ import ( var ( // ErrNotSupportedPlatform means the platform is not supported. ErrNotSupportedPlatform = errors.New("platform and architecture is not supported") + + // ErrNotSupportedOperatingSystem means the operating system is not supported. + ErrNotSupportedOperatingSystem = errors.New("operating system is not supported") ) diff --git a/components/engine/pkg/system/init_windows.go b/components/engine/pkg/system/init_windows.go index 75f8f2c061..825e4b587a 100644 --- a/components/engine/pkg/system/init_windows.go +++ b/components/engine/pkg/system/init_windows.go @@ -11,7 +11,7 @@ var lcowSupported = false // 2. Remove the getenv check when image-store is coalesced as shouldn't be needed anymore. func InitLCOW(experimental bool) { v := GetOSVersion() - if experimental && v.Build > 16270 && os.Getenv("LCOW_SUPPORTED") != "" { + if experimental && v.Build > 16278 && os.Getenv("LCOW_SUPPORTED") != "" { lcowSupported = true } } diff --git a/components/engine/pkg/system/lcow.go b/components/engine/pkg/system/lcow.go index b88c11e316..95958b2c84 100644 --- a/components/engine/pkg/system/lcow.go +++ b/components/engine/pkg/system/lcow.go @@ -56,3 +56,14 @@ func ParsePlatform(in string) *specs.Platform { } return p } + +// IsOSSupported determines if an operating system is supported by the host +func IsOSSupported(os string) bool { + if runtime.GOOS == os { + return true + } + if LCOWSupported() && os == "linux" { + return true + } + return false +} diff --git a/components/engine/plugin/backend_linux.go b/components/engine/plugin/backend_linux.go index 46bdfdbb1f..7d71808e34 100644 --- a/components/engine/plugin/backend_linux.go +++ b/components/engine/plugin/backend_linux.go @@ -10,6 +10,7 @@ import ( "os" "path" "path/filepath" + "runtime" "strings" "github.com/docker/distribution/manifest/schema2" @@ -145,7 +146,7 @@ func (s *tempConfigStore) Get(d digest.Digest) ([]byte, error) { return s.config, nil } -func (s *tempConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, layer.OS, error) { +func (s *tempConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, string, error) { return configToRootFS(c) } @@ -440,7 +441,8 @@ func (pm *Manager) Push(ctx context.Context, name string, metaHeader http.Header pm: pm, plugin: p, } - ls := &pluginLayerProvider{ + lss := make(map[string]distribution.PushLayerProvider) + lss[runtime.GOOS] = &pluginLayerProvider{ pm: pm, plugin: p, } @@ -463,7 +465,7 @@ func (pm *Manager) Push(ctx context.Context, name string, metaHeader http.Header RequireSchema2: true, }, ConfigMediaType: schema2.MediaTypePluginConfig, - LayerStore: ls, + LayerStores: lss, UploadManager: uploadManager, } @@ -532,7 +534,7 @@ func (s *pluginConfigStore) Get(d digest.Digest) ([]byte, error) { return ioutil.ReadAll(rwc) } -func (s *pluginConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, layer.OS, error) { +func (s *pluginConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, string, error) { return configToRootFS(c) } diff --git a/components/engine/plugin/blobstore.go b/components/engine/plugin/blobstore.go index 1f358e6c44..1578311821 100644 --- a/components/engine/plugin/blobstore.go +++ b/components/engine/plugin/blobstore.go @@ -126,8 +126,7 @@ type downloadManager struct { configDigest digest.Digest } -func (dm *downloadManager) Download(ctx context.Context, initialRootFS image.RootFS, os layer.OS, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) { - // TODO @jhowardmsft LCOW: May need revisiting. +func (dm *downloadManager) Download(ctx context.Context, initialRootFS image.RootFS, os string, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) { for _, l := range layers { b, err := dm.blobStore.New() if err != nil { @@ -179,6 +178,6 @@ func (dm *downloadManager) Put(dt []byte) (digest.Digest, error) { func (dm *downloadManager) Get(d digest.Digest) ([]byte, error) { return nil, fmt.Errorf("digest not found") } -func (dm *downloadManager) RootFSAndOSFromConfig(c []byte) (*image.RootFS, layer.OS, error) { +func (dm *downloadManager) RootFSAndOSFromConfig(c []byte) (*image.RootFS, string, error) { return configToRootFS(c) } diff --git a/components/engine/plugin/manager.go b/components/engine/plugin/manager.go index f144e8208b..d686443c6f 100644 --- a/components/engine/plugin/manager.go +++ b/components/engine/plugin/manager.go @@ -375,12 +375,9 @@ func isEqualPrivilege(a, b types.PluginPrivilege) bool { return reflect.DeepEqual(a.Value, b.Value) } -func configToRootFS(c []byte) (*image.RootFS, layer.OS, error) { - // TODO @jhowardmsft LCOW - Will need to revisit this. For now, calculate the operating system. - os := layer.OS(runtime.GOOS) - if system.LCOWSupported() { - os = "linux" - } +func configToRootFS(c []byte) (*image.RootFS, string, error) { + // TODO @jhowardmsft LCOW - Will need to revisit this. + os := runtime.GOOS var pluginConfig types.PluginConfig if err := json.Unmarshal(c, &pluginConfig); err != nil { return nil, "", err