diff --git a/CHANGELOG.md b/CHANGELOG.md index 7394460df3c..7b167f2cfa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ FEATURES: BUG FIXES: + * core: Fixed a bug where `nomad job plan` reports success and no updates if the job contains a scaling policy [[GH-8551](https://github.com/hashicorp/nomad/issues/8567)] * api: Added missing namespace field to scaling status GET response object [[GH-8527](https://github.com/hashicorp/nomad/issues/8527)] * api: Do not allow submission of jobs of type `system` that include task groups with scaling stanzas [[GH-8481](https://github.com/hashicorp/nomad/issues/8481)] * vault: Fixed a bug where upgrades from pre-0.11.3 that use Vault can lead to memory spikes and write large Raft messages. [[GH-8553](https://github.com/hashicorp/nomad/issues/8553)] diff --git a/nomad/job_endpoint.go b/nomad/job_endpoint.go index 7763c17092a..834ead17bba 100644 --- a/nomad/job_endpoint.go +++ b/nomad/job_endpoint.go @@ -1672,6 +1672,11 @@ func (j *Job) Plan(args *structs.JobPlanRequest, reply *structs.JobPlanResponse) return err } + // Ensure that all scaling policies have an appropriate ID + if err := propagateScalingPolicyIDs(oldJob, args.Job); err != nil { + return err + } + var index uint64 var updatedIndex uint64 @@ -1683,11 +1688,16 @@ func (j *Job) Plan(args *structs.JobPlanRequest, reply *structs.JobPlanResponse) if oldJob.SpecChanged(args.Job) { // Insert the updated Job into the snapshot updatedIndex = oldJob.JobModifyIndex + 1 - snap.UpsertJob(updatedIndex, args.Job) + if err := snap.UpsertJob(updatedIndex, args.Job); err != nil { + return err + } } } else if oldJob == nil { // Insert the updated Job into the snapshot - snap.UpsertJob(100, args.Job) + err := snap.UpsertJob(100, args.Job) + if err != nil { + return err + } } // Create an eval and mark it as requiring annotations and insert that as well diff --git a/nomad/job_endpoint_test.go b/nomad/job_endpoint_test.go index a0c4faf8bbe..7e72030a406 100644 --- a/nomad/job_endpoint_test.go +++ b/nomad/job_endpoint_test.go @@ -5369,6 +5369,42 @@ func TestJobEndpoint_Plan_NoDiff(t *testing.T) { } } +// TestJobEndpoint_Plan_Scaling asserts that the plan endpoint handles +// jobs with scaling stanza +func TestJobEndpoint_Plan_Scaling(t *testing.T) { + t.Parallel() + + s1, cleanupS1 := TestServer(t, func(c *Config) { + c.NumSchedulers = 0 // Prevent automatic dequeue + }) + defer cleanupS1() + codec := rpcClient(t, s1) + testutil.WaitForLeader(t, s1.RPC) + + // Create a plan request + job := mock.Job() + tg := job.TaskGroups[0] + tg.Tasks[0].Resources.MemoryMB = 999999999 + scaling := &structs.ScalingPolicy{Min: 1, Max: 100} + tg.Scaling = scaling.TargetTaskGroup(job, tg) + planReq := &structs.JobPlanRequest{ + Job: job, + Diff: false, + WriteRequest: structs.WriteRequest{ + Region: "global", + Namespace: job.Namespace, + }, + } + + // Try without a token, expect failure + var planResp structs.JobPlanResponse + err := msgpackrpc.CallWithCodec(codec, "Job.Plan", planReq, &planResp) + require.NoError(t, err) + + require.NotEmpty(t, planResp.FailedTGAllocs) + require.Contains(t, planResp.FailedTGAllocs, tg.Name) +} + func TestJobEndpoint_ImplicitConstraints_Vault(t *testing.T) { t.Parallel()