github.com/bigcommerce/nomad@v0.9.3-bc/drivers/rkt/driver.go (about)

     1  package rkt
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"math/rand"
    10  	"net"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"regexp"
    15  	"strconv"
    16  	"strings"
    17  	"sync"
    18  	"syscall"
    19  	"time"
    20  
    21  	appcschema "github.com/appc/spec/schema"
    22  	"github.com/hashicorp/consul-template/signals"
    23  	hclog "github.com/hashicorp/go-hclog"
    24  	version "github.com/hashicorp/go-version"
    25  	"github.com/hashicorp/nomad/client/config"
    26  	"github.com/hashicorp/nomad/client/taskenv"
    27  	"github.com/hashicorp/nomad/drivers/shared/eventer"
    28  	"github.com/hashicorp/nomad/drivers/shared/executor"
    29  	"github.com/hashicorp/nomad/helper"
    30  	"github.com/hashicorp/nomad/helper/pluginutils/hclutils"
    31  	"github.com/hashicorp/nomad/helper/pluginutils/loader"
    32  	"github.com/hashicorp/nomad/plugins/base"
    33  	"github.com/hashicorp/nomad/plugins/drivers"
    34  	"github.com/hashicorp/nomad/plugins/shared/hclspec"
    35  	pstructs "github.com/hashicorp/nomad/plugins/shared/structs"
    36  )
    37  
    38  const (
    39  	// pluginName is the name of the plugin
    40  	pluginName = "rkt"
    41  
    42  	// fingerprintPeriod is the interval at which the driver will send fingerprint responses
    43  	fingerprintPeriod = 30 * time.Second
    44  
    45  	// minRktVersion is the earliest supported version of rkt. rkt added support
    46  	// for CPU and memory isolators in 0.14.0. We cannot support an earlier
    47  	// version to maintain an uniform interface across all drivers
    48  	minRktVersion = "1.27.0"
    49  
    50  	// rktCmd is the command rkt is installed as.
    51  	rktCmd = "rkt"
    52  
    53  	// networkDeadline is how long to wait for container network
    54  	// information to become available.
    55  	networkDeadline = 1 * time.Minute
    56  
    57  	// taskHandleVersion is the version of task handle which this driver sets
    58  	// and understands how to decode driver state
    59  	taskHandleVersion = 1
    60  )
    61  
    62  var (
    63  	// PluginID is the rawexec plugin metadata registered in the plugin
    64  	// catalog.
    65  	PluginID = loader.PluginID{
    66  		Name:       pluginName,
    67  		PluginType: base.PluginTypeDriver,
    68  	}
    69  
    70  	// PluginConfig is the rawexec factory function registered in the
    71  	// plugin catalog.
    72  	PluginConfig = &loader.InternalPluginConfig{
    73  		Config:  map[string]interface{}{},
    74  		Factory: func(l hclog.Logger) interface{} { return NewRktDriver(l) },
    75  	}
    76  )
    77  
    78  // PluginLoader maps pre-0.9 client driver options to post-0.9 plugin options.
    79  func PluginLoader(opts map[string]string) (map[string]interface{}, error) {
    80  	conf := map[string]interface{}{}
    81  	if v, err := strconv.ParseBool(opts["driver.rkt.volumes.enabled"]); err == nil {
    82  		conf["volumes_enabled"] = v
    83  	}
    84  	return conf, nil
    85  }
    86  
    87  var (
    88  	// pluginInfo is the response returned for the PluginInfo RPC
    89  	pluginInfo = &base.PluginInfoResponse{
    90  		Type:              base.PluginTypeDriver,
    91  		PluginApiVersions: []string{drivers.ApiVersion010},
    92  		PluginVersion:     "0.1.0",
    93  		Name:              pluginName,
    94  	}
    95  
    96  	// configSpec is the hcl specification returned by the ConfigSchema RPC
    97  	configSpec = hclspec.NewObject(map[string]*hclspec.Spec{
    98  		"volumes_enabled": hclspec.NewDefault(
    99  			hclspec.NewAttr("volumes_enabled", "bool", false),
   100  			hclspec.NewLiteral("true"),
   101  		),
   102  	})
   103  
   104  	// taskConfigSpec is the hcl specification for the driver config section of
   105  	// a taskConfig within a job. It is returned in the TaskConfigSchema RPC
   106  	taskConfigSpec = hclspec.NewObject(map[string]*hclspec.Spec{
   107  		"image":              hclspec.NewAttr("image", "string", true),
   108  		"command":            hclspec.NewAttr("command", "string", false),
   109  		"args":               hclspec.NewAttr("args", "list(string)", false),
   110  		"trust_prefix":       hclspec.NewAttr("trust_prefix", "string", false),
   111  		"dns_servers":        hclspec.NewAttr("dns_servers", "list(string)", false),
   112  		"dns_search_domains": hclspec.NewAttr("dns_search_domains", "list(string)", false),
   113  		"net":                hclspec.NewAttr("net", "list(string)", false),
   114  		"port_map":           hclspec.NewAttr("port_map", "list(map(string))", false),
   115  		"volumes":            hclspec.NewAttr("volumes", "list(string)", false),
   116  		"insecure_options":   hclspec.NewAttr("insecure_options", "list(string)", false),
   117  		"no_overlay":         hclspec.NewAttr("no_overlay", "bool", false),
   118  		"debug":              hclspec.NewAttr("debug", "bool", false),
   119  		"group":              hclspec.NewAttr("group", "string", false),
   120  	})
   121  
   122  	// capabilities is returned by the Capabilities RPC and indicates what
   123  	// optional features this driver supports
   124  	capabilities = &drivers.Capabilities{
   125  		SendSignals: true,
   126  		Exec:        true,
   127  		FSIsolation: drivers.FSIsolationImage,
   128  	}
   129  
   130  	reRktVersion  = regexp.MustCompile(`rkt [vV]ersion[:]? (\d[.\d]+)`)
   131  	reAppcVersion = regexp.MustCompile(`appc [vV]ersion[:]? (\d[.\d]+)`)
   132  )
   133  
   134  // Config is the client configuration for the driver
   135  type Config struct {
   136  	// VolumesEnabled allows tasks to bind host paths (volumes) inside their
   137  	// container. Binding relative paths is always allowed and will be resolved
   138  	// relative to the allocation's directory.
   139  	VolumesEnabled bool `codec:"volumes_enabled"`
   140  }
   141  
   142  // TaskConfig is the driver configuration of a taskConfig within a job
   143  type TaskConfig struct {
   144  	ImageName        string             `codec:"image"`
   145  	Command          string             `codec:"command"`
   146  	Args             []string           `codec:"args"`
   147  	TrustPrefix      string             `codec:"trust_prefix"`
   148  	DNSServers       []string           `codec:"dns_servers"`        // DNS Server for containers
   149  	DNSSearchDomains []string           `codec:"dns_search_domains"` // DNS Search domains for containers
   150  	Net              []string           `codec:"net"`                // Networks for the containers
   151  	PortMap          hclutils.MapStrStr `codec:"port_map"`           // A map of host port and the port name defined in the image manifest file
   152  	Volumes          []string           `codec:"volumes"`            // Host-Volumes to mount in, syntax: /path/to/host/directory:/destination/path/in/container[:readOnly]
   153  	InsecureOptions  []string           `codec:"insecure_options"`   // list of args for --insecure-options
   154  
   155  	NoOverlay bool   `codec:"no_overlay"` // disable overlayfs for rkt run
   156  	Debug     bool   `codec:"debug"`      // Enable debug option for rkt command
   157  	Group     string `codec:"group"`      // Group override for the container
   158  }
   159  
   160  // TaskState is the state which is encoded in the handle returned in
   161  // StartTask. This information is needed to rebuild the taskConfig state and handler
   162  // during recovery.
   163  type TaskState struct {
   164  	ReattachConfig *pstructs.ReattachConfig
   165  	TaskConfig     *drivers.TaskConfig
   166  	Pid            int
   167  	StartedAt      time.Time
   168  	UUID           string
   169  }
   170  
   171  // Driver is a driver for running images via Rkt We attempt to chose sane
   172  // defaults for now, with more configuration available planned in the future.
   173  type Driver struct {
   174  	// eventer is used to handle multiplexing of TaskEvents calls such that an
   175  	// event can be broadcast to all callers
   176  	eventer *eventer.Eventer
   177  
   178  	// config is the driver configuration set by the SetConfig RPC
   179  	config *Config
   180  
   181  	// nomadConfig is the client config from nomad
   182  	nomadConfig *base.ClientDriverConfig
   183  
   184  	// tasks is the in memory datastore mapping taskIDs to rktTaskHandles
   185  	tasks *taskStore
   186  
   187  	// ctx is the context for the driver. It is passed to other subsystems to
   188  	// coordinate shutdown
   189  	ctx context.Context
   190  
   191  	// signalShutdown is called when the driver is shutting down and cancels the
   192  	// ctx passed to any subsystems
   193  	signalShutdown context.CancelFunc
   194  
   195  	// logger will log to the Nomad agent
   196  	logger hclog.Logger
   197  
   198  	// A tri-state boolean to know if the fingerprinting has happened and
   199  	// whether it has been successful
   200  	fingerprintSuccess *bool
   201  	fingerprintLock    sync.Mutex
   202  }
   203  
   204  func NewRktDriver(logger hclog.Logger) drivers.DriverPlugin {
   205  	ctx, cancel := context.WithCancel(context.Background())
   206  	logger = logger.Named(pluginName)
   207  	return &Driver{
   208  		eventer:        eventer.NewEventer(ctx, logger),
   209  		config:         &Config{},
   210  		tasks:          newTaskStore(),
   211  		ctx:            ctx,
   212  		signalShutdown: cancel,
   213  		logger:         logger,
   214  	}
   215  }
   216  
   217  func (d *Driver) PluginInfo() (*base.PluginInfoResponse, error) {
   218  	return pluginInfo, nil
   219  }
   220  
   221  func (d *Driver) ConfigSchema() (*hclspec.Spec, error) {
   222  	return configSpec, nil
   223  }
   224  
   225  func (d *Driver) SetConfig(cfg *base.Config) error {
   226  	var config Config
   227  	if len(cfg.PluginConfig) != 0 {
   228  		if err := base.MsgPackDecode(cfg.PluginConfig, &config); err != nil {
   229  			return err
   230  		}
   231  	}
   232  
   233  	d.config = &config
   234  	if cfg.AgentConfig != nil {
   235  		d.nomadConfig = cfg.AgentConfig.Driver
   236  	}
   237  	return nil
   238  }
   239  
   240  func (d *Driver) TaskConfigSchema() (*hclspec.Spec, error) {
   241  	return taskConfigSpec, nil
   242  }
   243  
   244  func (d *Driver) Capabilities() (*drivers.Capabilities, error) {
   245  	return capabilities, nil
   246  }
   247  
   248  func (d *Driver) Fingerprint(ctx context.Context) (<-chan *drivers.Fingerprint, error) {
   249  	ch := make(chan *drivers.Fingerprint)
   250  	go d.handleFingerprint(ctx, ch)
   251  	return ch, nil
   252  }
   253  
   254  func (d *Driver) handleFingerprint(ctx context.Context, ch chan *drivers.Fingerprint) {
   255  	defer close(ch)
   256  	ticker := time.NewTimer(0)
   257  	for {
   258  		select {
   259  		case <-ctx.Done():
   260  			return
   261  		case <-d.ctx.Done():
   262  			return
   263  		case <-ticker.C:
   264  			ticker.Reset(fingerprintPeriod)
   265  			ch <- d.buildFingerprint()
   266  		}
   267  	}
   268  }
   269  
   270  // setFingerprintSuccess marks the driver as having fingerprinted successfully
   271  func (d *Driver) setFingerprintSuccess() {
   272  	d.fingerprintLock.Lock()
   273  	d.fingerprintSuccess = helper.BoolToPtr(true)
   274  	d.fingerprintLock.Unlock()
   275  }
   276  
   277  // setFingerprintFailure marks the driver as having failed fingerprinting
   278  func (d *Driver) setFingerprintFailure() {
   279  	d.fingerprintLock.Lock()
   280  	d.fingerprintSuccess = helper.BoolToPtr(false)
   281  	d.fingerprintLock.Unlock()
   282  }
   283  
   284  // fingerprintSuccessful returns true if the driver has
   285  // never fingerprinted or has successfully fingerprinted
   286  func (d *Driver) fingerprintSuccessful() bool {
   287  	d.fingerprintLock.Lock()
   288  	defer d.fingerprintLock.Unlock()
   289  	return d.fingerprintSuccess == nil || *d.fingerprintSuccess
   290  }
   291  
   292  func (d *Driver) buildFingerprint() *drivers.Fingerprint {
   293  	fingerprint := &drivers.Fingerprint{
   294  		Attributes:        map[string]*pstructs.Attribute{},
   295  		Health:            drivers.HealthStateHealthy,
   296  		HealthDescription: drivers.DriverHealthy,
   297  	}
   298  
   299  	// Only enable if we are root
   300  	if syscall.Geteuid() != 0 {
   301  		if d.fingerprintSuccessful() {
   302  			d.logger.Debug("must run as root user, disabling")
   303  		}
   304  		d.setFingerprintFailure()
   305  		fingerprint.Health = drivers.HealthStateUndetected
   306  		fingerprint.HealthDescription = drivers.DriverRequiresRootMessage
   307  		return fingerprint
   308  	}
   309  
   310  	outBytes, err := exec.Command(rktCmd, "version").Output()
   311  	if err != nil {
   312  		fingerprint.Health = drivers.HealthStateUndetected
   313  		fingerprint.HealthDescription = fmt.Sprintf("Failed to execute %s version: %v", rktCmd, err)
   314  		d.setFingerprintFailure()
   315  		return fingerprint
   316  	}
   317  	out := strings.TrimSpace(string(outBytes))
   318  
   319  	rktMatches := reRktVersion.FindStringSubmatch(out)
   320  	appcMatches := reAppcVersion.FindStringSubmatch(out)
   321  	if len(rktMatches) != 2 || len(appcMatches) != 2 {
   322  		fingerprint.Health = drivers.HealthStateUndetected
   323  		fingerprint.HealthDescription = "Unable to parse rkt version string"
   324  		d.setFingerprintFailure()
   325  		return fingerprint
   326  	}
   327  
   328  	minVersion, _ := version.NewVersion(minRktVersion)
   329  	currentVersion, _ := version.NewVersion(rktMatches[1])
   330  	if currentVersion.LessThan(minVersion) {
   331  		// Do not allow ancient rkt versions
   332  		fingerprint.Health = drivers.HealthStateUndetected
   333  		fingerprint.HealthDescription = fmt.Sprintf("Unsuported rkt version %s", currentVersion)
   334  		if d.fingerprintSuccessful() {
   335  			d.logger.Warn("unsupported rkt version please upgrade to >= "+minVersion.String(),
   336  				"rkt_version", currentVersion)
   337  		}
   338  		d.setFingerprintFailure()
   339  		return fingerprint
   340  	}
   341  
   342  	fingerprint.Attributes["driver.rkt"] = pstructs.NewBoolAttribute(true)
   343  	fingerprint.Attributes["driver.rkt.version"] = pstructs.NewStringAttribute(rktMatches[1])
   344  	fingerprint.Attributes["driver.rkt.appc.version"] = pstructs.NewStringAttribute(appcMatches[1])
   345  	if d.config.VolumesEnabled {
   346  		fingerprint.Attributes["driver.rkt.volumes.enabled"] = pstructs.NewBoolAttribute(true)
   347  	}
   348  	d.setFingerprintSuccess()
   349  	return fingerprint
   350  
   351  }
   352  
   353  func (d *Driver) RecoverTask(handle *drivers.TaskHandle) error {
   354  	if handle == nil {
   355  		return fmt.Errorf("error: handle cannot be nil")
   356  	}
   357  
   358  	// COMPAT(0.10): pre 0.9 upgrade path check
   359  	if handle.Version == 0 {
   360  		return d.recoverPre09Task(handle)
   361  	}
   362  
   363  	// If already attached to handle there's nothing to recover.
   364  	if _, ok := d.tasks.Get(handle.Config.ID); ok {
   365  		d.logger.Trace("nothing to recover; task already exists",
   366  			"task_id", handle.Config.ID,
   367  			"task_name", handle.Config.Name,
   368  		)
   369  		return nil
   370  	}
   371  
   372  	var taskState TaskState
   373  	if err := handle.GetDriverState(&taskState); err != nil {
   374  		d.logger.Error("failed to decode taskConfig state from handle", "error", err, "task_id", handle.Config.ID)
   375  		return fmt.Errorf("failed to decode taskConfig state from handle: %v", err)
   376  	}
   377  
   378  	plugRC, err := pstructs.ReattachConfigToGoPlugin(taskState.ReattachConfig)
   379  	if err != nil {
   380  		d.logger.Error("failed to build ReattachConfig from taskConfig state", "error", err, "task_id", handle.Config.ID)
   381  		return fmt.Errorf("failed to build ReattachConfig from taskConfig state: %v", err)
   382  	}
   383  
   384  	execImpl, pluginClient, err := executor.ReattachToExecutor(plugRC,
   385  		d.logger.With("task_name", handle.Config.Name, "alloc_id", handle.Config.AllocID))
   386  	if err != nil {
   387  		d.logger.Error("failed to reattach to executor", "error", err, "task_id", handle.Config.ID)
   388  		return fmt.Errorf("failed to reattach to executor: %v", err)
   389  	}
   390  
   391  	// The taskConfig's environment is set via --set-env flags in Start, but the rkt
   392  	// command itself needs an environment with PATH set to find iptables.
   393  	// TODO (preetha) need to figure out how to read env.blacklist
   394  	eb := taskenv.NewEmptyBuilder()
   395  	filter := strings.Split(config.DefaultEnvBlacklist, ",")
   396  	rktEnv := eb.SetHostEnvvars(filter).Build()
   397  
   398  	h := &taskHandle{
   399  		exec:         execImpl,
   400  		env:          rktEnv,
   401  		pid:          taskState.Pid,
   402  		uuid:         taskState.UUID,
   403  		pluginClient: pluginClient,
   404  		taskConfig:   taskState.TaskConfig,
   405  		procState:    drivers.TaskStateRunning,
   406  		startedAt:    taskState.StartedAt,
   407  		exitResult:   &drivers.ExitResult{},
   408  	}
   409  
   410  	d.tasks.Set(taskState.TaskConfig.ID, h)
   411  
   412  	go h.run()
   413  	return nil
   414  }
   415  
   416  func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drivers.DriverNetwork, error) {
   417  	if _, ok := d.tasks.Get(cfg.ID); ok {
   418  		return nil, nil, fmt.Errorf("taskConfig with ID '%s' already started", cfg.ID)
   419  	}
   420  
   421  	var driverConfig TaskConfig
   422  
   423  	if err := cfg.DecodeDriverConfig(&driverConfig); err != nil {
   424  		return nil, nil, fmt.Errorf("failed to decode driver config: %v", err)
   425  	}
   426  
   427  	handle := drivers.NewTaskHandle(taskHandleVersion)
   428  	handle.Config = cfg
   429  
   430  	// todo(preetha) - port map in client v1 is a slice of maps that get merged. figure out if the caller will do this
   431  	//driverConfig.PortMap
   432  
   433  	// ACI image
   434  	img := driverConfig.ImageName
   435  
   436  	// Global arguments given to both prepare and run-prepared
   437  	globalArgs := make([]string, 0, 50)
   438  
   439  	// Add debug option to rkt command.
   440  	debug := driverConfig.Debug
   441  
   442  	// Add the given trust prefix
   443  	trustPrefix := driverConfig.TrustPrefix
   444  	insecure := false
   445  	if trustPrefix != "" {
   446  		var outBuf, errBuf bytes.Buffer
   447  		cmd := exec.Command(rktCmd, "trust", "--skip-fingerprint-review=true", fmt.Sprintf("--prefix=%s", trustPrefix), fmt.Sprintf("--debug=%t", debug))
   448  		cmd.Stdout = &outBuf
   449  		cmd.Stderr = &errBuf
   450  		if err := cmd.Run(); err != nil {
   451  			return nil, nil, fmt.Errorf("Error running rkt trust: %s\n\nOutput: %s\n\nError: %s",
   452  				err, outBuf.String(), errBuf.String())
   453  		}
   454  		d.logger.Debug("added trust prefix", "trust_prefix", trustPrefix, "task_name", cfg.Name)
   455  	} else {
   456  		// Disable signature verification if the trust command was not run.
   457  		insecure = true
   458  	}
   459  
   460  	// if we have a selective insecure_options, prefer them
   461  	// insecure options are rkt's global argument, so we do this before the actual "run"
   462  	if len(driverConfig.InsecureOptions) > 0 {
   463  		globalArgs = append(globalArgs, fmt.Sprintf("--insecure-options=%s", strings.Join(driverConfig.InsecureOptions, ",")))
   464  	} else if insecure {
   465  		globalArgs = append(globalArgs, "--insecure-options=all")
   466  	}
   467  
   468  	// debug is rkt's global argument, so add it before the actual "run"
   469  	globalArgs = append(globalArgs, fmt.Sprintf("--debug=%t", debug))
   470  
   471  	prepareArgs := make([]string, 0, 50)
   472  	runArgs := make([]string, 0, 50)
   473  
   474  	prepareArgs = append(prepareArgs, globalArgs...)
   475  	prepareArgs = append(prepareArgs, "prepare")
   476  	runArgs = append(runArgs, globalArgs...)
   477  	runArgs = append(runArgs, "run-prepared")
   478  
   479  	// disable overlayfs
   480  	if driverConfig.NoOverlay {
   481  		prepareArgs = append(prepareArgs, "--no-overlay=true")
   482  	}
   483  
   484  	// Convert underscores to dashes in taskConfig names for use in volume names #2358
   485  	sanitizedName := strings.Replace(cfg.Name, "_", "-", -1)
   486  
   487  	// Mount /alloc
   488  	allocVolName := fmt.Sprintf("%s-%s-alloc", cfg.AllocID, sanitizedName)
   489  	prepareArgs = append(prepareArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s", allocVolName, cfg.TaskDir().SharedAllocDir))
   490  	prepareArgs = append(prepareArgs, fmt.Sprintf("--mount=volume=%s,target=%s", allocVolName, "/alloc"))
   491  
   492  	// Mount /local
   493  	localVolName := fmt.Sprintf("%s-%s-local", cfg.AllocID, sanitizedName)
   494  	prepareArgs = append(prepareArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s", localVolName, cfg.TaskDir().LocalDir))
   495  	prepareArgs = append(prepareArgs, fmt.Sprintf("--mount=volume=%s,target=%s", localVolName, "/local"))
   496  
   497  	// Mount /secrets
   498  	secretsVolName := fmt.Sprintf("%s-%s-secrets", cfg.AllocID, sanitizedName)
   499  	prepareArgs = append(prepareArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s", secretsVolName, cfg.TaskDir().SecretsDir))
   500  	prepareArgs = append(prepareArgs, fmt.Sprintf("--mount=volume=%s,target=%s", secretsVolName, "/secrets"))
   501  
   502  	// Mount arbitrary volumes if enabled
   503  	if len(driverConfig.Volumes) > 0 {
   504  		if !d.config.VolumesEnabled {
   505  			return nil, nil, fmt.Errorf("volumes_enabled is false; cannot use rkt volumes: %+q", driverConfig.Volumes)
   506  		}
   507  		for i, rawvol := range driverConfig.Volumes {
   508  			parts := strings.Split(rawvol, ":")
   509  			readOnly := "false"
   510  			// job spec:
   511  			//   volumes = ["/host/path:/container/path[:readOnly]"]
   512  			// the third parameter is optional, mount is read-write by default
   513  			if len(parts) == 3 {
   514  				if parts[2] == "readOnly" {
   515  					d.logger.Debug("mounting volume as readOnly", "volume", strings.Join(parts[:2], parts[1]))
   516  					readOnly = "true"
   517  				} else {
   518  					d.logger.Warn("unknown volume parameter ignored for mount", "parameter", parts[2], "mount", parts[0])
   519  				}
   520  			} else if len(parts) != 2 {
   521  				return nil, nil, fmt.Errorf("invalid rkt volume: %q", rawvol)
   522  			}
   523  			volName := fmt.Sprintf("%s-%s-%d", cfg.AllocID, sanitizedName, i)
   524  			prepareArgs = append(prepareArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s,readOnly=%s", volName, parts[0], readOnly))
   525  			prepareArgs = append(prepareArgs, fmt.Sprintf("--mount=volume=%s,target=%s", volName, parts[1]))
   526  		}
   527  	}
   528  
   529  	// Mount task volumes, always do
   530  	for i, vol := range cfg.Mounts {
   531  		volName := fmt.Sprintf("%s-%s-taskmounts-%d", cfg.AllocID, sanitizedName, i)
   532  		prepareArgs = append(prepareArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s,readOnly=%v", volName, vol.HostPath, vol.Readonly))
   533  		prepareArgs = append(prepareArgs, fmt.Sprintf("--mount=volume=%s,target=%s", volName, vol.TaskPath))
   534  	}
   535  
   536  	// Mount task devices, always do
   537  	for i, vol := range cfg.Devices {
   538  		volName := fmt.Sprintf("%s-%s-taskdevices-%d", cfg.AllocID, sanitizedName, i)
   539  		readOnly := !strings.Contains(vol.Permissions, "w")
   540  		prepareArgs = append(prepareArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s,readOnly=%v", volName, vol.HostPath, readOnly))
   541  		prepareArgs = append(prepareArgs, fmt.Sprintf("--mount=volume=%s,target=%s", volName, vol.TaskPath))
   542  	}
   543  
   544  	// Inject environment variables
   545  	for k, v := range cfg.Env {
   546  		prepareArgs = append(prepareArgs, fmt.Sprintf("--set-env=%s=%s", k, v))
   547  	}
   548  
   549  	// Image is set here, because the commands that follow apply to it
   550  	prepareArgs = append(prepareArgs, img)
   551  
   552  	// Check if the user has overridden the exec command.
   553  	if driverConfig.Command != "" {
   554  		prepareArgs = append(prepareArgs, fmt.Sprintf("--exec=%v", driverConfig.Command))
   555  	}
   556  
   557  	// Add memory isolator
   558  	prepareArgs = append(prepareArgs, fmt.Sprintf("--memory=%v", cfg.Resources.LinuxResources.MemoryLimitBytes))
   559  
   560  	// Add CPU isolator
   561  	prepareArgs = append(prepareArgs, fmt.Sprintf("--cpu=%v", cfg.Resources.LinuxResources.CPUShares))
   562  
   563  	// Add DNS servers
   564  	if len(driverConfig.DNSServers) == 1 && (driverConfig.DNSServers[0] == "host" || driverConfig.DNSServers[0] == "none") {
   565  		// Special case single item lists with the special values "host" or "none"
   566  		runArgs = append(runArgs, fmt.Sprintf("--dns=%s", driverConfig.DNSServers[0]))
   567  	} else {
   568  		for _, ip := range driverConfig.DNSServers {
   569  			if err := net.ParseIP(ip); err == nil {
   570  				wrappedErr := fmt.Errorf("invalid ip address for container dns server %q", ip)
   571  				d.logger.Debug("error parsing DNS server", "error", wrappedErr)
   572  				return nil, nil, wrappedErr
   573  			}
   574  			runArgs = append(runArgs, fmt.Sprintf("--dns=%s", ip))
   575  		}
   576  	}
   577  
   578  	// set DNS search domains
   579  	for _, domain := range driverConfig.DNSSearchDomains {
   580  		runArgs = append(runArgs, fmt.Sprintf("--dns-search=%s", domain))
   581  	}
   582  
   583  	// set network
   584  	network := strings.Join(driverConfig.Net, ",")
   585  	if network != "" {
   586  		runArgs = append(runArgs, fmt.Sprintf("--net=%s", network))
   587  	}
   588  
   589  	// Setup port mapping and exposed ports
   590  	if len(cfg.Resources.NomadResources.Networks) == 0 {
   591  		d.logger.Debug("no network interfaces are available")
   592  		if len(driverConfig.PortMap) > 0 {
   593  			return nil, nil, fmt.Errorf("Trying to map ports but no network interface is available")
   594  		}
   595  	} else if network == "host" {
   596  		// Port mapping is skipped when host networking is used.
   597  		d.logger.Debug("Ignoring port_map when using --net=host", "task_name", cfg.Name)
   598  	} else {
   599  		network := cfg.Resources.NomadResources.Networks[0]
   600  		for _, port := range network.ReservedPorts {
   601  			var containerPort string
   602  
   603  			mapped, ok := driverConfig.PortMap[port.Label]
   604  			if !ok {
   605  				// If the user doesn't have a mapped port using port_map, driver stops running container.
   606  				return nil, nil, fmt.Errorf("port_map is not set. When you defined port in the resources, you need to configure port_map.")
   607  			}
   608  			containerPort = mapped
   609  
   610  			hostPortStr := strconv.Itoa(port.Value)
   611  
   612  			d.logger.Debug("driver.rkt: exposed port", "containerPort", containerPort)
   613  			// Add port option to rkt run arguments. rkt allows multiple port args
   614  			prepareArgs = append(prepareArgs, fmt.Sprintf("--port=%s:%s", containerPort, hostPortStr))
   615  		}
   616  
   617  		for _, port := range network.DynamicPorts {
   618  			// By default we will map the allocated port 1:1 to the container
   619  			var containerPort string
   620  
   621  			if mapped, ok := driverConfig.PortMap[port.Label]; ok {
   622  				containerPort = mapped
   623  			} else {
   624  				// If the user doesn't have mapped a port using port_map, driver stops running container.
   625  				return nil, nil, fmt.Errorf("port_map is not set. When you defined port in the resources, you need to configure port_map.")
   626  			}
   627  
   628  			hostPortStr := strconv.Itoa(port.Value)
   629  
   630  			d.logger.Debug("exposed port", "containerPort", containerPort, "task_name", cfg.Name)
   631  			// Add port option to rkt run arguments. rkt allows multiple port args
   632  			prepareArgs = append(prepareArgs, fmt.Sprintf("--port=%s:%s", containerPort, hostPortStr))
   633  		}
   634  
   635  	}
   636  
   637  	// If a user has been specified for the taskConfig, pass it through to the user
   638  	if cfg.User != "" {
   639  		prepareArgs = append(prepareArgs, fmt.Sprintf("--user=%s", cfg.User))
   640  	}
   641  
   642  	// There's no taskConfig-level parameter for groups so check the driver
   643  	// config for a custom group
   644  	if driverConfig.Group != "" {
   645  		prepareArgs = append(prepareArgs, fmt.Sprintf("--group=%s", driverConfig.Group))
   646  	}
   647  
   648  	// Add user passed arguments.
   649  	if len(driverConfig.Args) != 0 {
   650  
   651  		// Need to start arguments with "--"
   652  		prepareArgs = append(prepareArgs, "--")
   653  
   654  		for _, arg := range driverConfig.Args {
   655  			prepareArgs = append(prepareArgs, fmt.Sprintf("%v", arg))
   656  		}
   657  	}
   658  
   659  	pluginLogFile := filepath.Join(cfg.TaskDir().Dir, fmt.Sprintf("%s-executor.out", cfg.Name))
   660  	executorConfig := &executor.ExecutorConfig{
   661  		LogFile:  pluginLogFile,
   662  		LogLevel: "debug",
   663  	}
   664  
   665  	execImpl, pluginClient, err := executor.CreateExecutor(
   666  		d.logger.With("task_name", handle.Config.Name, "alloc_id", handle.Config.AllocID),
   667  		d.nomadConfig, executorConfig)
   668  	if err != nil {
   669  		return nil, nil, err
   670  	}
   671  
   672  	absPath, err := GetAbsolutePath(rktCmd)
   673  	if err != nil {
   674  		return nil, nil, err
   675  	}
   676  
   677  	var outBuf, errBuf bytes.Buffer
   678  	cmd := exec.Command(rktCmd, prepareArgs...)
   679  	cmd.Stdout = &outBuf
   680  	cmd.Stderr = &errBuf
   681  	d.logger.Debug("preparing taskConfig", "pod", img, "task_name", cfg.Name, "args", prepareArgs)
   682  	if err := cmd.Run(); err != nil {
   683  		return nil, nil, fmt.Errorf("Error preparing rkt pod: %s\n\nOutput: %s\n\nError: %s",
   684  			err, outBuf.String(), errBuf.String())
   685  	}
   686  	uuid := strings.TrimSpace(outBuf.String())
   687  	d.logger.Debug("taskConfig prepared", "pod", img, "task_name", cfg.Name, "uuid", uuid)
   688  	runArgs = append(runArgs, uuid)
   689  
   690  	// The taskConfig's environment is set via --set-env flags above, but the rkt
   691  	// command itself needs an environment with PATH set to find iptables.
   692  
   693  	// TODO (preetha) need to figure out how to pass env.blacklist from client config
   694  	eb := taskenv.NewEmptyBuilder()
   695  	filter := strings.Split(config.DefaultEnvBlacklist, ",")
   696  	rktEnv := eb.SetHostEnvvars(filter).Build()
   697  
   698  	// Enable ResourceLimits to place the executor in a parent cgroup of
   699  	// the rkt container. This allows stats collection via the executor to
   700  	// work just like it does for exec.
   701  	execCmd := &executor.ExecCommand{
   702  		Cmd:            absPath,
   703  		Args:           runArgs,
   704  		ResourceLimits: true,
   705  		Resources:      cfg.Resources,
   706  
   707  		// Use rktEnv, the environment needed for running rkt, not the task env
   708  		Env: rktEnv.List(),
   709  
   710  		TaskDir:    cfg.TaskDir().Dir,
   711  		StdoutPath: cfg.StdoutPath,
   712  		StderrPath: cfg.StderrPath,
   713  	}
   714  	ps, err := execImpl.Launch(execCmd)
   715  	if err != nil {
   716  		pluginClient.Kill()
   717  		return nil, nil, err
   718  	}
   719  
   720  	d.logger.Debug("started taskConfig", "aci", img, "uuid", uuid, "task_name", cfg.Name, "args", runArgs)
   721  	h := &taskHandle{
   722  		exec:         execImpl,
   723  		env:          rktEnv,
   724  		pid:          ps.Pid,
   725  		uuid:         uuid,
   726  		pluginClient: pluginClient,
   727  		taskConfig:   cfg,
   728  		procState:    drivers.TaskStateRunning,
   729  		startedAt:    time.Now().Round(time.Millisecond),
   730  		logger:       d.logger,
   731  	}
   732  
   733  	rktDriverState := TaskState{
   734  		ReattachConfig: pstructs.ReattachConfigFromGoPlugin(pluginClient.ReattachConfig()),
   735  		Pid:            ps.Pid,
   736  		TaskConfig:     cfg,
   737  		StartedAt:      h.startedAt,
   738  		UUID:           uuid,
   739  	}
   740  
   741  	if err := handle.SetDriverState(&rktDriverState); err != nil {
   742  		d.logger.Error("failed to start task, error setting driver state", "error", err, "task_name", cfg.Name)
   743  		execImpl.Shutdown("", 0)
   744  		pluginClient.Kill()
   745  		return nil, nil, fmt.Errorf("failed to set driver state: %v", err)
   746  	}
   747  
   748  	d.tasks.Set(cfg.ID, h)
   749  	go h.run()
   750  
   751  	// Do not attempt to retrieve driver network if one won't exist:
   752  	//  - "host" means the container itself has no networking metadata
   753  	//  - "none" means no network is configured
   754  	// https://coreos.com/rkt/docs/latest/networking/overview.html#no-loopback-only-networking
   755  	var driverNetwork *drivers.DriverNetwork
   756  	if network != "host" && network != "none" {
   757  		d.logger.Debug("retrieving network information for pod", "pod", img, "UUID", uuid, "task_name", cfg.Name)
   758  		driverNetwork, err = rktGetDriverNetwork(uuid, driverConfig.PortMap, d.logger)
   759  		if err != nil && !pluginClient.Exited() {
   760  			d.logger.Warn("network status retrieval for pod failed", "pod", img, "UUID", uuid, "task_name", cfg.Name, "error", err)
   761  
   762  			// If a portmap was given, this turns into a fatal error
   763  			if len(driverConfig.PortMap) != 0 {
   764  				pluginClient.Kill()
   765  				return nil, nil, fmt.Errorf("Trying to map ports but driver could not determine network information")
   766  			}
   767  		}
   768  	}
   769  
   770  	return handle, driverNetwork, nil
   771  
   772  }
   773  
   774  func (d *Driver) WaitTask(ctx context.Context, taskID string) (<-chan *drivers.ExitResult, error) {
   775  	handle, ok := d.tasks.Get(taskID)
   776  	if !ok {
   777  		return nil, drivers.ErrTaskNotFound
   778  	}
   779  
   780  	ch := make(chan *drivers.ExitResult)
   781  	go d.handleWait(ctx, handle, ch)
   782  
   783  	return ch, nil
   784  }
   785  
   786  func (d *Driver) StopTask(taskID string, timeout time.Duration, signal string) error {
   787  	handle, ok := d.tasks.Get(taskID)
   788  	if !ok {
   789  		return drivers.ErrTaskNotFound
   790  	}
   791  
   792  	if err := handle.exec.Shutdown(signal, timeout); err != nil {
   793  		if handle.pluginClient.Exited() {
   794  			return nil
   795  		}
   796  		return fmt.Errorf("executor Shutdown failed: %v", err)
   797  	}
   798  
   799  	return nil
   800  }
   801  
   802  func (d *Driver) DestroyTask(taskID string, force bool) error {
   803  	handle, ok := d.tasks.Get(taskID)
   804  	if !ok {
   805  		return drivers.ErrTaskNotFound
   806  	}
   807  
   808  	if handle.IsRunning() && !force {
   809  		return fmt.Errorf("cannot destroy running task")
   810  	}
   811  
   812  	if !handle.pluginClient.Exited() {
   813  		if handle.IsRunning() {
   814  			if err := handle.exec.Shutdown("", 0); err != nil {
   815  				handle.logger.Error("destroying executor failed", "err", err)
   816  			}
   817  		}
   818  
   819  		handle.pluginClient.Kill()
   820  	}
   821  
   822  	d.tasks.Delete(taskID)
   823  	return nil
   824  }
   825  
   826  func (d *Driver) InspectTask(taskID string) (*drivers.TaskStatus, error) {
   827  	handle, ok := d.tasks.Get(taskID)
   828  	if !ok {
   829  		return nil, drivers.ErrTaskNotFound
   830  	}
   831  
   832  	return handle.TaskStatus(), nil
   833  }
   834  
   835  func (d *Driver) TaskStats(ctx context.Context, taskID string, interval time.Duration) (<-chan *drivers.TaskResourceUsage, error) {
   836  	handle, ok := d.tasks.Get(taskID)
   837  	if !ok {
   838  		return nil, drivers.ErrTaskNotFound
   839  	}
   840  
   841  	return handle.exec.Stats(ctx, interval)
   842  }
   843  
   844  func (d *Driver) TaskEvents(ctx context.Context) (<-chan *drivers.TaskEvent, error) {
   845  	return d.eventer.TaskEvents(ctx)
   846  }
   847  
   848  func (d *Driver) SignalTask(taskID string, signal string) error {
   849  	handle, ok := d.tasks.Get(taskID)
   850  	if !ok {
   851  		return drivers.ErrTaskNotFound
   852  	}
   853  
   854  	sig := os.Interrupt
   855  	if s, ok := signals.SignalLookup[signal]; ok {
   856  		sig = s
   857  	} else {
   858  		d.logger.Warn("unknown signal to send to task, using SIGINT instead", "signal", signal, "task_id", handle.taskConfig.ID, "task_name", handle.taskConfig.Name)
   859  
   860  	}
   861  	return handle.exec.Signal(sig)
   862  }
   863  
   864  func (d *Driver) ExecTask(taskID string, cmdArgs []string, timeout time.Duration) (*drivers.ExecTaskResult, error) {
   865  	if len(cmdArgs) == 0 {
   866  		return nil, fmt.Errorf("error cmd must have at least one value")
   867  	}
   868  	handle, ok := d.tasks.Get(taskID)
   869  	if !ok {
   870  		return nil, drivers.ErrTaskNotFound
   871  	}
   872  	// enter + UUID + cmd + args...
   873  	cmd := cmdArgs[0]
   874  	args := cmdArgs[1:]
   875  	enterArgs := make([]string, 3+len(args))
   876  	enterArgs[0] = "enter"
   877  	enterArgs[1] = handle.uuid
   878  	enterArgs[2] = handle.env.ReplaceEnv(cmd)
   879  	copy(enterArgs[3:], handle.env.ParseAndReplace(args))
   880  	out, exitCode, err := handle.exec.Exec(time.Now().Add(timeout), rktCmd, enterArgs)
   881  	if err != nil {
   882  		return nil, err
   883  	}
   884  
   885  	return &drivers.ExecTaskResult{
   886  		Stdout: out,
   887  		ExitResult: &drivers.ExitResult{
   888  			ExitCode: exitCode,
   889  		},
   890  	}, nil
   891  
   892  }
   893  
   894  var _ drivers.ExecTaskStreamingRawDriver = (*Driver)(nil)
   895  
   896  func (d *Driver) ExecTaskStreamingRaw(ctx context.Context,
   897  	taskID string,
   898  	command []string,
   899  	tty bool,
   900  	stream drivers.ExecTaskStream) error {
   901  
   902  	if len(command) == 0 {
   903  		return fmt.Errorf("error cmd must have at least one value")
   904  	}
   905  	handle, ok := d.tasks.Get(taskID)
   906  	if !ok {
   907  		return drivers.ErrTaskNotFound
   908  	}
   909  
   910  	enterCmd := []string{rktCmd, "enter", handle.uuid, handle.env.ReplaceEnv(command[0])}
   911  	enterCmd = append(enterCmd, handle.env.ParseAndReplace(command[1:])...)
   912  
   913  	return handle.exec.ExecStreaming(ctx, enterCmd, tty, stream)
   914  }
   915  
   916  // GetAbsolutePath returns the absolute path of the passed binary by resolving
   917  // it in the path and following symlinks.
   918  func GetAbsolutePath(bin string) (string, error) {
   919  	lp, err := exec.LookPath(bin)
   920  	if err != nil {
   921  		return "", fmt.Errorf("failed to resolve path to %q executable: %v", bin, err)
   922  	}
   923  
   924  	return filepath.EvalSymlinks(lp)
   925  }
   926  
   927  func rktGetDriverNetwork(uuid string, driverConfigPortMap map[string]string, logger hclog.Logger) (*drivers.DriverNetwork, error) {
   928  	deadline := time.Now().Add(networkDeadline)
   929  	var lastErr error
   930  	try := 0
   931  
   932  	for time.Now().Before(deadline) {
   933  		try++
   934  		if status, err := rktGetStatus(uuid, logger); err == nil {
   935  			for _, net := range status.Networks {
   936  				if !net.IP.IsGlobalUnicast() {
   937  					continue
   938  				}
   939  
   940  				// Get the pod manifest so we can figure out which ports are exposed
   941  				var portmap map[string]int
   942  				manifest, err := rktGetManifest(uuid)
   943  				if err == nil {
   944  					portmap, err = rktManifestMakePortMap(manifest, driverConfigPortMap)
   945  					if err != nil {
   946  						lastErr = fmt.Errorf("could not create manifest-based portmap: %v", err)
   947  						return nil, lastErr
   948  					}
   949  				} else {
   950  					lastErr = fmt.Errorf("could not get pod manifest: %v", err)
   951  					return nil, lastErr
   952  				}
   953  
   954  				// This is a successful landing; log if its not the first attempt.
   955  				if try > 1 {
   956  					logger.Debug("retrieved network info for pod", "uuid", uuid, "attempt", try)
   957  				}
   958  				return &drivers.DriverNetwork{
   959  					PortMap: portmap,
   960  					IP:      status.Networks[0].IP.String(),
   961  				}, nil
   962  			}
   963  
   964  			if len(status.Networks) == 0 {
   965  				lastErr = fmt.Errorf("no networks found")
   966  			} else {
   967  				lastErr = fmt.Errorf("no good driver networks out of %d returned", len(status.Networks))
   968  			}
   969  		} else {
   970  			lastErr = fmt.Errorf("getting status failed: %v", err)
   971  		}
   972  
   973  		waitTime := getJitteredNetworkRetryTime()
   974  		logger.Debug("failed getting network info for pod, sleeping", "uuid", uuid, "attempt", try, "err", lastErr, "wait", waitTime)
   975  		time.Sleep(waitTime)
   976  	}
   977  	return nil, fmt.Errorf("timed out, last error: %v", lastErr)
   978  }
   979  
   980  // Given a rkt/appc pod manifest and driver portmap configuration, create
   981  // a driver portmap.
   982  func rktManifestMakePortMap(manifest *appcschema.PodManifest, configPortMap map[string]string) (map[string]int, error) {
   983  	if len(manifest.Apps) == 0 {
   984  		return nil, fmt.Errorf("manifest has no apps")
   985  	}
   986  	if len(manifest.Apps) != 1 {
   987  		return nil, fmt.Errorf("manifest has multiple apps!")
   988  	}
   989  	app := manifest.Apps[0]
   990  	if app.App == nil {
   991  		return nil, fmt.Errorf("specified app has no App object")
   992  	}
   993  
   994  	portMap := make(map[string]int)
   995  	for svc, name := range configPortMap {
   996  		for _, port := range app.App.Ports {
   997  			if port.Name.String() == name {
   998  				portMap[svc] = int(port.Port)
   999  			}
  1000  		}
  1001  	}
  1002  	return portMap, nil
  1003  }
  1004  
  1005  // Retrieve pod status for the pod with the given UUID.
  1006  func rktGetStatus(uuid string, logger hclog.Logger) (*Pod, error) {
  1007  	statusArgs := []string{
  1008  		"status",
  1009  		"--format=json",
  1010  		uuid,
  1011  	}
  1012  	var outBuf, errBuf bytes.Buffer
  1013  	cmd := exec.Command(rktCmd, statusArgs...)
  1014  	cmd.Stdout = &outBuf
  1015  	cmd.Stderr = &errBuf
  1016  	if err := cmd.Run(); err != nil {
  1017  		if outBuf.Len() > 0 {
  1018  			logger.Debug("status output for UUID", "uuid", uuid, "error", elide(outBuf))
  1019  		}
  1020  		if errBuf.Len() == 0 {
  1021  			return nil, err
  1022  		}
  1023  		logger.Debug("status error output", "uuid", uuid, "error", elide(errBuf))
  1024  		return nil, fmt.Errorf("%s. stderr: %q", err, elide(errBuf))
  1025  	}
  1026  	var status Pod
  1027  	if err := json.Unmarshal(outBuf.Bytes(), &status); err != nil {
  1028  		return nil, err
  1029  	}
  1030  	return &status, nil
  1031  }
  1032  
  1033  // Retrieves a pod manifest
  1034  func rktGetManifest(uuid string) (*appcschema.PodManifest, error) {
  1035  	statusArgs := []string{
  1036  		"cat-manifest",
  1037  		uuid,
  1038  	}
  1039  	var outBuf bytes.Buffer
  1040  	cmd := exec.Command(rktCmd, statusArgs...)
  1041  	cmd.Stdout = &outBuf
  1042  	cmd.Stderr = ioutil.Discard
  1043  	if err := cmd.Run(); err != nil {
  1044  		return nil, err
  1045  	}
  1046  	var manifest appcschema.PodManifest
  1047  	if err := json.Unmarshal(outBuf.Bytes(), &manifest); err != nil {
  1048  		return nil, err
  1049  	}
  1050  	return &manifest, nil
  1051  }
  1052  
  1053  // Create a time with a 0 to 100ms jitter for rktGetDriverNetwork retries
  1054  func getJitteredNetworkRetryTime() time.Duration {
  1055  	return time.Duration(900+rand.Intn(100)) * time.Millisecond
  1056  }
  1057  
  1058  // Conditionally elide a buffer to an arbitrary length
  1059  func elideToLen(inBuf bytes.Buffer, length int) bytes.Buffer {
  1060  	if inBuf.Len() > length {
  1061  		inBuf.Truncate(length)
  1062  		inBuf.WriteString("...")
  1063  	}
  1064  	return inBuf
  1065  }
  1066  
  1067  // Conditionally elide a buffer to an 80 character string
  1068  func elide(inBuf bytes.Buffer) string {
  1069  	tempBuf := elideToLen(inBuf, 80)
  1070  	return tempBuf.String()
  1071  }
  1072  
  1073  func (d *Driver) handleWait(ctx context.Context, handle *taskHandle, ch chan *drivers.ExitResult) {
  1074  	defer close(ch)
  1075  	var result *drivers.ExitResult
  1076  	ps, err := handle.exec.Wait(ctx)
  1077  	if err != nil {
  1078  		result = &drivers.ExitResult{
  1079  			Err: fmt.Errorf("executor: error waiting on process: %v", err),
  1080  		}
  1081  	} else {
  1082  		result = &drivers.ExitResult{
  1083  			ExitCode: ps.ExitCode,
  1084  			Signal:   ps.Signal,
  1085  		}
  1086  	}
  1087  
  1088  	select {
  1089  	case <-ctx.Done():
  1090  	case <-d.ctx.Done():
  1091  	case ch <- result:
  1092  	}
  1093  }
  1094  
  1095  func (d *Driver) Shutdown() {
  1096  	d.signalShutdown()
  1097  }