From f12c957b4dae67c8b8132ab411eb216eaebfc905 Mon Sep 17 00:00:00 2001 From: James Rasell Date: Tue, 25 Apr 2023 15:47:44 +0100 Subject: [PATCH] scale: do not allow scaling of jobs with type system. (#16969) --- .changelog/16969.txt | 3 +++ nomad/job_endpoint.go | 6 ++++++ nomad/job_endpoint_test.go | 28 ++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 .changelog/16969.txt diff --git a/.changelog/16969.txt b/.changelog/16969.txt new file mode 100644 index 00000000000..8739671abd4 --- /dev/null +++ b/.changelog/16969.txt @@ -0,0 +1,3 @@ +```release-note:bug +scale: Do not allow scale requests for jobs of type system +``` diff --git a/nomad/job_endpoint.go b/nomad/job_endpoint.go index 264bea4398c..664703941ec 100644 --- a/nomad/job_endpoint.go +++ b/nomad/job_endpoint.go @@ -1054,9 +1054,15 @@ func (j *Job) Scale(args *structs.JobScaleRequest, reply *structs.JobRegisterRes return err } + // Perform validation on the job to ensure we have something that can + // actually be scaled. This logic can only exist here, as we need access + // to the job object. if job == nil { return structs.NewErrRPCCoded(404, fmt.Sprintf("job %q not found", args.JobID)) } + if job.Type == structs.JobTypeSystem { + return structs.NewErrRPCCoded(http.StatusBadRequest, `cannot scale jobs of type "system"`) + } // Since job is going to be mutated we must copy it since state store methods // return a shared pointer. diff --git a/nomad/job_endpoint_test.go b/nomad/job_endpoint_test.go index 99536ed92fc..e88972d6eff 100644 --- a/nomad/job_endpoint_test.go +++ b/nomad/job_endpoint_test.go @@ -7840,6 +7840,34 @@ func TestJobEndpoint_Scale_Priority(t *testing.T) { requireAssertion.NotZero(eval.ModifyTime) } +func TestJobEndpoint_Scale_SystemJob(t *testing.T) { + ci.Parallel(t) + + testServer, testServerCleanup := TestServer(t, nil) + defer testServerCleanup() + codec := rpcClient(t, testServer) + testutil.WaitForLeader(t, testServer.RPC) + state := testServer.fsm.State() + + mockSystemJob := mock.SystemJob() + must.NoError(t, state.UpsertJob(structs.MsgTypeTestSetup, 10, nil, mockSystemJob)) + + scaleReq := &structs.JobScaleRequest{ + JobID: mockSystemJob.ID, + Target: map[string]string{ + structs.ScalingTargetGroup: mockSystemJob.TaskGroups[0].Name, + }, + Count: pointer.Of(int64(13)), + WriteRequest: structs.WriteRequest{ + Region: DefaultRegion, + Namespace: mockSystemJob.Namespace, + }, + } + var resp structs.JobRegisterResponse + must.ErrorContains(t, msgpackrpc.CallWithCodec(codec, "Job.Scale", scaleReq, &resp), + `400,cannot scale jobs of type "system"`) +} + func TestJobEndpoint_InvalidCount(t *testing.T) { ci.Parallel(t) require := require.New(t)