diff --git a/daemon/auth.go b/daemon/auth.go new file mode 100644 index 0000000000..a1400d88ae --- /dev/null +++ b/daemon/auth.go @@ -0,0 +1,13 @@ +package daemon + +import ( + "golang.org/x/net/context" + + "github.com/docker/docker/dockerversion" + "github.com/docker/engine-api/types" +) + +// AuthenticateToRegistry checks the validity of credentials in authConfig +func (daemon *Daemon) AuthenticateToRegistry(ctx context.Context, authConfig *types.AuthConfig) (string, string, error) { + return daemon.RegistryService.Auth(ctx, authConfig, dockerversion.DockerUserAgent(ctx)) +} diff --git a/daemon/container.go b/daemon/container.go index 645db26095..421b0c25d9 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -3,14 +3,19 @@ package daemon import ( "fmt" "path/filepath" + "regexp" "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/signal" + "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/truncindex" containertypes "github.com/docker/engine-api/types/container" + "github.com/docker/engine-api/types/strslice" + "github.com/docker/go-connections/nat" ) // GetContainer looks for a container using the provided information, which could be @@ -143,3 +148,108 @@ func (daemon *Daemon) GetByName(name string) (*container.Container, error) { } return e, nil } + +// newBaseContainer creates a new container with its initial +// configuration based on the root storage from the daemon. +func (daemon *Daemon) newBaseContainer(id string) *container.Container { + return container.NewBaseContainer(id, daemon.containerRoot(id)) +} + +func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint strslice.StrSlice, configCmd strslice.StrSlice) (string, []string) { + if len(configEntrypoint) != 0 { + return configEntrypoint[0], append(configEntrypoint[1:], configCmd...) + } + return configCmd[0], configCmd[1:] +} + +func (daemon *Daemon) generateHostname(id string, config *containertypes.Config) { + // Generate default hostname + if config.Hostname == "" { + config.Hostname = id[:12] + } +} + +func (daemon *Daemon) setSecurityOptions(container *container.Container, hostConfig *containertypes.HostConfig) error { + container.Lock() + defer container.Unlock() + return parseSecurityOpt(container, hostConfig) +} + +func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig *containertypes.HostConfig) error { + // 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 + if err := daemon.registerMountPoints(container, hostConfig); err != nil { + return err + } + + container.Lock() + defer container.Unlock() + + // Register any links from the host config before starting the container + if err := daemon.registerLinks(container, hostConfig); err != nil { + return err + } + + // make sure links is not nil + // this ensures that on the next daemon restart we don't try to migrate from legacy sqlite links + if hostConfig.Links == nil { + hostConfig.Links = []string{} + } + + container.HostConfig = hostConfig + return container.ToDisk() +} + +// verifyContainerSettings performs validation of the hostconfig and config +// structures. +func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) { + + // First perform verification of settings common across all platforms. + if config != nil { + if config.WorkingDir != "" { + config.WorkingDir = filepath.FromSlash(config.WorkingDir) // Ensure in platform semantics + if !system.IsAbs(config.WorkingDir) { + return nil, fmt.Errorf("The working directory '%s' is invalid. It needs to be an absolute path", config.WorkingDir) + } + } + + if len(config.StopSignal) > 0 { + _, err := signal.ParseSignal(config.StopSignal) + if err != nil { + return nil, err + } + } + + // Validate if the given hostname is RFC 1123 (https://tools.ietf.org/html/rfc1123) compliant. + if len(config.Hostname) > 0 { + // RFC1123 specifies that 63 bytes is the maximium length + // Windows has the limitation of 63 bytes in length + // Linux hostname is limited to HOST_NAME_MAX=64, not not including the terminating null byte. + // We limit the length to 63 bytes here to match RFC1035 and RFC1123. + matched, _ := regexp.MatchString("^(([[:alnum:]]|[[:alnum:]][[:alnum:]\\-]*[[:alnum:]])\\.)*([[:alnum:]]|[[:alnum:]][[:alnum:]\\-]*[[:alnum:]])$", config.Hostname) + if len(config.Hostname) > 63 || !matched { + return nil, fmt.Errorf("invalid hostname format: %s", config.Hostname) + } + } + } + + if hostConfig == nil { + return nil, nil + } + + for port := range hostConfig.PortBindings { + _, portStr := nat.SplitProtoPort(string(port)) + if _, err := nat.ParsePort(portStr); err != nil { + return nil, fmt.Errorf("Invalid port specification: %q", portStr) + } + for _, pb := range hostConfig.PortBindings[port] { + _, err := nat.NewPort(nat.SplitProtoPort(pb.HostPort)) + if err != nil { + return nil, fmt.Errorf("Invalid port specification: %q", pb.HostPort) + } + } + } + + // Now do platform-specific verification + return verifyPlatformContainerSettings(daemon, hostConfig, config, update) +} diff --git a/daemon/create.go b/daemon/create.go index c91849875d..2a3baa0f24 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -2,9 +2,11 @@ package daemon import ( "fmt" + "strings" "github.com/Sirupsen/logrus" "github.com/docker/docker/container" + "github.com/docker/docker/errors" "github.com/docker/docker/image" "github.com/docker/docker/layer" "github.com/docker/docker/pkg/idtools" @@ -234,3 +236,16 @@ func (daemon *Daemon) mergeAndVerifyConfig(config *containertypes.Config, img *i } return nil } + +// Checks if the client set configurations for more than one network while creating a container +func (daemon *Daemon) verifyNetworkingConfig(nwConfig *networktypes.NetworkingConfig) error { + if nwConfig == nil || len(nwConfig.EndpointsConfig) <= 1 { + return nil + } + l := make([]string, 0, len(nwConfig.EndpointsConfig)) + for k := range nwConfig.EndpointsConfig { + l = append(l, k) + } + err := fmt.Errorf("Container cannot be connected to network endpoints: %s", strings.Join(l, ", ")) + return errors.NewBadRequestError(err) +} diff --git a/daemon/daemon.go b/daemon/daemon.go index f247a3f6da..7d095930e1 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -14,7 +14,6 @@ import ( "os" "path" "path/filepath" - "regexp" "runtime" "strings" "sync" @@ -27,16 +26,12 @@ import ( "github.com/docker/docker/container" "github.com/docker/docker/daemon/events" "github.com/docker/docker/daemon/exec" - "github.com/docker/docker/errors" "github.com/docker/engine-api/types" containertypes "github.com/docker/engine-api/types/container" - networktypes "github.com/docker/engine-api/types/network" - "github.com/docker/engine-api/types/strslice" // register graph drivers _ "github.com/docker/docker/daemon/graphdriver/register" dmetadata "github.com/docker/docker/distribution/metadata" "github.com/docker/docker/distribution/xfer" - "github.com/docker/docker/dockerversion" "github.com/docker/docker/image" "github.com/docker/docker/layer" "github.com/docker/docker/libcontainerd" @@ -58,11 +53,9 @@ import ( volumedrivers "github.com/docker/docker/volume/drivers" "github.com/docker/docker/volume/local" "github.com/docker/docker/volume/store" - "github.com/docker/go-connections/nat" "github.com/docker/libnetwork" nwconfig "github.com/docker/libnetwork/config" "github.com/docker/libtrust" - "golang.org/x/net/context" ) var ( @@ -317,35 +310,6 @@ func (daemon *Daemon) waitForNetworks(c *container.Container) { } } -func (daemon *Daemon) generateHostname(id string, config *containertypes.Config) { - // Generate default hostname - if config.Hostname == "" { - config.Hostname = id[:12] - } -} - -func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint strslice.StrSlice, configCmd strslice.StrSlice) (string, []string) { - if len(configEntrypoint) != 0 { - return configEntrypoint[0], append(configEntrypoint[1:], configCmd...) - } - return configCmd[0], configCmd[1:] -} - -// GetLabels for a container or image id -func (daemon *Daemon) GetLabels(id string) map[string]string { - // TODO: TestCase - container := daemon.containers.Get(id) - if container != nil { - return container.Config.Labels - } - - img, err := daemon.GetImage(id) - if err == nil { - return img.ContainerConfig.Labels - } - return nil -} - func (daemon *Daemon) children(c *container.Container) map[string]*container.Container { return daemon.linkIndex.children(c) } @@ -762,37 +726,6 @@ func tempDir(rootDir string, rootUID, rootGID int) (string, error) { return tmpDir, idtools.MkdirAllAs(tmpDir, 0700, rootUID, rootGID) } -func (daemon *Daemon) setSecurityOptions(container *container.Container, hostConfig *containertypes.HostConfig) error { - container.Lock() - defer container.Unlock() - return parseSecurityOpt(container, hostConfig) -} - -func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig *containertypes.HostConfig) error { - // 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 - if err := daemon.registerMountPoints(container, hostConfig); err != nil { - return err - } - - container.Lock() - defer container.Unlock() - - // Register any links from the host config before starting the container - if err := daemon.registerLinks(container, hostConfig); err != nil { - return err - } - - // make sure links is not nil - // this ensures that on the next daemon restart we don't try to migrate from legacy sqlite links - if hostConfig.Links == nil { - hostConfig.Links = []string{} - } - - container.HostConfig = hostConfig - return container.ToDisk() -} - func (daemon *Daemon) setupInitLayer(initPath string) error { rootUID, rootGID := daemon.GetRemappedUIDGID() return setupInitLayer(initPath, rootUID, rootGID) @@ -806,73 +739,6 @@ func setDefaultMtu(config *Config) { config.Mtu = defaultNetworkMtu } -// verifyContainerSettings performs validation of the hostconfig and config -// structures. -func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) { - - // First perform verification of settings common across all platforms. - if config != nil { - if config.WorkingDir != "" { - config.WorkingDir = filepath.FromSlash(config.WorkingDir) // Ensure in platform semantics - if !system.IsAbs(config.WorkingDir) { - return nil, fmt.Errorf("The working directory '%s' is invalid. It needs to be an absolute path", config.WorkingDir) - } - } - - if len(config.StopSignal) > 0 { - _, err := signal.ParseSignal(config.StopSignal) - if err != nil { - return nil, err - } - } - - // Validate if the given hostname is RFC 1123 (https://tools.ietf.org/html/rfc1123) compliant. - if len(config.Hostname) > 0 { - // RFC1123 specifies that 63 bytes is the maximium length - // Windows has the limitation of 63 bytes in length - // Linux hostname is limited to HOST_NAME_MAX=64, not not including the terminating null byte. - // We limit the length to 63 bytes here to match RFC1035 and RFC1123. - matched, _ := regexp.MatchString("^(([[:alnum:]]|[[:alnum:]][[:alnum:]\\-]*[[:alnum:]])\\.)*([[:alnum:]]|[[:alnum:]][[:alnum:]\\-]*[[:alnum:]])$", config.Hostname) - if len(config.Hostname) > 63 || !matched { - return nil, fmt.Errorf("invalid hostname format: %s", config.Hostname) - } - } - } - - if hostConfig == nil { - return nil, nil - } - - for port := range hostConfig.PortBindings { - _, portStr := nat.SplitProtoPort(string(port)) - if _, err := nat.ParsePort(portStr); err != nil { - return nil, fmt.Errorf("Invalid port specification: %q", portStr) - } - for _, pb := range hostConfig.PortBindings[port] { - _, err := nat.NewPort(nat.SplitProtoPort(pb.HostPort)) - if err != nil { - return nil, fmt.Errorf("Invalid port specification: %q", pb.HostPort) - } - } - } - - // Now do platform-specific verification - return verifyPlatformContainerSettings(daemon, hostConfig, config, update) -} - -// Checks if the client set configurations for more than one network while creating a container -func (daemon *Daemon) verifyNetworkingConfig(nwConfig *networktypes.NetworkingConfig) error { - if nwConfig == nil || len(nwConfig.EndpointsConfig) <= 1 { - return nil - } - l := make([]string, 0, len(nwConfig.EndpointsConfig)) - for k := range nwConfig.EndpointsConfig { - l = append(l, k) - } - err := fmt.Errorf("Container cannot be connected to network endpoints: %s", strings.Join(l, ", ")) - return errors.NewBadRequestError(err) -} - func configureVolumes(config *Config, rootUID, rootGID int) (*store.VolumeStore, error) { volumesDriver, err := local.New(config.Root, rootUID, rootGID) if err != nil { @@ -883,84 +749,11 @@ func configureVolumes(config *Config, rootUID, rootGID int) (*store.VolumeStore, return store.New(config.Root) } -// AuthenticateToRegistry checks the validity of credentials in authConfig -func (daemon *Daemon) AuthenticateToRegistry(ctx context.Context, authConfig *types.AuthConfig) (string, string, error) { - return daemon.RegistryService.Auth(ctx, authConfig, dockerversion.DockerUserAgent(ctx)) -} - // IsShuttingDown tells whether the daemon is shutting down or not func (daemon *Daemon) IsShuttingDown() bool { return daemon.shutdown } -// GetContainerStats collects all the stats published by a container -func (daemon *Daemon) GetContainerStats(container *container.Container) (*types.StatsJSON, error) { - stats, err := daemon.stats(container) - if err != nil { - return nil, err - } - - if stats.Networks, err = daemon.getNetworkStats(container); err != nil { - return nil, err - } - - return stats, nil -} - -// Resolve Network SandboxID in case the container reuse another container's network stack -func (daemon *Daemon) getNetworkSandboxID(c *container.Container) (string, error) { - curr := c - for curr.HostConfig.NetworkMode.IsContainer() { - containerID := curr.HostConfig.NetworkMode.ConnectedContainer() - connected, err := daemon.GetContainer(containerID) - if err != nil { - return "", fmt.Errorf("Could not get container for %s", containerID) - } - curr = connected - } - return curr.NetworkSettings.SandboxID, nil -} - -func (daemon *Daemon) getNetworkStats(c *container.Container) (map[string]types.NetworkStats, error) { - sandboxID, err := daemon.getNetworkSandboxID(c) - if err != nil { - return nil, err - } - - sb, err := daemon.netController.SandboxByID(sandboxID) - if err != nil { - return nil, err - } - - lnstats, err := sb.Statistics() - if err != nil { - return nil, err - } - - stats := make(map[string]types.NetworkStats) - // Convert libnetwork nw stats into engine-api stats - for ifName, ifStats := range lnstats { - stats[ifName] = types.NetworkStats{ - RxBytes: ifStats.RxBytes, - RxPackets: ifStats.RxPackets, - RxErrors: ifStats.RxErrors, - RxDropped: ifStats.RxDropped, - TxBytes: ifStats.TxBytes, - TxPackets: ifStats.TxPackets, - TxErrors: ifStats.TxErrors, - TxDropped: ifStats.TxDropped, - } - } - - return stats, nil -} - -// newBaseContainer creates a new container with its initial -// configuration based on the root storage from the daemon. -func (daemon *Daemon) newBaseContainer(id string) *container.Container { - return container.NewBaseContainer(id, daemon.containerRoot(id)) -} - // initDiscovery initializes the discovery watcher for this daemon. func (daemon *Daemon) initDiscovery(config *Config) error { advertise, err := parseClusterAdvertiseSettings(config.ClusterStore, config.ClusterAdvertise) @@ -1112,13 +905,6 @@ func (daemon *Daemon) reloadClusterDiscovery(config *Config) error { return nil } -func validateID(id string) error { - if id == "" { - return fmt.Errorf("Invalid empty id") - } - return nil -} - func isBridgeNetworkDisabled(config *Config) bool { return config.bridgeConfig.Iface == disableNetworkBridge } diff --git a/daemon/names.go b/daemon/names.go index 74e8677677..feb1323bb1 100644 --- a/daemon/names.go +++ b/daemon/names.go @@ -106,3 +106,10 @@ func (daemon *Daemon) generateNewName(id string) (string, error) { } return name, nil } + +func validateID(id string) error { + if id == "" { + return fmt.Errorf("Invalid empty id") + } + return nil +} diff --git a/daemon/stats.go b/daemon/stats.go index 73db612605..64b10cc16f 100644 --- a/daemon/stats.go +++ b/daemon/stats.go @@ -3,6 +3,7 @@ package daemon import ( "encoding/json" "errors" + "fmt" "runtime" "golang.org/x/net/context" @@ -130,3 +131,65 @@ func (daemon *Daemon) subscribeToContainerStats(c *container.Container) chan int func (daemon *Daemon) unsubscribeToContainerStats(c *container.Container, ch chan interface{}) { daemon.statsCollector.unsubscribe(c, ch) } + +// GetContainerStats collects all the stats published by a container +func (daemon *Daemon) GetContainerStats(container *container.Container) (*types.StatsJSON, error) { + stats, err := daemon.stats(container) + if err != nil { + return nil, err + } + + if stats.Networks, err = daemon.getNetworkStats(container); err != nil { + return nil, err + } + + return stats, nil +} + +// Resolve Network SandboxID in case the container reuse another container's network stack +func (daemon *Daemon) getNetworkSandboxID(c *container.Container) (string, error) { + curr := c + for curr.HostConfig.NetworkMode.IsContainer() { + containerID := curr.HostConfig.NetworkMode.ConnectedContainer() + connected, err := daemon.GetContainer(containerID) + if err != nil { + return "", fmt.Errorf("Could not get container for %s", containerID) + } + curr = connected + } + return curr.NetworkSettings.SandboxID, nil +} + +func (daemon *Daemon) getNetworkStats(c *container.Container) (map[string]types.NetworkStats, error) { + sandboxID, err := daemon.getNetworkSandboxID(c) + if err != nil { + return nil, err + } + + sb, err := daemon.netController.SandboxByID(sandboxID) + if err != nil { + return nil, err + } + + lnstats, err := sb.Statistics() + if err != nil { + return nil, err + } + + stats := make(map[string]types.NetworkStats) + // Convert libnetwork nw stats into engine-api stats + for ifName, ifStats := range lnstats { + stats[ifName] = types.NetworkStats{ + RxBytes: ifStats.RxBytes, + RxPackets: ifStats.RxPackets, + RxErrors: ifStats.RxErrors, + RxDropped: ifStats.RxDropped, + TxBytes: ifStats.TxBytes, + TxPackets: ifStats.TxPackets, + TxErrors: ifStats.TxErrors, + TxDropped: ifStats.TxDropped, + } + } + + return stats, nil +}