diff --git a/windows/hcsshim.go b/windows/hcsshim.go index 031a6359f..98ddee816 100644 --- a/windows/hcsshim.go +++ b/windows/hcsshim.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/Microsoft/hcsshim" + "github.com/Microsoft/opengcs/client" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/log" specs "github.com/opencontainers/runtime-spec/specs-go" @@ -19,29 +20,46 @@ import ( // newContainerConfig generates a hcsshim container configuration from the // provided OCI Spec 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 ( - layerFolders = spec.Windows.LayerFolders - conf = &hcsshim.ContainerConfig{ - SystemType: "Container", - Name: id, - Owner: owner, - HostName: spec.Hostname, - IgnoreFlushesDuringBoot: spec.Windows.IgnoreFlushesDuringBoot, - AllowUnqualifiedDNSQuery: spec.Windows.Network.AllowUnqualifiedDNSQuery, - EndpointList: spec.Windows.Network.EndpointList, - NetworkSharedContainerName: spec.Windows.Network.NetworkSharedContainerName, + conf = &hcsshim.ContainerConfig{ + SystemType: "Container", + Name: id, + Owner: owner, + HostName: spec.Hostname, } ) - if spec.Windows.CredentialSpec != nil { - conf.Credentials = spec.Windows.CredentialSpec.(string) + if spec.Windows.Network != nil { + 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 for _, layerPath := range layerFolders { _, 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 { mds := make([]hcsshim.MappedDir, len(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 } - if spec.Windows.Network.DNSSearchList != nil { - conf.DNSSearchList = strings.Join(spec.Windows.Network.DNSSearchList, ",") + return conf, nil +} + +// 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.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) - } - } - } + conf.ContainerType = "Linux" + conf.HvPartition = true + if len(spec.Windows.LayerFolders) < 1 { + return nil, errors.Wrap(errdefs.ErrInvalidArgument, + "spec.Windows.LayerFolders must have at least 1 layer") + } var ( - err error - di = hcsshim.DriverInfo{ - Flavour: 1, // filter driver - HomeDir: filepath.Dir(layerFolders[0]), - } + layerFolders = spec.Windows.LayerFolders ) - // 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) + config := &client.Config{} + if err := config.GenerateDefault(nil); err != nil { + return nil, err } - 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 { - removeLayer(ctx, conf.LayerFolderPath) + return nil, errors.Wrapf(err, "unable to get GUID for %s", filename) } - }() - - 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) + conf.Layers = append(conf.Layers, hcsshim.Layer{ + ID: guid.ToString(), + Path: filepath.Join(layerPath, "layer.vhd"), + }) } return conf, nil @@ -165,21 +229,23 @@ func removeLayer(ctx context.Context, path string) error { return nil } -func newProcessConfig(spec *specs.Process, pset *pipeSet) *hcsshim.ProcessConfig { +func newProcessConfig(processSpec *specs.Process, pset *pipeSet) *hcsshim.ProcessConfig { conf := &hcsshim.ProcessConfig{ EmulateConsole: pset.src.Terminal, CreateStdInPipe: pset.stdin != nil, CreateStdOutPipe: pset.stdout != nil, CreateStdErrPipe: pset.stderr != nil, - User: spec.User.Username, - CommandLine: strings.Join(spec.Args, " "), + User: processSpec.User.Username, Environment: make(map[string]string), - WorkingDirectory: spec.Cwd, - ConsoleSize: [2]uint{spec.ConsoleSize.Height, spec.ConsoleSize.Width}, + WorkingDirectory: processSpec.Cwd, + } + + if processSpec.ConsoleSize != nil { + conf.ConsoleSize = [2]uint{processSpec.ConsoleSize.Height, processSpec.ConsoleSize.Width} } // Convert OCI Env format to HCS's - for _, s := range spec.Env { + for _, s := range processSpec.Env { arr := strings.SplitN(s, "=", 2) if len(arr) == 2 { conf.Environment[arr[0]] = arr[1] @@ -188,3 +254,15 @@ func newProcessConfig(spec *specs.Process, pset *pipeSet) *hcsshim.ProcessConfig 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 +} diff --git a/windows/runtime.go b/windows/runtime.go index ce69dabff..74f499e28 100644 --- a/windows/runtime.go +++ b/windows/runtime.go @@ -243,7 +243,7 @@ func (r *windowsRuntime) newTask(ctx context.Context, namespace, id string, spec conf *hcsshim.ContainerConfig 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 } defer func() { diff --git a/windows/task.go b/windows/task.go index 86078f71d..33e5b7041 100644 --- a/windows/task.go +++ b/windows/task.go @@ -111,7 +111,7 @@ func (t *task) Info() runtime.TaskInfo { } 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) if err != nil { return err @@ -194,7 +194,7 @@ func (t *task) Exec(ctx context.Context, id string, opts runtime.ExecOpts) (runt return nil, err } - conf := newProcessConfig(spec, pset) + conf := newWindowsProcessConfig(spec, pset) p, err := t.newProcess(ctx, id, conf, pset) if err != nil { return nil, err