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