Skip to content

Commit

Permalink
in Job.Scale, ensure that new count is within [min,max] configured in…
Browse files Browse the repository at this point in the history
… scaling policy

resolves #9758
  • Loading branch information
cgbaker authored and backspace committed Jan 22, 2021
1 parent 96a6463 commit 547db7f
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 0 deletions.
12 changes: 12 additions & 0 deletions nomad/job_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,18 @@ func (j *Job) Scale(args *structs.JobScaleRequest, reply *structs.JobRegisterRes
prevCount := found.Count
if args.Count != nil {

// if there is a scaling policy, check that the new count is within bounds
if found.Scaling != nil {
if *args.Count < found.Scaling.Min {
return structs.NewErrRPCCoded(400,
fmt.Sprintf("new scaling count cannot be less than the scaling policy minimum"))
}
if found.Scaling.Max < *args.Count {
return structs.NewErrRPCCoded(400,
fmt.Sprintf("new scaling count cannot be greater than the scaling policy maximum"))
}
}

// Lookup the latest deployment, to see whether this scaling event should be blocked
d, err := snap.LatestDeploymentByJobID(ws, namespace, args.JobID)
if err != nil {
Expand Down
44 changes: 44 additions & 0 deletions nomad/job_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6705,6 +6705,50 @@ func TestJobEndpoint_Scale_Invalid(t *testing.T) {
require.Contains(err.Error(), "should not contain count if error is true")
}

func TestJobEndpoint_Scale_OutOfBounds(t *testing.T) {
t.Parallel()
require := require.New(t)

s1, cleanupS1 := TestServer(t, nil)
defer cleanupS1()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
state := s1.fsm.State()

job, pol := mock.JobWithScalingPolicy()
pol.Min = 3
pol.Max = 10
job.TaskGroups[0].Count = 5

// register the job
err := state.UpsertJob(structs.MsgTypeTestSetup, 1000, job)
require.Nil(err)

var resp structs.JobRegisterResponse
scale := &structs.JobScaleRequest{
JobID: job.ID,
Target: map[string]string{
structs.ScalingTargetGroup: job.TaskGroups[0].Name,
},
Count: helper.Int64ToPtr(pol.Max + 1),
Message: "too high",
PolicyOverride: false,
WriteRequest: structs.WriteRequest{
Region: "global",
Namespace: job.Namespace,
},
}
err = msgpackrpc.CallWithCodec(codec, "Job.Scale", scale, &resp)
require.Error(err)
require.Contains(err.Error(), "cannot be greater than")

scale.Count = helper.Int64ToPtr(pol.Min - 1)
scale.Message = "too low"
err = msgpackrpc.CallWithCodec(codec, "Job.Scale", scale, &resp)
require.Error(err)
require.Contains(err.Error(), "cannot be less than")
}

func TestJobEndpoint_Scale_NoEval(t *testing.T) {
t.Parallel()
require := require.New(t)
Expand Down

0 comments on commit 547db7f

Please sign in to comment.