215 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			215 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| //+build windows
 | |
| 
 | |
| package windows
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/Microsoft/hcsshim"
 | |
| 	"github.com/containerd/containerd/errdefs"
 | |
| 	"github.com/containerd/containerd/log"
 | |
| 	specs "github.com/opencontainers/runtime-spec/specs-go"
 | |
| 	"github.com/pkg/errors"
 | |
| )
 | |
| 
 | |
| // 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) {
 | |
| 	var (
 | |
| 		conf = &hcsshim.ContainerConfig{
 | |
| 			SystemType: "Container",
 | |
| 			Name:       id,
 | |
| 			Owner:      owner,
 | |
| 			HostName:   spec.Hostname,
 | |
| 		}
 | |
| 	)
 | |
| 
 | |
| 	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)
 | |
| 		guid, err := hcsshim.NameToGuid(filename)
 | |
| 		if err != nil {
 | |
| 			return nil, errors.Wrapf(err, "unable to get GUID for %s", filename)
 | |
| 		}
 | |
| 		conf.Layers = append(conf.Layers, hcsshim.Layer{
 | |
| 			ID:   guid.ToString(),
 | |
| 			Path: layerPath,
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	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 {
 | |
| 			mds[i] = hcsshim.MappedDir{
 | |
| 				HostPath:      mount.Source,
 | |
| 				ContainerPath: mount.Destination,
 | |
| 				ReadOnly:      false,
 | |
| 			}
 | |
| 			for _, o := range mount.Options {
 | |
| 				if strings.ToLower(o) == "ro" {
 | |
| 					mds[i].ReadOnly = true
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		conf.MappedDirectories = mds
 | |
| 	}
 | |
| 
 | |
| 	return conf, nil
 | |
| }
 | |
| 
 | |
| // removeLayer deletes the given layer, all associated containers must have
 | |
| // been shutdown for this to succeed.
 | |
| func removeLayer(ctx context.Context, path string) error {
 | |
| 	var (
 | |
| 		err        error
 | |
| 		layerID    = filepath.Base(path)
 | |
| 		parentPath = filepath.Dir(path)
 | |
| 		di         = hcsshim.DriverInfo{
 | |
| 			Flavour: 1, // filter driver
 | |
| 			HomeDir: parentPath,
 | |
| 		}
 | |
| 	)
 | |
| 
 | |
| 	if err = hcsshim.UnprepareLayer(di, layerID); err != nil {
 | |
| 		log.G(ctx).WithError(err).Warnf("failed to unprepare layer %s for removal", path)
 | |
| 	}
 | |
| 
 | |
| 	if err = hcsshim.DeactivateLayer(di, layerID); err != nil {
 | |
| 		log.G(ctx).WithError(err).Warnf("failed to deactivate layer %s for removal", path)
 | |
| 	}
 | |
| 
 | |
| 	removePath := filepath.Join(parentPath, fmt.Sprintf("%s-removing", layerID))
 | |
| 	if err = os.Rename(path, removePath); err != nil {
 | |
| 		log.G(ctx).WithError(err).Warnf("failed to rename container layer %s for removal", path)
 | |
| 		removePath = path
 | |
| 	}
 | |
| 
 | |
| 	if err = hcsshim.DestroyLayer(di, removePath); err != nil {
 | |
| 		log.G(ctx).WithError(err).Errorf("failed to remove container layer %s", removePath)
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| 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:             processSpec.User.Username,
 | |
| 		Environment:      make(map[string]string),
 | |
| 		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 processSpec.Env {
 | |
| 		arr := strings.SplitN(s, "=", 2)
 | |
| 		if len(arr) == 2 {
 | |
| 			conf.Environment[arr[0]] = arr[1]
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return conf
 | |
| }
 | |
| 
 | |
| func newWindowsProcessConfig(processSpec *specs.Process, pset *pipeSet) *hcsshim.ProcessConfig {
 | |
| 	conf := newProcessConfig(processSpec, pset)
 | |
| 	conf.CommandLine = strings.Join(processSpec.Args, " ")
 | |
| 	return conf
 | |
| }
 | 
