1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #36240 from dnephin/investigate-image-component

Extract ImageService from Daemon
This commit is contained in:
Victor Vieux 2018-02-27 14:35:01 -08:00 committed by GitHub
commit 6cb75dd5b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 790 additions and 528 deletions

View file

@ -253,6 +253,7 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) {
Root: cli.Config.Root, Root: cli.Config.Root,
Name: name, Name: name,
Backend: d, Backend: d,
ImageBackend: d.ImageService(),
PluginBackend: d.PluginManager(), PluginBackend: d.PluginManager(),
NetworkSubnetsProvider: d, NetworkSubnetsProvider: d,
DefaultAdvertiseAddr: cli.Config.SwarmDefaultAdvertiseAddr, DefaultAdvertiseAddr: cli.Config.SwarmDefaultAdvertiseAddr,
@ -345,12 +346,12 @@ func newRouterOptions(config *config.Config, daemon *daemon.Daemon) (routerOptio
return opts, errors.Wrap(err, "failed to create fscache") return opts, errors.Wrap(err, "failed to create fscache")
} }
manager, err := dockerfile.NewBuildManager(daemon, sm, buildCache, daemon.IDMappings()) manager, err := dockerfile.NewBuildManager(daemon.BuilderBackend(), sm, buildCache, daemon.IDMappings())
if err != nil { if err != nil {
return opts, err return opts, err
} }
bb, err := buildbackend.NewBackend(daemon, manager, buildCache) bb, err := buildbackend.NewBackend(daemon.ImageService(), manager, buildCache)
if err != nil { if err != nil {
return opts, errors.Wrap(err, "failed to create buildmanager") return opts, errors.Wrap(err, "failed to create buildmanager")
} }
@ -507,14 +508,14 @@ func initRouter(opts routerOptions) {
// we need to add the checkpoint router before the container router or the DELETE gets masked // we need to add the checkpoint router before the container router or the DELETE gets masked
checkpointrouter.NewRouter(opts.daemon, decoder), checkpointrouter.NewRouter(opts.daemon, decoder),
container.NewRouter(opts.daemon, decoder), container.NewRouter(opts.daemon, decoder),
image.NewRouter(opts.daemon), image.NewRouter(opts.daemon.ImageService()),
systemrouter.NewRouter(opts.daemon, opts.cluster, opts.buildCache), systemrouter.NewRouter(opts.daemon, opts.cluster, opts.buildCache),
volume.NewRouter(opts.daemon), volume.NewRouter(opts.daemon),
build.NewRouter(opts.buildBackend, opts.daemon), build.NewRouter(opts.buildBackend, opts.daemon),
sessionrouter.NewRouter(opts.sessionManager), sessionrouter.NewRouter(opts.sessionManager),
swarmrouter.NewRouter(opts.cluster), swarmrouter.NewRouter(opts.cluster),
pluginrouter.NewRouter(opts.daemon.PluginManager()), pluginrouter.NewRouter(opts.daemon.PluginManager()),
distributionrouter.NewRouter(opts.daemon), distributionrouter.NewRouter(opts.daemon.ImageService()),
} }
if opts.daemon.NetworkControllerEnabled() { if opts.daemon.NetworkControllerEnabled() {

View file

@ -83,6 +83,7 @@ type Config struct {
Root string Root string
Name string Name string
Backend executorpkg.Backend Backend executorpkg.Backend
ImageBackend executorpkg.ImageBackend
PluginBackend plugin.Backend PluginBackend plugin.Backend
NetworkSubnetsProvider NetworkSubnetsProvider NetworkSubnetsProvider NetworkSubnetsProvider

View file

@ -31,7 +31,6 @@ type Backend interface {
FindNetwork(idName string) (libnetwork.Network, error) FindNetwork(idName string) (libnetwork.Network, error)
SetupIngress(clustertypes.NetworkCreateRequest, string) (<-chan struct{}, error) SetupIngress(clustertypes.NetworkCreateRequest, string) (<-chan struct{}, error)
ReleaseIngress() (<-chan struct{}, error) ReleaseIngress() (<-chan struct{}, error)
PullImage(ctx context.Context, image, tag, platform string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
CreateManagedContainer(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error) CreateManagedContainer(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error)
ContainerStart(name string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error ContainerStart(name string, hostConfig *container.HostConfig, checkpoint string, checkpointDir string) error
ContainerStop(name string, seconds *int) error ContainerStop(name string, seconds *int) error
@ -58,9 +57,14 @@ type Backend interface {
UnsubscribeFromEvents(listener chan interface{}) UnsubscribeFromEvents(listener chan interface{})
UpdateAttachment(string, string, string, *network.NetworkingConfig) error UpdateAttachment(string, string, string, *network.NetworkingConfig) error
WaitForDetachment(context.Context, string, string, string, string) error WaitForDetachment(context.Context, string, string, string, string) error
GetRepository(context.Context, reference.Named, *types.AuthConfig) (distribution.Repository, bool, error)
LookupImage(name string) (*types.ImageInspect, error)
PluginManager() *plugin.Manager PluginManager() *plugin.Manager
PluginGetter() *plugin.Store PluginGetter() *plugin.Store
GetAttachmentStore() *networkSettings.AttachmentStore GetAttachmentStore() *networkSettings.AttachmentStore
} }
// ImageBackend is used by an executor to perform image operations
type ImageBackend interface {
PullImage(ctx context.Context, image, tag, platform string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
GetRepository(context.Context, reference.Named, *types.AuthConfig) (distribution.Repository, bool, error)
LookupImage(name string) (*types.ImageInspect, error)
}

View file

@ -36,11 +36,12 @@ import (
// containerConfig. // containerConfig.
type containerAdapter struct { type containerAdapter struct {
backend executorpkg.Backend backend executorpkg.Backend
imageBackend executorpkg.ImageBackend
container *containerConfig container *containerConfig
dependencies exec.DependencyGetter dependencies exec.DependencyGetter
} }
func newContainerAdapter(b executorpkg.Backend, task *api.Task, node *api.NodeDescription, dependencies exec.DependencyGetter) (*containerAdapter, error) { func newContainerAdapter(b executorpkg.Backend, i executorpkg.ImageBackend, task *api.Task, node *api.NodeDescription, dependencies exec.DependencyGetter) (*containerAdapter, error) {
ctnr, err := newContainerConfig(task, node) ctnr, err := newContainerConfig(task, node)
if err != nil { if err != nil {
return nil, err return nil, err
@ -49,6 +50,7 @@ func newContainerAdapter(b executorpkg.Backend, task *api.Task, node *api.NodeDe
return &containerAdapter{ return &containerAdapter{
container: ctnr, container: ctnr,
backend: b, backend: b,
imageBackend: i,
dependencies: dependencies, dependencies: dependencies,
}, nil }, nil
} }
@ -66,7 +68,7 @@ func (c *containerAdapter) pullImage(ctx context.Context) error {
named, err := reference.ParseNormalizedNamed(spec.Image) named, err := reference.ParseNormalizedNamed(spec.Image)
if err == nil { if err == nil {
if _, ok := named.(reference.Canonical); ok { if _, ok := named.(reference.Canonical); ok {
_, err := c.backend.LookupImage(spec.Image) _, err := c.imageBackend.LookupImage(spec.Image)
if err == nil { if err == nil {
return nil return nil
} }
@ -92,7 +94,7 @@ func (c *containerAdapter) pullImage(ctx context.Context) error {
// TODO @jhowardmsft LCOW Support: This will need revisiting as // TODO @jhowardmsft LCOW Support: This will need revisiting as
// the stack is built up to include LCOW support for swarm. // the stack is built up to include LCOW support for swarm.
platform := runtime.GOOS platform := runtime.GOOS
err := c.backend.PullImage(ctx, c.container.image(), "", platform, metaHeaders, authConfig, pw) err := c.imageBackend.PullImage(ctx, c.container.image(), "", platform, metaHeaders, authConfig, pw)
pw.CloseWithError(err) pw.CloseWithError(err)
}() }()

View file

@ -20,8 +20,8 @@ type networkAttacherController struct {
closed chan struct{} closed chan struct{}
} }
func newNetworkAttacherController(b executorpkg.Backend, task *api.Task, node *api.NodeDescription, dependencies exec.DependencyGetter) (*networkAttacherController, error) { func newNetworkAttacherController(b executorpkg.Backend, i executorpkg.ImageBackend, task *api.Task, node *api.NodeDescription, dependencies exec.DependencyGetter) (*networkAttacherController, error) {
adapter, err := newContainerAdapter(b, task, node, dependencies) adapter, err := newContainerAdapter(b, i, task, node, dependencies)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -40,8 +40,8 @@ type controller struct {
var _ exec.Controller = &controller{} var _ exec.Controller = &controller{}
// NewController returns a docker exec runner for the provided task. // NewController returns a docker exec runner for the provided task.
func newController(b executorpkg.Backend, task *api.Task, node *api.NodeDescription, dependencies exec.DependencyGetter) (*controller, error) { func newController(b executorpkg.Backend, i executorpkg.ImageBackend, task *api.Task, node *api.NodeDescription, dependencies exec.DependencyGetter) (*controller, error) {
adapter, err := newContainerAdapter(b, task, node, dependencies) adapter, err := newContainerAdapter(b, i, task, node, dependencies)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -26,6 +26,7 @@ import (
type executor struct { type executor struct {
backend executorpkg.Backend backend executorpkg.Backend
imageBackend executorpkg.ImageBackend
pluginBackend plugin.Backend pluginBackend plugin.Backend
dependencies exec.DependencyManager dependencies exec.DependencyManager
mutex sync.Mutex // This mutex protects the following node field mutex sync.Mutex // This mutex protects the following node field
@ -33,10 +34,11 @@ type executor struct {
} }
// NewExecutor returns an executor from the docker client. // NewExecutor returns an executor from the docker client.
func NewExecutor(b executorpkg.Backend, p plugin.Backend) exec.Executor { func NewExecutor(b executorpkg.Backend, p plugin.Backend, i executorpkg.ImageBackend) exec.Executor {
return &executor{ return &executor{
backend: b, backend: b,
pluginBackend: p, pluginBackend: p,
imageBackend: i,
dependencies: agent.NewDependencyManager(), dependencies: agent.NewDependencyManager(),
} }
} }
@ -200,7 +202,7 @@ func (e *executor) Controller(t *api.Task) (exec.Controller, error) {
e.mutex.Unlock() e.mutex.Unlock()
if t.Spec.GetAttachment() != nil { if t.Spec.GetAttachment() != nil {
return newNetworkAttacherController(e.backend, t, nodeDescription, dependencyGetter) return newNetworkAttacherController(e.backend, e.imageBackend, t, nodeDescription, dependencyGetter)
} }
var ctlr exec.Controller var ctlr exec.Controller
@ -229,7 +231,7 @@ func (e *executor) Controller(t *api.Task) (exec.Controller, error) {
return ctlr, fmt.Errorf("unsupported runtime type: %q", runtimeKind) return ctlr, fmt.Errorf("unsupported runtime type: %q", runtimeKind)
} }
case *api.TaskSpec_Container: case *api.TaskSpec_Container:
c, err := newController(e.backend, t, nodeDescription, dependencyGetter) c, err := newController(e.backend, e.imageBackend, t, nodeDescription, dependencyGetter)
if err != nil { if err != nil {
return ctlr, err return ctlr, err
} }

View file

@ -52,7 +52,7 @@ func TestHealthStates(t *testing.T) {
EventsService: e, EventsService: e,
} }
controller, err := newController(daemon, task, nil, nil) controller, err := newController(daemon, nil, task, nil, nil)
if err != nil { if err != nil {
t.Fatalf("create controller fail %v", err) t.Fatalf("create controller fail %v", err)
} }

View file

@ -12,7 +12,7 @@ import (
) )
func newTestControllerWithMount(m api.Mount) (*controller, error) { func newTestControllerWithMount(m api.Mount) (*controller, error) {
return newController(&daemon.Daemon{}, &api.Task{ return newController(&daemon.Daemon{}, nil, &api.Task{
ID: stringid.GenerateRandomID(), ID: stringid.GenerateRandomID(),
ServiceID: stringid.GenerateRandomID(), ServiceID: stringid.GenerateRandomID(),
Spec: api.TaskSpec{ Spec: api.TaskSpec{

View file

@ -120,12 +120,15 @@ func (n *nodeRunner) start(conf nodeStartConfig) error {
JoinAddr: joinAddr, JoinAddr: joinAddr,
StateDir: n.cluster.root, StateDir: n.cluster.root,
JoinToken: conf.joinToken, JoinToken: conf.joinToken,
Executor: container.NewExecutor(n.cluster.config.Backend, n.cluster.config.PluginBackend), Executor: container.NewExecutor(
HeartbeatTick: 1, n.cluster.config.Backend,
ElectionTick: 3, n.cluster.config.PluginBackend,
UnlockKey: conf.lockKey, n.cluster.config.ImageBackend),
AutoLockManagers: conf.autolock, HeartbeatTick: 1,
PluginGetter: n.cluster.config.Backend.PluginGetter(), ElectionTick: 3,
UnlockKey: conf.lockKey,
AutoLockManagers: conf.autolock,
PluginGetter: n.cluster.config.Backend.PluginGetter(),
} }
if conf.availability != "" { if conf.availability != "" {
avail, ok := swarmapi.NodeSpec_Availability_value[strings.ToUpper(string(conf.availability))] avail, ok := swarmapi.NodeSpec_Availability_value[strings.ToUpper(string(conf.availability))]

View file

@ -570,7 +570,7 @@ func (c *Cluster) imageWithDigestString(ctx context.Context, image string, authC
return "", errors.Errorf("image reference not tagged: %s", image) return "", errors.Errorf("image reference not tagged: %s", image)
} }
repo, _, err := c.config.Backend.GetRepository(ctx, taggedRef, authConfig) repo, _, err := c.config.ImageBackend.GetRepository(ctx, taggedRef, authConfig)
if err != nil { if err != nil {
return "", err return "", err
} }

View file

@ -155,7 +155,7 @@ func (daemon *Daemon) CreateImageFromContainer(name string, c *backend.CreateIma
return "", err return "", err
} }
id, err := daemon.commitImage(backend.CommitConfig{ id, err := daemon.imageService.CommitImage(backend.CommitConfig{
Author: c.Author, Author: c.Author,
Comment: c.Comment, Comment: c.Comment,
Config: newConfig, Config: newConfig,
@ -171,7 +171,7 @@ func (daemon *Daemon) CreateImageFromContainer(name string, c *backend.CreateIma
var imageRef string var imageRef string
if c.Repo != "" { if c.Repo != "" {
imageRef, err = daemon.TagImage(string(id), c.Repo, c.Tag) imageRef, err = daemon.imageService.TagImage(string(id), c.Repo, c.Tag)
if err != nil { if err != nil {
return "", err return "", err
} }

View file

@ -158,7 +158,7 @@ func (daemon *Daemon) newContainer(name string, operatingSystem string, config *
base.ImageID = imgID base.ImageID = imgID
base.NetworkSettings = &network.Settings{IsAnonymousEndpoint: noExplicitName} base.NetworkSettings = &network.Settings{IsAnonymousEndpoint: noExplicitName}
base.Name = name base.Name = name
base.Driver = daemon.GraphDriverName(operatingSystem) base.Driver = daemon.imageService.GraphDriverForOS(operatingSystem)
base.OS = operatingSystem base.OS = operatingSystem
return base, err return base, err
} }

View file

@ -64,12 +64,6 @@ func (daemon *Daemon) setupConfigDir(c *container.Container) (setupErr error) {
return nil return nil
} }
// getSize returns real size & virtual size
func (daemon *Daemon) getSize(containerID string) (int64, int64) {
// TODO Windows
return 0, 0
}
func (daemon *Daemon) setupIpcDirs(container *container.Container) error { func (daemon *Daemon) setupIpcDirs(container *container.Container) error {
return nil return nil
} }

View file

@ -15,7 +15,6 @@ import (
"github.com/docker/docker/container" "github.com/docker/docker/container"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
"github.com/docker/docker/image" "github.com/docker/docker/image"
"github.com/docker/docker/layer"
"github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/system"
@ -42,7 +41,7 @@ func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, manage
os := runtime.GOOS os := runtime.GOOS
if params.Config.Image != "" { if params.Config.Image != "" {
img, err := daemon.GetImage(params.Config.Image) img, err := daemon.imageService.GetImage(params.Config.Image)
if err == nil { if err == nil {
os = img.OS os = img.OS
} }
@ -92,7 +91,7 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (
os := runtime.GOOS os := runtime.GOOS
if params.Config.Image != "" { if params.Config.Image != "" {
img, err = daemon.GetImage(params.Config.Image) img, err = daemon.imageService.GetImage(params.Config.Image)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -158,9 +157,11 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (
} }
// Set RWLayer for container after mount labels have been set // Set RWLayer for container after mount labels have been set
if err := daemon.setRWLayer(container); err != nil { rwLayer, err := daemon.imageService.CreateLayer(container, setupInitLayer(daemon.idMappings))
if err != nil {
return nil, errdefs.System(err) return nil, errdefs.System(err)
} }
container.RWLayer = rwLayer
rootIDs := daemon.idMappings.RootPair() rootIDs := daemon.idMappings.RootPair()
if err := idtools.MkdirAndChown(container.Root, 0700, rootIDs); err != nil { if err := idtools.MkdirAndChown(container.Root, 0700, rootIDs); err != nil {
@ -254,33 +255,6 @@ func (daemon *Daemon) generateSecurityOpt(hostConfig *containertypes.HostConfig)
return nil, nil return nil, nil
} }
func (daemon *Daemon) setRWLayer(container *container.Container) error {
var layerID layer.ChainID
if container.ImageID != "" {
img, err := daemon.imageStore.Get(container.ImageID)
if err != nil {
return err
}
layerID = img.RootFS.ChainID()
}
rwLayerOpts := &layer.CreateRWLayerOpts{
MountLabel: container.MountLabel,
InitFunc: setupInitLayer(daemon.idMappings),
StorageOpt: container.HostConfig.StorageOpt,
}
// Indexing by OS is safe here as validation of OS has already been performed in create() (the only
// caller), and guaranteed non-nil
rwLayer, err := daemon.layerStores[container.OS].CreateRWLayer(container.ID, layerID, rwLayerOpts)
if err != nil {
return err
}
container.RWLayer = rwLayer
return nil
}
// VolumeCreate creates a volume with the specified name, driver, and opts // VolumeCreate creates a volume with the specified name, driver, and opts
// This is called directly from the Engine API // This is called directly from the Engine API
func (daemon *Daemon) VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error) { func (daemon *Daemon) VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error) {

View file

@ -21,11 +21,13 @@ import (
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
containertypes "github.com/docker/docker/api/types/container" containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/builder"
"github.com/docker/docker/container" "github.com/docker/docker/container"
"github.com/docker/docker/daemon/config" "github.com/docker/docker/daemon/config"
"github.com/docker/docker/daemon/discovery" "github.com/docker/docker/daemon/discovery"
"github.com/docker/docker/daemon/events" "github.com/docker/docker/daemon/events"
"github.com/docker/docker/daemon/exec" "github.com/docker/docker/daemon/exec"
"github.com/docker/docker/daemon/images"
"github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/logger"
"github.com/docker/docker/daemon/network" "github.com/docker/docker/daemon/network"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
@ -34,7 +36,6 @@ import (
_ "github.com/docker/docker/daemon/graphdriver/register" _ "github.com/docker/docker/daemon/graphdriver/register"
"github.com/docker/docker/daemon/stats" "github.com/docker/docker/daemon/stats"
dmetadata "github.com/docker/docker/distribution/metadata" dmetadata "github.com/docker/docker/distribution/metadata"
"github.com/docker/docker/distribution/xfer"
"github.com/docker/docker/dockerversion" "github.com/docker/docker/dockerversion"
"github.com/docker/docker/image" "github.com/docker/docker/image"
"github.com/docker/docker/layer" "github.com/docker/docker/layer"
@ -57,7 +58,6 @@ import (
"github.com/docker/libnetwork" "github.com/docker/libnetwork"
"github.com/docker/libnetwork/cluster" "github.com/docker/libnetwork/cluster"
nwconfig "github.com/docker/libnetwork/config" nwconfig "github.com/docker/libnetwork/config"
"github.com/docker/libtrust"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -70,44 +70,38 @@ var (
// Daemon holds information about the Docker daemon. // Daemon holds information about the Docker daemon.
type Daemon struct { type Daemon struct {
ID string ID string
repository string repository string
containers container.Store containers container.Store
containersReplica container.ViewDB containersReplica container.ViewDB
execCommands *exec.Store execCommands *exec.Store
downloadManager *xfer.LayerDownloadManager imageService *images.ImageService
uploadManager *xfer.LayerUploadManager idIndex *truncindex.TruncIndex
trustKey libtrust.PrivateKey configStore *config.Config
idIndex *truncindex.TruncIndex statsCollector *stats.Collector
configStore *config.Config defaultLogConfig containertypes.LogConfig
statsCollector *stats.Collector RegistryService registry.Service
defaultLogConfig containertypes.LogConfig EventsService *events.Events
RegistryService registry.Service netController libnetwork.NetworkController
EventsService *events.Events volumes *store.VolumeStore
netController libnetwork.NetworkController discoveryWatcher discovery.Reloader
volumes *store.VolumeStore root string
discoveryWatcher discovery.Reloader seccompEnabled bool
root string apparmorEnabled bool
seccompEnabled bool shutdown bool
apparmorEnabled bool idMappings *idtools.IDMappings
shutdown bool // TODO: move graphDrivers field to an InfoService
idMappings *idtools.IDMappings graphDrivers map[string]string // By operating system
graphDrivers map[string]string // By operating system
referenceStore refstore.Store PluginStore *plugin.Store // todo: remove
imageStore image.Store pluginManager *plugin.Manager
imageRoot string linkIndex *linkIndex
layerStores map[string]layer.Store // By operating system containerd libcontainerd.Client
distributionMetadataStore dmetadata.Store defaultIsolation containertypes.Isolation // Default isolation mode on Windows
PluginStore *plugin.Store // todo: remove clusterProvider cluster.Provider
pluginManager *plugin.Manager cluster Cluster
linkIndex *linkIndex genericResources []swarm.GenericResource
containerd libcontainerd.Client metricsPluginListener net.Listener
containerdRemote libcontainerd.Remote
defaultIsolation containertypes.Isolation // Default isolation mode on Windows
clusterProvider cluster.Provider
cluster Cluster
genericResources []swarm.GenericResource
metricsPluginListener net.Listener
machineMemory uint64 machineMemory uint64
@ -162,7 +156,7 @@ func (daemon *Daemon) restore() error {
// Ignore the container if it does not support the current driver being used by the graph // Ignore the container if it does not support the current driver being used by the graph
currentDriverForContainerOS := daemon.graphDrivers[container.OS] currentDriverForContainerOS := daemon.graphDrivers[container.OS]
if (container.Driver == "" && currentDriverForContainerOS == "aufs") || container.Driver == currentDriverForContainerOS { if (container.Driver == "" && currentDriverForContainerOS == "aufs") || container.Driver == currentDriverForContainerOS {
rwlayer, err := daemon.layerStores[container.OS].GetRWLayer(container.ID) rwlayer, err := daemon.imageService.GetLayerByID(container.ID, container.OS)
if err != nil { if err != nil {
logrus.Errorf("Failed to load container mount %v: %v", id, err) logrus.Errorf("Failed to load container mount %v: %v", id, err)
continue continue
@ -705,7 +699,7 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
// be set through an environment variable, a daemon start parameter, or chosen through // be set through an environment variable, a daemon start parameter, or chosen through
// initialization of the layerstore through driver priority order for example. // initialization of the layerstore through driver priority order for example.
d.graphDrivers = make(map[string]string) d.graphDrivers = make(map[string]string)
d.layerStores = make(map[string]layer.Store) layerStores := make(map[string]layer.Store)
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
d.graphDrivers[runtime.GOOS] = "windowsfilter" d.graphDrivers[runtime.GOOS] = "windowsfilter"
if system.LCOWSupported() { if system.LCOWSupported() {
@ -754,7 +748,7 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
} }
for operatingSystem, gd := range d.graphDrivers { for operatingSystem, gd := range d.graphDrivers {
d.layerStores[operatingSystem], err = layer.NewStoreFromOptions(layer.StoreOptions{ layerStores[operatingSystem], err = layer.NewStoreFromOptions(layer.StoreOptions{
Root: config.Root, Root: config.Root,
MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"), MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"),
GraphDriver: gd, GraphDriver: gd,
@ -771,7 +765,7 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
// As layerstore initialization may set the driver // As layerstore initialization may set the driver
for os := range d.graphDrivers { for os := range d.graphDrivers {
d.graphDrivers[os] = d.layerStores[os].DriverName() d.graphDrivers[os] = layerStores[os].DriverName()
} }
// Configure and validate the kernels security support. Note this is a Linux/FreeBSD // Configure and validate the kernels security support. Note this is a Linux/FreeBSD
@ -780,22 +774,17 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
return nil, err return nil, err
} }
logrus.Debugf("Max Concurrent Downloads: %d", *config.MaxConcurrentDownloads) imageRoot := filepath.Join(config.Root, "image", d.graphDrivers[runtime.GOOS])
d.downloadManager = xfer.NewLayerDownloadManager(d.layerStores, *config.MaxConcurrentDownloads) ifs, err := image.NewFSStoreBackend(filepath.Join(imageRoot, "imagedb"))
logrus.Debugf("Max Concurrent Uploads: %d", *config.MaxConcurrentUploads)
d.uploadManager = xfer.NewLayerUploadManager(*config.MaxConcurrentUploads)
d.imageRoot = filepath.Join(config.Root, "image", d.graphDrivers[runtime.GOOS])
ifs, err := image.NewFSStoreBackend(filepath.Join(d.imageRoot, "imagedb"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
lgrMap := make(map[string]image.LayerGetReleaser) lgrMap := make(map[string]image.LayerGetReleaser)
for os, ls := range d.layerStores { for os, ls := range layerStores {
lgrMap[os] = ls lgrMap[os] = ls
} }
d.imageStore, err = image.NewImageStore(ifs, lgrMap) imageStore, err := image.NewImageStore(ifs, lgrMap)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -817,8 +806,6 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
return nil, err return nil, err
} }
eventsService := events.New()
// We have a single tag/reference store for the daemon globally. However, it's // We have a single tag/reference store for the daemon globally. However, it's
// stored under the graphdriver. On host platforms which only support a single // stored under the graphdriver. On host platforms which only support a single
// container OS, but multiple selectable graphdrivers, this means depending on which // container OS, but multiple selectable graphdrivers, this means depending on which
@ -829,14 +816,13 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
// operating systems, the list of graphdrivers available isn't user configurable. // operating systems, the list of graphdrivers available isn't user configurable.
// For backwards compatibility, we just put it under the windowsfilter // For backwards compatibility, we just put it under the windowsfilter
// directory regardless. // directory regardless.
refStoreLocation := filepath.Join(d.imageRoot, `repositories.json`) refStoreLocation := filepath.Join(imageRoot, `repositories.json`)
rs, err := refstore.NewReferenceStore(refStoreLocation) rs, err := refstore.NewReferenceStore(refStoreLocation)
if err != nil { if err != nil {
return nil, fmt.Errorf("Couldn't create reference store repository: %s", err) return nil, fmt.Errorf("Couldn't create reference store repository: %s", err)
} }
d.referenceStore = rs
d.distributionMetadataStore, err = dmetadata.NewFSMetadataStore(filepath.Join(d.imageRoot, "distribution")) distributionMetadataStore, err := dmetadata.NewFSMetadataStore(filepath.Join(imageRoot, "distribution"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -844,7 +830,7 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
// No content-addressability migration on Windows as it never supported pre-CA // No content-addressability migration on Windows as it never supported pre-CA
if runtime.GOOS != "windows" { if runtime.GOOS != "windows" {
migrationStart := time.Now() migrationStart := time.Now()
if err := v1.Migrate(config.Root, d.graphDrivers[runtime.GOOS], d.layerStores[runtime.GOOS], d.imageStore, rs, d.distributionMetadataStore); err != nil { if err := v1.Migrate(config.Root, d.graphDrivers[runtime.GOOS], layerStores[runtime.GOOS], imageStore, rs, distributionMetadataStore); err != nil {
logrus.Errorf("Graph migration failed: %q. Your old graph data was found to be too inconsistent for upgrading to content-addressable storage. Some of the old data was probably not upgraded. We recommend starting over with a clean storage directory if possible.", err) logrus.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())
@ -870,20 +856,34 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
return nil, err return nil, err
} }
d.execCommands = exec.NewStore() d.execCommands = exec.NewStore()
d.trustKey = trustKey
d.idIndex = truncindex.NewTruncIndex([]string{}) d.idIndex = truncindex.NewTruncIndex([]string{})
d.statsCollector = d.newStatsCollector(1 * time.Second) d.statsCollector = d.newStatsCollector(1 * time.Second)
d.EventsService = eventsService d.EventsService = events.New()
d.volumes = volStore d.volumes = volStore
d.root = config.Root d.root = config.Root
d.idMappings = idMappings d.idMappings = idMappings
d.seccompEnabled = sysInfo.Seccomp d.seccompEnabled = sysInfo.Seccomp
d.apparmorEnabled = sysInfo.AppArmor d.apparmorEnabled = sysInfo.AppArmor
d.containerdRemote = containerdRemote
d.linkIndex = newLinkIndex() d.linkIndex = newLinkIndex()
// TODO: imageStore, distributionMetadataStore, and ReferenceStore are only
// used above to run migration. They could be initialized in ImageService
// if migration is called from daemon/images. layerStore might move as well.
d.imageService = images.NewImageService(images.ImageServiceConfig{
ContainerStore: d.containers,
DistributionMetadataStore: distributionMetadataStore,
EventsService: d.EventsService,
ImageStore: imageStore,
LayerStores: layerStores,
MaxConcurrentDownloads: *config.MaxConcurrentDownloads,
MaxConcurrentUploads: *config.MaxConcurrentUploads,
ReferenceStore: rs,
RegistryService: registryService,
TrustKey: trustKey,
})
go d.execCommandGC() go d.execCommandGC()
d.containerd, err = containerdRemote.NewClient(ContainersNamespace, d) d.containerd, err = containerdRemote.NewClient(ContainersNamespace, d)
@ -1005,7 +1005,7 @@ func (daemon *Daemon) Shutdown() error {
logrus.Errorf("Stop container error: %v", err) logrus.Errorf("Stop container error: %v", err)
return return
} }
if mountid, err := daemon.layerStores[c.OS].GetMountID(c.ID); err == nil { if mountid, err := daemon.imageService.GetLayerMountID(c.ID, c.OS); err == nil {
daemon.cleanupMountsByID(mountid) daemon.cleanupMountsByID(mountid)
} }
logrus.Debugf("container stopped %s", c.ID) logrus.Debugf("container stopped %s", c.ID)
@ -1018,12 +1018,8 @@ func (daemon *Daemon) Shutdown() error {
} }
} }
for os, ls := range daemon.layerStores { if daemon.imageService != nil {
if ls != nil { daemon.imageService.Cleanup()
if err := ls.Cleanup(); err != nil {
logrus.Errorf("Error during layer Store.Cleanup(): %v %s", err, os)
}
}
} }
// If we are part of a cluster, clean up cluster's stuff // If we are part of a cluster, clean up cluster's stuff
@ -1064,7 +1060,7 @@ func (daemon *Daemon) Mount(container *container.Container) error {
if runtime.GOOS != "windows" { if runtime.GOOS != "windows" {
daemon.Unmount(container) daemon.Unmount(container)
return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')", return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')",
daemon.GraphDriverName(container.OS), container.ID, container.BaseFS, dir) daemon.imageService.GraphDriverForOS(container.OS), container.ID, container.BaseFS, dir)
} }
} }
container.BaseFS = dir // TODO: combine these fields container.BaseFS = dir // TODO: combine these fields
@ -1108,11 +1104,6 @@ func (daemon *Daemon) Subnets() ([]net.IPNet, []net.IPNet) {
return v4Subnets, v6Subnets return v4Subnets, v6Subnets
} }
// GraphDriverName returns the name of the graph driver used by the layer.Store
func (daemon *Daemon) GraphDriverName(os string) string {
return daemon.layerStores[os].DriverName()
}
// prepareTempDir prepares and returns the default directory to use // prepareTempDir prepares and returns the default directory to use
// for temporary files. // for temporary files.
// If it doesn't exist, it is created. If it exists, its content is removed. // If it doesn't exist, it is created. If it exists, its content is removed.
@ -1323,3 +1314,21 @@ func fixMemorySwappiness(resources *containertypes.Resources) {
func (daemon *Daemon) GetAttachmentStore() *network.AttachmentStore { func (daemon *Daemon) GetAttachmentStore() *network.AttachmentStore {
return &daemon.attachmentStore return &daemon.attachmentStore
} }
// IDMappings returns uid/gid mappings for the builder
func (daemon *Daemon) IDMappings() *idtools.IDMappings {
return daemon.idMappings
}
// ImageService returns the Daemon's ImageService
func (daemon *Daemon) ImageService() *images.ImageService {
return daemon.imageService
}
// BuilderBackend returns the backend used by builder
func (daemon *Daemon) BuilderBackend() builder.Backend {
return struct {
*Daemon
*images.ImageService
}{daemon, daemon.imageService}
}

View file

@ -10,7 +10,6 @@ import (
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/container" "github.com/docker/docker/container"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
"github.com/docker/docker/layer"
"github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/system"
"github.com/docker/docker/volume" "github.com/docker/docker/volume"
volumestore "github.com/docker/docker/volume/store" volumestore "github.com/docker/docker/volume/store"
@ -121,12 +120,11 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo
// When container creation fails and `RWLayer` has not been created yet, we // When container creation fails and `RWLayer` has not been created yet, we
// do not call `ReleaseRWLayer` // do not call `ReleaseRWLayer`
if container.RWLayer != nil { if container.RWLayer != nil {
metadata, err := daemon.layerStores[container.OS].ReleaseRWLayer(container.RWLayer) err := daemon.imageService.ReleaseLayer(container.RWLayer, container.OS)
layer.LogReleaseMetadata(metadata) if err != nil {
if err != nil && err != layer.ErrMountDoesNotExist && !os.IsNotExist(errors.Cause(err)) { err = errors.Wrapf(err, "container %s", container.ID)
e := errors.Wrapf(err, "driver %q failed to remove root filesystem for %s", daemon.GraphDriverName(container.OS), container.ID) container.SetRemovalError(err)
container.SetRemovalError(e) return err
return e
} }
container.RWLayer = nil container.RWLayer = nil
} }

View file

@ -8,34 +8,11 @@ import (
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
"github.com/docker/docker/layer"
"github.com/docker/docker/pkg/directory" "github.com/docker/docker/pkg/directory"
"github.com/docker/docker/volume" "github.com/docker/docker/volume"
"github.com/opencontainers/go-digest"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
func (daemon *Daemon) getLayerRefs() map[layer.ChainID]int {
tmpImages := daemon.imageStore.Map()
layerRefs := map[layer.ChainID]int{}
for id, img := range tmpImages {
dgst := digest.Digest(id)
if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.imageStore.Children(id)) != 0 {
continue
}
rootFS := *img.RootFS
rootFS.DiffIDs = nil
for _, id := range img.RootFS.DiffIDs {
rootFS.Append(id)
chid := rootFS.ChainID()
layerRefs[chid]++
}
}
return layerRefs
}
// SystemDiskUsage returns information about the daemon data disk usage // SystemDiskUsage returns information about the daemon data disk usage
func (daemon *Daemon) SystemDiskUsage(ctx context.Context) (*types.DiskUsage, error) { func (daemon *Daemon) SystemDiskUsage(ctx context.Context) (*types.DiskUsage, error) {
if !atomic.CompareAndSwapInt32(&daemon.diskUsageRunning, 0, 1) { if !atomic.CompareAndSwapInt32(&daemon.diskUsageRunning, 0, 1) {
@ -53,7 +30,7 @@ func (daemon *Daemon) SystemDiskUsage(ctx context.Context) (*types.DiskUsage, er
} }
// Get all top images with extra attributes // Get all top images with extra attributes
allImages, err := daemon.Images(filters.NewArgs(), false, true) allImages, err := daemon.imageService.Images(filters.NewArgs(), false, true)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to retrieve image list: %v", err) return nil, fmt.Errorf("failed to retrieve image list: %v", err)
} }
@ -93,28 +70,9 @@ func (daemon *Daemon) SystemDiskUsage(ctx context.Context) (*types.DiskUsage, er
return nil, err return nil, err
} }
// Get total layers size on disk allLayersSize, err := daemon.imageService.LayerDiskUsage(ctx)
var allLayersSize int64 if err != nil {
layerRefs := daemon.getLayerRefs() return nil, err
for _, ls := range daemon.layerStores {
allLayers := ls.Map()
for _, l := range allLayers {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
size, err := l.DiffSize()
if err == nil {
if _, ok := layerRefs[l.ChainID()]; ok {
allLayersSize += size
} else {
logrus.Warnf("found leaked image layer %v", l.ChainID())
}
} else {
logrus.Warnf("failed to get diff size for layer %v", l.ChainID())
}
}
}
} }
return &types.DiskUsage{ return &types.DiskUsage{

View file

@ -51,13 +51,13 @@ func (daemon *Daemon) containerExport(container *container.Container) (arch io.R
if !system.IsOSSupported(container.OS) { if !system.IsOSSupported(container.OS) {
return nil, fmt.Errorf("cannot export %s: %s ", container.ID, system.ErrNotSupportedOperatingSystem) return nil, fmt.Errorf("cannot export %s: %s ", container.ID, system.ErrNotSupportedOperatingSystem)
} }
rwlayer, err := daemon.layerStores[container.OS].GetRWLayer(container.ID) rwlayer, err := daemon.imageService.GetLayerByID(container.ID, container.OS)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer func() { defer func() {
if err != nil { if err != nil {
daemon.layerStores[container.OS].ReleaseRWLayer(rwlayer) daemon.imageService.ReleaseLayer(rwlayer, container.OS)
} }
}() }()
@ -78,7 +78,7 @@ func (daemon *Daemon) containerExport(container *container.Container) (arch io.R
arch = ioutils.NewReadCloserWrapper(archive, func() error { arch = ioutils.NewReadCloserWrapper(archive, func() error {
err := archive.Close() err := archive.Close()
rwlayer.Unmount() rwlayer.Unmount()
daemon.layerStores[container.OS].ReleaseRWLayer(rwlayer) daemon.imageService.ReleaseLayer(rwlayer, container.OS)
return err return err
}) })
daemon.LogContainerEvent(container, "export") daemon.LogContainerEvent(container, "export")

View file

@ -1,73 +0,0 @@
package daemon // import "github.com/docker/docker/daemon"
import (
"fmt"
"github.com/docker/distribution/reference"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/image"
)
// errImageDoesNotExist is error returned when no image can be found for a reference.
type errImageDoesNotExist struct {
ref reference.Reference
}
func (e errImageDoesNotExist) Error() string {
ref := e.ref
if named, ok := ref.(reference.Named); ok {
ref = reference.TagNameOnly(named)
}
return fmt.Sprintf("No such image: %s", reference.FamiliarString(ref))
}
func (e errImageDoesNotExist) NotFound() {}
// GetImageIDAndOS returns an image ID and operating system corresponding to the image referred to by
// refOrID.
func (daemon *Daemon) GetImageIDAndOS(refOrID string) (image.ID, string, error) {
ref, err := reference.ParseAnyReference(refOrID)
if err != nil {
return "", "", errdefs.InvalidParameter(err)
}
namedRef, ok := ref.(reference.Named)
if !ok {
digested, ok := ref.(reference.Digested)
if !ok {
return "", "", errImageDoesNotExist{ref}
}
id := image.IDFromDigest(digested.Digest())
if img, err := daemon.imageStore.Get(id); err == nil {
return id, img.OperatingSystem(), nil
}
return "", "", errImageDoesNotExist{ref}
}
if digest, err := daemon.referenceStore.Get(namedRef); err == nil {
// Search the image stores to get the operating system, defaulting to host OS.
id := image.IDFromDigest(digest)
if img, err := daemon.imageStore.Get(id); err == nil {
return id, img.OperatingSystem(), nil
}
}
// Search based on ID
if id, err := daemon.imageStore.Search(refOrID); err == nil {
img, err := daemon.imageStore.Get(id)
if err != nil {
return "", "", errImageDoesNotExist{ref}
}
return id, img.OperatingSystem(), nil
}
return "", "", errImageDoesNotExist{ref}
}
// GetImage returns an image corresponding to the image referred to by refOrID.
func (daemon *Daemon) GetImage(refOrID string) (*image.Image, error) {
imgID, _, err := daemon.GetImageIDAndOS(refOrID)
if err != nil {
return nil, err
}
return daemon.imageStore.Get(imgID)
}

View file

@ -1,29 +0,0 @@
package daemon // import "github.com/docker/docker/daemon"
import (
"github.com/docker/docker/api/types/events"
)
// LogImageEvent generates an event related to an image with only the default attributes.
func (daemon *Daemon) LogImageEvent(imageID, refName, action string) {
daemon.LogImageEventWithAttributes(imageID, refName, action, map[string]string{})
}
// LogImageEventWithAttributes generates an event related to an image with specific given attributes.
func (daemon *Daemon) LogImageEventWithAttributes(imageID, refName, action string, attributes map[string]string) {
img, err := daemon.GetImage(imageID)
if err == nil && img.Config != nil {
// image has not been removed yet.
// it could be missing if the event is `delete`.
copyAttributes(attributes, img.Config.Labels)
}
if refName != "" {
attributes["name"] = refName
}
actor := events.Actor{
ID: imageID,
Attributes: attributes,
}
daemon.EventsService.Log(action, events.ImageEventType, actor)
}

View file

@ -1,4 +1,4 @@
package daemon // import "github.com/docker/docker/daemon" package images // import "github.com/docker/docker/daemon/images"
import ( import (
"github.com/docker/docker/builder" "github.com/docker/docker/builder"
@ -7,15 +7,15 @@ import (
) )
// MakeImageCache creates a stateful image cache. // MakeImageCache creates a stateful image cache.
func (daemon *Daemon) MakeImageCache(sourceRefs []string) builder.ImageCache { func (i *ImageService) MakeImageCache(sourceRefs []string) builder.ImageCache {
if len(sourceRefs) == 0 { if len(sourceRefs) == 0 {
return cache.NewLocal(daemon.imageStore) return cache.NewLocal(i.imageStore)
} }
cache := cache.New(daemon.imageStore) cache := cache.New(i.imageStore)
for _, ref := range sourceRefs { for _, ref := range sourceRefs {
img, err := daemon.GetImage(ref) img, err := i.GetImage(ref)
if err != nil { if err != nil {
logrus.Warnf("Could not look up %s for cache resolution, skipping: %+v", ref, err) logrus.Warnf("Could not look up %s for cache resolution, skipping: %+v", ref, err)
continue continue

64
daemon/images/image.go Normal file
View file

@ -0,0 +1,64 @@
package images // import "github.com/docker/docker/daemon/images"
import (
"fmt"
"github.com/docker/distribution/reference"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/image"
)
// ErrImageDoesNotExist is error returned when no image can be found for a reference.
type ErrImageDoesNotExist struct {
ref reference.Reference
}
func (e ErrImageDoesNotExist) Error() string {
ref := e.ref
if named, ok := ref.(reference.Named); ok {
ref = reference.TagNameOnly(named)
}
return fmt.Sprintf("No such image: %s", reference.FamiliarString(ref))
}
// NotFound implements the NotFound interface
func (e ErrImageDoesNotExist) NotFound() {}
// GetImage returns an image corresponding to the image referred to by refOrID.
func (i *ImageService) GetImage(refOrID string) (*image.Image, error) {
ref, err := reference.ParseAnyReference(refOrID)
if err != nil {
return nil, errdefs.InvalidParameter(err)
}
namedRef, ok := ref.(reference.Named)
if !ok {
digested, ok := ref.(reference.Digested)
if !ok {
return nil, ErrImageDoesNotExist{ref}
}
id := image.IDFromDigest(digested.Digest())
if img, err := i.imageStore.Get(id); err == nil {
return img, nil
}
return nil, ErrImageDoesNotExist{ref}
}
if digest, err := i.referenceStore.Get(namedRef); err == nil {
// Search the image stores to get the operating system, defaulting to host OS.
id := image.IDFromDigest(digest)
if img, err := i.imageStore.Get(id); err == nil {
return img, nil
}
}
// Search based on ID
if id, err := i.imageStore.Search(refOrID); err == nil {
img, err := i.imageStore.Get(id)
if err != nil {
return nil, ErrImageDoesNotExist{ref}
}
return img, nil
}
return nil, ErrImageDoesNotExist{ref}
}

View file

@ -1,4 +1,4 @@
package daemon // import "github.com/docker/docker/daemon" package images // import "github.com/docker/docker/daemon/images"
import ( import (
"io" "io"
@ -10,7 +10,6 @@ import (
"github.com/docker/docker/image" "github.com/docker/docker/image"
"github.com/docker/docker/layer" "github.com/docker/docker/layer"
"github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/containerfs"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/system"
"github.com/docker/docker/registry" "github.com/docker/docker/registry"
@ -138,7 +137,7 @@ func newROLayerForImage(img *image.Image, layerStore layer.Store) (builder.ROLay
} }
// TODO: could this use the regular daemon PullImage ? // TODO: could this use the regular daemon PullImage ?
func (daemon *Daemon) pullForBuilder(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer, os string) (*image.Image, error) { func (i *ImageService) pullForBuilder(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer, os string) (*image.Image, error) {
ref, err := reference.ParseNormalizedNamed(name) ref, err := reference.ParseNormalizedNamed(name)
if err != nil { if err != nil {
return nil, err return nil, err
@ -148,7 +147,7 @@ func (daemon *Daemon) pullForBuilder(ctx context.Context, name string, authConfi
pullRegistryAuth := &types.AuthConfig{} pullRegistryAuth := &types.AuthConfig{}
if len(authConfigs) > 0 { if len(authConfigs) > 0 {
// The request came with a full auth config, use it // The request came with a full auth config, use it
repoInfo, err := daemon.RegistryService.ResolveRepository(ref) repoInfo, err := i.registryService.ResolveRepository(ref)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -157,26 +156,26 @@ func (daemon *Daemon) pullForBuilder(ctx context.Context, name string, authConfi
pullRegistryAuth = &resolvedConfig pullRegistryAuth = &resolvedConfig
} }
if err := daemon.pullImageWithReference(ctx, ref, os, nil, pullRegistryAuth, output); err != nil { if err := i.pullImageWithReference(ctx, ref, os, nil, pullRegistryAuth, output); err != nil {
return nil, err return nil, err
} }
return daemon.GetImage(name) return i.GetImage(name)
} }
// GetImageAndReleasableLayer returns an image and releaseable layer for a reference or ID. // GetImageAndReleasableLayer returns an image and releaseable layer for a reference or ID.
// Every call to GetImageAndReleasableLayer MUST call releasableLayer.Release() to prevent // Every call to GetImageAndReleasableLayer MUST call releasableLayer.Release() to prevent
// leaking of layers. // leaking of layers.
func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ROLayer, error) { func (i *ImageService) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ROLayer, error) {
if refOrID == "" { if refOrID == "" {
if !system.IsOSSupported(opts.OS) { if !system.IsOSSupported(opts.OS) {
return nil, nil, system.ErrNotSupportedOperatingSystem return nil, nil, system.ErrNotSupportedOperatingSystem
} }
layer, err := newROLayerForImage(nil, daemon.layerStores[opts.OS]) layer, err := newROLayerForImage(nil, i.layerStores[opts.OS])
return nil, layer, err return nil, layer, err
} }
if opts.PullOption != backend.PullOptionForcePull { if opts.PullOption != backend.PullOptionForcePull {
image, err := daemon.GetImage(refOrID) image, err := i.GetImage(refOrID)
if err != nil && opts.PullOption == backend.PullOptionNoPull { if err != nil && opts.PullOption == backend.PullOptionNoPull {
return nil, nil, err return nil, nil, err
} }
@ -185,41 +184,36 @@ func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID st
if !system.IsOSSupported(image.OperatingSystem()) { if !system.IsOSSupported(image.OperatingSystem()) {
return nil, nil, system.ErrNotSupportedOperatingSystem return nil, nil, system.ErrNotSupportedOperatingSystem
} }
layer, err := newROLayerForImage(image, daemon.layerStores[image.OperatingSystem()]) layer, err := newROLayerForImage(image, i.layerStores[image.OperatingSystem()])
return image, layer, err return image, layer, err
} }
} }
image, err := daemon.pullForBuilder(ctx, refOrID, opts.AuthConfig, opts.Output, opts.OS) image, err := i.pullForBuilder(ctx, refOrID, opts.AuthConfig, opts.Output, opts.OS)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
if !system.IsOSSupported(image.OperatingSystem()) { if !system.IsOSSupported(image.OperatingSystem()) {
return nil, nil, system.ErrNotSupportedOperatingSystem return nil, nil, system.ErrNotSupportedOperatingSystem
} }
layer, err := newROLayerForImage(image, daemon.layerStores[image.OperatingSystem()]) layer, err := newROLayerForImage(image, i.layerStores[image.OperatingSystem()])
return image, layer, err return image, layer, err
} }
// CreateImage creates a new image by adding a config and ID to the image store. // CreateImage creates a new image by adding a config and ID to the image store.
// This is similar to LoadImage() except that it receives JSON encoded bytes of // This is similar to LoadImage() except that it receives JSON encoded bytes of
// an image instead of a tar archive. // an image instead of a tar archive.
func (daemon *Daemon) CreateImage(config []byte, parent string) (builder.Image, error) { func (i *ImageService) CreateImage(config []byte, parent string) (builder.Image, error) {
id, err := daemon.imageStore.Create(config) id, err := i.imageStore.Create(config)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to create image") return nil, errors.Wrapf(err, "failed to create image")
} }
if parent != "" { if parent != "" {
if err := daemon.imageStore.SetParent(id, image.ID(parent)); err != nil { if err := i.imageStore.SetParent(id, image.ID(parent)); err != nil {
return nil, errors.Wrapf(err, "failed to set parent %s", parent) return nil, errors.Wrapf(err, "failed to set parent %s", parent)
} }
} }
return daemon.imageStore.Get(id) return i.imageStore.Get(id)
}
// IDMappings returns uid/gid mappings for the builder
func (daemon *Daemon) IDMappings() *idtools.IDMappings {
return daemon.idMappings
} }

View file

@ -1,4 +1,4 @@
package daemon // import "github.com/docker/docker/daemon" package images // import "github.com/docker/docker/daemon/images"
import ( import (
"encoding/json" "encoding/json"
@ -9,10 +9,12 @@ import (
"github.com/docker/docker/layer" "github.com/docker/docker/layer"
"github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/system"
"github.com/pkg/errors"
) )
func (daemon *Daemon) commitImage(c backend.CommitConfig) (image.ID, error) { // CommitImage creates a new image from a commit config
layerStore, ok := daemon.layerStores[c.ContainerOS] func (i *ImageService) CommitImage(c backend.CommitConfig) (image.ID, error) {
layerStore, ok := i.layerStores[c.ContainerOS]
if !ok { if !ok {
return "", system.ErrNotSupportedOperatingSystem return "", system.ErrNotSupportedOperatingSystem
} }
@ -31,7 +33,7 @@ func (daemon *Daemon) commitImage(c backend.CommitConfig) (image.ID, error) {
parent = new(image.Image) parent = new(image.Image)
parent.RootFS = image.NewRootFS() parent.RootFS = image.NewRootFS()
} else { } else {
parent, err = daemon.imageStore.Get(image.ID(c.ParentImageID)) parent, err = i.imageStore.Get(image.ID(c.ParentImageID))
if err != nil { if err != nil {
return "", err return "", err
} }
@ -56,13 +58,13 @@ func (daemon *Daemon) commitImage(c backend.CommitConfig) (image.ID, error) {
return "", err return "", err
} }
id, err := daemon.imageStore.Create(config) id, err := i.imageStore.Create(config)
if err != nil { if err != nil {
return "", err return "", err
} }
if c.ParentImageID != "" { if c.ParentImageID != "" {
if err := daemon.imageStore.SetParent(id, image.ID(c.ParentImageID)); err != nil { if err := i.imageStore.SetParent(id, image.ID(c.ParentImageID)); err != nil {
return "", err return "", err
} }
} }
@ -112,13 +114,14 @@ func exportContainerRw(layerStore layer.Store, id, mountLabel string) (arch io.R
// * it doesn't log a container commit event // * it doesn't log a container commit event
// //
// This is a temporary shim. Should be removed when builder stops using commit. // This is a temporary shim. Should be removed when builder stops using commit.
func (daemon *Daemon) CommitBuildStep(c backend.CommitConfig) (image.ID, error) { func (i *ImageService) CommitBuildStep(c backend.CommitConfig) (image.ID, error) {
container, err := daemon.GetContainer(c.ContainerID) container := i.containers.Get(c.ContainerID)
if err != nil { if container == nil {
return "", err // TODO: use typed error
return "", errors.Errorf("container not found: %s", c.ContainerID)
} }
c.ContainerMountLabel = container.MountLabel c.ContainerMountLabel = container.MountLabel
c.ContainerOS = container.OS c.ContainerOS = container.OS
c.ParentImageID = string(container.ImageID) c.ParentImageID = string(container.ImageID)
return daemon.commitImage(c) return i.CommitImage(c)
} }

View file

@ -1,4 +1,4 @@
package daemon // import "github.com/docker/docker/daemon" package images // import "github.com/docker/docker/daemon/images"
import ( import (
"fmt" "fmt"
@ -60,22 +60,24 @@ const (
// meaning any delete conflicts will cause the image to not be deleted and the // meaning any delete conflicts will cause the image to not be deleted and the
// conflict will not be reported. // conflict will not be reported.
// //
// FIXME: remove ImageDelete's dependency on Daemon, then move to the graph func (i *ImageService) ImageDelete(imageRef string, force, prune bool) ([]types.ImageDeleteResponseItem, error) {
// package. This would require that we no longer need the daemon to determine
// whether images are being used by a stopped or running container.
func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.ImageDeleteResponseItem, error) {
start := time.Now() start := time.Now()
records := []types.ImageDeleteResponseItem{} records := []types.ImageDeleteResponseItem{}
imgID, operatingSystem, err := daemon.GetImageIDAndOS(imageRef) img, err := i.GetImage(imageRef)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !system.IsOSSupported(operatingSystem) { if !system.IsOSSupported(img.OperatingSystem()) {
return nil, errors.Errorf("unable to delete image: %q", system.ErrNotSupportedOperatingSystem) return nil, errors.Errorf("unable to delete image: %q", system.ErrNotSupportedOperatingSystem)
} }
repoRefs := daemon.referenceStore.References(imgID.Digest()) imgID := img.ID()
repoRefs := i.referenceStore.References(imgID.Digest())
using := func(c *container.Container) bool {
return c.ImageID == imgID
}
var removedRepositoryRef bool var removedRepositoryRef bool
if !isImageIDPrefix(imgID.String(), imageRef) { if !isImageIDPrefix(imgID.String(), imageRef) {
@ -84,7 +86,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
// true, there are multiple repository references to this // true, there are multiple repository references to this
// image, or there are no containers using the given reference. // image, or there are no containers using the given reference.
if !force && isSingleReference(repoRefs) { if !force && isSingleReference(repoRefs) {
if container := daemon.getContainerUsingImage(imgID); container != nil { if container := i.containers.First(using); container != nil {
// If we removed the repository reference then // If we removed the repository reference then
// this image would remain "dangling" and since // this image would remain "dangling" and since
// we really want to avoid that the client must // we really want to avoid that the client must
@ -99,17 +101,17 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
return nil, err return nil, err
} }
parsedRef, err = daemon.removeImageRef(parsedRef) parsedRef, err = i.removeImageRef(parsedRef)
if err != nil { if err != nil {
return nil, err return nil, err
} }
untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)} untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
daemon.LogImageEvent(imgID.String(), imgID.String(), "untag") i.LogImageEvent(imgID.String(), imgID.String(), "untag")
records = append(records, untaggedRecord) records = append(records, untaggedRecord)
repoRefs = daemon.referenceStore.References(imgID.Digest()) repoRefs = i.referenceStore.References(imgID.Digest())
// If a tag reference was removed and the only remaining // If a tag reference was removed and the only remaining
// references to the same repository are digest references, // references to the same repository are digest references,
@ -127,7 +129,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
remainingRefs := []reference.Named{} remainingRefs := []reference.Named{}
for _, repoRef := range repoRefs { for _, repoRef := range repoRefs {
if _, repoRefIsCanonical := repoRef.(reference.Canonical); repoRefIsCanonical && parsedRef.Name() == repoRef.Name() { if _, repoRefIsCanonical := repoRef.(reference.Canonical); repoRefIsCanonical && parsedRef.Name() == repoRef.Name() {
if _, err := daemon.removeImageRef(repoRef); err != nil { if _, err := i.removeImageRef(repoRef); err != nil {
return records, err return records, err
} }
@ -157,25 +159,25 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
if !force { if !force {
c |= conflictSoft &^ conflictActiveReference c |= conflictSoft &^ conflictActiveReference
} }
if conflict := daemon.checkImageDeleteConflict(imgID, c); conflict != nil { if conflict := i.checkImageDeleteConflict(imgID, c); conflict != nil {
return nil, conflict return nil, conflict
} }
for _, repoRef := range repoRefs { for _, repoRef := range repoRefs {
parsedRef, err := daemon.removeImageRef(repoRef) parsedRef, err := i.removeImageRef(repoRef)
if err != nil { if err != nil {
return nil, err return nil, err
} }
untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)} untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
daemon.LogImageEvent(imgID.String(), imgID.String(), "untag") i.LogImageEvent(imgID.String(), imgID.String(), "untag")
records = append(records, untaggedRecord) records = append(records, untaggedRecord)
} }
} }
} }
if err := daemon.imageDeleteHelper(imgID, &records, force, prune, removedRepositoryRef); err != nil { if err := i.imageDeleteHelper(imgID, &records, force, prune, removedRepositoryRef); err != nil {
return nil, err return nil, err
} }
@ -223,26 +225,18 @@ func isImageIDPrefix(imageID, possiblePrefix string) bool {
return false return false
} }
// getContainerUsingImage returns a container that was created using the given
// imageID. Returns nil if there is no such container.
func (daemon *Daemon) getContainerUsingImage(imageID image.ID) *container.Container {
return daemon.containers.First(func(c *container.Container) bool {
return c.ImageID == imageID
})
}
// removeImageRef attempts to parse and remove the given image reference from // removeImageRef attempts to parse and remove the given image reference from
// this daemon's store of repository tag/digest references. The given // this daemon's store of repository tag/digest references. The given
// repositoryRef must not be an image ID but a repository name followed by an // 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 // optional tag or digest reference. If tag or digest is omitted, the default
// tag is used. Returns the resolved image reference and an error. // tag is used. Returns the resolved image reference and an error.
func (daemon *Daemon) removeImageRef(ref reference.Named) (reference.Named, error) { func (i *ImageService) removeImageRef(ref reference.Named) (reference.Named, error) {
ref = reference.TagNameOnly(ref) ref = reference.TagNameOnly(ref)
// Ignore the boolean value returned, as far as we're concerned, this // 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 // is an idempotent operation and it's okay if the reference didn't
// exist in the first place. // exist in the first place.
_, err := daemon.referenceStore.Delete(ref) _, err := i.referenceStore.Delete(ref)
return ref, err return ref, err
} }
@ -252,18 +246,18 @@ func (daemon *Daemon) removeImageRef(ref reference.Named) (reference.Named, erro
// on the first encountered error. Removed references are logged to this // on the first encountered error. Removed references are logged to this
// daemon's event service. An "Untagged" types.ImageDeleteResponseItem is added to the // daemon's event service. An "Untagged" types.ImageDeleteResponseItem is added to the
// given list of records. // given list of records.
func (daemon *Daemon) removeAllReferencesToImageID(imgID image.ID, records *[]types.ImageDeleteResponseItem) error { func (i *ImageService) removeAllReferencesToImageID(imgID image.ID, records *[]types.ImageDeleteResponseItem) error {
imageRefs := daemon.referenceStore.References(imgID.Digest()) imageRefs := i.referenceStore.References(imgID.Digest())
for _, imageRef := range imageRefs { for _, imageRef := range imageRefs {
parsedRef, err := daemon.removeImageRef(imageRef) parsedRef, err := i.removeImageRef(imageRef)
if err != nil { if err != nil {
return err return err
} }
untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)} untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
daemon.LogImageEvent(imgID.String(), imgID.String(), "untag") i.LogImageEvent(imgID.String(), imgID.String(), "untag")
*records = append(*records, untaggedRecord) *records = append(*records, untaggedRecord)
} }
@ -303,15 +297,15 @@ func (idc *imageDeleteConflict) Conflict() {}
// conflict is encountered, it will be returned immediately without deleting // conflict is encountered, it will be returned immediately without deleting
// the image. If quiet is true, any encountered conflicts will be ignored and // the image. If quiet is true, any encountered conflicts will be ignored and
// the function will return nil immediately without deleting the image. // 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 (i *ImageService) imageDeleteHelper(imgID image.ID, records *[]types.ImageDeleteResponseItem, force, prune, quiet bool) error {
// First, determine if this image has any conflicts. Ignore soft conflicts // First, determine if this image has any conflicts. Ignore soft conflicts
// if force is true. // if force is true.
c := conflictHard c := conflictHard
if !force { if !force {
c |= conflictSoft c |= conflictSoft
} }
if conflict := daemon.checkImageDeleteConflict(imgID, c); conflict != nil { if conflict := i.checkImageDeleteConflict(imgID, c); conflict != nil {
if quiet && (!daemon.imageIsDangling(imgID) || conflict.used) { if quiet && (!i.imageIsDangling(imgID) || conflict.used) {
// Ignore conflicts UNLESS the image is "dangling" or not being used in // Ignore conflicts UNLESS the image is "dangling" or not being used in
// which case we want the user to know. // which case we want the user to know.
return nil return nil
@ -322,23 +316,23 @@ func (daemon *Daemon) imageDeleteHelper(imgID image.ID, records *[]types.ImageDe
return conflict return conflict
} }
parent, err := daemon.imageStore.GetParent(imgID) parent, err := i.imageStore.GetParent(imgID)
if err != nil { if err != nil {
// There may be no parent // There may be no parent
parent = "" parent = ""
} }
// Delete all repository tag/digest references to this image. // Delete all repository tag/digest references to this image.
if err := daemon.removeAllReferencesToImageID(imgID, records); err != nil { if err := i.removeAllReferencesToImageID(imgID, records); err != nil {
return err return err
} }
removedLayers, err := daemon.imageStore.Delete(imgID) removedLayers, err := i.imageStore.Delete(imgID)
if err != nil { if err != nil {
return err return err
} }
daemon.LogImageEvent(imgID.String(), imgID.String(), "delete") i.LogImageEvent(imgID.String(), imgID.String(), "delete")
*records = append(*records, types.ImageDeleteResponseItem{Deleted: imgID.String()}) *records = append(*records, types.ImageDeleteResponseItem{Deleted: imgID.String()})
for _, removedLayer := range removedLayers { for _, removedLayer := range removedLayers {
*records = append(*records, types.ImageDeleteResponseItem{Deleted: removedLayer.ChainID.String()}) *records = append(*records, types.ImageDeleteResponseItem{Deleted: removedLayer.ChainID.String()})
@ -353,7 +347,7 @@ func (daemon *Daemon) imageDeleteHelper(imgID image.ID, records *[]types.ImageDe
// either running or stopped). // either running or stopped).
// Do not force prunings, but do so quietly (stopping on any encountered // Do not force prunings, but do so quietly (stopping on any encountered
// conflicts). // conflicts).
return daemon.imageDeleteHelper(parent, records, false, true, true) return i.imageDeleteHelper(parent, records, false, true, true)
} }
// checkImageDeleteConflict determines whether there are any conflicts // checkImageDeleteConflict determines whether there are any conflicts
@ -362,9 +356,9 @@ func (daemon *Daemon) imageDeleteHelper(imgID image.ID, records *[]types.ImageDe
// using the image. A soft conflict is any tags/digest referencing the given // using the image. A soft conflict is any tags/digest referencing the given
// image or any stopped container using the image. If ignoreSoftConflicts is // image or any stopped container using the image. If ignoreSoftConflicts is
// true, this function will not check for soft conflict conditions. // true, this function will not check for soft conflict conditions.
func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, mask conflictType) *imageDeleteConflict { func (i *ImageService) checkImageDeleteConflict(imgID image.ID, mask conflictType) *imageDeleteConflict {
// Check if the image has any descendant images. // Check if the image has any descendant images.
if mask&conflictDependentChild != 0 && len(daemon.imageStore.Children(imgID)) > 0 { if mask&conflictDependentChild != 0 && len(i.imageStore.Children(imgID)) > 0 {
return &imageDeleteConflict{ return &imageDeleteConflict{
hard: true, hard: true,
imgID: imgID, imgID: imgID,
@ -377,7 +371,7 @@ func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, mask conflictType
running := func(c *container.Container) bool { running := func(c *container.Container) bool {
return c.IsRunning() && c.ImageID == imgID return c.IsRunning() && c.ImageID == imgID
} }
if container := daemon.containers.First(running); container != nil { if container := i.containers.First(running); container != nil {
return &imageDeleteConflict{ return &imageDeleteConflict{
imgID: imgID, imgID: imgID,
hard: true, hard: true,
@ -388,7 +382,7 @@ func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, mask conflictType
} }
// Check if any repository tags/digest reference this image. // 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(i.referenceStore.References(imgID.Digest())) > 0 {
return &imageDeleteConflict{ return &imageDeleteConflict{
imgID: imgID, imgID: imgID,
message: "image is referenced in multiple repositories", message: "image is referenced in multiple repositories",
@ -400,7 +394,7 @@ func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, mask conflictType
stopped := func(c *container.Container) bool { stopped := func(c *container.Container) bool {
return !c.IsRunning() && c.ImageID == imgID return !c.IsRunning() && c.ImageID == imgID
} }
if container := daemon.containers.First(stopped); container != nil { if container := i.containers.First(stopped); container != nil {
return &imageDeleteConflict{ return &imageDeleteConflict{
imgID: imgID, imgID: imgID,
used: true, used: true,
@ -415,6 +409,6 @@ func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, mask conflictType
// imageIsDangling returns whether the given image is "dangling" which means // imageIsDangling returns whether the given image is "dangling" which means
// that there are no repository references to the given image and it has no // that there are no repository references to the given image and it has no
// child images. // child images.
func (daemon *Daemon) imageIsDangling(imgID image.ID) bool { func (i *ImageService) imageIsDangling(imgID image.ID) bool {
return !(len(daemon.referenceStore.References(imgID.Digest())) > 0 || len(daemon.imageStore.Children(imgID)) > 0) return !(len(i.referenceStore.References(imgID.Digest())) > 0 || len(i.imageStore.Children(imgID)) > 0)
} }

View file

@ -0,0 +1,39 @@
package images // import "github.com/docker/docker/daemon/images"
import (
"github.com/docker/docker/api/types/events"
)
// LogImageEvent generates an event related to an image with only the default attributes.
func (i *ImageService) LogImageEvent(imageID, refName, action string) {
i.LogImageEventWithAttributes(imageID, refName, action, map[string]string{})
}
// LogImageEventWithAttributes generates an event related to an image with specific given attributes.
func (i *ImageService) LogImageEventWithAttributes(imageID, refName, action string, attributes map[string]string) {
img, err := i.GetImage(imageID)
if err == nil && img.Config != nil {
// image has not been removed yet.
// it could be missing if the event is `delete`.
copyAttributes(attributes, img.Config.Labels)
}
if refName != "" {
attributes["name"] = refName
}
actor := events.Actor{
ID: imageID,
Attributes: attributes,
}
i.eventsService.Log(action, events.ImageEventType, actor)
}
// copyAttributes guarantees that labels are not mutated by event triggers.
func copyAttributes(attributes, labels map[string]string) {
if labels == nil {
return
}
for k, v := range labels {
attributes[k] = v
}
}

View file

@ -1,4 +1,4 @@
package daemon // import "github.com/docker/docker/daemon" package images // import "github.com/docker/docker/daemon/images"
import ( import (
"io" "io"
@ -11,15 +11,15 @@ import (
// stream. All images with the given tag and all versions containing // stream. All images with the given tag and all versions containing
// the same tag are exported. names is the set of tags to export, and // the same tag are exported. names is the set of tags to export, and
// outStream is the writer which the images are written to. // outStream is the writer which the images are written to.
func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error { func (i *ImageService) ExportImage(names []string, outStream io.Writer) error {
imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStores, daemon.referenceStore, daemon) imageExporter := tarexport.NewTarExporter(i.imageStore, i.layerStores, i.referenceStore, i)
return imageExporter.Save(names, outStream) return imageExporter.Save(names, outStream)
} }
// LoadImage uploads a set of images into the repository. This is the // LoadImage uploads a set of images into the repository. This is the
// complement of ImageExport. The input stream is an uncompressed tar // complement of ImageExport. The input stream is an uncompressed tar
// ball containing images and metadata. // ball containing images and metadata.
func (daemon *Daemon) LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error { func (i *ImageService) LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error {
imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStores, daemon.referenceStore, daemon) imageExporter := tarexport.NewTarExporter(i.imageStore, i.layerStores, i.referenceStore, i)
return imageExporter.Load(inTar, outStream, quiet) return imageExporter.Load(inTar, outStream, quiet)
} }

View file

@ -1,4 +1,4 @@
package daemon // import "github.com/docker/docker/daemon" package images // import "github.com/docker/docker/daemon/images"
import ( import (
"fmt" "fmt"
@ -11,9 +11,9 @@ import (
// ImageHistory returns a slice of ImageHistory structures for the specified image // ImageHistory returns a slice of ImageHistory structures for the specified image
// name by walking the image lineage. // name by walking the image lineage.
func (daemon *Daemon) ImageHistory(name string) ([]*image.HistoryResponseItem, error) { func (i *ImageService) ImageHistory(name string) ([]*image.HistoryResponseItem, error) {
start := time.Now() start := time.Now()
img, err := daemon.GetImage(name) img, err := i.GetImage(name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -33,12 +33,12 @@ func (daemon *Daemon) ImageHistory(name string) ([]*image.HistoryResponseItem, e
} }
rootFS.Append(img.RootFS.DiffIDs[layerCounter]) rootFS.Append(img.RootFS.DiffIDs[layerCounter])
l, err := daemon.layerStores[img.OperatingSystem()].Get(rootFS.ChainID()) l, err := i.layerStores[img.OperatingSystem()].Get(rootFS.ChainID())
if err != nil { if err != nil {
return nil, err return nil, err
} }
layerSize, err = l.DiffSize() layerSize, err = l.DiffSize()
layer.ReleaseAndLog(daemon.layerStores[img.OperatingSystem()], l) layer.ReleaseAndLog(i.layerStores[img.OperatingSystem()], l)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -62,7 +62,7 @@ func (daemon *Daemon) ImageHistory(name string) ([]*image.HistoryResponseItem, e
h.ID = id.String() h.ID = id.String()
var tags []string var tags []string
for _, r := range daemon.referenceStore.References(id.Digest()) { for _, r := range i.referenceStore.References(id.Digest()) {
if _, ok := r.(reference.NamedTagged); ok { if _, ok := r.(reference.NamedTagged); ok {
tags = append(tags, reference.FamiliarString(r)) tags = append(tags, reference.FamiliarString(r))
} }
@ -74,7 +74,7 @@ func (daemon *Daemon) ImageHistory(name string) ([]*image.HistoryResponseItem, e
if id == "" { if id == "" {
break break
} }
histImg, err = daemon.GetImage(id.String()) histImg, err = i.GetImage(id.String())
if err != nil { if err != nil {
break break
} }

View file

@ -1,4 +1,4 @@
package daemon // import "github.com/docker/docker/daemon" package images // import "github.com/docker/docker/daemon/images"
import ( import (
"encoding/json" "encoding/json"
@ -27,7 +27,7 @@ import (
// inConfig (if src is "-"), or from a URI specified in src. Progress output is // inConfig (if src is "-"), or from a URI specified in src. Progress output is
// written to outStream. Repository and tag names can optionally be given in // written to outStream. Repository and tag names can optionally be given in
// the repo and tag arguments, respectively. // the repo and tag arguments, respectively.
func (daemon *Daemon) ImportImage(src string, repository, os string, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error { func (i *ImageService) ImportImage(src string, repository, os string, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error {
var ( var (
rc io.ReadCloser rc io.ReadCloser
resp *http.Response resp *http.Response
@ -91,11 +91,11 @@ func (daemon *Daemon) ImportImage(src string, repository, os string, tag string,
if err != nil { if err != nil {
return err return err
} }
l, err := daemon.layerStores[os].Register(inflatedLayerData, "") l, err := i.layerStores[os].Register(inflatedLayerData, "")
if err != nil { if err != nil {
return err return err
} }
defer layer.ReleaseAndLog(daemon.layerStores[os], l) defer layer.ReleaseAndLog(i.layerStores[os], l)
created := time.Now().UTC() created := time.Now().UTC()
imgConfig, err := json.Marshal(&image.Image{ imgConfig, err := json.Marshal(&image.Image{
@ -120,19 +120,19 @@ func (daemon *Daemon) ImportImage(src string, repository, os string, tag string,
return err return err
} }
id, err := daemon.imageStore.Create(imgConfig) id, err := i.imageStore.Create(imgConfig)
if err != nil { if err != nil {
return err return err
} }
// FIXME: connect with commit code and call refstore directly // FIXME: connect with commit code and call refstore directly
if newRef != nil { if newRef != nil {
if err := daemon.TagImageWithReference(id, newRef); err != nil { if err := i.TagImageWithReference(id, newRef); err != nil {
return err return err
} }
} }
daemon.LogImageEvent(id.String(), id.String(), "import") i.LogImageEvent(id.String(), id.String(), "import")
outStream.Write(streamformatter.FormatStatus("", id.String())) outStream.Write(streamformatter.FormatStatus("", id.String()))
return nil return nil
} }

View file

@ -1,4 +1,4 @@
package daemon // import "github.com/docker/docker/daemon" package images // import "github.com/docker/docker/daemon/images"
import ( import (
"time" "time"
@ -13,15 +13,15 @@ import (
// LookupImage looks up an image by name and returns it as an ImageInspect // LookupImage looks up an image by name and returns it as an ImageInspect
// structure. // structure.
func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) { func (i *ImageService) LookupImage(name string) (*types.ImageInspect, error) {
img, err := daemon.GetImage(name) img, err := i.GetImage(name)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "no such image: %s", name) return nil, errors.Wrapf(err, "no such image: %s", name)
} }
if !system.IsOSSupported(img.OperatingSystem()) { if !system.IsOSSupported(img.OperatingSystem()) {
return nil, system.ErrNotSupportedOperatingSystem return nil, system.ErrNotSupportedOperatingSystem
} }
refs := daemon.referenceStore.References(img.ID().Digest()) refs := i.referenceStore.References(img.ID().Digest())
repoTags := []string{} repoTags := []string{}
repoDigests := []string{} repoDigests := []string{}
for _, ref := range refs { for _, ref := range refs {
@ -37,11 +37,11 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
var layerMetadata map[string]string var layerMetadata map[string]string
layerID := img.RootFS.ChainID() layerID := img.RootFS.ChainID()
if layerID != "" { if layerID != "" {
l, err := daemon.layerStores[img.OperatingSystem()].Get(layerID) l, err := i.layerStores[img.OperatingSystem()].Get(layerID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer layer.ReleaseAndLog(daemon.layerStores[img.OperatingSystem()], l) defer layer.ReleaseAndLog(i.layerStores[img.OperatingSystem()], l)
size, err = l.Size() size, err = l.Size()
if err != nil { if err != nil {
return nil, err return nil, err
@ -58,7 +58,7 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
comment = img.History[len(img.History)-1].Comment comment = img.History[len(img.History)-1].Comment
} }
lastUpdated, err := daemon.imageStore.GetLastUpdated(img.ID()) lastUpdated, err := i.imageStore.GetLastUpdated(img.ID())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -86,7 +86,7 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
}, },
} }
imageInspect.GraphDriver.Name = daemon.GraphDriverName(img.OperatingSystem()) imageInspect.GraphDriver.Name = i.layerStores[img.OperatingSystem()].DriverName()
imageInspect.GraphDriver.Data = layerMetadata imageInspect.GraphDriver.Data = layerMetadata
return imageInspect, nil return imageInspect, nil

View file

@ -1,11 +1,14 @@
package daemon // import "github.com/docker/docker/daemon" package images // import "github.com/docker/docker/daemon/images"
import ( import (
"fmt"
"sync/atomic" "sync/atomic"
"time"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
timetypes "github.com/docker/docker/api/types/time"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
"github.com/docker/docker/image" "github.com/docker/docker/image"
"github.com/docker/docker/layer" "github.com/docker/docker/layer"
@ -21,12 +24,16 @@ var imagesAcceptedFilters = map[string]bool{
"until": true, "until": true,
} }
// errPruneRunning is returned when a prune request is received while
// one is in progress
var errPruneRunning = fmt.Errorf("a prune operation is already running")
// ImagesPrune removes unused images // ImagesPrune removes unused images
func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error) { func (i *ImageService) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error) {
if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) { if !atomic.CompareAndSwapInt32(&i.pruneRunning, 0, 1) {
return nil, errPruneRunning return nil, errPruneRunning
} }
defer atomic.StoreInt32(&daemon.pruneRunning, 0) defer atomic.StoreInt32(&i.pruneRunning, 0)
// make sure that only accepted filters have been received // make sure that only accepted filters have been received
err := pruneFilters.Validate(imagesAcceptedFilters) err := pruneFilters.Validate(imagesAcceptedFilters)
@ -52,14 +59,14 @@ func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args
var allImages map[image.ID]*image.Image var allImages map[image.ID]*image.Image
if danglingOnly { if danglingOnly {
allImages = daemon.imageStore.Heads() allImages = i.imageStore.Heads()
} else { } else {
allImages = daemon.imageStore.Map() allImages = i.imageStore.Map()
} }
// Filter intermediary images and get their unique size // Filter intermediary images and get their unique size
allLayers := make(map[layer.ChainID]layer.Layer) allLayers := make(map[layer.ChainID]layer.Layer)
for _, ls := range daemon.layerStores { for _, ls := range i.layerStores {
for k, v := range ls.Map() { for k, v := range ls.Map() {
allLayers[k] = v allLayers[k] = v
} }
@ -71,7 +78,7 @@ func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args
return nil, ctx.Err() return nil, ctx.Err()
default: default:
dgst := digest.Digest(id) dgst := digest.Digest(id)
if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.imageStore.Children(id)) != 0 { if len(i.referenceStore.References(dgst)) == 0 && len(i.imageStore.Children(id)) != 0 {
continue continue
} }
if !until.IsZero() && img.Created.After(until) { if !until.IsZero() && img.Created.After(until) {
@ -96,7 +103,7 @@ deleteImagesLoop:
} }
deletedImages := []types.ImageDeleteResponseItem{} deletedImages := []types.ImageDeleteResponseItem{}
refs := daemon.referenceStore.References(id.Digest()) refs := i.referenceStore.References(id.Digest())
if len(refs) > 0 { if len(refs) > 0 {
shouldDelete := !danglingOnly shouldDelete := !danglingOnly
if !shouldDelete { if !shouldDelete {
@ -114,7 +121,7 @@ deleteImagesLoop:
if shouldDelete { if shouldDelete {
for _, ref := range refs { for _, ref := range refs {
imgDel, err := daemon.ImageDelete(ref.String(), false, true) imgDel, err := i.ImageDelete(ref.String(), false, true)
if imageDeleteFailed(ref.String(), err) { if imageDeleteFailed(ref.String(), err) {
continue continue
} }
@ -123,7 +130,7 @@ deleteImagesLoop:
} }
} else { } else {
hex := id.Digest().Hex() hex := id.Digest().Hex()
imgDel, err := daemon.ImageDelete(hex, false, true) imgDel, err := i.ImageDelete(hex, false, true)
if imageDeleteFailed(hex, err) { if imageDeleteFailed(hex, err) {
continue continue
} }
@ -166,3 +173,38 @@ func imageDeleteFailed(ref string, err error) bool {
return true return true
} }
} }
func matchLabels(pruneFilters filters.Args, labels map[string]string) bool {
if !pruneFilters.MatchKVList("label", labels) {
return false
}
// By default MatchKVList will return true if field (like 'label!') does not exist
// So we have to add additional Contains("label!") check
if pruneFilters.Contains("label!") {
if pruneFilters.MatchKVList("label!", labels) {
return false
}
}
return true
}
func getUntilFromPruneFilters(pruneFilters filters.Args) (time.Time, error) {
until := time.Time{}
if !pruneFilters.Contains("until") {
return until, nil
}
untilFilters := pruneFilters.Get("until")
if len(untilFilters) > 1 {
return until, fmt.Errorf("more than one until filter specified")
}
ts, err := timetypes.GetTimestamp(untilFilters[0], time.Now())
if err != nil {
return until, err
}
seconds, nanoseconds, err := timetypes.ParseTimestamps(ts, 0)
if err != nil {
return until, err
}
until = time.Unix(seconds, nanoseconds)
return until, nil
}

View file

@ -1,4 +1,4 @@
package daemon // import "github.com/docker/docker/daemon" package images // import "github.com/docker/docker/daemon/images"
import ( import (
"io" "io"
@ -19,7 +19,7 @@ import (
// PullImage initiates a pull operation. image is the repository name to pull, and // PullImage initiates a pull operation. image is the repository name to pull, and
// tag may be either empty, or indicate a specific tag to pull. // tag may be either empty, or indicate a specific tag to pull.
func (daemon *Daemon) PullImage(ctx context.Context, image, tag, os string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { func (i *ImageService) PullImage(ctx context.Context, image, tag, os string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
// Special case: "pull -a" may send an image name with a // Special case: "pull -a" may send an image name with a
// trailing :. This is ugly, but let's not break API // trailing :. This is ugly, but let's not break API
// compatibility. // compatibility.
@ -44,10 +44,10 @@ func (daemon *Daemon) PullImage(ctx context.Context, image, tag, os string, meta
} }
} }
return daemon.pullImageWithReference(ctx, ref, os, metaHeaders, authConfig, outStream) return i.pullImageWithReference(ctx, ref, os, metaHeaders, authConfig, outStream)
} }
func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference.Named, os string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference.Named, os string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
// Include a buffer so that slow client connections don't affect // Include a buffer so that slow client connections don't affect
// transfer performance. // transfer performance.
progressChan := make(chan progress.Progress, 100) progressChan := make(chan progress.Progress, 100)
@ -71,13 +71,13 @@ func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference.
MetaHeaders: metaHeaders, MetaHeaders: metaHeaders,
AuthConfig: authConfig, AuthConfig: authConfig,
ProgressOutput: progress.ChanOutput(progressChan), ProgressOutput: progress.ChanOutput(progressChan),
RegistryService: daemon.RegistryService, RegistryService: i.registryService,
ImageEventLogger: daemon.LogImageEvent, ImageEventLogger: i.LogImageEvent,
MetadataStore: daemon.distributionMetadataStore, MetadataStore: i.distributionMetadataStore,
ImageStore: distribution.NewImageConfigStoreFromStore(daemon.imageStore), ImageStore: distribution.NewImageConfigStoreFromStore(i.imageStore),
ReferenceStore: daemon.referenceStore, ReferenceStore: i.referenceStore,
}, },
DownloadManager: daemon.downloadManager, DownloadManager: i.downloadManager,
Schema2Types: distribution.ImageTypes, Schema2Types: distribution.ImageTypes,
OS: os, OS: os,
} }
@ -89,9 +89,9 @@ func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference.
} }
// GetRepository returns a repository from the registry. // GetRepository returns a repository from the registry.
func (daemon *Daemon) GetRepository(ctx context.Context, ref reference.Named, authConfig *types.AuthConfig) (dist.Repository, bool, error) { func (i *ImageService) GetRepository(ctx context.Context, ref reference.Named, authConfig *types.AuthConfig) (dist.Repository, bool, error) {
// get repository info // get repository info
repoInfo, err := daemon.RegistryService.ResolveRepository(ref) repoInfo, err := i.registryService.ResolveRepository(ref)
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
@ -101,7 +101,7 @@ func (daemon *Daemon) GetRepository(ctx context.Context, ref reference.Named, au
} }
// get endpoints // get endpoints
endpoints, err := daemon.RegistryService.LookupPullEndpoints(reference.Domain(repoInfo.Name)) endpoints, err := i.registryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }

View file

@ -1,4 +1,4 @@
package daemon // import "github.com/docker/docker/daemon" package images // import "github.com/docker/docker/daemon/images"
import ( import (
"io" "io"
@ -13,7 +13,7 @@ import (
) )
// PushImage initiates a push operation on the repository named localName. // PushImage initiates a push operation on the repository named localName.
func (daemon *Daemon) PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { func (i *ImageService) PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
ref, err := reference.ParseNormalizedNamed(image) ref, err := reference.ParseNormalizedNamed(image)
if err != nil { if err != nil {
return err return err
@ -44,16 +44,16 @@ func (daemon *Daemon) PushImage(ctx context.Context, image, tag string, metaHead
MetaHeaders: metaHeaders, MetaHeaders: metaHeaders,
AuthConfig: authConfig, AuthConfig: authConfig,
ProgressOutput: progress.ChanOutput(progressChan), ProgressOutput: progress.ChanOutput(progressChan),
RegistryService: daemon.RegistryService, RegistryService: i.registryService,
ImageEventLogger: daemon.LogImageEvent, ImageEventLogger: i.LogImageEvent,
MetadataStore: daemon.distributionMetadataStore, MetadataStore: i.distributionMetadataStore,
ImageStore: distribution.NewImageConfigStoreFromStore(daemon.imageStore), ImageStore: distribution.NewImageConfigStoreFromStore(i.imageStore),
ReferenceStore: daemon.referenceStore, ReferenceStore: i.referenceStore,
}, },
ConfigMediaType: schema2.MediaTypeImageConfig, ConfigMediaType: schema2.MediaTypeImageConfig,
LayerStores: distribution.NewLayerProvidersFromStores(daemon.layerStores), LayerStores: distribution.NewLayerProvidersFromStores(i.layerStores),
TrustKey: daemon.trustKey, TrustKey: i.trustKey,
UploadManager: daemon.uploadManager, UploadManager: i.uploadManager,
} }
err = distribution.Push(ctx, ref, imagePushConfig) err = distribution.Push(ctx, ref, imagePushConfig)

View file

@ -1,4 +1,4 @@
package daemon // import "github.com/docker/docker/daemon" package images // import "github.com/docker/docker/daemon/images"
import ( import (
"strconv" "strconv"
@ -19,7 +19,10 @@ var acceptedSearchFilterTags = map[string]bool{
// SearchRegistryForImages queries the registry for images matching // SearchRegistryForImages queries the registry for images matching
// term. authConfig is used to login. // term. authConfig is used to login.
func (daemon *Daemon) SearchRegistryForImages(ctx context.Context, filtersArgs string, term string, limit int, //
// TODO: this could be implemented in a registry service instead of the image
// service.
func (i *ImageService) SearchRegistryForImages(ctx context.Context, filtersArgs string, term string, limit int,
authConfig *types.AuthConfig, authConfig *types.AuthConfig,
headers map[string][]string) (*registrytypes.SearchResults, error) { headers map[string][]string) (*registrytypes.SearchResults, error) {
@ -60,7 +63,7 @@ func (daemon *Daemon) SearchRegistryForImages(ctx context.Context, filtersArgs s
} }
} }
unfilteredResult, err := daemon.RegistryService.Search(ctx, term, limit, authConfig, dockerversion.DockerUserAgent(ctx), headers) unfilteredResult, err := i.registryService.Search(ctx, term, limit, authConfig, dockerversion.DockerUserAgent(ctx), headers)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -1,4 +1,4 @@
package daemon // import "github.com/docker/docker/daemon" package images // import "github.com/docker/docker/daemon/images"
import ( import (
"errors" "errors"
@ -76,8 +76,8 @@ func TestSearchRegistryForImagesErrors(t *testing.T) {
}, },
} }
for index, e := range errorCases { for index, e := range errorCases {
daemon := &Daemon{ daemon := &ImageService{
RegistryService: &FakeService{ registryService: &FakeService{
shouldReturnError: e.shouldReturnError, shouldReturnError: e.shouldReturnError,
}, },
} }
@ -322,8 +322,8 @@ func TestSearchRegistryForImages(t *testing.T) {
}, },
} }
for index, s := range successCases { for index, s := range successCases {
daemon := &Daemon{ daemon := &ImageService{
RegistryService: &FakeService{ registryService: &FakeService{
term: term, term: term,
results: s.registryResults, results: s.registryResults,
}, },

View file

@ -1,4 +1,4 @@
package daemon // import "github.com/docker/docker/daemon" package images // import "github.com/docker/docker/daemon/images"
import ( import (
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
@ -7,8 +7,8 @@ import (
// TagImage creates the tag specified by newTag, pointing to the image named // TagImage creates the tag specified by newTag, pointing to the image named
// imageName (alternatively, imageName can also be an image ID). // imageName (alternatively, imageName can also be an image ID).
func (daemon *Daemon) TagImage(imageName, repository, tag string) (string, error) { func (i *ImageService) TagImage(imageName, repository, tag string) (string, error) {
imageID, _, err := daemon.GetImageIDAndOS(imageName) img, err := i.GetImage(imageName)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -23,19 +23,19 @@ func (daemon *Daemon) TagImage(imageName, repository, tag string) (string, error
} }
} }
err = daemon.TagImageWithReference(imageID, newTag) err = i.TagImageWithReference(img.ID(), newTag)
return reference.FamiliarString(newTag), err return reference.FamiliarString(newTag), err
} }
// TagImageWithReference adds the given reference to the image ID provided. // TagImageWithReference adds the given reference to the image ID provided.
func (daemon *Daemon) TagImageWithReference(imageID image.ID, newTag reference.Named) error { func (i *ImageService) TagImageWithReference(imageID image.ID, newTag reference.Named) error {
if err := daemon.referenceStore.AddTag(newTag, imageID.Digest(), true); err != nil { if err := i.referenceStore.AddTag(newTag, imageID.Digest(), true); err != nil {
return err return err
} }
if err := daemon.imageStore.SetLastUpdated(imageID); err != nil { if err := i.imageStore.SetLastUpdated(imageID); err != nil {
return err return err
} }
daemon.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), "tag") i.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), "tag")
return nil return nil
} }

View file

@ -1,6 +1,6 @@
// +build linux freebsd // +build linux freebsd
package daemon // import "github.com/docker/docker/daemon" package images // import "github.com/docker/docker/daemon/images"
import ( import (
"runtime" "runtime"
@ -8,8 +8,8 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
// getSize returns the real size & virtual size of the container. // GetContainerLayerSize returns the real size & virtual size of the container.
func (daemon *Daemon) getSize(containerID string) (int64, int64) { func (i *ImageService) GetContainerLayerSize(containerID string) (int64, int64) {
var ( var (
sizeRw, sizeRootfs int64 sizeRw, sizeRootfs int64
err error err error
@ -17,17 +17,17 @@ func (daemon *Daemon) getSize(containerID string) (int64, int64) {
// Safe to index by runtime.GOOS as Unix hosts don't support multiple // Safe to index by runtime.GOOS as Unix hosts don't support multiple
// container operating systems. // container operating systems.
rwlayer, err := daemon.layerStores[runtime.GOOS].GetRWLayer(containerID) rwlayer, err := i.layerStores[runtime.GOOS].GetRWLayer(containerID)
if err != nil { if err != nil {
logrus.Errorf("Failed to compute size of container rootfs %v: %v", containerID, err) logrus.Errorf("Failed to compute size of container rootfs %v: %v", containerID, err)
return sizeRw, sizeRootfs return sizeRw, sizeRootfs
} }
defer daemon.layerStores[runtime.GOOS].ReleaseRWLayer(rwlayer) defer i.layerStores[runtime.GOOS].ReleaseRWLayer(rwlayer)
sizeRw, err = rwlayer.Size() sizeRw, err = rwlayer.Size()
if err != nil { if err != nil {
logrus.Errorf("Driver %s couldn't return diff size of container %s: %s", logrus.Errorf("Driver %s couldn't return diff size of container %s: %s",
daemon.GraphDriverName(runtime.GOOS), containerID, err) i.layerStores[runtime.GOOS].DriverName(), containerID, err)
// FIXME: GetSize should return an error. Not changing it now in case // FIXME: GetSize should return an error. Not changing it now in case
// there is a side-effect. // there is a side-effect.
sizeRw = -1 sizeRw = -1

View file

@ -1,4 +1,4 @@
package daemon // import "github.com/docker/docker/daemon" package images
import ( import (
"github.com/docker/docker/image" "github.com/docker/docker/image"
@ -7,8 +7,14 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// GetContainerLayerSize returns real size & virtual size
func (i *ImageService) GetContainerLayerSize(containerID string) (int64, int64) {
// TODO Windows
return 0, 0
}
// GetLayerFolders returns the layer folders from an image RootFS // GetLayerFolders returns the layer folders from an image RootFS
func (daemon *Daemon) GetLayerFolders(img *image.Image, rwLayer layer.RWLayer) ([]string, error) { func (i *ImageService) GetLayerFolders(img *image.Image, rwLayer layer.RWLayer) ([]string, error) {
folders := []string{} folders := []string{}
max := len(img.RootFS.DiffIDs) max := len(img.RootFS.DiffIDs)
for index := 1; index <= max; index++ { for index := 1; index <= max; index++ {
@ -17,9 +23,9 @@ func (daemon *Daemon) GetLayerFolders(img *image.Image, rwLayer layer.RWLayer) (
if !system.IsOSSupported(img.OperatingSystem()) { if !system.IsOSSupported(img.OperatingSystem()) {
return nil, errors.Wrapf(system.ErrNotSupportedOperatingSystem, "cannot get layerpath for ImageID %s", img.RootFS.ChainID()) return nil, errors.Wrapf(system.ErrNotSupportedOperatingSystem, "cannot get layerpath for ImageID %s", img.RootFS.ChainID())
} }
layerPath, err := layer.GetLayerPath(daemon.layerStores[img.OperatingSystem()], img.RootFS.ChainID()) layerPath, err := layer.GetLayerPath(i.layerStores[img.OperatingSystem()], img.RootFS.ChainID())
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to get layer path from graphdriver %s for ImageID %s", daemon.layerStores[img.OperatingSystem()], img.RootFS.ChainID()) return nil, errors.Wrapf(err, "failed to get layer path from graphdriver %s for ImageID %s", i.layerStores[img.OperatingSystem()], img.RootFS.ChainID())
} }
// Reverse order, expecting parent first // Reverse order, expecting parent first
folders = append([]string{layerPath}, folders...) folders = append([]string{layerPath}, folders...)

View file

@ -1,4 +1,4 @@
package daemon // import "github.com/docker/docker/daemon" package images // import "github.com/docker/docker/daemon/images"
import ( import (
"encoding/json" "encoding/json"
@ -34,8 +34,8 @@ func (r byCreated) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r byCreated) Less(i, j int) bool { return r[i].Created < r[j].Created } 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 // Map returns a map of all images in the ImageStore
func (daemon *Daemon) Map() map[image.ID]*image.Image { func (i *ImageService) Map() map[image.ID]*image.Image {
return daemon.imageStore.Map() return i.imageStore.Map()
} }
// Images returns a filtered list of images. filterArgs is a JSON-encoded set // Images returns a filtered list of images. filterArgs is a JSON-encoded set
@ -43,7 +43,7 @@ func (daemon *Daemon) Map() map[image.ID]*image.Image {
// filter is a shell glob string applied to repository names. The argument // filter is a shell glob string applied to repository names. The argument
// named all controls whether all images in the graph are filtered, or just // named all controls whether all images in the graph are filtered, or just
// the heads. // the heads.
func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error) { func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error) {
var ( var (
allImages map[image.ID]*image.Image allImages map[image.ID]*image.Image
err error err error
@ -62,14 +62,14 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
} }
} }
if danglingOnly { if danglingOnly {
allImages = daemon.imageStore.Heads() allImages = i.imageStore.Heads()
} else { } else {
allImages = daemon.imageStore.Map() allImages = i.imageStore.Map()
} }
var beforeFilter, sinceFilter *image.Image var beforeFilter, sinceFilter *image.Image
err = imageFilters.WalkValues("before", func(value string) error { err = imageFilters.WalkValues("before", func(value string) error {
beforeFilter, err = daemon.GetImage(value) beforeFilter, err = i.GetImage(value)
return err return err
}) })
if err != nil { if err != nil {
@ -77,7 +77,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
} }
err = imageFilters.WalkValues("since", func(value string) error { err = imageFilters.WalkValues("since", func(value string) error {
sinceFilter, err = daemon.GetImage(value) sinceFilter, err = i.GetImage(value)
return err return err
}) })
if err != nil { if err != nil {
@ -124,7 +124,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
layerID := img.RootFS.ChainID() layerID := img.RootFS.ChainID()
var size int64 var size int64
if layerID != "" { if layerID != "" {
l, err := daemon.layerStores[img.OperatingSystem()].Get(layerID) l, err := i.layerStores[img.OperatingSystem()].Get(layerID)
if err != nil { if err != nil {
// The layer may have been deleted between the call to `Map()` or // The layer may have been deleted between the call to `Map()` or
// `Heads()` and the call to `Get()`, so we just ignore this error // `Heads()` and the call to `Get()`, so we just ignore this error
@ -135,7 +135,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
} }
size, err = l.Size() size, err = l.Size()
layer.ReleaseAndLog(daemon.layerStores[img.OperatingSystem()], l) layer.ReleaseAndLog(i.layerStores[img.OperatingSystem()], l)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -143,7 +143,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
newImage := newImage(img, size) newImage := newImage(img, size)
for _, ref := range daemon.referenceStore.References(id.Digest()) { for _, ref := range i.referenceStore.References(id.Digest()) {
if imageFilters.Contains("reference") { if imageFilters.Contains("reference") {
var found bool var found bool
var matchErr error var matchErr error
@ -165,7 +165,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
} }
} }
if newImage.RepoDigests == nil && newImage.RepoTags == nil { if newImage.RepoDigests == nil && newImage.RepoTags == nil {
if all || len(daemon.imageStore.Children(id)) == 0 { if all || len(i.imageStore.Children(id)) == 0 {
if imageFilters.Contains("dangling") && !danglingOnly { if imageFilters.Contains("dangling") && !danglingOnly {
//dangling=false case, so dangling image is not needed //dangling=false case, so dangling image is not needed
@ -186,8 +186,8 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
if withExtraAttrs { if withExtraAttrs {
// lazily init variables // lazily init variables
if imagesMap == nil { if imagesMap == nil {
allContainers = daemon.List() allContainers = i.containers.List()
allLayers = daemon.layerStores[img.OperatingSystem()].Map() allLayers = i.layerStores[img.OperatingSystem()].Map()
imagesMap = make(map[*image.Image]*types.ImageSummary) imagesMap = make(map[*image.Image]*types.ImageSummary)
layerRefs = make(map[layer.ChainID]int) layerRefs = make(map[layer.ChainID]int)
} }
@ -249,20 +249,20 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
// This new image contains only the layers from it's parent + 1 extra layer which contains the diff of all the layers in between. // This new image contains only the layers from it's parent + 1 extra layer which contains the diff of all the layers in between.
// The existing image(s) is not destroyed. // 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. // 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) { func (i *ImageService) SquashImage(id, parent string) (string, error) {
var ( var (
img *image.Image img *image.Image
err error err error
) )
if img, err = daemon.imageStore.Get(image.ID(id)); err != nil { if img, err = i.imageStore.Get(image.ID(id)); err != nil {
return "", err return "", err
} }
var parentImg *image.Image var parentImg *image.Image
var parentChainID layer.ChainID var parentChainID layer.ChainID
if len(parent) != 0 { if len(parent) != 0 {
parentImg, err = daemon.imageStore.Get(image.ID(parent)) parentImg, err = i.imageStore.Get(image.ID(parent))
if err != nil { if err != nil {
return "", errors.Wrap(err, "error getting specified parent layer") return "", errors.Wrap(err, "error getting specified parent layer")
} }
@ -272,11 +272,11 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) {
parentImg = &image.Image{RootFS: rootFS} parentImg = &image.Image{RootFS: rootFS}
} }
l, err := daemon.layerStores[img.OperatingSystem()].Get(img.RootFS.ChainID()) l, err := i.layerStores[img.OperatingSystem()].Get(img.RootFS.ChainID())
if err != nil { if err != nil {
return "", errors.Wrap(err, "error getting image layer") return "", errors.Wrap(err, "error getting image layer")
} }
defer daemon.layerStores[img.OperatingSystem()].Release(l) defer i.layerStores[img.OperatingSystem()].Release(l)
ts, err := l.TarStreamFrom(parentChainID) ts, err := l.TarStreamFrom(parentChainID)
if err != nil { if err != nil {
@ -284,11 +284,11 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) {
} }
defer ts.Close() defer ts.Close()
newL, err := daemon.layerStores[img.OperatingSystem()].Register(ts, parentChainID) newL, err := i.layerStores[img.OperatingSystem()].Register(ts, parentChainID)
if err != nil { if err != nil {
return "", errors.Wrap(err, "error registering layer") return "", errors.Wrap(err, "error registering layer")
} }
defer daemon.layerStores[img.OperatingSystem()].Release(newL) defer i.layerStores[img.OperatingSystem()].Release(newL)
newImage := *img newImage := *img
newImage.RootFS = nil newImage.RootFS = nil
@ -323,7 +323,7 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) {
return "", errors.Wrap(err, "error marshalling image config") return "", errors.Wrap(err, "error marshalling image config")
} }
newImgID, err := daemon.imageStore.Create(b) newImgID, err := i.imageStore.Create(b)
if err != nil { if err != nil {
return "", errors.Wrap(err, "error creating new image after squash") return "", errors.Wrap(err, "error creating new image after squash")
} }

32
daemon/images/locals.go Normal file
View file

@ -0,0 +1,32 @@
package images // import "github.com/docker/docker/daemon/images"
import (
"fmt"
metrics "github.com/docker/go-metrics"
)
type invalidFilter struct {
filter string
value interface{}
}
func (e invalidFilter) Error() string {
msg := "Invalid filter '" + e.filter
if e.value != nil {
msg += fmt.Sprintf("=%s", e.value)
}
return msg + "'"
}
func (e invalidFilter) InvalidParameter() {}
var imageActions metrics.LabeledTimer
func init() {
ns := metrics.NewNamespace("engine", "daemon", nil)
imageActions = ns.NewLabeledTimer("image_actions", "The number of seconds it takes to process each image action", "action")
// TODO: is it OK to register a namespace with the same name? Or does this
// need to be exported from somewhere?
metrics.Register(ns)
}

229
daemon/images/service.go Normal file
View file

@ -0,0 +1,229 @@
package images // import "github.com/docker/docker/daemon/images"
import (
"context"
"os"
"github.com/docker/docker/container"
daemonevents "github.com/docker/docker/daemon/events"
"github.com/docker/docker/distribution/metadata"
"github.com/docker/docker/distribution/xfer"
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
dockerreference "github.com/docker/docker/reference"
"github.com/docker/docker/registry"
"github.com/docker/libtrust"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
type containerStore interface {
// used by image delete
First(container.StoreFilter) *container.Container
// used by image prune, and image list
List() []*container.Container
// TODO: remove, only used for CommitBuildStep
Get(string) *container.Container
}
// ImageServiceConfig is the configuration used to create a new ImageService
type ImageServiceConfig struct {
ContainerStore containerStore
DistributionMetadataStore metadata.Store
EventsService *daemonevents.Events
ImageStore image.Store
LayerStores map[string]layer.Store
MaxConcurrentDownloads int
MaxConcurrentUploads int
ReferenceStore dockerreference.Store
RegistryService registry.Service
TrustKey libtrust.PrivateKey
}
// NewImageService returns a new ImageService from a configuration
func NewImageService(config ImageServiceConfig) *ImageService {
logrus.Debugf("Max Concurrent Downloads: %d", config.MaxConcurrentDownloads)
logrus.Debugf("Max Concurrent Uploads: %d", config.MaxConcurrentUploads)
return &ImageService{
containers: config.ContainerStore,
distributionMetadataStore: config.DistributionMetadataStore,
downloadManager: xfer.NewLayerDownloadManager(config.LayerStores, config.MaxConcurrentDownloads),
eventsService: config.EventsService,
imageStore: config.ImageStore,
layerStores: config.LayerStores,
referenceStore: config.ReferenceStore,
registryService: config.RegistryService,
trustKey: config.TrustKey,
uploadManager: xfer.NewLayerUploadManager(config.MaxConcurrentUploads),
}
}
// ImageService provides a backend for image management
type ImageService struct {
containers containerStore
distributionMetadataStore metadata.Store
downloadManager *xfer.LayerDownloadManager
eventsService *daemonevents.Events
imageStore image.Store
layerStores map[string]layer.Store // By operating system
pruneRunning int32
referenceStore dockerreference.Store
registryService registry.Service
trustKey libtrust.PrivateKey
uploadManager *xfer.LayerUploadManager
}
// CountImages returns the number of images stored by ImageService
// called from info.go
func (i *ImageService) CountImages() int {
return len(i.imageStore.Map())
}
// Children returns the children image.IDs for a parent image.
// called from list.go to filter containers
// TODO: refactor to expose an ancestry for image.ID?
func (i *ImageService) Children(id image.ID) []image.ID {
return i.imageStore.Children(id)
}
// CreateLayer creates a filesystem layer for a container.
// called from create.go
// TODO: accept an opt struct instead of container?
func (i *ImageService) CreateLayer(container *container.Container, initFunc layer.MountInit) (layer.RWLayer, error) {
var layerID layer.ChainID
if container.ImageID != "" {
img, err := i.imageStore.Get(container.ImageID)
if err != nil {
return nil, err
}
layerID = img.RootFS.ChainID()
}
rwLayerOpts := &layer.CreateRWLayerOpts{
MountLabel: container.MountLabel,
InitFunc: initFunc,
StorageOpt: container.HostConfig.StorageOpt,
}
// Indexing by OS is safe here as validation of OS has already been performed in create() (the only
// caller), and guaranteed non-nil
return i.layerStores[container.OS].CreateRWLayer(container.ID, layerID, rwLayerOpts)
}
// GetLayerByID returns a layer by ID and operating system
// called from daemon.go Daemon.restore(), and Daemon.containerExport()
func (i *ImageService) GetLayerByID(cid string, os string) (layer.RWLayer, error) {
return i.layerStores[os].GetRWLayer(cid)
}
// LayerStoreStatus returns the status for each layer store
// called from info.go
func (i *ImageService) LayerStoreStatus() map[string][][2]string {
result := make(map[string][][2]string)
for os, store := range i.layerStores {
result[os] = store.DriverStatus()
}
return result
}
// GetLayerMountID returns the mount ID for a layer
// called from daemon.go Daemon.Shutdown(), and Daemon.Cleanup() (cleanup is actually continerCleanup)
// TODO: needs to be refactored to Unmount (see callers), or removed and replaced
// with GetLayerByID
func (i *ImageService) GetLayerMountID(cid string, os string) (string, error) {
return i.layerStores[os].GetMountID(cid)
}
// Cleanup resources before the process is shutdown.
// called from daemon.go Daemon.Shutdown()
func (i *ImageService) Cleanup() {
for os, ls := range i.layerStores {
if ls != nil {
if err := ls.Cleanup(); err != nil {
logrus.Errorf("Error during layer Store.Cleanup(): %v %s", err, os)
}
}
}
}
// GraphDriverForOS returns the name of the graph drvier
// moved from Daemon.GraphDriverName, used by:
// - newContainer
// - to report an error in Daemon.Mount(container)
func (i *ImageService) GraphDriverForOS(os string) string {
return i.layerStores[os].DriverName()
}
// ReleaseLayer releases a layer allowing it to be removed
// called from delete.go Daemon.cleanupContainer(), and Daemon.containerExport()
func (i *ImageService) ReleaseLayer(rwlayer layer.RWLayer, containerOS string) error {
metadata, err := i.layerStores[containerOS].ReleaseRWLayer(rwlayer)
layer.LogReleaseMetadata(metadata)
if err != nil && err != layer.ErrMountDoesNotExist && !os.IsNotExist(errors.Cause(err)) {
return errors.Wrapf(err, "driver %q failed to remove root filesystem",
i.layerStores[containerOS].DriverName())
}
return nil
}
// LayerDiskUsage returns the number of bytes used by layer stores
// called from disk_usage.go
func (i *ImageService) LayerDiskUsage(ctx context.Context) (int64, error) {
var allLayersSize int64
layerRefs := i.getLayerRefs()
for _, ls := range i.layerStores {
allLayers := ls.Map()
for _, l := range allLayers {
select {
case <-ctx.Done():
return allLayersSize, 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", l.ChainID())
}
} else {
logrus.Warnf("failed to get diff size for layer %v", l.ChainID())
}
}
}
}
return allLayersSize, nil
}
func (i *ImageService) getLayerRefs() map[layer.ChainID]int {
tmpImages := i.imageStore.Map()
layerRefs := map[layer.ChainID]int{}
for id, img := range tmpImages {
dgst := digest.Digest(id)
if len(i.referenceStore.References(dgst)) == 0 && len(i.imageStore.Children(id)) != 0 {
continue
}
rootFS := *img.RootFS
rootFS.DiffIDs = nil
for _, id := range img.RootFS.DiffIDs {
rootFS.Append(id)
chid := rootFS.ChainID()
layerRefs[chid]++
}
}
return layerRefs
}
// UpdateConfig values
//
// called from reload.go
func (i *ImageService) UpdateConfig(maxDownloads, maxUploads *int) {
if i.downloadManager != nil && maxDownloads != nil {
i.downloadManager.SetConcurrency(*maxDownloads)
}
if i.uploadManager != nil && maxUploads != nil {
i.uploadManager.SetConcurrency(*maxUploads)
}
}

View file

@ -80,8 +80,9 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
var ds [][2]string var ds [][2]string
drivers := "" drivers := ""
statuses := daemon.imageService.LayerStoreStatus()
for os, gd := range daemon.graphDrivers { for os, gd := range daemon.graphDrivers {
ds = append(ds, daemon.layerStores[os].DriverStatus()...) ds = append(ds, statuses[os]...)
drivers += gd drivers += gd
if len(daemon.graphDrivers) > 1 { if len(daemon.graphDrivers) > 1 {
drivers += fmt.Sprintf(" (%s) ", os) drivers += fmt.Sprintf(" (%s) ", os)
@ -95,7 +96,7 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
ContainersRunning: cRunning, ContainersRunning: cRunning,
ContainersPaused: cPaused, ContainersPaused: cPaused,
ContainersStopped: cStopped, ContainersStopped: cStopped,
Images: len(daemon.imageStore.Map()), Images: daemon.imageService.CountImages(),
Driver: drivers, Driver: drivers,
DriverStatus: ds, DriverStatus: ds,
Plugins: daemon.showPluginsInfo(), Plugins: daemon.showPluginsInfo(),

View file

@ -79,7 +79,7 @@ func (daemon *Daemon) ContainerInspectCurrent(name string, size bool) (*types.Co
container.Unlock() container.Unlock()
if size { if size {
sizeRw, sizeRootFs := daemon.getSize(base.ID) sizeRw, sizeRootFs := daemon.imageService.GetContainerLayerSize(base.ID)
base.SizeRw = &sizeRw base.SizeRw = &sizeRw
base.SizeRootFs = &sizeRootFs base.SizeRootFs = &sizeRootFs
} }

View file

@ -9,6 +9,7 @@ import (
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
"github.com/docker/docker/container" "github.com/docker/docker/container"
"github.com/docker/docker/daemon/images"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
"github.com/docker/docker/image" "github.com/docker/docker/image"
"github.com/docker/docker/volume" "github.com/docker/docker/volume"
@ -239,7 +240,7 @@ func (daemon *Daemon) reducePsContainer(container *container.Snapshot, ctx *list
// release lock because size calculation is slow // release lock because size calculation is slow
if ctx.Size { if ctx.Size {
sizeRw, sizeRootFs := daemon.getSize(newC.ID) sizeRw, sizeRootFs := daemon.imageService.GetContainerLayerSize(newC.ID)
newC.SizeRw = sizeRw newC.SizeRw = sizeRw
newC.SizeRootFs = sizeRootFs newC.SizeRootFs = sizeRootFs
} }
@ -323,17 +324,17 @@ func (daemon *Daemon) foldFilter(view container.View, config *types.ContainerLis
if psFilters.Contains("ancestor") { if psFilters.Contains("ancestor") {
ancestorFilter = true ancestorFilter = true
psFilters.WalkValues("ancestor", func(ancestor string) error { psFilters.WalkValues("ancestor", func(ancestor string) error {
id, _, err := daemon.GetImageIDAndOS(ancestor) img, err := daemon.imageService.GetImage(ancestor)
if err != nil { if err != nil {
logrus.Warnf("Error while looking up for image %v", ancestor) logrus.Warnf("Error while looking up for image %v", ancestor)
return nil return nil
} }
if imagesFilter[id] { if imagesFilter[img.ID()] {
// Already seen this ancestor, skip it // Already seen this ancestor, skip it
return nil return nil
} }
// Then walk down the graph and put the imageIds in imagesFilter // Then walk down the graph and put the imageIds in imagesFilter
populateImageFilterByParents(imagesFilter, id, daemon.imageStore.Children) populateImageFilterByParents(imagesFilter, img.ID(), daemon.imageService.Children)
return nil return nil
}) })
} }
@ -591,11 +592,11 @@ func (daemon *Daemon) refreshImage(s *container.Snapshot, ctx *listContext) (*ty
c := s.Container c := s.Container
image := s.Image // keep the original ref if still valid (hasn't changed) image := s.Image // keep the original ref if still valid (hasn't changed)
if image != s.ImageID { if image != s.ImageID {
id, _, err := daemon.GetImageIDAndOS(image) img, err := daemon.imageService.GetImage(image)
if _, isDNE := err.(errImageDoesNotExist); err != nil && !isDNE { if _, isDNE := err.(images.ErrImageDoesNotExist); err != nil && !isDNE {
return nil, err return nil, err
} }
if err != nil || id.String() != s.ImageID { if err != nil || img.ImageID() != s.ImageID {
// ref changed, we need to use original ID // ref changed, we need to use original ID
image = s.ImageID image = s.ImageID
} }

View file

@ -14,7 +14,6 @@ const metricsPluginType = "MetricsCollector"
var ( var (
containerActions metrics.LabeledTimer containerActions metrics.LabeledTimer
imageActions metrics.LabeledTimer
networkActions metrics.LabeledTimer networkActions metrics.LabeledTimer
engineInfo metrics.LabeledGauge engineInfo metrics.LabeledGauge
engineCpus metrics.Gauge engineCpus metrics.Gauge
@ -52,7 +51,6 @@ func init() {
engineMemory = ns.NewGauge("engine_memory", "The number of bytes of memory that the host system of the engine has", metrics.Bytes) engineMemory = ns.NewGauge("engine_memory", "The number of bytes of memory that the host system of the engine has", metrics.Bytes)
healthChecksCounter = ns.NewCounter("health_checks", "The total number of health checks") healthChecksCounter = ns.NewCounter("health_checks", "The total number of health checks")
healthChecksFailedCounter = ns.NewCounter("health_checks_failed", "The total number of failed health checks") healthChecksFailedCounter = ns.NewCounter("health_checks_failed", "The total number of failed health checks")
imageActions = ns.NewLabeledTimer("image_actions", "The number of seconds it takes to process each image action", "action")
stateCtr = newStateCounter(ns.NewDesc("container_states", "The count of containers in various states", metrics.Unit("containers"), "state")) stateCtr = newStateCounter(ns.NewDesc("container_states", "The count of containers in various states", metrics.Unit("containers"), "state"))
ns.Add(stateCtr) ns.Add(stateCtr)

View file

@ -24,7 +24,7 @@ const (
) )
func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
img, err := daemon.GetImage(string(c.ImageID)) img, err := daemon.imageService.GetImage(string(c.ImageID))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -138,7 +138,7 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
} }
} }
s.Process.User.Username = c.Config.User s.Process.User.Username = c.Config.User
s.Windows.LayerFolders, err = daemon.GetLayerFolders(img, c.RWLayer) s.Windows.LayerFolders, err = daemon.imageService.GetLayerFolders(img, c.RWLayer)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "container %s", c.ID) return nil, errors.Wrapf(err, "container %s", c.ID)
} }

View file

@ -75,7 +75,7 @@ func (daemon *Daemon) ContainersPrune(ctx context.Context, pruneFilters filters.
if !matchLabels(pruneFilters, c.Config.Labels) { if !matchLabels(pruneFilters, c.Config.Labels) {
continue continue
} }
cSize, _ := daemon.getSize(c.ID) cSize, _ := daemon.imageService.GetContainerLayerSize(c.ID)
// TODO: sets RmLink to true? // TODO: sets RmLink to true?
err := daemon.ContainerRm(c.ID, &types.ContainerRmConfig{}) err := daemon.ContainerRm(c.ID, &types.ContainerRmConfig{})
if err != nil { if err != nil {

View file

@ -90,12 +90,6 @@ func (daemon *Daemon) reloadMaxConcurrentDownloadsAndUploads(conf *config.Config
daemon.configStore.MaxConcurrentDownloads = &maxConcurrentDownloads daemon.configStore.MaxConcurrentDownloads = &maxConcurrentDownloads
} }
logrus.Debugf("Reset Max Concurrent Downloads: %d", *daemon.configStore.MaxConcurrentDownloads) logrus.Debugf("Reset Max Concurrent Downloads: %d", *daemon.configStore.MaxConcurrentDownloads)
if daemon.downloadManager != nil {
daemon.downloadManager.SetConcurrency(*daemon.configStore.MaxConcurrentDownloads)
}
// prepare reload event attributes with updatable configurations
attributes["max-concurrent-downloads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentDownloads)
// If no value is set for max-concurrent-upload we assume it is the default value // If no value is set for max-concurrent-upload we assume it is the default value
// We always "reset" as the cost is lightweight and easy to maintain. // We always "reset" as the cost is lightweight and easy to maintain.
@ -106,10 +100,10 @@ func (daemon *Daemon) reloadMaxConcurrentDownloadsAndUploads(conf *config.Config
daemon.configStore.MaxConcurrentUploads = &maxConcurrentUploads daemon.configStore.MaxConcurrentUploads = &maxConcurrentUploads
} }
logrus.Debugf("Reset Max Concurrent Uploads: %d", *daemon.configStore.MaxConcurrentUploads) logrus.Debugf("Reset Max Concurrent Uploads: %d", *daemon.configStore.MaxConcurrentUploads)
if daemon.uploadManager != nil {
daemon.uploadManager.SetConcurrency(*daemon.configStore.MaxConcurrentUploads)
}
daemon.imageService.UpdateConfig(conf.MaxConcurrentDownloads, conf.MaxConcurrentUploads)
// prepare reload event attributes with updatable configurations
attributes["max-concurrent-downloads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentDownloads)
// prepare reload event attributes with updatable configurations // prepare reload event attributes with updatable configurations
attributes["max-concurrent-uploads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentUploads) attributes["max-concurrent-uploads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentUploads)
} }

View file

@ -7,6 +7,7 @@ import (
"time" "time"
"github.com/docker/docker/daemon/config" "github.com/docker/docker/daemon/config"
"github.com/docker/docker/daemon/images"
"github.com/docker/docker/pkg/discovery" "github.com/docker/docker/pkg/discovery"
_ "github.com/docker/docker/pkg/discovery/memory" _ "github.com/docker/docker/pkg/discovery/memory"
"github.com/docker/docker/registry" "github.com/docker/docker/registry"
@ -15,11 +16,13 @@ import (
) )
func TestDaemonReloadLabels(t *testing.T) { func TestDaemonReloadLabels(t *testing.T) {
daemon := &Daemon{} daemon := &Daemon{
daemon.configStore = &config.Config{ configStore: &config.Config{
CommonConfig: config.CommonConfig{ CommonConfig: config.CommonConfig{
Labels: []string{"foo:bar"}, Labels: []string{"foo:bar"},
},
}, },
imageService: images.NewImageService(images.ImageServiceConfig{}),
} }
valuesSets := make(map[string]interface{}) valuesSets := make(map[string]interface{})
@ -43,7 +46,8 @@ func TestDaemonReloadLabels(t *testing.T) {
func TestDaemonReloadAllowNondistributableArtifacts(t *testing.T) { func TestDaemonReloadAllowNondistributableArtifacts(t *testing.T) {
daemon := &Daemon{ daemon := &Daemon{
configStore: &config.Config{}, configStore: &config.Config{},
imageService: images.NewImageService(images.ImageServiceConfig{}),
} }
var err error var err error
@ -97,7 +101,9 @@ func TestDaemonReloadAllowNondistributableArtifacts(t *testing.T) {
} }
func TestDaemonReloadMirrors(t *testing.T) { func TestDaemonReloadMirrors(t *testing.T) {
daemon := &Daemon{} daemon := &Daemon{
imageService: images.NewImageService(images.ImageServiceConfig{}),
}
var err error var err error
daemon.RegistryService, err = registry.NewService(registry.ServiceOptions{ daemon.RegistryService, err = registry.NewService(registry.ServiceOptions{
InsecureRegistries: []string{}, InsecureRegistries: []string{},
@ -194,7 +200,9 @@ func TestDaemonReloadMirrors(t *testing.T) {
} }
func TestDaemonReloadInsecureRegistries(t *testing.T) { func TestDaemonReloadInsecureRegistries(t *testing.T) {
daemon := &Daemon{} daemon := &Daemon{
imageService: images.NewImageService(images.ImageServiceConfig{}),
}
var err error var err error
// initialize daemon with existing insecure registries: "127.0.0.0/8", "10.10.1.11:5000", "10.10.1.22:5000" // initialize daemon with existing insecure registries: "127.0.0.0/8", "10.10.1.11:5000", "10.10.1.22:5000"
daemon.RegistryService, err = registry.NewService(registry.ServiceOptions{ daemon.RegistryService, err = registry.NewService(registry.ServiceOptions{
@ -284,7 +292,9 @@ func TestDaemonReloadInsecureRegistries(t *testing.T) {
} }
func TestDaemonReloadNotAffectOthers(t *testing.T) { func TestDaemonReloadNotAffectOthers(t *testing.T) {
daemon := &Daemon{} daemon := &Daemon{
imageService: images.NewImageService(images.ImageServiceConfig{}),
}
daemon.configStore = &config.Config{ daemon.configStore = &config.Config{
CommonConfig: config.CommonConfig{ CommonConfig: config.CommonConfig{
Labels: []string{"foo:bar"}, Labels: []string{"foo:bar"},
@ -316,7 +326,9 @@ func TestDaemonReloadNotAffectOthers(t *testing.T) {
} }
func TestDaemonDiscoveryReload(t *testing.T) { func TestDaemonDiscoveryReload(t *testing.T) {
daemon := &Daemon{} daemon := &Daemon{
imageService: images.NewImageService(images.ImageServiceConfig{}),
}
daemon.configStore = &config.Config{ daemon.configStore = &config.Config{
CommonConfig: config.CommonConfig{ CommonConfig: config.CommonConfig{
ClusterStore: "memory://127.0.0.1", ClusterStore: "memory://127.0.0.1",
@ -393,7 +405,9 @@ func TestDaemonDiscoveryReload(t *testing.T) {
} }
func TestDaemonDiscoveryReloadFromEmptyDiscovery(t *testing.T) { func TestDaemonDiscoveryReloadFromEmptyDiscovery(t *testing.T) {
daemon := &Daemon{} daemon := &Daemon{
imageService: images.NewImageService(images.ImageServiceConfig{}),
}
daemon.configStore = &config.Config{} daemon.configStore = &config.Config{}
valuesSet := make(map[string]interface{}) valuesSet := make(map[string]interface{})
@ -438,7 +452,9 @@ func TestDaemonDiscoveryReloadFromEmptyDiscovery(t *testing.T) {
} }
func TestDaemonDiscoveryReloadOnlyClusterAdvertise(t *testing.T) { func TestDaemonDiscoveryReloadOnlyClusterAdvertise(t *testing.T) {
daemon := &Daemon{} daemon := &Daemon{
imageService: images.NewImageService(images.ImageServiceConfig{}),
}
daemon.configStore = &config.Config{ daemon.configStore = &config.Config{
CommonConfig: config.CommonConfig{ CommonConfig: config.CommonConfig{
ClusterStore: "memory://127.0.0.1", ClusterStore: "memory://127.0.0.1",
@ -482,7 +498,9 @@ func TestDaemonDiscoveryReloadOnlyClusterAdvertise(t *testing.T) {
} }
func TestDaemonReloadNetworkDiagnosticPort(t *testing.T) { func TestDaemonReloadNetworkDiagnosticPort(t *testing.T) {
daemon := &Daemon{} daemon := &Daemon{
imageService: images.NewImageService(images.ImageServiceConfig{}),
}
daemon.configStore = &config.Config{} daemon.configStore = &config.Config{}
valuesSet := make(map[string]interface{}) valuesSet := make(map[string]interface{})

View file

@ -223,7 +223,7 @@ func (daemon *Daemon) Cleanup(container *container.Container) {
if err := daemon.conditionalUnmountOnCleanup(container); err != nil { if err := daemon.conditionalUnmountOnCleanup(container); err != nil {
// FIXME: remove once reference counting for graphdrivers has been refactored // FIXME: remove once reference counting for graphdrivers has been refactored
// Ensure that all the mounts are gone // Ensure that all the mounts are gone
if mountid, err := daemon.layerStores[container.OS].GetMountID(container.ID); err == nil { if mountid, err := daemon.imageService.GetLayerMountID(container.ID, container.OS); err == nil {
daemon.cleanupMountsByID(mountid) daemon.cleanupMountsByID(mountid)
} }
} }