From 6a2a938b4e5a7f9fa47a32821b3c0712cf32e229 Mon Sep 17 00:00:00 2001 From: Abhishek Chanda Date: Tue, 29 Sep 2015 14:48:10 -0700 Subject: [PATCH 01/15] Add a driver for rkt rkt docs here https://github.com/coreos/rkt --- client/driver/rkt.go | 206 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 client/driver/rkt.go diff --git a/client/driver/rkt.go b/client/driver/rkt.go new file mode 100644 index 000000000..2ed689bc4 --- /dev/null +++ b/client/driver/rkt.go @@ -0,0 +1,206 @@ +package driver + +import ( + "bytes" + "encoding/json" + "fmt" + "log" + "os" + "os/exec" + "regexp" + "runtime" + "strings" + "syscall" + "time" + + "github.com/hashicorp/nomad/client/config" + "github.com/hashicorp/nomad/nomad/structs" +) + +var ( + reRktVersion = regexp.MustCompile("rkt version ([\\d\\.]+).+") + reAppcVersion = regexp.MustCompile("appc version ([\\d\\.]+).+") +) + +// RktDriver is a driver for running images via Rkt +// We attempt to chose sane defaults for now, with more configuration available +// planned in the future +type RktDriver struct { + DriverContext +} + +// rktHandle is returned from Start/Open as a handle to the PID +type rktHandle struct { + proc *os.Process + name string + waitCh chan error + doneCh chan struct{} +} + +// rktPID is a struct to map the pid running the process to the vm image on +// disk +type rktPID struct { + Pid int + Name string +} + +// NewRktDriver is used to create a new exec driver +func NewRktDriver(ctx *DriverContext) Driver { + return &RktDriver{*ctx} +} + +func (d *RktDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { + // Only enable if we are root when running on non-windows systems. + if runtime.GOOS != "windows" && syscall.Geteuid() != 0 { + d.logger.Printf("[DEBUG] driver.rkt: must run as root user, disabling") + return false, nil + } + + outBytes, err := exec.Command("rkt", "version").Output() + if err != nil { + return false, nil + } + out := strings.TrimSpace(string(outBytes)) + + rktMatches := reRktVersion.FindStringSubmatch(out) + appcMatches := reRktVersion.FindStringSubmatch(out) + if len(rktMatches) != 2 || len(appcMatches) != 2 { + return false, fmt.Errorf("Unable to parse Rkt version string: %#v", rktMatches) + } + + node.Attributes["driver.rkt"] = "1" + node.Attributes["driver.rkt.version"] = rktMatches[0] + node.Attributes["driver.appc.version"] = appcMatches[1] + + return true, nil +} + +// Run an existing Rkt image. +func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { + trust_prefix, ok := task.Config["trust_prefix"] + 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.Stdout = &outBuf + cmd.Stderr = &errBuf + d.logger.Printf("[DEBUG] Starting rkt command: %q", strings.Join(trust_args, " ")) + if err := cmd.Start(); err != nil { + return nil, fmt.Errorf( + "Error running rkt: %s\n\nOutput: %s\n\nError: %s", + err, outBuf.String(), errBuf.String()) + } + d.logger.Printf("[DEBUG] Added trust prefix: %q", trust_prefix) + + name, ok := task.Config["name"] + 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.Stdout = &aoutBuf + acmd.Stderr = &aerrBuf + d.logger.Printf("[DEBUG] Starting rkt command: %q", strings.Join(run_args, " ")) + if err := acmd.Start(); err != nil { + return nil, fmt.Errorf( + "Error running rkt: %s\n\nOutput: %s\n\nError: %s", + err, aoutBuf.String(), aerrBuf.String()) + } + d.logger.Printf("[DEBUG] Started ACI: %q", name) + h := &rktHandle{ + proc: cmd.Process, + name: name, + doneCh: make(chan struct{}), + waitCh: make(chan error, 1), + } + go h.run() + return h, nil +} + +func (d *RktDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) { + // Parse the handle + pidBytes := []byte(strings.TrimPrefix(handleID, "RKT:")) + qpid := &rktPID{} + if err := json.Unmarshal(pidBytes, qpid); err != nil { + return nil, fmt.Errorf("failed to parse Rkt handle '%s': %v", handleID, err) + } + + // Find the process + proc, err := os.FindProcess(qpid.Pid) + if proc == nil || err != nil { + return nil, fmt.Errorf("failed to find Rkt PID %d: %v", qpid.Pid, err) + } + + // Return a driver handle + h := &rktHandle{ + proc: proc, + name: qpid.Name, + doneCh: make(chan struct{}), + waitCh: make(chan error, 1), + } + + go h.run() + return h, nil +} + +func (h *rktHandle) ID() string { + // Return a handle to the PID + pid := &rktPID{ + Pid: h.proc.Pid, + Name: h.name, + } + data, err := json.Marshal(pid) + if err != nil { + log.Printf("[ERR] failed to marshal rkt PID to JSON: %s", err) + } + return fmt.Sprintf("Rkt:%s", string(data)) +} + +func (h *rktHandle) WaitCh() chan error { + return h.waitCh +} + +func (h *rktHandle) Update(task *structs.Task) error { + // Update is not possible + return nil +} + +// 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 { + case <-h.doneCh: + return nil + case <-time.After(5 * time.Second): + return h.proc.Kill() + } +} + +func (h *rktHandle) run() { + ps, err := h.proc.Wait() + close(h.doneCh) + if err != nil { + h.waitCh <- err + } else if !ps.Success() { + h.waitCh <- fmt.Errorf("task exited with error") + } + close(h.waitCh) +} From f893fb7c35da059deec1ff72399fe6900b7d5ddb Mon Sep 17 00:00:00 2001 From: Abhishek Chanda Date: Tue, 29 Sep 2015 15:30:18 -0700 Subject: [PATCH 02/15] Fix name of prefix --- client/driver/rkt.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/driver/rkt.go b/client/driver/rkt.go index 2ed689bc4..c03b42189 100644 --- a/client/driver/rkt.go +++ b/client/driver/rkt.go @@ -133,7 +133,7 @@ func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, e func (d *RktDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) { // Parse the handle - pidBytes := []byte(strings.TrimPrefix(handleID, "RKT:")) + pidBytes := []byte(strings.TrimPrefix(handleID, "Rkt:")) qpid := &rktPID{} if err := json.Unmarshal(pidBytes, qpid); err != nil { return nil, fmt.Errorf("failed to parse Rkt handle '%s': %v", handleID, err) From 481ba6ecb62bb8cdae9c0ac089ed1e19336c5ecc Mon Sep 17 00:00:00 2001 From: Abhishek Chanda Date: Tue, 29 Sep 2015 15:33:25 -0700 Subject: [PATCH 03/15] Add a test fort he rkt driver --- client/driver/rkt_test.go | 99 ++++++++++++++++++++++++++++ client/testutil/driver_compatible.go | 6 ++ 2 files changed, 105 insertions(+) create mode 100644 client/driver/rkt_test.go diff --git a/client/driver/rkt_test.go b/client/driver/rkt_test.go new file mode 100644 index 000000000..d4573f526 --- /dev/null +++ b/client/driver/rkt_test.go @@ -0,0 +1,99 @@ +package driver + +import ( + "fmt" + "os" + "os/exec" + "testing" + + "github.com/hashicorp/nomad/client/config" + "github.com/hashicorp/nomad/nomad/structs" + + 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}, + name: "foo", + doneCh: make(chan struct{}), + waitCh: make(chan error, 1), + } + + actual := h.ID() + expected := `Rkt:{"Pid":123,"Name":"foo"}` + if actual != expected { + t.Errorf("Expected `%s`, found `%s`", expected, actual) + } +} + +// The fingerprinter test should always pass, even if rkt is not installed. +func TestRktDriver_Fingerprint(t *testing.T) { + ctestutils.RktCompatible(t) + d := NewRktDriver(testDriverContext("")) + node := &structs.Node{ + Attributes: make(map[string]string), + } + apply, err := d.Fingerprint(&config.Config{}, node) + if err != nil { + t.Fatalf("err: %v", err) + } + if !apply { + t.Fatalf("should apply") + } + if node.Attributes["driver.rkt"] == "" { + t.Fatalf("Missing Rkt driver") + } + if node.Attributes["driver.rkt.version"] == "" { + t.Fatalf("Missing Rkt driver version") + } +} + +func TestRktDriver_Start(t *testing.T) { + if !rktLocated() { + t.Skip("Rkt not found; skipping") + } + + // TODO: use test server to load from a fixture + task := &structs.Task{ + Name: "linux", + 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) + + handle, err := d.Start(ctx, task) + if err != nil { + t.Fatalf("err: %v", err) + } + if handle == nil { + t.Fatalf("missing handle") + } + + // Attempt to open + handle2, err := d.Open(ctx, handle.ID()) + if err != nil { + t.Fatalf("err: %v", err) + } + if handle2 == nil { + t.Fatalf("missing handle") + } + + // Clean up + if err := handle.Kill(); err != nil { + fmt.Printf("\nError killing Rkt test: %s", err) + } +} diff --git a/client/testutil/driver_compatible.go b/client/testutil/driver_compatible.go index 8b3ed705b..da300b1a1 100644 --- a/client/testutil/driver_compatible.go +++ b/client/testutil/driver_compatible.go @@ -18,6 +18,12 @@ func QemuCompatible(t *testing.T) { } } +func RktCompatible(t *testing.T) { + if runtime.GOOS != "windows" && syscall.Geteuid() != 0 { + t.Skip("Must be root on non-windows environments to run test") + } +} + func MountCompatible(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("Windows does not support mount") From 0e659aefe75469675f0ca75d78a2dee8eac34eae Mon Sep 17 00:00:00 2001 From: Abhishek Chanda Date: Tue, 29 Sep 2015 15:53:42 -0700 Subject: [PATCH 04/15] Fix constructing the handle --- client/driver/rkt.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/driver/rkt.go b/client/driver/rkt.go index c03b42189..2e68e1af0 100644 --- a/client/driver/rkt.go +++ b/client/driver/rkt.go @@ -122,7 +122,7 @@ func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, e } d.logger.Printf("[DEBUG] Started ACI: %q", name) h := &rktHandle{ - proc: cmd.Process, + proc: acmd.Process, name: name, doneCh: make(chan struct{}), waitCh: make(chan error, 1), From 61b4be2389dfdcf73c158479a36b8c2b2292874f Mon Sep 17 00:00:00 2001 From: Abhishek Chanda Date: Tue, 29 Sep 2015 15:55:23 -0700 Subject: [PATCH 05/15] Remove a stray comment --- client/driver/rkt.go | 37 ++++++---------- client/driver/rkt_test.go | 59 +++++++++++++++++++------ client/testutil/driver_compatible.go | 7 ++- website/source/docs/drivers/rkt.html.md | 43 ++++++++++++++++++ 4 files changed, 108 insertions(+), 38 deletions(-) create mode 100644 website/source/docs/drivers/rkt.html.md 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. From 0bc00223ed15558ae155233ee4cf16c7fe2c0b60 Mon Sep 17 00:00:00 2001 From: Abhishek Chanda Date: Thu, 1 Oct 2015 14:34:08 -0700 Subject: [PATCH 06/15] Do not register to the metadata service --- client/driver/rkt.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/driver/rkt.go b/client/driver/rkt.go index 42337842a..8df7537f6 100644 --- a/client/driver/rkt.go +++ b/client/driver/rkt.go @@ -103,7 +103,7 @@ func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, e // Run the ACI var aoutBuf, aerrBuf bytes.Buffer - acmd := exec.Command("rkt", "run", "--interactive", name) + acmd := exec.Command("rkt", "run", "--mds-register=false", "--interactive", name) acmd.Stdout = &aoutBuf acmd.Stderr = &aerrBuf d.logger.Printf("[DEBUG] Starting rkt command: %q", acmd.Args) From 740187e6695c1560268f54c94e343e3943730dcc Mon Sep 17 00:00:00 2001 From: Abhishek Chanda Date: Thu, 1 Oct 2015 15:25:01 -0700 Subject: [PATCH 07/15] Do not run containers interactively --- client/driver/rkt.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/driver/rkt.go b/client/driver/rkt.go index 8df7537f6..b544bd6b3 100644 --- a/client/driver/rkt.go +++ b/client/driver/rkt.go @@ -103,7 +103,7 @@ func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, e // Run the ACI var aoutBuf, aerrBuf bytes.Buffer - acmd := exec.Command("rkt", "run", "--mds-register=false", "--interactive", name) + acmd := exec.Command("rkt", "run", "--mds-register=false", name) acmd.Stdout = &aoutBuf acmd.Stderr = &aerrBuf d.logger.Printf("[DEBUG] Starting rkt command: %q", acmd.Args) From 00556d4b3b52ba3a9bd5efd8ce95d5fa087bf8cd Mon Sep 17 00:00:00 2001 From: Abhishek Chanda Date: Thu, 1 Oct 2015 15:30:36 -0700 Subject: [PATCH 08/15] Add missing import and remove unsued one --- client/driver/rkt_test.go | 2 +- client/testutil/driver_compatible.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/client/driver/rkt_test.go b/client/driver/rkt_test.go index 6fbc2383d..5056e0f1d 100644 --- a/client/driver/rkt_test.go +++ b/client/driver/rkt_test.go @@ -3,8 +3,8 @@ package driver import ( "fmt" "os" - "os/exec" "testing" + "time" "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/nomad/structs" diff --git a/client/testutil/driver_compatible.go b/client/testutil/driver_compatible.go index 5e9242685..81c958866 100644 --- a/client/testutil/driver_compatible.go +++ b/client/testutil/driver_compatible.go @@ -4,6 +4,7 @@ import ( "runtime" "syscall" "testing" + "os/exec" ) func ExecCompatible(t *testing.T) { From 97c2172108c11c656c685f3eaf7819ffe774dc73 Mon Sep 17 00:00:00 2001 From: Abhishek Chanda Date: Thu, 1 Oct 2015 23:12:35 -0700 Subject: [PATCH 09/15] Register rkt as a built in driver --- client/driver/driver.go | 1 + 1 file changed, 1 insertion(+) diff --git a/client/driver/driver.go b/client/driver/driver.go index 5310dca51..46b18538c 100644 --- a/client/driver/driver.go +++ b/client/driver/driver.go @@ -19,6 +19,7 @@ var BuiltinDrivers = map[string]Factory{ "exec": NewExecDriver, "java": NewJavaDriver, "qemu": NewQemuDriver, + "rkt": NewRktDriver, } // NewDriver is used to instantiate and return a new driver From bedee8cd8fb29584c14654f0ffa2f37ca159f7d2 Mon Sep 17 00:00:00 2001 From: Abhishek Chanda Date: Fri, 2 Oct 2015 14:19:53 -0700 Subject: [PATCH 10/15] Fix function call Make it skip if rkt is not installed --- client/testutil/driver_compatible.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/testutil/driver_compatible.go b/client/testutil/driver_compatible.go index 81c958866..c354a3286 100644 --- a/client/testutil/driver_compatible.go +++ b/client/testutil/driver_compatible.go @@ -19,13 +19,15 @@ func QemuCompatible(t *testing.T) { } } -func RktCompatible(t *testing.T) bool { +func RktCompatible(t *testing.T) { 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 + if err != nil { + t.Skip("Must have rkt installed for rkt specific tests to run") + } } func MountCompatible(t *testing.T) { From 5c76624f2ec6cfa0bf1ce12ac3fd8e301b40aa27 Mon Sep 17 00:00:00 2001 From: Abhishek Chanda Date: Mon, 5 Oct 2015 21:45:11 -0700 Subject: [PATCH 11/15] Add prefix to all message logs for rkt driver --- client/driver/rkt.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/driver/rkt.go b/client/driver/rkt.go index b544bd6b3..a0a2dcf34 100644 --- a/client/driver/rkt.go +++ b/client/driver/rkt.go @@ -88,13 +88,13 @@ func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, e 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", cmd.Args) + d.logger.Printf("[DEBUG] driver.rkt: 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()) } - d.logger.Printf("[DEBUG] Added trust prefix: %q", trust_prefix) + d.logger.Printf("[DEBUG] driver.rkt: added trust prefix: %q", trust_prefix) name, ok := task.Config["name"] if !ok || name == "" { @@ -106,13 +106,13 @@ func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, e acmd := exec.Command("rkt", "run", "--mds-register=false", name) acmd.Stdout = &aoutBuf acmd.Stderr = &aerrBuf - d.logger.Printf("[DEBUG] Starting rkt command: %q", acmd.Args) + d.logger.Printf("[DEBUG] driver:rkt: 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()) } - d.logger.Printf("[DEBUG] Started ACI: %q", name) + d.logger.Printf("[DEBUG] driver.rkt: started ACI: %q", name) h := &rktHandle{ proc: acmd.Process, name: name, @@ -159,7 +159,7 @@ func (h *rktHandle) ID() string { } data, err := json.Marshal(pid) if err != nil { - h.logger.Printf("[ERR] failed to marshal rkt PID to JSON: %s", err) + h.logger.Printf("[ERR] driver.rkt: failed to marshal rkt PID to JSON: %s", err) } return fmt.Sprintf("Rkt:%s", string(data)) } From 4b216736abc49200f3a73c084d505e514805b39a Mon Sep 17 00:00:00 2001 From: Abhishek Chanda Date: Tue, 6 Oct 2015 07:29:56 -0700 Subject: [PATCH 12/15] Use Start to run commands Run blocks --- client/driver/rkt.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/driver/rkt.go b/client/driver/rkt.go index a0a2dcf34..9bf628234 100644 --- a/client/driver/rkt.go +++ b/client/driver/rkt.go @@ -89,7 +89,7 @@ func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, e cmd.Stdout = &outBuf cmd.Stderr = &errBuf d.logger.Printf("[DEBUG] driver.rkt: starting rkt command: %q", cmd.Args) - if err := cmd.Run(); err != nil { + if err := cmd.Start(); err != nil { return nil, fmt.Errorf( "Error running rkt: %s\n\nOutput: %s\n\nError: %s", err, outBuf.String(), errBuf.String()) @@ -107,7 +107,7 @@ func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, e acmd.Stdout = &aoutBuf acmd.Stderr = &aerrBuf d.logger.Printf("[DEBUG] driver:rkt: starting rkt command: %q", acmd.Args) - if err := acmd.Run(); err != nil { + if err := acmd.Start(); err != nil { return nil, fmt.Errorf( "Error running rkt: %s\n\nOutput: %s\n\nError: %s", err, aoutBuf.String(), aerrBuf.String()) From 558333dce97bf4f9187df3fc7176b6d90557cd55 Mon Sep 17 00:00:00 2001 From: Abhishek Chanda Date: Wed, 7 Oct 2015 11:15:17 -0700 Subject: [PATCH 13/15] Use Run for adding trust prefix The trust needs to be added before anything can progress --- client/driver/rkt.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/driver/rkt.go b/client/driver/rkt.go index 9bf628234..e8eaaeca4 100644 --- a/client/driver/rkt.go +++ b/client/driver/rkt.go @@ -89,7 +89,7 @@ func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, e cmd.Stdout = &outBuf cmd.Stderr = &errBuf d.logger.Printf("[DEBUG] driver.rkt: starting rkt command: %q", cmd.Args) - if err := cmd.Start(); err != nil { + 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()) From 43a8a1af96538805876b932c16ff35737dc16b7b Mon Sep 17 00:00:00 2001 From: Abhishek Chanda Date: Wed, 7 Oct 2015 14:48:06 -0700 Subject: [PATCH 14/15] Add an optional exec parameter to task config This overrides the default exec command in the ACI --- client/driver/rkt.go | 19 ++++++++++++++++++- client/driver/rkt_test.go | 2 ++ website/source/docs/drivers/rkt.html.md | 3 ++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/client/driver/rkt.go b/client/driver/rkt.go index e8eaaeca4..f2feb93de 100644 --- a/client/driver/rkt.go +++ b/client/driver/rkt.go @@ -101,9 +101,26 @@ func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, e return nil, fmt.Errorf("Missing ACI name for rkt") } + exec_cmd, ok := task.Config["exec"] + if !ok || exec_cmd == "" { + d.logger.Printf("[WARN] driver.rkt: could not find a command to execute in the ACI, the default command will be executed") + } + // Run the ACI var aoutBuf, aerrBuf bytes.Buffer - acmd := exec.Command("rkt", "run", "--mds-register=false", name) + run_cmd := []string{ + "rkt", + "run", + "--mds-register=false", + name, + } + if exec_cmd != "" { + splitted := strings.Fields(exec_cmd) + run_cmd = append(run_cmd, "--exec=", splitted[0], "--") + run_cmd = append(run_cmd, splitted[1:]...) + run_cmd = append(run_cmd, "---") + } + acmd := exec.Command(run_cmd[0], run_cmd[1:]...) acmd.Stdout = &aoutBuf acmd.Stderr = &aerrBuf d.logger.Printf("[DEBUG] driver:rkt: starting rkt command: %q", acmd.Args) diff --git a/client/driver/rkt_test.go b/client/driver/rkt_test.go index 5056e0f1d..804c56c3c 100644 --- a/client/driver/rkt_test.go +++ b/client/driver/rkt_test.go @@ -60,6 +60,7 @@ func TestRktDriver_Start(t *testing.T) { Config: map[string]string{ "trust_prefix": "coreos.com/etcd", "name": "coreos.com/etcd:v2.0.4", + "exec": "/etcd --version", }, } @@ -98,6 +99,7 @@ func TestRktDriver_Start_Wait(t *testing.T) { Config: map[string]string{ "trust_prefix": "coreos.com/etcd", "name": "coreos.com/etcd:v2.0.4", + "exec": "/etcd --version", }, } diff --git a/website/source/docs/drivers/rkt.html.md b/website/source/docs/drivers/rkt.html.md index 34fc0367d..0381c6a8f 100644 --- a/website/source/docs/drivers/rkt.html.md +++ b/website/source/docs/drivers/rkt.html.md @@ -12,7 +12,7 @@ Name: `rkt` The `Rkt` driver provides an interface for using CoreOS Rkt for running application containers. Currently, the driver supports launching -containers. +containers. ## Task Configuration @@ -21,6 +21,7 @@ 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 +* `exec` - **(Optional**) A command to execute on the ACI ## Client Requirements From 9140796a490db8c2393f71736e3365a0c52a4b20 Mon Sep 17 00:00:00 2001 From: Abhishek Chanda Date: Wed, 7 Oct 2015 22:24:16 +0000 Subject: [PATCH 15/15] Run gofmt --- client/driver/rkt.go | 298 +++++++++++++-------------- client/driver/rkt_test.go | 212 +++++++++---------- client/testutil/driver_compatible.go | 18 +- 3 files changed, 264 insertions(+), 264 deletions(-) diff --git a/client/driver/rkt.go b/client/driver/rkt.go index f2feb93de..23f4d29ee 100644 --- a/client/driver/rkt.go +++ b/client/driver/rkt.go @@ -1,214 +1,214 @@ package driver import ( - "bytes" - "encoding/json" - "fmt" - "log" - "os" - "os/exec" - "regexp" - "runtime" - "strings" - "syscall" - "time" + "bytes" + "encoding/json" + "fmt" + "log" + "os" + "os/exec" + "regexp" + "runtime" + "strings" + "syscall" + "time" - "github.com/hashicorp/nomad/client/config" - "github.com/hashicorp/nomad/nomad/structs" + "github.com/hashicorp/nomad/client/config" + "github.com/hashicorp/nomad/nomad/structs" ) var ( - reRktVersion = regexp.MustCompile("rkt version ([\\d\\.]+).+") - reAppcVersion = regexp.MustCompile("appc version ([\\d\\.]+).+") + reRktVersion = regexp.MustCompile("rkt version ([\\d\\.]+).+") + reAppcVersion = regexp.MustCompile("appc version ([\\d\\.]+).+") ) // RktDriver is a driver for running images via Rkt // We attempt to chose sane defaults for now, with more configuration available // planned in the future type RktDriver struct { - DriverContext + DriverContext } // rktHandle is returned from Start/Open as a handle to the PID type rktHandle struct { - proc *os.Process - name string - logger *log.Logger - waitCh chan error - doneCh chan struct{} + proc *os.Process + name string + logger *log.Logger + waitCh chan error + doneCh chan struct{} } // rktPID is a struct to map the pid running the process to the vm image on // disk type rktPID struct { - Pid int - Name string + Pid int + Name string } // NewRktDriver is used to create a new exec driver func NewRktDriver(ctx *DriverContext) Driver { - return &RktDriver{*ctx} + return &RktDriver{*ctx} } func (d *RktDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { - // Only enable if we are root when running on non-windows systems. - if runtime.GOOS != "windows" && syscall.Geteuid() != 0 { - d.logger.Printf("[DEBUG] driver.rkt: must run as root user, disabling") - return false, nil - } + // Only enable if we are root when running on non-windows systems. + if runtime.GOOS != "windows" && syscall.Geteuid() != 0 { + d.logger.Printf("[DEBUG] driver.rkt: must run as root user, disabling") + return false, nil + } - outBytes, err := exec.Command("rkt", "version").Output() - if err != nil { - return false, nil - } - out := strings.TrimSpace(string(outBytes)) + outBytes, err := exec.Command("rkt", "version").Output() + if err != nil { + return false, nil + } + out := strings.TrimSpace(string(outBytes)) - rktMatches := reRktVersion.FindStringSubmatch(out) - appcMatches := reRktVersion.FindStringSubmatch(out) - if len(rktMatches) != 2 || len(appcMatches) != 2 { - return false, fmt.Errorf("Unable to parse Rkt version string: %#v", rktMatches) - } + rktMatches := reRktVersion.FindStringSubmatch(out) + appcMatches := reRktVersion.FindStringSubmatch(out) + if len(rktMatches) != 2 || len(appcMatches) != 2 { + return false, fmt.Errorf("Unable to parse Rkt version string: %#v", rktMatches) + } - node.Attributes["driver.rkt"] = "true" - node.Attributes["driver.rkt.version"] = rktMatches[0] - node.Attributes["driver.rkt.appc.version"] = appcMatches[1] + node.Attributes["driver.rkt"] = "true" + node.Attributes["driver.rkt.version"] = rktMatches[0] + node.Attributes["driver.rkt.appc.version"] = appcMatches[1] - return true, nil + return true, nil } // Run an existing Rkt image. func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { - trust_prefix, ok := task.Config["trust_prefix"] - if !ok || trust_prefix == "" { - return nil, fmt.Errorf("Missing trust prefix for rkt") - } + trust_prefix, ok := task.Config["trust_prefix"] + if !ok || trust_prefix == "" { + return nil, fmt.Errorf("Missing trust prefix for rkt") + } - // Add the given trust prefix - var outBuf, errBuf bytes.Buffer - cmd := exec.Command("rkt", "trust", fmt.Sprintf("--prefix=%s", trust_prefix)) - cmd.Stdout = &outBuf - cmd.Stderr = &errBuf - d.logger.Printf("[DEBUG] driver.rkt: 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()) - } - d.logger.Printf("[DEBUG] driver.rkt: added trust prefix: %q", trust_prefix) + // Add the given trust prefix + var outBuf, errBuf bytes.Buffer + cmd := exec.Command("rkt", "trust", fmt.Sprintf("--prefix=%s", trust_prefix)) + cmd.Stdout = &outBuf + cmd.Stderr = &errBuf + d.logger.Printf("[DEBUG] driver.rkt: 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()) + } + d.logger.Printf("[DEBUG] driver.rkt: added trust prefix: %q", trust_prefix) - name, ok := task.Config["name"] - if !ok || name == "" { - return nil, fmt.Errorf("Missing ACI name for rkt") - } + name, ok := task.Config["name"] + if !ok || name == "" { + return nil, fmt.Errorf("Missing ACI name for rkt") + } - exec_cmd, ok := task.Config["exec"] - if !ok || exec_cmd == "" { - d.logger.Printf("[WARN] driver.rkt: could not find a command to execute in the ACI, the default command will be executed") - } + exec_cmd, ok := task.Config["exec"] + if !ok || exec_cmd == "" { + d.logger.Printf("[WARN] driver.rkt: could not find a command to execute in the ACI, the default command will be executed") + } - // Run the ACI - var aoutBuf, aerrBuf bytes.Buffer - run_cmd := []string{ - "rkt", - "run", - "--mds-register=false", - name, - } - if exec_cmd != "" { - splitted := strings.Fields(exec_cmd) - run_cmd = append(run_cmd, "--exec=", splitted[0], "--") - run_cmd = append(run_cmd, splitted[1:]...) - run_cmd = append(run_cmd, "---") - } - acmd := exec.Command(run_cmd[0], run_cmd[1:]...) - acmd.Stdout = &aoutBuf - acmd.Stderr = &aerrBuf - d.logger.Printf("[DEBUG] driver:rkt: starting rkt command: %q", acmd.Args) - if err := acmd.Start(); err != nil { - return nil, fmt.Errorf( - "Error running rkt: %s\n\nOutput: %s\n\nError: %s", - err, aoutBuf.String(), aerrBuf.String()) - } - d.logger.Printf("[DEBUG] driver.rkt: started ACI: %q", name) - h := &rktHandle{ - proc: acmd.Process, - name: name, - logger: d.logger, - doneCh: make(chan struct{}), - waitCh: make(chan error, 1), - } - go h.run() - return h, nil + // Run the ACI + var aoutBuf, aerrBuf bytes.Buffer + run_cmd := []string{ + "rkt", + "run", + "--mds-register=false", + name, + } + if exec_cmd != "" { + splitted := strings.Fields(exec_cmd) + run_cmd = append(run_cmd, "--exec=", splitted[0], "--") + run_cmd = append(run_cmd, splitted[1:]...) + run_cmd = append(run_cmd, "---") + } + acmd := exec.Command(run_cmd[0], run_cmd[1:]...) + acmd.Stdout = &aoutBuf + acmd.Stderr = &aerrBuf + d.logger.Printf("[DEBUG] driver:rkt: starting rkt command: %q", acmd.Args) + if err := acmd.Start(); err != nil { + return nil, fmt.Errorf( + "Error running rkt: %s\n\nOutput: %s\n\nError: %s", + err, aoutBuf.String(), aerrBuf.String()) + } + d.logger.Printf("[DEBUG] driver.rkt: started ACI: %q", name) + h := &rktHandle{ + proc: acmd.Process, + name: name, + logger: d.logger, + doneCh: make(chan struct{}), + waitCh: make(chan error, 1), + } + go h.run() + return h, nil } func (d *RktDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) { - // Parse the handle - pidBytes := []byte(strings.TrimPrefix(handleID, "Rkt:")) - qpid := &rktPID{} - if err := json.Unmarshal(pidBytes, qpid); err != nil { - return nil, fmt.Errorf("failed to parse Rkt handle '%s': %v", handleID, err) - } + // Parse the handle + pidBytes := []byte(strings.TrimPrefix(handleID, "Rkt:")) + qpid := &rktPID{} + if err := json.Unmarshal(pidBytes, qpid); err != nil { + return nil, fmt.Errorf("failed to parse Rkt handle '%s': %v", handleID, err) + } - // Find the process - proc, err := os.FindProcess(qpid.Pid) - if proc == nil || err != nil { - return nil, fmt.Errorf("failed to find Rkt PID %d: %v", qpid.Pid, err) - } + // Find the process + proc, err := os.FindProcess(qpid.Pid) + if proc == nil || err != nil { + return nil, fmt.Errorf("failed to find Rkt PID %d: %v", qpid.Pid, err) + } - // Return a driver handle - h := &rktHandle{ - proc: proc, - name: qpid.Name, - logger: d.logger, - doneCh: make(chan struct{}), - waitCh: make(chan error, 1), - } + // Return a driver handle + h := &rktHandle{ + proc: proc, + name: qpid.Name, + logger: d.logger, + doneCh: make(chan struct{}), + waitCh: make(chan error, 1), + } - go h.run() - return h, nil + go h.run() + return h, nil } func (h *rktHandle) ID() string { - // Return a handle to the PID - pid := &rktPID{ - Pid: h.proc.Pid, - Name: h.name, - } - data, err := json.Marshal(pid) - if err != nil { - h.logger.Printf("[ERR] driver.rkt: failed to marshal rkt PID to JSON: %s", err) - } - return fmt.Sprintf("Rkt:%s", string(data)) + // Return a handle to the PID + pid := &rktPID{ + Pid: h.proc.Pid, + Name: h.name, + } + data, err := json.Marshal(pid) + if err != nil { + h.logger.Printf("[ERR] driver.rkt: failed to marshal rkt PID to JSON: %s", err) + } + return fmt.Sprintf("Rkt:%s", string(data)) } func (h *rktHandle) WaitCh() chan error { - return h.waitCh + return h.waitCh } func (h *rktHandle) Update(task *structs.Task) error { - // Update is not possible - return nil + // Update is not possible + return nil } // Kill is used to terminate the task. We send an Interrupt // and then provide a 5 second grace period before doing a Kill. func (h *rktHandle) Kill() error { - h.proc.Signal(os.Interrupt) - select { - case <-h.doneCh: - return nil - case <-time.After(5 * time.Second): - return h.proc.Kill() - } + h.proc.Signal(os.Interrupt) + select { + case <-h.doneCh: + return nil + case <-time.After(5 * time.Second): + return h.proc.Kill() + } } func (h *rktHandle) run() { - ps, err := h.proc.Wait() - close(h.doneCh) - if err != nil { - h.waitCh <- err - } else if !ps.Success() { - h.waitCh <- fmt.Errorf("task exited with error") - } - close(h.waitCh) + ps, err := h.proc.Wait() + close(h.doneCh) + if err != nil { + h.waitCh <- err + } else if !ps.Success() { + h.waitCh <- fmt.Errorf("task exited with error") + } + close(h.waitCh) } diff --git a/client/driver/rkt_test.go b/client/driver/rkt_test.go index 804c56c3c..ea42b2268 100644 --- a/client/driver/rkt_test.go +++ b/client/driver/rkt_test.go @@ -1,134 +1,134 @@ package driver import ( - "fmt" - "os" - "testing" - "time" + "fmt" + "os" + "testing" + "time" - "github.com/hashicorp/nomad/client/config" - "github.com/hashicorp/nomad/nomad/structs" + "github.com/hashicorp/nomad/client/config" + "github.com/hashicorp/nomad/nomad/structs" - ctestutils "github.com/hashicorp/nomad/client/testutil" + ctestutils "github.com/hashicorp/nomad/client/testutil" ) func TestRktDriver_Handle(t *testing.T) { - h := &rktHandle{ - proc: &os.Process{Pid: 123}, - name: "foo", - doneCh: make(chan struct{}), - waitCh: make(chan error, 1), - } + h := &rktHandle{ + proc: &os.Process{Pid: 123}, + name: "foo", + doneCh: make(chan struct{}), + waitCh: make(chan error, 1), + } - actual := h.ID() - expected := `Rkt:{"Pid":123,"Name":"foo"}` - if actual != expected { - t.Errorf("Expected `%s`, found `%s`", expected, actual) - } + actual := h.ID() + expected := `Rkt:{"Pid":123,"Name":"foo"}` + if actual != expected { + t.Errorf("Expected `%s`, found `%s`", expected, actual) + } } // The fingerprinter test should always pass, even if rkt is not installed. func TestRktDriver_Fingerprint(t *testing.T) { - ctestutils.RktCompatible(t) - d := NewRktDriver(testDriverContext("")) - node := &structs.Node{ - Attributes: make(map[string]string), - } - apply, err := d.Fingerprint(&config.Config{}, node) - if err != nil { - t.Fatalf("err: %v", err) - } - if !apply { - t.Fatalf("should apply") - } - if node.Attributes["driver.rkt"] == "" { - t.Fatalf("Missing Rkt driver") - } - 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") - } + ctestutils.RktCompatible(t) + d := NewRktDriver(testDriverContext("")) + node := &structs.Node{ + Attributes: make(map[string]string), + } + apply, err := d.Fingerprint(&config.Config{}, node) + if err != nil { + t.Fatalf("err: %v", err) + } + if !apply { + t.Fatalf("should apply") + } + if node.Attributes["driver.rkt"] == "" { + t.Fatalf("Missing Rkt driver") + } + 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) { - ctestutils.RktCompatible(t) - // TODO: use test server to load from a fixture - task := &structs.Task{ - Name: "etcd", - Config: map[string]string{ - "trust_prefix": "coreos.com/etcd", - "name": "coreos.com/etcd:v2.0.4", - "exec": "/etcd --version", - }, - } + ctestutils.RktCompatible(t) + // TODO: use test server to load from a fixture + task := &structs.Task{ + Name: "etcd", + Config: map[string]string{ + "trust_prefix": "coreos.com/etcd", + "name": "coreos.com/etcd:v2.0.4", + "exec": "/etcd --version", + }, + } - driverCtx := testDriverContext(task.Name) - ctx := testDriverExecContext(task, driverCtx) - d := NewRktDriver(driverCtx) - defer ctx.AllocDir.Destroy() + 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") - } + handle, err := d.Start(ctx, task) + if err != nil { + t.Fatalf("err: %v", err) + } + if handle == nil { + t.Fatalf("missing handle") + } - // Attempt to open - handle2, err := d.Open(ctx, handle.ID()) - if err != nil { - t.Fatalf("err: %v", err) - } - if handle2 == nil { - t.Fatalf("missing handle") - } + // Attempt to open + handle2, err := d.Open(ctx, handle.ID()) + if err != nil { + t.Fatalf("err: %v", err) + } + if handle2 == nil { + t.Fatalf("missing handle") + } - // Clean up - if err := handle.Kill(); err != nil { - fmt.Printf("\nError killing Rkt test: %s", err) - } + // Clean up + if err := handle.Kill(); err != nil { + 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", - "exec": "/etcd --version", - }, - } + ctestutils.RktCompatible(t) + task := &structs.Task{ + Name: "etcd", + Config: map[string]string{ + "trust_prefix": "coreos.com/etcd", + "name": "coreos.com/etcd:v2.0.4", + "exec": "/etcd --version", + }, + } - driverCtx := testDriverContext(task.Name) - ctx := testDriverExecContext(task, driverCtx) - d := NewRktDriver(driverCtx) - defer ctx.AllocDir.Destroy() + 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() + 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) - } + // 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") - } + 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 c354a3286..b4cdfba9f 100644 --- a/client/testutil/driver_compatible.go +++ b/client/testutil/driver_compatible.go @@ -1,10 +1,10 @@ package testutil import ( + "os/exec" "runtime" "syscall" "testing" - "os/exec" ) func ExecCompatible(t *testing.T) { @@ -20,14 +20,14 @@ func QemuCompatible(t *testing.T) { } func RktCompatible(t *testing.T) { - 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() - if err != nil { - t.Skip("Must have rkt installed for rkt specific tests to run") - } + 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() + if err != nil { + t.Skip("Must have rkt installed for rkt specific tests to run") + } } func MountCompatible(t *testing.T) {