diff --git a/command/agent/job_endpoint_test.go b/command/agent/job_endpoint_test.go index 8c6109a44..3dc25b5bc 100644 --- a/command/agent/job_endpoint_test.go +++ b/command/agent/job_endpoint_test.go @@ -1276,9 +1276,9 @@ func TestHTTP_JobActions(t *testing.T) { // Two actions by default, both in Task web and Group web must.Len(t, 2, actionsResp, must.Sprint("expected 2 actions")) - must.Eq(t, "date test", actionsResp[0].Name) + must.Eq(t, "date-test", actionsResp[0].Name) - must.Eq(t, "echo test", actionsResp[1].Name) + must.Eq(t, "echo-test", actionsResp[1].Name) // Both have Args lists length of 1 must.Len(t, 1, actionsResp[0].Args, must.Sprint("expected 1 arg")) @@ -1351,9 +1351,9 @@ func TestHTTP_JobActions(t *testing.T) { dateTestCount := 0 echoTestCount := 0 for _, action := range actionsResp3 { - if action.Name == "date test" { + if action.Name == "date-test" { dateTestCount++ - } else if action.Name == "echo test" { + } else if action.Name == "echo-test" { echoTestCount++ } } diff --git a/command/agent/testingutils_test.go b/command/agent/testingutils_test.go index 51787cb1d..0162951cf 100644 --- a/command/agent/testingutils_test.go +++ b/command/agent/testingutils_test.go @@ -81,12 +81,12 @@ func MockJob() *api.Job { // actions Actions: []*api.Action{ { - Name: "date test", + Name: "date-test", Command: "/bin/date", Args: []string{"-u"}, }, { - Name: "echo test", + Name: "echo-test", Command: "/bin/echo", Args: []string{"hello world"}, }, diff --git a/nomad/mock/job.go b/nomad/mock/job.go index 226db4521..0e4600ec9 100644 --- a/nomad/mock/job.go +++ b/nomad/mock/job.go @@ -78,12 +78,12 @@ func Job() *structs.Job { }, Actions: []*structs.Action{ { - Name: "date test", + Name: "date-test", Command: "/bin/date", Args: []string{"-u"}, }, { - Name: "echo test", + Name: "echo-test", Command: "/bin/echo", Args: []string{"hello world"}, }, @@ -706,7 +706,7 @@ func BigBenchmarkJob() *structs.Job { return job } -// A multi-group, multi-task job with actions testing. +// ActionsJob produces a multi-group, multi-task job with actions for testing. func ActionsJob() *structs.Job { job := MinJob() @@ -726,12 +726,12 @@ func ActionsJob() *structs.Job { for _, task := range tg.Tasks { task.Actions = []*structs.Action{ { - Name: "date test", + Name: "date-test", Command: "/bin/date", Args: []string{"-u"}, }, { - Name: "echo test", + Name: "echo-test", Command: "/bin/echo", Args: []string{"hello world"}, }, diff --git a/nomad/structs/actions.go b/nomad/structs/actions.go index aada647a4..78a2f9810 100644 --- a/nomad/structs/actions.go +++ b/nomad/structs/actions.go @@ -9,11 +9,16 @@ package structs import ( "errors" + "fmt" + "regexp" "slices" "github.com/hashicorp/go-multierror" ) +// validJobActionName is used to validate a job action name. +var validJobActionName = regexp.MustCompile("^[a-zA-Z0-9-]{1,128}$") + type Action struct { Name string Command string @@ -60,7 +65,10 @@ func (a *Action) Validate() error { var mErr *multierror.Error if a.Command == "" { - mErr = multierror.Append(mErr, errors.New("Missing command")) + mErr = multierror.Append(mErr, errors.New("command cannot be empty")) + } + if !validJobActionName.MatchString(a.Name) { + mErr = multierror.Append(mErr, fmt.Errorf("invalid name '%s'", a.Name)) } return mErr.ErrorOrNil() diff --git a/nomad/structs/actions_test.go b/nomad/structs/actions_test.go index 09c457855..463fd1929 100644 --- a/nomad/structs/actions_test.go +++ b/nomad/structs/actions_test.go @@ -149,13 +149,41 @@ func TestAction_Validate(t *testing.T) { expectedError: nil, }, { - name: "empty command", - inputAction: &Action{}, - expectedError: errors.New("Missing command"), + name: "empty command", + inputAction: &Action{ + Name: "adrian-iv", + }, + expectedError: errors.New("command cannot be empty"), }, { - name: "valid", - inputAction: &Action{Command: "env"}, + name: "empty name", + inputAction: &Action{ + Command: "env", + }, + expectedError: errors.New(`invalid name ''`), + }, + { + name: "too long name", + inputAction: &Action{ + Name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + Command: "env", + }, + expectedError: errors.New(`invalid name 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'`), + }, + { + name: "invalid character name", + inputAction: &Action{ + Name: `\//?|?|?%&%@$&£@$)`, + Command: "env", + }, + expectedError: errors.New(`invalid name '\//?|?|?%&%@$&£@$)'`), + }, + { + name: "valid", + inputAction: &Action{ + Name: "adrian-iv", + Command: "env", + }, expectedError: nil, }, }