LCOW: Move daemon stores to per platform

Signed-off-by: John Howard <jhoward@microsoft.com>
This commit is contained in:
John Howard 2017-05-16 16:56:56 -07:00
parent 6c33684987
commit 3aa4a00715
40 changed files with 448 additions and 269 deletions

View file

@ -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

View file

@ -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))

View file

@ -320,6 +320,7 @@ type ContainerJSONBase struct {
Name string
RestartCount int
Driver string
Platform string
MountLabel string
ProcessLabel string
AppArmorProfile string

View file

@ -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)

View file

@ -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
}

View file

@ -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)

View file

@ -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)

View file

@ -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
}

View file

@ -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
}

View file

@ -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

View file

@ -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
}

View file

@ -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 {

View file

@ -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
}

View file

@ -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)
}
}

View file

@ -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())
}
}
}

View file

@ -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

View file

@ -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"
)

View file

@ -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,

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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))
}

View file

@ -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

View file

@ -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,

View file

@ -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,
}

View file

@ -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
}

View file

@ -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")
}

View file

@ -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
}
}

View file

@ -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,

View file

@ -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(),

View file

@ -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
}

View file

@ -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 {

View file

@ -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)
}
}

View file

@ -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...)

View file

@ -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
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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() {

View file

@ -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) {

View file

@ -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)
}