github.com/jrxfive/nomad@v0.6.1-0.20170802162750-1fef470e89bf/client/driver/rkt.go (about)

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