diff --git a/client/driver/rkt.go b/client/driver/rkt.go index 2e68e1af0..42337842a 100644 --- a/client/driver/rkt.go +++ b/client/driver/rkt.go @@ -33,6 +33,7 @@ type RktDriver struct { type rktHandle struct { proc *os.Process name string + logger *log.Logger waitCh chan error doneCh chan struct{} } @@ -68,9 +69,9 @@ func (d *RktDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, e return false, fmt.Errorf("Unable to parse Rkt version string: %#v", rktMatches) } - node.Attributes["driver.rkt"] = "1" + node.Attributes["driver.rkt"] = "true" node.Attributes["driver.rkt.version"] = rktMatches[0] - node.Attributes["driver.appc.version"] = appcMatches[1] + node.Attributes["driver.rkt.appc.version"] = appcMatches[1] return true, nil } @@ -81,19 +82,14 @@ func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, e if !ok || trust_prefix == "" { return nil, fmt.Errorf("Missing trust prefix for rkt") } - trust_args := []string{ - "sudo", - "rkt", - "trust", - "--prefix=" + trust_prefix, - } + // Add the given trust prefix var outBuf, errBuf bytes.Buffer - cmd := exec.Command(trust_args[0], trust_args[1:]...) + cmd := exec.Command("rkt", "trust", fmt.Sprintf("--prefix=%s", trust_prefix)) cmd.Stdout = &outBuf cmd.Stderr = &errBuf - d.logger.Printf("[DEBUG] Starting rkt command: %q", strings.Join(trust_args, " ")) - if err := cmd.Start(); err != nil { + d.logger.Printf("[DEBUG] Starting rkt command: %q", cmd.Args) + if err := cmd.Run(); err != nil { return nil, fmt.Errorf( "Error running rkt: %s\n\nOutput: %s\n\nError: %s", err, outBuf.String(), errBuf.String()) @@ -104,18 +100,14 @@ func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, e if !ok || name == "" { return nil, fmt.Errorf("Missing ACI name for rkt") } - run_args := []string{ - "sudo", - "rkt", - "run", "--interactive", name, - } + // Run the ACI var aoutBuf, aerrBuf bytes.Buffer - acmd := exec.Command(run_args[0], run_args[1:]...) + acmd := exec.Command("rkt", "run", "--interactive", name) acmd.Stdout = &aoutBuf acmd.Stderr = &aerrBuf - d.logger.Printf("[DEBUG] Starting rkt command: %q", strings.Join(run_args, " ")) - if err := acmd.Start(); err != nil { + d.logger.Printf("[DEBUG] Starting rkt command: %q", acmd.Args) + if err := acmd.Run(); err != nil { return nil, fmt.Errorf( "Error running rkt: %s\n\nOutput: %s\n\nError: %s", err, aoutBuf.String(), aerrBuf.String()) @@ -124,6 +116,7 @@ func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, e h := &rktHandle{ proc: acmd.Process, name: name, + logger: d.logger, doneCh: make(chan struct{}), waitCh: make(chan error, 1), } @@ -149,6 +142,7 @@ func (d *RktDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error h := &rktHandle{ proc: proc, name: qpid.Name, + logger: d.logger, doneCh: make(chan struct{}), waitCh: make(chan error, 1), } @@ -165,7 +159,7 @@ func (h *rktHandle) ID() string { } data, err := json.Marshal(pid) if err != nil { - log.Printf("[ERR] failed to marshal rkt PID to JSON: %s", err) + h.logger.Printf("[ERR] failed to marshal rkt PID to JSON: %s", err) } return fmt.Sprintf("Rkt:%s", string(data)) } @@ -181,9 +175,6 @@ func (h *rktHandle) Update(task *structs.Task) error { // Kill is used to terminate the task. We send an Interrupt // and then provide a 5 second grace period before doing a Kill. -// -// TODO: allow a 'shutdown_command' that can be executed over a ssh connection -// to the VM func (h *rktHandle) Kill() error { h.proc.Signal(os.Interrupt) select { diff --git a/client/driver/rkt_test.go b/client/driver/rkt_test.go index d4573f526..6fbc2383d 100644 --- a/client/driver/rkt_test.go +++ b/client/driver/rkt_test.go @@ -12,14 +12,6 @@ import ( ctestutils "github.com/hashicorp/nomad/client/testutil" ) -// rktLocated looks to see whether rkt binaries are available on this system -// before we try to run tests. We may need to tweak this for cross-OS support -// but I think this should work on *nix at least. -func rktLocated() bool { - _, err := exec.Command("rkt", "version").CombinedOutput() - return err == nil -} - func TestRktDriver_Handle(t *testing.T) { h := &rktHandle{ proc: &os.Process{Pid: 123}, @@ -55,16 +47,16 @@ func TestRktDriver_Fingerprint(t *testing.T) { if node.Attributes["driver.rkt.version"] == "" { t.Fatalf("Missing Rkt driver version") } + if node.Attributes["driver.rkt.appc.version"] == "" { + t.Fatalf("Missing appc version for the Rkt driver") + } } func TestRktDriver_Start(t *testing.T) { - if !rktLocated() { - t.Skip("Rkt not found; skipping") - } - + ctestutils.RktCompatible(t) // TODO: use test server to load from a fixture task := &structs.Task{ - Name: "linux", + Name: "etcd", Config: map[string]string{ "trust_prefix": "coreos.com/etcd", "name": "coreos.com/etcd:v2.0.4", @@ -74,6 +66,7 @@ func TestRktDriver_Start(t *testing.T) { driverCtx := testDriverContext(task.Name) ctx := testDriverExecContext(task, driverCtx) d := NewRktDriver(driverCtx) + defer ctx.AllocDir.Destroy() handle, err := d.Start(ctx, task) if err != nil { @@ -97,3 +90,43 @@ func TestRktDriver_Start(t *testing.T) { fmt.Printf("\nError killing Rkt test: %s", err) } } + +func TestRktDriver_Start_Wait(t *testing.T) { + ctestutils.RktCompatible(t) + task := &structs.Task{ + Name: "etcd", + Config: map[string]string{ + "trust_prefix": "coreos.com/etcd", + "name": "coreos.com/etcd:v2.0.4", + }, + } + + driverCtx := testDriverContext(task.Name) + ctx := testDriverExecContext(task, driverCtx) + d := NewRktDriver(driverCtx) + defer ctx.AllocDir.Destroy() + + handle, err := d.Start(ctx, task) + if err != nil { + t.Fatalf("err: %v", err) + } + if handle == nil { + t.Fatalf("missing handle") + } + defer handle.Kill() + + // Update should be a no-op + err = handle.Update(task) + if err != nil { + t.Fatalf("err: %v", err) + } + + select { + case err := <-handle.WaitCh(): + if err != nil { + t.Fatalf("err: %v", err) + } + case <-time.After(5 * time.Second): + t.Fatalf("timeout") + } +} diff --git a/client/testutil/driver_compatible.go b/client/testutil/driver_compatible.go index da300b1a1..5e9242685 100644 --- a/client/testutil/driver_compatible.go +++ b/client/testutil/driver_compatible.go @@ -18,10 +18,13 @@ func QemuCompatible(t *testing.T) { } } -func RktCompatible(t *testing.T) { - if runtime.GOOS != "windows" && syscall.Geteuid() != 0 { +func RktCompatible(t *testing.T) bool { + if runtime.GOOS == "windows" || syscall.Geteuid() != 0 { t.Skip("Must be root on non-windows environments to run test") } + // else see if rkt exists + _, err := exec.Command("rkt", "version").CombinedOutput() + return err == nil } func MountCompatible(t *testing.T) { diff --git a/website/source/docs/drivers/rkt.html.md b/website/source/docs/drivers/rkt.html.md new file mode 100644 index 000000000..34fc0367d --- /dev/null +++ b/website/source/docs/drivers/rkt.html.md @@ -0,0 +1,43 @@ +--- +layout: "docs" +page_title: "Drivers: Rkt" +sidebar_current: "docs-drivers-rkt" +description: |- + The Rkt task driver is used to run application containers using Rkt. +--- + +# Rkt Driver + +Name: `rkt` + +The `Rkt` driver provides an interface for using CoreOS Rkt for running +application containers. Currently, the driver supports launching +containers. + +## Task Configuration + +The `Rkt` driver supports the following configuration in the job spec: + +* `trust_prefix` - **(Required)** The trust prefix to be passed to rkt. Must be reachable from +the box running the nomad agent. +* `name` - **(Required)** Fully qualified name of an image to run using rkt + +## Client Requirements + +The `Rkt` driver requires rkt to be installed and in your systems `$PATH`. +The `trust_prefix` must be accessible by the node running Nomad. This can be an +internal source, private to your cluster, but it must be reachable by the client +over HTTP. + +## Client Attributes + +The `Rkt` driver will set the following client attributes: + +* `driver.rkt` - Set to `true` if Rkt is found on the host node. Nomad determines +this by executing `rkt version` on the host and parsing the output +* `driver.rkt.version` - Version of `rkt` eg: `0.8.1` +* `driver.rkt.appc.version` - Version of `appc` that `rkt` is using eg: `0.8.1` + +## Resource Isolation + +This driver does not support any resource isolation as of now.