Browse code

Windows: OCI HVRuntime and LayerPaths to options

Signed-off-by: John Howard <jhoward@microsoft.com>

John Howard authored on 2016/09/20 06:47:48
Showing 6 changed files
... ...
@@ -1,16 +1,10 @@
1 1
 package daemon
2 2
 
3 3
 import (
4
-	"errors"
5
-	"fmt"
6
-	"os"
7
-	"path/filepath"
8 4
 	"syscall"
9 5
 
10 6
 	containertypes "github.com/docker/docker/api/types/container"
11 7
 	"github.com/docker/docker/container"
12
-	"github.com/docker/docker/image"
13
-	"github.com/docker/docker/layer"
14 8
 	"github.com/docker/docker/libcontainerd"
15 9
 	"github.com/docker/docker/libcontainerd/windowsoci"
16 10
 	"github.com/docker/docker/oci"
... ...
@@ -30,11 +24,6 @@ func (daemon *Daemon) createSpec(c *container.Container) (*libcontainerd.Spec, e
30 30
 		return nil, err
31 31
 	}
32 32
 
33
-	img, err := daemon.imageStore.Get(c.ImageID)
34
-	if err != nil {
35
-		return nil, fmt.Errorf("Failed to graph.Get on ImageID %s - %s", c.ImageID, err)
36
-	}
37
-
38 33
 	// In base spec
39 34
 	s.Hostname = c.FullHostname()
40 35
 
... ...
@@ -80,60 +69,6 @@ func (daemon *Daemon) createSpec(c *container.Container) (*libcontainerd.Spec, e
80 80
 	s.Root.Path = c.BaseFS
81 81
 	s.Root.Readonly = c.HostConfig.ReadonlyRootfs
82 82
 
83
-	// s.Windows.LayerFolder.
84
-	m, err := c.RWLayer.Metadata()
85
-	if err != nil {
86
-		return nil, fmt.Errorf("Failed to get layer metadata - %s", err)
87
-	}
88
-	s.Windows.LayerFolder = m["dir"]
89
-
90
-	// s.Windows.LayerPaths
91
-	var layerPaths []string
92
-	if img.RootFS != nil && img.RootFS.Type == image.TypeLayers {
93
-		// Get the layer path for each layer.
94
-		max := len(img.RootFS.DiffIDs)
95
-		for i := 1; i <= max; i++ {
96
-			img.RootFS.DiffIDs = img.RootFS.DiffIDs[:i]
97
-			path, err := layer.GetLayerPath(daemon.layerStore, img.RootFS.ChainID())
98
-			if err != nil {
99
-				return nil, fmt.Errorf("Failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.layerStore, img.RootFS.ChainID(), err)
100
-			}
101
-			// Reverse order, expecting parent most first
102
-			layerPaths = append([]string{path}, layerPaths...)
103
-		}
104
-	}
105
-	s.Windows.LayerPaths = layerPaths
106
-
107
-	// Are we going to run as a Hyper-V container?
108
-	hv := false
109
-	if c.HostConfig.Isolation.IsDefault() {
110
-		// Container is set to use the default, so take the default from the daemon configuration
111
-		hv = daemon.defaultIsolation.IsHyperV()
112
-	} else {
113
-		// Container is requesting an isolation mode. Honour it.
114
-		hv = c.HostConfig.Isolation.IsHyperV()
115
-	}
116
-	if hv {
117
-		hvr := &windowsoci.WindowsHvRuntime{}
118
-		if img.RootFS != nil && img.RootFS.Type == image.TypeLayers {
119
-			// For TP5, the utility VM is part of the base layer.
120
-			// TODO-jstarks: Add support for separate utility VM images
121
-			// once it is decided how they can be stored.
122
-			uvmpath := filepath.Join(layerPaths[len(layerPaths)-1], "UtilityVM")
123
-			_, err = os.Stat(uvmpath)
124
-			if err != nil {
125
-				if os.IsNotExist(err) {
126
-					err = errors.New("container image does not contain a utility VM")
127
-				}
128
-				return nil, err
129
-			}
130
-
131
-			hvr.ImagePath = uvmpath
132
-		}
133
-
134
-		s.Windows.HvRuntime = hvr
135
-	}
136
-
137 83
 	// In s.Windows.Networking
138 84
 	// Connect all the libnetwork allocated networks to the container
139 85
 	var epList []string
... ...
@@ -1,12 +1,60 @@
1 1
 package daemon
2 2
 
3 3
 import (
4
+	"fmt"
5
+	"path/filepath"
6
+
4 7
 	"github.com/docker/docker/container"
8
+	"github.com/docker/docker/layer"
5 9
 	"github.com/docker/docker/libcontainerd"
6 10
 )
7 11
 
8 12
 func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Container) (*[]libcontainerd.CreateOption, error) {
9 13
 	createOptions := []libcontainerd.CreateOption{}
14
+
15
+	// Are we going to run as a Hyper-V container?
16
+	hvOpts := &libcontainerd.HyperVIsolationOption{}
17
+	if container.HostConfig.Isolation.IsDefault() {
18
+		// Container is set to use the default, so take the default from the daemon configuration
19
+		hvOpts.IsHyperV = daemon.defaultIsolation.IsHyperV()
20
+	} else {
21
+		// Container is requesting an isolation mode. Honour it.
22
+		hvOpts.IsHyperV = container.HostConfig.Isolation.IsHyperV()
23
+	}
24
+
25
+	// Generate the layer folder of the layer options
26
+	layerOpts := &libcontainerd.LayerOption{}
27
+	m, err := container.RWLayer.Metadata()
28
+	if err != nil {
29
+		return nil, fmt.Errorf("failed to get layer metadata - %s", err)
30
+	}
31
+	if hvOpts.IsHyperV {
32
+		hvOpts.SandboxPath = filepath.Dir(m["dir"])
33
+	} else {
34
+		layerOpts.LayerFolderPath = m["dir"]
35
+	}
36
+
37
+	// Generate the layer paths of the layer options
38
+	img, err := daemon.imageStore.Get(container.ImageID)
39
+	if err != nil {
40
+		return nil, fmt.Errorf("failed to graph.Get on ImageID %s - %s", container.ImageID, err)
41
+	}
42
+	// Get the layer path for each layer.
43
+	max := len(img.RootFS.DiffIDs)
44
+	for i := 1; i <= max; i++ {
45
+		img.RootFS.DiffIDs = img.RootFS.DiffIDs[:i]
46
+		layerPath, err := layer.GetLayerPath(daemon.layerStore, img.RootFS.ChainID())
47
+		if err != nil {
48
+			return nil, fmt.Errorf("failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.layerStore, img.RootFS.ChainID(), err)
49
+		}
50
+		// Reverse order, expecting parent most first
51
+		layerOpts.LayerPaths = append([]string{layerPath}, layerOpts.LayerPaths...)
52
+	}
53
+
54
+	// Now build the full set of options
10 55
 	createOptions = append(createOptions, &libcontainerd.FlushOption{IgnoreFlushesDuringBoot: !container.HasBeenStartedBefore})
56
+	createOptions = append(createOptions, hvOpts)
57
+	createOptions = append(createOptions, layerOpts)
58
+
11 59
 	return &createOptions, nil
12 60
 }
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"errors"
5 5
 	"fmt"
6 6
 	"io"
7
+	"os"
7 8
 	"path/filepath"
8 9
 	"strings"
9 10
 	"syscall"
... ...
@@ -36,20 +37,73 @@ const (
36 36
 const defaultOwner = "docker"
37 37
 
38 38
 // Create is the entrypoint to create a container from a spec, and if successfully
39
-// created, start it too.
39
+// created, start it too. Table below shows the fields required for HCS JSON calling parameters,
40
+// where if not populated, is omitted.
41
+// +-----------------+--------------------------------------------+--------------------------------------------+
42
+// |                 | Isolation=Process                          | Isolation=Hyper-V                          |
43
+// +-----------------+--------------------------------------------+--------------------------------------------+
44
+// | VolumePath      | \\?\\Volume{GUIDa}                         |                                            |
45
+// | LayerFolderPath | %root%\windowsfilter\containerID           |                                            |
46
+// | Layers[]        | ID=GUIDb;Path=%root%\windowsfilter\layerID | ID=GUIDb;Path=%root%\windowsfilter\layerID |
47
+// | SandboxPath     |                                            | %root%\windowsfilter                       |
48
+// | HvRuntime       |                                            | ImagePath=%root%\BaseLayerID\UtilityVM     |
49
+// +-----------------+--------------------------------------------+--------------------------------------------+
50
+//
51
+// Isolation=Process example:
52
+//
53
+// {
54
+//	"SystemType": "Container",
55
+//	"Name": "5e0055c814a6005b8e57ac59f9a522066e0af12b48b3c26a9416e23907698776",
56
+//	"Owner": "docker",
57
+//	"IsDummy": false,
58
+//	"VolumePath": "\\\\\\\\?\\\\Volume{66d1ef4c-7a00-11e6-8948-00155ddbef9d}",
59
+//	"IgnoreFlushesDuringBoot": true,
60
+//	"LayerFolderPath": "C:\\\\control\\\\windowsfilter\\\\5e0055c814a6005b8e57ac59f9a522066e0af12b48b3c26a9416e23907698776",
61
+//	"Layers": [{
62
+//		"ID": "18955d65-d45a-557b-bf1c-49d6dfefc526",
63
+//		"Path": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c"
64
+//	}],
65
+//	"HostName": "5e0055c814a6",
66
+//	"MappedDirectories": [],
67
+//	"HvPartition": false,
68
+//	"EndpointList": ["eef2649d-bb17-4d53-9937-295a8efe6f2c"],
69
+//	"Servicing": false
70
+//}
71
+//
72
+// Isolation=Hyper-V example:
73
+//
74
+//{
75
+//	"SystemType": "Container",
76
+//	"Name": "475c2c58933b72687a88a441e7e0ca4bd72d76413c5f9d5031fee83b98f6045d",
77
+//	"Owner": "docker",
78
+//	"IsDummy": false,
79
+//	"IgnoreFlushesDuringBoot": true,
80
+//	"Layers": [{
81
+//		"ID": "18955d65-d45a-557b-bf1c-49d6dfefc526",
82
+//		"Path": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c"
83
+//	}],
84
+//	"HostName": "475c2c58933b",
85
+//	"MappedDirectories": [],
86
+//	"SandboxPath": "C:\\\\control\\\\windowsfilter",
87
+//	"HvPartition": true,
88
+//	"EndpointList": ["e1bb1e61-d56f-405e-b75d-fd520cefa0cb"],
89
+//	"HvRuntime": {
90
+//		"ImagePath": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c\\\\UtilityVM"
91
+//	},
92
+//	"Servicing": false
93
+//}
40 94
 func (clnt *client) Create(containerID string, checkpoint string, checkpointDir string, spec Spec, options ...CreateOption) error {
41 95
 	clnt.lock(containerID)
42 96
 	defer clnt.unlock(containerID)
43 97
 	logrus.Debugln("libcontainerd: client.Create() with spec", spec)
44 98
 
45 99
 	configuration := &hcsshim.ContainerConfig{
46
-		SystemType:              "Container",
47
-		Name:                    containerID,
48
-		Owner:                   defaultOwner,
49
-		VolumePath:              spec.Root.Path,
100
+		SystemType: "Container",
101
+		Name:       containerID,
102
+		Owner:      defaultOwner,
50 103
 		IgnoreFlushesDuringBoot: false,
51
-		LayerFolderPath:         spec.Windows.LayerFolder,
52 104
 		HostName:                spec.Hostname,
105
+		HvPartition:             false,
53 106
 	}
54 107
 
55 108
 	if spec.Windows.Networking != nil {
... ...
@@ -80,33 +134,45 @@ func (clnt *client) Create(containerID string, checkpoint string, checkpointDir
80 80
 		}
81 81
 	}
82 82
 
83
-	if spec.Windows.HvRuntime != nil {
84
-		configuration.VolumePath = "" // Always empty for Hyper-V containers
85
-		configuration.HvPartition = true
86
-		configuration.HvRuntime = &hcsshim.HvRuntime{
87
-			ImagePath: spec.Windows.HvRuntime.ImagePath,
88
-		}
89
-	}
90
-
91
-	if configuration.HvPartition {
92
-		configuration.SandboxPath = filepath.Dir(spec.Windows.LayerFolder)
93
-	} else {
94
-		configuration.VolumePath = spec.Root.Path
95
-		configuration.LayerFolderPath = spec.Windows.LayerFolder
96
-	}
97
-
83
+	var layerOpt *LayerOption
98 84
 	for _, option := range options {
99 85
 		if s, ok := option.(*ServicingOption); ok {
100 86
 			configuration.Servicing = s.IsServicing
101 87
 			continue
102 88
 		}
103
-		if s, ok := option.(*FlushOption); ok {
104
-			configuration.IgnoreFlushesDuringBoot = s.IgnoreFlushesDuringBoot
89
+		if f, ok := option.(*FlushOption); ok {
90
+			configuration.IgnoreFlushesDuringBoot = f.IgnoreFlushesDuringBoot
91
+			continue
92
+		}
93
+		if h, ok := option.(*HyperVIsolationOption); ok {
94
+			configuration.HvPartition = h.IsHyperV
95
+			configuration.SandboxPath = h.SandboxPath
105 96
 			continue
106 97
 		}
98
+		if l, ok := option.(*LayerOption); ok {
99
+			layerOpt = l
100
+			continue
101
+		}
102
+	}
103
+
104
+	// We must have a layer option with at least one path
105
+	if layerOpt == nil || layerOpt.LayerPaths == nil {
106
+		return fmt.Errorf("no layer option or paths were supplied to the runtime")
107
+	}
108
+
109
+	if configuration.HvPartition {
110
+		// Make sure the Utility VM image is present in the base layer directory.
111
+		// TODO @swernli/jhowardmsft at some point post RS1 this may be re-locatable.
112
+		configuration.HvRuntime = &hcsshim.HvRuntime{ImagePath: filepath.Join(layerOpt.LayerPaths[len(layerOpt.LayerPaths)-1], "UtilityVM")}
113
+		if _, err := os.Stat(configuration.HvRuntime.ImagePath); os.IsNotExist(err) {
114
+			return fmt.Errorf("utility VM image '%s' could not be found", configuration.HvRuntime.ImagePath)
115
+		}
116
+	} else {
117
+		configuration.VolumePath = spec.Root.Path
118
+		configuration.LayerFolderPath = layerOpt.LayerFolderPath
107 119
 	}
108 120
 
109
-	for _, layerPath := range spec.Windows.LayerPaths {
121
+	for _, layerPath := range layerOpt.LayerPaths {
110 122
 		_, filename := filepath.Split(layerPath)
111 123
 		g, err := hcsshim.NameToGuid(filename)
112 124
 		if err != nil {
... ...
@@ -45,6 +45,22 @@ type FlushOption struct {
45 45
 	IgnoreFlushesDuringBoot bool
46 46
 }
47 47
 
48
+// HyperVIsolationOption is a CreateOption that indicates whether the runtime
49
+// should start the container as a Hyper-V container, and if so, the sandbox path.
50
+type HyperVIsolationOption struct {
51
+	IsHyperV    bool
52
+	SandboxPath string `json:",omitempty"`
53
+}
54
+
55
+// LayerOption is a CreateOption that indicates to the runtime the layer folder
56
+// and layer paths for a container.
57
+type LayerOption struct {
58
+	// LayerFolder is the path to the current layer folder. Empty for Hyper-V containers.
59
+	LayerFolderPath string `json:",omitempty"`
60
+	// Layer paths of the parent layers
61
+	LayerPaths []string
62
+}
63
+
48 64
 // Checkpoint holds the details of a checkpoint (not supported in windows)
49 65
 type Checkpoint struct {
50 66
 	Name string
... ...
@@ -21,6 +21,16 @@ func (s *ServicingOption) Apply(interface{}) error {
21 21
 }
22 22
 
23 23
 // Apply for the flush option is a no-op.
24
-func (s *FlushOption) Apply(interface{}) error {
24
+func (f *FlushOption) Apply(interface{}) error {
25
+	return nil
26
+}
27
+
28
+// Apply for the hypervisolation option is a no-op.
29
+func (h *HyperVIsolationOption) Apply(interface{}) error {
30
+	return nil
31
+}
32
+
33
+// Apply for the layer option is a no-op.
34
+func (h *LayerOption) Apply(interface{}) error {
25 35
 	return nil
26 36
 }
... ...
@@ -39,12 +39,6 @@ type Windows struct {
39 39
 	Resources *WindowsResources `json:"resources,omitempty"`
40 40
 	// Networking contains the platform specific network settings for the container.
41 41
 	Networking *WindowsNetworking `json:"networking,omitempty"`
42
-	// LayerFolder is the path to the current layer folder
43
-	LayerFolder string `json:"layer_folder,omitempty"`
44
-	// Layer paths of the parent layers
45
-	LayerPaths []string `json:"layer_paths,omitempty"`
46
-	// HvRuntime contains settings specific to Hyper-V containers, omitted if not using Hyper-V isolation
47
-	HvRuntime *WindowsHvRuntime `json:"hv_runtime,omitempty"`
48 42
 }
49 43
 
50 44
 // Process contains information to start a specific application inside the container.
... ...
@@ -122,12 +116,6 @@ type Mount struct {
122 122
 	Options []string `json:"options,omitempty"`
123 123
 }
124 124
 
125
-// WindowsHvRuntime contains settings specific to Hyper-V containers
126
-type WindowsHvRuntime struct {
127
-	// ImagePath is the path to the Utility VM image for this container
128
-	ImagePath string `json:"image_path,omitempty"`
129
-}
130
-
131 125
 // WindowsNetworking contains the platform specific network settings for the container
132 126
 type WindowsNetworking struct {
133 127
 	// List of endpoints to be attached to the container