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