containerd/windows/hcsshim.go
Daniel Nephin f74862a0dd Add structcheck, unused, and varcheck linters.
Warn on unused and dead code

Signed-off-by: Daniel Nephin <dnephin@gmail.com>
2017-11-21 11:14:37 -05:00

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
}