diff --git a/client/driver/exec_test.go b/client/driver/exec_test.go index 0eb27886b31..80e7083cb39 100644 --- a/client/driver/exec_test.go +++ b/client/driver/exec_test.go @@ -120,7 +120,7 @@ func TestExecDriver_Start_Wait(t *testing.T) { } } -func TestExecDriver_Start_Artifact_Wait(t *testing.T) { +func TestRawExecDriver_Start_Artifact_Wait(t *testing.T) { ctestutils.ExecCompatible(t) task := &structs.Task{ Name: "sleep", diff --git a/client/driver/raw_exec.go b/client/driver/raw_exec.go index cdc41e676ad..c72eeb7f4cd 100644 --- a/client/driver/raw_exec.go +++ b/client/driver/raw_exec.go @@ -2,14 +2,17 @@ package driver import ( "fmt" + "log" "os" "os/exec" + "path" "path/filepath" "runtime" "strconv" "strings" "time" + "github.com/hashicorp/go-getter" "github.com/hashicorp/nomad/client/allocdir" "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/client/driver/args" @@ -61,12 +64,6 @@ func (d *RawExecDriver) Fingerprint(cfg *config.Config, node *structs.Node) (boo } func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { - // Get the command - command, ok := task.Config["command"] - if !ok || command == "" { - return nil, fmt.Errorf("missing command for raw_exec driver") - } - // Get the tasks local directory. taskName := d.DriverContext.taskName taskDir, ok := ctx.AllocDir.TaskDirs[taskName] @@ -75,6 +72,35 @@ func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandl } taskLocal := filepath.Join(taskDir, allocdir.TaskLocal) + // Get the command + command, ok := task.Config["command"] + if !ok || command == "" { + source, sok := task.Config["artifact_source"] + if !sok || source == "" { + return nil, fmt.Errorf("missing command or source for exec driver") + } + + // Proceed to download an artifact to be executed. + // We use go-getter to support a variety of protocols, but need to change + // file permissions of the resulted download to be executable + destDir := filepath.Join(taskDir, allocdir.TaskLocal) + + // Create a location to download the artifact. + artifactName := path.Base(source) + command = filepath.Join(destDir, artifactName) + if err := getter.GetFile(command, source); err != nil { + return nil, fmt.Errorf("[Err] driver.Exec: Error downloading source for Exec driver: %s", err) + } + + cmd := exec.Command("chmod", "+x", command) + if err := cmd.Run(); err != nil { + log.Printf("[Err] driver.Exec: Error making artifact executable: %s", err) + } + + // re-assign the command to be the local execution path + command = filepath.Join(allocdir.TaskLocal, artifactName) + } + // Get the environment variables. envVars := TaskEnvironmentVariables(ctx, task) diff --git a/client/driver/raw_exec_test.go b/client/driver/raw_exec_test.go index f3b63e12e43..85ca7ebc9ce 100644 --- a/client/driver/raw_exec_test.go +++ b/client/driver/raw_exec_test.go @@ -92,6 +92,50 @@ func TestRawExecDriver_StartOpen_Wait(t *testing.T) { } } +func TestRawExecDriver_StartOpen_Artifact_Source_Wait(t *testing.T) { + task := &structs.Task{ + Name: "sleep", + Config: map[string]string{ + "artifact_source": "https://dl.dropboxusercontent.com/u/47675/jar_thing/hi_linux_amd64", + }, + } + driverCtx := testDriverContext(task.Name) + ctx := testDriverExecContext(task, driverCtx) + defer ctx.AllocDir.Destroy() + + d := NewRawExecDriver(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") + } + + // Task should terminate quickly + select { + case <-handle2.WaitCh(): + case <-time.After(2 * time.Second): + t.Fatalf("timeout") + } + + // Check they are both tracking the same PID. + pid1 := handle.(*rawExecHandle).proc.Pid + pid2 := handle2.(*rawExecHandle).proc.Pid + if pid1 != pid2 { + t.Fatalf("tracking incorrect Pid; %v != %v", pid1, pid2) + } +} + func TestRawExecDriver_Start_Wait(t *testing.T) { task := &structs.Task{ Name: "sleep", diff --git a/website/source/docs/drivers/raw_exec.html.md b/website/source/docs/drivers/raw_exec.html.md index 35b0c95bf76..9fc3c38342b 100644 --- a/website/source/docs/drivers/raw_exec.html.md +++ b/website/source/docs/drivers/raw_exec.html.md @@ -19,7 +19,8 @@ As such, it should be used with extreme care and is disabled by default. The `raw_exec` driver supports the following configuration in the job spec: * `command` - The command to execute. Must be provided. - +* `artifact_source` – Source location of an executable artifact. Must be accessible +from the Nomad client * `args` - The argument list to the command, space seperated. Optional. ## Client Requirements @@ -35,6 +36,9 @@ options = { } ``` +You must specify either a `command` or a `artifact_source` to be executed. Any +`command` is assumed to be present on the running client. + ## Client Attributes The `raw_exec` driver will set the following client attributes: