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