From 3aa4a0071536d3b106374eaa44d8a55765901aa6 Mon Sep 17 00:00:00 2001 From: John Howard Date: Tue, 16 May 2017 16:56:56 -0700 Subject: [PATCH] LCOW: Move daemon stores to per platform Signed-off-by: John Howard --- api/server/backend/build/backend.go | 2 +- api/server/backend/build/tag.go | 9 +- api/types/types.go | 1 + cmd/dockerd/daemon.go | 6 - daemon/build.go | 7 +- daemon/cache.go | 8 +- daemon/commit.go | 17 +- daemon/container.go | 7 +- daemon/create.go | 4 +- daemon/daemon.go | 238 +++++++++++------- daemon/daemon_solaris.go | 2 +- daemon/daemon_unix.go | 14 +- daemon/daemon_windows.go | 2 +- daemon/delete.go | 4 +- daemon/disk_usage.go | 38 +-- daemon/getsize_unix.go | 8 +- .../graphdriver/register/register_windows.go | 3 +- daemon/graphdriver/windows/windows.go | 4 + daemon/image.go | 44 ++-- daemon/image_delete.go | 50 ++-- daemon/image_exporter.go | 7 +- daemon/image_history.go | 13 +- daemon/image_inspect.go | 18 +- daemon/image_pull.go | 13 +- daemon/image_push.go | 14 +- daemon/image_tag.go | 8 +- daemon/images.go | 58 +++-- daemon/import.go | 12 +- daemon/info.go | 24 +- daemon/inspect.go | 1 + daemon/list.go | 6 +- daemon/prune.go | 18 +- daemon/start.go | 2 +- daemon/start_windows.go | 6 +- distribution/metadata/metadata.go | 4 +- distribution/metadata/v1_id_service_test.go | 3 +- .../metadata/v2_metadata_service_test.go | 3 +- distribution/xfer/download.go | 24 +- reference/store.go | 6 +- reference/store_test.go | 9 +- 40 files changed, 448 insertions(+), 269 deletions(-) diff --git a/api/server/backend/build/backend.go b/api/server/backend/build/backend.go index 00aa7c3126..98051653cf 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, reference.Named) error + TagImageWithReference(image.ID, string, reference.Named) error } // Backend provides build functionality to the API router diff --git a/api/server/backend/build/tag.go b/api/server/backend/build/tag.go index 5c3918a3e1..e2de4c13bb 100644 --- a/api/server/backend/build/tag.go +++ b/api/server/backend/build/tag.go @@ -3,9 +3,11 @@ 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" ) @@ -33,7 +35,12 @@ 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 { - if err := bt.imageComponent.TagImageWithReference(imageID, rt); err != nil { + // TODO @jhowardmsft LCOW support. Will need revisiting. + platform := runtime.GOOS + if platform == "windows" && system.LCOWSupported() { + platform = "linux" + } + if err := bt.imageComponent.TagImageWithReference(imageID, platform, rt); err != nil { return err } fmt.Fprintf(bt.stdout, "Successfully tagged %s\n", reference.FamiliarString(rt)) diff --git a/api/types/types.go b/api/types/types.go index 37adca2f57..8f309b2100 100644 --- a/api/types/types.go +++ b/api/types/types.go @@ -320,6 +320,7 @@ type ContainerJSONBase struct { Name string RestartCount int Driver string + Platform string MountLabel string ProcessLabel string AppArmorProfile string diff --git a/cmd/dockerd/daemon.go b/cmd/dockerd/daemon.go index 3578f5db57..ebd23611b9 100644 --- a/cmd/dockerd/daemon.go +++ b/cmd/dockerd/daemon.go @@ -267,12 +267,6 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) { logrus.Info("Daemon has completed initialization") - logrus.WithFields(logrus.Fields{ - "version": dockerversion.Version, - "commit": dockerversion.GitCommit, - "graphdriver": d.GraphDriverName(), - }).Info("Docker daemon") - cli.d = d initRouter(api, d, c) diff --git a/daemon/build.go b/daemon/build.go index 3faa2affd4..a9cce776bd 100644 --- a/daemon/build.go +++ b/daemon/build.go @@ -2,6 +2,7 @@ package daemon import ( "io" + "runtime" "github.com/Sirupsen/logrus" "github.com/docker/distribution/reference" @@ -147,7 +148,8 @@ func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID st image, _ := daemon.GetImage(refOrID) // TODO: shouldn't we error out if error is different from "not found" ? if image != nil { - layer, err := newReleasableLayerForImage(image, daemon.layerStore) + // TODO LCOW @jhowardmsft. For now using runtime.GOOS for this, will need enhancing for platform when porting the builder + layer, err := newReleasableLayerForImage(image, daemon.stores[runtime.GOOS].layerStore) return image, layer, err } } @@ -156,7 +158,8 @@ func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID st if err != nil { return nil, nil, err } - layer, err := newReleasableLayerForImage(image, daemon.layerStore) + // TODO LCOW @jhowardmsft. For now using runtime.GOOS for this, will need enhancing for platform when porting the builder + layer, err := newReleasableLayerForImage(image, daemon.stores[runtime.GOOS].layerStore) return image, layer, err } diff --git a/daemon/cache.go b/daemon/cache.go index 8e3d207758..88d4e38fd4 100644 --- a/daemon/cache.go +++ b/daemon/cache.go @@ -1,6 +1,8 @@ package daemon import ( + "runtime" + "github.com/Sirupsen/logrus" "github.com/docker/docker/builder" "github.com/docker/docker/image/cache" @@ -9,10 +11,12 @@ import ( // MakeImageCache creates a stateful image cache. func (daemon *Daemon) MakeImageCache(sourceRefs []string) builder.ImageCache { if len(sourceRefs) == 0 { - return cache.NewLocal(daemon.imageStore) + // TODO @jhowardmsft LCOW. For now, assume it is the OS of the host + return cache.NewLocal(daemon.stores[runtime.GOOS].imageStore) } - cache := cache.New(daemon.imageStore) + // TODO @jhowardmsft LCOW. For now, assume it is the OS of the host + cache := cache.New(daemon.stores[runtime.GOOS].imageStore) for _, ref := range sourceRefs { img, err := daemon.GetImage(ref) diff --git a/daemon/commit.go b/daemon/commit.go index 0c7246c063..51089b6162 100644 --- a/daemon/commit.go +++ b/daemon/commit.go @@ -160,26 +160,21 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str }() var parent *image.Image - os := runtime.GOOS if container.ImageID == "" { parent = new(image.Image) parent.RootFS = image.NewRootFS() } else { - parent, err = daemon.imageStore.Get(container.ImageID) + parent, err = daemon.stores[container.Platform].imageStore.Get(container.ImageID) if err != nil { return "", err } - // To support LCOW, Windows needs to pass the platform in when registering the layer in the store - if runtime.GOOS == "windows" { - os = parent.OS - } } - l, err := daemon.layerStore.Register(rwTar, parent.RootFS.ChainID(), layer.Platform(os)) + l, err := daemon.stores[container.Platform].layerStore.Register(rwTar, rootFS.ChainID(), layer.Platform(container.Platform)) if err != nil { return "", err } - defer layer.ReleaseAndLog(daemon.layerStore, l) + defer layer.ReleaseAndLog(daemon.stores[container.Platform].layerStore, l) containerConfig := c.ContainerConfig if containerConfig == nil { @@ -198,13 +193,13 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str return "", err } - id, err := daemon.imageStore.Create(config) + id, err := daemon.stores[container.Platform].imageStore.Create(config) if err != nil { return "", err } if container.ImageID != "" { - if err := daemon.imageStore.SetParent(id, container.ImageID); err != nil { + if err := daemon.stores[container.Platform].imageStore.SetParent(id, container.ImageID); err != nil { return "", err } } @@ -223,7 +218,7 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str return "", err } } - if err := daemon.TagImageWithReference(id, newTag); err != nil { + if err := daemon.TagImageWithReference(id, container.Platform, newTag); err != nil { return "", err } imageRef = reference.FamiliarString(newTag) diff --git a/daemon/container.go b/daemon/container.go index 6a660ba4f3..f29983590b 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "path/filepath" + "runtime" "time" "github.com/docker/docker/api/errors" @@ -144,8 +145,10 @@ func (daemon *Daemon) newContainer(name string, config *containertypes.Config, h base.ImageID = imgID base.NetworkSettings = &network.Settings{IsAnonymousEndpoint: noExplicitName} base.Name = name - base.Driver = daemon.GraphDriverName() - + // TODO @jhowardmsft LCOW - Get it from the platform of the container. For now, assume it is the OS of the host + base.Driver = daemon.GraphDriverName(runtime.GOOS) + // TODO @jhowardmsft LCOW - Similarly on this field. To solve this it will need a CLI/REST change in a subsequent PR during LCOW development + base.Platform = runtime.GOOS return base, err } diff --git a/daemon/create.go b/daemon/create.go index 697886274f..b9ae49ee94 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -215,7 +215,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.imageStore.Get(container.ImageID) + img, err := daemon.stores[container.Platform].imageStore.Get(container.ImageID) if err != nil { return err } @@ -228,7 +228,7 @@ func (daemon *Daemon) setRWLayer(container *container.Container) error { StorageOpt: container.HostConfig.StorageOpt, } - rwLayer, err := daemon.layerStore.CreateRWLayer(container.ID, layerID, rwLayerOpts) + rwLayer, err := daemon.stores[container.Platform].layerStore.CreateRWLayer(container.ID, layerID, rwLayerOpts) if err != nil { return err } diff --git a/daemon/daemon.go b/daemon/daemon.go index fafd4b2668..54c946dcdc 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -69,43 +69,49 @@ 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 + referenceStore refstore.Store +} + // Daemon holds information about the Docker daemon. type Daemon struct { - ID string - repository string - containers container.Store - execCommands *exec.Store - referenceStore refstore.Store - downloadManager *xfer.LayerDownloadManager - uploadManager *xfer.LayerUploadManager - distributionMetadataStore dmetadata.Store - 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 - layerStore layer.Store - imageStore image.Store - PluginStore *plugin.Store // todo: remove - pluginManager *plugin.Manager - nameIndex *registrar.Registrar - linkIndex *linkIndex - containerd libcontainerd.Client - containerdRemote libcontainerd.Remote - defaultIsolation containertypes.Isolation // Default isolation mode on Windows - clusterProvider cluster.Provider - cluster Cluster - metricsPluginListener net.Listener + ID string + repository string + containers container.Store + 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 + PluginStore *plugin.Store // todo: remove + pluginManager *plugin.Manager + nameIndex *registrar.Registrar + linkIndex *linkIndex + containerd libcontainerd.Client + containerdRemote libcontainerd.Remote + defaultIsolation containertypes.Isolation // Default isolation mode on Windows + clusterProvider cluster.Provider + cluster Cluster + metricsPluginListener net.Listener machineMemory uint64 @@ -137,10 +143,7 @@ func (daemon *Daemon) HasExperimental() bool { } func (daemon *Daemon) restore() error { - var ( - currentDriver = daemon.GraphDriverName() - containers = make(map[string]*container.Container) - ) + containers := make(map[string]*container.Container) logrus.Info("Loading containers: start.") @@ -158,8 +161,9 @@ func (daemon *Daemon) restore() error { } // Ignore the container if it does not support the current driver being used by the graph - if (container.Driver == "" && currentDriver == "aufs") || container.Driver == currentDriver { - rwlayer, err := daemon.layerStore.GetRWLayer(container.ID) + currentDriverForContainerPlatform := daemon.stores[container.Platform].graphDriver + if (container.Driver == "" && currentDriverForContainerPlatform == "aufs") || container.Driver == currentDriverForContainerPlatform { + rwlayer, err := daemon.stores[container.Platform].layerStore.GetRWLayer(container.ID) if err != nil { logrus.Errorf("Failed to load container mount %v: %v", id, err) continue @@ -595,9 +599,24 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe } } - driverName := os.Getenv("DOCKER_DRIVER") - if driverName == "" { - driverName = config.GraphDriver + // On Windows we don't support the environment variable, or a user supplied graphdriver + // as Windows has no choice in terms of which graphdrivers to use. It's a case of + // running Windows containers on Windows - windowsfilter, running Linux containers on Windows, + // 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) + if runtime.GOOS == "windows" { + d.stores["windows"] = daemonStore{graphDriver: "windowsfilter"} + if system.LCOWSupported() { + d.stores["linux"] = daemonStore{graphDriver: "lcow"} + } + } else { + driverName := os.Getenv("DOCKER_DRIVER") + if driverName == "" { + driverName = config.GraphDriver + } + d.stores[runtime.GOOS] = daemonStore{graphDriver: driverName} // May still be empty. Layerstore init determines instead. } d.RegistryService = registryService @@ -625,42 +644,55 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe return nil, errors.Wrap(err, "couldn't create plugin manager") } - d.layerStore, err = layer.NewStoreFromOptions(layer.StoreOptions{ - StorePath: config.Root, - MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"), - GraphDriver: driverName, - GraphDriverOptions: config.GraphOptions, - IDMappings: idMappings, - PluginGetter: d.PluginStore, - ExperimentalEnabled: config.Experimental, - }) - if err != nil { - return nil, err + var graphDrivers []string + for platform, 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, + }) + if err != nil { + return nil, err + } + ds.graphDriver = ls.DriverName() // As layerstore may set the driver + ds.layerStore = ls + d.stores[platform] = ds + graphDrivers = append(graphDrivers, ls.DriverName()) } - graphDriver := d.layerStore.DriverName() - imageRoot := filepath.Join(config.Root, "image", graphDriver) - // Configure and validate the kernels security support - if err := configureKernelSecuritySupport(config, graphDriver); err != nil { + if err := configureKernelSecuritySupport(config, graphDrivers); err != nil { return nil, err } logrus.Debugf("Max Concurrent Downloads: %d", *config.MaxConcurrentDownloads) - d.downloadManager = xfer.NewLayerDownloadManager(d.layerStore, *config.MaxConcurrentDownloads) + lsMap := make(map[string]layer.Store) + for platform, ds := range d.stores { + lsMap[platform] = ds.layerStore + } + d.downloadManager = xfer.NewLayerDownloadManager(lsMap, *config.MaxConcurrentDownloads) logrus.Debugf("Max Concurrent Uploads: %d", *config.MaxConcurrentUploads) d.uploadManager = xfer.NewLayerUploadManager(*config.MaxConcurrentUploads) - ifs, err := image.NewFSStoreBackend(filepath.Join(imageRoot, "imagedb")) - if err != nil { - return nil, err - } + for platform, 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 + } - // TODO LCOW @jhowardmsft. For now assume it's the runtime OS. This will be modified - // as the stores are split in a follow-up commit. - d.imageStore, err = image.NewImageStore(ifs, runtime.GOOS, d.layerStore) - if err != nil { - return nil, err + var is image.Store + is, err = image.NewImageStore(ifs, platform, ds.layerStore) + if err != nil { + return nil, err + } + ds.imageRoot = imageRoot + ds.imageStore = is + d.stores[platform] = ds } // Configure the volumes driver @@ -680,23 +712,31 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe return nil, err } - distributionMetadataStore, err := dmetadata.NewFSMetadataStore(filepath.Join(imageRoot, "distribution")) - if err != nil { - return nil, err - } - eventsService := events.New() - referenceStore, err := refstore.NewReferenceStore(filepath.Join(imageRoot, "repositories.json")) - if err != nil { - return nil, fmt.Errorf("Couldn't create Tag store repositories: %s", err) - } + for platform, ds := range d.stores { + dms, err := dmetadata.NewFSMetadataStore(filepath.Join(ds.imageRoot, "distribution"), platform) + if err != nil { + return nil, err + } - migrationStart := time.Now() - if err := v1.Migrate(config.Root, graphDriver, d.layerStore, d.imageStore, referenceStore, 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) + rs, err := refstore.NewReferenceStore(filepath.Join(ds.imageRoot, "repositories.json"), platform) + if err != nil { + return nil, fmt.Errorf("Couldn't create Tag store repositories: %s", err) + } + ds.distributionMetadataStore = dms + ds.referenceStore = rs + 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()) + } } - 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 // initialized, the daemon is registered and we can store the discovery backend as it's read-only @@ -715,8 +755,6 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe d.repository = daemonRepo d.containers = container.NewMemoryStore() d.execCommands = exec.NewStore() - d.referenceStore = referenceStore - d.distributionMetadataStore = distributionMetadataStore d.trustKey = trustKey d.idIndex = truncindex.NewTruncIndex([]string{}) d.statsCollector = d.newStatsCollector(1 * time.Second) @@ -763,6 +801,22 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe engineCpus.Set(float64(info.NCPU)) engineMemory.Set(float64(info.MemTotal)) + gd := "" + for platform, ds := range d.stores { + if len(gd) > 0 { + gd += ", " + } + gd += ds.graphDriver + if len(d.stores) > 1 { + gd = fmt.Sprintf("%s (%s)", gd, platform) + } + } + logrus.WithFields(logrus.Fields{ + "version": dockerversion.Version, + "commit": dockerversion.GitCommit, + "graphdriver(s)": gd, + }).Info("Docker daemon") + return d, nil } @@ -869,7 +923,7 @@ func (daemon *Daemon) Shutdown() error { logrus.Errorf("Stop container error: %v", err) return } - if mountid, err := daemon.layerStore.GetMountID(c.ID); err == nil { + if mountid, err := daemon.stores[c.Platform].layerStore.GetMountID(c.ID); err == nil { daemon.cleanupMountsByID(mountid) } logrus.Debugf("container stopped %s", c.ID) @@ -882,9 +936,11 @@ func (daemon *Daemon) Shutdown() error { } } - if daemon.layerStore != nil { - if err := daemon.layerStore.Cleanup(); err != nil { - logrus.Errorf("Error during layer Store.Cleanup(): %v", err) + 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) + } } } @@ -927,7 +983,7 @@ func (daemon *Daemon) Mount(container *container.Container) error { if container.BaseFS != "" && runtime.GOOS != "windows" { daemon.Unmount(container) return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')", - daemon.GraphDriverName(), container.ID, container.BaseFS, dir) + daemon.GraphDriverName(container.Platform), container.ID, container.BaseFS, dir) } } container.BaseFS = dir // TODO: combine these fields @@ -969,8 +1025,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() string { - return daemon.layerStore.DriverName() +func (daemon *Daemon) GraphDriverName(platform string) string { + return daemon.stores[platform].layerStore.DriverName() } // prepareTempDir prepares and returns the default directory to use diff --git a/daemon/daemon_solaris.go b/daemon/daemon_solaris.go index de0f3ac3e7..7f2004e65a 100644 --- a/daemon/daemon_solaris.go +++ b/daemon/daemon_solaris.go @@ -353,7 +353,7 @@ func configureMaxThreads(config *Config) error { } // configureKernelSecuritySupport configures and validate security support for the kernel -func configureKernelSecuritySupport(config *Config, driverName string) error { +func configureKernelSecuritySupport(config *config.Config, driverNames []string) error { return nil } diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index 07c323d63f..662098ed19 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -702,14 +702,22 @@ func overlaySupportsSelinux() (bool, error) { } // configureKernelSecuritySupport configures and validates security support for the kernel -func configureKernelSecuritySupport(config *config.Config, driverName string) error { +func configureKernelSecuritySupport(config *config.Config, driverNames []string) error { if config.EnableSelinuxSupport { if !selinuxEnabled() { logrus.Warn("Docker could not enable SELinux on the host system") return nil } - if driverName == "overlay" || driverName == "overlay2" { + overlayFound := false + for _, d := range driverNames { + if d == "overlay" || d == "overlay2" { + overlayFound = true + break + } + } + + if overlayFound { // If driver is overlay or overlay2, make sure kernel // supports selinux with overlay. supported, err := overlaySupportsSelinux() @@ -718,7 +726,7 @@ func configureKernelSecuritySupport(config *config.Config, driverName string) er } if !supported { - logrus.Warnf("SELinux is not supported with the %s graph driver on this kernel", driverName) + logrus.Warnf("SELinux is not supported with the %v graph driver on this kernel", driverNames) } } } else { diff --git a/daemon/daemon_windows.go b/daemon/daemon_windows.go index 49a7b212b8..9a92fb4367 100644 --- a/daemon/daemon_windows.go +++ b/daemon/daemon_windows.go @@ -260,7 +260,7 @@ func checkSystem() error { } // configureKernelSecuritySupport configures and validate security support for the kernel -func configureKernelSecuritySupport(config *config.Config, driverName string) error { +func configureKernelSecuritySupport(config *config.Config, driverNames []string) error { return nil } diff --git a/daemon/delete.go b/daemon/delete.go index af709445ce..3dcd289101 100644 --- a/daemon/delete.go +++ b/daemon/delete.go @@ -115,10 +115,10 @@ 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.layerStore.ReleaseRWLayer(container.RWLayer) + metadata, err := daemon.stores[container.Platform].layerStore.ReleaseRWLayer(container.RWLayer) layer.LogReleaseMetadata(metadata) if err != nil && err != layer.ErrMountDoesNotExist { - return errors.Wrapf(err, "driver %q failed to remove root filesystem for %s", daemon.GraphDriverName(), container.ID) + return errors.Wrapf(err, "driver %q failed to remove root filesystem for %s", daemon.GraphDriverName(container.Platform), container.ID) } } diff --git a/daemon/disk_usage.go b/daemon/disk_usage.go index fb15c3c373..c64a243304 100644 --- a/daemon/disk_usage.go +++ b/daemon/disk_usage.go @@ -15,12 +15,12 @@ import ( "github.com/opencontainers/go-digest" ) -func (daemon *Daemon) getLayerRefs() map[layer.ChainID]int { - tmpImages := daemon.imageStore.Map() +func (daemon *Daemon) getLayerRefs(platform string) map[layer.ChainID]int { + tmpImages := daemon.stores[platform].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.imageStore.Children(id)) != 0 { + if len(daemon.stores[platform].referenceStore.References(dgst)) == 0 && len(daemon.stores[platform].imageStore.Children(id)) != 0 { continue } @@ -53,6 +53,7 @@ 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) @@ -94,23 +95,26 @@ func (daemon *Daemon) SystemDiskUsage(ctx context.Context) (*types.DiskUsage, er } // Get total layers size on disk - layerRefs := daemon.getLayerRefs() - allLayers := daemon.layerStore.Map() var allLayersSize int64 - 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 + for platform := range daemon.stores { + layerRefs := daemon.getLayerRefs(platform) + allLayers := daemon.stores[platform].layerStore.Map() + var allLayersSize int64 + 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) + } } else { - logrus.Warnf("found leaked image layer %v", l.ChainID()) + logrus.Warnf("failed to get diff size for layer %v %s", l.ChainID(), platform) } - } 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 bd088fd41c..434fa4388a 100644 --- a/daemon/getsize_unix.go +++ b/daemon/getsize_unix.go @@ -3,6 +3,8 @@ package daemon import ( + "runtime" + "github.com/Sirupsen/logrus" ) @@ -13,17 +15,17 @@ func (daemon *Daemon) getSize(containerID string) (int64, int64) { err error ) - rwlayer, err := daemon.layerStore.GetRWLayer(containerID) + rwlayer, err := daemon.stores[runtime.GOOS].layerStore.GetRWLayer(containerID) if err != nil { logrus.Errorf("Failed to compute size of container rootfs %v: %v", containerID, err) return sizeRw, sizeRootfs } - defer daemon.layerStore.ReleaseRWLayer(rwlayer) + defer daemon.stores[runtime.GOOS].layerStore.ReleaseRWLayer(rwlayer) sizeRw, err = rwlayer.Size() if err != nil { logrus.Errorf("Driver %s couldn't return diff size of container %s: %s", - daemon.GraphDriverName(), containerID, err) + daemon.GraphDriverName(runtime.GOOS), containerID, err) // FIXME: GetSize should return an error. Not changing it now in case // there is a side-effect. sizeRw = -1 diff --git a/daemon/graphdriver/register/register_windows.go b/daemon/graphdriver/register/register_windows.go index efaa5005ed..5bb1fd62a8 100644 --- a/daemon/graphdriver/register/register_windows.go +++ b/daemon/graphdriver/register/register_windows.go @@ -1,6 +1,7 @@ package register import ( - // register the windows graph driver + // register the windows graph drivers + _ "github.com/docker/docker/daemon/graphdriver/lcow" _ "github.com/docker/docker/daemon/graphdriver/windows" ) diff --git a/daemon/graphdriver/windows/windows.go b/daemon/graphdriver/windows/windows.go index 441621f6eb..74550e3e9a 100644 --- a/daemon/graphdriver/windows/windows.go +++ b/daemon/graphdriver/windows/windows.go @@ -94,6 +94,10 @@ func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap) return nil, fmt.Errorf("%s is on an ReFS volume - ReFS volumes are not supported", home) } + if err := idtools.MkdirAllAs(home, 0700, 0, 0); err != nil { + return nil, fmt.Errorf("windowsfilter failed to create '%s': %v", home, err) + } + d := &Driver{ info: hcsshim.DriverInfo{ HomeDir: home, diff --git a/daemon/image.go b/daemon/image.go index d10457118f..a51049dbb5 100644 --- a/daemon/image.go +++ b/daemon/image.go @@ -21,37 +21,43 @@ func (e ErrImageDoesNotExist) Error() string { return fmt.Sprintf("No such image: %s", reference.FamiliarString(ref)) } -// GetImageID returns an image ID corresponding to the image referred to by +// GetImageIDAndPlatform returns an image ID and platform corresponding to the image referred to by // refOrID. -func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) { +func (daemon *Daemon) GetImageIDAndPlatform(refOrID string) (image.ID, string, error) { ref, err := reference.ParseAnyReference(refOrID) if err != nil { - return "", err + return "", "", err } namedRef, ok := ref.(reference.Named) if !ok { digested, ok := ref.(reference.Digested) if !ok { - return "", ErrImageDoesNotExist{ref} + return "", "", ErrImageDoesNotExist{ref} } id := image.IDFromDigest(digested.Digest()) - if _, err := daemon.imageStore.Get(id); err != nil { - return "", ErrImageDoesNotExist{ref} + for platform := range daemon.stores { + if _, err = daemon.stores[platform].imageStore.Get(id); err == nil { + return id, platform, nil + } } - return id, nil + return "", "", ErrImageDoesNotExist{ref} } - if id, err := daemon.referenceStore.Get(namedRef); err == nil { - return image.IDFromDigest(id), nil + for platform := range daemon.stores { + if id, err := daemon.stores[platform].referenceStore.Get(namedRef); err == nil { + return image.IDFromDigest(id), platform, nil + } } // deprecated: repo:shortid https://github.com/docker/docker/pull/799 if tagged, ok := namedRef.(reference.Tagged); ok { if tag := tagged.Tag(); stringid.IsShortID(stringid.TruncateID(tag)) { - if id, err := daemon.imageStore.Search(tag); err == nil { - for _, storeRef := range daemon.referenceStore.References(id.Digest()) { - if storeRef.Name() == namedRef.Name() { - return id, nil + for platform := range daemon.stores { + if id, err := daemon.stores[platform].imageStore.Search(tag); err == nil { + for _, storeRef := range daemon.stores[platform].referenceStore.References(id.Digest()) { + if storeRef.Name() == namedRef.Name() { + return id, platform, nil + } } } } @@ -59,18 +65,20 @@ func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) { } // Search based on ID - if id, err := daemon.imageStore.Search(refOrID); err == nil { - return id, nil + for platform := range daemon.stores { + if id, err := daemon.stores[platform].imageStore.Search(refOrID); err == nil { + return id, platform, nil + } } - return "", ErrImageDoesNotExist{ref} + return "", "", ErrImageDoesNotExist{ref} } // GetImage returns an image corresponding to the image referred to by refOrID. func (daemon *Daemon) GetImage(refOrID string) (*image.Image, error) { - imgID, err := daemon.GetImageID(refOrID) + imgID, platform, err := daemon.GetImageIDAndPlatform(refOrID) if err != nil { return nil, err } - return daemon.imageStore.Get(imgID) + return daemon.stores[platform].imageStore.Get(imgID) } diff --git a/daemon/image_delete.go b/daemon/image_delete.go index b7dbd249eb..4e228594bc 100644 --- a/daemon/image_delete.go +++ b/daemon/image_delete.go @@ -65,12 +65,12 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I start := time.Now() records := []types.ImageDeleteResponseItem{} - imgID, err := daemon.GetImageID(imageRef) + imgID, platform, err := daemon.GetImageIDAndPlatform(imageRef) if err != nil { return nil, daemon.imageNotExistToErrcode(err) } - repoRefs := daemon.referenceStore.References(imgID.Digest()) + repoRefs := daemon.stores[platform].referenceStore.References(imgID.Digest()) var removedRepositoryRef bool if !isImageIDPrefix(imgID.String(), imageRef) { @@ -94,7 +94,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I return nil, err } - parsedRef, err = daemon.removeImageRef(parsedRef) + parsedRef, err = daemon.removeImageRef(platform, parsedRef) if err != nil { return nil, err } @@ -104,7 +104,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I daemon.LogImageEvent(imgID.String(), imgID.String(), "untag") records = append(records, untaggedRecord) - repoRefs = daemon.referenceStore.References(imgID.Digest()) + repoRefs = daemon.stores[platform].referenceStore.References(imgID.Digest()) // If a tag reference was removed and the only remaining // references to the same repository are digest references, @@ -122,7 +122,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(repoRef); err != nil { + if _, err := daemon.removeImageRef(platform, repoRef); err != nil { return records, err } @@ -152,12 +152,12 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I if !force { c |= conflictSoft &^ conflictActiveReference } - if conflict := daemon.checkImageDeleteConflict(imgID, c); conflict != nil { + if conflict := daemon.checkImageDeleteConflict(imgID, platform, c); conflict != nil { return nil, conflict } for _, repoRef := range repoRefs { - parsedRef, err := daemon.removeImageRef(repoRef) + parsedRef, err := daemon.removeImageRef(platform, repoRef) if err != nil { return nil, err } @@ -170,7 +170,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I } } - if err := daemon.imageDeleteHelper(imgID, &records, force, prune, removedRepositoryRef); err != nil { + if err := daemon.imageDeleteHelper(imgID, platform, &records, force, prune, removedRepositoryRef); err != nil { return nil, err } @@ -231,13 +231,13 @@ 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(ref reference.Named) (reference.Named, error) { +func (daemon *Daemon) removeImageRef(platform string, ref reference.Named) (reference.Named, error) { ref = reference.TagNameOnly(ref) // Ignore the boolean value returned, as far as we're concerned, this // is an idempotent operation and it's okay if the reference didn't // exist in the first place. - _, err := daemon.referenceStore.Delete(ref) + _, err := daemon.stores[platform].referenceStore.Delete(ref) return ref, err } @@ -247,11 +247,11 @@ func (daemon *Daemon) removeImageRef(ref reference.Named) (reference.Named, erro // 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, records *[]types.ImageDeleteResponseItem) error { - imageRefs := daemon.referenceStore.References(imgID.Digest()) +func (daemon *Daemon) removeAllReferencesToImageID(imgID image.ID, platform string, records *[]types.ImageDeleteResponseItem) error { + imageRefs := daemon.stores[platform].referenceStore.References(imgID.Digest()) for _, imageRef := range imageRefs { - parsedRef, err := daemon.removeImageRef(imageRef) + parsedRef, err := daemon.removeImageRef(platform, imageRef) if err != nil { return err } @@ -296,15 +296,15 @@ func (idc *imageDeleteConflict) Error() string { // 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, records *[]types.ImageDeleteResponseItem, force, prune, quiet bool) error { +func (daemon *Daemon) imageDeleteHelper(imgID image.ID, platform string, 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, c); conflict != nil { - if quiet && (!daemon.imageIsDangling(imgID) || conflict.used) { + if conflict := daemon.checkImageDeleteConflict(imgID, platform, c); conflict != nil { + if quiet && (!daemon.imageIsDangling(imgID, platform) || conflict.used) { // Ignore conflicts UNLESS the image is "dangling" or not being used in // which case we want the user to know. return nil @@ -315,18 +315,18 @@ func (daemon *Daemon) imageDeleteHelper(imgID image.ID, records *[]types.ImageDe return conflict } - parent, err := daemon.imageStore.GetParent(imgID) + parent, err := daemon.stores[platform].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, records); err != nil { + if err := daemon.removeAllReferencesToImageID(imgID, platform, records); err != nil { return err } - removedLayers, err := daemon.imageStore.Delete(imgID) + removedLayers, err := daemon.stores[platform].imageStore.Delete(imgID) if err != nil { return err } @@ -346,7 +346,7 @@ func (daemon *Daemon) imageDeleteHelper(imgID image.ID, records *[]types.ImageDe // either running or stopped). // Do not force prunings, but do so quietly (stopping on any encountered // conflicts). - return daemon.imageDeleteHelper(parent, records, false, true, true) + return daemon.imageDeleteHelper(parent, platform, records, false, true, true) } // checkImageDeleteConflict determines whether there are any conflicts @@ -355,9 +355,9 @@ func (daemon *Daemon) imageDeleteHelper(imgID image.ID, records *[]types.ImageDe // 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, mask conflictType) *imageDeleteConflict { +func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, platform string, mask conflictType) *imageDeleteConflict { // Check if the image has any descendant images. - if mask&conflictDependentChild != 0 && len(daemon.imageStore.Children(imgID)) > 0 { + if mask&conflictDependentChild != 0 && len(daemon.stores[platform].imageStore.Children(imgID)) > 0 { return &imageDeleteConflict{ hard: true, imgID: imgID, @@ -381,7 +381,7 @@ func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, mask conflictType } // Check if any repository tags/digest reference this image. - if mask&conflictActiveReference != 0 && len(daemon.referenceStore.References(imgID.Digest())) > 0 { + if mask&conflictActiveReference != 0 && len(daemon.stores[platform].referenceStore.References(imgID.Digest())) > 0 { return &imageDeleteConflict{ imgID: imgID, message: "image is referenced in multiple repositories", @@ -408,6 +408,6 @@ func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, mask conflictType // 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) bool { - return !(len(daemon.referenceStore.References(imgID.Digest())) > 0 || len(daemon.imageStore.Children(imgID)) > 0) +func (daemon *Daemon) imageIsDangling(imgID image.ID, platform string) bool { + return !(len(daemon.stores[platform].referenceStore.References(imgID.Digest())) > 0 || len(daemon.stores[platform].imageStore.Children(imgID)) > 0) } diff --git a/daemon/image_exporter.go b/daemon/image_exporter.go index 95d1d3dcdb..7885f8cfac 100644 --- a/daemon/image_exporter.go +++ b/daemon/image_exporter.go @@ -2,6 +2,7 @@ package daemon import ( "io" + "runtime" "github.com/docker/docker/image/tarexport" ) @@ -12,7 +13,8 @@ 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 { - imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore, daemon) + // TODO @jhowardmsft LCOW. For now, assume it is the OS of the host + imageExporter := tarexport.NewTarExporter(daemon.stores[runtime.GOOS].imageStore, daemon.stores[runtime.GOOS].layerStore, daemon.stores[runtime.GOOS].referenceStore, daemon) return imageExporter.Save(names, outStream) } @@ -20,6 +22,7 @@ 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 { - imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore, daemon) + // TODO @jhowardmsft LCOW. For now, assume it is the OS of the host + imageExporter := tarexport.NewTarExporter(daemon.stores[runtime.GOOS].imageStore, daemon.stores[runtime.GOOS].layerStore, daemon.stores[runtime.GOOS].referenceStore, daemon) return imageExporter.Load(inTar, outStream, quiet) } diff --git a/daemon/image_history.go b/daemon/image_history.go index b763c86c03..c9e81554e9 100644 --- a/daemon/image_history.go +++ b/daemon/image_history.go @@ -2,6 +2,7 @@ package daemon import ( "fmt" + "runtime" "time" "github.com/docker/distribution/reference" @@ -18,6 +19,12 @@ 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 @@ -33,12 +40,12 @@ func (daemon *Daemon) ImageHistory(name string) ([]*image.HistoryResponseItem, e } rootFS.Append(img.RootFS.DiffIDs[layerCounter]) - l, err := daemon.layerStore.Get(rootFS.ChainID()) + l, err := daemon.stores[platform].layerStore.Get(rootFS.ChainID()) if err != nil { return nil, err } layerSize, err = l.DiffSize() - layer.ReleaseAndLog(daemon.layerStore, l) + layer.ReleaseAndLog(daemon.stores[platform].layerStore, l) if err != nil { return nil, err } @@ -62,7 +69,7 @@ func (daemon *Daemon) ImageHistory(name string) ([]*image.HistoryResponseItem, e h.ID = id.String() var tags []string - for _, r := range daemon.referenceStore.References(id.Digest()) { + for _, r := range daemon.stores[platform].referenceStore.References(id.Digest()) { if _, ok := r.(reference.NamedTagged); ok { tags = append(tags, reference.FamiliarString(r)) } diff --git a/daemon/image_inspect.go b/daemon/image_inspect.go index 267a41946a..672a4cc012 100644 --- a/daemon/image_inspect.go +++ b/daemon/image_inspect.go @@ -1,6 +1,7 @@ package daemon import ( + "runtime" "time" "github.com/docker/distribution/reference" @@ -17,7 +18,13 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) { return nil, errors.Wrapf(err, "no such image: %s", name) } - refs := daemon.referenceStore.References(img.ID().Digest()) + // If the image OS isn't set, assume it's the host OS + platform := img.OS + if platform == "" { + platform = runtime.GOOS + } + + refs := daemon.stores[platform].referenceStore.References(img.ID().Digest()) repoTags := []string{} repoDigests := []string{} for _, ref := range refs { @@ -33,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.layerStore.Get(layerID) + l, err := daemon.stores[platform].layerStore.Get(layerID) if err != nil { return nil, err } - defer layer.ReleaseAndLog(daemon.layerStore, l) + defer layer.ReleaseAndLog(daemon.stores[platform].layerStore, l) size, err = l.Size() if err != nil { return nil, err @@ -67,15 +74,14 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) { Author: img.Author, Config: img.Config, Architecture: img.Architecture, - Os: img.OS, + Os: platform, OsVersion: img.OSVersion, Size: size, VirtualSize: size, // TODO: field unused, deprecate RootFS: rootFSToAPIType(img.RootFS), } - imageInspect.GraphDriver.Name = daemon.GraphDriverName() - + imageInspect.GraphDriver.Name = daemon.GraphDriverName(platform) imageInspect.GraphDriver.Data = layerMetadata return imageInspect, nil diff --git a/daemon/image_pull.go b/daemon/image_pull.go index 304fd9f024..79bd5b588e 100644 --- a/daemon/image_pull.go +++ b/daemon/image_pull.go @@ -2,6 +2,7 @@ package daemon import ( "io" + "runtime" "strings" dist "github.com/docker/distribution" @@ -59,6 +60,12 @@ func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference. close(writesDone) }() + // ------------------------------------------------------------------------------ + // TODO @jhowardmsft LCOW. For now, use just the store for the host OS. This will + // need some work to complete - we won't know the platform until after metadata + // is pulled from the repository. This affects plugin as well to complete. + // ------------------------------------------------------------------------------ + imagePullConfig := &distribution.ImagePullConfig{ Config: distribution.Config{ MetaHeaders: metaHeaders, @@ -66,9 +73,9 @@ func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference. ProgressOutput: progress.ChanOutput(progressChan), RegistryService: daemon.RegistryService, ImageEventLogger: daemon.LogImageEvent, - MetadataStore: daemon.distributionMetadataStore, - ImageStore: distribution.NewImageConfigStoreFromStore(daemon.imageStore), - ReferenceStore: daemon.referenceStore, + MetadataStore: daemon.stores[runtime.GOOS].distributionMetadataStore, + ImageStore: distribution.NewImageConfigStoreFromStore(daemon.stores[runtime.GOOS].imageStore), + ReferenceStore: daemon.stores[runtime.GOOS].referenceStore, }, DownloadManager: daemon.downloadManager, Schema2Types: distribution.ImageTypes, diff --git a/daemon/image_push.go b/daemon/image_push.go index 0f060d117f..f11669c660 100644 --- a/daemon/image_push.go +++ b/daemon/image_push.go @@ -2,6 +2,7 @@ package daemon import ( "io" + "runtime" "github.com/docker/distribution/manifest/schema2" "github.com/docker/distribution/reference" @@ -39,6 +40,11 @@ func (daemon *Daemon) PushImage(ctx context.Context, image, tag string, metaHead close(writesDone) }() + // ------------------------------------------------------------------------------ + // TODO @jhowardmsft LCOW. For now, use just the store for the host OS. This will + // need some work to complete. + // ------------------------------------------------------------------------------ + imagePushConfig := &distribution.ImagePushConfig{ Config: distribution.Config{ MetaHeaders: metaHeaders, @@ -46,12 +52,12 @@ func (daemon *Daemon) PushImage(ctx context.Context, image, tag string, metaHead ProgressOutput: progress.ChanOutput(progressChan), RegistryService: daemon.RegistryService, ImageEventLogger: daemon.LogImageEvent, - MetadataStore: daemon.distributionMetadataStore, - ImageStore: distribution.NewImageConfigStoreFromStore(daemon.imageStore), - ReferenceStore: daemon.referenceStore, + MetadataStore: daemon.stores[runtime.GOOS].distributionMetadataStore, + ImageStore: distribution.NewImageConfigStoreFromStore(daemon.stores[runtime.GOOS].imageStore), + ReferenceStore: daemon.stores[runtime.GOOS].referenceStore, }, ConfigMediaType: schema2.MediaTypeImageConfig, - LayerStore: distribution.NewLayerProviderFromStore(daemon.layerStore), + LayerStore: distribution.NewLayerProviderFromStore(daemon.stores[runtime.GOOS].layerStore), TrustKey: daemon.trustKey, UploadManager: daemon.uploadManager, } diff --git a/daemon/image_tag.go b/daemon/image_tag.go index 10a584b361..199ca9ad84 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, err := daemon.GetImageID(imageName) + imageID, platform, err := daemon.GetImageIDAndPlatform(imageName) if err != nil { return err } @@ -23,12 +23,12 @@ func (daemon *Daemon) TagImage(imageName, repository, tag string) error { } } - return daemon.TagImageWithReference(imageID, newTag) + return daemon.TagImageWithReference(imageID, platform, newTag) } // TagImageWithReference adds the given reference to the image ID provided. -func (daemon *Daemon) TagImageWithReference(imageID image.ID, newTag reference.Named) error { - if err := daemon.referenceStore.AddTag(newTag, imageID.Digest(), true); err != nil { +func (daemon *Daemon) TagImageWithReference(imageID image.ID, platform string, newTag reference.Named) error { + if err := daemon.stores[platform].referenceStore.AddTag(newTag, imageID.Digest(), true); err != nil { return err } diff --git a/daemon/images.go b/daemon/images.go index 3b7ddf8bf5..6e29cae4d0 100644 --- a/daemon/images.go +++ b/daemon/images.go @@ -15,6 +15,7 @@ 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{ @@ -35,7 +36,12 @@ 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 { - return daemon.imageStore.Map() + // TODO @jhowardmsft LCOW. This will need work to enumerate the stores for all platforms. + platform := runtime.GOOS + if platform == "windows" && system.LCOWSupported() { + platform = "linux" + } + return daemon.stores[platform].imageStore.Map() } // Images returns a filtered list of images. filterArgs is a JSON-encoded set @@ -44,6 +50,13 @@ 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 will need work to enumerate the stores for all platforms. + platform := runtime.GOOS + if platform == "windows" && system.LCOWSupported() { + platform = "linux" + } + var ( allImages map[image.ID]*image.Image err error @@ -62,9 +75,9 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs } } if danglingOnly { - allImages = daemon.imageStore.Heads() + allImages = daemon.stores[platform].imageStore.Heads() } else { - allImages = daemon.imageStore.Map() + allImages = daemon.stores[platform].imageStore.Map() } var beforeFilter, sinceFilter *image.Image @@ -117,7 +130,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs layerID := img.RootFS.ChainID() var size int64 if layerID != "" { - l, err := daemon.layerStore.Get(layerID) + l, err := daemon.stores[platform].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 @@ -128,7 +141,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs } size, err = l.Size() - layer.ReleaseAndLog(daemon.layerStore, l) + layer.ReleaseAndLog(daemon.stores[platform].layerStore, l) if err != nil { return nil, err } @@ -136,7 +149,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs newImage := newImage(img, size) - for _, ref := range daemon.referenceStore.References(id.Digest()) { + for _, ref := range daemon.stores[platform].referenceStore.References(id.Digest()) { if imageFilters.Include("reference") { var found bool var matchErr error @@ -158,7 +171,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs } } if newImage.RepoDigests == nil && newImage.RepoTags == nil { - if all || len(daemon.imageStore.Children(id)) == 0 { + if all || len(daemon.stores[platform].imageStore.Children(id)) == 0 { if imageFilters.Include("dangling") && !danglingOnly { //dangling=false case, so dangling image is not needed @@ -180,7 +193,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs // lazily init variables if imagesMap == nil { allContainers = daemon.List() - allLayers = daemon.layerStore.Map() + allLayers = daemon.stores[platform].layerStore.Map() imagesMap = make(map[*image.Image]*types.ImageSummary) layerRefs = make(map[layer.ChainID]int) } @@ -243,7 +256,16 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs // The existing image(s) is not destroyed. // If no parent is specified, a new image with the diff of all the specified image's layers merged into a new layer that has no parents. func (daemon *Daemon) SquashImage(id, parent string) (string, error) { - img, err := daemon.imageStore.Get(image.ID(id)) + + var ( + 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 { return "", err } @@ -251,7 +273,7 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) { var parentImg *image.Image var parentChainID layer.ChainID if len(parent) != 0 { - parentImg, err = daemon.imageStore.Get(image.ID(parent)) + parentImg, err = daemon.stores[img.Platform()].imageStore.Get(image.ID(parent)) if err != nil { return "", errors.Wrap(err, "error getting specified parent layer") } @@ -261,11 +283,11 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) { parentImg = &image.Image{RootFS: rootFS} } - l, err := daemon.layerStore.Get(img.RootFS.ChainID()) + l, err := daemon.stores[img.Platform()].layerStore.Get(img.RootFS.ChainID()) if err != nil { return "", errors.Wrap(err, "error getting image layer") } - defer daemon.layerStore.Release(l) + defer daemon.stores[img.Platform()].layerStore.Release(l) ts, err := l.TarStreamFrom(parentChainID) if err != nil { @@ -273,17 +295,11 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) { } defer ts.Close() - // To support LCOW, Windows needs to pass the platform into the store when registering the layer. - platform := layer.Platform("") - if runtime.GOOS == "windows" { - platform = l.Platform() - } - - newL, err := daemon.layerStore.Register(ts, parentChainID, platform) + newL, err := daemon.stores[img.Platform()].layerStore.Register(ts, parentChainID, layer.Platform(img.Platform())) if err != nil { return "", errors.Wrap(err, "error registering layer") } - defer daemon.layerStore.Release(newL) + defer daemon.stores[img.Platform()].layerStore.Release(newL) var newImage image.Image newImage = *img @@ -320,7 +336,7 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) { return "", errors.Wrap(err, "error marshalling image config") } - newImgID, err := daemon.imageStore.Create(b) + newImgID, err := daemon.stores[img.Platform()].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 9dc61c1ea4..1923bce172 100644 --- a/daemon/import.go +++ b/daemon/import.go @@ -91,11 +91,11 @@ func (daemon *Daemon) ImportImage(src string, repository, tag string, msg string // but for Linux images, there's no reason it couldn't. However it // would need another CLI flag as there's no meta-data indicating // the OS of the thing being imported. - l, err := daemon.layerStore.Register(inflatedLayerData, "", "") + l, err := daemon.stores[runtime.GOOS].layerStore.Register(inflatedLayerData, "", "") if err != nil { return err } - defer layer.ReleaseAndLog(daemon.layerStore, l) + defer layer.ReleaseAndLog(daemon.stores[runtime.GOOS].layerStore, l) // TODO LCOW @jhowardmsft as for above comment created := time.Now().UTC() imgConfig, err := json.Marshal(&image.Image{ @@ -103,7 +103,7 @@ func (daemon *Daemon) ImportImage(src string, repository, tag string, msg string DockerVersion: dockerversion.Version, Config: config, Architecture: runtime.GOARCH, - OS: runtime.GOOS, + OS: runtime.GOOS, // TODO LCOW @jhowardmsft as for above commment Created: created, Comment: msg, }, @@ -120,14 +120,16 @@ func (daemon *Daemon) ImportImage(src string, repository, tag string, msg string return err } - id, err := daemon.imageStore.Create(imgConfig) + // TODO @jhowardmsft LCOW - Again, assume the OS of the host for now + id, err := daemon.stores[runtime.GOOS].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, newRef); err != nil { + // TODO @jhowardmsft LCOW - Again, assume the OS of the host for now + if err := daemon.TagImageWithReference(id, runtime.GOOS, newRef); err != nil { return err } } diff --git a/daemon/info.go b/daemon/info.go index 3c28cdf7f1..a11775c2e9 100644 --- a/daemon/info.go +++ b/daemon/info.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "runtime" + "strings" "time" "github.com/Sirupsen/logrus" @@ -77,15 +78,32 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) { securityOptions = append(securityOptions, "name=userns") } + imageCount := 0 + 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) + } + } + + // TODO @jhowardmsft LCOW support. For now, hard-code the platform shown for the driver status + p := runtime.GOOS + if p == "windows" && system.LCOWSupported() { + p = "linux" + } + + drivers = strings.TrimSpace(drivers) v := &types.Info{ ID: daemon.ID, Containers: int(cRunning + cPaused + cStopped), ContainersRunning: int(cRunning), ContainersPaused: int(cPaused), ContainersStopped: int(cStopped), - Images: len(daemon.imageStore.Map()), - Driver: daemon.GraphDriverName(), - DriverStatus: daemon.layerStore.DriverStatus(), + Images: imageCount, + Driver: drivers, + DriverStatus: daemon.stores[p].layerStore.DriverStatus(), Plugins: daemon.showPluginsInfo(), IPv4Forwarding: !sysInfo.IPv4ForwardingDisabled, BridgeNfIptables: !sysInfo.BridgeNFCallIPTablesDisabled, diff --git a/daemon/inspect.go b/daemon/inspect.go index 4eb1d091d3..c981e7701d 100644 --- a/daemon/inspect.go +++ b/daemon/inspect.go @@ -170,6 +170,7 @@ func (daemon *Daemon) getInspectData(container *container.Container) (*types.Con Name: container.Name, RestartCount: container.RestartCount, Driver: container.Driver, + Platform: container.Platform, MountLabel: container.MountLabel, ProcessLabel: container.ProcessLabel, ExecIDs: container.GetExecIDs(), diff --git a/daemon/list.go b/daemon/list.go index 4d831460f2..e6909173e9 100644 --- a/daemon/list.go +++ b/daemon/list.go @@ -317,7 +317,7 @@ func (daemon *Daemon) foldFilter(config *types.ContainerListOptions) (*listConte if psFilters.Include("ancestor") { ancestorFilter = true psFilters.WalkValues("ancestor", func(ancestor string) error { - id, err := daemon.GetImageID(ancestor) + id, platform, err := daemon.GetImageIDAndPlatform(ancestor) if err != nil { logrus.Warnf("Error while looking up for image %v", ancestor) return nil @@ -327,7 +327,7 @@ func (daemon *Daemon) foldFilter(config *types.ContainerListOptions) (*listConte return nil } // Then walk down the graph and put the imageIds in imagesFilter - populateImageFilterByParents(imagesFilter, id, daemon.imageStore.Children) + populateImageFilterByParents(imagesFilter, id, daemon.stores[platform].imageStore.Children) return nil }) } @@ -558,7 +558,7 @@ func (daemon *Daemon) transformContainer(container *container.Container, ctx *li image := container.Config.Image // if possible keep the original ref if image != container.ImageID.String() { - id, err := daemon.GetImageID(image) + id, _, err := daemon.GetImageIDAndPlatform(image) if _, isDNE := err.(ErrImageDoesNotExist); err != nil && !isDNE { return nil, err } diff --git a/daemon/prune.go b/daemon/prune.go index 602de618c9..561ac1e01e 100644 --- a/daemon/prune.go +++ b/daemon/prune.go @@ -3,6 +3,7 @@ package daemon import ( "fmt" "regexp" + "runtime" "sync/atomic" "time" @@ -14,6 +15,7 @@ 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" @@ -157,6 +159,12 @@ 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 platform == "windows" && system.LCOWSupported() { + platform = "linux" + } + if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) { return nil, errPruneRunning } @@ -186,9 +194,9 @@ func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args var allImages map[image.ID]*image.Image if danglingOnly { - allImages = daemon.imageStore.Heads() + allImages = daemon.stores[platform].imageStore.Heads() } else { - allImages = daemon.imageStore.Map() + allImages = daemon.stores[platform].imageStore.Map() } allContainers := daemon.List() imageRefs := map[string]bool{} @@ -202,7 +210,7 @@ func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args } // Filter intermediary images and get their unique size - allLayers := daemon.layerStore.Map() + allLayers := daemon.stores[platform].layerStore.Map() topImages := map[image.ID]*image.Image{} for id, img := range allImages { select { @@ -210,7 +218,7 @@ func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args return nil, ctx.Err() default: dgst := digest.Digest(id) - if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.imageStore.Children(id)) != 0 { + if len(daemon.stores[platform].referenceStore.References(dgst)) == 0 && len(daemon.stores[platform].imageStore.Children(id)) != 0 { continue } if !until.IsZero() && img.Created.After(until) { @@ -241,7 +249,7 @@ deleteImagesLoop: } deletedImages := []types.ImageDeleteResponseItem{} - refs := daemon.referenceStore.References(dgst) + refs := daemon.stores[platform].referenceStore.References(dgst) if len(refs) > 0 { shouldDelete := !danglingOnly if !shouldDelete { diff --git a/daemon/start.go b/daemon/start.go index eddb5d3d50..61bc32f586 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -207,7 +207,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.layerStore.GetMountID(container.ID); err == nil { + if mountid, err := daemon.stores[container.Platform].layerStore.GetMountID(container.ID); err == nil { daemon.cleanupMountsByID(mountid) } } diff --git a/daemon/start_windows.go b/daemon/start_windows.go index 2578c5f330..da70a16112 100644 --- a/daemon/start_windows.go +++ b/daemon/start_windows.go @@ -41,7 +41,7 @@ func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Contain layerOpts.LayerFolderPath = m["dir"] // Generate the layer paths of the layer options - img, err := daemon.imageStore.Get(container.ImageID) + img, err := daemon.stores[container.Platform].imageStore.Get(container.ImageID) if err != nil { return nil, fmt.Errorf("failed to graph.Get on ImageID %s - %s", container.ImageID, err) } @@ -49,9 +49,9 @@ func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Contain max := len(img.RootFS.DiffIDs) for i := 1; i <= max; i++ { img.RootFS.DiffIDs = img.RootFS.DiffIDs[:i] - layerPath, err := layer.GetLayerPath(daemon.layerStore, img.RootFS.ChainID()) + layerPath, err := layer.GetLayerPath(daemon.stores[container.Platform].layerStore, img.RootFS.ChainID()) if err != nil { - return nil, fmt.Errorf("failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.layerStore, img.RootFS.ChainID(), err) + return nil, fmt.Errorf("failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.stores[container.Platform].layerStore, img.RootFS.ChainID(), err) } // Reverse order, expecting parent most first layerOpts.LayerPaths = append([]string{layerPath}, layerOpts.LayerPaths...) diff --git a/distribution/metadata/metadata.go b/distribution/metadata/metadata.go index 05ba4f817d..3dae79555d 100644 --- a/distribution/metadata/metadata.go +++ b/distribution/metadata/metadata.go @@ -26,15 +26,17 @@ type Store interface { type FSMetadataStore struct { sync.RWMutex basePath string + platform string } // NewFSMetadataStore creates a new filesystem-based metadata store. -func NewFSMetadataStore(basePath string) (*FSMetadataStore, error) { +func NewFSMetadataStore(basePath, platform 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 556886581e..385901ec46 100644 --- a/distribution/metadata/v1_id_service_test.go +++ b/distribution/metadata/v1_id_service_test.go @@ -3,6 +3,7 @@ package metadata import ( "io/ioutil" "os" + "runtime" "testing" "github.com/docker/docker/layer" @@ -15,7 +16,7 @@ func TestV1IDService(t *testing.T) { } defer os.RemoveAll(tmpDir) - metadataStore, err := NewFSMetadataStore(tmpDir) + metadataStore, err := NewFSMetadataStore(tmpDir, runtime.GOOS) 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 8e3e4614c0..b5d59b2297 100644 --- a/distribution/metadata/v2_metadata_service_test.go +++ b/distribution/metadata/v2_metadata_service_test.go @@ -6,6 +6,7 @@ import ( "math/rand" "os" "reflect" + "runtime" "testing" "github.com/docker/docker/layer" @@ -19,7 +20,7 @@ func TestV2MetadataService(t *testing.T) { } defer os.RemoveAll(tmpDir) - metadataStore, err := NewFSMetadataStore(tmpDir) + metadataStore, err := NewFSMetadataStore(tmpDir, runtime.GOOS) if err != nil { t.Fatalf("could not create metadata store: %v", err) } diff --git a/distribution/xfer/download.go b/distribution/xfer/download.go index ca713c663a..6769ee1cdf 100644 --- a/distribution/xfer/download.go +++ b/distribution/xfer/download.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "io" + "runtime" "time" "github.com/Sirupsen/logrus" @@ -22,7 +23,7 @@ const maxDownloadAttempts = 5 // registers and downloads those, taking into account dependencies between // layers. type LayerDownloadManager struct { - layerStore layer.Store + layerStores map[string]layer.Store tm TransferManager waitDuration time.Duration } @@ -33,9 +34,9 @@ func (ldm *LayerDownloadManager) SetConcurrency(concurrency int) { } // NewLayerDownloadManager returns a new LayerDownloadManager. -func NewLayerDownloadManager(layerStore layer.Store, concurrencyLimit int, options ...func(*LayerDownloadManager)) *LayerDownloadManager { +func NewLayerDownloadManager(layerStores map[string]layer.Store, concurrencyLimit int, options ...func(*LayerDownloadManager)) *LayerDownloadManager { manager := LayerDownloadManager{ - layerStore: layerStore, + layerStores: layerStores, tm: NewTransferManager(concurrencyLimit), waitDuration: time.Second, } @@ -104,6 +105,11 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima downloadsByKey = make(map[string]*downloadTransfer) ) + // Assume that the platform is the host OS if blank + if platform == "" { + platform = layer.Platform(runtime.GOOS) + } + rootFS := initialRootFS for _, descriptor := range layers { key := descriptor.Key() @@ -115,13 +121,13 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima if err == nil { getRootFS := rootFS getRootFS.Append(diffID) - l, err := ldm.layerStore.Get(getRootFS.ChainID()) + l, err := ldm.layerStores[string(platform)].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.layerStore, topLayer) + layer.ReleaseAndLog(ldm.layerStores[string(platform)], topLayer) } topLayer = l missingLayer = false @@ -165,7 +171,7 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima if topDownload == nil { return rootFS, func() { if topLayer != nil { - layer.ReleaseAndLog(ldm.layerStore, topLayer) + layer.ReleaseAndLog(ldm.layerStores[string(platform)], topLayer) } }, nil } @@ -176,7 +182,7 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima defer func() { if topLayer != nil { - layer.ReleaseAndLog(ldm.layerStore, topLayer) + layer.ReleaseAndLog(ldm.layerStores[string(platform)], topLayer) } }() @@ -216,7 +222,7 @@ func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor, return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { d := &downloadTransfer{ Transfer: NewTransfer(), - layerStore: ldm.layerStore, + layerStore: ldm.layerStores[string(platform)], } go func() { @@ -380,7 +386,7 @@ func (ldm *LayerDownloadManager) makeDownloadFuncFromDownload(descriptor Downloa return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { d := &downloadTransfer{ Transfer: NewTransfer(), - layerStore: ldm.layerStore, + layerStore: ldm.layerStores[string(platform)], } go func() { diff --git a/reference/store.go b/reference/store.go index 8466e6e2cc..6599235401 100644 --- a/reference/store.go +++ b/reference/store.go @@ -46,6 +46,9 @@ type store struct { // referencesByIDCache is a cache of references indexed by ID, to speed // up References. referencesByIDCache map[digest.Digest]map[string]reference.Named + // platform is the container target platform for this store (which may be + // different to the host operating system + platform string } // Repository maps tags to digests. The key is a stringified Reference, @@ -70,7 +73,7 @@ func (a lexicalAssociations) Less(i, j int) bool { // NewReferenceStore creates a new reference store, tied to a file path where // the set of references are serialized in JSON format. -func NewReferenceStore(jsonPath string) (Store, error) { +func NewReferenceStore(jsonPath, platform string) (Store, error) { abspath, err := filepath.Abs(jsonPath) if err != nil { return nil, err @@ -80,6 +83,7 @@ func NewReferenceStore(jsonPath string) (Store, error) { jsonPath: abspath, Repositories: make(map[string]repository), referencesByIDCache: make(map[digest.Digest]map[string]reference.Named), + platform: platform, } // Load the json file if it exists, otherwise create it. if err := store.reload(); os.IsNotExist(err) { diff --git a/reference/store_test.go b/reference/store_test.go index 8f0ff6304e..2c796e76f9 100644 --- a/reference/store_test.go +++ b/reference/store_test.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "os" "path/filepath" + "runtime" "strings" "testing" @@ -40,7 +41,7 @@ func TestLoad(t *testing.T) { } jsonFile.Close() - store, err := NewReferenceStore(jsonFile.Name()) + store, err := NewReferenceStore(jsonFile.Name(), runtime.GOOS) if err != nil { t.Fatalf("error creating tag store: %v", err) } @@ -69,7 +70,7 @@ func TestSave(t *testing.T) { jsonFile.Close() defer os.RemoveAll(jsonFile.Name()) - store, err := NewReferenceStore(jsonFile.Name()) + store, err := NewReferenceStore(jsonFile.Name(), runtime.GOOS) if err != nil { t.Fatalf("error creating tag store: %v", err) } @@ -111,7 +112,7 @@ func TestAddDeleteGet(t *testing.T) { jsonFile.Close() defer os.RemoveAll(jsonFile.Name()) - store, err := NewReferenceStore(jsonFile.Name()) + store, err := NewReferenceStore(jsonFile.Name(), runtime.GOOS) if err != nil { t.Fatalf("error creating tag store: %v", err) } @@ -328,7 +329,7 @@ func TestInvalidTags(t *testing.T) { tmpDir, err := ioutil.TempDir("", "tag-store-test") defer os.RemoveAll(tmpDir) - store, err := NewReferenceStore(filepath.Join(tmpDir, "repositories.json")) + store, err := NewReferenceStore(filepath.Join(tmpDir, "repositories.json"), runtime.GOOS) if err != nil { t.Fatalf("error creating tag store: %v", err) }