10 "github.com/Microsoft/hcsshim"
11 "github.com/Sirupsen/logrus"
12 "github.com/docker/docker/api/types"
13 containertypes "github.com/docker/docker/api/types/container"
14 "github.com/docker/docker/container"
15 "github.com/docker/docker/daemon/config"
16 "github.com/docker/docker/image"
17 "github.com/docker/docker/pkg/fileutils"
18 "github.com/docker/docker/pkg/idtools"
19 "github.com/docker/docker/pkg/parsers"
20 "github.com/docker/docker/pkg/platform"
21 "github.com/docker/docker/pkg/sysinfo"
22 "github.com/docker/docker/pkg/system"
23 "github.com/docker/docker/runconfig"
24 "github.com/docker/libnetwork"
25 nwconfig "github.com/docker/libnetwork/config"
26 "github.com/docker/libnetwork/datastore"
27 winlibnetwork "github.com/docker/libnetwork/drivers/windows"
28 "github.com/docker/libnetwork/netlabel"
29 "github.com/docker/libnetwork/options"
30 blkiodev "github.com/opencontainers/runc/libcontainer/configs"
31 "golang.org/x/sys/windows"
35 defaultNetworkSpace = "172.16.0.0/12"
36 platformSupported = true
37 windowsMinCPUShares = 1
38 windowsMaxCPUShares = 10000
39 windowsMinCPUPercent = 1
40 windowsMaxCPUPercent = 100
41 windowsMinCPUCount = 1
43 errInvalidState = syscall.Errno(0x139F)
46 // Windows has no concept of an execution state directory. So use config.Root here.
47 func getPluginExecRoot(root string) string {
48 return filepath.Join(root, "plugins")
51 func getBlkioWeightDevices(config *containertypes.HostConfig) ([]blkiodev.WeightDevice, error) {
55 func (daemon *Daemon) parseSecurityOpt(container *container.Container, hostConfig *containertypes.HostConfig) error {
56 return parseSecurityOpt(container, hostConfig)
59 func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error {
63 func getBlkioReadIOpsDevices(config *containertypes.HostConfig) ([]blkiodev.ThrottleDevice, error) {
67 func getBlkioWriteIOpsDevices(config *containertypes.HostConfig) ([]blkiodev.ThrottleDevice, error) {
71 func getBlkioReadBpsDevices(config *containertypes.HostConfig) ([]blkiodev.ThrottleDevice, error) {
75 func getBlkioWriteBpsDevices(config *containertypes.HostConfig) ([]blkiodev.ThrottleDevice, error) {
79 func (daemon *Daemon) getLayerInit() func(string) error {
83 func checkKernel() error {
87 func (daemon *Daemon) getCgroupDriver() string {
91 // adaptContainerSettings is called during container creation to modify any
92 // settings necessary in the HostConfig structure.
93 func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConfig, adjustCPUShares bool) error {
94 if hostConfig == nil {
101 func verifyContainerResources(resources *containertypes.Resources, isHyperv bool) ([]string, error) {
102 warnings := []string{}
105 // The processor resource controls are mutually exclusive on
106 // Windows Server Containers, the order of precedence is
107 // CPUCount first, then CPUShares, and CPUPercent last.
108 if resources.CPUCount > 0 {
109 if resources.CPUShares > 0 {
110 warnings = append(warnings, "Conflicting options: CPU count takes priority over CPU shares on Windows Server Containers. CPU shares discarded")
111 logrus.Warn("Conflicting options: CPU count takes priority over CPU shares on Windows Server Containers. CPU shares discarded")
112 resources.CPUShares = 0
114 if resources.CPUPercent > 0 {
115 warnings = append(warnings, "Conflicting options: CPU count takes priority over CPU percent on Windows Server Containers. CPU percent discarded")
116 logrus.Warn("Conflicting options: CPU count takes priority over CPU percent on Windows Server Containers. CPU percent discarded")
117 resources.CPUPercent = 0
119 } else if resources.CPUShares > 0 {
120 if resources.CPUPercent > 0 {
121 warnings = append(warnings, "Conflicting options: CPU shares takes priority over CPU percent on Windows Server Containers. CPU percent discarded")
122 logrus.Warn("Conflicting options: CPU shares takes priority over CPU percent on Windows Server Containers. CPU percent discarded")
123 resources.CPUPercent = 0
128 if resources.CPUShares < 0 || resources.CPUShares > windowsMaxCPUShares {
129 return warnings, fmt.Errorf("range of CPUShares is from %d to %d", windowsMinCPUShares, windowsMaxCPUShares)
131 if resources.CPUPercent < 0 || resources.CPUPercent > windowsMaxCPUPercent {
132 return warnings, fmt.Errorf("range of CPUPercent is from %d to %d", windowsMinCPUPercent, windowsMaxCPUPercent)
134 if resources.CPUCount < 0 {
135 return warnings, fmt.Errorf("invalid CPUCount: CPUCount cannot be negative")
138 if resources.NanoCPUs > 0 && resources.CPUPercent > 0 {
139 return warnings, fmt.Errorf("conflicting options: Nano CPUs and CPU Percent cannot both be set")
141 if resources.NanoCPUs > 0 && resources.CPUShares > 0 {
142 return warnings, fmt.Errorf("conflicting options: Nano CPUs and CPU Shares cannot both be set")
144 // The precision we could get is 0.01, because on Windows we have to convert to CPUPercent.
145 // We don't set the lower limit here and it is up to the underlying platform (e.g., Windows) to return an error.
146 if resources.NanoCPUs < 0 || resources.NanoCPUs > int64(sysinfo.NumCPU())*1e9 {
147 return warnings, fmt.Errorf("range of CPUs is from 0.01 to %d.00, as there are only %d CPUs available", sysinfo.NumCPU(), sysinfo.NumCPU())
150 osv := system.GetOSVersion()
151 if resources.NanoCPUs > 0 && isHyperv && osv.Build < 16175 {
152 leftoverNanoCPUs := resources.NanoCPUs % 1e9
153 if leftoverNanoCPUs != 0 && resources.NanoCPUs > 1e9 {
154 resources.NanoCPUs = ((resources.NanoCPUs + 1e9/2) / 1e9) * 1e9
155 warningString := fmt.Sprintf("Your current OS version does not support Hyper-V containers with NanoCPUs greater than 1000000000 but not divisible by 1000000000. NanoCPUs rounded to %d", resources.NanoCPUs)
156 warnings = append(warnings, warningString)
157 logrus.Warn(warningString)
161 if len(resources.BlkioDeviceReadBps) > 0 {
162 return warnings, fmt.Errorf("invalid option: Windows does not support BlkioDeviceReadBps")
164 if len(resources.BlkioDeviceReadIOps) > 0 {
165 return warnings, fmt.Errorf("invalid option: Windows does not support BlkioDeviceReadIOps")
167 if len(resources.BlkioDeviceWriteBps) > 0 {
168 return warnings, fmt.Errorf("invalid option: Windows does not support BlkioDeviceWriteBps")
170 if len(resources.BlkioDeviceWriteIOps) > 0 {
171 return warnings, fmt.Errorf("invalid option: Windows does not support BlkioDeviceWriteIOps")
173 if resources.BlkioWeight > 0 {
174 return warnings, fmt.Errorf("invalid option: Windows does not support BlkioWeight")
176 if len(resources.BlkioWeightDevice) > 0 {
177 return warnings, fmt.Errorf("invalid option: Windows does not support BlkioWeightDevice")
179 if resources.CgroupParent != "" {
180 return warnings, fmt.Errorf("invalid option: Windows does not support CgroupParent")
182 if resources.CPUPeriod != 0 {
183 return warnings, fmt.Errorf("invalid option: Windows does not support CPUPeriod")
185 if resources.CpusetCpus != "" {
186 return warnings, fmt.Errorf("invalid option: Windows does not support CpusetCpus")
188 if resources.CpusetMems != "" {
189 return warnings, fmt.Errorf("invalid option: Windows does not support CpusetMems")
191 if resources.KernelMemory != 0 {
192 return warnings, fmt.Errorf("invalid option: Windows does not support KernelMemory")
194 if resources.MemoryReservation != 0 {
195 return warnings, fmt.Errorf("invalid option: Windows does not support MemoryReservation")
197 if resources.MemorySwap != 0 {
198 return warnings, fmt.Errorf("invalid option: Windows does not support MemorySwap")
200 if resources.MemorySwappiness != nil && *resources.MemorySwappiness != -1 {
201 return warnings, fmt.Errorf("invalid option: Windows does not support MemorySwappiness")
203 if resources.OomKillDisable != nil && *resources.OomKillDisable {
204 return warnings, fmt.Errorf("invalid option: Windows does not support OomKillDisable")
206 if resources.PidsLimit != 0 {
207 return warnings, fmt.Errorf("invalid option: Windows does not support PidsLimit")
209 if len(resources.Ulimits) != 0 {
210 return warnings, fmt.Errorf("invalid option: Windows does not support Ulimits")
215 // verifyPlatformContainerSettings performs platform-specific validation of the
216 // hostconfig and config structures.
217 func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) {
218 warnings := []string{}
220 hyperv := daemon.runAsHyperVContainer(hostConfig)
221 if !hyperv && system.IsWindowsClient() && !system.IsIoTCore() {
222 // @engine maintainers. This block should not be removed. It partially enforces licensing
223 // restrictions on Windows. Ping @jhowardmsft if there are concerns or PRs to change this.
224 return warnings, fmt.Errorf("Windows client operating systems only support Hyper-V containers")
227 w, err := verifyContainerResources(&hostConfig.Resources, hyperv)
228 warnings = append(warnings, w...)
232 // reloadPlatform updates configuration with platform specific options
233 // and updates the passed attributes
234 func (daemon *Daemon) reloadPlatform(config *config.Config, attributes map[string]string) {
237 // verifyDaemonSettings performs validation of daemon config struct
238 func verifyDaemonSettings(config *config.Config) error {
242 // checkSystem validates platform-specific requirements
243 func checkSystem() error {
244 // Validate the OS version. Note that docker.exe must be manifested for this
245 // call to return the correct version.
246 osv := system.GetOSVersion()
247 if osv.MajorVersion < 10 {
248 return fmt.Errorf("This version of Windows does not support the docker daemon")
250 if osv.Build < 14393 {
251 return fmt.Errorf("The docker daemon requires build 14393 or later of Windows Server 2016 or Windows 10")
254 vmcompute := windows.NewLazySystemDLL("vmcompute.dll")
255 if vmcompute.Load() != nil {
256 return fmt.Errorf("Failed to load vmcompute.dll. Ensure that the Containers role is installed.")
262 // configureKernelSecuritySupport configures and validate security support for the kernel
263 func configureKernelSecuritySupport(config *config.Config, driverNames []string) error {
267 // configureMaxThreads sets the Go runtime max threads threshold
268 func configureMaxThreads(config *config.Config) error {
272 func (daemon *Daemon) initNetworkController(config *config.Config, activeSandboxes map[string]interface{}) (libnetwork.NetworkController, error) {
273 netOptions, err := daemon.networkOptions(config, nil, nil)
277 controller, err := libnetwork.New(netOptions...)
279 return nil, fmt.Errorf("error obtaining controller instance: %v", err)
282 hnsresponse, err := hcsshim.HNSListNetworkRequest("GET", "", "")
287 // Remove networks not present in HNS
288 for _, v := range controller.Networks() {
289 options := v.Info().DriverOptions()
290 hnsid := options[winlibnetwork.HNSID]
293 for _, v := range hnsresponse {
301 // global networks should not be deleted by local HNS
302 if v.Info().Scope() != datastore.GlobalScope {
305 logrus.Errorf("Error occurred when removing network %v", err)
311 _, err = controller.NewNetwork("null", "none", "", libnetwork.NetworkOptionPersist(false))
316 defaultNetworkExists := false
318 if network, err := controller.NetworkByName(runconfig.DefaultDaemonNetworkMode().NetworkName()); err == nil {
319 options := network.Info().DriverOptions()
320 for _, v := range hnsresponse {
321 if options[winlibnetwork.HNSID] == v.Id {
322 defaultNetworkExists = true
328 // discover and add HNS networks to windows
329 // network that exist are removed and added again
330 for _, v := range hnsresponse {
331 if strings.ToLower(v.Type) == "private" {
332 continue // workaround for HNS reporting unsupported networks
334 var n libnetwork.Network
335 s := func(current libnetwork.Network) bool {
336 options := current.Info().DriverOptions()
337 if options[winlibnetwork.HNSID] == v.Id {
344 controller.WalkNetworks(s)
346 // global networks should not be deleted by local HNS
347 if n.Info().Scope() == datastore.GlobalScope {
351 // This will not cause network delete from HNS as the network
352 // is not yet populated in the libnetwork windows driver
356 netOption := map[string]string{
357 winlibnetwork.NetworkName: v.Name,
358 winlibnetwork.HNSID: v.Id,
361 v4Conf := []*libnetwork.IpamConf{}
362 for _, subnet := range v.Subnets {
363 ipamV4Conf := libnetwork.IpamConf{}
364 ipamV4Conf.PreferredPool = subnet.AddressPrefix
365 ipamV4Conf.Gateway = subnet.GatewayAddress
366 v4Conf = append(v4Conf, &ipamV4Conf)
371 // If there is no nat network create one from the first NAT network
372 // encountered if it doesn't already exist
373 if !defaultNetworkExists &&
374 runconfig.DefaultDaemonNetworkMode() == containertypes.NetworkMode(strings.ToLower(v.Type)) &&
376 name = runconfig.DefaultDaemonNetworkMode().NetworkName()
377 defaultNetworkExists = true
380 v6Conf := []*libnetwork.IpamConf{}
381 _, err := controller.NewNetwork(strings.ToLower(v.Type), name, "",
382 libnetwork.NetworkOptionGeneric(options.Generic{
383 netlabel.GenericData: netOption,
385 libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil),
389 logrus.Errorf("Error occurred when creating network %v", err)
393 if !config.DisableBridge {
394 // Initialize default driver "bridge"
395 if err := initBridgeDriver(controller, config); err != nil {
400 return controller, nil
403 func initBridgeDriver(controller libnetwork.NetworkController, config *config.Config) error {
404 if _, err := controller.NetworkByName(runconfig.DefaultDaemonNetworkMode().NetworkName()); err == nil {
408 netOption := map[string]string{
409 winlibnetwork.NetworkName: runconfig.DefaultDaemonNetworkMode().NetworkName(),
412 var ipamOption libnetwork.NetworkOption
413 var subnetPrefix string
415 if config.BridgeConfig.FixedCIDR != "" {
416 subnetPrefix = config.BridgeConfig.FixedCIDR
418 // TP5 doesn't support properly detecting subnet
419 osv := system.GetOSVersion()
420 if osv.Build < 14360 {
421 subnetPrefix = defaultNetworkSpace
425 if subnetPrefix != "" {
426 ipamV4Conf := libnetwork.IpamConf{}
427 ipamV4Conf.PreferredPool = subnetPrefix
428 v4Conf := []*libnetwork.IpamConf{&ipamV4Conf}
429 v6Conf := []*libnetwork.IpamConf{}
430 ipamOption = libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil)
433 _, err := controller.NewNetwork(string(runconfig.DefaultDaemonNetworkMode()), runconfig.DefaultDaemonNetworkMode().NetworkName(), "",
434 libnetwork.NetworkOptionGeneric(options.Generic{
435 netlabel.GenericData: netOption,
441 return fmt.Errorf("Error creating default network: %v", err)
447 // registerLinks sets up links between containers and writes the
448 // configuration out for persistence. As of Windows TP4, links are not supported.
449 func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *containertypes.HostConfig) error {
453 func (daemon *Daemon) cleanupMountsByID(in string) error {
457 func (daemon *Daemon) cleanupMounts() error {
461 func setupRemappedRoot(config *config.Config) (*idtools.IDMappings, error) {
462 return &idtools.IDMappings{}, nil
465 func setupDaemonRoot(config *config.Config, rootDir string, rootIDs idtools.IDPair) error {
466 config.Root = rootDir
467 // Create the root directory if it doesn't exists
468 if err := system.MkdirAllWithACL(config.Root, 0, system.SddlAdministratorsLocalSystem); err != nil && !os.IsExist(err) {
474 // runasHyperVContainer returns true if we are going to run as a Hyper-V container
475 func (daemon *Daemon) runAsHyperVContainer(hostConfig *containertypes.HostConfig) bool {
476 if hostConfig.Isolation.IsDefault() {
477 // Container is set to use the default, so take the default from the daemon configuration
478 return daemon.defaultIsolation.IsHyperV()
481 // Container is requesting an isolation mode. Honour it.
482 return hostConfig.Isolation.IsHyperV()
486 // conditionalMountOnStart is a platform specific helper function during the
487 // container start to call mount.
488 func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error {
489 // Bail out now for Linux containers
490 if system.LCOWSupported() && container.Platform != "windows" {
494 // We do not mount if a Hyper-V container
495 if !daemon.runAsHyperVContainer(container.HostConfig) {
496 return daemon.Mount(container)
501 // conditionalUnmountOnCleanup is a platform specific helper function called
502 // during the cleanup of a container to unmount.
503 func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error {
504 // Bail out now for Linux containers
505 if system.LCOWSupported() && container.Platform != "windows" {
509 // We do not unmount if a Hyper-V container
510 if !daemon.runAsHyperVContainer(container.HostConfig) {
511 return daemon.Unmount(container)
516 func driverOptions(config *config.Config) []nwconfig.Option {
517 return []nwconfig.Option{}
520 func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) {
522 return nil, errNotRunning{c.ID}
525 // Obtain the stats from HCS via libcontainerd
526 stats, err := daemon.containerd.Stats(c.ID)
531 // Start with an empty structure
532 s := &types.StatsJSON{}
534 // Populate the CPU/processor statistics
535 s.CPUStats = types.CPUStats{
536 CPUUsage: types.CPUUsage{
537 TotalUsage: stats.Processor.TotalRuntime100ns,
538 UsageInKernelmode: stats.Processor.RuntimeKernel100ns,
539 UsageInUsermode: stats.Processor.RuntimeKernel100ns,
543 // Populate the memory statistics
544 s.MemoryStats = types.MemoryStats{
545 Commit: stats.Memory.UsageCommitBytes,
546 CommitPeak: stats.Memory.UsageCommitPeakBytes,
547 PrivateWorkingSet: stats.Memory.UsagePrivateWorkingSetBytes,
550 // Populate the storage statistics
551 s.StorageStats = types.StorageStats{
552 ReadCountNormalized: stats.Storage.ReadCountNormalized,
553 ReadSizeBytes: stats.Storage.ReadSizeBytes,
554 WriteCountNormalized: stats.Storage.WriteCountNormalized,
555 WriteSizeBytes: stats.Storage.WriteSizeBytes,
558 // Populate the network statistics
559 s.Networks = make(map[string]types.NetworkStats)
561 for _, nstats := range stats.Network {
562 s.Networks[nstats.EndpointId] = types.NetworkStats{
563 RxBytes: nstats.BytesReceived,
564 RxPackets: nstats.PacketsReceived,
565 RxDropped: nstats.DroppedPacketsIncoming,
566 TxBytes: nstats.BytesSent,
567 TxPackets: nstats.PacketsSent,
568 TxDropped: nstats.DroppedPacketsOutgoing,
573 s.Stats.Read = stats.Timestamp
574 s.Stats.NumProcs = platform.NumProcs()
579 // setDefaultIsolation determine the default isolation mode for the
580 // daemon to run in. This is only applicable on Windows
581 func (daemon *Daemon) setDefaultIsolation() error {
582 daemon.defaultIsolation = containertypes.Isolation("process")
583 // On client SKUs, default to Hyper-V. Note that IoT reports as a client SKU
584 // but it should not be treated as such.
585 if system.IsWindowsClient() && !system.IsIoTCore() {
586 daemon.defaultIsolation = containertypes.Isolation("hyperv")
588 for _, option := range daemon.configStore.ExecOptions {
589 key, val, err := parsers.ParseKeyValueOpt(option)
593 key = strings.ToLower(key)
597 if !containertypes.Isolation(val).IsValid() {
598 return fmt.Errorf("Invalid exec-opt value for 'isolation':'%s'", val)
600 if containertypes.Isolation(val).IsHyperV() {
601 daemon.defaultIsolation = containertypes.Isolation("hyperv")
603 if containertypes.Isolation(val).IsProcess() {
604 if system.IsWindowsClient() && !system.IsIoTCore() {
605 // @engine maintainers. This block should not be removed. It partially enforces licensing
606 // restrictions on Windows. Ping @jhowardmsft if there are concerns or PRs to change this.
607 return fmt.Errorf("Windows client operating systems only support Hyper-V containers")
609 daemon.defaultIsolation = containertypes.Isolation("process")
612 return fmt.Errorf("Unrecognised exec-opt '%s'\n", key)
616 logrus.Infof("Windows default isolation mode: %s", daemon.defaultIsolation)
620 func rootFSToAPIType(rootfs *image.RootFS) types.RootFS {
622 for _, l := range rootfs.DiffIDs {
623 layers = append(layers, l.String())
631 func setupDaemonProcess(config *config.Config) error {
635 // verifyVolumesInfo is a no-op on windows.
636 // This is called during daemon initialization to migrate volumes from pre-1.7.
637 // volumes were not supported on windows pre-1.7
638 func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error {
642 func (daemon *Daemon) setupSeccompProfile() error {
646 func getRealPath(path string) (string, error) {
647 if system.IsIoTCore() {
648 // Due to https://github.com/golang/go/issues/20506, path expansion
649 // does not work correctly on the default IoT Core configuration.
650 // TODO @darrenstahlmsft remove this once golang/go/20506 is fixed
653 return fileutils.ReadSymlinkedDirectory(path)