|
@@ -18,6 +18,8 @@ import (
|
|
"time"
|
|
"time"
|
|
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/Sirupsen/logrus"
|
|
|
|
+ "github.com/docker/distribution/digest"
|
|
|
|
+ "github.com/docker/distribution/reference"
|
|
"github.com/docker/docker/api"
|
|
"github.com/docker/docker/api"
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/cliconfig"
|
|
"github.com/docker/docker/cliconfig"
|
|
@@ -29,9 +31,13 @@ import (
|
|
_ "github.com/docker/docker/daemon/graphdriver/vfs" // register vfs
|
|
_ "github.com/docker/docker/daemon/graphdriver/vfs" // register vfs
|
|
"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/distribution"
|
|
|
|
+ dmetadata "github.com/docker/docker/distribution/metadata"
|
|
derr "github.com/docker/docker/errors"
|
|
derr "github.com/docker/docker/errors"
|
|
- "github.com/docker/docker/graph"
|
|
|
|
"github.com/docker/docker/image"
|
|
"github.com/docker/docker/image"
|
|
|
|
+ "github.com/docker/docker/image/tarexport"
|
|
|
|
+ "github.com/docker/docker/layer"
|
|
|
|
+ "github.com/docker/docker/migrate/v1"
|
|
"github.com/docker/docker/pkg/archive"
|
|
"github.com/docker/docker/pkg/archive"
|
|
"github.com/docker/docker/pkg/discovery"
|
|
"github.com/docker/docker/pkg/discovery"
|
|
"github.com/docker/docker/pkg/fileutils"
|
|
"github.com/docker/docker/pkg/fileutils"
|
|
@@ -50,12 +56,14 @@ import (
|
|
"github.com/docker/docker/pkg/truncindex"
|
|
"github.com/docker/docker/pkg/truncindex"
|
|
"github.com/docker/docker/registry"
|
|
"github.com/docker/docker/registry"
|
|
"github.com/docker/docker/runconfig"
|
|
"github.com/docker/docker/runconfig"
|
|
|
|
+ "github.com/docker/docker/tag"
|
|
"github.com/docker/docker/utils"
|
|
"github.com/docker/docker/utils"
|
|
volumedrivers "github.com/docker/docker/volume/drivers"
|
|
volumedrivers "github.com/docker/docker/volume/drivers"
|
|
"github.com/docker/docker/volume/local"
|
|
"github.com/docker/docker/volume/local"
|
|
"github.com/docker/docker/volume/store"
|
|
"github.com/docker/docker/volume/store"
|
|
"github.com/docker/libnetwork"
|
|
"github.com/docker/libnetwork"
|
|
lntypes "github.com/docker/libnetwork/types"
|
|
lntypes "github.com/docker/libnetwork/types"
|
|
|
|
+ "github.com/docker/libtrust"
|
|
"github.com/opencontainers/runc/libcontainer"
|
|
"github.com/opencontainers/runc/libcontainer"
|
|
)
|
|
)
|
|
|
|
|
|
@@ -66,6 +74,15 @@ var (
|
|
errSystemNotSupported = errors.New("The Docker daemon is not supported on this platform.")
|
|
errSystemNotSupported = errors.New("The Docker daemon is not supported on this platform.")
|
|
)
|
|
)
|
|
|
|
|
|
|
|
+// ErrImageDoesNotExist is error returned when no image can be found for a reference.
|
|
|
|
+type ErrImageDoesNotExist struct {
|
|
|
|
+ RefOrID string
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (e ErrImageDoesNotExist) Error() string {
|
|
|
|
+ return fmt.Sprintf("no such id: %s", e.RefOrID)
|
|
|
|
+}
|
|
|
|
+
|
|
type contStore struct {
|
|
type contStore struct {
|
|
s map[string]*Container
|
|
s map[string]*Container
|
|
sync.Mutex
|
|
sync.Mutex
|
|
@@ -103,29 +120,33 @@ func (c *contStore) List() []*Container {
|
|
|
|
|
|
// Daemon holds information about the Docker daemon.
|
|
// Daemon holds information about the Docker daemon.
|
|
type Daemon struct {
|
|
type Daemon struct {
|
|
- ID string
|
|
|
|
- repository string
|
|
|
|
- sysInitPath string
|
|
|
|
- containers *contStore
|
|
|
|
- execCommands *exec.Store
|
|
|
|
- graph *graph.Graph
|
|
|
|
- repositories *graph.TagStore
|
|
|
|
- idIndex *truncindex.TruncIndex
|
|
|
|
- configStore *Config
|
|
|
|
- containerGraphDB *graphdb.Database
|
|
|
|
- driver graphdriver.Driver
|
|
|
|
- execDriver execdriver.Driver
|
|
|
|
- statsCollector *statsCollector
|
|
|
|
- defaultLogConfig runconfig.LogConfig
|
|
|
|
- RegistryService *registry.Service
|
|
|
|
- EventsService *events.Events
|
|
|
|
- netController libnetwork.NetworkController
|
|
|
|
- volumes *store.VolumeStore
|
|
|
|
- discoveryWatcher discovery.Watcher
|
|
|
|
- root string
|
|
|
|
- shutdown bool
|
|
|
|
- uidMaps []idtools.IDMap
|
|
|
|
- gidMaps []idtools.IDMap
|
|
|
|
|
|
+ ID string
|
|
|
|
+ repository string
|
|
|
|
+ sysInitPath string
|
|
|
|
+ containers *contStore
|
|
|
|
+ execCommands *exec.Store
|
|
|
|
+ tagStore tag.Store
|
|
|
|
+ distributionPool *distribution.Pool
|
|
|
|
+ distributionMetadataStore dmetadata.Store
|
|
|
|
+ trustKey libtrust.PrivateKey
|
|
|
|
+ idIndex *truncindex.TruncIndex
|
|
|
|
+ configStore *Config
|
|
|
|
+ containerGraphDB *graphdb.Database
|
|
|
|
+ driver graphdriver.Driver
|
|
|
|
+ execDriver execdriver.Driver
|
|
|
|
+ statsCollector *statsCollector
|
|
|
|
+ defaultLogConfig runconfig.LogConfig
|
|
|
|
+ RegistryService *registry.Service
|
|
|
|
+ EventsService *events.Events
|
|
|
|
+ netController libnetwork.NetworkController
|
|
|
|
+ volumes *store.VolumeStore
|
|
|
|
+ discoveryWatcher discovery.Watcher
|
|
|
|
+ root string
|
|
|
|
+ shutdown bool
|
|
|
|
+ uidMaps []idtools.IDMap
|
|
|
|
+ gidMaps []idtools.IDMap
|
|
|
|
+ layerStore layer.Store
|
|
|
|
+ imageStore image.Store
|
|
}
|
|
}
|
|
|
|
|
|
// Get looks for a container using the provided information, which could be
|
|
// Get looks for a container using the provided information, which could be
|
|
@@ -229,9 +250,7 @@ func (daemon *Daemon) Register(container *Container) error {
|
|
|
|
|
|
container.unmountIpcMounts(mount.Unmount)
|
|
container.unmountIpcMounts(mount.Unmount)
|
|
|
|
|
|
- if err := daemon.Unmount(container); err != nil {
|
|
|
|
- logrus.Debugf("unmount error %s", err)
|
|
|
|
- }
|
|
|
|
|
|
+ daemon.Unmount(container)
|
|
if err := container.toDiskLocking(); err != nil {
|
|
if err := container.toDiskLocking(); err != nil {
|
|
logrus.Errorf("Error saving stopped state to disk: %v", err)
|
|
logrus.Errorf("Error saving stopped state to disk: %v", err)
|
|
}
|
|
}
|
|
@@ -456,7 +475,7 @@ func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint *stringutils.StrSlic
|
|
return cmdSlice[0], cmdSlice[1:]
|
|
return cmdSlice[0], cmdSlice[1:]
|
|
}
|
|
}
|
|
|
|
|
|
-func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID string) (*Container, error) {
|
|
|
|
|
|
+func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID image.ID) (*Container, error) {
|
|
var (
|
|
var (
|
|
id string
|
|
id string
|
|
err error
|
|
err error
|
|
@@ -542,7 +561,7 @@ func (daemon *Daemon) GetLabels(id string) map[string]string {
|
|
return container.Config.Labels
|
|
return container.Config.Labels
|
|
}
|
|
}
|
|
|
|
|
|
- img, err := daemon.repositories.LookupImage(id)
|
|
|
|
|
|
+ img, err := daemon.GetImage(id)
|
|
if err == nil {
|
|
if err == nil {
|
|
return img.ContainerConfig.Labels
|
|
return img.ContainerConfig.Labels
|
|
}
|
|
}
|
|
@@ -702,8 +721,25 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
|
|
return nil, err
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
|
|
- logrus.Debug("Creating images graph")
|
|
|
|
- g, err := graph.NewGraph(filepath.Join(config.Root, "graph"), d.driver, uidMaps, gidMaps)
|
|
|
|
|
|
+ imageRoot := filepath.Join(config.Root, "image", d.driver.String())
|
|
|
|
+ fms, err := layer.NewFSMetadataStore(filepath.Join(imageRoot, "layerdb"))
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ d.layerStore, err = layer.NewStore(fms, d.driver)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ distributionPool := distribution.NewPool()
|
|
|
|
+
|
|
|
|
+ ifs, err := image.NewFSStoreBackend(filepath.Join(imageRoot, "imagedb"))
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ d.imageStore, err = image.NewImageStore(ifs, d.layerStore)
|
|
if err != nil {
|
|
if err != nil {
|
|
return nil, err
|
|
return nil, err
|
|
}
|
|
}
|
|
@@ -725,23 +761,24 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
|
|
return nil, err
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ distributionMetadataStore, err := dmetadata.NewFSMetadataStore(filepath.Join(imageRoot, "distribution"))
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+
|
|
eventsService := events.New()
|
|
eventsService := events.New()
|
|
- logrus.Debug("Creating repository list")
|
|
|
|
- tagCfg := &graph.TagStoreConfig{
|
|
|
|
- Graph: g,
|
|
|
|
- Key: trustKey,
|
|
|
|
- Registry: registryService,
|
|
|
|
- Events: eventsService,
|
|
|
|
- }
|
|
|
|
- repositories, err := graph.NewTagStore(filepath.Join(config.Root, "repositories-"+d.driver.String()), tagCfg)
|
|
|
|
|
|
+
|
|
|
|
+ tagStore, err := tag.NewTagStore(filepath.Join(imageRoot, "repositories.json"))
|
|
if err != nil {
|
|
if err != nil {
|
|
- return nil, fmt.Errorf("Couldn't create Tag store repositories-%s: %s", d.driver.String(), err)
|
|
|
|
|
|
+ return nil, fmt.Errorf("Couldn't create Tag store repositories: %s", err)
|
|
}
|
|
}
|
|
|
|
|
|
- if restorer, ok := d.driver.(graphdriver.ImageRestorer); ok {
|
|
|
|
- if _, err := restorer.RestoreCustomImages(repositories, g); err != nil {
|
|
|
|
- return nil, fmt.Errorf("Couldn't restore custom images: %s", err)
|
|
|
|
- }
|
|
|
|
|
|
+ if err := restoreCustomImage(d.driver, d.imageStore, d.layerStore, tagStore); err != nil {
|
|
|
|
+ return nil, fmt.Errorf("Couldn't restore custom images: %s", err)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if err := v1.Migrate(config.Root, d.driver.String(), d.layerStore, d.imageStore, tagStore, distributionMetadataStore); err != nil {
|
|
|
|
+ return nil, err
|
|
}
|
|
}
|
|
|
|
|
|
// Discovery is only enabled when the daemon is launched with an address to advertise. When
|
|
// Discovery is only enabled when the daemon is launched with an address to advertise. When
|
|
@@ -792,8 +829,10 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
|
|
d.repository = daemonRepo
|
|
d.repository = daemonRepo
|
|
d.containers = &contStore{s: make(map[string]*Container)}
|
|
d.containers = &contStore{s: make(map[string]*Container)}
|
|
d.execCommands = exec.NewStore()
|
|
d.execCommands = exec.NewStore()
|
|
- d.graph = g
|
|
|
|
- d.repositories = repositories
|
|
|
|
|
|
+ d.tagStore = tagStore
|
|
|
|
+ d.distributionPool = distributionPool
|
|
|
|
+ d.distributionMetadataStore = distributionMetadataStore
|
|
|
|
+ d.trustKey = trustKey
|
|
d.idIndex = truncindex.NewTruncIndex([]string{})
|
|
d.idIndex = truncindex.NewTruncIndex([]string{})
|
|
d.configStore = config
|
|
d.configStore = config
|
|
d.sysInitPath = sysInitPath
|
|
d.sysInitPath = sysInitPath
|
|
@@ -910,28 +949,44 @@ func (daemon *Daemon) Shutdown() error {
|
|
// Mount sets container.basefs
|
|
// Mount sets container.basefs
|
|
// (is it not set coming in? why is it unset?)
|
|
// (is it not set coming in? why is it unset?)
|
|
func (daemon *Daemon) Mount(container *Container) error {
|
|
func (daemon *Daemon) Mount(container *Container) error {
|
|
- dir, err := daemon.driver.Get(container.ID, container.getMountLabel())
|
|
|
|
|
|
+ var layerID layer.ChainID
|
|
|
|
+ if container.ImageID != "" {
|
|
|
|
+ img, err := daemon.imageStore.Get(container.ImageID)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ layerID = img.RootFS.ChainID()
|
|
|
|
+ }
|
|
|
|
+ rwlayer, err := daemon.layerStore.Mount(container.ID, layerID, container.getMountLabel(), daemon.setupInitLayer)
|
|
if err != nil {
|
|
if err != nil {
|
|
- return fmt.Errorf("Error getting container %s from driver %s: %s", container.ID, daemon.driver, err)
|
|
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ dir, err := rwlayer.Path()
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
}
|
|
}
|
|
|
|
+ logrus.Debugf("container mounted via layerStore: %v", dir)
|
|
|
|
|
|
if container.basefs != dir {
|
|
if container.basefs != dir {
|
|
// The mount path reported by the graph driver should always be trusted on Windows, since the
|
|
// The mount path reported by the graph driver should always be trusted on Windows, since the
|
|
// volume path for a given mounted layer may change over time. This should only be an error
|
|
// volume path for a given mounted layer may change over time. This should only be an error
|
|
// on non-Windows operating systems.
|
|
// on non-Windows operating systems.
|
|
if container.basefs != "" && runtime.GOOS != "windows" {
|
|
if container.basefs != "" && runtime.GOOS != "windows" {
|
|
- daemon.driver.Put(container.ID)
|
|
|
|
|
|
+ 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.driver, container.ID, container.basefs, dir)
|
|
daemon.driver, container.ID, container.basefs, dir)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- container.basefs = dir
|
|
|
|
|
|
+ container.basefs = dir // TODO: combine these fields
|
|
|
|
+ container.rwlayer = rwlayer
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
// Unmount unsets the container base filesystem
|
|
// Unmount unsets the container base filesystem
|
|
-func (daemon *Daemon) Unmount(container *Container) error {
|
|
|
|
- return daemon.driver.Put(container.ID)
|
|
|
|
|
|
+func (daemon *Daemon) Unmount(container *Container) {
|
|
|
|
+ if err := daemon.layerStore.Unmount(container.ID); err != nil {
|
|
|
|
+ logrus.Errorf("Error unmounting container %s: %s", container.ID, err)
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
// Run uses the execution driver to run a given container
|
|
// Run uses the execution driver to run a given container
|
|
@@ -962,82 +1017,46 @@ func (daemon *Daemon) unsubscribeToContainerStats(c *Container, ch chan interfac
|
|
}
|
|
}
|
|
|
|
|
|
func (daemon *Daemon) changes(container *Container) ([]archive.Change, error) {
|
|
func (daemon *Daemon) changes(container *Container) ([]archive.Change, error) {
|
|
- initID := fmt.Sprintf("%s-init", container.ID)
|
|
|
|
- return daemon.driver.Changes(container.ID, initID)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func (daemon *Daemon) diff(container *Container) (archive.Archive, error) {
|
|
|
|
- initID := fmt.Sprintf("%s-init", container.ID)
|
|
|
|
- return daemon.driver.Diff(container.ID, initID)
|
|
|
|
|
|
+ return daemon.layerStore.Changes(container.ID)
|
|
}
|
|
}
|
|
|
|
|
|
-func (daemon *Daemon) createRootfs(container *Container) error {
|
|
|
|
- // Step 1: create the container directory.
|
|
|
|
- // This doubles as a barrier to avoid race conditions.
|
|
|
|
- rootUID, rootGID, err := idtools.GetRootUIDGID(daemon.uidMaps, daemon.gidMaps)
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
|
|
+// TagImage creates a tag in the repository reponame, pointing to the image named
|
|
|
|
+// imageName. If force is true, an existing tag with the same name may be
|
|
|
|
+// overwritten.
|
|
|
|
+func (daemon *Daemon) TagImage(newTag reference.Named, imageName string, force bool) error {
|
|
|
|
+ if _, isDigested := newTag.(reference.Digested); isDigested {
|
|
|
|
+ return errors.New("refusing to create a tag with a digest reference")
|
|
}
|
|
}
|
|
- if err := idtools.MkdirAs(container.root, 0700, rootUID, rootGID); err != nil {
|
|
|
|
- return err
|
|
|
|
|
|
+ if newTag.Name() == string(digest.Canonical) {
|
|
|
|
+ return errors.New("refusing to create an ambiguous tag using digest algorithm as name")
|
|
}
|
|
}
|
|
- initID := fmt.Sprintf("%s-init", container.ID)
|
|
|
|
|
|
|
|
- if err := daemon.driver.Create(initID, container.ImageID, container.getMountLabel()); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- initPath, err := daemon.driver.Get(initID, "")
|
|
|
|
|
|
+ newTag = registry.NormalizeLocalReference(newTag)
|
|
|
|
+ imageID, err := daemon.GetImageID(imageName)
|
|
if err != nil {
|
|
if err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
-
|
|
|
|
- if err := setupInitLayer(initPath, rootUID, rootGID); err != nil {
|
|
|
|
- if err := daemon.driver.Put(initID); err != nil {
|
|
|
|
- logrus.Errorf("Failed to Put init layer: %v", err)
|
|
|
|
- }
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // We want to unmount init layer before we take snapshot of it
|
|
|
|
- // for the actual container.
|
|
|
|
- if err := daemon.driver.Put(initID); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if err := daemon.driver.Create(container.ID, initID, ""); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- return nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// Graph returns *graph.Graph which can be using for layers graph operations.
|
|
|
|
-func (daemon *Daemon) Graph() *graph.Graph {
|
|
|
|
- return daemon.graph
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// TagImage creates a tag in the repository reponame, pointing to the image named
|
|
|
|
-// imageName. If force is true, an existing tag with the same name may be
|
|
|
|
-// overwritten.
|
|
|
|
-func (daemon *Daemon) TagImage(repoName, tag, imageName string, force bool) error {
|
|
|
|
- if err := daemon.repositories.Tag(repoName, tag, imageName, force); err != nil {
|
|
|
|
- return err
|
|
|
|
- }
|
|
|
|
- daemon.EventsService.Log("tag", utils.ImageReference(repoName, tag), "")
|
|
|
|
- return nil
|
|
|
|
|
|
+ daemon.EventsService.Log("tag", newTag.String(), "")
|
|
|
|
+ return daemon.tagStore.Add(newTag, imageID, force)
|
|
}
|
|
}
|
|
|
|
|
|
// 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(image string, tag string, imagePullConfig *graph.ImagePullConfig) error {
|
|
|
|
- return daemon.repositories.Pull(image, tag, imagePullConfig)
|
|
|
|
-}
|
|
|
|
|
|
+func (daemon *Daemon) PullImage(ref reference.Named, metaHeaders map[string][]string, authConfig *cliconfig.AuthConfig, outStream io.Writer) error {
|
|
|
|
+ imagePullConfig := &distribution.ImagePullConfig{
|
|
|
|
+ MetaHeaders: metaHeaders,
|
|
|
|
+ AuthConfig: authConfig,
|
|
|
|
+ OutStream: outStream,
|
|
|
|
+ RegistryService: daemon.RegistryService,
|
|
|
|
+ EventsService: daemon.EventsService,
|
|
|
|
+ MetadataStore: daemon.distributionMetadataStore,
|
|
|
|
+ LayerStore: daemon.layerStore,
|
|
|
|
+ ImageStore: daemon.imageStore,
|
|
|
|
+ TagStore: daemon.tagStore,
|
|
|
|
+ Pool: daemon.distributionPool,
|
|
|
|
+ }
|
|
|
|
|
|
-// ImportImage imports an image, getting the archived layer data either from
|
|
|
|
-// 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
|
|
|
|
-// the repo and tag arguments, respectively.
|
|
|
|
-func (daemon *Daemon) ImportImage(src, repo, tag, msg string, inConfig io.ReadCloser, outStream io.Writer, containerConfig *runconfig.Config) error {
|
|
|
|
- return daemon.repositories.Import(src, repo, tag, msg, inConfig, outStream, containerConfig)
|
|
|
|
|
|
+ return distribution.Pull(ref, imagePullConfig)
|
|
}
|
|
}
|
|
|
|
|
|
// ExportImage exports a list of images to the given output stream. The
|
|
// ExportImage exports a list of images to the given output stream. The
|
|
@@ -1046,47 +1065,214 @@ func (daemon *Daemon) ImportImage(src, repo, tag, msg string, inConfig io.ReadCl
|
|
// 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 (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error {
|
|
- return daemon.repositories.ImageExport(names, outStream)
|
|
|
|
|
|
+ imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.tagStore)
|
|
|
|
+ return imageExporter.Save(names, outStream)
|
|
}
|
|
}
|
|
|
|
|
|
// PushImage initiates a push operation on the repository named localName.
|
|
// PushImage initiates a push operation on the repository named localName.
|
|
-func (daemon *Daemon) PushImage(localName string, imagePushConfig *graph.ImagePushConfig) error {
|
|
|
|
- return daemon.repositories.Push(localName, imagePushConfig)
|
|
|
|
|
|
+func (daemon *Daemon) PushImage(ref reference.Named, metaHeaders map[string][]string, authConfig *cliconfig.AuthConfig, outStream io.Writer) error {
|
|
|
|
+ imagePushConfig := &distribution.ImagePushConfig{
|
|
|
|
+ MetaHeaders: metaHeaders,
|
|
|
|
+ AuthConfig: authConfig,
|
|
|
|
+ OutStream: outStream,
|
|
|
|
+ RegistryService: daemon.RegistryService,
|
|
|
|
+ EventsService: daemon.EventsService,
|
|
|
|
+ MetadataStore: daemon.distributionMetadataStore,
|
|
|
|
+ LayerStore: daemon.layerStore,
|
|
|
|
+ ImageStore: daemon.imageStore,
|
|
|
|
+ TagStore: daemon.tagStore,
|
|
|
|
+ TrustKey: daemon.trustKey,
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return distribution.Push(ref, imagePushConfig)
|
|
}
|
|
}
|
|
|
|
|
|
// 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 (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
|
|
- return daemon.repositories.Lookup(name)
|
|
|
|
|
|
+ img, err := daemon.GetImage(name)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, fmt.Errorf("No such image: %s", name)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ refs := daemon.tagStore.References(img.ID())
|
|
|
|
+ repoTags := []string{}
|
|
|
|
+ repoDigests := []string{}
|
|
|
|
+ for _, ref := range refs {
|
|
|
|
+ switch ref.(type) {
|
|
|
|
+ case reference.Tagged:
|
|
|
|
+ repoTags = append(repoTags, ref.String())
|
|
|
|
+ case reference.Digested:
|
|
|
|
+ repoDigests = append(repoDigests, ref.String())
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var size int64
|
|
|
|
+ var layerMetadata map[string]string
|
|
|
|
+ layerID := img.RootFS.ChainID()
|
|
|
|
+ if layerID != "" {
|
|
|
|
+ l, err := daemon.layerStore.Get(layerID)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ defer layer.ReleaseAndLog(daemon.layerStore, l)
|
|
|
|
+ size, err = l.Size()
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ layerMetadata, err = l.Metadata()
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ imageInspect := &types.ImageInspect{
|
|
|
|
+ ID: img.ID().String(),
|
|
|
|
+ RepoTags: repoTags,
|
|
|
|
+ RepoDigests: repoDigests,
|
|
|
|
+ Parent: img.Parent.String(),
|
|
|
|
+ Comment: img.Comment,
|
|
|
|
+ Created: img.Created.Format(time.RFC3339Nano),
|
|
|
|
+ Container: img.Container,
|
|
|
|
+ ContainerConfig: &img.ContainerConfig,
|
|
|
|
+ DockerVersion: img.DockerVersion,
|
|
|
|
+ Author: img.Author,
|
|
|
|
+ Config: img.Config,
|
|
|
|
+ Architecture: img.Architecture,
|
|
|
|
+ Os: img.OS,
|
|
|
|
+ Size: size,
|
|
|
|
+ VirtualSize: size, // TODO: field unused, deprecate
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ imageInspect.GraphDriver.Name = daemon.driver.String()
|
|
|
|
+
|
|
|
|
+ imageInspect.GraphDriver.Data = layerMetadata
|
|
|
|
+
|
|
|
|
+ return imageInspect, nil
|
|
}
|
|
}
|
|
|
|
|
|
// 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) error {
|
|
func (daemon *Daemon) LoadImage(inTar io.ReadCloser, outStream io.Writer) error {
|
|
- return daemon.repositories.Load(inTar, outStream)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// ListImages returns a filtered list of images. filterArgs is a JSON-encoded set
|
|
|
|
-// of filter arguments which will be interpreted by pkg/parsers/filters.
|
|
|
|
-// 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
|
|
|
|
-// the heads.
|
|
|
|
-func (daemon *Daemon) ListImages(filterArgs, filter string, all bool) ([]*types.Image, error) {
|
|
|
|
- return daemon.repositories.Images(filterArgs, filter, all)
|
|
|
|
|
|
+ imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.tagStore)
|
|
|
|
+ return imageExporter.Load(inTar, outStream)
|
|
}
|
|
}
|
|
|
|
|
|
// 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) ([]*types.ImageHistory, error) {
|
|
func (daemon *Daemon) ImageHistory(name string) ([]*types.ImageHistory, error) {
|
|
- return daemon.repositories.History(name)
|
|
|
|
|
|
+ img, err := daemon.GetImage(name)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ history := []*types.ImageHistory{}
|
|
|
|
+
|
|
|
|
+ layerCounter := 0
|
|
|
|
+ rootFS := *img.RootFS
|
|
|
|
+ rootFS.DiffIDs = nil
|
|
|
|
+
|
|
|
|
+ for _, h := range img.History {
|
|
|
|
+ var layerSize int64
|
|
|
|
+
|
|
|
|
+ if !h.EmptyLayer {
|
|
|
|
+ if len(img.RootFS.DiffIDs) <= layerCounter {
|
|
|
|
+ return nil, errors.New("too many non-empty layers in History section")
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ rootFS.Append(img.RootFS.DiffIDs[layerCounter])
|
|
|
|
+ l, err := daemon.layerStore.Get(rootFS.ChainID())
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ layerSize, err = l.DiffSize()
|
|
|
|
+ layer.ReleaseAndLog(daemon.layerStore, l)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ layerCounter++
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ history = append([]*types.ImageHistory{{
|
|
|
|
+ ID: "<missing>",
|
|
|
|
+ Created: h.Created.Unix(),
|
|
|
|
+ CreatedBy: h.CreatedBy,
|
|
|
|
+ Comment: h.Comment,
|
|
|
|
+ Size: layerSize,
|
|
|
|
+ }}, history...)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Fill in image IDs and tags
|
|
|
|
+ histImg := img
|
|
|
|
+ id := img.ID()
|
|
|
|
+ for _, h := range history {
|
|
|
|
+ h.ID = id.String()
|
|
|
|
+
|
|
|
|
+ var tags []string
|
|
|
|
+ for _, r := range daemon.tagStore.References(id) {
|
|
|
|
+ if _, ok := r.(reference.NamedTagged); ok {
|
|
|
|
+ tags = append(tags, r.String())
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ h.Tags = tags
|
|
|
|
+
|
|
|
|
+ id = histImg.Parent
|
|
|
|
+ if id == "" {
|
|
|
|
+ break
|
|
|
|
+ }
|
|
|
|
+ histImg, err = daemon.GetImage(id.String())
|
|
|
|
+ if err != nil {
|
|
|
|
+ break
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return history, nil
|
|
}
|
|
}
|
|
|
|
|
|
-// GetImage returns pointer to an Image struct corresponding to the given
|
|
|
|
-// name. The name can include an optional tag; otherwise the default tag will
|
|
|
|
-// be used.
|
|
|
|
-func (daemon *Daemon) GetImage(name string) (*image.Image, error) {
|
|
|
|
- return daemon.repositories.LookupImage(name)
|
|
|
|
|
|
+// GetImageID returns an image ID corresponding to the image referred to by
|
|
|
|
+// refOrID.
|
|
|
|
+func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
|
|
|
|
+ // Treat as an ID
|
|
|
|
+ if id, err := digest.ParseDigest(refOrID); err == nil {
|
|
|
|
+ return image.ID(id), nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Treat it as a possible tag or digest reference
|
|
|
|
+ if ref, err := reference.ParseNamed(refOrID); err == nil {
|
|
|
|
+ ref = registry.NormalizeLocalReference(ref)
|
|
|
|
+ if id, err := daemon.tagStore.Get(ref); err == nil {
|
|
|
|
+ return id, nil
|
|
|
|
+ }
|
|
|
|
+ if tagged, ok := ref.(reference.Tagged); ok {
|
|
|
|
+ if id, err := daemon.imageStore.Search(tagged.Tag()); err == nil {
|
|
|
|
+ for _, namedRef := range daemon.tagStore.References(id) {
|
|
|
|
+ if namedRef.Name() == ref.Name() {
|
|
|
|
+ return id, nil
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Search based on ID
|
|
|
|
+ if id, err := daemon.imageStore.Search(refOrID); err == nil {
|
|
|
|
+ return id, nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return "", ErrImageDoesNotExist{refOrID}
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// GetImage returns an image corresponding to the image referred to by refOrID.
|
|
|
|
+func (daemon *Daemon) GetImage(refOrID string) (*image.Image, error) {
|
|
|
|
+ imgID, err := daemon.GetImageID(refOrID)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ return daemon.imageStore.Get(imgID)
|
|
}
|
|
}
|
|
|
|
|
|
func (daemon *Daemon) config() *Config {
|
|
func (daemon *Daemon) config() *Config {
|
|
@@ -1132,33 +1318,23 @@ func (daemon *Daemon) GetRemappedUIDGID() (int, int) {
|
|
// of the image with imgID, that had the same config when it was
|
|
// of the image with imgID, that had the same config when it was
|
|
// created. nil is returned if a child cannot be found. An error is
|
|
// created. nil is returned if a child cannot be found. An error is
|
|
// returned if the parent image cannot be found.
|
|
// returned if the parent image cannot be found.
|
|
-func (daemon *Daemon) ImageGetCached(imgID string, config *runconfig.Config) (*image.Image, error) {
|
|
|
|
- // for now just exit if imgID has no children.
|
|
|
|
- // maybe parentRefs in graph could be used to store
|
|
|
|
- // the Image obj children for faster lookup below but this can
|
|
|
|
- // be quite memory hungry.
|
|
|
|
- if !daemon.Graph().HasChildren(imgID) {
|
|
|
|
- return nil, nil
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+func (daemon *Daemon) ImageGetCached(imgID image.ID, config *runconfig.Config) (*image.Image, error) {
|
|
// Retrieve all images
|
|
// Retrieve all images
|
|
- images := daemon.Graph().Map()
|
|
|
|
|
|
+ imgs := daemon.Map()
|
|
|
|
|
|
- // Store the tree in a map of map (map[parentId][childId])
|
|
|
|
- imageMap := make(map[string]map[string]struct{})
|
|
|
|
- for _, img := range images {
|
|
|
|
- if _, exists := imageMap[img.Parent]; !exists {
|
|
|
|
- imageMap[img.Parent] = make(map[string]struct{})
|
|
|
|
|
|
+ var siblings []image.ID
|
|
|
|
+ for id, img := range imgs {
|
|
|
|
+ if img.Parent == imgID {
|
|
|
|
+ siblings = append(siblings, id)
|
|
}
|
|
}
|
|
- imageMap[img.Parent][img.ID] = struct{}{}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
// Loop on the children of the given image and check the config
|
|
// Loop on the children of the given image and check the config
|
|
var match *image.Image
|
|
var match *image.Image
|
|
- for elem := range imageMap[imgID] {
|
|
|
|
- img, ok := images[elem]
|
|
|
|
|
|
+ for _, id := range siblings {
|
|
|
|
+ img, ok := imgs[id]
|
|
if !ok {
|
|
if !ok {
|
|
- return nil, fmt.Errorf("unable to find image %q", elem)
|
|
|
|
|
|
+ return nil, fmt.Errorf("unable to find image %q", id)
|
|
}
|
|
}
|
|
if runconfig.Compare(&img.ContainerConfig, config) {
|
|
if runconfig.Compare(&img.ContainerConfig, config) {
|
|
if match == nil || match.Created.Before(img.Created) {
|
|
if match == nil || match.Created.Before(img.Created) {
|
|
@@ -1179,6 +1355,12 @@ func tempDir(rootDir string, rootUID, rootGID int) (string, error) {
|
|
}
|
|
}
|
|
|
|
|
|
func (daemon *Daemon) setHostConfig(container *Container, hostConfig *runconfig.HostConfig) error {
|
|
func (daemon *Daemon) setHostConfig(container *Container, hostConfig *runconfig.HostConfig) error {
|
|
|
|
+ container.Lock()
|
|
|
|
+ if err := parseSecurityOpt(container, hostConfig); err != nil {
|
|
|
|
+ container.Unlock()
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ container.Unlock()
|
|
|
|
|
|
// Do not lock while creating volumes since this could be calling out to external plugins
|
|
// Do not lock while creating volumes since this could be calling out to external plugins
|
|
// Don't want to block other actions, like `docker ps` because we're waiting on an external plugin
|
|
// Don't want to block other actions, like `docker ps` because we're waiting on an external plugin
|
|
@@ -1199,6 +1381,11 @@ func (daemon *Daemon) setHostConfig(container *Container, hostConfig *runconfig.
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+func (daemon *Daemon) setupInitLayer(initPath string) error {
|
|
|
|
+ rootUID, rootGID := daemon.GetRemappedUIDGID()
|
|
|
|
+ return setupInitLayer(initPath, rootUID, rootGID)
|
|
|
|
+}
|
|
|
|
+
|
|
func setDefaultMtu(config *Config) {
|
|
func setDefaultMtu(config *Config) {
|
|
// do nothing if the config does not have the default 0 value.
|
|
// do nothing if the config does not have the default 0 value.
|
|
if config.Mtu != 0 {
|
|
if config.Mtu != 0 {
|