github.com/huiliang/nomad@v0.2.1-0.20151124023127-7a8b664699ff/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  	"path/filepath"
    11  	"regexp"
    12  	"runtime"
    13  	"strings"
    14  	"syscall"
    15  	"time"
    16  
    17  	"github.com/hashicorp/nomad/client/allocdir"
    18  	"github.com/hashicorp/nomad/client/config"
    19  	"github.com/hashicorp/nomad/client/driver/args"
    20  	cstructs "github.com/hashicorp/nomad/client/driver/structs"
    21  	"github.com/hashicorp/nomad/client/fingerprint"
    22  	"github.com/hashicorp/nomad/nomad/structs"
    23  	"github.com/mitchellh/mapstructure"
    24  )
    25  
    26  var (
    27  	reRktVersion  = regexp.MustCompile(`rkt version (\d[.\d]+)`)
    28  	reAppcVersion = regexp.MustCompile(`appc version (\d[.\d]+)`)
    29  )
    30  
    31  // RktDriver is a driver for running images via Rkt
    32  // We attempt to chose sane defaults for now, with more configuration available
    33  // planned in the future
    34  type RktDriver struct {
    35  	DriverContext
    36  	fingerprint.StaticFingerprinter
    37  }
    38  
    39  type RktDriverConfig struct {
    40  	ImageName string   `mapstructure:"image"`
    41  	Args      []string `mapstructure:"args"`
    42  }
    43  
    44  // rktHandle is returned from Start/Open as a handle to the PID
    45  type rktHandle struct {
    46  	proc   *os.Process
    47  	image  string
    48  	logger *log.Logger
    49  	waitCh chan *cstructs.WaitResult
    50  	doneCh chan struct{}
    51  }
    52  
    53  // rktPID is a struct to map the pid running the process to the vm image on
    54  // disk
    55  type rktPID struct {
    56  	Pid   int
    57  	Image string
    58  }
    59  
    60  // NewRktDriver is used to create a new exec driver
    61  func NewRktDriver(ctx *DriverContext) Driver {
    62  	return &RktDriver{DriverContext: *ctx}
    63  }
    64  
    65  func (d *RktDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) {
    66  	// Only enable if we are root when running on non-windows systems.
    67  	if runtime.GOOS != "windows" && syscall.Geteuid() != 0 {
    68  		d.logger.Printf("[DEBUG] driver.rkt: must run as root user, disabling")
    69  		return false, nil
    70  	}
    71  
    72  	outBytes, err := exec.Command("rkt", "version").Output()
    73  	if err != nil {
    74  		return false, nil
    75  	}
    76  	out := strings.TrimSpace(string(outBytes))
    77  
    78  	rktMatches := reRktVersion.FindStringSubmatch(out)
    79  	appcMatches := reAppcVersion.FindStringSubmatch(out)
    80  	if len(rktMatches) != 2 || len(appcMatches) != 2 {
    81  		return false, fmt.Errorf("Unable to parse Rkt version string: %#v", rktMatches)
    82  	}
    83  
    84  	node.Attributes["driver.rkt"] = "1"
    85  	node.Attributes["driver.rkt.version"] = rktMatches[1]
    86  	node.Attributes["driver.rkt.appc.version"] = appcMatches[1]
    87  
    88  	return true, nil
    89  }
    90  
    91  // Run an existing Rkt image.
    92  func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) {
    93  	var driverConfig RktDriverConfig
    94  	if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil {
    95  		return nil, err
    96  	}
    97  	// Validate that the config is valid.
    98  	img := driverConfig.ImageName
    99  	if img == "" {
   100  		return nil, fmt.Errorf("Missing ACI image for rkt")
   101  	}
   102  
   103  	// Get the tasks local directory.
   104  	taskName := d.DriverContext.taskName
   105  	taskDir, ok := ctx.AllocDir.TaskDirs[taskName]
   106  	if !ok {
   107  		return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName)
   108  	}
   109  	taskLocal := filepath.Join(taskDir, allocdir.TaskLocal)
   110  
   111  	// Add the given trust prefix
   112  	trust_prefix, trust_cmd := task.Config["trust_prefix"]
   113  	if trust_cmd {
   114  		var outBuf, errBuf bytes.Buffer
   115  		cmd := exec.Command("rkt", "trust", fmt.Sprintf("--prefix=%s", trust_prefix))
   116  		cmd.Stdout = &outBuf
   117  		cmd.Stderr = &errBuf
   118  		if err := cmd.Run(); err != nil {
   119  			return nil, fmt.Errorf("Error running rkt trust: %s\n\nOutput: %s\n\nError: %s",
   120  				err, outBuf.String(), errBuf.String())
   121  		}
   122  		d.logger.Printf("[DEBUG] driver.rkt: added trust prefix: %q", trust_prefix)
   123  	}
   124  
   125  	// Build the command.
   126  	var cmd_args []string
   127  
   128  	// Inject the environment variables.
   129  	envVars := TaskEnvironmentVariables(ctx, task)
   130  
   131  	// Clear the task directories as they are not currently supported.
   132  	envVars.ClearTaskLocalDir()
   133  	envVars.ClearAllocDir()
   134  
   135  	for k, v := range envVars.Map() {
   136  		cmd_args = append(cmd_args, fmt.Sprintf("--set-env=%v=%v", k, v))
   137  	}
   138  
   139  	// Disble signature verification if the trust command was not run.
   140  	if !trust_cmd {
   141  		cmd_args = append(cmd_args, "--insecure-skip-verify")
   142  	}
   143  
   144  	// Append the run command.
   145  	cmd_args = append(cmd_args, "run", "--mds-register=false", img)
   146  
   147  	// Check if the user has overriden the exec command.
   148  	if exec_cmd, ok := task.Config["command"]; ok {
   149  		cmd_args = append(cmd_args, fmt.Sprintf("--exec=%v", exec_cmd))
   150  	}
   151  
   152  	// Add user passed arguments.
   153  	if len(driverConfig.Args) != 0 {
   154  		parsed := args.ParseAndReplace(driverConfig.Args, envVars.Map())
   155  
   156  		// Need to start arguments with "--"
   157  		if len(parsed) > 0 {
   158  			cmd_args = append(cmd_args, "--")
   159  		}
   160  
   161  		for _, arg := range parsed {
   162  			cmd_args = append(cmd_args, fmt.Sprintf("%v", arg))
   163  		}
   164  	}
   165  
   166  	// Create files to capture stdin and out.
   167  	stdoutFilename := filepath.Join(taskLocal, fmt.Sprintf("%s.stdout", taskName))
   168  	stderrFilename := filepath.Join(taskLocal, fmt.Sprintf("%s.stderr", taskName))
   169  
   170  	stdo, err := os.OpenFile(stdoutFilename, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
   171  	if err != nil {
   172  		return nil, fmt.Errorf("Error opening file to redirect stdout: %v", err)
   173  	}
   174  
   175  	stde, err := os.OpenFile(stderrFilename, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
   176  	if err != nil {
   177  		return nil, fmt.Errorf("Error opening file to redirect stderr: %v", err)
   178  	}
   179  
   180  	cmd := exec.Command("rkt", cmd_args...)
   181  	cmd.Stdout = stdo
   182  	cmd.Stderr = stde
   183  
   184  	if err := cmd.Start(); err != nil {
   185  		return nil, fmt.Errorf("Error running rkt: %v", err)
   186  	}
   187  
   188  	d.logger.Printf("[DEBUG] driver.rkt: started ACI %q with: %v", img, cmd.Args)
   189  	h := &rktHandle{
   190  		proc:   cmd.Process,
   191  		image:  img,
   192  		logger: d.logger,
   193  		doneCh: make(chan struct{}),
   194  		waitCh: make(chan *cstructs.WaitResult, 1),
   195  	}
   196  	go h.run()
   197  	return h, nil
   198  }
   199  
   200  func (d *RktDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) {
   201  	// Parse the handle
   202  	pidBytes := []byte(strings.TrimPrefix(handleID, "Rkt:"))
   203  	qpid := &rktPID{}
   204  	if err := json.Unmarshal(pidBytes, qpid); err != nil {
   205  		return nil, fmt.Errorf("failed to parse Rkt handle '%s': %v", handleID, err)
   206  	}
   207  
   208  	// Find the process
   209  	proc, err := os.FindProcess(qpid.Pid)
   210  	if proc == nil || err != nil {
   211  		return nil, fmt.Errorf("failed to find Rkt PID %d: %v", qpid.Pid, err)
   212  	}
   213  
   214  	// Return a driver handle
   215  	h := &rktHandle{
   216  		proc:   proc,
   217  		image:  qpid.Image,
   218  		logger: d.logger,
   219  		doneCh: make(chan struct{}),
   220  		waitCh: make(chan *cstructs.WaitResult, 1),
   221  	}
   222  
   223  	go h.run()
   224  	return h, nil
   225  }
   226  
   227  func (h *rktHandle) ID() string {
   228  	// Return a handle to the PID
   229  	pid := &rktPID{
   230  		Pid:   h.proc.Pid,
   231  		Image: h.image,
   232  	}
   233  	data, err := json.Marshal(pid)
   234  	if err != nil {
   235  		h.logger.Printf("[ERR] driver.rkt: failed to marshal rkt PID to JSON: %s", err)
   236  	}
   237  	return fmt.Sprintf("Rkt:%s", string(data))
   238  }
   239  
   240  func (h *rktHandle) WaitCh() chan *cstructs.WaitResult {
   241  	return h.waitCh
   242  }
   243  
   244  func (h *rktHandle) Update(task *structs.Task) error {
   245  	// Update is not possible
   246  	return nil
   247  }
   248  
   249  // Kill is used to terminate the task. We send an Interrupt
   250  // and then provide a 5 second grace period before doing a Kill.
   251  func (h *rktHandle) Kill() error {
   252  	h.proc.Signal(os.Interrupt)
   253  	select {
   254  	case <-h.doneCh:
   255  		return nil
   256  	case <-time.After(5 * time.Second):
   257  		return h.proc.Kill()
   258  	}
   259  }
   260  
   261  func (h *rktHandle) run() {
   262  	ps, err := h.proc.Wait()
   263  	close(h.doneCh)
   264  	code := 0
   265  	if !ps.Success() {
   266  		// TODO: Better exit code parsing.
   267  		code = 1
   268  	}
   269  	h.waitCh <- cstructs.NewWaitResult(code, 0, err)
   270  	close(h.waitCh)
   271  }