github.com/jmitchell/nomad@v0.1.3-0.20151007230021-7ab84c2862d8/client/driver/rkt.go (about)

     1  package driver
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"log"
     8  	"os"
     9  	"os/exec"
    10  	"regexp"
    11  	"runtime"
    12  	"strings"
    13  	"syscall"
    14  	"time"
    15  
    16  	"github.com/hashicorp/nomad/client/config"
    17  	"github.com/hashicorp/nomad/nomad/structs"
    18  )
    19  
    20  var (
    21  	reRktVersion  = regexp.MustCompile("rkt version ([\\d\\.]+).+")
    22  	reAppcVersion = regexp.MustCompile("appc version ([\\d\\.]+).+")
    23  )
    24  
    25  // RktDriver is a driver for running images via Rkt
    26  // We attempt to chose sane defaults for now, with more configuration available
    27  // planned in the future
    28  type RktDriver struct {
    29  	DriverContext
    30  }
    31  
    32  // rktHandle is returned from Start/Open as a handle to the PID
    33  type rktHandle struct {
    34  	proc   *os.Process
    35  	name   string
    36  	logger *log.Logger
    37  	waitCh chan error
    38  	doneCh chan struct{}
    39  }
    40  
    41  // rktPID is a struct to map the pid running the process to the vm image on
    42  // disk
    43  type rktPID struct {
    44  	Pid  int
    45  	Name string
    46  }
    47  
    48  // NewRktDriver is used to create a new exec driver
    49  func NewRktDriver(ctx *DriverContext) Driver {
    50  	return &RktDriver{*ctx}
    51  }
    52  
    53  func (d *RktDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) {
    54  	// Only enable if we are root when running on non-windows systems.
    55  	if runtime.GOOS != "windows" && syscall.Geteuid() != 0 {
    56  		d.logger.Printf("[DEBUG] driver.rkt: must run as root user, disabling")
    57  		return false, nil
    58  	}
    59  
    60  	outBytes, err := exec.Command("rkt", "version").Output()
    61  	if err != nil {
    62  		return false, nil
    63  	}
    64  	out := strings.TrimSpace(string(outBytes))
    65  
    66  	rktMatches := reRktVersion.FindStringSubmatch(out)
    67  	appcMatches := reRktVersion.FindStringSubmatch(out)
    68  	if len(rktMatches) != 2 || len(appcMatches) != 2 {
    69  		return false, fmt.Errorf("Unable to parse Rkt version string: %#v", rktMatches)
    70  	}
    71  
    72  	node.Attributes["driver.rkt"] = "true"
    73  	node.Attributes["driver.rkt.version"] = rktMatches[0]
    74  	node.Attributes["driver.rkt.appc.version"] = appcMatches[1]
    75  
    76  	return true, nil
    77  }
    78  
    79  // Run an existing Rkt image.
    80  func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) {
    81  	trust_prefix, ok := task.Config["trust_prefix"]
    82  	if !ok || trust_prefix == "" {
    83  		return nil, fmt.Errorf("Missing trust prefix for rkt")
    84  	}
    85  
    86  	// Add the given trust prefix
    87  	var outBuf, errBuf bytes.Buffer
    88  	cmd := exec.Command("rkt", "trust", fmt.Sprintf("--prefix=%s", trust_prefix))
    89  	cmd.Stdout = &outBuf
    90  	cmd.Stderr = &errBuf
    91  	d.logger.Printf("[DEBUG] driver.rkt: starting rkt command: %q", cmd.Args)
    92  	if err := cmd.Run(); err != nil {
    93  		return nil, fmt.Errorf(
    94  			"Error running rkt: %s\n\nOutput: %s\n\nError: %s",
    95  			err, outBuf.String(), errBuf.String())
    96  	}
    97  	d.logger.Printf("[DEBUG] driver.rkt: added trust prefix: %q", trust_prefix)
    98  
    99  	name, ok := task.Config["name"]
   100  	if !ok || name == "" {
   101  		return nil, fmt.Errorf("Missing ACI name for rkt")
   102  	}
   103  
   104  	exec_cmd, ok := task.Config["exec"]
   105  	if !ok || exec_cmd == "" {
   106  		d.logger.Printf("[WARN] driver.rkt: could not find a command to execute in the ACI, the default command will be executed")
   107  	}
   108  
   109  	// Run the ACI
   110  	var aoutBuf, aerrBuf bytes.Buffer
   111  	run_cmd := []string{
   112  		"rkt",
   113  		"run",
   114  		"--mds-register=false",
   115  		name,
   116  	}
   117  	if exec_cmd != "" {
   118  		splitted := strings.Fields(exec_cmd)
   119  		run_cmd = append(run_cmd, "--exec=", splitted[0], "--")
   120  		run_cmd = append(run_cmd, splitted[1:]...)
   121  		run_cmd = append(run_cmd, "---")
   122  	}
   123  	acmd := exec.Command(run_cmd[0], run_cmd[1:]...)
   124  	acmd.Stdout = &aoutBuf
   125  	acmd.Stderr = &aerrBuf
   126  	d.logger.Printf("[DEBUG] driver:rkt: starting rkt command: %q", acmd.Args)
   127  	if err := acmd.Start(); err != nil {
   128  		return nil, fmt.Errorf(
   129  			"Error running rkt: %s\n\nOutput: %s\n\nError: %s",
   130  			err, aoutBuf.String(), aerrBuf.String())
   131  	}
   132  	d.logger.Printf("[DEBUG] driver.rkt: started ACI: %q", name)
   133  	h := &rktHandle{
   134  		proc:   acmd.Process,
   135  		name:   name,
   136  		logger: d.logger,
   137  		doneCh: make(chan struct{}),
   138  		waitCh: make(chan error, 1),
   139  	}
   140  	go h.run()
   141  	return h, nil
   142  }
   143  
   144  func (d *RktDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) {
   145  	// Parse the handle
   146  	pidBytes := []byte(strings.TrimPrefix(handleID, "Rkt:"))
   147  	qpid := &rktPID{}
   148  	if err := json.Unmarshal(pidBytes, qpid); err != nil {
   149  		return nil, fmt.Errorf("failed to parse Rkt handle '%s': %v", handleID, err)
   150  	}
   151  
   152  	// Find the process
   153  	proc, err := os.FindProcess(qpid.Pid)
   154  	if proc == nil || err != nil {
   155  		return nil, fmt.Errorf("failed to find Rkt PID %d: %v", qpid.Pid, err)
   156  	}
   157  
   158  	// Return a driver handle
   159  	h := &rktHandle{
   160  		proc:   proc,
   161  		name:   qpid.Name,
   162  		logger: d.logger,
   163  		doneCh: make(chan struct{}),
   164  		waitCh: make(chan error, 1),
   165  	}
   166  
   167  	go h.run()
   168  	return h, nil
   169  }
   170  
   171  func (h *rktHandle) ID() string {
   172  	// Return a handle to the PID
   173  	pid := &rktPID{
   174  		Pid:  h.proc.Pid,
   175  		Name: h.name,
   176  	}
   177  	data, err := json.Marshal(pid)
   178  	if err != nil {
   179  		h.logger.Printf("[ERR] driver.rkt: failed to marshal rkt PID to JSON: %s", err)
   180  	}
   181  	return fmt.Sprintf("Rkt:%s", string(data))
   182  }
   183  
   184  func (h *rktHandle) WaitCh() chan error {
   185  	return h.waitCh
   186  }
   187  
   188  func (h *rktHandle) Update(task *structs.Task) error {
   189  	// Update is not possible
   190  	return nil
   191  }
   192  
   193  // Kill is used to terminate the task. We send an Interrupt
   194  // and then provide a 5 second grace period before doing a Kill.
   195  func (h *rktHandle) Kill() error {
   196  	h.proc.Signal(os.Interrupt)
   197  	select {
   198  	case <-h.doneCh:
   199  		return nil
   200  	case <-time.After(5 * time.Second):
   201  		return h.proc.Kill()
   202  	}
   203  }
   204  
   205  func (h *rktHandle) run() {
   206  	ps, err := h.proc.Wait()
   207  	close(h.doneCh)
   208  	if err != nil {
   209  		h.waitCh <- err
   210  	} else if !ps.Success() {
   211  		h.waitCh <- fmt.Errorf("task exited with error")
   212  	}
   213  	close(h.waitCh)
   214  }