Split Windows config generation to support LCOW
Signed-off-by: Darren Stahl <darst@microsoft.com>
This commit is contained in:
parent
4a782f7b54
commit
d0b613665a
@ -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
|
||||||
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user