diff --git a/api/server/backend/build/backend.go b/api/server/backend/build/backend.go index f93fba93b7..a702d04a40 100644 --- a/api/server/backend/build/backend.go +++ b/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/api/server/backend/build/tag.go b/api/server/backend/build/tag.go index 7bd5dcdeb2..5c3918a3e1 100644 --- a/api/server/backend/build/tag.go +++ b/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/builder/builder.go b/builder/builder.go index 5a3e2cd8cd..41c68316dd 100644 --- a/builder/builder.go +++ b/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. diff --git a/builder/dockerfile/builder.go b/builder/dockerfile/builder.go index 0354b2d3f9..0bcf7e58fc 100644 --- a/builder/dockerfile/builder.go +++ b/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), } @@ -367,14 +367,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 { diff --git a/builder/dockerfile/dispatchers_test.go b/builder/dockerfile/dispatchers_test.go index dc9148bca5..29cecd8fcd 100644 --- a/builder/dockerfile/dispatchers_test.go +++ b/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/builder/dockerfile/imageprobe.go b/builder/dockerfile/imageprobe.go index 239eb9e38f..1a6df69385 100644 --- a/builder/dockerfile/imageprobe.go +++ b/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/builder/dockerfile/internals.go b/builder/dockerfile/internals.go index adce7b9709..ac79860f28 100644 --- a/builder/dockerfile/internals.go +++ b/builder/dockerfile/internals.go @@ -154,7 +154,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") } diff --git a/builder/dockerfile/mockbackend_test.go b/builder/dockerfile/mockbackend_test.go index 986861cbbb..f128253ddc 100644 --- a/builder/dockerfile/mockbackend_test.go +++ b/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 } diff --git a/daemon/build.go b/daemon/build.go index b8de3e76a9..6f7b158bf9 100644 --- a/daemon/build.go +++ b/daemon/build.go @@ -2,7 +2,6 @@ package daemon import ( "io" - "runtime" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" @@ -24,6 +23,7 @@ type releaseableLayer struct { layerStore layer.Store roLayer layer.Layer rwLayer layer.RWLayer + os string } func (rl *releaseableLayer) Mount() (containerfs.ContainerFS, error) { @@ -35,7 +35,7 @@ func (rl *releaseableLayer) Mount() (containerfs.ContainerFS, error) { } mountID := stringid.GenerateRandomID() - rl.rwLayer, err = rl.layerStore.CreateRWLayer(mountID, chainID, nil) + rl.rwLayer, err = rl.layerStore.CreateRWLayer(mountID, chainID, rl.os, nil) if err != nil { return nil, errors.Wrap(err, "failed to create rwlayer") } @@ -67,12 +67,12 @@ 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, os) if err != nil { return nil, err } - // TODO: An optimization would be to handle empty layers before returning - return &releaseableLayer{layerStore: rl.layerStore, roLayer: newLayer}, nil + // TODO: An optimization woudld be to handle empty layers before returning + return &releaseableLayer{layerStore: rl.layerStore, roLayer: newLayer, os: os}, nil } func (rl *releaseableLayer) DiffID() layer.DiffID { @@ -128,9 +128,9 @@ func (rl *releaseableLayer) releaseROLayer() error { return err } -func newReleasableLayerForImage(img *image.Image, layerStore layer.Store) (builder.ReleaseableLayer, error) { +func newReleasableLayerForImage(img *image.Image, layerStore layer.Store, os string) (builder.ReleaseableLayer, error) { if img == nil || img.RootFS.ChainID() == "" { - return &releaseableLayer{layerStore: layerStore}, nil + return &releaseableLayer{layerStore: layerStore, os: os}, nil } // Hold a reference to the image layer so that it can't be removed before // it is released @@ -138,11 +138,11 @@ func newReleasableLayerForImage(img *image.Image, layerStore layer.Store) (build if err != nil { return nil, errors.Wrapf(err, "failed to get layer for image %s", img.ImageID()) } - return &releaseableLayer{layerStore: layerStore, roLayer: roLayer}, nil + return &releaseableLayer{layerStore: layerStore, roLayer: roLayer, os: os}, nil } // 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,7 @@ 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) + layer, err := newReleasableLayerForImage(nil, daemon.layerStore, opts.OS) return nil, layer, err } @@ -183,7 +183,7 @@ 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) + layer, err := newReleasableLayerForImage(image, daemon.layerStore, image.OperatingSystem()) return image, layer, err } } @@ -192,29 +192,26 @@ 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) + layer, err := newReleasableLayerForImage(image, daemon.layerStore, 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/daemon/cache.go b/daemon/cache.go index 5ad68ec0af..ab3a249869 100644 --- a/daemon/cache.go +++ b/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/daemon/commit.go b/daemon/commit.go index b2c969316b..6c61e75d5e 100644 --- a/daemon/commit.go +++ b/daemon/commit.go @@ -180,17 +180,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.layerStore.Register(rwTar, parent.RootFS.ChainID(), container.OS) if err != nil { return "", err } - defer layer.ReleaseAndLog(daemon.stores[container.OS].layerStore, l) + defer layer.ReleaseAndLog(daemon.layerStore, l) containerConfig := c.ContainerConfig if containerConfig == nil { @@ -209,13 +209,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 +234,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) diff --git a/daemon/create.go b/daemon/create.go index 54ce12c239..f5d0778ab9 100644 --- a/daemon/create.go +++ b/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,7 @@ func (daemon *Daemon) setRWLayer(container *container.Container) error { StorageOpt: container.HostConfig.StorageOpt, } - rwLayer, err := daemon.stores[container.OS].layerStore.CreateRWLayer(container.ID, layerID, rwLayerOpts) + rwLayer, err := daemon.layerStore.CreateRWLayer(container.ID, layerID, container.OS, rwLayerOpts) if err != nil { return err } diff --git a/daemon/daemon.go b/daemon/daemon.go index 3378411fd6..70ebb404e8 100644 --- a/daemon/daemon.go +++ b/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 + layerStore layer.Store + 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 @@ -161,9 +157,9 @@ func (daemon *Daemon) restore() error { } // 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.layerStore.GetRWLayer(container.ID) if err != nil { logrus.Errorf("Failed to load container mount %v: %v", id, err) continue @@ -706,11 +702,11 @@ 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) 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 +715,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,55 +746,43 @@ 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, - MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"), - GraphDriver: ds.graphDriver, - GraphDriverOptions: config.GraphOptions, - IDMappings: idMappings, - PluginGetter: d.PluginStore, - ExperimentalEnabled: config.Experimental, - OS: operatingSystem, - }) - 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()) + d.layerStore, err = layer.NewStoreFromOptions(layer.StoreOptions{ + Root: config.Root, + MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"), + GraphDrivers: d.graphDrivers, + GraphDriverOptions: config.GraphOptions, + IDMappings: idMappings, + PluginGetter: d.PluginStore, + ExperimentalEnabled: config.Experimental, + }) + if err != nil { + return nil, err } - // Configure and validate the kernels security support - if err := configureKernelSecuritySupport(config, graphDrivers); err != nil { + // As layerstore may set the driver + for os := range d.graphDrivers { + d.graphDrivers[os] = d.layerStore.DriverName(os) + } + + // 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.layerStore, *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 + } + d.imageStore, err = image.NewImageStore(ifs, d.layerStore) + if err != nil { + return nil, err } // Configure the volumes driver @@ -830,30 +814,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.layerStore, 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 +901,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 +988,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.layerStore.GetMountID(c.ID); err == nil { daemon.cleanupMountsByID(mountid) } logrus.Debugf("container stopped %s", c.ID) @@ -1022,12 +1001,8 @@ 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) - } - } + if err := daemon.layerStore.Cleanup(); err != nil { + logrus.Errorf("Error during layer Store.Cleanup(): %v", err) } // If we are part of a cluster, clean up cluster's stuff @@ -1107,8 +1082,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.layerStore.DriverName(os) } // prepareTempDir prepares and returns the default directory to use diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index 82e721fe61..bf8741096c 100644 --- a/daemon/daemon_unix.go +++ b/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/daemon/daemon_windows.go b/daemon/daemon_windows.go index 8582d4e580..77e12e908c 100644 --- a/daemon/daemon_windows.go +++ b/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/daemon/delete.go b/daemon/delete.go index 5fb9b9a1d9..3ba2ffff87 100644 --- a/daemon/delete.go +++ b/daemon/delete.go @@ -118,7 +118,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.layerStore.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/daemon/disk_usage.go b/daemon/disk_usage.go index 7142ef0781..2add962066 100644 --- a/daemon/disk_usage.go +++ b/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,24 +95,22 @@ 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() - for _, l := range allLayers { - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - size, err := l.DiffSize() - if err == nil { - if _, ok := layerRefs[l.ChainID()]; ok { - allLayersSize += size - } else { - logrus.Warnf("found leaked image layer %v platform %s", l.ChainID(), platform) - } + layerRefs := daemon.getLayerRefs() + allLayers := daemon.layerStore.Map() + for _, l := range allLayers { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + size, err := l.DiffSize() + if err == nil { + if _, ok := layerRefs[l.ChainID()]; ok { + allLayersSize += size } else { - logrus.Warnf("failed to get diff size for layer %v %s", l.ChainID(), platform) + logrus.Warnf("found leaked image layer %v", l.ChainID()) } + } else { + logrus.Warnf("failed to get diff size for layer %v", l.ChainID()) } } } diff --git a/daemon/getsize_unix.go b/daemon/getsize_unix.go index fff90f2756..69e06c4cd3 100644 --- a/daemon/getsize_unix.go +++ b/daemon/getsize_unix.go @@ -15,12 +15,12 @@ func (daemon *Daemon) getSize(containerID string) (int64, int64) { err error ) - rwlayer, err := daemon.stores[runtime.GOOS].layerStore.GetRWLayer(containerID) + rwlayer, err := daemon.layerStore.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.layerStore.ReleaseRWLayer(rwlayer) sizeRw, err = rwlayer.Size() if err != nil { diff --git a/daemon/image.go b/daemon/image.go index fdbd6e2be7..5c4037126a 100644 --- a/daemon/image.go +++ b/daemon/image.go @@ -38,32 +38,31 @@ 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} } + imageOS := img.OperatingSystem() + if imageOS == "" { + imageOS = runtime.GOOS + } + return id, imageOS, nil } return "", "", errImageDoesNotExist{ref} @@ -71,9 +70,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/daemon/image_delete.go b/daemon/image_delete.go index 36ac2c7c68..3f4d35a87f 100644 --- a/daemon/image_delete.go +++ b/daemon/image_delete.go @@ -66,7 +66,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I start := time.Now() records := []types.ImageDeleteResponseItem{} - imgID, os, err := daemon.GetImageIDAndOS(imageRef) + imgID, _, err := daemon.GetImageIDAndOS(imageRef) if err != nil { return nil, err } @@ -95,7 +95,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 +123,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 +153,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 +171,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 +232,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 +248,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 +299,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 +318,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 +349,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 +358,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 +411,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/daemon/image_exporter.go b/daemon/image_exporter.go index ce9aa7a050..95d1d3dcdb 100644 --- a/daemon/image_exporter.go +++ b/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.layerStore, 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.layerStore, daemon.referenceStore, daemon) return imageExporter.Load(inTar, outStream, quiet) } diff --git a/daemon/image_history.go b/daemon/image_history.go index e7dd85cbab..b763c86c03 100644 --- a/daemon/image_history.go +++ b/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.layerStore.Get(rootFS.ChainID()) if err != nil { return nil, err } layerSize, err = l.DiffSize() - layer.ReleaseAndLog(daemon.stores[platform].layerStore, l) + layer.ReleaseAndLog(daemon.layerStore, l) if err != nil { return nil, err } diff --git a/daemon/image_inspect.go b/daemon/image_inspect.go index fefb93c6e1..47213fff03 100644 --- a/daemon/image_inspect.go +++ b/daemon/image_inspect.go @@ -19,9 +19,9 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) { } // If the image OS isn't set, assume it's the host OS - platform := img.OS - if platform == "" { - platform = runtime.GOOS + os := img.OS + if os == "" { + os = runtime.GOOS } refs := daemon.referenceStore.References(img.ID().Digest()) @@ -40,11 +40,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.layerStore.Get(layerID) if err != nil { return nil, err } - defer layer.ReleaseAndLog(daemon.stores[platform].layerStore, l) + defer layer.ReleaseAndLog(daemon.layerStore, l) size, err = l.Size() if err != nil { return nil, err @@ -61,7 +61,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 +79,7 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) { Author: img.Author, Config: img.Config, Architecture: img.Architecture, - Os: platform, + Os: os, OsVersion: img.OSVersion, Size: size, VirtualSize: size, // TODO: field unused, deprecate @@ -89,7 +89,7 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) { }, } - imageInspect.GraphDriver.Name = daemon.GraphDriverName(platform) + imageInspect.GraphDriver.Name = daemon.GraphDriverName(os) imageInspect.GraphDriver.Data = layerMetadata return imageInspect, nil diff --git a/daemon/image_pull.go b/daemon/image_pull.go index 4944721f9f..318e3582d3 100644 --- a/daemon/image_pull.go +++ b/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/daemon/image_push.go b/daemon/image_push.go index b558073379..0f060d117f 100644 --- a/daemon/image_push.go +++ b/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), + LayerStore: distribution.NewLayerProviderFromStore(daemon.layerStore), TrustKey: daemon.trustKey, UploadManager: daemon.uploadManager, } diff --git a/daemon/image_tag.go b/daemon/image_tag.go index cfac6d6ac9..a955f7d970 100644 --- a/daemon/image_tag.go +++ b/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/daemon/images.go b/daemon/images.go index 7cd2c628f6..aad62ea144 100644 --- a/daemon/images.go +++ b/daemon/images.go @@ -3,7 +3,6 @@ package daemon import ( "encoding/json" "fmt" - "runtime" "sort" "time" @@ -15,7 +14,6 @@ import ( "github.com/docker/docker/container" "github.com/docker/docker/image" "github.com/docker/docker/layer" - "github.com/docker/docker/pkg/system" ) var acceptedImageFilterTags = map[string]bool{ @@ -36,12 +34,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 +43,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 +61,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 @@ -130,7 +116,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs layerID := img.RootFS.ChainID() var size int64 if layerID != "" { - l, err := daemon.stores[platform].layerStore.Get(layerID) + l, err := daemon.layerStore.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 +127,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.layerStore, l) if err != nil { return nil, err } @@ -171,7 +157,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 +179,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.layerStore.Map() imagesMap = make(map[*image.Image]*types.ImageSummary) layerRefs = make(map[layer.ChainID]int) } @@ -261,19 +247,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 +264,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.layerStore.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.layerStore.Release(l) ts, err := l.TarStreamFrom(parentChainID) if err != nil { @@ -295,11 +276,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.layerStore.Register(ts, parentChainID, img.OperatingSystem()) if err != nil { return "", errors.Wrap(err, "error registering layer") } - defer daemon.stores[img.OperatingSystem()].layerStore.Release(newL) + defer daemon.layerStore.Release(newL) newImage := *img newImage.RootFS = nil @@ -334,7 +315,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/daemon/import.go b/daemon/import.go index 3ac83a9fa9..b12c88c23f 100644 --- a/daemon/import.go +++ b/daemon/import.go @@ -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.layerStore.Register(inflatedLayerData, "", os) if err != nil { return err } - defer layer.ReleaseAndLog(daemon.stores[os].layerStore, l) + defer layer.ReleaseAndLog(daemon.layerStore, 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/daemon/info.go b/daemon/info.go index bbb027ed54..e7733330d3 100644 --- a/daemon/info.go +++ b/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.layerStore.DriverStatus(os)...) + 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/daemon/list.go b/daemon/list.go index 8598d25280..626de7767a 100644 --- a/daemon/list.go +++ b/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/daemon/oci_windows.go b/daemon/oci_windows.go index c7c94f327a..3479ce2ead 100644 --- a/daemon/oci_windows.go +++ b/daemon/oci_windows.go @@ -138,9 +138,9 @@ 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()) + layerPath, err := layer.GetLayerPath(daemon.layerStore, 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.layerStore, img.RootFS.ChainID(), err) } // Reverse order, expecting parent most first s.Windows.LayerFolders = append([]string{layerPath}, s.Windows.LayerFolders...) @@ -210,15 +210,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/daemon/prune.go b/daemon/prune.go index 1850752cf5..79620b164d 100644 --- a/daemon/prune.go +++ b/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,7 @@ 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 := daemon.layerStore.Map() topImages := map[image.ID]*image.Image{} for id, img := range allImages { select { @@ -221,7 +213,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/daemon/start.go b/daemon/start.go index 008fb050d4..a0e526cfc0 100644 --- a/daemon/start.go +++ b/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.layerStore.GetMountID(container.ID); err == nil { daemon.cleanupMountsByID(mountid) } } diff --git a/distribution/config.go b/distribution/config.go index 16d729c0ec..483e9e10e7 100644 --- a/distribution/config.go +++ b/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. @@ -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,11 +154,11 @@ 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 + return unmarshalledConfig.RootFS, os, nil } type storeLayerProvider struct { diff --git a/distribution/metadata/metadata.go b/distribution/metadata/metadata.go index 3dae79555d..05ba4f817d 100644 --- a/distribution/metadata/metadata.go +++ b/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/distribution/metadata/v1_id_service_test.go b/distribution/metadata/v1_id_service_test.go index 957ed5d63a..337278613e 100644 --- a/distribution/metadata/v1_id_service_test.go +++ b/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/distribution/metadata/v2_metadata_service_test.go b/distribution/metadata/v2_metadata_service_test.go index b5d59b2297..8e3e4614c0 100644 --- a/distribution/metadata/v2_metadata_service_test.go +++ b/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/distribution/pull.go b/distribution/pull.go index 446197cf2b..008ccfb5e8 100644 --- a/distribution/pull.go +++ b/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/distribution/pull_v1.go b/distribution/pull_v1.go index 6f1c2eee0e..17e9a7cce2 100644 --- a/distribution/pull_v1.go +++ b/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/distribution/pull_v2.go b/distribution/pull_v2.go index 35ff529b4f..0b3a3b0cd8 100644 --- a/distribution/pull_v2.go +++ b/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/distribution/xfer/download.go b/distribution/xfer/download.go index 043119e320..c308aac599 100644 --- a/distribution/xfer/download.go +++ b/distribution/xfer/download.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "io" - "runtime" "time" "github.com/docker/distribution" @@ -23,7 +22,7 @@ const maxDownloadAttempts = 5 // registers and downloads those, taking into account dependencies between // layers. type LayerDownloadManager struct { - layerStores map[string]layer.Store + layerStore layer.Store tm TransferManager waitDuration time.Duration } @@ -34,9 +33,9 @@ func (ldm *LayerDownloadManager) SetConcurrency(concurrency int) { } // NewLayerDownloadManager returns a new LayerDownloadManager. -func NewLayerDownloadManager(layerStores map[string]layer.Store, concurrencyLimit int, options ...func(*LayerDownloadManager)) *LayerDownloadManager { +func NewLayerDownloadManager(layerStore layer.Store, concurrencyLimit int, options ...func(*LayerDownloadManager)) *LayerDownloadManager { manager := LayerDownloadManager{ - layerStores: layerStores, + layerStore: layerStore, tm: NewTransferManager(concurrencyLimit), waitDuration: time.Second, } @@ -95,7 +94,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,11 +104,6 @@ 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 - if os == "" { - os = layer.OS(runtime.GOOS) - } - rootFS := initialRootFS for _, descriptor := range layers { key := descriptor.Key() @@ -121,20 +115,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.layerStore.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.layerStore, 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 +165,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.layerStore, topLayer) } }, nil } @@ -182,7 +176,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.layerStore, topLayer) } }() @@ -218,11 +212,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.layerStore, } go func() { @@ -382,11 +376,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.layerStore, } go func() { diff --git a/distribution/xfer/download_test.go b/distribution/xfer/download_test.go index 8af27f6e71..280d5b6b9e 100644 --- a/distribution/xfer/download_test.go +++ b/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,7 +57,7 @@ func (ml *mockLayer) DiffSize() (size int64, err error) { return 0, nil } -func (ml *mockLayer) OS() layer.OS { +func (ml *mockLayer) OS() string { return ml.os } @@ -91,7 +91,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, os string) (layer.Layer, error) { return ls.RegisterWithDescriptor(reader, parentID, distribution.Descriptor{}) } @@ -131,7 +131,7 @@ func (ls *mockLayerStore) Get(chainID layer.ChainID) (layer.Layer, error) { func (ls *mockLayerStore) Release(l layer.Layer) ([]layer.Metadata, error) { return []layer.Metadata{}, nil } -func (ls *mockLayerStore) CreateRWLayer(string, layer.ChainID, *layer.CreateRWLayerOpts) (layer.RWLayer, error) { +func (ls *mockLayerStore) CreateRWLayer(string, layer.ChainID, string, *layer.CreateRWLayerOpts) (layer.RWLayer, error) { return nil, errors.New("not implemented") } @@ -150,11 +150,11 @@ func (ls *mockLayerStore) Cleanup() error { return nil } -func (ls *mockLayerStore) DriverStatus() [][2]string { +func (ls *mockLayerStore) DriverStatus(string) [][2]string { return [][2]string{} } -func (ls *mockLayerStore) DriverName() string { +func (ls *mockLayerStore) DriverName(string) string { return "mock" } @@ -272,9 +272,7 @@ func TestSuccessfulDownload(t *testing.T) { } layerStore := &mockLayerStore{make(map[layer.ChainID]*mockLayer)} - lsMap := make(map[string]layer.Store) - lsMap[runtime.GOOS] = layerStore - ldm := NewLayerDownloadManager(lsMap, maxDownloadConcurrency, func(m *LayerDownloadManager) { m.waitDuration = time.Millisecond }) + ldm := NewLayerDownloadManager(layerStore, maxDownloadConcurrency, func(m *LayerDownloadManager) { m.waitDuration = time.Millisecond }) progressChan := make(chan progress.Progress) progressDone := make(chan struct{}) @@ -293,13 +291,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(), "", runtime.GOOS) 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) } @@ -336,9 +334,7 @@ func TestSuccessfulDownload(t *testing.T) { func TestCancelledDownload(t *testing.T) { layerStore := &mockLayerStore{make(map[layer.ChainID]*mockLayer)} - lsMap := make(map[string]layer.Store) - lsMap[runtime.GOOS] = layerStore - ldm := NewLayerDownloadManager(lsMap, maxDownloadConcurrency, func(m *LayerDownloadManager) { m.waitDuration = time.Millisecond }) + ldm := NewLayerDownloadManager(layerStore, maxDownloadConcurrency, func(m *LayerDownloadManager) { m.waitDuration = time.Millisecond }) progressChan := make(chan progress.Progress) progressDone := make(chan struct{}) @@ -357,7 +353,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/image/store.go b/image/store.go index 04842857a1..07e9244972 100644 --- a/image/store.go +++ b/image/store.go @@ -3,13 +3,11 @@ package image import ( "encoding/json" "fmt" - "strings" "sync" "time" "github.com/docker/distribution/digestset" "github.com/docker/docker/layer" - "github.com/docker/docker/pkg/system" "github.com/opencontainers/go-digest" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -47,17 +45,15 @@ type store struct { 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) { +func NewImageStore(fs StoreBackend, ls LayerGetReleaser) (Store, error) { is := &store{ ls: ls, images: make(map[ID]*imageMeta), fs: fs, digestSet: digestset.NewSet(), - os: os, } // load all current images and retain layers @@ -118,14 +114,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{}) diff --git a/image/store_test.go b/image/store_test.go index 23a60a98fa..3ba4cdef47 100644 --- a/image/store_test.go +++ b/image/store_test.go @@ -1,7 +1,6 @@ package image import ( - "runtime" "testing" "github.com/docker/docker/internal/testutil" @@ -26,7 +25,7 @@ func TestRestore(t *testing.T) { err = fs.SetMetadata(id2, "parent", []byte(id1)) assert.NoError(t, err) - is, err := NewImageStore(fs, runtime.GOOS, &mockLayerGetReleaser{}) + is, err := NewImageStore(fs, &mockLayerGetReleaser{}) assert.NoError(t, err) assert.Len(t, is.Map(), 2) @@ -143,7 +142,7 @@ func TestParentReset(t *testing.T) { func defaultImageStore(t *testing.T) (Store, func()) { fsBackend, cleanup := defaultFSStoreBackend(t) - store, err := NewImageStore(fsBackend, runtime.GOOS, &mockLayerGetReleaser{}) + store, err := NewImageStore(fsBackend, &mockLayerGetReleaser{}) assert.NoError(t, err) return store, cleanup diff --git a/image/tarexport/load.go b/image/tarexport/load.go index 81beaf4a71..d2b57bcc1a 100644 --- a/image/tarexport/load.go +++ b/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) } @@ -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) @@ -409,19 +409,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/layer/empty.go b/layer/empty.go index 7924a56e3a..90cb031457 100644 --- a/layer/empty.go +++ b/layer/empty.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "io/ioutil" + "runtime" ) // DigestSHA256EmptyTar is the canonical sha256 digest of empty tar file - @@ -55,8 +56,8 @@ func (el *emptyLayer) Metadata() (map[string]string, error) { return make(map[string]string), nil } -func (el *emptyLayer) OS() OS { - return "" +func (el *emptyLayer) OS() string { + return runtime.GOOS } // IsEmpty returns true if the layer is an EmptyLayer diff --git a/layer/filestore_unix.go b/layer/filestore_unix.go index 35e282ee45..bd0200849d 100644 --- a/layer/filestore_unix.go +++ b/layer/filestore_unix.go @@ -2,12 +2,14 @@ package layer +import "runtime" + // SetOS writes the "os" file to the layer filestore -func (fm *fileMetadataTransaction) SetOS(os OS) error { +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 +func (fms *fileMetadataStore) GetOS(layer ChainID) (string, error) { + return runtime.GOOS, nil } diff --git a/layer/filestore_windows.go b/layer/filestore_windows.go index 25daa2104c..68abaef78a 100644 --- a/layer/filestore_windows.go +++ b/layer/filestore_windows.go @@ -8,7 +8,7 @@ import ( ) // SetOS writes the "os" file to the layer filestore -func (fm *fileMetadataTransaction) SetOS(os OS) error { +func (fm *fileMetadataTransaction) SetOS(os string) error { if os == "" { return nil } @@ -16,7 +16,7 @@ func (fm *fileMetadataTransaction) SetOS(os OS) error { } // GetOS reads the "os" file from the layer filestore -func (fms *fileMetadataStore) GetOS(layer ChainID) (OS, error) { +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/layer/layer.go b/layer/layer.go index 08fc789d99..cfbc7ac83a 100644 --- a/layer/layer.go +++ b/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 @@ -109,7 +101,7 @@ type Layer interface { Parent() Layer // OS returns the operating system of the layer - OS() OS + OS() string // Size returns the size of the entire layer chain. The size // is calculated from the total size of all files in the layers. @@ -156,6 +148,9 @@ type RWLayer interface { // Metadata returns the low level metadata for the mutable layer Metadata() (map[string]string, error) + + // OS returns the operating system of the writable layer + OS() string } // Metadata holds information about a @@ -191,25 +186,25 @@ 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, string) (Layer, error) Get(ChainID) (Layer, error) Map() map[ChainID]Layer Release(Layer) ([]Metadata, error) - CreateRWLayer(id string, parent ChainID, opts *CreateRWLayerOpts) (RWLayer, error) + CreateRWLayer(id string, parent ChainID, os string, opts *CreateRWLayerOpts) (RWLayer, error) GetRWLayer(id string) (RWLayer, error) GetMountID(id string) (string, error) ReleaseRWLayer(RWLayer) ([]Metadata, error) Cleanup() error - DriverStatus() [][2]string - DriverName() string + DriverStatus(os string) [][2]string + DriverName(os string) string } // 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, string, distribution.Descriptor) (Layer, error) } // MetadataTransaction represents functions for setting layer metadata @@ -220,7 +215,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 +236,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/layer/layer_store.go b/layer/layer_store.go index 27abcdeec7..bd09af47a2 100644 --- a/layer/layer_store.go +++ b/layer/layer_store.go @@ -5,7 +5,7 @@ import ( "fmt" "io" "io/ioutil" - "strings" + "runtime" "sync" "github.com/docker/distribution" @@ -28,70 +28,77 @@ import ( const maxLayerDepth = 125 type layerStore struct { - store MetadataStore - driver graphdriver.Driver + store MetadataStore + drivers map[string]graphdriver.Driver + useTarSplit map[string]bool layerMap map[ChainID]*roLayer layerL sync.Mutex mounts map[string]*mountedLayer mountL sync.Mutex - - useTarSplit bool - - os string } // StoreOptions are the options used to create a new Store instance type StoreOptions struct { - StorePath string + Root string + GraphDrivers map[string]string MetadataStorePathTemplate string - GraphDriver string GraphDriverOptions []string IDMappings *idtools.IDMappings PluginGetter plugingetter.PluginGetter ExperimentalEnabled bool - OS string } // 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, - DriverOptions: options.GraphDriverOptions, - UIDMaps: options.IDMappings.UIDs(), - GIDMaps: options.IDMappings.GIDs(), - ExperimentalEnabled: options.ExperimentalEnabled, - }) - if err != nil { - return nil, fmt.Errorf("error initializing graphdriver: %v", err) + drivers := make(map[string]graphdriver.Driver) + for os, drivername := range options.GraphDrivers { + var err error + drivers[os], err = graphdriver.New(drivername, + options.PluginGetter, + graphdriver.Options{ + Root: options.Root, + DriverOptions: options.GraphDriverOptions, + UIDMaps: options.IDMappings.UIDs(), + GIDMaps: options.IDMappings.GIDs(), + ExperimentalEnabled: options.ExperimentalEnabled, + }) + if err != nil { + return nil, fmt.Errorf("error initializing graphdriver: %v", err) + } + logrus.Debugf("Initialized graph driver %s", drivername) } - logrus.Debugf("Using graph driver %s", driver) - fms, err := NewFSMetadataStore(fmt.Sprintf(options.MetadataStorePathTemplate, driver)) + fms, err := NewFSMetadataStore(fmt.Sprintf(options.MetadataStorePathTemplate, options.GraphDrivers[runtime.GOOS])) if err != nil { return nil, err } - return NewStoreFromGraphDriver(fms, driver, options.OS) + return NewStoreFromGraphDrivers(fms, drivers) } -// NewStoreFromGraphDriver creates a new Store instance using the provided -// metadata store and graph driver. The metadata store will be used to restore +// NewStoreFromGraphDrivers creates a new Store instance using the provided +// metadata store and graph drivers. The metadata store will be used to restore // the Store. -func NewStoreFromGraphDriver(store MetadataStore, driver graphdriver.Driver, os string) (Store, error) { - caps := graphdriver.Capabilities{} - if capDriver, ok := driver.(graphdriver.CapabilityDriver); ok { - caps = capDriver.Capabilities() +func NewStoreFromGraphDrivers(store MetadataStore, drivers map[string]graphdriver.Driver) (Store, error) { + + useTarSplit := make(map[string]bool) + for os, driver := range drivers { + + caps := graphdriver.Capabilities{} + if capDriver, ok := driver.(graphdriver.CapabilityDriver); ok { + caps = capDriver.Capabilities() + } + useTarSplit[os] = !caps.ReproducesExactDiffs } ls := &layerStore{ store: store, - driver: driver, + drivers: drivers, layerMap: map[ChainID]*roLayer{}, mounts: map[string]*mountedLayer{}, - useTarSplit: !caps.ReproducesExactDiffs, - os: os, + useTarSplit: useTarSplit, } ids, mounts, err := store.List() @@ -227,7 +234,7 @@ func (ls *layerStore) applyTar(tx MetadataTransaction, ts io.Reader, parent stri tr := io.TeeReader(ts, digester.Hash()) rdr := tr - if ls.useTarSplit { + if ls.useTarSplit[layer.os] { tsw, err := tx.TarSplitWriter(true) if err != nil { return err @@ -243,7 +250,7 @@ func (ls *layerStore) applyTar(tx MetadataTransaction, ts io.Reader, parent stri } } - applySize, err := ls.driver.ApplyDiff(layer.cacheID, parent, rdr) + applySize, err := ls.drivers[layer.os].ApplyDiff(layer.cacheID, parent, rdr) if err != nil { return err } @@ -259,11 +266,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) { +func (ls *layerStore) Register(ts io.Reader, parent ChainID, os string) (Layer, error) { return ls.registerWithDescriptor(ts, parent, os, 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, os string, 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 +278,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 { @@ -298,6 +298,14 @@ func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, os OS } } + // Validate the operating system is valid + if os == "" { + os = runtime.GOOS + } + if err := system.ValidatePlatform(system.ParsePlatform(os)); err != nil { + return nil, err + } + // Create new roLayer layer := &roLayer{ parent: p, @@ -309,7 +317,7 @@ func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, os OS os: os, } - if err = ls.driver.Create(layer.cacheID, pid, nil); err != nil { + if err = ls.drivers[os].Create(layer.cacheID, pid, nil); err != nil { return nil, err } @@ -321,7 +329,7 @@ func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, os OS defer func() { if err != nil { logrus.Debugf("Cleaning up layer %s: %v", layer.cacheID, err) - if err := ls.driver.Remove(layer.cacheID); err != nil { + if err := ls.drivers[os].Remove(layer.cacheID); err != nil { logrus.Errorf("Error cleaning up cache layer %s: %v", layer.cacheID, err) } if err := tx.Cancel(); err != nil { @@ -405,7 +413,7 @@ func (ls *layerStore) Map() map[ChainID]Layer { } func (ls *layerStore) deleteLayer(layer *roLayer, metadata *Metadata) error { - err := ls.driver.Remove(layer.cacheID) + err := ls.drivers[layer.os].Remove(layer.cacheID) if err != nil { return err } @@ -475,7 +483,7 @@ func (ls *layerStore) Release(l Layer) ([]Metadata, error) { return ls.releaseLayer(layer) } -func (ls *layerStore) CreateRWLayer(name string, parent ChainID, opts *CreateRWLayerOpts) (RWLayer, error) { +func (ls *layerStore) CreateRWLayer(name string, parent ChainID, os string, opts *CreateRWLayerOpts) (RWLayer, error) { var ( storageOpt map[string]string initFunc MountInit @@ -515,16 +523,21 @@ func (ls *layerStore) CreateRWLayer(name string, parent ChainID, opts *CreateRWL }() } + // Ensure the operating system is set to the host OS if not populated. + if os == "" { + os = runtime.GOOS + } m = &mountedLayer{ name: name, parent: p, mountID: ls.mountID(name), layerStore: ls, references: map[RWLayer]*referencedRWLayer{}, + os: os, } if initFunc != nil { - pid, err = ls.initMount(m.mountID, pid, mountLabel, initFunc, storageOpt) + pid, err = ls.initMount(m.mountID, m.os, pid, mountLabel, initFunc, storageOpt) if err != nil { return nil, err } @@ -535,7 +548,7 @@ func (ls *layerStore) CreateRWLayer(name string, parent ChainID, opts *CreateRWL StorageOpt: storageOpt, } - if err = ls.driver.CreateReadWrite(m.mountID, pid, createOpts); err != nil { + if err = ls.drivers[os].CreateReadWrite(m.mountID, pid, createOpts); err != nil { return nil, err } if err = ls.saveMount(m); err != nil { @@ -584,14 +597,14 @@ func (ls *layerStore) ReleaseRWLayer(l RWLayer) ([]Metadata, error) { return []Metadata{}, nil } - if err := ls.driver.Remove(m.mountID); err != nil { + if err := ls.drivers[l.OS()].Remove(m.mountID); err != nil { logrus.Errorf("Error removing mounted layer %s: %s", m.name, err) m.retakeReference(l) return nil, err } if m.initID != "" { - if err := ls.driver.Remove(m.initID); err != nil { + if err := ls.drivers[l.OS()].Remove(m.initID); err != nil { logrus.Errorf("Error removing init layer %s: %s", m.name, err) m.retakeReference(l) return nil, err @@ -637,7 +650,7 @@ func (ls *layerStore) saveMount(mount *mountedLayer) error { return nil } -func (ls *layerStore) initMount(graphID, parent, mountLabel string, initFunc MountInit, storageOpt map[string]string) (string, error) { +func (ls *layerStore) initMount(graphID, os, parent, mountLabel string, initFunc MountInit, storageOpt map[string]string) (string, error) { // Use "-init" to maintain compatibility with graph drivers // which are expecting this layer with this special name. If all // graph drivers can be updated to not rely on knowing about this layer @@ -649,20 +662,20 @@ func (ls *layerStore) initMount(graphID, parent, mountLabel string, initFunc Mou StorageOpt: storageOpt, } - if err := ls.driver.CreateReadWrite(initID, parent, createOpts); err != nil { + if err := ls.drivers[os].CreateReadWrite(initID, parent, createOpts); err != nil { return "", err } - p, err := ls.driver.Get(initID, "") + p, err := ls.drivers[os].Get(initID, "") if err != nil { return "", err } if err := initFunc(p); err != nil { - ls.driver.Put(initID) + ls.drivers[os].Put(initID) return "", err } - if err := ls.driver.Put(initID); err != nil { + if err := ls.drivers[os].Put(initID); err != nil { return "", err } @@ -670,13 +683,13 @@ func (ls *layerStore) initMount(graphID, parent, mountLabel string, initFunc Mou } func (ls *layerStore) getTarStream(rl *roLayer) (io.ReadCloser, error) { - if !ls.useTarSplit { + if !ls.useTarSplit[rl.os] { var parentCacheID string if rl.parent != nil { parentCacheID = rl.parent.cacheID } - return ls.driver.Diff(rl.cacheID, parentCacheID) + return ls.drivers[rl.os].Diff(rl.cacheID, parentCacheID) } r, err := ls.store.TarSplitReader(rl.chainID) @@ -686,7 +699,7 @@ func (ls *layerStore) getTarStream(rl *roLayer) (io.ReadCloser, error) { pr, pw := io.Pipe() go func() { - err := ls.assembleTarTo(rl.cacheID, r, nil, pw) + err := ls.assembleTarTo(rl.cacheID, rl.os, r, nil, pw) if err != nil { pw.CloseWithError(err) } else { @@ -697,10 +710,10 @@ func (ls *layerStore) getTarStream(rl *roLayer) (io.ReadCloser, error) { return pr, nil } -func (ls *layerStore) assembleTarTo(graphID string, metadata io.ReadCloser, size *int64, w io.Writer) error { - diffDriver, ok := ls.driver.(graphdriver.DiffGetterDriver) +func (ls *layerStore) assembleTarTo(graphID, os string, metadata io.ReadCloser, size *int64, w io.Writer) error { + diffDriver, ok := ls.drivers[os].(graphdriver.DiffGetterDriver) if !ok { - diffDriver = &naiveDiffPathDriver{ls.driver} + diffDriver = &naiveDiffPathDriver{ls.drivers[os]} } defer metadata.Close() @@ -719,15 +732,27 @@ func (ls *layerStore) assembleTarTo(graphID string, metadata io.ReadCloser, size } func (ls *layerStore) Cleanup() error { - return ls.driver.Cleanup() + var err error + for _, driver := range ls.drivers { + if e := driver.Cleanup(); e != nil { + err = fmt.Errorf("%s - %s", err.Error(), e.Error()) + } + } + return err } -func (ls *layerStore) DriverStatus() [][2]string { - return ls.driver.Status() +func (ls *layerStore) DriverStatus(os string) [][2]string { + if os == "" { + os = runtime.GOOS + } + return ls.drivers[os].Status() } -func (ls *layerStore) DriverName() string { - return ls.driver.String() +func (ls *layerStore) DriverName(os string) string { + if os == "" { + os = runtime.GOOS + } + return ls.drivers[os].String() } type naiveDiffPathDriver struct { diff --git a/layer/layer_store_windows.go b/layer/layer_store_windows.go index 8e93119fd4..ceee16e26f 100644 --- a/layer/layer_store_windows.go +++ b/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) { +func (ls *layerStore) RegisterWithDescriptor(ts io.Reader, parent ChainID, os string, descriptor distribution.Descriptor) (Layer, error) { return ls.registerWithDescriptor(ts, parent, os, descriptor) } diff --git a/layer/layer_test.go b/layer/layer_test.go index f682035ce1..6f6d2cd3f4 100644 --- a/layer/layer_test.go +++ b/layer/layer_test.go @@ -73,7 +73,9 @@ func newTestStore(t *testing.T) (Store, string, func()) { if err != nil { t.Fatal(err) } - ls, err := NewStoreFromGraphDriver(fms, graph, runtime.GOOS) + graphs := make(map[string]graphdriver.Driver) + graphs[runtime.GOOS] = graph + ls, err := NewStoreFromGraphDrivers(fms, graphs) if err != nil { t.Fatal(err) } @@ -88,7 +90,7 @@ type layerInit func(root containerfs.ContainerFS) error func createLayer(ls Store, parent ChainID, layerFunc layerInit) (Layer, error) { containerID := stringid.GenerateRandomID() - mount, err := ls.CreateRWLayer(containerID, parent, nil) + mount, err := ls.CreateRWLayer(containerID, parent, runtime.GOOS, nil) if err != nil { return nil, err } @@ -108,7 +110,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, runtime.GOOS) if err != nil { return nil, err } @@ -277,7 +279,7 @@ func TestMountAndRegister(t *testing.T) { size, _ := layer.Size() t.Logf("Layer size: %d", size) - mount2, err := ls.CreateRWLayer("new-test-mount", layer.ChainID(), nil) + mount2, err := ls.CreateRWLayer("new-test-mount", layer.ChainID(), runtime.GOOS, nil) if err != nil { t.Fatal(err) } @@ -385,7 +387,7 @@ func TestStoreRestore(t *testing.T) { t.Fatal(err) } - m, err := ls.CreateRWLayer("some-mount_name", layer3.ChainID(), nil) + m, err := ls.CreateRWLayer("some-mount_name", layer3.ChainID(), runtime.GOOS, nil) if err != nil { t.Fatal(err) } @@ -403,7 +405,7 @@ func TestStoreRestore(t *testing.T) { t.Fatal(err) } - ls2, err := NewStoreFromGraphDriver(ls.(*layerStore).store, ls.(*layerStore).driver, runtime.GOOS) + ls2, err := NewStoreFromGraphDrivers(ls.(*layerStore).store, ls.(*layerStore).drivers) if err != nil { t.Fatal(err) } @@ -416,7 +418,7 @@ func TestStoreRestore(t *testing.T) { assertLayerEqual(t, layer3b, layer3) // Create again with same name, should return error - if _, err := ls2.CreateRWLayer("some-mount_name", layer3b.ChainID(), nil); err == nil { + if _, err := ls2.CreateRWLayer("some-mount_name", layer3b.ChainID(), runtime.GOOS, nil); err == nil { t.Fatal("Expected error creating mount with same name") } else if err != ErrMountNameConflict { t.Fatal(err) @@ -498,13 +500,13 @@ 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), "", runtime.GOOS) if err != nil { t.Fatal(err) } // hack layer to add file - p, err := ls.(*layerStore).driver.Get(layer1.(*referencedCacheLayer).cacheID, "") + p, err := ls.(*layerStore).drivers[runtime.GOOS].Get(layer1.(*referencedCacheLayer).cacheID, "") if err != nil { t.Fatal(err) } @@ -513,11 +515,11 @@ func TestTarStreamStability(t *testing.T) { t.Fatal(err) } - if err := ls.(*layerStore).driver.Put(layer1.(*referencedCacheLayer).cacheID); err != nil { + if err := ls.(*layerStore).drivers[runtime.GOOS].Put(layer1.(*referencedCacheLayer).cacheID); err != nil { t.Fatal(err) } - layer2, err := ls.Register(bytes.NewReader(tar2), layer1.ChainID(), OS(runtime.GOOS)) + layer2, err := ls.Register(bytes.NewReader(tar2), layer1.ChainID(), runtime.GOOS) if err != nil { t.Fatal(err) } @@ -685,12 +687,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(), runtime.GOOS) 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(), runtime.GOOS) if err != nil { t.Fatal(err) } @@ -725,12 +727,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), "", runtime.GOOS) if err != nil { t.Fatal(err) } - layer2, err := ls.Register(bytes.NewReader(tar2), "", OS(runtime.GOOS)) + layer2, err := ls.Register(bytes.NewReader(tar2), "", runtime.GOOS) if err != nil { t.Fatal(err) } diff --git a/layer/layer_unix_test.go b/layer/layer_unix_test.go index 9aa1afd597..ddfac76150 100644 --- a/layer/layer_unix_test.go +++ b/layer/layer_unix_test.go @@ -2,7 +2,10 @@ package layer -import "testing" +import ( + "runtime" + "testing" +) func graphDiffSize(ls Store, l Layer) (int64, error) { cl := getCachedLayer(l) @@ -10,7 +13,7 @@ func graphDiffSize(ls Store, l Layer) (int64, error) { if cl.parent != nil { parent = cl.parent.cacheID } - return ls.(*layerStore).driver.DiffSize(cl.cacheID, parent) + return ls.(*layerStore).drivers[runtime.GOOS].DiffSize(cl.cacheID, parent) } // Unix as Windows graph driver does not support Changes which is indirectly diff --git a/layer/layer_windows.go b/layer/layer_windows.go index d02d4d0dda..1d621229be 100644 --- a/layer/layer_windows.go +++ b/layer/layer_windows.go @@ -25,16 +25,15 @@ func GetLayerPath(s Store, layer ChainID) (string, error) { return "", ErrLayerDoesNotExist } - if layerGetter, ok := ls.driver.(Getter); ok { + if layerGetter, ok := ls.drivers[rl.os].(Getter); ok { return layerGetter.GetLayerPath(rl.cacheID) } - - path, err := ls.driver.Get(rl.cacheID, "") + path, err := ls.drivers[rl.os].Get(rl.cacheID, "") if err != nil { return "", err } - if err := ls.driver.Put(rl.cacheID); err != nil { + if err := ls.drivers[rl.os].Put(rl.cacheID); err != nil { return "", err } diff --git a/layer/migration.go b/layer/migration.go index 9c0c2c94ed..f42cfa9af0 100644 --- a/layer/migration.go +++ b/layer/migration.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "os" + "runtime" "github.com/opencontainers/go-digest" "github.com/sirupsen/logrus" @@ -16,7 +17,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, os string, parent ChainID) (err error) { ls.mountL.Lock() defer ls.mountL.Unlock() m, ok := ls.mounts[name] @@ -31,7 +32,11 @@ func (ls *layerStore) CreateRWLayerByGraphID(name string, graphID string, parent return nil } - if !ls.driver.Exists(graphID) { + // Ensure the operating system is set to the host OS if not populated. + if os == "" { + os = runtime.GOOS + } + if !ls.drivers[os].Exists(graphID) { return fmt.Errorf("graph ID does not exist: %q", graphID) } @@ -60,11 +65,12 @@ func (ls *layerStore) CreateRWLayerByGraphID(name string, graphID string, parent mountID: graphID, layerStore: ls, references: map[RWLayer]*referencedRWLayer{}, + os: os, } // Check for existing init layer initID := fmt.Sprintf("%s-init", graphID) - if ls.driver.Exists(initID) { + if ls.drivers[os].Exists(initID) { m.initID = initID } @@ -95,7 +101,10 @@ func (ls *layerStore) ChecksumForGraphID(id, parent, oldTarDataPath, newTarDataP } dgst := digest.Canonical.Digester() - err = ls.assembleTarTo(id, uncompressed, &size, dgst.Hash()) + // Note - we use the host OS here. This is a safe assumption as its during migration, and + // no host OS which supports migration also supports multiple image OS's. In other words, + // it's only on Linux, not on Windows. + err = ls.assembleTarTo(id, runtime.GOOS, uncompressed, &size, dgst.Hash()) if err != nil { return } @@ -111,7 +120,10 @@ func (ls *layerStore) ChecksumForGraphID(id, parent, oldTarDataPath, newTarDataP } func (ls *layerStore) checksumForGraphIDNoTarsplit(id, parent, newTarDataPath string) (diffID DiffID, size int64, err error) { - rawarchive, err := ls.driver.Diff(id, parent) + // Note - we use the host OS here. This is a safe assumption as its during migration, and + // no host OS which supports migration also supports multiple image OS's. In other words, + // it's only on Linux, not on Windows. + rawarchive, err := ls.drivers[runtime.GOOS].Diff(id, parent) if err != nil { return } diff --git a/layer/migration_test.go b/layer/migration_test.go index cc63a014db..cc4e14877a 100644 --- a/layer/migration_test.go +++ b/layer/migration_test.go @@ -94,7 +94,9 @@ func TestLayerMigration(t *testing.T) { if err != nil { t.Fatal(err) } - ls, err := NewStoreFromGraphDriver(fms, graph, runtime.GOOS) + graphs := make(map[string]graphdriver.Driver) + graphs[runtime.GOOS] = graph + ls, err := NewStoreFromGraphDrivers(fms, graphs) if err != nil { t.Fatal(err) } @@ -110,14 +112,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), "", runtime.GOOS) 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(), runtime.GOOS) if err != nil { t.Fatal(err) } @@ -222,7 +224,9 @@ func TestLayerMigrationNoTarsplit(t *testing.T) { if err != nil { t.Fatal(err) } - ls, err := NewStoreFromGraphDriver(fms, graph, runtime.GOOS) + graphs := make(map[string]graphdriver.Driver) + graphs[runtime.GOOS] = graph + ls, err := NewStoreFromGraphDrivers(fms, graphs) if err != nil { t.Fatal(err) } @@ -238,7 +242,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), "", runtime.GOOS) if err != nil { t.Fatal(err) } @@ -246,7 +250,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(), runtime.GOOS) if err != nil { t.Fatal(err) } @@ -308,7 +312,7 @@ func TestMountMigration(t *testing.T) { t.Fatal(err) } - graph := ls.(*layerStore).driver + graph := ls.(*layerStore).drivers[runtime.GOOS] layer1, err := createLayer(ls, "", initWithFiles(baseFiles...)) if err != nil { @@ -334,7 +338,7 @@ func TestMountMigration(t *testing.T) { t.Fatal(err) } - if err := ls.(*layerStore).CreateRWLayerByGraphID("migration-mount", containerID, layer1.ChainID()); err != nil { + if err := ls.(*layerStore).CreateRWLayerByGraphID("migration-mount", containerID, runtime.GOOS, layer1.ChainID()); err != nil { t.Fatal(err) } @@ -380,7 +384,7 @@ func TestMountMigration(t *testing.T) { Kind: archive.ChangeAdd, }) - if _, err := ls.CreateRWLayer("migration-mount", layer1.ChainID(), nil); err == nil { + if _, err := ls.CreateRWLayer("migration-mount", layer1.ChainID(), runtime.GOOS, nil); err == nil { t.Fatal("Expected error creating mount with same name") } else if err != ErrMountNameConflict { t.Fatal(err) diff --git a/layer/mount_test.go b/layer/mount_test.go index 44d461f9b8..399a34cbde 100644 --- a/layer/mount_test.go +++ b/layer/mount_test.go @@ -35,7 +35,7 @@ func TestMountInit(t *testing.T) { rwLayerOpts := &CreateRWLayerOpts{ InitFunc: mountInit, } - m, err := ls.CreateRWLayer("fun-mount", layer.ChainID(), rwLayerOpts) + m, err := ls.CreateRWLayer("fun-mount", layer.ChainID(), runtime.GOOS, rwLayerOpts) if err != nil { t.Fatal(err) } @@ -95,7 +95,7 @@ func TestMountSize(t *testing.T) { InitFunc: mountInit, } - m, err := ls.CreateRWLayer("mount-size", layer.ChainID(), rwLayerOpts) + m, err := ls.CreateRWLayer("mount-size", layer.ChainID(), runtime.GOOS, rwLayerOpts) if err != nil { t.Fatal(err) } @@ -147,7 +147,7 @@ func TestMountChanges(t *testing.T) { InitFunc: mountInit, } - m, err := ls.CreateRWLayer("mount-changes", layer.ChainID(), rwLayerOpts) + m, err := ls.CreateRWLayer("mount-changes", layer.ChainID(), runtime.GOOS, rwLayerOpts) if err != nil { t.Fatal(err) } diff --git a/layer/mounted_layer.go b/layer/mounted_layer.go index 47ef966987..80a4bda0a3 100644 --- a/layer/mounted_layer.go +++ b/layer/mounted_layer.go @@ -2,6 +2,7 @@ package layer import ( "io" + "runtime" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/containerfs" @@ -14,6 +15,7 @@ type mountedLayer struct { parent *roLayer path string layerStore *layerStore + os string references map[RWLayer]*referencedRWLayer } @@ -29,7 +31,7 @@ func (ml *mountedLayer) cacheParent() string { } func (ml *mountedLayer) TarStream() (io.ReadCloser, error) { - return ml.layerStore.driver.Diff(ml.mountID, ml.cacheParent()) + return ml.layerStore.drivers[ml.OS()].Diff(ml.mountID, ml.cacheParent()) } func (ml *mountedLayer) Name() string { @@ -46,16 +48,24 @@ func (ml *mountedLayer) Parent() Layer { return nil } +func (ml *mountedLayer) OS() string { + // For backwards compatibility, return the host OS if not set. + if ml.os == "" { + return runtime.GOOS + } + return ml.os +} + func (ml *mountedLayer) Size() (int64, error) { - return ml.layerStore.driver.DiffSize(ml.mountID, ml.cacheParent()) + return ml.layerStore.drivers[ml.OS()].DiffSize(ml.mountID, ml.cacheParent()) } func (ml *mountedLayer) Changes() ([]archive.Change, error) { - return ml.layerStore.driver.Changes(ml.mountID, ml.cacheParent()) + return ml.layerStore.drivers[ml.OS()].Changes(ml.mountID, ml.cacheParent()) } func (ml *mountedLayer) Metadata() (map[string]string, error) { - return ml.layerStore.driver.GetMetadata(ml.mountID) + return ml.layerStore.drivers[ml.OS()].GetMetadata(ml.mountID) } func (ml *mountedLayer) getReference() RWLayer { @@ -90,11 +100,11 @@ type referencedRWLayer struct { } func (rl *referencedRWLayer) Mount(mountLabel string) (containerfs.ContainerFS, error) { - return rl.layerStore.driver.Get(rl.mountedLayer.mountID, mountLabel) + return rl.layerStore.drivers[rl.OS()].Get(rl.mountedLayer.mountID, mountLabel) } // Unmount decrements the activity count and unmounts the underlying layer // Callers should only call `Unmount` once per call to `Mount`, even on error. func (rl *referencedRWLayer) Unmount() error { - return rl.layerStore.driver.Put(rl.mountedLayer.mountID) + return rl.layerStore.drivers[rl.OS()].Put(rl.mountedLayer.mountID) } diff --git a/layer/ro_layer.go b/layer/ro_layer.go index ca3e1189f3..aca061efd4 100644 --- a/layer/ro_layer.go +++ b/layer/ro_layer.go @@ -16,7 +16,7 @@ type roLayer struct { size int64 layerStore *layerStore descriptor distribution.Descriptor - os OS + os string referenceCount int references map[Layer]struct{} @@ -52,7 +52,7 @@ func (rl *roLayer) TarStreamFrom(parent ChainID) (io.ReadCloser, error) { if parent != ChainID("") && parentCacheID == "" { return nil, fmt.Errorf("layer ID '%s' is not a parent of the specified layer: cannot provide diff to non-parent", parent) } - return rl.layerStore.driver.Diff(rl.cacheID, parentCacheID) + return rl.layerStore.drivers[rl.OS()].Diff(rl.cacheID, parentCacheID) } func (rl *roLayer) ChainID() ChainID { @@ -86,7 +86,7 @@ func (rl *roLayer) DiffSize() (size int64, err error) { } func (rl *roLayer) Metadata() (map[string]string, error) { - return rl.layerStore.driver.GetMetadata(rl.cacheID) + return rl.layerStore.drivers[rl.OS()].GetMetadata(rl.cacheID) } type referencedCacheLayer struct { diff --git a/layer/ro_layer_unix.go b/layer/ro_layer_unix.go index f0c2003f34..3f8e928ed2 100644 --- a/layer/ro_layer_unix.go +++ b/layer/ro_layer_unix.go @@ -2,6 +2,8 @@ package layer -func (rl *roLayer) OS() OS { - return "" +import "runtime" + +func (rl *roLayer) OS() string { + return runtime.GOOS } diff --git a/layer/ro_layer_windows.go b/layer/ro_layer_windows.go index 1210f1028f..4351a1fc52 100644 --- a/layer/ro_layer_windows.go +++ b/layer/ro_layer_windows.go @@ -8,7 +8,7 @@ func (rl *roLayer) Descriptor() distribution.Descriptor { return rl.descriptor } -func (rl *roLayer) OS() OS { +func (rl *roLayer) OS() string { if rl.os == "" { return "windows" } diff --git a/migrate/v1/migratev1_test.go b/migrate/v1/migratev1_test.go index bab176fe3c..e3bfb7260c 100644 --- a/migrate/v1/migratev1_test.go +++ b/migrate/v1/migratev1_test.go @@ -94,7 +94,7 @@ func TestMigrateContainers(t *testing.T) { t.Fatal(err) } - is, err := image.NewImageStore(ifs, runtime.GOOS, ls) + is, err := image.NewImageStore(ifs, ls) if err != nil { t.Fatal(err) } @@ -172,12 +172,12 @@ func TestMigrateImages(t *testing.T) { t.Fatal(err) } - is, err := image.NewImageStore(ifs, runtime.GOOS, ls) + is, err := image.NewImageStore(ifs, ls) 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,7 +430,7 @@ func (l *mockLayer) DiffSize() (int64, error) { return 0, nil } -func (l *mockLayer) OS() layer.OS { +func (l *mockLayer) OS() string { return "" } diff --git a/pkg/system/init_windows.go b/pkg/system/init_windows.go index 75f8f2c061..825e4b587a 100644 --- a/pkg/system/init_windows.go +++ b/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/plugin/backend_linux.go b/plugin/backend_linux.go index 46bdfdbb1f..203a0796c0 100644 --- a/plugin/backend_linux.go +++ b/plugin/backend_linux.go @@ -145,7 +145,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) } @@ -532,7 +532,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/plugin/blobstore.go b/plugin/blobstore.go index 1f358e6c44..1578311821 100644 --- a/plugin/blobstore.go +++ b/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/plugin/manager.go b/plugin/manager.go index f144e8208b..d686443c6f 100644 --- a/plugin/manager.go +++ b/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