open-nomad/e2e/eval_priority/eval_priority.go
James Rasell 751c8217d1
core: allow setting and propagation of eval priority on job de/registration (#11532)
This change modifies the Nomad job register and deregister RPCs to
accept an updated option set which includes eval priority. This
param is optional and override the use of the job priority to set
the eval priority.

In order to ensure all evaluations as a result of the request use
the same eval priority, the priority is shared to the
allocReconciler and deploymentWatcher. This creates a new
distinction between eval priority and job priority.

The Nomad agent HTTP API has been modified to allow setting the
eval priority on job update and delete. To keep consistency with
the current v1 API, job update accepts this as a payload param;
job delete accepts this as a query param.

Any user supplied value is validated within the agent HTTP handler
removing the need to pass invalid requests to the server.

The register and deregister opts functions now all for setting
the eval priority on requests.

The change includes a small change to the DeregisterOpts function
which handles nil opts. This brings the function inline with the
RegisterOpts.
2021-11-23 09:23:31 +01:00

190 lines
6.5 KiB
Go

package eval_priority
import (
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/e2e/e2eutil"
"github.com/hashicorp/nomad/e2e/framework"
"github.com/hashicorp/nomad/helper/uuid"
)
type EvalPriorityTest struct {
framework.TC
jobIDs []string
}
func init() {
framework.AddSuites(&framework.TestSuite{
Component: "EvalPriority",
CanRunLocal: true,
Cases: []framework.TestCase{
new(EvalPriorityTest),
},
})
}
func (tc *EvalPriorityTest) BeforeAll(f *framework.F) {
e2eutil.WaitForLeader(f.T(), tc.Nomad())
e2eutil.WaitForNodesReady(f.T(), tc.Nomad(), 1)
}
func (tc *EvalPriorityTest) AfterEach(f *framework.F) {
for _, id := range tc.jobIDs {
_, _, err := tc.Nomad().Jobs().Deregister(id, true, nil)
f.NoError(err)
}
tc.jobIDs = []string{}
_, err := e2eutil.Command("nomad", "system", "gc")
f.NoError(err)
}
// TestEvalPrioritySet performs a test which registers, updates, and
// deregsiters a job setting the eval priority on every call.
func (tc *EvalPriorityTest) TestEvalPrioritySet(f *framework.F) {
// Generate a jobID and attempt to register the job using the eval
// priority. In case there is a problem found here and the job registers,
// we need to ensure it gets cleaned up.
jobID := "test-eval-priority-" + uuid.Generate()[0:8]
f.NoError(e2eutil.RegisterWithArgs(jobID, "eval_priority/inputs/thirteen_job_priority.nomad",
"-eval-priority=80"))
tc.jobIDs = append(tc.jobIDs, jobID)
// Wait for the deployment to finish.
f.NoError(e2eutil.WaitForLastDeploymentStatus(jobID, "default", "successful", nil))
// Pull the job evaluation list from the API and ensure that this didn't
// error and contains two evals.
//
// Eval 1: the job registration eval.
// Eval 2: the deployment watcher eval.
registerEvals, _, err := tc.Nomad().Jobs().Evaluations(jobID, nil)
f.NoError(err)
f.Len(registerEvals, 2, "job expected to have two evals")
// seenEvals tracks the evaluations we have tested for priority quality
// against our expected value. This allows us to easily perform multiple
// checks with confidence.
seenEvals := map[string]bool{}
// All evaluations should have the priority set to the overridden priority.
for _, eval := range registerEvals {
f.Equal(80, eval.Priority)
seenEvals[eval.ID] = true
}
// Update the job image and set an eval priority higher than the job
// priority.
f.NoError(e2eutil.RegisterWithArgs(jobID, "eval_priority/inputs/thirteen_job_priority.nomad",
"-eval-priority=7", "-var", "image=busybox:1.34"))
f.NoError(e2eutil.WaitForLastDeploymentStatus(jobID, "default", "successful",
&e2eutil.WaitConfig{Retries: 200}))
// Pull the latest list of evaluations for the job which will include those
// as a result of the job update.
updateEvals, _, err := tc.Nomad().Jobs().Evaluations(jobID, nil)
f.NoError(err)
f.NotNil(updateEvals, "expected non-nil evaluation list response")
f.NotEmpty(updateEvals, "expected non-empty evaluation list response")
// Iterate the evals, ignoring those we have already seen and check their
// priority is as expected.
for _, eval := range updateEvals {
if ok := seenEvals[eval.ID]; ok {
continue
}
f.Equal(7, eval.Priority)
seenEvals[eval.ID] = true
}
// Deregister the job using an increased priority.
deregOpts := api.DeregisterOptions{EvalPriority: 100, Purge: true}
deregEvalID, _, err := tc.Nomad().Jobs().DeregisterOpts(jobID, &deregOpts, nil)
f.NoError(err)
f.NotEmpty(deregEvalID, "expected non-empty evaluation ID")
// Detail the deregistration evaluation and check its priority.
evalInfo, _, err := tc.Nomad().Evaluations().Info(deregEvalID, nil)
f.NoError(err)
f.Equal(100, evalInfo.Priority)
// If the job was successfully purged, clear the test suite state.
if err == nil {
tc.jobIDs = []string{}
}
}
// TestEvalPriorityNotSet performs a test which registers, updates, and
// deregsiters a job never setting the eval priority.
func (tc *EvalPriorityTest) TestEvalPriorityNotSet(f *framework.F) {
// Generate a jobID and attempt to register the job using the eval
// priority. In case there is a problem found here and the job registers,
// we need to ensure it gets cleaned up.
jobID := "test-eval-priority-" + uuid.Generate()[0:8]
f.NoError(e2eutil.Register(jobID, "eval_priority/inputs/thirteen_job_priority.nomad"))
tc.jobIDs = append(tc.jobIDs, jobID)
// Wait for the deployment to finish.
f.NoError(e2eutil.WaitForLastDeploymentStatus(jobID, "default", "successful", nil))
// Pull the job evaluation list from the API and ensure that this didn't
// error and contains two evals.
//
// Eval 1: the job registration eval.
// Eval 2: the deployment watcher eval.
registerEvals, _, err := tc.Nomad().Jobs().Evaluations(jobID, nil)
f.NoError(err)
f.Len(registerEvals, 2, "job expected to have two evals")
// seenEvals tracks the evaluations we have tested for priority quality
// against our expected value. This allows us to easily perform multiple
// checks with confidence.
seenEvals := map[string]bool{}
// All evaluations should have the priority set to the job priority.
for _, eval := range registerEvals {
f.Equal(13, eval.Priority)
seenEvals[eval.ID] = true
}
// Update the job image without setting an eval priority.
f.NoError(e2eutil.RegisterWithArgs(jobID, "eval_priority/inputs/thirteen_job_priority.nomad",
"-var", "image=busybox:1.34"))
f.NoError(e2eutil.WaitForLastDeploymentStatus(jobID, "default", "successful",
&e2eutil.WaitConfig{Retries: 200}))
// Pull the latest list of evaluations for the job which will include those
// as a result of the job update.
updateEvals, _, err := tc.Nomad().Jobs().Evaluations(jobID, nil)
f.NoError(err)
f.NotNil(updateEvals, "expected non-nil evaluation list response")
f.NotEmpty(updateEvals, "expected non-empty evaluation list response")
// Iterate the evals, ignoring those we have already seen and check their
// priority is as expected.
for _, eval := range updateEvals {
if ok := seenEvals[eval.ID]; ok {
continue
}
f.Equal(13, eval.Priority)
seenEvals[eval.ID] = true
}
// Deregister the job without setting an eval priority.
deregOpts := api.DeregisterOptions{Purge: true}
deregEvalID, _, err := tc.Nomad().Jobs().DeregisterOpts(jobID, &deregOpts, nil)
f.NoError(err)
f.NotEmpty(deregEvalID, "expected non-empty evaluation ID")
// Detail the deregistration evaluation and check its priority.
evalInfo, _, err := tc.Nomad().Evaluations().Info(deregEvalID, nil)
f.NoError(err)
f.Equal(13, evalInfo.Priority)
// If the job was successfully purged, clear the test suite state.
if err == nil {
tc.jobIDs = []string{}
}
}