github.com/ph/moby@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  }