diff --git a/api/util_test.go b/api/util_test.go index cc6276b0f..c91ae0738 100644 --- a/api/util_test.go +++ b/api/util_test.go @@ -21,6 +21,7 @@ func assertWriteMeta(t *testing.T, wm *WriteMeta) { func testJob() *Job { task := NewTask("task1", "exec"). + SetConfig("command", "/bin/sleep"). Require(&Resources{ CPU: 100, MemoryMB: 256, diff --git a/command/agent/job_endpoint_test.go b/command/agent/job_endpoint_test.go index 4c71e7f8c..98599392d 100644 --- a/command/agent/job_endpoint_test.go +++ b/command/agent/job_endpoint_test.go @@ -67,6 +67,7 @@ func TestHTTP_PrefixJobsList(t *testing.T) { // Create the job job := mock.Job() job.ID = ids[i] + job.TaskGroups[0].Count = 1 args := structs.JobRegisterRequest{ Job: job, WriteRequest: structs.WriteRequest{Region: "global"}, diff --git a/command/util_test.go b/command/util_test.go index 67d712b62..2eb2a7433 100644 --- a/command/util_test.go +++ b/command/util_test.go @@ -40,6 +40,7 @@ func testServer( func testJob(jobID string) *api.Job { task := api.NewTask("task1", "exec"). + SetConfig("command", "/bin/sleep"). Require(&api.Resources{ MemoryMB: 256, DiskMB: 20, diff --git a/nomad/job_endpoint.go b/nomad/job_endpoint.go index a88a27f19..9f3a616b2 100644 --- a/nomad/job_endpoint.go +++ b/nomad/job_endpoint.go @@ -6,6 +6,8 @@ import ( "github.com/armon/go-metrics" "github.com/hashicorp/go-memdb" + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/nomad/client/driver" "github.com/hashicorp/nomad/nomad/structs" "github.com/hashicorp/nomad/nomad/watch" ) @@ -34,6 +36,31 @@ func (j *Job) Register(args *structs.JobRegisterRequest, reply *structs.JobRegis return err } + // Validate the driver configurations. + var driverErrors multierror.Error + for _, tg := range args.Job.TaskGroups { + for _, task := range tg.Tasks { + d, err := driver.NewDriver( + task.Driver, + driver.NewEmptyDriverContext(), + ) + if err != nil { + msg := "failed to create driver for task %q in group %q for validation: %v" + driverErrors.Errors = append(driverErrors.Errors, fmt.Errorf(msg, tg.Name, task.Name, err)) + continue + } + + if err := d.Validate(task.Config); err != nil { + formatted := fmt.Errorf("group %q -> task %q -> config: %v", tg.Name, task.Name, err) + driverErrors.Errors = append(driverErrors.Errors, formatted) + } + } + } + + if len(driverErrors.Errors) != 0 { + return driverErrors.ErrorOrNil() + } + if args.Job.Type == structs.JobTypeCore { return fmt.Errorf("job type cannot be core") } diff --git a/nomad/job_endpoint_test.go b/nomad/job_endpoint_test.go index b83db2f4e..c65e81047 100644 --- a/nomad/job_endpoint_test.go +++ b/nomad/job_endpoint_test.go @@ -2,6 +2,7 @@ package nomad import ( "reflect" + "strings" "testing" "time" @@ -85,6 +86,35 @@ func TestJobEndpoint_Register(t *testing.T) { } } +func TestJobEndpoint_Register_InvalidDriverConfig(t *testing.T) { + s1 := testServer(t, func(c *Config) { + c.NumSchedulers = 0 // Prevent automatic dequeue + }) + defer s1.Shutdown() + codec := rpcClient(t, s1) + testutil.WaitForLeader(t, s1.RPC) + + // Create the register request with a job containing an invalid driver + // config + job := mock.Job() + job.TaskGroups[0].Tasks[0].Config["foo"] = 1 + req := &structs.JobRegisterRequest{ + Job: job, + WriteRequest: structs.WriteRequest{Region: "global"}, + } + + // Fetch the response + var resp structs.JobRegisterResponse + err := msgpackrpc.CallWithCodec(codec, "Job.Register", req, &resp) + if err == nil { + t.Fatalf("expected a validation error") + } + + if !strings.Contains(err.Error(), "-> config:") { + t.Fatalf("expected a driver config validation error but got: %v", err) + } +} + func TestJobEndpoint_Register_Existing(t *testing.T) { s1 := testServer(t, func(c *Config) { c.NumSchedulers = 0 // Prevent automatic dequeue diff --git a/scripts/test.sh b/scripts/test.sh index 293216d54..30b1560d0 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -10,4 +10,4 @@ go build -o $TEMPDIR/nomad || exit 1 # Run the tests echo "--> Running tests" -go list ./... | grep -v '/vendor/' | sudo -E PATH=$TEMPDIR:$PATH xargs -n1 go test -cover -timeout=300s +go list ./... | grep -v '/vendor/' | sudo -E PATH=$TEMPDIR:$PATH xargs -n1 go test -cover -timeout=360s