From fb48bf518bfc200ee9a230a7bedaf640df4e5dae Mon Sep 17 00:00:00 2001 From: Vincent Demeester Date: Tue, 24 May 2016 17:49:26 +0200 Subject: [PATCH] Move some container related methods and structs to smaller files Signed-off-by: Vincent Demeester --- daemon/changes.go | 2 +- daemon/container.go | 145 +++++++++++++++++++++++++ daemon/create.go | 12 +++ daemon/daemon.go | 257 -------------------------------------------- daemon/kill.go | 4 + daemon/names.go | 108 +++++++++++++++++++ daemon/stats.go | 9 ++ 7 files changed, 279 insertions(+), 258 deletions(-) create mode 100644 daemon/container.go create mode 100644 daemon/names.go diff --git a/daemon/changes.go b/daemon/changes.go index 5bc5b9d55e..7a58763cd9 100644 --- a/daemon/changes.go +++ b/daemon/changes.go @@ -11,5 +11,5 @@ func (daemon *Daemon) ContainerChanges(name string) ([]archive.Change, error) { container.Lock() defer container.Unlock() - return daemon.changes(container) + return container.RWLayer.Changes() } diff --git a/daemon/container.go b/daemon/container.go new file mode 100644 index 0000000000..645db26095 --- /dev/null +++ b/daemon/container.go @@ -0,0 +1,145 @@ +package daemon + +import ( + "fmt" + "path/filepath" + "time" + + "github.com/docker/docker/container" + "github.com/docker/docker/daemon/network" + "github.com/docker/docker/errors" + "github.com/docker/docker/image" + "github.com/docker/docker/pkg/truncindex" + containertypes "github.com/docker/engine-api/types/container" +) + +// GetContainer looks for a container using the provided information, which could be +// one of the following inputs from the caller: +// - A full container ID, which will exact match a container in daemon's list +// - A container name, which will only exact match via the GetByName() function +// - A partial container ID prefix (e.g. short ID) of any length that is +// unique enough to only return a single container object +// If none of these searches succeed, an error is returned +func (daemon *Daemon) GetContainer(prefixOrName string) (*container.Container, error) { + if len(prefixOrName) == 0 { + return nil, errors.NewBadRequestError(fmt.Errorf("No container name or ID supplied")) + } + + if containerByID := daemon.containers.Get(prefixOrName); containerByID != nil { + // prefix is an exact match to a full container ID + return containerByID, nil + } + + // GetByName will match only an exact name provided; we ignore errors + if containerByName, _ := daemon.GetByName(prefixOrName); containerByName != nil { + // prefix is an exact match to a full container Name + return containerByName, nil + } + + containerID, indexError := daemon.idIndex.Get(prefixOrName) + if indexError != nil { + // When truncindex defines an error type, use that instead + if indexError == truncindex.ErrNotExist { + err := fmt.Errorf("No such container: %s", prefixOrName) + return nil, errors.NewRequestNotFoundError(err) + } + return nil, indexError + } + return daemon.containers.Get(containerID), nil +} + +// Exists returns a true if a container of the specified ID or name exists, +// false otherwise. +func (daemon *Daemon) Exists(id string) bool { + c, _ := daemon.GetContainer(id) + return c != nil +} + +// IsPaused returns a bool indicating if the specified container is paused. +func (daemon *Daemon) IsPaused(id string) bool { + c, _ := daemon.GetContainer(id) + return c.State.IsPaused() +} + +func (daemon *Daemon) containerRoot(id string) string { + return filepath.Join(daemon.repository, id) +} + +// Load reads the contents of a container from disk +// This is typically done at startup. +func (daemon *Daemon) load(id string) (*container.Container, error) { + container := daemon.newBaseContainer(id) + + if err := container.FromDisk(); err != nil { + return nil, err + } + + if container.ID != id { + return container, fmt.Errorf("Container %s is stored at %s", container.ID, id) + } + + return container, nil +} + +// Register makes a container object usable by the daemon as +func (daemon *Daemon) Register(c *container.Container) error { + // Attach to stdout and stderr + if c.Config.OpenStdin { + c.NewInputPipes() + } else { + c.NewNopInputPipe() + } + + daemon.containers.Add(c.ID, c) + daemon.idIndex.Add(c.ID) + + return nil +} + +func (daemon *Daemon) newContainer(name string, config *containertypes.Config, imgID image.ID) (*container.Container, error) { + var ( + id string + err error + noExplicitName = name == "" + ) + id, name, err = daemon.generateIDAndName(name) + if err != nil { + return nil, err + } + + daemon.generateHostname(id, config) + entrypoint, args := daemon.getEntrypointAndArgs(config.Entrypoint, config.Cmd) + + base := daemon.newBaseContainer(id) + base.Created = time.Now().UTC() + base.Path = entrypoint + base.Args = args //FIXME: de-duplicate from config + base.Config = config + base.HostConfig = &containertypes.HostConfig{} + base.ImageID = imgID + base.NetworkSettings = &network.Settings{IsAnonymousEndpoint: noExplicitName} + base.Name = name + base.Driver = daemon.GraphDriverName() + + return base, err +} + +// GetByName returns a container given a name. +func (daemon *Daemon) GetByName(name string) (*container.Container, error) { + if len(name) == 0 { + return nil, fmt.Errorf("No container name supplied") + } + fullName := name + if name[0] != '/' { + fullName = "/" + name + } + id, err := daemon.nameIndex.Get(fullName) + if err != nil { + return nil, fmt.Errorf("Could not find entity for %s", name) + } + e := daemon.containers.Get(id) + if e == nil { + return nil, fmt.Errorf("Could not find container for entity id %s", id) + } + return e, nil +} diff --git a/daemon/create.go b/daemon/create.go index eea3a1351d..b4e19dd372 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -218,3 +218,15 @@ func (daemon *Daemon) VolumeCreate(name, driverName string, opts, labels map[str apiV.Mountpoint = v.Path() return apiV, nil } + +func (daemon *Daemon) mergeAndVerifyConfig(config *containertypes.Config, img *image.Image) error { + if img != nil && img.Config != nil { + if err := merge(config, img.Config); err != nil { + return err + } + } + if len(config.Entrypoint) == 0 && len(config.Cmd) == 0 { + return fmt.Errorf("No command specified") + } + return nil +} diff --git a/daemon/daemon.go b/daemon/daemon.go index 951bc5d22a..c49e10cc4f 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -36,7 +36,6 @@ import ( "github.com/docker/engine-api/types/strslice" // register graph drivers _ "github.com/docker/docker/daemon/graphdriver/register" - "github.com/docker/docker/daemon/network" dmetadata "github.com/docker/docker/distribution/metadata" "github.com/docker/docker/distribution/xfer" "github.com/docker/docker/dockerversion" @@ -44,16 +43,13 @@ import ( "github.com/docker/docker/layer" "github.com/docker/docker/libcontainerd" "github.com/docker/docker/migrate/v1" - "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/graphdb" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/namesgenerator" "github.com/docker/docker/pkg/progress" "github.com/docker/docker/pkg/registrar" "github.com/docker/docker/pkg/signal" "github.com/docker/docker/pkg/streamformatter" - "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/truncindex" @@ -73,9 +69,6 @@ import ( ) var ( - validContainerNameChars = utils.RestrictedNameChars - validContainerNamePattern = utils.RestrictedNamePattern - errSystemNotSupported = fmt.Errorf("The Docker daemon is not supported on this platform.") ) @@ -112,110 +105,6 @@ type Daemon struct { defaultIsolation containertypes.Isolation // Default isolation mode on Windows } -// GetContainer looks for a container using the provided information, which could be -// one of the following inputs from the caller: -// - A full container ID, which will exact match a container in daemon's list -// - A container name, which will only exact match via the GetByName() function -// - A partial container ID prefix (e.g. short ID) of any length that is -// unique enough to only return a single container object -// If none of these searches succeed, an error is returned -func (daemon *Daemon) GetContainer(prefixOrName string) (*container.Container, error) { - if len(prefixOrName) == 0 { - return nil, errors.NewBadRequestError(fmt.Errorf("No container name or ID supplied")) - } - - if containerByID := daemon.containers.Get(prefixOrName); containerByID != nil { - // prefix is an exact match to a full container ID - return containerByID, nil - } - - // GetByName will match only an exact name provided; we ignore errors - if containerByName, _ := daemon.GetByName(prefixOrName); containerByName != nil { - // prefix is an exact match to a full container Name - return containerByName, nil - } - - containerID, indexError := daemon.idIndex.Get(prefixOrName) - if indexError != nil { - // When truncindex defines an error type, use that instead - if indexError == truncindex.ErrNotExist { - err := fmt.Errorf("No such container: %s", prefixOrName) - return nil, errors.NewRequestNotFoundError(err) - } - return nil, indexError - } - return daemon.containers.Get(containerID), nil -} - -// Exists returns a true if a container of the specified ID or name exists, -// false otherwise. -func (daemon *Daemon) Exists(id string) bool { - c, _ := daemon.GetContainer(id) - return c != nil -} - -// IsPaused returns a bool indicating if the specified container is paused. -func (daemon *Daemon) IsPaused(id string) bool { - c, _ := daemon.GetContainer(id) - return c.State.IsPaused() -} - -func (daemon *Daemon) containerRoot(id string) string { - return filepath.Join(daemon.repository, id) -} - -// Load reads the contents of a container from disk -// This is typically done at startup. -func (daemon *Daemon) load(id string) (*container.Container, error) { - container := daemon.newBaseContainer(id) - - if err := container.FromDisk(); err != nil { - return nil, err - } - - if container.ID != id { - return container, fmt.Errorf("Container %s is stored at %s", container.ID, id) - } - - return container, nil -} - -func (daemon *Daemon) registerName(container *container.Container) error { - if daemon.Exists(container.ID) { - return fmt.Errorf("Container is already loaded") - } - if err := validateID(container.ID); err != nil { - return err - } - if container.Name == "" { - name, err := daemon.generateNewName(container.ID) - if err != nil { - return err - } - container.Name = name - - if err := container.ToDiskLocking(); err != nil { - logrus.Errorf("Error saving container name to disk: %v", err) - } - } - return daemon.nameIndex.Reserve(container.Name, container.ID) -} - -// Register makes a container object usable by the daemon as -func (daemon *Daemon) Register(c *container.Container) error { - // Attach to stdout and stderr - if c.Config.OpenStdin { - c.NewInputPipes() - } else { - c.NewNopInputPipe() - } - - daemon.containers.Add(c.ID, c) - daemon.idIndex.Add(c.ID) - - return nil -} - func (daemon *Daemon) restore() error { var ( debug = utils.IsDebugEnabled() @@ -431,88 +320,6 @@ func (daemon *Daemon) waitForNetworks(c *container.Container) { } } -func (daemon *Daemon) mergeAndVerifyConfig(config *containertypes.Config, img *image.Image) error { - if img != nil && img.Config != nil { - if err := merge(config, img.Config); err != nil { - return err - } - } - if len(config.Entrypoint) == 0 && len(config.Cmd) == 0 { - return fmt.Errorf("No command specified") - } - return nil -} - -func (daemon *Daemon) generateIDAndName(name string) (string, string, error) { - var ( - err error - id = stringid.GenerateNonCryptoID() - ) - - if name == "" { - if name, err = daemon.generateNewName(id); err != nil { - return "", "", err - } - return id, name, nil - } - - if name, err = daemon.reserveName(id, name); err != nil { - return "", "", err - } - - return id, name, nil -} - -func (daemon *Daemon) reserveName(id, name string) (string, error) { - if !validContainerNamePattern.MatchString(name) { - return "", fmt.Errorf("Invalid container name (%s), only %s are allowed", name, validContainerNameChars) - } - if name[0] != '/' { - name = "/" + name - } - - if err := daemon.nameIndex.Reserve(name, id); err != nil { - if err == registrar.ErrNameReserved { - id, err := daemon.nameIndex.Get(name) - if err != nil { - logrus.Errorf("got unexpected error while looking up reserved name: %v", err) - return "", err - } - return "", fmt.Errorf("Conflict. The name %q is already in use by container %s. You have to remove (or rename) that container to be able to reuse that name.", name, id) - } - return "", fmt.Errorf("error reserving name: %s, error: %v", name, err) - } - return name, nil -} - -func (daemon *Daemon) releaseName(name string) { - daemon.nameIndex.Release(name) -} - -func (daemon *Daemon) generateNewName(id string) (string, error) { - var name string - for i := 0; i < 6; i++ { - name = namesgenerator.GetRandomName(i) - if name[0] != '/' { - name = "/" + name - } - - if err := daemon.nameIndex.Reserve(name, id); err != nil { - if err == registrar.ErrNameReserved { - continue - } - return "", err - } - return name, nil - } - - name = "/" + stringid.TruncateID(id) - if err := daemon.nameIndex.Reserve(name, id); err != nil { - return "", err - } - return name, nil -} - func (daemon *Daemon) generateHostname(id string, config *containertypes.Config) { // Generate default hostname if config.Hostname == "" { @@ -527,54 +334,6 @@ func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint strslice.StrSlice, c return configCmd[0], configCmd[1:] } -func (daemon *Daemon) newContainer(name string, config *containertypes.Config, imgID image.ID) (*container.Container, error) { - var ( - id string - err error - noExplicitName = name == "" - ) - id, name, err = daemon.generateIDAndName(name) - if err != nil { - return nil, err - } - - daemon.generateHostname(id, config) - entrypoint, args := daemon.getEntrypointAndArgs(config.Entrypoint, config.Cmd) - - base := daemon.newBaseContainer(id) - base.Created = time.Now().UTC() - base.Path = entrypoint - base.Args = args //FIXME: de-duplicate from config - base.Config = config - base.HostConfig = &containertypes.HostConfig{} - base.ImageID = imgID - base.NetworkSettings = &network.Settings{IsAnonymousEndpoint: noExplicitName} - base.Name = name - base.Driver = daemon.GraphDriverName() - - return base, err -} - -// GetByName returns a container given a name. -func (daemon *Daemon) GetByName(name string) (*container.Container, error) { - if len(name) == 0 { - return nil, fmt.Errorf("No container name supplied") - } - fullName := name - if name[0] != '/' { - fullName = "/" + name - } - id, err := daemon.nameIndex.Get(fullName) - if err != nil { - return nil, fmt.Errorf("Could not find entity for %s", name) - } - e := daemon.containers.Get(id) - if e == nil { - return nil, fmt.Errorf("Could not find container for entity id %s", id) - } - return e, nil -} - // GetLabels for a container or image id func (daemon *Daemon) GetLabels(id string) map[string]string { // TODO: TestCase @@ -947,22 +706,6 @@ func (daemon *Daemon) Unmount(container *container.Container) error { return nil } -func (daemon *Daemon) kill(c *container.Container, sig int) error { - return daemon.containerd.Signal(c.ID, sig) -} - -func (daemon *Daemon) subscribeToContainerStats(c *container.Container) chan interface{} { - return daemon.statsCollector.collect(c) -} - -func (daemon *Daemon) unsubscribeToContainerStats(c *container.Container, ch chan interface{}) { - daemon.statsCollector.unsubscribe(c, ch) -} - -func (daemon *Daemon) changes(container *container.Container) ([]archive.Change, error) { - return container.RWLayer.Changes() -} - func writeDistributionProgress(cancelFunc func(), outStream io.Writer, progressChan <-chan progress.Progress) { progressOutput := streamformatter.NewJSONStreamFormatter().NewProgressOutput(outStream, false) operationCancelled := false diff --git a/daemon/kill.go b/daemon/kill.go index 3967f0f299..15a22a7c41 100644 --- a/daemon/kill.go +++ b/daemon/kill.go @@ -151,3 +151,7 @@ func (daemon *Daemon) killPossiblyDeadProcess(container *container.Container, si } return err } + +func (daemon *Daemon) kill(c *container.Container, sig int) error { + return daemon.containerd.Signal(c.ID, sig) +} diff --git a/daemon/names.go b/daemon/names.go new file mode 100644 index 0000000000..74e8677677 --- /dev/null +++ b/daemon/names.go @@ -0,0 +1,108 @@ +package daemon + +import ( + "fmt" + + "github.com/Sirupsen/logrus" + "github.com/docker/docker/container" + "github.com/docker/docker/pkg/namesgenerator" + "github.com/docker/docker/pkg/registrar" + "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/utils" +) + +var ( + validContainerNameChars = utils.RestrictedNameChars + validContainerNamePattern = utils.RestrictedNamePattern +) + +func (daemon *Daemon) registerName(container *container.Container) error { + if daemon.Exists(container.ID) { + return fmt.Errorf("Container is already loaded") + } + if err := validateID(container.ID); err != nil { + return err + } + if container.Name == "" { + name, err := daemon.generateNewName(container.ID) + if err != nil { + return err + } + container.Name = name + + if err := container.ToDiskLocking(); err != nil { + logrus.Errorf("Error saving container name to disk: %v", err) + } + } + return daemon.nameIndex.Reserve(container.Name, container.ID) +} + +func (daemon *Daemon) generateIDAndName(name string) (string, string, error) { + var ( + err error + id = stringid.GenerateNonCryptoID() + ) + + if name == "" { + if name, err = daemon.generateNewName(id); err != nil { + return "", "", err + } + return id, name, nil + } + + if name, err = daemon.reserveName(id, name); err != nil { + return "", "", err + } + + return id, name, nil +} + +func (daemon *Daemon) reserveName(id, name string) (string, error) { + if !validContainerNamePattern.MatchString(name) { + return "", fmt.Errorf("Invalid container name (%s), only %s are allowed", name, validContainerNameChars) + } + if name[0] != '/' { + name = "/" + name + } + + if err := daemon.nameIndex.Reserve(name, id); err != nil { + if err == registrar.ErrNameReserved { + id, err := daemon.nameIndex.Get(name) + if err != nil { + logrus.Errorf("got unexpected error while looking up reserved name: %v", err) + return "", err + } + return "", fmt.Errorf("Conflict. The name %q is already in use by container %s. You have to remove (or rename) that container to be able to reuse that name.", name, id) + } + return "", fmt.Errorf("error reserving name: %s, error: %v", name, err) + } + return name, nil +} + +func (daemon *Daemon) releaseName(name string) { + daemon.nameIndex.Release(name) +} + +func (daemon *Daemon) generateNewName(id string) (string, error) { + var name string + for i := 0; i < 6; i++ { + name = namesgenerator.GetRandomName(i) + if name[0] != '/' { + name = "/" + name + } + + if err := daemon.nameIndex.Reserve(name, id); err != nil { + if err == registrar.ErrNameReserved { + continue + } + return "", err + } + return name, nil + } + + name = "/" + stringid.TruncateID(id) + if err := daemon.nameIndex.Reserve(name, id); err != nil { + return "", err + } + return name, nil +} diff --git a/daemon/stats.go b/daemon/stats.go index 5f25bf1d61..73db612605 100644 --- a/daemon/stats.go +++ b/daemon/stats.go @@ -8,6 +8,7 @@ import ( "golang.org/x/net/context" "github.com/docker/docker/api/types/backend" + "github.com/docker/docker/container" "github.com/docker/docker/pkg/ioutils" "github.com/docker/engine-api/types" "github.com/docker/engine-api/types/versions" @@ -121,3 +122,11 @@ func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, c } } } + +func (daemon *Daemon) subscribeToContainerStats(c *container.Container) chan interface{} { + return daemon.statsCollector.collect(c) +} + +func (daemon *Daemon) unsubscribeToContainerStats(c *container.Container, ch chan interface{}) { + daemon.statsCollector.unsubscribe(c, ch) +}