github.com/kunnos/engine@v1.13.1/daemon/daemon_windows.go (about) 1 package daemon 2 3 import ( 4 "fmt" 5 "os" 6 "strings" 7 8 "github.com/Microsoft/hcsshim" 9 "github.com/sirupsen/logrus" 10 "github.com/docker/docker/api/types" 11 containertypes "github.com/docker/docker/api/types/container" 12 "github.com/docker/docker/container" 13 "github.com/docker/docker/image" 14 "github.com/docker/docker/pkg/idtools" 15 "github.com/docker/docker/pkg/parsers" 16 "github.com/docker/docker/pkg/platform" 17 "github.com/docker/docker/pkg/sysinfo" 18 "github.com/docker/docker/pkg/system" 19 "github.com/docker/docker/runconfig" 20 "github.com/docker/libnetwork" 21 nwconfig "github.com/docker/libnetwork/config" 22 "github.com/docker/libnetwork/datastore" 23 winlibnetwork "github.com/docker/libnetwork/drivers/windows" 24 "github.com/docker/libnetwork/netlabel" 25 "github.com/docker/libnetwork/options" 26 blkiodev "github.com/opencontainers/runc/libcontainer/configs" 27 "golang.org/x/sys/windows" 28 ) 29 30 const ( 31 defaultNetworkSpace = "172.16.0.0/12" 32 platformSupported = true 33 windowsMinCPUShares = 1 34 windowsMaxCPUShares = 10000 35 windowsMinCPUPercent = 1 36 windowsMaxCPUPercent = 100 37 windowsMinCPUCount = 1 38 ) 39 40 func getBlkioWeightDevices(config *containertypes.HostConfig) ([]blkiodev.WeightDevice, error) { 41 return nil, nil 42 } 43 44 func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error { 45 return nil 46 } 47 48 func getBlkioReadIOpsDevices(config *containertypes.HostConfig) ([]blkiodev.ThrottleDevice, error) { 49 return nil, nil 50 } 51 52 func getBlkioWriteIOpsDevices(config *containertypes.HostConfig) ([]blkiodev.ThrottleDevice, error) { 53 return nil, nil 54 } 55 56 func getBlkioReadBpsDevices(config *containertypes.HostConfig) ([]blkiodev.ThrottleDevice, error) { 57 return nil, nil 58 } 59 60 func getBlkioWriteBpsDevices(config *containertypes.HostConfig) ([]blkiodev.ThrottleDevice, error) { 61 return nil, nil 62 } 63 64 func (daemon *Daemon) getLayerInit() func(string) error { 65 return nil 66 } 67 68 func checkKernel() error { 69 return nil 70 } 71 72 func (daemon *Daemon) getCgroupDriver() string { 73 return "" 74 } 75 76 // adaptContainerSettings is called during container creation to modify any 77 // settings necessary in the HostConfig structure. 78 func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConfig, adjustCPUShares bool) error { 79 if hostConfig == nil { 80 return nil 81 } 82 83 return nil 84 } 85 86 func verifyContainerResources(resources *containertypes.Resources, isHyperv bool) ([]string, error) { 87 warnings := []string{} 88 89 if !isHyperv { 90 // The processor resource controls are mutually exclusive on 91 // Windows Server Containers, the order of precedence is 92 // CPUCount first, then CPUShares, and CPUPercent last. 93 if resources.CPUCount > 0 { 94 if resources.CPUShares > 0 { 95 warnings = append(warnings, "Conflicting options: CPU count takes priority over CPU shares on Windows Server Containers. CPU shares discarded") 96 logrus.Warn("Conflicting options: CPU count takes priority over CPU shares on Windows Server Containers. CPU shares discarded") 97 resources.CPUShares = 0 98 } 99 if resources.CPUPercent > 0 { 100 warnings = append(warnings, "Conflicting options: CPU count takes priority over CPU percent on Windows Server Containers. CPU percent discarded") 101 logrus.Warn("Conflicting options: CPU count takes priority over CPU percent on Windows Server Containers. CPU percent discarded") 102 resources.CPUPercent = 0 103 } 104 } else if resources.CPUShares > 0 { 105 if resources.CPUPercent > 0 { 106 warnings = append(warnings, "Conflicting options: CPU shares takes priority over CPU percent on Windows Server Containers. CPU percent discarded") 107 logrus.Warn("Conflicting options: CPU shares takes priority over CPU percent on Windows Server Containers. CPU percent discarded") 108 resources.CPUPercent = 0 109 } 110 } 111 } 112 113 if resources.CPUShares < 0 || resources.CPUShares > windowsMaxCPUShares { 114 return warnings, fmt.Errorf("range of CPUShares is from %d to %d", windowsMinCPUShares, windowsMaxCPUShares) 115 } 116 if resources.CPUPercent < 0 || resources.CPUPercent > windowsMaxCPUPercent { 117 return warnings, fmt.Errorf("range of CPUPercent is from %d to %d", windowsMinCPUPercent, windowsMaxCPUPercent) 118 } 119 if resources.CPUCount < 0 { 120 return warnings, fmt.Errorf("invalid CPUCount: CPUCount cannot be negative") 121 } 122 123 if resources.NanoCPUs > 0 && resources.CPUPercent > 0 { 124 return warnings, fmt.Errorf("conflicting options: Nano CPUs and CPU Percent cannot both be set") 125 } 126 if resources.NanoCPUs > 0 && resources.CPUShares > 0 { 127 return warnings, fmt.Errorf("conflicting options: Nano CPUs and CPU Shares cannot both be set") 128 } 129 // The precision we could get is 0.01, because on Windows we have to convert to CPUPercent. 130 // We don't set the lower limit here and it is up to the underlying platform (e.g., Windows) to return an error. 131 if resources.NanoCPUs < 0 || resources.NanoCPUs > int64(sysinfo.NumCPU())*1e9 { 132 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()) 133 } 134 135 if len(resources.BlkioDeviceReadBps) > 0 { 136 return warnings, fmt.Errorf("invalid option: Windows does not support BlkioDeviceReadBps") 137 } 138 if len(resources.BlkioDeviceReadIOps) > 0 { 139 return warnings, fmt.Errorf("invalid option: Windows does not support BlkioDeviceReadIOps") 140 } 141 if len(resources.BlkioDeviceWriteBps) > 0 { 142 return warnings, fmt.Errorf("invalid option: Windows does not support BlkioDeviceWriteBps") 143 } 144 if len(resources.BlkioDeviceWriteIOps) > 0 { 145 return warnings, fmt.Errorf("invalid option: Windows does not support BlkioDeviceWriteIOps") 146 } 147 if resources.BlkioWeight > 0 { 148 return warnings, fmt.Errorf("invalid option: Windows does not support BlkioWeight") 149 } 150 if len(resources.BlkioWeightDevice) > 0 { 151 return warnings, fmt.Errorf("invalid option: Windows does not support BlkioWeightDevice") 152 } 153 if resources.CgroupParent != "" { 154 return warnings, fmt.Errorf("invalid option: Windows does not support CgroupParent") 155 } 156 if resources.CPUPeriod != 0 { 157 return warnings, fmt.Errorf("invalid option: Windows does not support CPUPeriod") 158 } 159 if resources.CpusetCpus != "" { 160 return warnings, fmt.Errorf("invalid option: Windows does not support CpusetCpus") 161 } 162 if resources.CpusetMems != "" { 163 return warnings, fmt.Errorf("invalid option: Windows does not support CpusetMems") 164 } 165 if resources.KernelMemory != 0 { 166 return warnings, fmt.Errorf("invalid option: Windows does not support KernelMemory") 167 } 168 if resources.MemoryReservation != 0 { 169 return warnings, fmt.Errorf("invalid option: Windows does not support MemoryReservation") 170 } 171 if resources.MemorySwap != 0 { 172 return warnings, fmt.Errorf("invalid option: Windows does not support MemorySwap") 173 } 174 if resources.MemorySwappiness != nil && *resources.MemorySwappiness != -1 { 175 return warnings, fmt.Errorf("invalid option: Windows does not support MemorySwappiness") 176 } 177 if resources.OomKillDisable != nil && *resources.OomKillDisable { 178 return warnings, fmt.Errorf("invalid option: Windows does not support OomKillDisable") 179 } 180 if resources.PidsLimit != 0 { 181 return warnings, fmt.Errorf("invalid option: Windows does not support PidsLimit") 182 } 183 if len(resources.Ulimits) != 0 { 184 return warnings, fmt.Errorf("invalid option: Windows does not support Ulimits") 185 } 186 return warnings, nil 187 } 188 189 // verifyPlatformContainerSettings performs platform-specific validation of the 190 // hostconfig and config structures. 191 func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) { 192 warnings := []string{} 193 194 hyperv := daemon.runAsHyperVContainer(hostConfig) 195 if !hyperv && system.IsWindowsClient() { 196 // @engine maintainers. This block should not be removed. It partially enforces licensing 197 // restrictions on Windows. Ping @jhowardmsft if there are concerns or PRs to change this. 198 return warnings, fmt.Errorf("Windows client operating systems only support Hyper-V containers") 199 } 200 201 w, err := verifyContainerResources(&hostConfig.Resources, hyperv) 202 warnings = append(warnings, w...) 203 if err != nil { 204 return warnings, err 205 } 206 return warnings, nil 207 } 208 209 // platformReload update configuration with platform specific options 210 func (daemon *Daemon) platformReload(config *Config) map[string]string { 211 return map[string]string{} 212 } 213 214 // verifyDaemonSettings performs validation of daemon config struct 215 func verifyDaemonSettings(config *Config) error { 216 return nil 217 } 218 219 // checkSystem validates platform-specific requirements 220 func checkSystem() error { 221 // Validate the OS version. Note that docker.exe must be manifested for this 222 // call to return the correct version. 223 osv := system.GetOSVersion() 224 if osv.MajorVersion < 10 { 225 return fmt.Errorf("This version of Windows does not support the docker daemon") 226 } 227 if osv.Build < 14393 { 228 return fmt.Errorf("The docker daemon requires build 14393 or later of Windows Server 2016 or Windows 10") 229 } 230 231 vmcompute := windows.NewLazySystemDLL("vmcompute.dll") 232 if vmcompute.Load() != nil { 233 return fmt.Errorf("Failed to load vmcompute.dll. Ensure that the Containers role is installed.") 234 } 235 return nil 236 } 237 238 // configureKernelSecuritySupport configures and validate security support for the kernel 239 func configureKernelSecuritySupport(config *Config, driverName string) error { 240 return nil 241 } 242 243 // configureMaxThreads sets the Go runtime max threads threshold 244 func configureMaxThreads(config *Config) error { 245 return nil 246 } 247 248 func (daemon *Daemon) initNetworkController(config *Config, activeSandboxes map[string]interface{}) (libnetwork.NetworkController, error) { 249 netOptions, err := daemon.networkOptions(config, nil, nil) 250 if err != nil { 251 return nil, err 252 } 253 controller, err := libnetwork.New(netOptions...) 254 if err != nil { 255 return nil, fmt.Errorf("error obtaining controller instance: %v", err) 256 } 257 258 hnsresponse, err := hcsshim.HNSListNetworkRequest("GET", "", "") 259 if err != nil { 260 return nil, err 261 } 262 263 // Remove networks not present in HNS 264 for _, v := range controller.Networks() { 265 options := v.Info().DriverOptions() 266 hnsid := options[winlibnetwork.HNSID] 267 found := false 268 269 for _, v := range hnsresponse { 270 if v.Id == hnsid { 271 found = true 272 break 273 } 274 } 275 276 if !found { 277 // global networks should not be deleted by local HNS 278 if v.Info().Scope() != datastore.GlobalScope { 279 err = v.Delete() 280 if err != nil { 281 logrus.Errorf("Error occurred when removing network %v", err) 282 } 283 } 284 } 285 } 286 287 _, err = controller.NewNetwork("null", "none", "", libnetwork.NetworkOptionPersist(false)) 288 if err != nil { 289 return nil, err 290 } 291 292 defaultNetworkExists := false 293 294 if network, err := controller.NetworkByName(runconfig.DefaultDaemonNetworkMode().NetworkName()); err == nil { 295 options := network.Info().DriverOptions() 296 for _, v := range hnsresponse { 297 if options[winlibnetwork.HNSID] == v.Id { 298 defaultNetworkExists = true 299 break 300 } 301 } 302 } 303 304 // discover and add HNS networks to windows 305 // network that exist are removed and added again 306 for _, v := range hnsresponse { 307 var n libnetwork.Network 308 s := func(current libnetwork.Network) bool { 309 options := current.Info().DriverOptions() 310 if options[winlibnetwork.HNSID] == v.Id { 311 n = current 312 return true 313 } 314 return false 315 } 316 317 controller.WalkNetworks(s) 318 if n != nil { 319 // global networks should not be deleted by local HNS 320 if n.Info().Scope() == datastore.GlobalScope { 321 continue 322 } 323 v.Name = n.Name() 324 // This will not cause network delete from HNS as the network 325 // is not yet populated in the libnetwork windows driver 326 n.Delete() 327 } 328 329 netOption := map[string]string{ 330 winlibnetwork.NetworkName: v.Name, 331 winlibnetwork.HNSID: v.Id, 332 } 333 334 v4Conf := []*libnetwork.IpamConf{} 335 for _, subnet := range v.Subnets { 336 ipamV4Conf := libnetwork.IpamConf{} 337 ipamV4Conf.PreferredPool = subnet.AddressPrefix 338 ipamV4Conf.Gateway = subnet.GatewayAddress 339 v4Conf = append(v4Conf, &ipamV4Conf) 340 } 341 342 name := v.Name 343 344 // If there is no nat network create one from the first NAT network 345 // encountered 346 if !defaultNetworkExists && runconfig.DefaultDaemonNetworkMode() == containertypes.NetworkMode(strings.ToLower(v.Type)) { 347 name = runconfig.DefaultDaemonNetworkMode().NetworkName() 348 defaultNetworkExists = true 349 } 350 351 v6Conf := []*libnetwork.IpamConf{} 352 _, err := controller.NewNetwork(strings.ToLower(v.Type), name, "", 353 libnetwork.NetworkOptionGeneric(options.Generic{ 354 netlabel.GenericData: netOption, 355 }), 356 libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil), 357 ) 358 359 if err != nil { 360 logrus.Errorf("Error occurred when creating network %v", err) 361 } 362 } 363 364 if !config.DisableBridge { 365 // Initialize default driver "bridge" 366 if err := initBridgeDriver(controller, config); err != nil { 367 return nil, err 368 } 369 } 370 371 return controller, nil 372 } 373 374 func initBridgeDriver(controller libnetwork.NetworkController, config *Config) error { 375 if _, err := controller.NetworkByName(runconfig.DefaultDaemonNetworkMode().NetworkName()); err == nil { 376 return nil 377 } 378 379 netOption := map[string]string{ 380 winlibnetwork.NetworkName: runconfig.DefaultDaemonNetworkMode().NetworkName(), 381 } 382 383 var ipamOption libnetwork.NetworkOption 384 var subnetPrefix string 385 386 if config.bridgeConfig.FixedCIDR != "" { 387 subnetPrefix = config.bridgeConfig.FixedCIDR 388 } else { 389 // TP5 doesn't support properly detecting subnet 390 osv := system.GetOSVersion() 391 if osv.Build < 14360 { 392 subnetPrefix = defaultNetworkSpace 393 } 394 } 395 396 if subnetPrefix != "" { 397 ipamV4Conf := libnetwork.IpamConf{} 398 ipamV4Conf.PreferredPool = subnetPrefix 399 v4Conf := []*libnetwork.IpamConf{&ipamV4Conf} 400 v6Conf := []*libnetwork.IpamConf{} 401 ipamOption = libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil) 402 } 403 404 _, err := controller.NewNetwork(string(runconfig.DefaultDaemonNetworkMode()), runconfig.DefaultDaemonNetworkMode().NetworkName(), "", 405 libnetwork.NetworkOptionGeneric(options.Generic{ 406 netlabel.GenericData: netOption, 407 }), 408 ipamOption, 409 ) 410 411 if err != nil { 412 return fmt.Errorf("Error creating default network: %v", err) 413 } 414 415 return nil 416 } 417 418 // registerLinks sets up links between containers and writes the 419 // configuration out for persistence. As of Windows TP4, links are not supported. 420 func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *containertypes.HostConfig) error { 421 return nil 422 } 423 424 func (daemon *Daemon) cleanupMountsByID(in string) error { 425 return nil 426 } 427 428 func (daemon *Daemon) cleanupMounts() error { 429 return nil 430 } 431 432 func setupRemappedRoot(config *Config) ([]idtools.IDMap, []idtools.IDMap, error) { 433 return nil, nil, nil 434 } 435 436 func setupDaemonRoot(config *Config, rootDir string, rootUID, rootGID int) error { 437 config.Root = rootDir 438 // Create the root directory if it doesn't exists 439 if err := system.MkdirAllWithACL(config.Root, 0); err != nil && !os.IsExist(err) { 440 return err 441 } 442 return nil 443 } 444 445 // runasHyperVContainer returns true if we are going to run as a Hyper-V container 446 func (daemon *Daemon) runAsHyperVContainer(hostConfig *containertypes.HostConfig) bool { 447 if hostConfig.Isolation.IsDefault() { 448 // Container is set to use the default, so take the default from the daemon configuration 449 return daemon.defaultIsolation.IsHyperV() 450 } 451 452 // Container is requesting an isolation mode. Honour it. 453 return hostConfig.Isolation.IsHyperV() 454 455 } 456 457 // conditionalMountOnStart is a platform specific helper function during the 458 // container start to call mount. 459 func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error { 460 // We do not mount if a Hyper-V container 461 if !daemon.runAsHyperVContainer(container.HostConfig) { 462 return daemon.Mount(container) 463 } 464 return nil 465 } 466 467 // conditionalUnmountOnCleanup is a platform specific helper function called 468 // during the cleanup of a container to unmount. 469 func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error { 470 // We do not unmount if a Hyper-V container 471 if !daemon.runAsHyperVContainer(container.HostConfig) { 472 return daemon.Unmount(container) 473 } 474 return nil 475 } 476 477 func driverOptions(config *Config) []nwconfig.Option { 478 return []nwconfig.Option{} 479 } 480 481 func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) { 482 if !c.IsRunning() { 483 return nil, errNotRunning{c.ID} 484 } 485 486 // Obtain the stats from HCS via libcontainerd 487 stats, err := daemon.containerd.Stats(c.ID) 488 if err != nil { 489 return nil, err 490 } 491 492 // Start with an empty structure 493 s := &types.StatsJSON{} 494 495 // Populate the CPU/processor statistics 496 s.CPUStats = types.CPUStats{ 497 CPUUsage: types.CPUUsage{ 498 TotalUsage: stats.Processor.TotalRuntime100ns, 499 UsageInKernelmode: stats.Processor.RuntimeKernel100ns, 500 UsageInUsermode: stats.Processor.RuntimeKernel100ns, 501 }, 502 } 503 504 // Populate the memory statistics 505 s.MemoryStats = types.MemoryStats{ 506 Commit: stats.Memory.UsageCommitBytes, 507 CommitPeak: stats.Memory.UsageCommitPeakBytes, 508 PrivateWorkingSet: stats.Memory.UsagePrivateWorkingSetBytes, 509 } 510 511 // Populate the storage statistics 512 s.StorageStats = types.StorageStats{ 513 ReadCountNormalized: stats.Storage.ReadCountNormalized, 514 ReadSizeBytes: stats.Storage.ReadSizeBytes, 515 WriteCountNormalized: stats.Storage.WriteCountNormalized, 516 WriteSizeBytes: stats.Storage.WriteSizeBytes, 517 } 518 519 // Populate the network statistics 520 s.Networks = make(map[string]types.NetworkStats) 521 522 for _, nstats := range stats.Network { 523 s.Networks[nstats.EndpointId] = types.NetworkStats{ 524 RxBytes: nstats.BytesReceived, 525 RxPackets: nstats.PacketsReceived, 526 RxDropped: nstats.DroppedPacketsIncoming, 527 TxBytes: nstats.BytesSent, 528 TxPackets: nstats.PacketsSent, 529 TxDropped: nstats.DroppedPacketsOutgoing, 530 } 531 } 532 533 // Set the timestamp 534 s.Stats.Read = stats.Timestamp 535 s.Stats.NumProcs = platform.NumProcs() 536 537 return s, nil 538 } 539 540 // setDefaultIsolation determine the default isolation mode for the 541 // daemon to run in. This is only applicable on Windows 542 func (daemon *Daemon) setDefaultIsolation() error { 543 daemon.defaultIsolation = containertypes.Isolation("process") 544 // On client SKUs, default to Hyper-V 545 if system.IsWindowsClient() { 546 daemon.defaultIsolation = containertypes.Isolation("hyperv") 547 } 548 for _, option := range daemon.configStore.ExecOptions { 549 key, val, err := parsers.ParseKeyValueOpt(option) 550 if err != nil { 551 return err 552 } 553 key = strings.ToLower(key) 554 switch key { 555 556 case "isolation": 557 if !containertypes.Isolation(val).IsValid() { 558 return fmt.Errorf("Invalid exec-opt value for 'isolation':'%s'", val) 559 } 560 if containertypes.Isolation(val).IsHyperV() { 561 daemon.defaultIsolation = containertypes.Isolation("hyperv") 562 } 563 if containertypes.Isolation(val).IsProcess() { 564 if system.IsWindowsClient() { 565 // @engine maintainers. This block should not be removed. It partially enforces licensing 566 // restrictions on Windows. Ping @jhowardmsft if there are concerns or PRs to change this. 567 return fmt.Errorf("Windows client operating systems only support Hyper-V containers") 568 } 569 daemon.defaultIsolation = containertypes.Isolation("process") 570 } 571 default: 572 return fmt.Errorf("Unrecognised exec-opt '%s'\n", key) 573 } 574 } 575 576 logrus.Infof("Windows default isolation mode: %s", daemon.defaultIsolation) 577 return nil 578 } 579 580 func rootFSToAPIType(rootfs *image.RootFS) types.RootFS { 581 var layers []string 582 for _, l := range rootfs.DiffIDs { 583 layers = append(layers, l.String()) 584 } 585 return types.RootFS{ 586 Type: rootfs.Type, 587 Layers: layers, 588 } 589 } 590 591 func setupDaemonProcess(config *Config) error { 592 return nil 593 } 594 595 // verifyVolumesInfo is a no-op on windows. 596 // This is called during daemon initialization to migrate volumes from pre-1.7. 597 // volumes were not supported on windows pre-1.7 598 func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error { 599 return nil 600 } 601 602 func (daemon *Daemon) setupSeccompProfile() error { 603 return nil 604 }