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