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  }