github.com/taylorchu/nomad@v0.5.3-rc1.0.20170407200202-db11e7dd7b55/client/driver/rkt.go (about)

     1  package driver
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"log"
     8  	"net"
     9  	"os"
    10  	"os/exec"
    11  	"path/filepath"
    12  	"regexp"
    13  	"runtime"
    14  	"strconv"
    15  	"strings"
    16  	"syscall"
    17  	"time"
    18  
    19  	"github.com/hashicorp/go-plugin"
    20  	"github.com/hashicorp/go-version"
    21  	"github.com/hashicorp/nomad/client/allocdir"
    22  	"github.com/hashicorp/nomad/client/config"
    23  	"github.com/hashicorp/nomad/client/driver/executor"
    24  	dstructs "github.com/hashicorp/nomad/client/driver/structs"
    25  	cstructs "github.com/hashicorp/nomad/client/structs"
    26  	"github.com/hashicorp/nomad/helper"
    27  	"github.com/hashicorp/nomad/helper/fields"
    28  	"github.com/hashicorp/nomad/nomad/structs"
    29  	"github.com/mitchellh/mapstructure"
    30  )
    31  
    32  var (
    33  	reRktVersion  = regexp.MustCompile(`rkt [vV]ersion[:]? (\d[.\d]+)`)
    34  	reAppcVersion = regexp.MustCompile(`appc [vV]ersion[:]? (\d[.\d]+)`)
    35  )
    36  
    37  const (
    38  	// minRktVersion is the earliest supported version of rkt. rkt added support
    39  	// for CPU and memory isolators in 0.14.0. We cannot support an earlier
    40  	// version to maintain an uniform interface across all drivers
    41  	minRktVersion = "1.0.0"
    42  
    43  	// The key populated in the Node Attributes to indicate the presence of the
    44  	// Rkt driver
    45  	rktDriverAttr = "driver.rkt"
    46  
    47  	// rktVolumesConfigOption is the key for enabling the use of custom
    48  	// bind volumes.
    49  	rktVolumesConfigOption  = "rkt.volumes.enabled"
    50  	rktVolumesConfigDefault = true
    51  
    52  	// rktCmd is the command rkt is installed as.
    53  	rktCmd = "rkt"
    54  )
    55  
    56  // RktDriver is a driver for running images via Rkt
    57  // We attempt to chose sane defaults for now, with more configuration available
    58  // planned in the future
    59  type RktDriver struct {
    60  	DriverContext
    61  
    62  	// A tri-state boolean to know if the fingerprinting has happened and
    63  	// whether it has been successful
    64  	fingerprintSuccess *bool
    65  }
    66  
    67  type RktDriverConfig struct {
    68  	ImageName        string              `mapstructure:"image"`
    69  	Command          string              `mapstructure:"command"`
    70  	Args             []string            `mapstructure:"args"`
    71  	TrustPrefix      string              `mapstructure:"trust_prefix"`
    72  	DNSServers       []string            `mapstructure:"dns_servers"`        // DNS Server for containers
    73  	DNSSearchDomains []string            `mapstructure:"dns_search_domains"` // DNS Search domains for containers
    74  	Net              []string            `mapstructure:"net"`                // Networks for the containers
    75  	PortMapRaw       []map[string]string `mapstructure:"port_map"`           //
    76  	PortMap          map[string]string   `mapstructure:"-"`                  // A map of host port and the port name defined in the image manifest file
    77  	Volumes          []string            `mapstructure:"volumes"`            // Host-Volumes to mount in, syntax: /path/to/host/directory:/destination/path/in/container
    78  
    79  	Debug bool `mapstructure:"debug"` // Enable debug option for rkt command
    80  }
    81  
    82  // rktHandle is returned from Start/Open as a handle to the PID
    83  type rktHandle struct {
    84  	pluginClient   *plugin.Client
    85  	executorPid    int
    86  	executor       executor.Executor
    87  	logger         *log.Logger
    88  	killTimeout    time.Duration
    89  	maxKillTimeout time.Duration
    90  	waitCh         chan *dstructs.WaitResult
    91  	doneCh         chan struct{}
    92  }
    93  
    94  // rktPID is a struct to map the pid running the process to the vm image on
    95  // disk
    96  type rktPID struct {
    97  	PluginConfig   *PluginReattachConfig
    98  	ExecutorPid    int
    99  	KillTimeout    time.Duration
   100  	MaxKillTimeout time.Duration
   101  }
   102  
   103  // NewRktDriver is used to create a new exec driver
   104  func NewRktDriver(ctx *DriverContext) Driver {
   105  	return &RktDriver{DriverContext: *ctx}
   106  }
   107  
   108  func (d *RktDriver) FSIsolation() cstructs.FSIsolation {
   109  	return cstructs.FSIsolationImage
   110  }
   111  
   112  // Validate is used to validate the driver configuration
   113  func (d *RktDriver) Validate(config map[string]interface{}) error {
   114  	fd := &fields.FieldData{
   115  		Raw: config,
   116  		Schema: map[string]*fields.FieldSchema{
   117  			"image": &fields.FieldSchema{
   118  				Type:     fields.TypeString,
   119  				Required: true,
   120  			},
   121  			"command": &fields.FieldSchema{
   122  				Type: fields.TypeString,
   123  			},
   124  			"args": &fields.FieldSchema{
   125  				Type: fields.TypeArray,
   126  			},
   127  			"trust_prefix": &fields.FieldSchema{
   128  				Type: fields.TypeString,
   129  			},
   130  			"dns_servers": &fields.FieldSchema{
   131  				Type: fields.TypeArray,
   132  			},
   133  			"dns_search_domains": &fields.FieldSchema{
   134  				Type: fields.TypeArray,
   135  			},
   136  			"net": &fields.FieldSchema{
   137  				Type: fields.TypeArray,
   138  			},
   139  			"port_map": &fields.FieldSchema{
   140  				Type: fields.TypeArray,
   141  			},
   142  			"debug": &fields.FieldSchema{
   143  				Type: fields.TypeBool,
   144  			},
   145  			"volumes": &fields.FieldSchema{
   146  				Type: fields.TypeArray,
   147  			},
   148  		},
   149  	}
   150  
   151  	if err := fd.Validate(); err != nil {
   152  		return err
   153  	}
   154  
   155  	return nil
   156  }
   157  
   158  func (d *RktDriver) Abilities() DriverAbilities {
   159  	return DriverAbilities{
   160  		SendSignals: false,
   161  	}
   162  }
   163  
   164  func (d *RktDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) {
   165  	// Only enable if we are root when running on non-windows systems.
   166  	if runtime.GOOS != "windows" && syscall.Geteuid() != 0 {
   167  		if d.fingerprintSuccess == nil || *d.fingerprintSuccess {
   168  			d.logger.Printf("[DEBUG] driver.rkt: must run as root user, disabling")
   169  		}
   170  		delete(node.Attributes, rktDriverAttr)
   171  		d.fingerprintSuccess = helper.BoolToPtr(false)
   172  		return false, nil
   173  	}
   174  
   175  	outBytes, err := exec.Command(rktCmd, "version").Output()
   176  	if err != nil {
   177  		delete(node.Attributes, rktDriverAttr)
   178  		d.fingerprintSuccess = helper.BoolToPtr(false)
   179  		return false, nil
   180  	}
   181  	out := strings.TrimSpace(string(outBytes))
   182  
   183  	rktMatches := reRktVersion.FindStringSubmatch(out)
   184  	appcMatches := reAppcVersion.FindStringSubmatch(out)
   185  	if len(rktMatches) != 2 || len(appcMatches) != 2 {
   186  		delete(node.Attributes, rktDriverAttr)
   187  		d.fingerprintSuccess = helper.BoolToPtr(false)
   188  		return false, fmt.Errorf("Unable to parse Rkt version string: %#v", rktMatches)
   189  	}
   190  
   191  	node.Attributes[rktDriverAttr] = "1"
   192  	node.Attributes["driver.rkt.version"] = rktMatches[1]
   193  	node.Attributes["driver.rkt.appc.version"] = appcMatches[1]
   194  
   195  	minVersion, _ := version.NewVersion(minRktVersion)
   196  	currentVersion, _ := version.NewVersion(node.Attributes["driver.rkt.version"])
   197  	if currentVersion.LessThan(minVersion) {
   198  		// Do not allow ancient rkt versions
   199  		d.logger.Printf("[WARN] driver.rkt: please upgrade rkt to a version >= %s", minVersion)
   200  		node.Attributes[rktDriverAttr] = "0"
   201  	}
   202  
   203  	// Advertise if this node supports rkt volumes
   204  	if d.config.ReadBoolDefault(rktVolumesConfigOption, rktVolumesConfigDefault) {
   205  		node.Attributes["driver."+rktVolumesConfigOption] = "1"
   206  	}
   207  	d.fingerprintSuccess = helper.BoolToPtr(true)
   208  	return true, nil
   209  }
   210  
   211  func (d *RktDriver) Periodic() (bool, time.Duration) {
   212  	return true, 15 * time.Second
   213  }
   214  
   215  func (d *RktDriver) Prestart(ctx *ExecContext, task *structs.Task) (*CreatedResources, error) {
   216  	return nil, nil
   217  }
   218  
   219  // Run an existing Rkt image.
   220  func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) {
   221  	var driverConfig RktDriverConfig
   222  	if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil {
   223  		return nil, err
   224  	}
   225  
   226  	driverConfig.PortMap = mapMergeStrStr(driverConfig.PortMapRaw...)
   227  
   228  	// ACI image
   229  	img := driverConfig.ImageName
   230  
   231  	// Build the command.
   232  	var cmdArgs []string
   233  
   234  	// Add debug option to rkt command.
   235  	debug := driverConfig.Debug
   236  
   237  	// Add the given trust prefix
   238  	trustPrefix := driverConfig.TrustPrefix
   239  	insecure := false
   240  	if trustPrefix != "" {
   241  		var outBuf, errBuf bytes.Buffer
   242  		cmd := exec.Command(rktCmd, "trust", "--skip-fingerprint-review=true", fmt.Sprintf("--prefix=%s", trustPrefix), fmt.Sprintf("--debug=%t", debug))
   243  		cmd.Stdout = &outBuf
   244  		cmd.Stderr = &errBuf
   245  		if err := cmd.Run(); err != nil {
   246  			return nil, fmt.Errorf("Error running rkt trust: %s\n\nOutput: %s\n\nError: %s",
   247  				err, outBuf.String(), errBuf.String())
   248  		}
   249  		d.logger.Printf("[DEBUG] driver.rkt: added trust prefix: %q", trustPrefix)
   250  	} else {
   251  		// Disble signature verification if the trust command was not run.
   252  		insecure = true
   253  	}
   254  	cmdArgs = append(cmdArgs, "run")
   255  
   256  	// Convert underscores to dashes in task names for use in volume names #2358
   257  	sanitizedName := strings.Replace(task.Name, "_", "-", -1)
   258  
   259  	// Mount /alloc
   260  	allocVolName := fmt.Sprintf("%s-%s-alloc", d.DriverContext.allocID, sanitizedName)
   261  	cmdArgs = append(cmdArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s", allocVolName, ctx.TaskDir.SharedAllocDir))
   262  	cmdArgs = append(cmdArgs, fmt.Sprintf("--mount=volume=%s,target=%s", allocVolName, allocdir.SharedAllocContainerPath))
   263  
   264  	// Mount /local
   265  	localVolName := fmt.Sprintf("%s-%s-local", d.DriverContext.allocID, sanitizedName)
   266  	cmdArgs = append(cmdArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s", localVolName, ctx.TaskDir.LocalDir))
   267  	cmdArgs = append(cmdArgs, fmt.Sprintf("--mount=volume=%s,target=%s", localVolName, allocdir.TaskLocalContainerPath))
   268  
   269  	// Mount /secrets
   270  	secretsVolName := fmt.Sprintf("%s-%s-secrets", d.DriverContext.allocID, sanitizedName)
   271  	cmdArgs = append(cmdArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s", secretsVolName, ctx.TaskDir.SecretsDir))
   272  	cmdArgs = append(cmdArgs, fmt.Sprintf("--mount=volume=%s,target=%s", secretsVolName, allocdir.TaskSecretsContainerPath))
   273  
   274  	// Mount arbitrary volumes if enabled
   275  	if len(driverConfig.Volumes) > 0 {
   276  		if enabled := d.config.ReadBoolDefault(rktVolumesConfigOption, rktVolumesConfigDefault); !enabled {
   277  			return nil, fmt.Errorf("%s is false; cannot use rkt volumes: %+q", rktVolumesConfigOption, driverConfig.Volumes)
   278  		}
   279  		for i, rawvol := range driverConfig.Volumes {
   280  			parts := strings.Split(rawvol, ":")
   281  			if len(parts) != 2 {
   282  				return nil, fmt.Errorf("invalid rkt volume: %q", rawvol)
   283  			}
   284  			volName := fmt.Sprintf("%s-%s-%d", d.DriverContext.allocID, sanitizedName, i)
   285  			cmdArgs = append(cmdArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s", volName, parts[0]))
   286  			cmdArgs = append(cmdArgs, fmt.Sprintf("--mount=volume=%s,target=%s", volName, parts[1]))
   287  		}
   288  	}
   289  
   290  	cmdArgs = append(cmdArgs, img)
   291  	if insecure {
   292  		cmdArgs = append(cmdArgs, "--insecure-options=all")
   293  	}
   294  	cmdArgs = append(cmdArgs, fmt.Sprintf("--debug=%t", debug))
   295  
   296  	// Inject environment variables
   297  	for k, v := range d.taskEnv.EnvMap() {
   298  		cmdArgs = append(cmdArgs, fmt.Sprintf("--set-env=%v=%q", k, v))
   299  	}
   300  
   301  	// Check if the user has overridden the exec command.
   302  	if driverConfig.Command != "" {
   303  		cmdArgs = append(cmdArgs, fmt.Sprintf("--exec=%v", driverConfig.Command))
   304  	}
   305  
   306  	// Add memory isolator
   307  	cmdArgs = append(cmdArgs, fmt.Sprintf("--memory=%vM", int64(task.Resources.MemoryMB)))
   308  
   309  	// Add CPU isolator
   310  	cmdArgs = append(cmdArgs, fmt.Sprintf("--cpu=%vm", int64(task.Resources.CPU)))
   311  
   312  	// Add DNS servers
   313  	if len(driverConfig.DNSServers) == 1 && (driverConfig.DNSServers[0] == "host" || driverConfig.DNSServers[0] == "none") {
   314  		// Special case single item lists with the special values "host" or "none"
   315  		cmdArgs = append(cmdArgs, fmt.Sprintf("--dns=%s", driverConfig.DNSServers[0]))
   316  	} else {
   317  		for _, ip := range driverConfig.DNSServers {
   318  			if err := net.ParseIP(ip); err == nil {
   319  				msg := fmt.Errorf("invalid ip address for container dns server %q", ip)
   320  				d.logger.Printf("[DEBUG] driver.rkt: %v", msg)
   321  				return nil, msg
   322  			} else {
   323  				cmdArgs = append(cmdArgs, fmt.Sprintf("--dns=%s", ip))
   324  			}
   325  		}
   326  	}
   327  
   328  	// set DNS search domains
   329  	for _, domain := range driverConfig.DNSSearchDomains {
   330  		cmdArgs = append(cmdArgs, fmt.Sprintf("--dns-search=%s", domain))
   331  	}
   332  
   333  	// set network
   334  	network := strings.Join(driverConfig.Net, ",")
   335  	if network != "" {
   336  		cmdArgs = append(cmdArgs, fmt.Sprintf("--net=%s", network))
   337  	}
   338  
   339  	// Setup port mapping and exposed ports
   340  	if len(task.Resources.Networks) == 0 {
   341  		d.logger.Println("[DEBUG] driver.rkt: No network interfaces are available")
   342  		if len(driverConfig.PortMap) > 0 {
   343  			return nil, fmt.Errorf("Trying to map ports but no network interface is available")
   344  		}
   345  	} else {
   346  		// TODO add support for more than one network
   347  		network := task.Resources.Networks[0]
   348  		for _, port := range network.ReservedPorts {
   349  			var containerPort string
   350  
   351  			mapped, ok := driverConfig.PortMap[port.Label]
   352  			if !ok {
   353  				// If the user doesn't have a mapped port using port_map, driver stops running container.
   354  				return nil, fmt.Errorf("port_map is not set. When you defined port in the resources, you need to configure port_map.")
   355  			}
   356  			containerPort = mapped
   357  
   358  			hostPortStr := strconv.Itoa(port.Value)
   359  
   360  			d.logger.Printf("[DEBUG] driver.rkt: exposed port %s", containerPort)
   361  			// Add port option to rkt run arguments. rkt allows multiple port args
   362  			cmdArgs = append(cmdArgs, fmt.Sprintf("--port=%s:%s", containerPort, hostPortStr))
   363  		}
   364  
   365  		for _, port := range network.DynamicPorts {
   366  			// By default we will map the allocated port 1:1 to the container
   367  			var containerPort string
   368  
   369  			if mapped, ok := driverConfig.PortMap[port.Label]; ok {
   370  				containerPort = mapped
   371  			} else {
   372  				// If the user doesn't have mapped a port using port_map, driver stops running container.
   373  				return nil, fmt.Errorf("port_map is not set. When you defined port in the resources, you need to configure port_map.")
   374  			}
   375  
   376  			hostPortStr := strconv.Itoa(port.Value)
   377  
   378  			d.logger.Printf("[DEBUG] driver.rkt: exposed port %s", containerPort)
   379  			// Add port option to rkt run arguments. rkt allows multiple port args
   380  			cmdArgs = append(cmdArgs, fmt.Sprintf("--port=%s:%s", containerPort, hostPortStr))
   381  		}
   382  
   383  	}
   384  
   385  	// Add user passed arguments.
   386  	if len(driverConfig.Args) != 0 {
   387  		parsed := d.taskEnv.ParseAndReplace(driverConfig.Args)
   388  
   389  		// Need to start arguments with "--"
   390  		if len(parsed) > 0 {
   391  			cmdArgs = append(cmdArgs, "--")
   392  		}
   393  
   394  		for _, arg := range parsed {
   395  			cmdArgs = append(cmdArgs, fmt.Sprintf("%v", arg))
   396  		}
   397  	}
   398  
   399  	// Set the host environment variables.
   400  	filter := strings.Split(d.config.ReadDefault("env.blacklist", config.DefaultEnvBlacklist), ",")
   401  	d.taskEnv.AppendHostEnvvars(filter)
   402  
   403  	pluginLogFile := filepath.Join(ctx.TaskDir.Dir, fmt.Sprintf("%s-executor.out", task.Name))
   404  	executorConfig := &dstructs.ExecutorConfig{
   405  		LogFile:  pluginLogFile,
   406  		LogLevel: d.config.LogLevel,
   407  	}
   408  
   409  	execIntf, pluginClient, err := createExecutor(d.config.LogOutput, d.config, executorConfig)
   410  	if err != nil {
   411  		return nil, err
   412  	}
   413  	executorCtx := &executor.ExecutorContext{
   414  		TaskEnv: d.taskEnv,
   415  		Driver:  "rkt",
   416  		AllocID: d.DriverContext.allocID,
   417  		Task:    task,
   418  		TaskDir: ctx.TaskDir.Dir,
   419  		LogDir:  ctx.TaskDir.LogDir,
   420  	}
   421  	if err := execIntf.SetContext(executorCtx); err != nil {
   422  		pluginClient.Kill()
   423  		return nil, fmt.Errorf("failed to set executor context: %v", err)
   424  	}
   425  
   426  	absPath, err := GetAbsolutePath(rktCmd)
   427  	if err != nil {
   428  		return nil, err
   429  	}
   430  
   431  	execCmd := &executor.ExecCommand{
   432  		Cmd:  absPath,
   433  		Args: cmdArgs,
   434  		User: task.User,
   435  	}
   436  	ps, err := execIntf.LaunchCmd(execCmd)
   437  	if err != nil {
   438  		pluginClient.Kill()
   439  		return nil, err
   440  	}
   441  
   442  	d.logger.Printf("[DEBUG] driver.rkt: started ACI %q with: %v", img, cmdArgs)
   443  	maxKill := d.DriverContext.config.MaxKillTimeout
   444  	h := &rktHandle{
   445  		pluginClient:   pluginClient,
   446  		executor:       execIntf,
   447  		executorPid:    ps.Pid,
   448  		logger:         d.logger,
   449  		killTimeout:    GetKillTimeout(task.KillTimeout, maxKill),
   450  		maxKillTimeout: maxKill,
   451  		doneCh:         make(chan struct{}),
   452  		waitCh:         make(chan *dstructs.WaitResult, 1),
   453  	}
   454  	if err := h.executor.SyncServices(consulContext(d.config, "")); err != nil {
   455  		h.logger.Printf("[ERR] driver.rkt: error registering services for task: %q: %v", task.Name, err)
   456  	}
   457  	go h.run()
   458  	return h, nil
   459  }
   460  
   461  func (d *RktDriver) Cleanup(*ExecContext, *CreatedResources) error { return nil }
   462  
   463  func (d *RktDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) {
   464  	// Parse the handle
   465  	pidBytes := []byte(strings.TrimPrefix(handleID, "Rkt:"))
   466  	id := &rktPID{}
   467  	if err := json.Unmarshal(pidBytes, id); err != nil {
   468  		return nil, fmt.Errorf("failed to parse Rkt handle '%s': %v", handleID, err)
   469  	}
   470  
   471  	pluginConfig := &plugin.ClientConfig{
   472  		Reattach: id.PluginConfig.PluginConfig(),
   473  	}
   474  	exec, pluginClient, err := createExecutorWithConfig(pluginConfig, d.config.LogOutput)
   475  	if err != nil {
   476  		d.logger.Println("[ERROR] driver.rkt: error connecting to plugin so destroying plugin pid and user pid")
   477  		if e := destroyPlugin(id.PluginConfig.Pid, id.ExecutorPid); e != nil {
   478  			d.logger.Printf("[ERROR] driver.rkt: error destroying plugin and executor pid: %v", e)
   479  		}
   480  		return nil, fmt.Errorf("error connecting to plugin: %v", err)
   481  	}
   482  
   483  	ver, _ := exec.Version()
   484  	d.logger.Printf("[DEBUG] driver.rkt: version of executor: %v", ver.Version)
   485  	// Return a driver handle
   486  	h := &rktHandle{
   487  		pluginClient:   pluginClient,
   488  		executorPid:    id.ExecutorPid,
   489  		executor:       exec,
   490  		logger:         d.logger,
   491  		killTimeout:    id.KillTimeout,
   492  		maxKillTimeout: id.MaxKillTimeout,
   493  		doneCh:         make(chan struct{}),
   494  		waitCh:         make(chan *dstructs.WaitResult, 1),
   495  	}
   496  	if err := h.executor.SyncServices(consulContext(d.config, "")); err != nil {
   497  		h.logger.Printf("[ERR] driver.rkt: error registering services: %v", err)
   498  	}
   499  	go h.run()
   500  	return h, nil
   501  }
   502  
   503  func (h *rktHandle) ID() string {
   504  	// Return a handle to the PID
   505  	pid := &rktPID{
   506  		PluginConfig:   NewPluginReattachConfig(h.pluginClient.ReattachConfig()),
   507  		KillTimeout:    h.killTimeout,
   508  		MaxKillTimeout: h.maxKillTimeout,
   509  		ExecutorPid:    h.executorPid,
   510  	}
   511  	data, err := json.Marshal(pid)
   512  	if err != nil {
   513  		h.logger.Printf("[ERR] driver.rkt: failed to marshal rkt PID to JSON: %s", err)
   514  	}
   515  	return fmt.Sprintf("Rkt:%s", string(data))
   516  }
   517  
   518  func (h *rktHandle) WaitCh() chan *dstructs.WaitResult {
   519  	return h.waitCh
   520  }
   521  
   522  func (h *rktHandle) Update(task *structs.Task) error {
   523  	// Store the updated kill timeout.
   524  	h.killTimeout = GetKillTimeout(task.KillTimeout, h.maxKillTimeout)
   525  	h.executor.UpdateTask(task)
   526  
   527  	// Update is not possible
   528  	return nil
   529  }
   530  
   531  func (h *rktHandle) Signal(s os.Signal) error {
   532  	return fmt.Errorf("Rkt does not support signals")
   533  }
   534  
   535  // Kill is used to terminate the task. We send an Interrupt
   536  // and then provide a 5 second grace period before doing a Kill.
   537  func (h *rktHandle) Kill() error {
   538  	h.executor.ShutDown()
   539  	select {
   540  	case <-h.doneCh:
   541  		return nil
   542  	case <-time.After(h.killTimeout):
   543  		return h.executor.Exit()
   544  	}
   545  }
   546  
   547  func (h *rktHandle) Stats() (*cstructs.TaskResourceUsage, error) {
   548  	return nil, DriverStatsNotImplemented
   549  }
   550  
   551  func (h *rktHandle) run() {
   552  	ps, werr := h.executor.Wait()
   553  	close(h.doneCh)
   554  	if ps.ExitCode == 0 && werr != nil {
   555  		if e := killProcess(h.executorPid); e != nil {
   556  			h.logger.Printf("[ERROR] driver.rkt: error killing user process: %v", e)
   557  		}
   558  	}
   559  	// Remove services
   560  	if err := h.executor.DeregisterServices(); err != nil {
   561  		h.logger.Printf("[ERR] driver.rkt: failed to deregister services: %v", err)
   562  	}
   563  
   564  	// Exit the executor
   565  	if err := h.executor.Exit(); err != nil {
   566  		h.logger.Printf("[ERR] driver.rkt: error killing executor: %v", err)
   567  	}
   568  	h.pluginClient.Kill()
   569  
   570  	// Send the results
   571  	h.waitCh <- dstructs.NewWaitResult(ps.ExitCode, 0, werr)
   572  	close(h.waitCh)
   573  }