Signed-off-by: John Howard <jhoward@microsoft.com>
| ... | ... |
@@ -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 |