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