diff --git a/daemon/daemon.go b/daemon/daemon.go index 0db8caff94..3d036328cc 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -1,12 +1,11 @@ package daemon import ( + "errors" "fmt" "io" "io/ioutil" - "net" "os" - "path" "path/filepath" "regexp" "runtime" @@ -14,45 +13,38 @@ import ( "sync" "time" - "github.com/docker/libcontainer/label" - "github.com/docker/libnetwork" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/options" - "github.com/Sirupsen/logrus" "github.com/docker/docker/api" - "github.com/docker/docker/autogen/dockerversion" "github.com/docker/docker/daemon/events" "github.com/docker/docker/daemon/execdriver" "github.com/docker/docker/daemon/execdriver/execdrivers" "github.com/docker/docker/daemon/graphdriver" - _ "github.com/docker/docker/daemon/graphdriver/vfs" "github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/network" "github.com/docker/docker/graph" "github.com/docker/docker/image" - "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/broadcastwriter" "github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/graphdb" "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/namesgenerator" "github.com/docker/docker/pkg/parsers" - "github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/sysinfo" + "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/truncindex" "github.com/docker/docker/registry" "github.com/docker/docker/runconfig" "github.com/docker/docker/trust" - "github.com/docker/docker/utils" - volumedrivers "github.com/docker/docker/volume/drivers" - "github.com/docker/docker/volume/local" + "github.com/docker/libnetwork" ) var ( validContainerNameChars = `[a-zA-Z0-9][a-zA-Z0-9_.-]` validContainerNamePattern = regexp.MustCompile(`^/?` + validContainerNameChars + `+$`) + + // TODO Windows. Change this once daemon is up and running. + ErrSystemNotSupported = errors.New("The Docker daemon is only supported on linux") ) type contStore struct { @@ -146,7 +138,7 @@ func (daemon *Daemon) Exists(id string) bool { } func (daemon *Daemon) containerRoot(id string) string { - return path.Join(daemon.repository, id) + return filepath.Join(daemon.repository, id) } // Load reads the contents of a container from disk @@ -461,31 +453,6 @@ func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint *runconfig.Entrypoin return entrypoint, args } -func parseSecurityOpt(container *Container, config *runconfig.HostConfig) error { - var ( - labelOpts []string - err error - ) - - for _, opt := range config.SecurityOpt { - con := strings.SplitN(opt, ":", 2) - if len(con) == 1 { - return fmt.Errorf("Invalid --security-opt: %q", opt) - } - switch con[0] { - case "label": - labelOpts = append(labelOpts, con[1]) - case "apparmor": - container.AppArmorProfile = con[1] - default: - return fmt.Errorf("Invalid --security-opt: %q", opt) - } - } - - container.ProcessLabel, container.MountLabel, err = label.InitLabels(labelOpts) - return err -} - func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID string) (*Container, error) { var ( id string @@ -518,32 +485,6 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID return container, err } -func (daemon *Daemon) createRootfs(container *Container) error { - // Step 1: create the container directory. - // This doubles as a barrier to avoid race conditions. - if err := os.Mkdir(container.root, 0700); err != nil { - return err - } - initID := fmt.Sprintf("%s-init", container.ID) - if err := daemon.driver.Create(initID, container.ImageID); err != nil { - return err - } - initPath, err := daemon.driver.Get(initID, "") - if err != nil { - return err - } - defer daemon.driver.Put(initID) - - if err := graph.SetupInitLayer(initPath); err != nil { - return err - } - - if err := daemon.driver.Create(container.ID, initID); err != nil { - return err - } - return nil -} - func GetFullContainerName(name string) (string, error) { if name == "" { return "", fmt.Errorf("Container name cannot be empty") @@ -602,7 +543,7 @@ func (daemon *Daemon) Parents(name string) ([]string, error) { } func (daemon *Daemon) RegisterLink(parent, child *Container, alias string) error { - fullName := path.Join(parent.Name, alias) + fullName := filepath.Join(parent.Name, alias) if !daemon.containerGraph.Exists(fullName) { _, err := daemon.containerGraph.Set(fullName, child.ID) return err @@ -648,26 +589,16 @@ func (daemon *Daemon) RegisterLinks(container *Container, hostConfig *runconfig. } func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemon, err error) { - // Check for mutually incompatible config options - if config.Bridge.Iface != "" && config.Bridge.IP != "" { - return nil, fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one.") + // Ensure we have compatible configuration options + if err := checkConfigOptions(config); err != nil { + return nil, err } - if !config.Bridge.EnableIPTables && !config.Bridge.InterContainerCommunication { - return nil, fmt.Errorf("You specified --iptables=false with --icc=false. ICC uses iptables to function. Please set --icc or --iptables to true.") - } - if !config.Bridge.EnableIPTables && config.Bridge.EnableIPMasq { - config.Bridge.EnableIPMasq = false - } - config.DisableNetwork = config.Bridge.Iface == disableNetworkBridge + + // Do we have a disabled network? + config.DisableNetwork = isNetworkDisabled(config) // Check that the system is supported and we have sufficient privileges - if runtime.GOOS != "linux" { - return nil, fmt.Errorf("The Docker daemon is only supported on linux") - } - if os.Geteuid() != 0 { - return nil, fmt.Errorf("The Docker daemon needs to be run as root") - } - if err := checkKernel(); err != nil { + if err := checkSystem(); err != nil { return nil, err } @@ -697,7 +628,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo } config.Root = realRoot // Create the root directory if it doesn't exists - if err := os.MkdirAll(config.Root, 0700); err != nil && !os.IsExist(err) { + if err := system.MkdirAll(config.Root, 0700); err != nil && !os.IsExist(err) { return nil, err } @@ -714,6 +645,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo d := &Daemon{} d.driver = driver + // Ensure the graph driver is shutdown at a later point defer func() { if err != nil { if err := d.Shutdown(); err != nil { @@ -730,50 +662,41 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo } logrus.Debugf("Using default logging driver %s", config.LogConfig.Type) - if config.EnableSelinuxSupport { - if selinuxEnabled() { - // As Docker on btrfs and SELinux are incompatible at present, error on both being enabled - if d.driver.String() == "btrfs" { - return nil, fmt.Errorf("SELinux is not supported with the BTRFS graph driver") - } - logrus.Debug("SELinux enabled successfully") - } else { - logrus.Warn("Docker could not enable SELinux on the host system") - } - } else { - selinuxSetDisabled() + // Configure and validate the kernels security support + if err := configureKernelSecuritySupport(config, d.driver.String()); err != nil { + return nil, err } - daemonRepo := path.Join(config.Root, "containers") + daemonRepo := filepath.Join(config.Root, "containers") - if err := os.MkdirAll(daemonRepo, 0700); err != nil && !os.IsExist(err) { + if err := system.MkdirAll(daemonRepo, 0700); err != nil && !os.IsExist(err) { return nil, err } // Migrate the container if it is aufs and aufs is enabled - if err := migrateIfAufs(d.driver, config.Root); err != nil { + if err := migrateIfDownlevel(d.driver, config.Root); err != nil { return nil, err } logrus.Debug("Creating images graph") - g, err := graph.NewGraph(path.Join(config.Root, "graph"), d.driver) + g, err := graph.NewGraph(filepath.Join(config.Root, "graph"), d.driver) if err != nil { return nil, err } - volumesDriver, err := local.New(config.Root) - if err != nil { + // Configure the volumes driver + if err := configureVolumes(config); err != nil { return nil, err } - volumedrivers.Register(volumesDriver, volumesDriver.Name()) trustKey, err := api.LoadOrCreateTrustKey(config.TrustKeyPath) if err != nil { return nil, err } - trustDir := path.Join(config.Root, "trust") - if err := os.MkdirAll(trustDir, 0700); err != nil && !os.IsExist(err) { + trustDir := filepath.Join(config.Root, "trust") + + if err := system.MkdirAll(trustDir, 0700); err != nil && !os.IsExist(err) { return nil, err } trustService, err := trust.NewTrustStore(trustDir) @@ -790,7 +713,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo Events: eventsService, Trust: trustService, } - repositories, err := graph.NewTagStore(path.Join(config.Root, "repositories-"+d.driver.String()), tagCfg) + repositories, err := graph.NewTagStore(filepath.Join(config.Root, "repositories-"+d.driver.String()), tagCfg) if err != nil { return nil, fmt.Errorf("Couldn't create Tag store: %s", err) } @@ -802,7 +725,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo } } - graphdbPath := path.Join(config.Root, "linkgraph.db") + graphdbPath := filepath.Join(config.Root, "linkgraph.db") graph, err := graphdb.NewSqliteConn(graphdbPath) if err != nil { return nil, err @@ -810,24 +733,9 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo d.containerGraph = graph - localCopy := path.Join(config.Root, "init", fmt.Sprintf("dockerinit-%s", dockerversion.VERSION)) - sysInitPath := utils.DockerInitPath(localCopy) - if sysInitPath == "" { - return nil, fmt.Errorf("Could not locate dockerinit: This usually means docker was built incorrectly. See https://docs.docker.com/contributing/devenvironment for official build instructions.") - } - - if sysInitPath != localCopy { - // When we find a suitable dockerinit binary (even if it's our local binary), we copy it into config.Root at localCopy for future use (so that the original can go away without that being a problem, for example during a package upgrade). - if err := os.Mkdir(path.Dir(localCopy), 0700); err != nil && !os.IsExist(err) { - return nil, err - } - if _, err := fileutils.CopyFile(sysInitPath, localCopy); err != nil { - return nil, err - } - if err := os.Chmod(localCopy, 0700); err != nil { - return nil, err - } - sysInitPath = localCopy + sysInitPath, err := configureSysInit(config) + if err != nil { + return nil, err } sysInfo := sysinfo.New(false) @@ -860,104 +768,6 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo return d, nil } -func initNetworkController(config *Config) (libnetwork.NetworkController, error) { - controller, err := libnetwork.New() - if err != nil { - return nil, fmt.Errorf("error obtaining controller instance: %v", err) - } - - // Initialize default driver "null" - - if err := controller.ConfigureNetworkDriver("null", options.Generic{}); err != nil { - return nil, fmt.Errorf("Error initializing null driver: %v", err) - } - - // Initialize default network on "null" - if _, err := controller.NewNetwork("null", "none"); err != nil { - return nil, fmt.Errorf("Error creating default \"null\" network: %v", err) - } - - // Initialize default driver "host" - if err := controller.ConfigureNetworkDriver("host", options.Generic{}); err != nil { - return nil, fmt.Errorf("Error initializing host driver: %v", err) - } - - // Initialize default network on "host" - if _, err := controller.NewNetwork("host", "host"); err != nil { - return nil, fmt.Errorf("Error creating default \"host\" network: %v", err) - } - - // Initialize default driver "bridge" - option := options.Generic{ - "EnableIPForwarding": config.Bridge.EnableIPForward} - - if err := controller.ConfigureNetworkDriver("bridge", options.Generic{netlabel.GenericData: option}); err != nil { - return nil, fmt.Errorf("Error initializing bridge driver: %v", err) - } - - netOption := options.Generic{ - "BridgeName": config.Bridge.Iface, - "Mtu": config.Mtu, - "EnableIPTables": config.Bridge.EnableIPTables, - "EnableIPMasquerade": config.Bridge.EnableIPMasq, - "EnableICC": config.Bridge.InterContainerCommunication, - "EnableUserlandProxy": config.Bridge.EnableUserlandProxy, - } - - if config.Bridge.IP != "" { - ip, bipNet, err := net.ParseCIDR(config.Bridge.IP) - if err != nil { - return nil, err - } - - bipNet.IP = ip - netOption["AddressIPv4"] = bipNet - } - - if config.Bridge.FixedCIDR != "" { - _, fCIDR, err := net.ParseCIDR(config.Bridge.FixedCIDR) - if err != nil { - return nil, err - } - - netOption["FixedCIDR"] = fCIDR - } - - if config.Bridge.FixedCIDRv6 != "" { - _, fCIDRv6, err := net.ParseCIDR(config.Bridge.FixedCIDRv6) - if err != nil { - return nil, err - } - - netOption["FixedCIDRv6"] = fCIDRv6 - } - - if config.Bridge.DefaultGatewayIPv4 != nil { - netOption["DefaultGatewayIPv4"] = config.Bridge.DefaultGatewayIPv4 - } - - if config.Bridge.DefaultGatewayIPv6 != nil { - netOption["DefaultGatewayIPv6"] = config.Bridge.DefaultGatewayIPv6 - } - - // --ip processing - if config.Bridge.DefaultIP != nil { - netOption["DefaultBindingIP"] = config.Bridge.DefaultIP - } - - // Initialize default network on "bridge" with the same name - _, err = controller.NewNetwork("bridge", "bridge", - libnetwork.NetworkOptionGeneric(options.Generic{ - netlabel.GenericData: netOption, - netlabel.EnableIPv6: config.Bridge.EnableIPv6, - })) - if err != nil { - return nil, fmt.Errorf("Error creating default \"bridge\" network: %v", err) - } - - return controller, nil -} - func (daemon *Daemon) Shutdown() error { if daemon.containerGraph != nil { if err := daemon.containerGraph.Close(); err != nil { @@ -1005,13 +815,18 @@ func (daemon *Daemon) Mount(container *Container) error { if err != nil { return fmt.Errorf("Error getting container %s from driver %s: %s", container.ID, daemon.driver, err) } - if container.basefs == "" { - container.basefs = dir - } else if container.basefs != dir { - daemon.driver.Put(container.ID) - return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')", - daemon.driver, container.ID, container.basefs, dir) + + if container.basefs != dir { + // 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 + // on non-Windows operating systems. + if container.basefs != "" && runtime.GOOS != "windows" { + daemon.driver.Put(container.ID) + return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')", + daemon.driver, container.ID, container.basefs, dir) + } } + container.basefs = dir return nil } @@ -1020,16 +835,6 @@ func (daemon *Daemon) Unmount(container *Container) error { return nil } -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) -} - func (daemon *Daemon) Run(c *Container, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (execdriver.ExitStatus, error) { return daemon.execDriver.Run(c.command, pipes, startCallback) } @@ -1134,80 +939,7 @@ func tempDir(rootDir string) (string, error) { if tmpDir = os.Getenv("DOCKER_TMPDIR"); tmpDir == "" { tmpDir = filepath.Join(rootDir, "tmp") } - return tmpDir, os.MkdirAll(tmpDir, 0700) -} - -func checkKernel() error { - // Check for unsupported kernel versions - // FIXME: it would be cleaner to not test for specific versions, but rather - // test for specific functionalities. - // Unfortunately we can't test for the feature "does not cause a kernel panic" - // without actually causing a kernel panic, so we need this workaround until - // the circumstances of pre-3.10 crashes are clearer. - // For details see https://github.com/docker/docker/issues/407 - if k, err := kernel.GetKernelVersion(); err != nil { - logrus.Warnf("%s", err) - } else { - if kernel.CompareKernelVersion(k, &kernel.KernelVersionInfo{Kernel: 3, Major: 10, Minor: 0}) < 0 { - if os.Getenv("DOCKER_NOWARN_KERNEL_VERSION") == "" { - logrus.Warnf("You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.10.0.", k.String()) - } - } - } - return nil -} - -func (daemon *Daemon) verifyContainerSettings(hostConfig *runconfig.HostConfig) ([]string, error) { - var warnings []string - - if hostConfig == nil { - return warnings, nil - } - - if hostConfig.LxcConf.Len() > 0 && !strings.Contains(daemon.ExecutionDriver().Name(), "lxc") { - return warnings, fmt.Errorf("Cannot use --lxc-conf with execdriver: %s", daemon.ExecutionDriver().Name()) - } - if hostConfig.Memory != 0 && hostConfig.Memory < 4194304 { - return warnings, fmt.Errorf("Minimum memory limit allowed is 4MB") - } - if hostConfig.Memory > 0 && !daemon.SystemConfig().MemoryLimit { - warnings = append(warnings, "Your kernel does not support memory limit capabilities. Limitation discarded.") - logrus.Warnf("Your kernel does not support memory limit capabilities. Limitation discarded.") - hostConfig.Memory = 0 - } - if hostConfig.Memory > 0 && hostConfig.MemorySwap != -1 && !daemon.SystemConfig().SwapLimit { - warnings = append(warnings, "Your kernel does not support swap limit capabilities, memory limited without swap.") - logrus.Warnf("Your kernel does not support swap limit capabilities, memory limited without swap.") - hostConfig.MemorySwap = -1 - } - if hostConfig.Memory > 0 && hostConfig.MemorySwap > 0 && hostConfig.MemorySwap < hostConfig.Memory { - return warnings, fmt.Errorf("Minimum memoryswap limit should be larger than memory limit, see usage.") - } - if hostConfig.Memory == 0 && hostConfig.MemorySwap > 0 { - return warnings, fmt.Errorf("You should always set the Memory limit when using Memoryswap limit, see usage.") - } - if hostConfig.CpuPeriod > 0 && !daemon.SystemConfig().CpuCfsPeriod { - warnings = append(warnings, "Your kernel does not support CPU cfs period. Period discarded.") - logrus.Warnf("Your kernel does not support CPU cfs period. Period discarded.") - hostConfig.CpuPeriod = 0 - } - if hostConfig.CpuQuota > 0 && !daemon.SystemConfig().CpuCfsQuota { - warnings = append(warnings, "Your kernel does not support CPU cfs quota. Quota discarded.") - logrus.Warnf("Your kernel does not support CPU cfs quota. Quota discarded.") - hostConfig.CpuQuota = 0 - } - if hostConfig.BlkioWeight > 0 && (hostConfig.BlkioWeight < 10 || hostConfig.BlkioWeight > 1000) { - return warnings, fmt.Errorf("Range of blkio weight is from 10 to 1000.") - } - if hostConfig.OomKillDisable && !daemon.SystemConfig().OomKillDisable { - hostConfig.OomKillDisable = false - return warnings, fmt.Errorf("Your kernel does not support oom kill disable.") - } - if daemon.SystemConfig().IPv4ForwardingDisabled { - warnings = append(warnings, "IPv4 forwarding is disabled. Networking will not work.") - logrus.Warnf("IPv4 forwarding is disabled. Networking will not work") - } - return warnings, nil + return tmpDir, system.MkdirAll(tmpDir, 0700) } func (daemon *Daemon) setHostConfig(container *Container, hostConfig *runconfig.HostConfig) error { diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go new file mode 100644 index 0000000000..e4325a0160 --- /dev/null +++ b/daemon/daemon_unix.go @@ -0,0 +1,354 @@ +// +build !windows + +package daemon + +import ( + "fmt" + "net" + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/Sirupsen/logrus" + "github.com/docker/docker/autogen/dockerversion" + "github.com/docker/docker/daemon/graphdriver" + _ "github.com/docker/docker/daemon/graphdriver/vfs" + "github.com/docker/docker/graph" + "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/pkg/fileutils" + "github.com/docker/docker/pkg/parsers/kernel" + "github.com/docker/docker/runconfig" + "github.com/docker/docker/utils" + volumedrivers "github.com/docker/docker/volume/drivers" + "github.com/docker/docker/volume/local" + "github.com/docker/libcontainer/label" + "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/options" +) + +const runDir = "/var/run/docker" +const defaultVolumesPathName = "volumes" + +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) +} + +func parseSecurityOpt(container *Container, config *runconfig.HostConfig) error { + var ( + labelOpts []string + err error + ) + + for _, opt := range config.SecurityOpt { + con := strings.SplitN(opt, ":", 2) + if len(con) == 1 { + return fmt.Errorf("Invalid --security-opt: %q", opt) + } + switch con[0] { + case "label": + labelOpts = append(labelOpts, con[1]) + case "apparmor": + container.AppArmorProfile = con[1] + default: + return fmt.Errorf("Invalid --security-opt: %q", opt) + } + } + + container.ProcessLabel, container.MountLabel, err = label.InitLabels(labelOpts) + return err +} + +func (daemon *Daemon) createRootfs(container *Container) error { + // Step 1: create the container directory. + // This doubles as a barrier to avoid race conditions. + if err := os.Mkdir(container.root, 0700); err != nil { + return err + } + initID := fmt.Sprintf("%s-init", container.ID) + if err := daemon.driver.Create(initID, container.ImageID); err != nil { + return err + } + initPath, err := daemon.driver.Get(initID, "") + if err != nil { + return err + } + defer daemon.driver.Put(initID) + + if err := graph.SetupInitLayer(initPath); err != nil { + return err + } + + if err := daemon.driver.Create(container.ID, initID); err != nil { + return err + } + return nil +} + +func checkKernel() error { + // Check for unsupported kernel versions + // FIXME: it would be cleaner to not test for specific versions, but rather + // test for specific functionalities. + // Unfortunately we can't test for the feature "does not cause a kernel panic" + // without actually causing a kernel panic, so we need this workaround until + // the circumstances of pre-3.10 crashes are clearer. + // For details see https://github.com/docker/docker/issues/407 + if k, err := kernel.GetKernelVersion(); err != nil { + logrus.Warnf("%s", err) + } else { + if kernel.CompareKernelVersion(k, &kernel.KernelVersionInfo{Kernel: 3, Major: 10, Minor: 0}) < 0 { + if os.Getenv("DOCKER_NOWARN_KERNEL_VERSION") == "" { + logrus.Warnf("You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.10.0.", k.String()) + } + } + } + return nil +} + +func (daemon *Daemon) verifyContainerSettings(hostConfig *runconfig.HostConfig) ([]string, error) { + var warnings []string + + if hostConfig == nil { + return warnings, nil + } + + if hostConfig.LxcConf.Len() > 0 && !strings.Contains(daemon.ExecutionDriver().Name(), "lxc") { + return warnings, fmt.Errorf("Cannot use --lxc-conf with execdriver: %s", daemon.ExecutionDriver().Name()) + } + if hostConfig.Memory != 0 && hostConfig.Memory < 4194304 { + return warnings, fmt.Errorf("Minimum memory limit allowed is 4MB") + } + if hostConfig.Memory > 0 && !daemon.SystemConfig().MemoryLimit { + warnings = append(warnings, "Your kernel does not support memory limit capabilities. Limitation discarded.") + logrus.Warnf("Your kernel does not support memory limit capabilities. Limitation discarded.") + hostConfig.Memory = 0 + } + if hostConfig.Memory > 0 && hostConfig.MemorySwap != -1 && !daemon.SystemConfig().SwapLimit { + warnings = append(warnings, "Your kernel does not support swap limit capabilities, memory limited without swap.") + logrus.Warnf("Your kernel does not support swap limit capabilities, memory limited without swap.") + hostConfig.MemorySwap = -1 + } + if hostConfig.Memory > 0 && hostConfig.MemorySwap > 0 && hostConfig.MemorySwap < hostConfig.Memory { + return warnings, fmt.Errorf("Minimum memoryswap limit should be larger than memory limit, see usage.") + } + if hostConfig.Memory == 0 && hostConfig.MemorySwap > 0 { + return warnings, fmt.Errorf("You should always set the Memory limit when using Memoryswap limit, see usage.") + } + if hostConfig.CpuPeriod > 0 && !daemon.SystemConfig().CpuCfsPeriod { + warnings = append(warnings, "Your kernel does not support CPU cfs period. Period discarded.") + logrus.Warnf("Your kernel does not support CPU cfs period. Period discarded.") + hostConfig.CpuPeriod = 0 + } + if hostConfig.CpuQuota > 0 && !daemon.SystemConfig().CpuCfsQuota { + warnings = append(warnings, "Your kernel does not support CPU cfs quota. Quota discarded.") + logrus.Warnf("Your kernel does not support CPU cfs quota. Quota discarded.") + hostConfig.CpuQuota = 0 + } + if hostConfig.BlkioWeight > 0 && (hostConfig.BlkioWeight < 10 || hostConfig.BlkioWeight > 1000) { + return warnings, fmt.Errorf("Range of blkio weight is from 10 to 1000.") + } + if hostConfig.OomKillDisable && !daemon.SystemConfig().OomKillDisable { + hostConfig.OomKillDisable = false + return warnings, fmt.Errorf("Your kernel does not support oom kill disable.") + } + if daemon.SystemConfig().IPv4ForwardingDisabled { + warnings = append(warnings, "IPv4 forwarding is disabled. Networking will not work.") + logrus.Warnf("IPv4 forwarding is disabled. Networking will not work") + } + return warnings, nil +} + +// checkConfigOptions checks for mutually incompatible config options +func checkConfigOptions(config *Config) error { + // Check for mutually incompatible config options + if config.Bridge.Iface != "" && config.Bridge.IP != "" { + return fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one.") + } + if !config.Bridge.EnableIPTables && !config.Bridge.InterContainerCommunication { + return fmt.Errorf("You specified --iptables=false with --icc=false. ICC uses iptables to function. Please set --icc or --iptables to true.") + } + if !config.Bridge.EnableIPTables && config.Bridge.EnableIPMasq { + config.Bridge.EnableIPMasq = false + } + return nil +} + +// checkSystem validates the system is supported and we have sufficient privileges +func checkSystem() error { + // TODO Windows. Once daemon is running on Windows, move this code back to + // NewDaemon() in daemon.go, and extend the check to support Windows. + if runtime.GOOS != "linux" { + return ErrSystemNotSupported + } + if os.Geteuid() != 0 { + return fmt.Errorf("The Docker daemon needs to be run as root") + } + if err := checkKernel(); err != nil { + return err + } + return nil +} + +// configureKernelSecuritySupport configures and validate security support for the kernel +func configureKernelSecuritySupport(config *Config, driverName string) error { + if config.EnableSelinuxSupport { + if selinuxEnabled() { + // As Docker on btrfs and SELinux are incompatible at present, error on both being enabled + if driverName == "btrfs" { + return fmt.Errorf("SELinux is not supported with the BTRFS graph driver") + } + logrus.Debug("SELinux enabled successfully") + } else { + logrus.Warn("Docker could not enable SELinux on the host system") + } + } else { + selinuxSetDisabled() + } + return nil +} + +// MigrateIfDownlevel is a wrapper for AUFS migration for downlevel +func migrateIfDownlevel(driver graphdriver.Driver, root string) error { + return migrateIfAufs(driver, root) +} + +func configureVolumes(config *Config) error { + volumesDriver, err := local.New(config.Root) + if err != nil { + return err + } + volumedrivers.Register(volumesDriver, volumesDriver.Name()) + return nil +} + +func configureSysInit(config *Config) (string, error) { + localCopy := filepath.Join(config.Root, "init", fmt.Sprintf("dockerinit-%s", dockerversion.VERSION)) + sysInitPath := utils.DockerInitPath(localCopy) + if sysInitPath == "" { + return "", fmt.Errorf("Could not locate dockerinit: This usually means docker was built incorrectly. See https://docs.docker.com/contributing/devenvironment for official build instructions.") + } + + if sysInitPath != localCopy { + // When we find a suitable dockerinit binary (even if it's our local binary), we copy it into config.Root at localCopy for future use (so that the original can go away without that being a problem, for example during a package upgrade). + if err := os.Mkdir(filepath.Dir(localCopy), 0700); err != nil && !os.IsExist(err) { + return "", err + } + if _, err := fileutils.CopyFile(sysInitPath, localCopy); err != nil { + return "", err + } + if err := os.Chmod(localCopy, 0700); err != nil { + return "", err + } + sysInitPath = localCopy + } + return sysInitPath, nil +} + +func isNetworkDisabled(config *Config) bool { + return config.Bridge.Iface == disableNetworkBridge +} + +func initNetworkController(config *Config) (libnetwork.NetworkController, error) { + controller, err := libnetwork.New() + if err != nil { + return nil, fmt.Errorf("error obtaining controller instance: %v", err) + } + + // Initialize default driver "null" + + if err := controller.ConfigureNetworkDriver("null", options.Generic{}); err != nil { + return nil, fmt.Errorf("Error initializing null driver: %v", err) + } + + // Initialize default network on "null" + if _, err := controller.NewNetwork("null", "none"); err != nil { + return nil, fmt.Errorf("Error creating default \"null\" network: %v", err) + } + + // Initialize default driver "host" + if err := controller.ConfigureNetworkDriver("host", options.Generic{}); err != nil { + return nil, fmt.Errorf("Error initializing host driver: %v", err) + } + + // Initialize default network on "host" + if _, err := controller.NewNetwork("host", "host"); err != nil { + return nil, fmt.Errorf("Error creating default \"host\" network: %v", err) + } + + // Initialize default driver "bridge" + option := options.Generic{ + "EnableIPForwarding": config.Bridge.EnableIPForward} + + if err := controller.ConfigureNetworkDriver("bridge", options.Generic{netlabel.GenericData: option}); err != nil { + return nil, fmt.Errorf("Error initializing bridge driver: %v", err) + } + + netOption := options.Generic{ + "BridgeName": config.Bridge.Iface, + "Mtu": config.Mtu, + "EnableIPTables": config.Bridge.EnableIPTables, + "EnableIPMasquerade": config.Bridge.EnableIPMasq, + "EnableICC": config.Bridge.InterContainerCommunication, + "EnableUserlandProxy": config.Bridge.EnableUserlandProxy, + } + + if config.Bridge.IP != "" { + ip, bipNet, err := net.ParseCIDR(config.Bridge.IP) + if err != nil { + return nil, err + } + + bipNet.IP = ip + netOption["AddressIPv4"] = bipNet + } + + if config.Bridge.FixedCIDR != "" { + _, fCIDR, err := net.ParseCIDR(config.Bridge.FixedCIDR) + if err != nil { + return nil, err + } + + netOption["FixedCIDR"] = fCIDR + } + + if config.Bridge.FixedCIDRv6 != "" { + _, fCIDRv6, err := net.ParseCIDR(config.Bridge.FixedCIDRv6) + if err != nil { + return nil, err + } + + netOption["FixedCIDRv6"] = fCIDRv6 + } + + if config.Bridge.DefaultGatewayIPv4 != nil { + netOption["DefaultGatewayIPv4"] = config.Bridge.DefaultGatewayIPv4 + } + + if config.Bridge.DefaultGatewayIPv6 != nil { + netOption["DefaultGatewayIPv6"] = config.Bridge.DefaultGatewayIPv6 + } + + // --ip processing + if config.Bridge.DefaultIP != nil { + netOption["DefaultBindingIP"] = config.Bridge.DefaultIP + } + + // Initialize default network on "bridge" with the same name + _, err = controller.NewNetwork("bridge", "bridge", + libnetwork.NetworkOptionGeneric(options.Generic{ + netlabel.GenericData: netOption, + netlabel.EnableIPv6: config.Bridge.EnableIPv6, + })) + if err != nil { + return nil, fmt.Errorf("Error creating default \"bridge\" network: %v", err) + } + + return controller, nil +} diff --git a/daemon/daemon_windows.go b/daemon/daemon_windows.go new file mode 100644 index 0000000000..5cb7e41118 --- /dev/null +++ b/daemon/daemon_windows.go @@ -0,0 +1,107 @@ +package daemon + +import ( + "fmt" + "os" + "runtime" + "syscall" + + "github.com/docker/docker/daemon/graphdriver" + "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/runconfig" + "github.com/docker/libnetwork" +) + +var runDir = os.Getenv("TEMP") + +func (daemon *Daemon) Changes(container *Container) ([]archive.Change, error) { + return daemon.driver.Changes(container.ID, container.ImageID) +} + +func (daemon *Daemon) Diff(container *Container) (archive.Archive, error) { + return daemon.driver.Diff(container.ID, container.ImageID) +} + +func parseSecurityOpt(container *Container, config *runconfig.HostConfig) error { + return nil +} + +func (daemon *Daemon) createRootfs(container *Container) error { + // Step 1: create the container directory. + // This doubles as a barrier to avoid race conditions. + if err := os.Mkdir(container.root, 0700); err != nil { + return err + } + if err := daemon.driver.Create(container.ID, container.ImageID); err != nil { + return err + } + return nil +} + +func checkKernel() error { + return nil +} + +func (daemon *Daemon) verifyContainerSettings(hostConfig *runconfig.HostConfig) ([]string, error) { + // TODO Windows. Verifications TBC + return nil, nil +} + +// checkConfigOptions checks for mutually incompatible config options +func checkConfigOptions(config *Config) error { + return nil +} + +// checkSystem validates the system is supported and we have sufficient privileges +func checkSystem() error { + var dwVersion uint32 + + // TODO Windows. Once daemon is running on Windows, move this code back to + // NewDaemon() in daemon.go, and extend the check to support Windows. + if runtime.GOOS != "linux" && runtime.GOOS != "windows" { + return ErrSystemNotSupported + } + + // TODO Windows. May need at some point to ensure have elevation and + // possibly LocalSystem. + + // Validate the OS version. Note that docker.exe must be manifested for this + // call to return the correct version. + dwVersion, err := syscall.GetVersion() + if err != nil { + return fmt.Errorf("Failed to call GetVersion()") + } + if int(dwVersion&0xFF) < 10 { + return fmt.Errorf("This version of Windows does not support the docker daemon") + } + + return nil +} + +// configureKernelSecuritySupport configures and validate security support for the kernel +func configureKernelSecuritySupport(config *Config, driverName string) error { + return nil +} + +func migrateIfDownlevel(driver graphdriver.Driver, root string) error { + return nil +} + +func configureVolumes(config *Config) error { + // Windows does not support volumes at this time + return nil +} + +func configureSysInit(config *Config) (string, error) { + // TODO Windows. + return os.Getenv("TEMP"), nil +} + +func isNetworkDisabled(config *Config) bool { + return false +} + +func initNetworkController(config *Config) (libnetwork.NetworkController, error) { + // TODO Windows + return nil, nil +} diff --git a/docker/daemon.go b/docker/daemon.go index 0dbcd22c93..1add3c90c2 100644 --- a/docker/daemon.go +++ b/docker/daemon.go @@ -14,13 +14,10 @@ import ( apiserver "github.com/docker/docker/api/server" "github.com/docker/docker/autogen/dockerversion" "github.com/docker/docker/daemon" - _ "github.com/docker/docker/daemon/execdriver/lxc" - _ "github.com/docker/docker/daemon/execdriver/native" "github.com/docker/docker/pkg/homedir" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/pidfile" "github.com/docker/docker/pkg/signal" - "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/timeutils" "github.com/docker/docker/pkg/tlsconfig" "github.com/docker/docker/registry" @@ -113,8 +110,8 @@ func mainDaemon() { EnableCors: daemonCfg.EnableCors, CorsHeaders: daemonCfg.CorsHeaders, Version: dockerversion.VERSION, - SocketGroup: daemonCfg.SocketGroup, } + serverConfig = setPlatformServerConfig(serverConfig, daemonCfg) if *flTls { if *flTlsVerify { @@ -212,14 +209,3 @@ func shutdownDaemon(d *daemon.Daemon, timeout time.Duration) { logrus.Error("Force shutdown daemon") } } - -// currentUserIsOwner checks whether the current user is the owner of the given -// file. -func currentUserIsOwner(f string) bool { - if fileInfo, err := system.Stat(f); err == nil && fileInfo != nil { - if int(fileInfo.Uid()) == os.Getuid() { - return true - } - } - return false -} diff --git a/docker/daemon_unix.go b/docker/daemon_unix.go new file mode 100644 index 0000000000..d1d7f53546 --- /dev/null +++ b/docker/daemon_unix.go @@ -0,0 +1,30 @@ +// +build daemon,!windows + +package main + +import ( + "os" + + apiserver "github.com/docker/docker/api/server" + "github.com/docker/docker/daemon" + "github.com/docker/docker/pkg/system" + + _ "github.com/docker/docker/daemon/execdriver/lxc" + _ "github.com/docker/docker/daemon/execdriver/native" +) + +func setPlatformServerConfig(serverConfig *apiserver.ServerConfig, daemonCfg *daemon.Config) *apiserver.ServerConfig { + serverConfig.SocketGroup = daemonCfg.SocketGroup + return serverConfig +} + +// currentUserIsOwner checks whether the current user is the owner of the given +// file. +func currentUserIsOwner(f string) bool { + if fileInfo, err := system.Stat(f); err == nil && fileInfo != nil { + if int(fileInfo.Uid()) == os.Getuid() { + return true + } + } + return false +} diff --git a/docker/daemon_windows.go b/docker/daemon_windows.go new file mode 100644 index 0000000000..2ba15ee65a --- /dev/null +++ b/docker/daemon_windows.go @@ -0,0 +1,18 @@ +// +build daemon + +package main + +import ( + apiserver "github.com/docker/docker/api/server" + "github.com/docker/docker/daemon" +) + +func setPlatformServerConfig(serverConfig *apiserver.ServerConfig, daemonCfg *daemon.Config) *apiserver.ServerConfig { + return serverConfig +} + +// currentUserIsOwner checks whether the current user is the owner of the given +// file. +func currentUserIsOwner(f string) bool { + return false +}