Split Windows config generation to support LCOW

Signed-off-by: Darren Stahl <darst@microsoft.com>
This commit is contained in:
Darren Stahl 2017-08-28 15:08:08 -07:00
parent 4a782f7b54
commit d0b613665a
3 changed files with 148 additions and 70 deletions

View File

@ -10,6 +10,7 @@ import (
"strings" "strings"
"github.com/Microsoft/hcsshim" "github.com/Microsoft/hcsshim"
"github.com/Microsoft/opengcs/client"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
@ -19,29 +20,46 @@ import (
// newContainerConfig generates a hcsshim container configuration from the // newContainerConfig generates a hcsshim container configuration from the
// provided OCI Spec // provided OCI Spec
func newContainerConfig(ctx context.Context, owner, id string, spec *specs.Spec) (*hcsshim.ContainerConfig, error) { func newContainerConfig(ctx context.Context, owner, id string, spec *specs.Spec) (*hcsshim.ContainerConfig, error) {
if len(spec.Windows.LayerFolders) == 0 {
return nil, errors.Wrap(errdefs.ErrInvalidArgument,
"spec.Windows.LayerFolders cannot be empty")
}
var ( var (
layerFolders = spec.Windows.LayerFolders conf = &hcsshim.ContainerConfig{
conf = &hcsshim.ContainerConfig{ SystemType: "Container",
SystemType: "Container", Name: id,
Name: id, Owner: owner,
Owner: owner, HostName: spec.Hostname,
HostName: spec.Hostname,
IgnoreFlushesDuringBoot: spec.Windows.IgnoreFlushesDuringBoot,
AllowUnqualifiedDNSQuery: spec.Windows.Network.AllowUnqualifiedDNSQuery,
EndpointList: spec.Windows.Network.EndpointList,
NetworkSharedContainerName: spec.Windows.Network.NetworkSharedContainerName,
} }
) )
if spec.Windows.CredentialSpec != nil { if spec.Windows.Network != nil {
conf.Credentials = spec.Windows.CredentialSpec.(string) conf.AllowUnqualifiedDNSQuery = spec.Windows.Network.AllowUnqualifiedDNSQuery
conf.EndpointList = spec.Windows.Network.EndpointList
conf.NetworkSharedContainerName = spec.Windows.Network.NetworkSharedContainerName
if spec.Windows.Network.DNSSearchList != nil {
conf.DNSSearchList = strings.Join(spec.Windows.Network.DNSSearchList, ",")
}
} }
return conf, nil
}
// newWindowsContainerConfig generates a hcsshim Windows container
// configuration from the provided OCI Spec
func newWindowsContainerConfig(ctx context.Context, owner, id string, spec *specs.Spec) (*hcsshim.ContainerConfig, error) {
conf, err := newContainerConfig(ctx, owner, id, spec)
if err != nil {
return nil, err
}
conf.IgnoreFlushesDuringBoot = spec.Windows.IgnoreFlushesDuringBoot
if len(spec.Windows.LayerFolders) < 1 {
return nil, errors.Wrap(errdefs.ErrInvalidArgument,
"spec.Windows.LayerFolders must have at least 1 layers")
}
var (
layerFolders = spec.Windows.LayerFolders
homeDir = filepath.Dir(layerFolders[0])
layerFolderPath = filepath.Join(homeDir, id)
)
// TODO: use the create request Mount for those // TODO: use the create request Mount for those
for _, layerPath := range layerFolders { for _, layerPath := range layerFolders {
_, filename := filepath.Split(layerPath) _, filename := filepath.Split(layerPath)
@ -55,6 +73,60 @@ func newContainerConfig(ctx context.Context, owner, id string, spec *specs.Spec)
}) })
} }
var (
di = hcsshim.DriverInfo{
Flavour: 1, // filter driver
HomeDir: homeDir,
}
)
conf.LayerFolderPath = layerFolderPath
// TODO: Once there is a snapshotter for windows, this can be deleted.
// The R/W Layer should come from the Rootfs Mounts provided
//
// Windows doesn't support creating a container with a readonly
// filesystem, so always create a RW one
if err = hcsshim.CreateSandboxLayer(di, id, layerFolders[0], layerFolders); err != nil {
return nil, errors.Wrapf(err, "failed to create sandbox layer for %s: layers: %#v, driverInfo: %#v",
id, layerFolders, di)
}
defer func() {
if err != nil {
removeLayer(ctx, conf.LayerFolderPath)
}
}()
if err = hcsshim.ActivateLayer(di, id); err != nil {
return nil, errors.Wrapf(err, "failed to activate layer %s", conf.LayerFolderPath)
}
if err = hcsshim.PrepareLayer(di, id, layerFolders); err != nil {
return nil, errors.Wrapf(err, "failed to prepare layer %s", conf.LayerFolderPath)
}
conf.VolumePath, err = hcsshim.GetLayerMountPath(di, id)
if err != nil {
return nil, errors.Wrapf(err, "failed to getmount path for layer %s: driverInfo: %#v", id, di)
}
if spec.Windows.HyperV != nil {
conf.HvPartition = true
for _, layerPath := range layerFolders {
utilityVMPath := spec.Windows.HyperV.UtilityVMPath
_, err := os.Stat(utilityVMPath)
if err == nil {
conf.HvRuntime = &hcsshim.HvRuntime{ImagePath: utilityVMPath}
break
} else if !os.IsNotExist(err) {
return nil, errors.Wrapf(err, "failed to access layer %s", layerPath)
}
}
}
if spec.Windows.CredentialSpec != nil {
conf.Credentials = spec.Windows.CredentialSpec.(string)
}
if len(spec.Mounts) > 0 { if len(spec.Mounts) > 0 {
mds := make([]hcsshim.MappedDir, len(spec.Mounts)) mds := make([]hcsshim.MappedDir, len(spec.Mounts))
for i, mount := range spec.Mounts { for i, mount := range spec.Mounts {
@ -72,59 +144,51 @@ func newContainerConfig(ctx context.Context, owner, id string, spec *specs.Spec)
conf.MappedDirectories = mds conf.MappedDirectories = mds
} }
if spec.Windows.Network.DNSSearchList != nil { return conf, nil
conf.DNSSearchList = strings.Join(spec.Windows.Network.DNSSearchList, ",") }
// newLinuxConfig generates a hcsshim Linux container configuration from the
// provided OCI Spec
func newLinuxConfig(ctx context.Context, owner, id string, spec *specs.Spec) (*hcsshim.ContainerConfig, error) {
conf, err := newContainerConfig(ctx, owner, id, spec)
if err != nil {
return nil, err
} }
if spec.Windows.HyperV != nil { conf.ContainerType = "Linux"
conf.HvPartition = true conf.HvPartition = true
for _, layerPath := range layerFolders {
utilityVMPath := spec.Windows.HyperV.UtilityVMPath
_, err := os.Stat(utilityVMPath)
if err == nil {
conf.HvRuntime = &hcsshim.HvRuntime{ImagePath: utilityVMPath}
break
} else if !os.IsNotExist(err) {
return nil, errors.Wrapf(err, "failed to access layer %s", layerPath)
}
}
}
if len(spec.Windows.LayerFolders) < 1 {
return nil, errors.Wrap(errdefs.ErrInvalidArgument,
"spec.Windows.LayerFolders must have at least 1 layer")
}
var ( var (
err error layerFolders = spec.Windows.LayerFolders
di = hcsshim.DriverInfo{
Flavour: 1, // filter driver
HomeDir: filepath.Dir(layerFolders[0]),
}
) )
// TODO: Once there is a snapshotter for windows, this can be deleted. config := &client.Config{}
// The R/W Layer should come from the Rootfs Mounts provided if err := config.GenerateDefault(nil); err != nil {
// return nil, err
// Windows doesn't support creating a container with a readonly
// filesystem, so always create a RW one
if err = hcsshim.CreateSandboxLayer(di, id, layerFolders[0], layerFolders); err != nil {
return nil, errors.Wrapf(err, "failed to create sandbox layer for %s: layers: %#v, driverInfo: %#v",
id, layerFolders, di)
} }
conf.LayerFolderPath = filepath.Join(di.HomeDir, id)
defer func() { conf.HvRuntime = &hcsshim.HvRuntime{
ImagePath: config.KirdPath,
LinuxKernelFile: config.KernelFile,
LinuxInitrdFile: config.InitrdFile,
LinuxBootParameters: config.BootParameters,
}
// TODO: use the create request Mount for those
for _, layerPath := range layerFolders {
_, filename := filepath.Split(layerPath)
guid, err := hcsshim.NameToGuid(filename)
if err != nil { if err != nil {
removeLayer(ctx, conf.LayerFolderPath) return nil, errors.Wrapf(err, "unable to get GUID for %s", filename)
} }
}() conf.Layers = append(conf.Layers, hcsshim.Layer{
ID: guid.ToString(),
if err = hcsshim.ActivateLayer(di, id); err != nil { Path: filepath.Join(layerPath, "layer.vhd"),
return nil, errors.Wrapf(err, "failed to activate layer %s", conf.LayerFolderPath) })
}
if err = hcsshim.PrepareLayer(di, id, layerFolders); err != nil {
return nil, errors.Wrapf(err, "failed to prepare layer %s", conf.LayerFolderPath)
}
conf.VolumePath, err = hcsshim.GetLayerMountPath(di, id)
if err != nil {
return nil, errors.Wrapf(err, "failed to getmount path for layer %s: driverInfo: %#v", id, di)
} }
return conf, nil return conf, nil
@ -165,21 +229,23 @@ func removeLayer(ctx context.Context, path string) error {
return nil return nil
} }
func newProcessConfig(spec *specs.Process, pset *pipeSet) *hcsshim.ProcessConfig { func newProcessConfig(processSpec *specs.Process, pset *pipeSet) *hcsshim.ProcessConfig {
conf := &hcsshim.ProcessConfig{ conf := &hcsshim.ProcessConfig{
EmulateConsole: pset.src.Terminal, EmulateConsole: pset.src.Terminal,
CreateStdInPipe: pset.stdin != nil, CreateStdInPipe: pset.stdin != nil,
CreateStdOutPipe: pset.stdout != nil, CreateStdOutPipe: pset.stdout != nil,
CreateStdErrPipe: pset.stderr != nil, CreateStdErrPipe: pset.stderr != nil,
User: spec.User.Username, User: processSpec.User.Username,
CommandLine: strings.Join(spec.Args, " "),
Environment: make(map[string]string), Environment: make(map[string]string),
WorkingDirectory: spec.Cwd, WorkingDirectory: processSpec.Cwd,
ConsoleSize: [2]uint{spec.ConsoleSize.Height, spec.ConsoleSize.Width}, }
if processSpec.ConsoleSize != nil {
conf.ConsoleSize = [2]uint{processSpec.ConsoleSize.Height, processSpec.ConsoleSize.Width}
} }
// Convert OCI Env format to HCS's // Convert OCI Env format to HCS's
for _, s := range spec.Env { for _, s := range processSpec.Env {
arr := strings.SplitN(s, "=", 2) arr := strings.SplitN(s, "=", 2)
if len(arr) == 2 { if len(arr) == 2 {
conf.Environment[arr[0]] = arr[1] conf.Environment[arr[0]] = arr[1]
@ -188,3 +254,15 @@ func newProcessConfig(spec *specs.Process, pset *pipeSet) *hcsshim.ProcessConfig
return conf return conf
} }
func newWindowsProcessConfig(processSpec *specs.Process, pset *pipeSet) *hcsshim.ProcessConfig {
conf := newProcessConfig(processSpec, pset)
conf.CommandLine = strings.Join(processSpec.Args, " ")
return conf
}
func newLinuxProcessConfig(processSpec *specs.Process, pset *pipeSet) (*hcsshim.ProcessConfig, error) {
conf := newProcessConfig(processSpec, pset)
conf.CommandArgs = processSpec.Args
return conf, nil
}

View File

@ -243,7 +243,7 @@ func (r *windowsRuntime) newTask(ctx context.Context, namespace, id string, spec
conf *hcsshim.ContainerConfig conf *hcsshim.ContainerConfig
nsid = namespace + "-" + id nsid = namespace + "-" + id
) )
if conf, err = newContainerConfig(ctx, hcsshimOwner, nsid, spec); err != nil { if conf, err = newWindowsContainerConfig(ctx, hcsshimOwner, nsid, spec); err != nil {
return nil, err return nil, err
} }
defer func() { defer func() {

View File

@ -111,7 +111,7 @@ func (t *task) Info() runtime.TaskInfo {
} }
func (t *task) Start(ctx context.Context) error { func (t *task) Start(ctx context.Context) error {
conf := newProcessConfig(t.spec.Process, t.io) conf := newWindowsProcessConfig(t.spec.Process, t.io)
p, err := t.newProcess(ctx, t.id, conf, t.io) p, err := t.newProcess(ctx, t.id, conf, t.io)
if err != nil { if err != nil {
return err return err
@ -194,7 +194,7 @@ func (t *task) Exec(ctx context.Context, id string, opts runtime.ExecOpts) (runt
return nil, err return nil, err
} }
conf := newProcessConfig(spec, pset) conf := newWindowsProcessConfig(spec, pset)
p, err := t.newProcess(ctx, id, conf, pset) p, err := t.newProcess(ctx, id, conf, pset)
if err != nil { if err != nil {
return nil, err return nil, err