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