cli: `-namespace` should override job namespace

When a jobspec doesn't include a namespace, we provide it with the default
namespace, but this ends up overriding the explicit `-namespace` flag. This
changeset uses the same logic as region parsing to create an order of
precedence: the query string parameter (the `-namespace` flag) overrides the
API request body which overrides the jobspec.
This commit is contained in:
Tim Gross 2021-07-08 13:13:43 -04:00
parent 8f25a9d7cd
commit a3bc87a2eb
3 changed files with 99 additions and 3 deletions

3
.changelog/10875.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:bug
cli: Fixed a bug where `-namespace` flag was not respected for `job run` and `job plan` commands.
```

View File

@ -706,10 +706,9 @@ func (s *HTTPServer) apiJobAndRequestToStructs(job *api.Job, req *http.Request,
AuthToken: apiReq.SecretID,
}
queryRegion := req.URL.Query().Get("region")
s.parseToken(req, &writeReq.AuthToken)
parseNamespace(req, &writeReq.Namespace)
queryRegion := req.URL.Query().Get("region")
requestRegion, jobRegion := regionForJob(
job, queryRegion, writeReq.Region, s.agent.config.Region,
)
@ -717,7 +716,11 @@ func (s *HTTPServer) apiJobAndRequestToStructs(job *api.Job, req *http.Request,
sJob := ApiJobToStructJob(job)
sJob.Region = jobRegion
writeReq.Region = requestRegion
writeReq.Namespace = sJob.Namespace
queryNamespace := req.URL.Query().Get("namespace")
namespace := namespaceForJob(job.Namespace, queryNamespace, writeReq.Namespace)
sJob.Namespace = namespace
writeReq.Namespace = namespace
return sJob, writeReq
}
@ -774,6 +777,25 @@ func regionForJob(job *api.Job, queryRegion, apiRegion, agentRegion string) (str
return requestRegion, jobRegion
}
func namespaceForJob(jobNamespace *string, queryNamespace, apiNamespace string) string {
// Namespace in query param (-namespace flag) takes precedence.
if queryNamespace != "" {
return queryNamespace
}
// Next the request body...
if apiNamespace != "" {
return apiNamespace
}
if jobNamespace != nil && *jobNamespace != "" {
return *jobNamespace
}
return structs.DefaultNamespace
}
func ApiJobToStructJob(job *api.Job) *structs.Job {
job.Canonicalize()

View File

@ -1845,6 +1845,77 @@ func TestJobs_RegionForJob(t *testing.T) {
}
}
func TestJobs_NamespaceForJob(t *testing.T) {
t.Parallel()
// test namespace for pointer inputs
ns := "dev"
cases := []struct {
name string
job *api.Job
queryNamespace string
apiNamespace string
expected string
}{
{
name: "no namespace provided",
job: &api.Job{},
expected: structs.DefaultNamespace,
},
{
name: "jobspec has namespace",
job: &api.Job{Namespace: &ns},
expected: "dev",
},
{
name: "-namespace flag overrides empty job namespace",
job: &api.Job{},
queryNamespace: "prod",
expected: "prod",
},
{
name: "-namespace flag overrides job namespace",
job: &api.Job{Namespace: &ns},
queryNamespace: "prod",
expected: "prod",
},
{
name: "-namespace flag overrides job namespace even if default",
job: &api.Job{Namespace: &ns},
queryNamespace: structs.DefaultNamespace,
expected: structs.DefaultNamespace,
},
{
name: "API param overrides empty job namespace",
job: &api.Job{},
apiNamespace: "prod",
expected: "prod",
},
{
name: "-namespace flag overrides API param",
job: &api.Job{Namespace: &ns},
queryNamespace: "prod",
apiNamespace: "whatever",
expected: "prod",
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
require.Equal(t, tc.expected,
namespaceForJob(tc.job.Namespace, tc.queryNamespace, tc.apiNamespace),
)
})
}
}
func TestJobs_ApiJobToStructsJob(t *testing.T) {
apiJob := &api.Job{
Stop: helper.BoolToPtr(true),