github.com/kobeld/docker@v1.12.0-rc1/daemon/daemon_unix.go (about) 1 // +build linux freebsd 2 3 package daemon 4 5 import ( 6 "bytes" 7 "fmt" 8 "io/ioutil" 9 "net" 10 "os" 11 "path/filepath" 12 "runtime" 13 "runtime/debug" 14 "strconv" 15 "strings" 16 "syscall" 17 "time" 18 19 "github.com/Sirupsen/logrus" 20 "github.com/docker/docker/container" 21 "github.com/docker/docker/image" 22 "github.com/docker/docker/layer" 23 "github.com/docker/docker/pkg/idtools" 24 "github.com/docker/docker/pkg/parsers" 25 "github.com/docker/docker/pkg/parsers/kernel" 26 "github.com/docker/docker/pkg/sysinfo" 27 "github.com/docker/docker/reference" 28 "github.com/docker/docker/runconfig" 29 runconfigopts "github.com/docker/docker/runconfig/opts" 30 "github.com/docker/engine-api/types" 31 "github.com/docker/engine-api/types/blkiodev" 32 pblkiodev "github.com/docker/engine-api/types/blkiodev" 33 containertypes "github.com/docker/engine-api/types/container" 34 "github.com/docker/libnetwork" 35 nwconfig "github.com/docker/libnetwork/config" 36 "github.com/docker/libnetwork/drivers/bridge" 37 "github.com/docker/libnetwork/netlabel" 38 "github.com/docker/libnetwork/netutils" 39 "github.com/docker/libnetwork/options" 40 lntypes "github.com/docker/libnetwork/types" 41 "github.com/opencontainers/runc/libcontainer/label" 42 "github.com/opencontainers/runc/libcontainer/user" 43 "github.com/opencontainers/specs/specs-go" 44 ) 45 46 const ( 47 // See https://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/tree/kernel/sched/sched.h?id=8cd9234c64c584432f6992fe944ca9e46ca8ea76#n269 48 linuxMinCPUShares = 2 49 linuxMaxCPUShares = 262144 50 platformSupported = true 51 // It's not kernel limit, we want this 4M limit to supply a reasonable functional container 52 linuxMinMemory = 4194304 53 // constants for remapped root settings 54 defaultIDSpecifier string = "default" 55 defaultRemappedID string = "dockremap" 56 57 // constant for cgroup drivers 58 cgroupFsDriver = "cgroupfs" 59 cgroupSystemdDriver = "systemd" 60 ) 61 62 func getMemoryResources(config containertypes.Resources) *specs.Memory { 63 memory := specs.Memory{} 64 65 if config.Memory > 0 { 66 limit := uint64(config.Memory) 67 memory.Limit = &limit 68 } 69 70 if config.MemoryReservation > 0 { 71 reservation := uint64(config.MemoryReservation) 72 memory.Reservation = &reservation 73 } 74 75 if config.MemorySwap != 0 { 76 swap := uint64(config.MemorySwap) 77 memory.Swap = &swap 78 } 79 80 if config.MemorySwappiness != nil { 81 swappiness := uint64(*config.MemorySwappiness) 82 memory.Swappiness = &swappiness 83 } 84 85 if config.KernelMemory != 0 { 86 kernelMemory := uint64(config.KernelMemory) 87 memory.Kernel = &kernelMemory 88 } 89 90 return &memory 91 } 92 93 func getCPUResources(config containertypes.Resources) *specs.CPU { 94 cpu := specs.CPU{} 95 96 if config.CPUShares != 0 { 97 shares := uint64(config.CPUShares) 98 cpu.Shares = &shares 99 } 100 101 if config.CpusetCpus != "" { 102 cpuset := config.CpusetCpus 103 cpu.Cpus = &cpuset 104 } 105 106 if config.CpusetMems != "" { 107 cpuset := config.CpusetMems 108 cpu.Mems = &cpuset 109 } 110 111 if config.CPUPeriod != 0 { 112 period := uint64(config.CPUPeriod) 113 cpu.Period = &period 114 } 115 116 if config.CPUQuota != 0 { 117 quota := uint64(config.CPUQuota) 118 cpu.Quota = "a 119 } 120 121 return &cpu 122 } 123 124 func getBlkioWeightDevices(config containertypes.Resources) ([]specs.WeightDevice, error) { 125 var stat syscall.Stat_t 126 var blkioWeightDevices []specs.WeightDevice 127 128 for _, weightDevice := range config.BlkioWeightDevice { 129 if err := syscall.Stat(weightDevice.Path, &stat); err != nil { 130 return nil, err 131 } 132 weight := weightDevice.Weight 133 d := specs.WeightDevice{Weight: &weight} 134 d.Major = int64(stat.Rdev / 256) 135 d.Minor = int64(stat.Rdev % 256) 136 blkioWeightDevices = append(blkioWeightDevices, d) 137 } 138 139 return blkioWeightDevices, nil 140 } 141 142 func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error { 143 var ( 144 labelOpts []string 145 err error 146 ) 147 148 for _, opt := range config.SecurityOpt { 149 if opt == "no-new-privileges" { 150 container.NoNewPrivileges = true 151 } else { 152 var con []string 153 if strings.Contains(opt, "=") { 154 con = strings.SplitN(opt, "=", 2) 155 } else if strings.Contains(opt, ":") { 156 con = strings.SplitN(opt, ":", 2) 157 logrus.Warn("Security options with `:` as a separator are deprecated and will be completely unsupported in 1.13, use `=` instead.") 158 } 159 160 if len(con) != 2 { 161 return fmt.Errorf("Invalid --security-opt 1: %q", opt) 162 } 163 164 switch con[0] { 165 case "label": 166 labelOpts = append(labelOpts, con[1]) 167 case "apparmor": 168 container.AppArmorProfile = con[1] 169 case "seccomp": 170 container.SeccompProfile = con[1] 171 default: 172 return fmt.Errorf("Invalid --security-opt 2: %q", opt) 173 } 174 } 175 } 176 177 container.ProcessLabel, container.MountLabel, err = label.InitLabels(labelOpts) 178 return err 179 } 180 181 func getBlkioThrottleDevices(devs []*blkiodev.ThrottleDevice) ([]specs.ThrottleDevice, error) { 182 var throttleDevices []specs.ThrottleDevice 183 var stat syscall.Stat_t 184 185 for _, d := range devs { 186 if err := syscall.Stat(d.Path, &stat); err != nil { 187 return nil, err 188 } 189 rate := d.Rate 190 d := specs.ThrottleDevice{Rate: &rate} 191 d.Major = int64(stat.Rdev / 256) 192 d.Minor = int64(stat.Rdev % 256) 193 throttleDevices = append(throttleDevices, d) 194 } 195 196 return throttleDevices, nil 197 } 198 199 func checkKernelVersion(k, major, minor int) bool { 200 if v, err := kernel.GetKernelVersion(); err != nil { 201 logrus.Warnf("error getting kernel version: %s", err) 202 } else { 203 if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: k, Major: major, Minor: minor}) < 0 { 204 return false 205 } 206 } 207 return true 208 } 209 210 func checkKernel() error { 211 // Check for unsupported kernel versions 212 // FIXME: it would be cleaner to not test for specific versions, but rather 213 // test for specific functionalities. 214 // Unfortunately we can't test for the feature "does not cause a kernel panic" 215 // without actually causing a kernel panic, so we need this workaround until 216 // the circumstances of pre-3.10 crashes are clearer. 217 // For details see https://github.com/docker/docker/issues/407 218 // Docker 1.11 and above doesn't actually run on kernels older than 3.4, 219 // due to containerd-shim usage of PR_SET_CHILD_SUBREAPER (introduced in 3.4). 220 if !checkKernelVersion(3, 10, 0) { 221 v, _ := kernel.GetKernelVersion() 222 if os.Getenv("DOCKER_NOWARN_KERNEL_VERSION") == "" { 223 logrus.Fatalf("Your Linux kernel version %s is not supported for running docker. Please upgrade your kernel to 3.10.0 or newer.", v.String()) 224 } 225 } 226 return nil 227 } 228 229 // adaptContainerSettings is called during container creation to modify any 230 // settings necessary in the HostConfig structure. 231 func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConfig, adjustCPUShares bool) error { 232 if adjustCPUShares && hostConfig.CPUShares > 0 { 233 // Handle unsupported CPUShares 234 if hostConfig.CPUShares < linuxMinCPUShares { 235 logrus.Warnf("Changing requested CPUShares of %d to minimum allowed of %d", hostConfig.CPUShares, linuxMinCPUShares) 236 hostConfig.CPUShares = linuxMinCPUShares 237 } else if hostConfig.CPUShares > linuxMaxCPUShares { 238 logrus.Warnf("Changing requested CPUShares of %d to maximum allowed of %d", hostConfig.CPUShares, linuxMaxCPUShares) 239 hostConfig.CPUShares = linuxMaxCPUShares 240 } 241 } 242 if hostConfig.Memory > 0 && hostConfig.MemorySwap == 0 { 243 // By default, MemorySwap is set to twice the size of Memory. 244 hostConfig.MemorySwap = hostConfig.Memory * 2 245 } 246 if hostConfig.ShmSize == 0 { 247 hostConfig.ShmSize = container.DefaultSHMSize 248 } 249 var err error 250 if hostConfig.SecurityOpt == nil { 251 hostConfig.SecurityOpt, err = daemon.generateSecurityOpt(hostConfig.IpcMode, hostConfig.PidMode, hostConfig.Privileged) 252 if err != nil { 253 return err 254 } 255 } 256 if hostConfig.MemorySwappiness == nil { 257 defaultSwappiness := int64(-1) 258 hostConfig.MemorySwappiness = &defaultSwappiness 259 } 260 if hostConfig.OomKillDisable == nil { 261 defaultOomKillDisable := false 262 hostConfig.OomKillDisable = &defaultOomKillDisable 263 } 264 265 return nil 266 } 267 268 func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysinfo.SysInfo, update bool) ([]string, error) { 269 warnings := []string{} 270 271 // memory subsystem checks and adjustments 272 if resources.Memory != 0 && resources.Memory < linuxMinMemory { 273 return warnings, fmt.Errorf("Minimum memory limit allowed is 4MB") 274 } 275 if resources.Memory > 0 && !sysInfo.MemoryLimit { 276 warnings = append(warnings, "Your kernel does not support memory limit capabilities. Limitation discarded.") 277 logrus.Warn("Your kernel does not support memory limit capabilities. Limitation discarded.") 278 resources.Memory = 0 279 resources.MemorySwap = -1 280 } 281 if resources.Memory > 0 && resources.MemorySwap != -1 && !sysInfo.SwapLimit { 282 warnings = append(warnings, "Your kernel does not support swap limit capabilities, memory limited without swap.") 283 logrus.Warn("Your kernel does not support swap limit capabilities, memory limited without swap.") 284 resources.MemorySwap = -1 285 } 286 if resources.Memory > 0 && resources.MemorySwap > 0 && resources.MemorySwap < resources.Memory { 287 return warnings, fmt.Errorf("Minimum memoryswap limit should be larger than memory limit, see usage") 288 } 289 if resources.Memory == 0 && resources.MemorySwap > 0 && !update { 290 return warnings, fmt.Errorf("You should always set the Memory limit when using Memoryswap limit, see usage") 291 } 292 if resources.MemorySwappiness != nil && *resources.MemorySwappiness != -1 && !sysInfo.MemorySwappiness { 293 warnings = append(warnings, "Your kernel does not support memory swappiness capabilities, memory swappiness discarded.") 294 logrus.Warn("Your kernel does not support memory swappiness capabilities, memory swappiness discarded.") 295 resources.MemorySwappiness = nil 296 } 297 if resources.MemorySwappiness != nil { 298 swappiness := *resources.MemorySwappiness 299 if swappiness < -1 || swappiness > 100 { 300 return warnings, fmt.Errorf("Invalid value: %v, valid memory swappiness range is 0-100", swappiness) 301 } 302 } 303 if resources.MemoryReservation > 0 && !sysInfo.MemoryReservation { 304 warnings = append(warnings, "Your kernel does not support memory soft limit capabilities. Limitation discarded.") 305 logrus.Warn("Your kernel does not support memory soft limit capabilities. Limitation discarded.") 306 resources.MemoryReservation = 0 307 } 308 if resources.MemoryReservation > 0 && resources.MemoryReservation < linuxMinMemory { 309 return warnings, fmt.Errorf("Minimum memory reservation allowed is 4MB") 310 } 311 if resources.Memory > 0 && resources.MemoryReservation > 0 && resources.Memory < resources.MemoryReservation { 312 return warnings, fmt.Errorf("Minimum memory limit should be larger than memory reservation limit, see usage") 313 } 314 if resources.KernelMemory > 0 && !sysInfo.KernelMemory { 315 warnings = append(warnings, "Your kernel does not support kernel memory limit capabilities. Limitation discarded.") 316 logrus.Warn("Your kernel does not support kernel memory limit capabilities. Limitation discarded.") 317 resources.KernelMemory = 0 318 } 319 if resources.KernelMemory > 0 && resources.KernelMemory < linuxMinMemory { 320 return warnings, fmt.Errorf("Minimum kernel memory limit allowed is 4MB") 321 } 322 if resources.KernelMemory > 0 && !checkKernelVersion(4, 0, 0) { 323 warnings = append(warnings, "You specified a kernel memory limit on a kernel older than 4.0. Kernel memory limits are experimental on older kernels, it won't work as expected and can cause your system to be unstable.") 324 logrus.Warn("You specified a kernel memory limit on a kernel older than 4.0. Kernel memory limits are experimental on older kernels, it won't work as expected and can cause your system to be unstable.") 325 } 326 if resources.OomKillDisable != nil && !sysInfo.OomKillDisable { 327 // only produce warnings if the setting wasn't to *disable* the OOM Kill; no point 328 // warning the caller if they already wanted the feature to be off 329 if *resources.OomKillDisable { 330 warnings = append(warnings, "Your kernel does not support OomKillDisable, OomKillDisable discarded.") 331 logrus.Warn("Your kernel does not support OomKillDisable, OomKillDisable discarded.") 332 } 333 resources.OomKillDisable = nil 334 } 335 336 if resources.PidsLimit != 0 && !sysInfo.PidsLimit { 337 warnings = append(warnings, "Your kernel does not support pids limit capabilities, pids limit discarded.") 338 logrus.Warn("Your kernel does not support pids limit capabilities, pids limit discarded.") 339 resources.PidsLimit = 0 340 } 341 342 // cpu subsystem checks and adjustments 343 if resources.CPUShares > 0 && !sysInfo.CPUShares { 344 warnings = append(warnings, "Your kernel does not support CPU shares. Shares discarded.") 345 logrus.Warn("Your kernel does not support CPU shares. Shares discarded.") 346 resources.CPUShares = 0 347 } 348 if resources.CPUPeriod > 0 && !sysInfo.CPUCfsPeriod { 349 warnings = append(warnings, "Your kernel does not support CPU cfs period. Period discarded.") 350 logrus.Warn("Your kernel does not support CPU cfs period. Period discarded.") 351 resources.CPUPeriod = 0 352 } 353 if resources.CPUPeriod != 0 && (resources.CPUPeriod < 1000 || resources.CPUPeriod > 1000000) { 354 return warnings, fmt.Errorf("CPU cfs period can not be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000)") 355 } 356 if resources.CPUQuota > 0 && !sysInfo.CPUCfsQuota { 357 warnings = append(warnings, "Your kernel does not support CPU cfs quota. Quota discarded.") 358 logrus.Warn("Your kernel does not support CPU cfs quota. Quota discarded.") 359 resources.CPUQuota = 0 360 } 361 if resources.CPUQuota > 0 && resources.CPUQuota < 1000 { 362 return warnings, fmt.Errorf("CPU cfs quota can not be less than 1ms (i.e. 1000)") 363 } 364 if resources.CPUPercent > 0 { 365 warnings = append(warnings, "%s does not support CPU percent. Percent discarded.", runtime.GOOS) 366 logrus.Warnf("%s does not support CPU percent. Percent discarded.", runtime.GOOS) 367 resources.CPUPercent = 0 368 } 369 370 // cpuset subsystem checks and adjustments 371 if (resources.CpusetCpus != "" || resources.CpusetMems != "") && !sysInfo.Cpuset { 372 warnings = append(warnings, "Your kernel does not support cpuset. Cpuset discarded.") 373 logrus.Warn("Your kernel does not support cpuset. Cpuset discarded.") 374 resources.CpusetCpus = "" 375 resources.CpusetMems = "" 376 } 377 cpusAvailable, err := sysInfo.IsCpusetCpusAvailable(resources.CpusetCpus) 378 if err != nil { 379 return warnings, fmt.Errorf("Invalid value %s for cpuset cpus", resources.CpusetCpus) 380 } 381 if !cpusAvailable { 382 return warnings, fmt.Errorf("Requested CPUs are not available - requested %s, available: %s", resources.CpusetCpus, sysInfo.Cpus) 383 } 384 memsAvailable, err := sysInfo.IsCpusetMemsAvailable(resources.CpusetMems) 385 if err != nil { 386 return warnings, fmt.Errorf("Invalid value %s for cpuset mems", resources.CpusetMems) 387 } 388 if !memsAvailable { 389 return warnings, fmt.Errorf("Requested memory nodes are not available - requested %s, available: %s", resources.CpusetMems, sysInfo.Mems) 390 } 391 392 // blkio subsystem checks and adjustments 393 if resources.BlkioWeight > 0 && !sysInfo.BlkioWeight { 394 warnings = append(warnings, "Your kernel does not support Block I/O weight. Weight discarded.") 395 logrus.Warn("Your kernel does not support Block I/O weight. Weight discarded.") 396 resources.BlkioWeight = 0 397 } 398 if resources.BlkioWeight > 0 && (resources.BlkioWeight < 10 || resources.BlkioWeight > 1000) { 399 return warnings, fmt.Errorf("Range of blkio weight is from 10 to 1000") 400 } 401 if resources.IOMaximumBandwidth != 0 || resources.IOMaximumIOps != 0 { 402 return warnings, fmt.Errorf("Invalid QoS settings: %s does not support Maximum IO Bandwidth or Maximum IO IOps", runtime.GOOS) 403 } 404 if len(resources.BlkioWeightDevice) > 0 && !sysInfo.BlkioWeightDevice { 405 warnings = append(warnings, "Your kernel does not support Block I/O weight_device.") 406 logrus.Warn("Your kernel does not support Block I/O weight_device. Weight-device discarded.") 407 resources.BlkioWeightDevice = []*pblkiodev.WeightDevice{} 408 } 409 if len(resources.BlkioDeviceReadBps) > 0 && !sysInfo.BlkioReadBpsDevice { 410 warnings = append(warnings, "Your kernel does not support Block read limit in bytes per second.") 411 logrus.Warn("Your kernel does not support Block I/O read limit in bytes per second. --device-read-bps discarded.") 412 resources.BlkioDeviceReadBps = []*pblkiodev.ThrottleDevice{} 413 } 414 if len(resources.BlkioDeviceWriteBps) > 0 && !sysInfo.BlkioWriteBpsDevice { 415 warnings = append(warnings, "Your kernel does not support Block write limit in bytes per second.") 416 logrus.Warn("Your kernel does not support Block I/O write limit in bytes per second. --device-write-bps discarded.") 417 resources.BlkioDeviceWriteBps = []*pblkiodev.ThrottleDevice{} 418 } 419 if len(resources.BlkioDeviceReadIOps) > 0 && !sysInfo.BlkioReadIOpsDevice { 420 warnings = append(warnings, "Your kernel does not support Block read limit in IO per second.") 421 logrus.Warn("Your kernel does not support Block I/O read limit in IO per second. -device-read-iops discarded.") 422 resources.BlkioDeviceReadIOps = []*pblkiodev.ThrottleDevice{} 423 } 424 if len(resources.BlkioDeviceWriteIOps) > 0 && !sysInfo.BlkioWriteIOpsDevice { 425 warnings = append(warnings, "Your kernel does not support Block write limit in IO per second.") 426 logrus.Warn("Your kernel does not support Block I/O write limit in IO per second. --device-write-iops discarded.") 427 resources.BlkioDeviceWriteIOps = []*pblkiodev.ThrottleDevice{} 428 } 429 430 return warnings, nil 431 } 432 433 func (daemon *Daemon) getCgroupDriver() string { 434 cgroupDriver := cgroupFsDriver 435 436 if UsingSystemd(daemon.configStore) { 437 cgroupDriver = cgroupSystemdDriver 438 } 439 return cgroupDriver 440 } 441 442 // getCD gets the raw value of the native.cgroupdriver option, if set. 443 func getCD(config *Config) string { 444 for _, option := range config.ExecOptions { 445 key, val, err := parsers.ParseKeyValueOpt(option) 446 if err != nil || !strings.EqualFold(key, "native.cgroupdriver") { 447 continue 448 } 449 return val 450 } 451 return "" 452 } 453 454 // VerifyCgroupDriver validates native.cgroupdriver 455 func VerifyCgroupDriver(config *Config) error { 456 cd := getCD(config) 457 if cd == "" || cd == cgroupFsDriver || cd == cgroupSystemdDriver { 458 return nil 459 } 460 return fmt.Errorf("native.cgroupdriver option %s not supported", cd) 461 } 462 463 // UsingSystemd returns true if cli option includes native.cgroupdriver=systemd 464 func UsingSystemd(config *Config) bool { 465 return getCD(config) == cgroupSystemdDriver 466 } 467 468 // verifyPlatformContainerSettings performs platform-specific validation of the 469 // hostconfig and config structures. 470 func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) { 471 warnings := []string{} 472 sysInfo := sysinfo.New(true) 473 474 warnings, err := daemon.verifyExperimentalContainerSettings(hostConfig, config) 475 if err != nil { 476 return warnings, err 477 } 478 479 w, err := verifyContainerResources(&hostConfig.Resources, sysInfo, update) 480 if err != nil { 481 return warnings, err 482 } 483 warnings = append(warnings, w...) 484 485 if hostConfig.ShmSize < 0 { 486 return warnings, fmt.Errorf("SHM size must be greater than 0") 487 } 488 489 if hostConfig.OomScoreAdj < -1000 || hostConfig.OomScoreAdj > 1000 { 490 return warnings, fmt.Errorf("Invalid value %d, range for oom score adj is [-1000, 1000]", hostConfig.OomScoreAdj) 491 } 492 493 // ip-forwarding does not affect container with '--net=host' (or '--net=none') 494 if sysInfo.IPv4ForwardingDisabled && !(hostConfig.NetworkMode.IsHost() || hostConfig.NetworkMode.IsNone()) { 495 warnings = append(warnings, "IPv4 forwarding is disabled. Networking will not work.") 496 logrus.Warn("IPv4 forwarding is disabled. Networking will not work") 497 } 498 // check for various conflicting options with user namespaces 499 if daemon.configStore.RemappedRoot != "" && hostConfig.UsernsMode.IsPrivate() { 500 if hostConfig.Privileged { 501 return warnings, fmt.Errorf("Privileged mode is incompatible with user namespaces") 502 } 503 if hostConfig.NetworkMode.IsHost() { 504 return warnings, fmt.Errorf("Cannot share the host's network namespace when user namespaces are enabled") 505 } 506 if hostConfig.PidMode.IsHost() { 507 return warnings, fmt.Errorf("Cannot share the host PID namespace when user namespaces are enabled") 508 } 509 if hostConfig.ReadonlyRootfs { 510 return warnings, fmt.Errorf("Cannot use the --read-only option when user namespaces are enabled") 511 } 512 } 513 if hostConfig.CgroupParent != "" && UsingSystemd(daemon.configStore) { 514 // CgroupParent for systemd cgroup should be named as "xxx.slice" 515 if len(hostConfig.CgroupParent) <= 6 || !strings.HasSuffix(hostConfig.CgroupParent, ".slice") { 516 return warnings, fmt.Errorf("cgroup-parent for systemd cgroup should be a valid slice named as \"xxx.slice\"") 517 } 518 } 519 if hostConfig.Runtime == "" { 520 hostConfig.Runtime = daemon.configStore.GetDefaultRuntimeName() 521 } 522 523 if rt := daemon.configStore.GetRuntime(hostConfig.Runtime); rt == nil { 524 return warnings, fmt.Errorf("Unknown runtime specified %s", hostConfig.Runtime) 525 } 526 527 return warnings, nil 528 } 529 530 // platformReload update configuration with platform specific options 531 func (daemon *Daemon) platformReload(config *Config, attributes *map[string]string) { 532 if config.IsValueSet("runtimes") { 533 daemon.configStore.Runtimes = config.Runtimes 534 // Always set the default one 535 daemon.configStore.Runtimes[types.DefaultRuntimeName] = types.Runtime{Path: DefaultRuntimeBinary} 536 } 537 538 if config.DefaultRuntime != "" { 539 daemon.configStore.DefaultRuntime = config.DefaultRuntime 540 } 541 542 // Update attributes 543 var runtimeList bytes.Buffer 544 for name, rt := range daemon.configStore.Runtimes { 545 if runtimeList.Len() > 0 { 546 runtimeList.WriteRune(' ') 547 } 548 runtimeList.WriteString(fmt.Sprintf("%s:%s", name, rt)) 549 } 550 551 (*attributes)["runtimes"] = runtimeList.String() 552 (*attributes)["default-runtime"] = daemon.configStore.DefaultRuntime 553 } 554 555 // verifyDaemonSettings performs validation of daemon config struct 556 func verifyDaemonSettings(config *Config) error { 557 // Check for mutually incompatible config options 558 if config.bridgeConfig.Iface != "" && config.bridgeConfig.IP != "" { 559 return fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one") 560 } 561 if !config.bridgeConfig.EnableIPTables && !config.bridgeConfig.InterContainerCommunication { 562 return fmt.Errorf("You specified --iptables=false with --icc=false. ICC=false uses iptables to function. Please set --icc or --iptables to true") 563 } 564 if !config.bridgeConfig.EnableIPTables && config.bridgeConfig.EnableIPMasq { 565 config.bridgeConfig.EnableIPMasq = false 566 } 567 if err := VerifyCgroupDriver(config); err != nil { 568 return err 569 } 570 if config.CgroupParent != "" && UsingSystemd(config) { 571 if len(config.CgroupParent) <= 6 || !strings.HasSuffix(config.CgroupParent, ".slice") { 572 return fmt.Errorf("cgroup-parent for systemd cgroup should be a valid slice named as \"xxx.slice\"") 573 } 574 } 575 576 if config.DefaultRuntime == "" { 577 config.DefaultRuntime = types.DefaultRuntimeName 578 } 579 if config.Runtimes == nil { 580 config.Runtimes = make(map[string]types.Runtime) 581 } 582 config.Runtimes[types.DefaultRuntimeName] = types.Runtime{Path: DefaultRuntimeBinary} 583 584 return nil 585 } 586 587 // checkSystem validates platform-specific requirements 588 func checkSystem() error { 589 if os.Geteuid() != 0 { 590 return fmt.Errorf("The Docker daemon needs to be run as root") 591 } 592 return checkKernel() 593 } 594 595 // configureMaxThreads sets the Go runtime max threads threshold 596 // which is 90% of the kernel setting from /proc/sys/kernel/threads-max 597 func configureMaxThreads(config *Config) error { 598 mt, err := ioutil.ReadFile("/proc/sys/kernel/threads-max") 599 if err != nil { 600 return err 601 } 602 mtint, err := strconv.Atoi(strings.TrimSpace(string(mt))) 603 if err != nil { 604 return err 605 } 606 maxThreads := (mtint / 100) * 90 607 debug.SetMaxThreads(maxThreads) 608 logrus.Debugf("Golang's threads limit set to %d", maxThreads) 609 return nil 610 } 611 612 // configureKernelSecuritySupport configures and validates security support for the kernel 613 func configureKernelSecuritySupport(config *Config, driverName string) error { 614 if config.EnableSelinuxSupport { 615 if selinuxEnabled() { 616 // As Docker on overlayFS and SELinux are incompatible at present, error on overlayfs being enabled 617 if driverName == "overlay" { 618 return fmt.Errorf("SELinux is not supported with the %s graph driver", driverName) 619 } 620 logrus.Debug("SELinux enabled successfully") 621 } else { 622 logrus.Warn("Docker could not enable SELinux on the host system") 623 } 624 } else { 625 selinuxSetDisabled() 626 } 627 return nil 628 } 629 630 func (daemon *Daemon) initNetworkController(config *Config, activeSandboxes map[string]interface{}) (libnetwork.NetworkController, error) { 631 netOptions, err := daemon.networkOptions(config, activeSandboxes) 632 if err != nil { 633 return nil, err 634 } 635 636 controller, err := libnetwork.New(netOptions...) 637 if err != nil { 638 return nil, fmt.Errorf("error obtaining controller instance: %v", err) 639 } 640 641 if len(activeSandboxes) > 0 { 642 logrus.Infof("There are old running containers, the network config will not take affect") 643 return controller, nil 644 } 645 646 // Initialize default network on "null" 647 if n, _ := controller.NetworkByName("none"); n == nil { 648 if _, err := controller.NewNetwork("null", "none", "", libnetwork.NetworkOptionPersist(true)); err != nil { 649 return nil, fmt.Errorf("Error creating default \"null\" network: %v", err) 650 } 651 } 652 653 // Initialize default network on "host" 654 if n, _ := controller.NetworkByName("host"); n == nil { 655 if _, err := controller.NewNetwork("host", "host", "", libnetwork.NetworkOptionPersist(true)); err != nil { 656 return nil, fmt.Errorf("Error creating default \"host\" network: %v", err) 657 } 658 } 659 if !config.DisableBridge { 660 // Initialize default driver "bridge" 661 if err := initBridgeDriver(controller, config); err != nil { 662 return nil, err 663 } 664 } 665 666 return controller, nil 667 } 668 669 func driverOptions(config *Config) []nwconfig.Option { 670 bridgeConfig := options.Generic{ 671 "EnableIPForwarding": config.bridgeConfig.EnableIPForward, 672 "EnableIPTables": config.bridgeConfig.EnableIPTables, 673 "EnableUserlandProxy": config.bridgeConfig.EnableUserlandProxy} 674 bridgeOption := options.Generic{netlabel.GenericData: bridgeConfig} 675 676 dOptions := []nwconfig.Option{} 677 dOptions = append(dOptions, nwconfig.OptionDriverConfig("bridge", bridgeOption)) 678 return dOptions 679 } 680 681 func initBridgeDriver(controller libnetwork.NetworkController, config *Config) error { 682 if n, err := controller.NetworkByName("bridge"); err == nil { 683 if err = n.Delete(); err != nil { 684 return fmt.Errorf("could not delete the default bridge network: %v", err) 685 } 686 } 687 688 bridgeName := bridge.DefaultBridgeName 689 if config.bridgeConfig.Iface != "" { 690 bridgeName = config.bridgeConfig.Iface 691 } 692 netOption := map[string]string{ 693 bridge.BridgeName: bridgeName, 694 bridge.DefaultBridge: strconv.FormatBool(true), 695 netlabel.DriverMTU: strconv.Itoa(config.Mtu), 696 bridge.EnableIPMasquerade: strconv.FormatBool(config.bridgeConfig.EnableIPMasq), 697 bridge.EnableICC: strconv.FormatBool(config.bridgeConfig.InterContainerCommunication), 698 } 699 700 // --ip processing 701 if config.bridgeConfig.DefaultIP != nil { 702 netOption[bridge.DefaultBindingIP] = config.bridgeConfig.DefaultIP.String() 703 } 704 705 var ( 706 ipamV4Conf *libnetwork.IpamConf 707 ipamV6Conf *libnetwork.IpamConf 708 ) 709 710 ipamV4Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)} 711 712 nw, nw6List, err := netutils.ElectInterfaceAddresses(bridgeName) 713 if err == nil { 714 ipamV4Conf.PreferredPool = lntypes.GetIPNetCanonical(nw).String() 715 hip, _ := lntypes.GetHostPartIP(nw.IP, nw.Mask) 716 if hip.IsGlobalUnicast() { 717 ipamV4Conf.Gateway = nw.IP.String() 718 } 719 } 720 721 if config.bridgeConfig.IP != "" { 722 ipamV4Conf.PreferredPool = config.bridgeConfig.IP 723 ip, _, err := net.ParseCIDR(config.bridgeConfig.IP) 724 if err != nil { 725 return err 726 } 727 ipamV4Conf.Gateway = ip.String() 728 } else if bridgeName == bridge.DefaultBridgeName && ipamV4Conf.PreferredPool != "" { 729 logrus.Infof("Default bridge (%s) is assigned with an IP address %s. Daemon option --bip can be used to set a preferred IP address", bridgeName, ipamV4Conf.PreferredPool) 730 } 731 732 if config.bridgeConfig.FixedCIDR != "" { 733 _, fCIDR, err := net.ParseCIDR(config.bridgeConfig.FixedCIDR) 734 if err != nil { 735 return err 736 } 737 738 ipamV4Conf.SubPool = fCIDR.String() 739 } 740 741 if config.bridgeConfig.DefaultGatewayIPv4 != nil { 742 ipamV4Conf.AuxAddresses["DefaultGatewayIPv4"] = config.bridgeConfig.DefaultGatewayIPv4.String() 743 } 744 745 var deferIPv6Alloc bool 746 if config.bridgeConfig.FixedCIDRv6 != "" { 747 _, fCIDRv6, err := net.ParseCIDR(config.bridgeConfig.FixedCIDRv6) 748 if err != nil { 749 return err 750 } 751 752 // In case user has specified the daemon flag --fixed-cidr-v6 and the passed network has 753 // at least 48 host bits, we need to guarantee the current behavior where the containers' 754 // IPv6 addresses will be constructed based on the containers' interface MAC address. 755 // We do so by telling libnetwork to defer the IPv6 address allocation for the endpoints 756 // on this network until after the driver has created the endpoint and returned the 757 // constructed address. Libnetwork will then reserve this address with the ipam driver. 758 ones, _ := fCIDRv6.Mask.Size() 759 deferIPv6Alloc = ones <= 80 760 761 if ipamV6Conf == nil { 762 ipamV6Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)} 763 } 764 ipamV6Conf.PreferredPool = fCIDRv6.String() 765 766 // In case the --fixed-cidr-v6 is specified and the current docker0 bridge IPv6 767 // address belongs to the same network, we need to inform libnetwork about it, so 768 // that it can be reserved with IPAM and it will not be given away to somebody else 769 for _, nw6 := range nw6List { 770 if fCIDRv6.Contains(nw6.IP) { 771 ipamV6Conf.Gateway = nw6.IP.String() 772 break 773 } 774 } 775 } 776 777 if config.bridgeConfig.DefaultGatewayIPv6 != nil { 778 if ipamV6Conf == nil { 779 ipamV6Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)} 780 } 781 ipamV6Conf.AuxAddresses["DefaultGatewayIPv6"] = config.bridgeConfig.DefaultGatewayIPv6.String() 782 } 783 784 v4Conf := []*libnetwork.IpamConf{ipamV4Conf} 785 v6Conf := []*libnetwork.IpamConf{} 786 if ipamV6Conf != nil { 787 v6Conf = append(v6Conf, ipamV6Conf) 788 } 789 // Initialize default network on "bridge" with the same name 790 _, err = controller.NewNetwork("bridge", "bridge", "", 791 libnetwork.NetworkOptionEnableIPv6(config.bridgeConfig.EnableIPv6), 792 libnetwork.NetworkOptionDriverOpts(netOption), 793 libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil), 794 libnetwork.NetworkOptionDeferIPv6Alloc(deferIPv6Alloc)) 795 if err != nil { 796 return fmt.Errorf("Error creating default \"bridge\" network: %v", err) 797 } 798 return nil 799 } 800 801 // setupInitLayer populates a directory with mountpoints suitable 802 // for bind-mounting things into the container. 803 // 804 // This extra layer is used by all containers as the top-most ro layer. It protects 805 // the container from unwanted side-effects on the rw layer. 806 func setupInitLayer(initLayer string, rootUID, rootGID int) error { 807 for pth, typ := range map[string]string{ 808 "/dev/pts": "dir", 809 "/dev/shm": "dir", 810 "/proc": "dir", 811 "/sys": "dir", 812 "/.dockerenv": "file", 813 "/etc/resolv.conf": "file", 814 "/etc/hosts": "file", 815 "/etc/hostname": "file", 816 "/dev/console": "file", 817 "/etc/mtab": "/proc/mounts", 818 } { 819 parts := strings.Split(pth, "/") 820 prev := "/" 821 for _, p := range parts[1:] { 822 prev = filepath.Join(prev, p) 823 syscall.Unlink(filepath.Join(initLayer, prev)) 824 } 825 826 if _, err := os.Stat(filepath.Join(initLayer, pth)); err != nil { 827 if os.IsNotExist(err) { 828 if err := idtools.MkdirAllNewAs(filepath.Join(initLayer, filepath.Dir(pth)), 0755, rootUID, rootGID); err != nil { 829 return err 830 } 831 switch typ { 832 case "dir": 833 if err := idtools.MkdirAllNewAs(filepath.Join(initLayer, pth), 0755, rootUID, rootGID); err != nil { 834 return err 835 } 836 case "file": 837 f, err := os.OpenFile(filepath.Join(initLayer, pth), os.O_CREATE, 0755) 838 if err != nil { 839 return err 840 } 841 f.Chown(rootUID, rootGID) 842 f.Close() 843 default: 844 if err := os.Symlink(typ, filepath.Join(initLayer, pth)); err != nil { 845 return err 846 } 847 } 848 } else { 849 return err 850 } 851 } 852 } 853 854 // Layer is ready to use, if it wasn't before. 855 return nil 856 } 857 858 // Parse the remapped root (user namespace) option, which can be one of: 859 // username - valid username from /etc/passwd 860 // username:groupname - valid username; valid groupname from /etc/group 861 // uid - 32-bit unsigned int valid Linux UID value 862 // uid:gid - uid value; 32-bit unsigned int Linux GID value 863 // 864 // If no groupname is specified, and a username is specified, an attempt 865 // will be made to lookup a gid for that username as a groupname 866 // 867 // If names are used, they are verified to exist in passwd/group 868 func parseRemappedRoot(usergrp string) (string, string, error) { 869 870 var ( 871 userID, groupID int 872 username, groupname string 873 ) 874 875 idparts := strings.Split(usergrp, ":") 876 if len(idparts) > 2 { 877 return "", "", fmt.Errorf("Invalid user/group specification in --userns-remap: %q", usergrp) 878 } 879 880 if uid, err := strconv.ParseInt(idparts[0], 10, 32); err == nil { 881 // must be a uid; take it as valid 882 userID = int(uid) 883 luser, err := user.LookupUid(userID) 884 if err != nil { 885 return "", "", fmt.Errorf("Uid %d has no entry in /etc/passwd: %v", userID, err) 886 } 887 username = luser.Name 888 if len(idparts) == 1 { 889 // if the uid was numeric and no gid was specified, take the uid as the gid 890 groupID = userID 891 lgrp, err := user.LookupGid(groupID) 892 if err != nil { 893 return "", "", fmt.Errorf("Gid %d has no entry in /etc/group: %v", groupID, err) 894 } 895 groupname = lgrp.Name 896 } 897 } else { 898 lookupName := idparts[0] 899 // special case: if the user specified "default", they want Docker to create or 900 // use (after creation) the "dockremap" user/group for root remapping 901 if lookupName == defaultIDSpecifier { 902 lookupName = defaultRemappedID 903 } 904 luser, err := user.LookupUser(lookupName) 905 if err != nil && idparts[0] != defaultIDSpecifier { 906 // error if the name requested isn't the special "dockremap" ID 907 return "", "", fmt.Errorf("Error during uid lookup for %q: %v", lookupName, err) 908 } else if err != nil { 909 // special case-- if the username == "default", then we have been asked 910 // to create a new entry pair in /etc/{passwd,group} for which the /etc/sub{uid,gid} 911 // ranges will be used for the user and group mappings in user namespaced containers 912 _, _, err := idtools.AddNamespaceRangesUser(defaultRemappedID) 913 if err == nil { 914 return defaultRemappedID, defaultRemappedID, nil 915 } 916 return "", "", fmt.Errorf("Error during %q user creation: %v", defaultRemappedID, err) 917 } 918 username = luser.Name 919 if len(idparts) == 1 { 920 // we only have a string username, and no group specified; look up gid from username as group 921 group, err := user.LookupGroup(lookupName) 922 if err != nil { 923 return "", "", fmt.Errorf("Error during gid lookup for %q: %v", lookupName, err) 924 } 925 groupID = group.Gid 926 groupname = group.Name 927 } 928 } 929 930 if len(idparts) == 2 { 931 // groupname or gid is separately specified and must be resolved 932 // to an unsigned 32-bit gid 933 if gid, err := strconv.ParseInt(idparts[1], 10, 32); err == nil { 934 // must be a gid, take it as valid 935 groupID = int(gid) 936 lgrp, err := user.LookupGid(groupID) 937 if err != nil { 938 return "", "", fmt.Errorf("Gid %d has no entry in /etc/passwd: %v", groupID, err) 939 } 940 groupname = lgrp.Name 941 } else { 942 // not a number; attempt a lookup 943 if _, err := user.LookupGroup(idparts[1]); err != nil { 944 return "", "", fmt.Errorf("Error during groupname lookup for %q: %v", idparts[1], err) 945 } 946 groupname = idparts[1] 947 } 948 } 949 return username, groupname, nil 950 } 951 952 func setupRemappedRoot(config *Config) ([]idtools.IDMap, []idtools.IDMap, error) { 953 if runtime.GOOS != "linux" && config.RemappedRoot != "" { 954 return nil, nil, fmt.Errorf("User namespaces are only supported on Linux") 955 } 956 957 // if the daemon was started with remapped root option, parse 958 // the config option to the int uid,gid values 959 var ( 960 uidMaps, gidMaps []idtools.IDMap 961 ) 962 if config.RemappedRoot != "" { 963 username, groupname, err := parseRemappedRoot(config.RemappedRoot) 964 if err != nil { 965 return nil, nil, err 966 } 967 if username == "root" { 968 // Cannot setup user namespaces with a 1-to-1 mapping; "--root=0:0" is a no-op 969 // effectively 970 logrus.Warn("User namespaces: root cannot be remapped with itself; user namespaces are OFF") 971 return uidMaps, gidMaps, nil 972 } 973 logrus.Infof("User namespaces: ID ranges will be mapped to subuid/subgid ranges of: %s:%s", username, groupname) 974 // update remapped root setting now that we have resolved them to actual names 975 config.RemappedRoot = fmt.Sprintf("%s:%s", username, groupname) 976 977 uidMaps, gidMaps, err = idtools.CreateIDMappings(username, groupname) 978 if err != nil { 979 return nil, nil, fmt.Errorf("Can't create ID mappings: %v", err) 980 } 981 } 982 return uidMaps, gidMaps, nil 983 } 984 985 func setupDaemonRoot(config *Config, rootDir string, rootUID, rootGID int) error { 986 config.Root = rootDir 987 // the docker root metadata directory needs to have execute permissions for all users (g+x,o+x) 988 // so that syscalls executing as non-root, operating on subdirectories of the graph root 989 // (e.g. mounted layers of a container) can traverse this path. 990 // The user namespace support will create subdirectories for the remapped root host uid:gid 991 // pair owned by that same uid:gid pair for proper write access to those needed metadata and 992 // layer content subtrees. 993 if _, err := os.Stat(rootDir); err == nil { 994 // root current exists; verify the access bits are correct by setting them 995 if err = os.Chmod(rootDir, 0711); err != nil { 996 return err 997 } 998 } else if os.IsNotExist(err) { 999 // no root exists yet, create it 0711 with root:root ownership 1000 if err := os.MkdirAll(rootDir, 0711); err != nil { 1001 return err 1002 } 1003 } 1004 1005 // if user namespaces are enabled we will create a subtree underneath the specified root 1006 // with any/all specified remapped root uid/gid options on the daemon creating 1007 // a new subdirectory with ownership set to the remapped uid/gid (so as to allow 1008 // `chdir()` to work for containers namespaced to that uid/gid) 1009 if config.RemappedRoot != "" { 1010 config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", rootUID, rootGID)) 1011 logrus.Debugf("Creating user namespaced daemon root: %s", config.Root) 1012 // Create the root directory if it doesn't exist 1013 if err := idtools.MkdirAllAs(config.Root, 0700, rootUID, rootGID); err != nil { 1014 return fmt.Errorf("Cannot create daemon root: %s: %v", config.Root, err) 1015 } 1016 } 1017 return nil 1018 } 1019 1020 // registerLinks writes the links to a file. 1021 func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *containertypes.HostConfig) error { 1022 if hostConfig == nil || hostConfig.NetworkMode.IsUserDefined() { 1023 return nil 1024 } 1025 1026 for _, l := range hostConfig.Links { 1027 name, alias, err := runconfigopts.ParseLink(l) 1028 if err != nil { 1029 return err 1030 } 1031 child, err := daemon.GetContainer(name) 1032 if err != nil { 1033 return fmt.Errorf("Could not get container for %s", name) 1034 } 1035 for child.HostConfig.NetworkMode.IsContainer() { 1036 parts := strings.SplitN(string(child.HostConfig.NetworkMode), ":", 2) 1037 child, err = daemon.GetContainer(parts[1]) 1038 if err != nil { 1039 return fmt.Errorf("Could not get container for %s", parts[1]) 1040 } 1041 } 1042 if child.HostConfig.NetworkMode.IsHost() { 1043 return runconfig.ErrConflictHostNetworkAndLinks 1044 } 1045 if err := daemon.registerLink(container, child, alias); err != nil { 1046 return err 1047 } 1048 } 1049 1050 // After we load all the links into the daemon 1051 // set them to nil on the hostconfig 1052 return container.WriteHostConfig() 1053 } 1054 1055 // conditionalMountOnStart is a platform specific helper function during the 1056 // container start to call mount. 1057 func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error { 1058 return daemon.Mount(container) 1059 } 1060 1061 // conditionalUnmountOnCleanup is a platform specific helper function called 1062 // during the cleanup of a container to unmount. 1063 func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error { 1064 return daemon.Unmount(container) 1065 } 1066 1067 func restoreCustomImage(is image.Store, ls layer.Store, rs reference.Store) error { 1068 // Unix has no custom images to register 1069 return nil 1070 } 1071 1072 func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) { 1073 if !c.IsRunning() { 1074 return nil, errNotRunning{c.ID} 1075 } 1076 stats, err := daemon.containerd.Stats(c.ID) 1077 if err != nil { 1078 return nil, err 1079 } 1080 s := &types.StatsJSON{} 1081 cgs := stats.CgroupStats 1082 if cgs != nil { 1083 s.BlkioStats = types.BlkioStats{ 1084 IoServiceBytesRecursive: copyBlkioEntry(cgs.BlkioStats.IoServiceBytesRecursive), 1085 IoServicedRecursive: copyBlkioEntry(cgs.BlkioStats.IoServicedRecursive), 1086 IoQueuedRecursive: copyBlkioEntry(cgs.BlkioStats.IoQueuedRecursive), 1087 IoServiceTimeRecursive: copyBlkioEntry(cgs.BlkioStats.IoServiceTimeRecursive), 1088 IoWaitTimeRecursive: copyBlkioEntry(cgs.BlkioStats.IoWaitTimeRecursive), 1089 IoMergedRecursive: copyBlkioEntry(cgs.BlkioStats.IoMergedRecursive), 1090 IoTimeRecursive: copyBlkioEntry(cgs.BlkioStats.IoTimeRecursive), 1091 SectorsRecursive: copyBlkioEntry(cgs.BlkioStats.SectorsRecursive), 1092 } 1093 cpu := cgs.CpuStats 1094 s.CPUStats = types.CPUStats{ 1095 CPUUsage: types.CPUUsage{ 1096 TotalUsage: cpu.CpuUsage.TotalUsage, 1097 PercpuUsage: cpu.CpuUsage.PercpuUsage, 1098 UsageInKernelmode: cpu.CpuUsage.UsageInKernelmode, 1099 UsageInUsermode: cpu.CpuUsage.UsageInUsermode, 1100 }, 1101 ThrottlingData: types.ThrottlingData{ 1102 Periods: cpu.ThrottlingData.Periods, 1103 ThrottledPeriods: cpu.ThrottlingData.ThrottledPeriods, 1104 ThrottledTime: cpu.ThrottlingData.ThrottledTime, 1105 }, 1106 } 1107 mem := cgs.MemoryStats.Usage 1108 s.MemoryStats = types.MemoryStats{ 1109 Usage: mem.Usage, 1110 MaxUsage: mem.MaxUsage, 1111 Stats: cgs.MemoryStats.Stats, 1112 Failcnt: mem.Failcnt, 1113 Limit: mem.Limit, 1114 } 1115 // if the container does not set memory limit, use the machineMemory 1116 if mem.Limit > daemon.statsCollector.machineMemory && daemon.statsCollector.machineMemory > 0 { 1117 s.MemoryStats.Limit = daemon.statsCollector.machineMemory 1118 } 1119 if cgs.PidsStats != nil { 1120 s.PidsStats = types.PidsStats{ 1121 Current: cgs.PidsStats.Current, 1122 } 1123 } 1124 } 1125 s.Read = time.Unix(int64(stats.Timestamp), 0) 1126 return s, nil 1127 } 1128 1129 // setDefaultIsolation determines the default isolation mode for the 1130 // daemon to run in. This is only applicable on Windows 1131 func (daemon *Daemon) setDefaultIsolation() error { 1132 return nil 1133 } 1134 1135 func rootFSToAPIType(rootfs *image.RootFS) types.RootFS { 1136 var layers []string 1137 for _, l := range rootfs.DiffIDs { 1138 layers = append(layers, l.String()) 1139 } 1140 return types.RootFS{ 1141 Type: rootfs.Type, 1142 Layers: layers, 1143 } 1144 }