github.com/djenriquez/nomad-1@v0.8.1/client/driver/rkt.go (about)

     1  // +build linux
     2  
     3  package driver
     4  
     5  import (
     6  	"bytes"
     7  	"context"
     8  	"encoding/json"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"log"
    12  	"math/rand"
    13  	"net"
    14  	"os"
    15  	"os/exec"
    16  	"path/filepath"
    17  	"regexp"
    18  	"runtime"
    19  	"strconv"
    20  	"strings"
    21  	"syscall"
    22  	"time"
    23  
    24  	appcschema "github.com/appc/spec/schema"
    25  	rktv1 "github.com/rkt/rkt/api/v1"
    26  
    27  	"github.com/hashicorp/go-plugin"
    28  	"github.com/hashicorp/go-version"
    29  	"github.com/hashicorp/nomad/client/allocdir"
    30  	"github.com/hashicorp/nomad/client/config"
    31  	"github.com/hashicorp/nomad/client/driver/env"
    32  	"github.com/hashicorp/nomad/client/driver/executor"
    33  	dstructs "github.com/hashicorp/nomad/client/driver/structs"
    34  	cstructs "github.com/hashicorp/nomad/client/structs"
    35  	"github.com/hashicorp/nomad/helper"
    36  	"github.com/hashicorp/nomad/helper/fields"
    37  	"github.com/hashicorp/nomad/nomad/structs"
    38  	"github.com/mitchellh/mapstructure"
    39  )
    40  
    41  var (
    42  	reRktVersion  = regexp.MustCompile(`rkt [vV]ersion[:]? (\d[.\d]+)`)
    43  	reAppcVersion = regexp.MustCompile(`appc [vV]ersion[:]? (\d[.\d]+)`)
    44  )
    45  
    46  const (
    47  	// minRktVersion is the earliest supported version of rkt. rkt added support
    48  	// for CPU and memory isolators in 0.14.0. We cannot support an earlier
    49  	// version to maintain an uniform interface across all drivers
    50  	minRktVersion = "1.27.0"
    51  
    52  	// The key populated in the Node Attributes to indicate the presence of the
    53  	// Rkt driver
    54  	rktDriverAttr = "driver.rkt"
    55  
    56  	// rktVolumesConfigOption is the key for enabling the use of custom
    57  	// bind volumes.
    58  	rktVolumesConfigOption  = "rkt.volumes.enabled"
    59  	rktVolumesConfigDefault = true
    60  
    61  	// rktCmd is the command rkt is installed as.
    62  	rktCmd = "rkt"
    63  
    64  	// rktNetworkDeadline is how long to wait for container network to start
    65  	rktNetworkDeadline = 1 * time.Minute
    66  )
    67  
    68  // RktDriver is a driver for running images via Rkt
    69  // We attempt to chose sane defaults for now, with more configuration available
    70  // planned in the future
    71  type RktDriver struct {
    72  	DriverContext
    73  
    74  	// A tri-state boolean to know if the fingerprinting has happened and
    75  	// whether it has been successful
    76  	fingerprintSuccess *bool
    77  }
    78  
    79  type RktDriverConfig struct {
    80  	ImageName        string              `mapstructure:"image"`
    81  	Command          string              `mapstructure:"command"`
    82  	Args             []string            `mapstructure:"args"`
    83  	TrustPrefix      string              `mapstructure:"trust_prefix"`
    84  	DNSServers       []string            `mapstructure:"dns_servers"`        // DNS Server for containers
    85  	DNSSearchDomains []string            `mapstructure:"dns_search_domains"` // DNS Search domains for containers
    86  	Net              []string            `mapstructure:"net"`                // Networks for the containers
    87  	PortMapRaw       []map[string]string `mapstructure:"port_map"`           //
    88  	PortMap          map[string]string   `mapstructure:"-"`                  // A map of host port and the port name defined in the image manifest file
    89  	Volumes          []string            `mapstructure:"volumes"`            // Host-Volumes to mount in, syntax: /path/to/host/directory:/destination/path/in/container[:readOnly]
    90  	InsecureOptions  []string            `mapstructure:"insecure_options"`   // list of args for --insecure-options
    91  
    92  	NoOverlay bool   `mapstructure:"no_overlay"` // disable overlayfs for rkt run
    93  	Debug     bool   `mapstructure:"debug"`      // Enable debug option for rkt command
    94  	Group     string `mapstructure:"group"`      // Group override for the container
    95  }
    96  
    97  // rktHandle is returned from Start/Open as a handle to the PID
    98  type rktHandle struct {
    99  	uuid           string
   100  	env            *env.TaskEnv
   101  	taskDir        *allocdir.TaskDir
   102  	pluginClient   *plugin.Client
   103  	executorPid    int
   104  	executor       executor.Executor
   105  	logger         *log.Logger
   106  	killTimeout    time.Duration
   107  	maxKillTimeout time.Duration
   108  	waitCh         chan *dstructs.WaitResult
   109  	doneCh         chan struct{}
   110  }
   111  
   112  // rktPID is a struct to map the pid running the process to the vm image on
   113  // disk
   114  type rktPID struct {
   115  	UUID           string
   116  	PluginConfig   *PluginReattachConfig
   117  	ExecutorPid    int
   118  	KillTimeout    time.Duration
   119  	MaxKillTimeout time.Duration
   120  }
   121  
   122  // Retrieve pod status for the pod with the given UUID.
   123  func rktGetStatus(uuid string, logger *log.Logger) (*rktv1.Pod, error) {
   124  	statusArgs := []string{
   125  		"status",
   126  		"--format=json",
   127  		uuid,
   128  	}
   129  	var outBuf, errBuf bytes.Buffer
   130  	cmd := exec.Command(rktCmd, statusArgs...)
   131  	cmd.Stdout = &outBuf
   132  	cmd.Stderr = &errBuf
   133  	if err := cmd.Run(); err != nil {
   134  		if outBuf.Len() > 0 {
   135  			logger.Printf("[DEBUG] driver.rkt: status output for UUID %s: %q", uuid, elide(outBuf))
   136  		}
   137  		if errBuf.Len() == 0 {
   138  			return nil, err
   139  		}
   140  		logger.Printf("[DEBUG] driver.rkt: status error output for UUID %s: %q", uuid, elide(errBuf))
   141  		return nil, fmt.Errorf("%s. stderr: %q", err, elide(errBuf))
   142  	}
   143  	var status rktv1.Pod
   144  	if err := json.Unmarshal(outBuf.Bytes(), &status); err != nil {
   145  		return nil, err
   146  	}
   147  	return &status, nil
   148  }
   149  
   150  // Retrieves a pod manifest
   151  func rktGetManifest(uuid string) (*appcschema.PodManifest, error) {
   152  	statusArgs := []string{
   153  		"cat-manifest",
   154  		uuid,
   155  	}
   156  	var outBuf bytes.Buffer
   157  	cmd := exec.Command(rktCmd, statusArgs...)
   158  	cmd.Stdout = &outBuf
   159  	cmd.Stderr = ioutil.Discard
   160  	if err := cmd.Run(); err != nil {
   161  		return nil, err
   162  	}
   163  	var manifest appcschema.PodManifest
   164  	if err := json.Unmarshal(outBuf.Bytes(), &manifest); err != nil {
   165  		return nil, err
   166  	}
   167  	return &manifest, nil
   168  }
   169  
   170  func rktGetDriverNetwork(uuid string, driverConfigPortMap map[string]string, logger *log.Logger) (*cstructs.DriverNetwork, error) {
   171  	deadline := time.Now().Add(rktNetworkDeadline)
   172  	var lastErr error
   173  	try := 0
   174  
   175  	for time.Now().Before(deadline) {
   176  		try++
   177  		if status, err := rktGetStatus(uuid, logger); err == nil {
   178  			for _, net := range status.Networks {
   179  				if !net.IP.IsGlobalUnicast() {
   180  					continue
   181  				}
   182  
   183  				// Get the pod manifest so we can figure out which ports are exposed
   184  				var portmap map[string]int
   185  				manifest, err := rktGetManifest(uuid)
   186  				if err == nil {
   187  					portmap, err = rktManifestMakePortMap(manifest, driverConfigPortMap)
   188  					if err != nil {
   189  						lastErr = fmt.Errorf("could not create manifest-based portmap: %v", err)
   190  						return nil, lastErr
   191  					}
   192  				} else {
   193  					lastErr = fmt.Errorf("could not get pod manifest: %v", err)
   194  					return nil, lastErr
   195  				}
   196  
   197  				// This is a successful landing.
   198  				return &cstructs.DriverNetwork{
   199  					PortMap: portmap,
   200  					IP:      status.Networks[0].IP.String(),
   201  				}, nil
   202  			}
   203  
   204  			if len(status.Networks) == 0 {
   205  				lastErr = fmt.Errorf("no networks found")
   206  			} else {
   207  				lastErr = fmt.Errorf("no good driver networks out of %d returned", len(status.Networks))
   208  			}
   209  		} else {
   210  			lastErr = fmt.Errorf("getting status failed: %v", err)
   211  		}
   212  
   213  		waitTime := getJitteredNetworkRetryTime()
   214  		logger.Printf("[DEBUG] driver.rkt: getting network info for pod UUID %s failed attempt %d: %v. Sleeping for %v", uuid, try, lastErr, waitTime)
   215  		time.Sleep(waitTime)
   216  	}
   217  	return nil, fmt.Errorf("timed out, last error: %v", lastErr)
   218  }
   219  
   220  // Given a rkt/appc pod manifest and driver portmap configuration, create
   221  // a driver portmap.
   222  func rktManifestMakePortMap(manifest *appcschema.PodManifest, configPortMap map[string]string) (map[string]int, error) {
   223  	if len(manifest.Apps) == 0 {
   224  		return nil, fmt.Errorf("manifest has no apps")
   225  	}
   226  	if len(manifest.Apps) != 1 {
   227  		return nil, fmt.Errorf("manifest has multiple apps!")
   228  	}
   229  	app := manifest.Apps[0]
   230  	if app.App == nil {
   231  		return nil, fmt.Errorf("specified app has no App object")
   232  	}
   233  
   234  	portMap := make(map[string]int)
   235  	for svc, name := range configPortMap {
   236  		for _, port := range app.App.Ports {
   237  			if port.Name.String() == name {
   238  				portMap[svc] = int(port.Port)
   239  			}
   240  		}
   241  	}
   242  	return portMap, nil
   243  }
   244  
   245  // rktRemove pod after it has exited.
   246  func rktRemove(uuid string) error {
   247  	errBuf := &bytes.Buffer{}
   248  	cmd := exec.Command(rktCmd, "rm", uuid)
   249  	cmd.Stdout = ioutil.Discard
   250  	cmd.Stderr = errBuf
   251  	if err := cmd.Run(); err != nil {
   252  		if msg := errBuf.String(); len(msg) > 0 {
   253  			return fmt.Errorf("error removing pod: %s", msg)
   254  		}
   255  		return err
   256  	}
   257  
   258  	return nil
   259  }
   260  
   261  // NewRktDriver is used to create a new rkt driver
   262  func NewRktDriver(ctx *DriverContext) Driver {
   263  	return &RktDriver{DriverContext: *ctx}
   264  }
   265  
   266  func (d *RktDriver) FSIsolation() cstructs.FSIsolation {
   267  	return cstructs.FSIsolationImage
   268  }
   269  
   270  // Validate is used to validate the driver configuration
   271  func (d *RktDriver) Validate(config map[string]interface{}) error {
   272  	fd := &fields.FieldData{
   273  		Raw: config,
   274  		Schema: map[string]*fields.FieldSchema{
   275  			"image": {
   276  				Type:     fields.TypeString,
   277  				Required: true,
   278  			},
   279  			"command": {
   280  				Type: fields.TypeString,
   281  			},
   282  			"args": {
   283  				Type: fields.TypeArray,
   284  			},
   285  			"trust_prefix": {
   286  				Type: fields.TypeString,
   287  			},
   288  			"dns_servers": {
   289  				Type: fields.TypeArray,
   290  			},
   291  			"dns_search_domains": {
   292  				Type: fields.TypeArray,
   293  			},
   294  			"net": {
   295  				Type: fields.TypeArray,
   296  			},
   297  			"port_map": {
   298  				Type: fields.TypeArray,
   299  			},
   300  			"debug": {
   301  				Type: fields.TypeBool,
   302  			},
   303  			"volumes": {
   304  				Type: fields.TypeArray,
   305  			},
   306  			"no_overlay": {
   307  				Type: fields.TypeBool,
   308  			},
   309  			"insecure_options": {
   310  				Type: fields.TypeArray,
   311  			},
   312  			"group": {
   313  				Type: fields.TypeString,
   314  			},
   315  		},
   316  	}
   317  
   318  	if err := fd.Validate(); err != nil {
   319  		return err
   320  	}
   321  
   322  	return nil
   323  }
   324  
   325  func (d *RktDriver) Abilities() DriverAbilities {
   326  	return DriverAbilities{
   327  		SendSignals: false,
   328  		Exec:        true,
   329  	}
   330  }
   331  
   332  func (d *RktDriver) Fingerprint(req *cstructs.FingerprintRequest, resp *cstructs.FingerprintResponse) error {
   333  	// Only enable if we are root when running on non-windows systems.
   334  	if runtime.GOOS != "windows" && syscall.Geteuid() != 0 {
   335  		if d.fingerprintSuccess == nil || *d.fingerprintSuccess {
   336  			d.logger.Printf("[DEBUG] driver.rkt: must run as root user, disabling")
   337  		}
   338  		d.fingerprintSuccess = helper.BoolToPtr(false)
   339  		resp.RemoveAttribute(rktDriverAttr)
   340  		return nil
   341  	}
   342  
   343  	outBytes, err := exec.Command(rktCmd, "version").Output()
   344  	if err != nil {
   345  		d.fingerprintSuccess = helper.BoolToPtr(false)
   346  		return nil
   347  	}
   348  	out := strings.TrimSpace(string(outBytes))
   349  
   350  	rktMatches := reRktVersion.FindStringSubmatch(out)
   351  	appcMatches := reAppcVersion.FindStringSubmatch(out)
   352  	if len(rktMatches) != 2 || len(appcMatches) != 2 {
   353  		d.fingerprintSuccess = helper.BoolToPtr(false)
   354  		resp.RemoveAttribute(rktDriverAttr)
   355  		return fmt.Errorf("Unable to parse Rkt version string: %#v", rktMatches)
   356  	}
   357  
   358  	minVersion, _ := version.NewVersion(minRktVersion)
   359  	currentVersion, _ := version.NewVersion(rktMatches[1])
   360  	if currentVersion.LessThan(minVersion) {
   361  		// Do not allow ancient rkt versions
   362  		if d.fingerprintSuccess == nil {
   363  			// Only log on first failure
   364  			d.logger.Printf("[WARN] driver.rkt: unsupported rkt version %s; please upgrade to >= %s",
   365  				currentVersion, minVersion)
   366  		}
   367  		d.fingerprintSuccess = helper.BoolToPtr(false)
   368  		resp.RemoveAttribute(rktDriverAttr)
   369  		return nil
   370  	}
   371  
   372  	// Output version information when the fingerprinter first sees rkt
   373  	if info, ok := req.Node.Drivers["rkt"]; ok && info != nil && !info.Detected {
   374  		d.logger.Printf("[DEBUG] driver.rkt: detect version: %s", strings.Replace(out, "\n", " ", -1))
   375  	}
   376  	resp.AddAttribute(rktDriverAttr, "1")
   377  	resp.AddAttribute("driver.rkt.version", rktMatches[1])
   378  	resp.AddAttribute("driver.rkt.appc.version", appcMatches[1])
   379  	resp.Detected = true
   380  
   381  	// Advertise if this node supports rkt volumes
   382  	if d.config.ReadBoolDefault(rktVolumesConfigOption, rktVolumesConfigDefault) {
   383  		resp.AddAttribute("driver."+rktVolumesConfigOption, "1")
   384  	}
   385  	d.fingerprintSuccess = helper.BoolToPtr(true)
   386  	return nil
   387  }
   388  
   389  func (d *RktDriver) Periodic() (bool, time.Duration) {
   390  	return true, 15 * time.Second
   391  }
   392  
   393  func (d *RktDriver) Prestart(ctx *ExecContext, task *structs.Task) (*PrestartResponse, error) {
   394  	return nil, nil
   395  }
   396  
   397  // Run an existing Rkt image.
   398  func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (*StartResponse, error) {
   399  	var driverConfig RktDriverConfig
   400  	if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil {
   401  		return nil, err
   402  	}
   403  
   404  	driverConfig.PortMap = mapMergeStrStr(driverConfig.PortMapRaw...)
   405  
   406  	// ACI image
   407  	img := driverConfig.ImageName
   408  
   409  	// Global arguments given to both prepare and run-prepared
   410  	globalArgs := make([]string, 0, 50)
   411  
   412  	// Add debug option to rkt command.
   413  	debug := driverConfig.Debug
   414  
   415  	// Add the given trust prefix
   416  	trustPrefix := driverConfig.TrustPrefix
   417  	insecure := false
   418  	if trustPrefix != "" {
   419  		var outBuf, errBuf bytes.Buffer
   420  		cmd := exec.Command(rktCmd, "trust", "--skip-fingerprint-review=true", fmt.Sprintf("--prefix=%s", trustPrefix), fmt.Sprintf("--debug=%t", debug))
   421  		cmd.Stdout = &outBuf
   422  		cmd.Stderr = &errBuf
   423  		if err := cmd.Run(); err != nil {
   424  			return nil, fmt.Errorf("Error running rkt trust: %s\n\nOutput: %s\n\nError: %s",
   425  				err, outBuf.String(), errBuf.String())
   426  		}
   427  		d.logger.Printf("[DEBUG] driver.rkt: added trust prefix: %q", trustPrefix)
   428  	} else {
   429  		// Disable signature verification if the trust command was not run.
   430  		insecure = true
   431  	}
   432  
   433  	// if we have a selective insecure_options, prefer them
   434  	// insecure options are rkt's global argument, so we do this before the actual "run"
   435  	if len(driverConfig.InsecureOptions) > 0 {
   436  		globalArgs = append(globalArgs, fmt.Sprintf("--insecure-options=%s", strings.Join(driverConfig.InsecureOptions, ",")))
   437  	} else if insecure {
   438  		globalArgs = append(globalArgs, "--insecure-options=all")
   439  	}
   440  
   441  	// debug is rkt's global argument, so add it before the actual "run"
   442  	globalArgs = append(globalArgs, fmt.Sprintf("--debug=%t", debug))
   443  
   444  	prepareArgs := make([]string, 0, 50)
   445  	runArgs := make([]string, 0, 50)
   446  
   447  	prepareArgs = append(prepareArgs, globalArgs...)
   448  	prepareArgs = append(prepareArgs, "prepare")
   449  	runArgs = append(runArgs, globalArgs...)
   450  	runArgs = append(runArgs, "run-prepared")
   451  
   452  	// disable overlayfs
   453  	if driverConfig.NoOverlay {
   454  		prepareArgs = append(prepareArgs, "--no-overlay=true")
   455  	}
   456  
   457  	// Convert underscores to dashes in task names for use in volume names #2358
   458  	sanitizedName := strings.Replace(task.Name, "_", "-", -1)
   459  
   460  	// Mount /alloc
   461  	allocVolName := fmt.Sprintf("%s-%s-alloc", d.DriverContext.allocID, sanitizedName)
   462  	prepareArgs = append(prepareArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s", allocVolName, ctx.TaskDir.SharedAllocDir))
   463  	prepareArgs = append(prepareArgs, fmt.Sprintf("--mount=volume=%s,target=%s", allocVolName, ctx.TaskEnv.EnvMap[env.AllocDir]))
   464  
   465  	// Mount /local
   466  	localVolName := fmt.Sprintf("%s-%s-local", d.DriverContext.allocID, sanitizedName)
   467  	prepareArgs = append(prepareArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s", localVolName, ctx.TaskDir.LocalDir))
   468  	prepareArgs = append(prepareArgs, fmt.Sprintf("--mount=volume=%s,target=%s", localVolName, ctx.TaskEnv.EnvMap[env.TaskLocalDir]))
   469  
   470  	// Mount /secrets
   471  	secretsVolName := fmt.Sprintf("%s-%s-secrets", d.DriverContext.allocID, sanitizedName)
   472  	prepareArgs = append(prepareArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s", secretsVolName, ctx.TaskDir.SecretsDir))
   473  	prepareArgs = append(prepareArgs, fmt.Sprintf("--mount=volume=%s,target=%s", secretsVolName, ctx.TaskEnv.EnvMap[env.SecretsDir]))
   474  
   475  	// Mount arbitrary volumes if enabled
   476  	if len(driverConfig.Volumes) > 0 {
   477  		if enabled := d.config.ReadBoolDefault(rktVolumesConfigOption, rktVolumesConfigDefault); !enabled {
   478  			return nil, fmt.Errorf("%s is false; cannot use rkt volumes: %+q", rktVolumesConfigOption, driverConfig.Volumes)
   479  		}
   480  		for i, rawvol := range driverConfig.Volumes {
   481  			parts := strings.Split(rawvol, ":")
   482  			readOnly := "false"
   483  			// job spec:
   484  			//   volumes = ["/host/path:/container/path[:readOnly]"]
   485  			// the third parameter is optional, mount is read-write by default
   486  			if len(parts) == 3 {
   487  				if parts[2] == "readOnly" {
   488  					d.logger.Printf("[DEBUG] Mounting %s:%s as readOnly", parts[0], parts[1])
   489  					readOnly = "true"
   490  				} else {
   491  					d.logger.Printf("[WARN] Unknown volume parameter '%s' ignored for mount %s", parts[2], parts[0])
   492  				}
   493  			} else if len(parts) != 2 {
   494  				return nil, fmt.Errorf("invalid rkt volume: %q", rawvol)
   495  			}
   496  			volName := fmt.Sprintf("%s-%s-%d", d.DriverContext.allocID, sanitizedName, i)
   497  			prepareArgs = append(prepareArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s,readOnly=%s", volName, parts[0], readOnly))
   498  			prepareArgs = append(prepareArgs, fmt.Sprintf("--mount=volume=%s,target=%s", volName, parts[1]))
   499  		}
   500  	}
   501  
   502  	// Inject environment variables
   503  	for k, v := range ctx.TaskEnv.Map() {
   504  		prepareArgs = append(prepareArgs, fmt.Sprintf("--set-env=%s=%s", k, v))
   505  	}
   506  
   507  	// Image is set here, because the commands that follow apply to it
   508  	prepareArgs = append(prepareArgs, img)
   509  
   510  	// Check if the user has overridden the exec command.
   511  	if driverConfig.Command != "" {
   512  		prepareArgs = append(prepareArgs, fmt.Sprintf("--exec=%v", driverConfig.Command))
   513  	}
   514  
   515  	// Add memory isolator
   516  	prepareArgs = append(prepareArgs, fmt.Sprintf("--memory=%vM", int64(task.Resources.MemoryMB)))
   517  
   518  	// Add CPU isolator
   519  	prepareArgs = append(prepareArgs, fmt.Sprintf("--cpu=%vm", int64(task.Resources.CPU)))
   520  
   521  	// Add DNS servers
   522  	if len(driverConfig.DNSServers) == 1 && (driverConfig.DNSServers[0] == "host" || driverConfig.DNSServers[0] == "none") {
   523  		// Special case single item lists with the special values "host" or "none"
   524  		runArgs = append(runArgs, fmt.Sprintf("--dns=%s", driverConfig.DNSServers[0]))
   525  	} else {
   526  		for _, ip := range driverConfig.DNSServers {
   527  			if err := net.ParseIP(ip); err == nil {
   528  				msg := fmt.Errorf("invalid ip address for container dns server %q", ip)
   529  				d.logger.Printf("[DEBUG] driver.rkt: %v", msg)
   530  				return nil, msg
   531  			} else {
   532  				runArgs = append(runArgs, fmt.Sprintf("--dns=%s", ip))
   533  			}
   534  		}
   535  	}
   536  
   537  	// set DNS search domains
   538  	for _, domain := range driverConfig.DNSSearchDomains {
   539  		runArgs = append(runArgs, fmt.Sprintf("--dns-search=%s", domain))
   540  	}
   541  
   542  	// set network
   543  	network := strings.Join(driverConfig.Net, ",")
   544  	if network != "" {
   545  		runArgs = append(runArgs, fmt.Sprintf("--net=%s", network))
   546  	}
   547  
   548  	// Setup port mapping and exposed ports
   549  	if len(task.Resources.Networks) == 0 {
   550  		d.logger.Println("[DEBUG] driver.rkt: No network interfaces are available")
   551  		if len(driverConfig.PortMap) > 0 {
   552  			return nil, fmt.Errorf("Trying to map ports but no network interface is available")
   553  		}
   554  	} else if network == "host" {
   555  		// Port mapping is skipped when host networking is used.
   556  		d.logger.Println("[DEBUG] driver.rkt: Ignoring port_map when using --net=host")
   557  	} else {
   558  		// TODO add support for more than one network
   559  		network := task.Resources.Networks[0]
   560  		for _, port := range network.ReservedPorts {
   561  			var containerPort string
   562  
   563  			mapped, ok := driverConfig.PortMap[port.Label]
   564  			if !ok {
   565  				// If the user doesn't have a mapped port using port_map, driver stops running container.
   566  				return nil, fmt.Errorf("port_map is not set. When you defined port in the resources, you need to configure port_map.")
   567  			}
   568  			containerPort = mapped
   569  
   570  			hostPortStr := strconv.Itoa(port.Value)
   571  
   572  			d.logger.Printf("[DEBUG] driver.rkt: exposed port %s", containerPort)
   573  			// Add port option to rkt run arguments. rkt allows multiple port args
   574  			prepareArgs = append(prepareArgs, fmt.Sprintf("--port=%s:%s", containerPort, hostPortStr))
   575  		}
   576  
   577  		for _, port := range network.DynamicPorts {
   578  			// By default we will map the allocated port 1:1 to the container
   579  			var containerPort string
   580  
   581  			if mapped, ok := driverConfig.PortMap[port.Label]; ok {
   582  				containerPort = mapped
   583  			} else {
   584  				// If the user doesn't have mapped a port using port_map, driver stops running container.
   585  				return nil, fmt.Errorf("port_map is not set. When you defined port in the resources, you need to configure port_map.")
   586  			}
   587  
   588  			hostPortStr := strconv.Itoa(port.Value)
   589  
   590  			d.logger.Printf("[DEBUG] driver.rkt: exposed port %s", containerPort)
   591  			// Add port option to rkt run arguments. rkt allows multiple port args
   592  			prepareArgs = append(prepareArgs, fmt.Sprintf("--port=%s:%s", containerPort, hostPortStr))
   593  		}
   594  
   595  	}
   596  
   597  	// If a user has been specified for the task, pass it through to the user
   598  	if task.User != "" {
   599  		prepareArgs = append(prepareArgs, fmt.Sprintf("--user=%s", task.User))
   600  	}
   601  
   602  	// There's no task-level parameter for groups so check the driver
   603  	// config for a custom group
   604  	if driverConfig.Group != "" {
   605  		prepareArgs = append(prepareArgs, fmt.Sprintf("--group=%s", driverConfig.Group))
   606  	}
   607  
   608  	// Add user passed arguments.
   609  	if len(driverConfig.Args) != 0 {
   610  		parsed := ctx.TaskEnv.ParseAndReplace(driverConfig.Args)
   611  
   612  		// Need to start arguments with "--"
   613  		if len(parsed) > 0 {
   614  			prepareArgs = append(prepareArgs, "--")
   615  		}
   616  
   617  		for _, arg := range parsed {
   618  			prepareArgs = append(prepareArgs, fmt.Sprintf("%v", arg))
   619  		}
   620  	}
   621  
   622  	pluginLogFile := filepath.Join(ctx.TaskDir.Dir, fmt.Sprintf("%s-executor.out", task.Name))
   623  	executorConfig := &dstructs.ExecutorConfig{
   624  		LogFile:  pluginLogFile,
   625  		LogLevel: d.config.LogLevel,
   626  	}
   627  
   628  	execIntf, pluginClient, err := createExecutor(d.config.LogOutput, d.config, executorConfig)
   629  	if err != nil {
   630  		return nil, err
   631  	}
   632  
   633  	absPath, err := GetAbsolutePath(rktCmd)
   634  	if err != nil {
   635  		return nil, err
   636  	}
   637  
   638  	var outBuf, errBuf bytes.Buffer
   639  	cmd := exec.Command(rktCmd, prepareArgs...)
   640  	cmd.Stdout = &outBuf
   641  	cmd.Stderr = &errBuf
   642  	d.logger.Printf("[DEBUG] driver.rkt: preparing pod %q for task %q with: %v", img, d.taskName, prepareArgs)
   643  	if err := cmd.Run(); err != nil {
   644  		return nil, fmt.Errorf("Error preparing rkt pod: %s\n\nOutput: %s\n\nError: %s",
   645  			err, outBuf.String(), errBuf.String())
   646  	}
   647  	uuid := strings.TrimSpace(outBuf.String())
   648  	d.logger.Printf("[DEBUG] driver.rkt: pod %q for task %q prepared. (UUID: %s)", img, d.taskName, uuid)
   649  	runArgs = append(runArgs, uuid)
   650  
   651  	// The task's environment is set via --set-env flags above, but the rkt
   652  	// command itself needs an evironment with PATH set to find iptables.
   653  	eb := env.NewEmptyBuilder()
   654  	filter := strings.Split(d.config.ReadDefault("env.blacklist", config.DefaultEnvBlacklist), ",")
   655  	rktEnv := eb.SetHostEnvvars(filter).Build()
   656  	executorCtx := &executor.ExecutorContext{
   657  		TaskEnv: rktEnv,
   658  		Driver:  "rkt",
   659  		Task:    task,
   660  		TaskDir: ctx.TaskDir.Dir,
   661  		LogDir:  ctx.TaskDir.LogDir,
   662  	}
   663  	if err := execIntf.SetContext(executorCtx); err != nil {
   664  		pluginClient.Kill()
   665  		return nil, fmt.Errorf("failed to set executor context: %v", err)
   666  	}
   667  
   668  	execCmd := &executor.ExecCommand{
   669  		Cmd:  absPath,
   670  		Args: runArgs,
   671  	}
   672  	ps, err := execIntf.LaunchCmd(execCmd)
   673  	if err != nil {
   674  		pluginClient.Kill()
   675  		return nil, err
   676  	}
   677  
   678  	d.logger.Printf("[DEBUG] driver.rkt: started ACI %q (UUID: %s) for task %q with: %v", img, uuid, d.taskName, runArgs)
   679  	maxKill := d.DriverContext.config.MaxKillTimeout
   680  	h := &rktHandle{
   681  		uuid:           uuid,
   682  		env:            rktEnv,
   683  		taskDir:        ctx.TaskDir,
   684  		pluginClient:   pluginClient,
   685  		executor:       execIntf,
   686  		executorPid:    ps.Pid,
   687  		logger:         d.logger,
   688  		killTimeout:    GetKillTimeout(task.KillTimeout, maxKill),
   689  		maxKillTimeout: maxKill,
   690  		doneCh:         make(chan struct{}),
   691  		waitCh:         make(chan *dstructs.WaitResult, 1),
   692  	}
   693  	go h.run()
   694  
   695  	// Only return a driver network if *not* using host networking
   696  	var driverNetwork *cstructs.DriverNetwork
   697  	if network != "host" {
   698  		d.logger.Printf("[DEBUG] driver.rkt: retrieving network information for pod %q (UUID: %s) for task %q", img, uuid, d.taskName)
   699  		driverNetwork, err = rktGetDriverNetwork(uuid, driverConfig.PortMap, d.logger)
   700  		if err != nil && !pluginClient.Exited() {
   701  			d.logger.Printf("[WARN] driver.rkt: network status retrieval for pod %q (UUID: %s) for task %q failed. Last error: %v", img, uuid, d.taskName, err)
   702  
   703  			// If a portmap was given, this turns into a fatal error
   704  			if len(driverConfig.PortMap) != 0 {
   705  				pluginClient.Kill()
   706  				return nil, fmt.Errorf("Trying to map ports but driver could not determine network information")
   707  			}
   708  		}
   709  	}
   710  
   711  	return &StartResponse{Handle: h, Network: driverNetwork}, nil
   712  }
   713  
   714  func (d *RktDriver) Cleanup(*ExecContext, *CreatedResources) error { return nil }
   715  
   716  func (d *RktDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) {
   717  	// Parse the handle
   718  	pidBytes := []byte(strings.TrimPrefix(handleID, "Rkt:"))
   719  	id := &rktPID{}
   720  	if err := json.Unmarshal(pidBytes, id); err != nil {
   721  		return nil, fmt.Errorf("failed to parse Rkt handle '%s': %v", handleID, err)
   722  	}
   723  
   724  	pluginConfig := &plugin.ClientConfig{
   725  		Reattach: id.PluginConfig.PluginConfig(),
   726  	}
   727  	exec, pluginClient, err := createExecutorWithConfig(pluginConfig, d.config.LogOutput)
   728  	if err != nil {
   729  		d.logger.Println("[ERR] driver.rkt: error connecting to plugin so destroying plugin pid and user pid")
   730  		if e := destroyPlugin(id.PluginConfig.Pid, id.ExecutorPid); e != nil {
   731  			d.logger.Printf("[ERR] driver.rkt: error destroying plugin and executor pid: %v", e)
   732  		}
   733  		return nil, fmt.Errorf("error connecting to plugin: %v", err)
   734  	}
   735  
   736  	// The task's environment is set via --set-env flags in Start, but the rkt
   737  	// command itself needs an evironment with PATH set to find iptables.
   738  	eb := env.NewEmptyBuilder()
   739  	filter := strings.Split(d.config.ReadDefault("env.blacklist", config.DefaultEnvBlacklist), ",")
   740  	rktEnv := eb.SetHostEnvvars(filter).Build()
   741  
   742  	ver, _ := exec.Version()
   743  	d.logger.Printf("[DEBUG] driver.rkt: version of executor: %v", ver.Version)
   744  	// Return a driver handle
   745  	h := &rktHandle{
   746  		uuid:           id.UUID,
   747  		env:            rktEnv,
   748  		taskDir:        ctx.TaskDir,
   749  		pluginClient:   pluginClient,
   750  		executorPid:    id.ExecutorPid,
   751  		executor:       exec,
   752  		logger:         d.logger,
   753  		killTimeout:    id.KillTimeout,
   754  		maxKillTimeout: id.MaxKillTimeout,
   755  		doneCh:         make(chan struct{}),
   756  		waitCh:         make(chan *dstructs.WaitResult, 1),
   757  	}
   758  	go h.run()
   759  	return h, nil
   760  }
   761  
   762  func (h *rktHandle) ID() string {
   763  	// Return a handle to the PID
   764  	pid := &rktPID{
   765  		UUID:           h.uuid,
   766  		PluginConfig:   NewPluginReattachConfig(h.pluginClient.ReattachConfig()),
   767  		KillTimeout:    h.killTimeout,
   768  		MaxKillTimeout: h.maxKillTimeout,
   769  		ExecutorPid:    h.executorPid,
   770  	}
   771  	data, err := json.Marshal(pid)
   772  	if err != nil {
   773  		h.logger.Printf("[ERR] driver.rkt: failed to marshal rkt PID to JSON: %s", err)
   774  	}
   775  	return fmt.Sprintf("Rkt:%s", string(data))
   776  }
   777  
   778  func (h *rktHandle) WaitCh() chan *dstructs.WaitResult {
   779  	return h.waitCh
   780  }
   781  
   782  func (h *rktHandle) Update(task *structs.Task) error {
   783  	// Store the updated kill timeout.
   784  	h.killTimeout = GetKillTimeout(task.KillTimeout, h.maxKillTimeout)
   785  	h.executor.UpdateTask(task)
   786  
   787  	// Update is not possible
   788  	return nil
   789  }
   790  
   791  func (h *rktHandle) Exec(ctx context.Context, cmd string, args []string) ([]byte, int, error) {
   792  	if h.uuid == "" {
   793  		return nil, 0, fmt.Errorf("unable to find rkt pod UUID")
   794  	}
   795  	// enter + UUID + cmd + args...
   796  	enterArgs := make([]string, 3+len(args))
   797  	enterArgs[0] = "enter"
   798  	enterArgs[1] = h.uuid
   799  	enterArgs[2] = cmd
   800  	copy(enterArgs[3:], args)
   801  	return executor.ExecScript(ctx, h.taskDir.Dir, h.env, nil, rktCmd, enterArgs)
   802  }
   803  
   804  func (h *rktHandle) Signal(s os.Signal) error {
   805  	return fmt.Errorf("Rkt does not support signals")
   806  }
   807  
   808  // Kill is used to terminate the task. We send an Interrupt
   809  // and then provide a 5 second grace period before doing a Kill.
   810  func (h *rktHandle) Kill() error {
   811  	h.executor.ShutDown()
   812  	select {
   813  	case <-h.doneCh:
   814  		return nil
   815  	case <-time.After(h.killTimeout):
   816  		return h.executor.Exit()
   817  	}
   818  }
   819  
   820  func (h *rktHandle) Stats() (*cstructs.TaskResourceUsage, error) {
   821  	return nil, DriverStatsNotImplemented
   822  }
   823  
   824  func (h *rktHandle) run() {
   825  	ps, werr := h.executor.Wait()
   826  	close(h.doneCh)
   827  	if ps.ExitCode == 0 && werr != nil {
   828  		if e := killProcess(h.executorPid); e != nil {
   829  			h.logger.Printf("[ERR] driver.rkt: error killing user process: %v", e)
   830  		}
   831  	}
   832  
   833  	// Exit the executor
   834  	if err := h.executor.Exit(); err != nil {
   835  		h.logger.Printf("[ERR] driver.rkt: error killing executor: %v", err)
   836  	}
   837  	h.pluginClient.Kill()
   838  
   839  	// Remove the pod
   840  	if err := rktRemove(h.uuid); err != nil {
   841  		h.logger.Printf("[ERR] driver.rkt: error removing pod (UUID: %q) - must gc manually: %v", h.uuid, err)
   842  	} else {
   843  		h.logger.Printf("[DEBUG] driver.rkt: removed pod (UUID: %q)", h.uuid)
   844  	}
   845  
   846  	// Send the results
   847  	h.waitCh <- dstructs.NewWaitResult(ps.ExitCode, 0, werr)
   848  	close(h.waitCh)
   849  }
   850  
   851  // Create a time with a 0 to 100ms jitter for rktGetDriverNetwork retries
   852  func getJitteredNetworkRetryTime() time.Duration {
   853  	return time.Duration(900+rand.Intn(100)) * time.Millisecond
   854  }
   855  
   856  // Conditionally elide a buffer to an arbitrary length
   857  func elideToLen(inBuf bytes.Buffer, length int) bytes.Buffer {
   858  	if inBuf.Len() > length {
   859  		inBuf.Truncate(length)
   860  		inBuf.WriteString("...")
   861  	}
   862  	return inBuf
   863  }
   864  
   865  // Conditionally elide a buffer to an 80 character string
   866  func elide(inBuf bytes.Buffer) string {
   867  	tempBuf := elideToLen(inBuf, 80)
   868  	return tempBuf.String()
   869  }