From 2d88d57e52b498c5aec440dd2cb75d95ebee7675 Mon Sep 17 00:00:00 2001 From: Chris Baker <1675087+cgbaker@users.noreply.github.com> Date: Sat, 21 Mar 2020 15:03:22 +0000 Subject: [PATCH] fixed http endpoints for job.register and job.scalestatus --- api/scaling.go | 2 +- command/agent/job_endpoint.go | 18 +- command/agent/job_endpoint_test.go | 31 +- command/agent/scaling_endpoint_test.go | 2068 ------------------------ 4 files changed, 21 insertions(+), 2098 deletions(-) diff --git a/api/scaling.go b/api/scaling.go index 3948b9921..e2323edfa 100644 --- a/api/scaling.go +++ b/api/scaling.go @@ -69,7 +69,7 @@ type ScalingPolicyListStub struct { ModifyIndex uint64 } -// JobScaleStatusResponse is the payload for a generic scaling action +// JobScaleStatusResponse is used to return information about job scaling status type JobScaleStatusResponse struct { JobID string JobCreateIndex uint64 diff --git a/command/agent/job_endpoint.go b/command/agent/job_endpoint.go index aafc52839..65196a6ed 100644 --- a/command/agent/job_endpoint.go +++ b/command/agent/job_endpoint.go @@ -473,32 +473,24 @@ func (s *HTTPServer) jobScale(resp http.ResponseWriter, req *http.Request, func (s *HTTPServer) jobScaleStatus(resp http.ResponseWriter, req *http.Request, jobName string) (interface{}, error) { - args := structs.JobSpecificRequest{ + args := structs.JobScaleStatusRequest{ JobID: jobName, } if s.parse(resp, req, &args.Region, &args.QueryOptions) { return nil, nil } - var out structs.SingleJobResponse - if err := s.agent.RPC("Job.GetJob", &args, &out); err != nil { + var out structs.JobScaleStatusResponse + if err := s.agent.RPC("Job.ScaleStatus", &args, &out); err != nil { return nil, err } setMeta(resp, &out.QueryMeta) - if out.Job == nil { + if out.JobScaleStatus == nil { return nil, CodedError(404, "job not found") } - status := &api.JobScaleStatusResponse{ - JobID: out.Job.ID, - JobCreateIndex: out.Job.CreateIndex, - JobModifyIndex: out.Job.ModifyIndex, - Stopped: out.Job.Stop, - TaskGroups: nil, // TODO - } - - return status, nil + return out.JobScaleStatus, nil } func (s *HTTPServer) jobScaleAction(resp http.ResponseWriter, req *http.Request, diff --git a/command/agent/job_endpoint_test.go b/command/agent/job_endpoint_test.go index afbf0f607..e7934681f 100644 --- a/command/agent/job_endpoint_test.go +++ b/command/agent/job_endpoint_test.go @@ -666,14 +666,14 @@ func TestHTTP_JobDelete(t *testing.T) { }) } -func TestHTTP_Job_GroupScale(t *testing.T) { +func TestHTTP_Job_ScaleTaskGroup(t *testing.T) { t.Parallel() require := require.New(t) httpTest(t, nil, func(s *TestAgent) { // Create the job - job, policy := mock.JobWithScalingPolicy() + job := mock.Job() args := structs.JobRegisterRequest{ Job: job, WriteRequest: structs.WriteRequest{ @@ -682,22 +682,21 @@ func TestHTTP_Job_GroupScale(t *testing.T) { }, } var resp structs.JobRegisterResponse - if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil { - t.Fatalf("err: %v", err) - } - - // FINISH: cgbaker: do something with args.reason + require.NoError(s.Agent.RPC("Job.Register", &args, &resp)) newCount := job.TaskGroups[0].Count + 1 scaleReq := &api.ScalingRequest{ - JobID: job.ID, - Value: newCount, - Reason: "testing", + Count: int64(newCount), + Reason: helper.StringToPtr("testing"), + Target: map[string]string{ + "Job": job.ID, + "Group": job.TaskGroups[0].Name, + }, } buf := encodeReq(scaleReq) // Make the HTTP request to scale the job group - req, err := http.NewRequest("POST", policy.Target, buf) + req, err := http.NewRequest("POST", "/v1/job/"+job.ID+"/scale", buf) require.NoError(err) respW := httptest.NewRecorder() @@ -728,14 +727,14 @@ func TestHTTP_Job_GroupScale(t *testing.T) { }) } -func TestHTTP_Job_GroupScaleStatus(t *testing.T) { +func TestHTTP_Job_ScaleStatus(t *testing.T) { t.Parallel() require := require.New(t) httpTest(t, nil, func(s *TestAgent) { // Create the job - job, policy := mock.JobWithScalingPolicy() + job := mock.Job() args := structs.JobRegisterRequest{ Job: job, WriteRequest: structs.WriteRequest{ @@ -749,7 +748,7 @@ func TestHTTP_Job_GroupScaleStatus(t *testing.T) { } // Make the HTTP request to scale the job group - req, err := http.NewRequest("GET", policy.Target, nil) + req, err := http.NewRequest("GET", "/v1/job/"+job.ID+"/scale", nil) require.NoError(err) respW := httptest.NewRecorder() @@ -758,9 +757,9 @@ func TestHTTP_Job_GroupScaleStatus(t *testing.T) { require.NoError(err) // Check the response - status := obj.(*api.JobScaleStatusResponse) + status := obj.(*structs.JobScaleStatus) require.NotEmpty(resp.EvalID) - require.Equal(job.TaskGroups[0].Count, status.Value.(int)) + require.Equal(job.TaskGroups[0].Count, status.TaskGroups[job.TaskGroups[0].Name].Desired) // Check for the index require.NotEmpty(respW.Header().Get("X-Nomad-Index")) diff --git a/command/agent/scaling_endpoint_test.go b/command/agent/scaling_endpoint_test.go index c0d44737c..10c3c4186 100644 --- a/command/agent/scaling_endpoint_test.go +++ b/command/agent/scaling_endpoint_test.go @@ -104,2071 +104,3 @@ func TestHTTP_ScalingPolicyGet(t *testing.T) { require.Equal(p.ID, obj.(*structs.ScalingPolicy).ID) }) } - -// func TestHTTP_JobQuery_Payload(t *testing.T) { -// t.Parallel() -// httpTest(t, nil, func(s *TestAgent) { -// // Create the job -// job := mock.Job() -// -// // Insert Payload compressed -// expected := []byte("hello world") -// compressed := snappy.Encode(nil, expected) -// job.Payload = compressed -// -// // Directly manipulate the state -// state := s.Agent.server.State() -// if err := state.UpsertJob(1000, job); err != nil { -// t.Fatalf("Failed to upsert job: %v", err) -// } -// -// // Make the HTTP request -// req, err := http.NewRequest("GET", "/v1/job/"+job.ID, nil) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// respW := httptest.NewRecorder() -// -// // Make the request -// obj, err := s.Server.JobSpecificRequest(respW, req) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Check for the index -// if respW.Header().Get("X-Nomad-Index") == "" { -// t.Fatalf("missing index") -// } -// if respW.Header().Get("X-Nomad-KnownLeader") != "true" { -// t.Fatalf("missing known leader") -// } -// if respW.Header().Get("X-Nomad-LastContact") == "" { -// t.Fatalf("missing last contact") -// } -// -// // Check the job -// j := obj.(*structs.Job) -// if j.ID != job.ID { -// t.Fatalf("bad: %#v", j) -// } -// -// // Check the payload is decompressed -// if !reflect.DeepEqual(j.Payload, expected) { -// t.Fatalf("Payload not decompressed properly; got %#v; want %#v", j.Payload, expected) -// } -// }) -// } -// -// func TestHTTP_JobUpdate(t *testing.T) { -// t.Parallel() -// httpTest(t, nil, func(s *TestAgent) { -// // Create the job -// job := MockJob() -// args := api.JobRegisterRequest{ -// Job: job, -// WriteRequest: api.WriteRequest{ -// Region: "global", -// Namespace: api.DefaultNamespace, -// }, -// } -// buf := encodeReq(args) -// -// // Make the HTTP request -// req, err := http.NewRequest("PUT", "/v1/job/"+*job.ID, buf) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// respW := httptest.NewRecorder() -// -// // Make the request -// obj, err := s.Server.JobSpecificRequest(respW, req) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Check the response -// dereg := obj.(structs.JobRegisterResponse) -// if dereg.EvalID == "" { -// t.Fatalf("bad: %v", dereg) -// } -// -// // Check for the index -// if respW.Header().Get("X-Nomad-Index") == "" { -// t.Fatalf("missing index") -// } -// -// // Check the job is registered -// getReq := structs.JobSpecificRequest{ -// JobID: *job.ID, -// QueryOptions: structs.QueryOptions{ -// Region: "global", -// Namespace: structs.DefaultNamespace, -// }, -// } -// var getResp structs.SingleJobResponse -// if err := s.Agent.RPC("Job.GetJob", &getReq, &getResp); err != nil { -// t.Fatalf("err: %v", err) -// } -// -// if getResp.Job == nil { -// t.Fatalf("job does not exist") -// } -// }) -// } -// -// func TestHTTP_JobUpdateRegion(t *testing.T) { -// t.Parallel() -// -// cases := []struct { -// Name string -// ConfigRegion string -// APIRegion string -// ExpectedRegion string -// }{ -// { -// Name: "api region takes precedence", -// ConfigRegion: "not-global", -// APIRegion: "north-america", -// ExpectedRegion: "north-america", -// }, -// { -// Name: "config region is set", -// ConfigRegion: "north-america", -// APIRegion: "", -// ExpectedRegion: "north-america", -// }, -// { -// Name: "api region is set", -// ConfigRegion: "", -// APIRegion: "north-america", -// ExpectedRegion: "north-america", -// }, -// { -// Name: "defaults to node region global if no region is provided", -// ConfigRegion: "", -// APIRegion: "", -// ExpectedRegion: "global", -// }, -// { -// Name: "defaults to node region not-global if no region is provided", -// ConfigRegion: "", -// APIRegion: "", -// ExpectedRegion: "not-global", -// }, -// } -// -// for _, tc := range cases { -// t.Run(tc.Name, func(t *testing.T) { -// httpTest(t, func(c *Config) { c.Region = tc.ExpectedRegion }, func(s *TestAgent) { -// // Create the job -// job := MockRegionalJob() -// -// if tc.ConfigRegion == "" { -// job.Region = nil -// } else { -// job.Region = &tc.ConfigRegion -// } -// -// args := api.JobRegisterRequest{ -// Job: job, -// WriteRequest: api.WriteRequest{ -// Namespace: api.DefaultNamespace, -// Region: tc.APIRegion, -// }, -// } -// -// buf := encodeReq(args) -// -// // Make the HTTP request -// url := "/v1/job/" + *job.ID -// -// req, err := http.NewRequest("PUT", url, buf) -// require.NoError(t, err) -// respW := httptest.NewRecorder() -// -// // Make the request -// obj, err := s.Server.JobSpecificRequest(respW, req) -// require.NoError(t, err) -// -// // Check the response -// dereg := obj.(structs.JobRegisterResponse) -// require.NotEmpty(t, dereg.EvalID) -// -// // Check for the index -// require.NotEmpty(t, respW.Header().Get("X-Nomad-Index"), "missing index") -// -// // Check the job is registered -// getReq := structs.JobSpecificRequest{ -// JobID: *job.ID, -// QueryOptions: structs.QueryOptions{ -// Region: tc.ExpectedRegion, -// Namespace: structs.DefaultNamespace, -// }, -// } -// var getResp structs.SingleJobResponse -// err = s.Agent.RPC("Job.GetJob", &getReq, &getResp) -// require.NoError(t, err) -// require.NotNil(t, getResp.Job, "job does not exist") -// require.Equal(t, tc.ExpectedRegion, getResp.Job.Region) -// }) -// }) -// } -// } -// -// func TestHTTP_JobDelete(t *testing.T) { -// t.Parallel() -// httpTest(t, nil, func(s *TestAgent) { -// // Create the job -// job := mock.Job() -// args := structs.JobRegisterRequest{ -// Job: job, -// WriteRequest: structs.WriteRequest{ -// Region: "global", -// Namespace: structs.DefaultNamespace, -// }, -// } -// var resp structs.JobRegisterResponse -// if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Make the HTTP request to do a soft delete -// req, err := http.NewRequest("DELETE", "/v1/job/"+job.ID, nil) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// respW := httptest.NewRecorder() -// -// // Make the request -// obj, err := s.Server.JobSpecificRequest(respW, req) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Check the response -// dereg := obj.(structs.JobDeregisterResponse) -// if dereg.EvalID == "" { -// t.Fatalf("bad: %v", dereg) -// } -// -// // Check for the index -// if respW.Header().Get("X-Nomad-Index") == "" { -// t.Fatalf("missing index") -// } -// -// // Check the job is still queryable -// getReq1 := structs.JobSpecificRequest{ -// JobID: job.ID, -// QueryOptions: structs.QueryOptions{ -// Region: "global", -// Namespace: structs.DefaultNamespace, -// }, -// } -// var getResp1 structs.SingleJobResponse -// if err := s.Agent.RPC("Job.GetJob", &getReq1, &getResp1); err != nil { -// t.Fatalf("err: %v", err) -// } -// if getResp1.Job == nil { -// t.Fatalf("job doesn't exists") -// } -// if !getResp1.Job.Stop { -// t.Fatalf("job should be marked as stop") -// } -// -// // Make the HTTP request to do a purge delete -// req2, err := http.NewRequest("DELETE", "/v1/job/"+job.ID+"?purge=true", nil) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// respW.Flush() -// -// // Make the request -// obj, err = s.Server.JobSpecificRequest(respW, req2) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Check the response -// dereg = obj.(structs.JobDeregisterResponse) -// if dereg.EvalID == "" { -// t.Fatalf("bad: %v", dereg) -// } -// -// // Check for the index -// if respW.Header().Get("X-Nomad-Index") == "" { -// t.Fatalf("missing index") -// } -// -// // Check the job is gone -// getReq2 := structs.JobSpecificRequest{ -// JobID: job.ID, -// QueryOptions: structs.QueryOptions{ -// Region: "global", -// Namespace: structs.DefaultNamespace, -// }, -// } -// var getResp2 structs.SingleJobResponse -// if err := s.Agent.RPC("Job.GetJob", &getReq2, &getResp2); err != nil { -// t.Fatalf("err: %v", err) -// } -// if getResp2.Job != nil { -// t.Fatalf("job still exists") -// } -// }) -// } -// -// func TestHTTP_JobForceEvaluate(t *testing.T) { -// t.Parallel() -// httpTest(t, nil, func(s *TestAgent) { -// // Create the job -// job := mock.Job() -// args := structs.JobRegisterRequest{ -// Job: job, -// WriteRequest: structs.WriteRequest{ -// Region: "global", -// Namespace: structs.DefaultNamespace, -// }, -// } -// var resp structs.JobRegisterResponse -// if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Make the HTTP request -// req, err := http.NewRequest("POST", "/v1/job/"+job.ID+"/evaluate", nil) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// respW := httptest.NewRecorder() -// -// // Make the request -// obj, err := s.Server.JobSpecificRequest(respW, req) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Check the response -// reg := obj.(structs.JobRegisterResponse) -// if reg.EvalID == "" { -// t.Fatalf("bad: %v", reg) -// } -// -// // Check for the index -// if respW.Header().Get("X-Nomad-Index") == "" { -// t.Fatalf("missing index") -// } -// }) -// } -// -// func TestHTTP_JobEvaluate_ForceReschedule(t *testing.T) { -// t.Parallel() -// httpTest(t, nil, func(s *TestAgent) { -// // Create the job -// job := mock.Job() -// args := structs.JobRegisterRequest{ -// Job: job, -// WriteRequest: structs.WriteRequest{ -// Region: "global", -// Namespace: structs.DefaultNamespace, -// }, -// } -// var resp structs.JobRegisterResponse -// if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil { -// t.Fatalf("err: %v", err) -// } -// jobEvalReq := api.JobEvaluateRequest{ -// JobID: job.ID, -// EvalOptions: api.EvalOptions{ -// ForceReschedule: true, -// }, -// } -// -// buf := encodeReq(jobEvalReq) -// -// // Make the HTTP request -// req, err := http.NewRequest("POST", "/v1/job/"+job.ID+"/evaluate", buf) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// respW := httptest.NewRecorder() -// -// // Make the request -// obj, err := s.Server.JobSpecificRequest(respW, req) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Check the response -// reg := obj.(structs.JobRegisterResponse) -// if reg.EvalID == "" { -// t.Fatalf("bad: %v", reg) -// } -// -// // Check for the index -// if respW.Header().Get("X-Nomad-Index") == "" { -// t.Fatalf("missing index") -// } -// }) -// } -// -// func TestHTTP_JobEvaluations(t *testing.T) { -// t.Parallel() -// httpTest(t, nil, func(s *TestAgent) { -// // Create the job -// job := mock.Job() -// args := structs.JobRegisterRequest{ -// Job: job, -// WriteRequest: structs.WriteRequest{ -// Region: "global", -// Namespace: structs.DefaultNamespace, -// }, -// } -// var resp structs.JobRegisterResponse -// if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Make the HTTP request -// req, err := http.NewRequest("GET", "/v1/job/"+job.ID+"/evaluations", nil) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// respW := httptest.NewRecorder() -// -// // Make the request -// obj, err := s.Server.JobSpecificRequest(respW, req) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Check the response -// evals := obj.([]*structs.Evaluation) -// // Can be multiple evals, use the last one, since they are in order -// idx := len(evals) - 1 -// if len(evals) < 0 || evals[idx].ID != resp.EvalID { -// t.Fatalf("bad: %v", evals) -// } -// -// // Check for the index -// if respW.Header().Get("X-Nomad-Index") == "" { -// t.Fatalf("missing index") -// } -// if respW.Header().Get("X-Nomad-KnownLeader") != "true" { -// t.Fatalf("missing known leader") -// } -// if respW.Header().Get("X-Nomad-LastContact") == "" { -// t.Fatalf("missing last contact") -// } -// }) -// } -// -// func TestHTTP_JobAllocations(t *testing.T) { -// t.Parallel() -// httpTest(t, nil, func(s *TestAgent) { -// // Create the job -// alloc1 := mock.Alloc() -// args := structs.JobRegisterRequest{ -// Job: alloc1.Job, -// WriteRequest: structs.WriteRequest{ -// Region: "global", -// Namespace: structs.DefaultNamespace, -// }, -// } -// var resp structs.JobRegisterResponse -// if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Directly manipulate the state -// expectedDisplayMsg := "test message" -// testEvent := structs.NewTaskEvent("test event").SetMessage(expectedDisplayMsg) -// var events []*structs.TaskEvent -// events = append(events, testEvent) -// taskState := &structs.TaskState{Events: events} -// alloc1.TaskStates = make(map[string]*structs.TaskState) -// alloc1.TaskStates["test"] = taskState -// state := s.Agent.server.State() -// err := state.UpsertAllocs(1000, []*structs.Allocation{alloc1}) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Make the HTTP request -// req, err := http.NewRequest("GET", "/v1/job/"+alloc1.Job.ID+"/allocations?all=true", nil) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// respW := httptest.NewRecorder() -// -// // Make the request -// obj, err := s.Server.JobSpecificRequest(respW, req) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Check the response -// allocs := obj.([]*structs.AllocListStub) -// if len(allocs) != 1 && allocs[0].ID != alloc1.ID { -// t.Fatalf("bad: %v", allocs) -// } -// displayMsg := allocs[0].TaskStates["test"].Events[0].DisplayMessage -// assert.Equal(t, expectedDisplayMsg, displayMsg) -// -// // Check for the index -// if respW.Header().Get("X-Nomad-Index") == "" { -// t.Fatalf("missing index") -// } -// if respW.Header().Get("X-Nomad-KnownLeader") != "true" { -// t.Fatalf("missing known leader") -// } -// if respW.Header().Get("X-Nomad-LastContact") == "" { -// t.Fatalf("missing last contact") -// } -// }) -// } -// -// func TestHTTP_JobDeployments(t *testing.T) { -// assert := assert.New(t) -// t.Parallel() -// httpTest(t, nil, func(s *TestAgent) { -// // Create the job -// j := mock.Job() -// args := structs.JobRegisterRequest{ -// Job: j, -// WriteRequest: structs.WriteRequest{ -// Region: "global", -// Namespace: structs.DefaultNamespace, -// }, -// } -// var resp structs.JobRegisterResponse -// assert.Nil(s.Agent.RPC("Job.Register", &args, &resp), "JobRegister") -// -// // Directly manipulate the state -// state := s.Agent.server.State() -// d := mock.Deployment() -// d.JobID = j.ID -// d.JobCreateIndex = resp.JobModifyIndex -// -// assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment") -// -// // Make the HTTP request -// req, err := http.NewRequest("GET", "/v1/job/"+j.ID+"/deployments", nil) -// assert.Nil(err, "HTTP") -// respW := httptest.NewRecorder() -// -// // Make the request -// obj, err := s.Server.JobSpecificRequest(respW, req) -// assert.Nil(err, "JobSpecificRequest") -// -// // Check the response -// deploys := obj.([]*structs.Deployment) -// assert.Len(deploys, 1, "deployments") -// assert.Equal(d.ID, deploys[0].ID, "deployment id") -// -// assert.NotZero(respW.Header().Get("X-Nomad-Index"), "missing index") -// assert.Equal("true", respW.Header().Get("X-Nomad-KnownLeader"), "missing known leader") -// assert.NotZero(respW.Header().Get("X-Nomad-LastContact"), "missing last contact") -// }) -// } -// -// func TestHTTP_JobDeployment(t *testing.T) { -// assert := assert.New(t) -// t.Parallel() -// httpTest(t, nil, func(s *TestAgent) { -// // Create the job -// j := mock.Job() -// args := structs.JobRegisterRequest{ -// Job: j, -// WriteRequest: structs.WriteRequest{ -// Region: "global", -// Namespace: structs.DefaultNamespace, -// }, -// } -// var resp structs.JobRegisterResponse -// assert.Nil(s.Agent.RPC("Job.Register", &args, &resp), "JobRegister") -// -// // Directly manipulate the state -// state := s.Agent.server.State() -// d := mock.Deployment() -// d.JobID = j.ID -// d.JobCreateIndex = resp.JobModifyIndex -// assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment") -// -// // Make the HTTP request -// req, err := http.NewRequest("GET", "/v1/job/"+j.ID+"/deployment", nil) -// assert.Nil(err, "HTTP") -// respW := httptest.NewRecorder() -// -// // Make the request -// obj, err := s.Server.JobSpecificRequest(respW, req) -// assert.Nil(err, "JobSpecificRequest") -// -// // Check the response -// out := obj.(*structs.Deployment) -// assert.NotNil(out, "deployment") -// assert.Equal(d.ID, out.ID, "deployment id") -// -// assert.NotZero(respW.Header().Get("X-Nomad-Index"), "missing index") -// assert.Equal("true", respW.Header().Get("X-Nomad-KnownLeader"), "missing known leader") -// assert.NotZero(respW.Header().Get("X-Nomad-LastContact"), "missing last contact") -// }) -// } -// -// func TestHTTP_JobVersions(t *testing.T) { -// t.Parallel() -// httpTest(t, nil, func(s *TestAgent) { -// // Create the job -// job := mock.Job() -// args := structs.JobRegisterRequest{ -// Job: job, -// WriteRequest: structs.WriteRequest{ -// Region: "global", -// Namespace: structs.DefaultNamespace, -// }, -// } -// var resp structs.JobRegisterResponse -// if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil { -// t.Fatalf("err: %v", err) -// } -// -// job2 := mock.Job() -// job2.ID = job.ID -// job2.Priority = 100 -// -// args2 := structs.JobRegisterRequest{ -// Job: job2, -// WriteRequest: structs.WriteRequest{ -// Region: "global", -// Namespace: structs.DefaultNamespace, -// }, -// } -// var resp2 structs.JobRegisterResponse -// if err := s.Agent.RPC("Job.Register", &args2, &resp2); err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Make the HTTP request -// req, err := http.NewRequest("GET", "/v1/job/"+job.ID+"/versions?diffs=true", nil) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// respW := httptest.NewRecorder() -// -// // Make the request -// obj, err := s.Server.JobSpecificRequest(respW, req) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Check the response -// vResp := obj.(structs.JobVersionsResponse) -// versions := vResp.Versions -// if len(versions) != 2 { -// t.Fatalf("got %d versions; want 2", len(versions)) -// } -// -// if v := versions[0]; v.Version != 1 || v.Priority != 100 { -// t.Fatalf("bad %v", v) -// } -// -// if v := versions[1]; v.Version != 0 { -// t.Fatalf("bad %v", v) -// } -// -// if len(vResp.Diffs) != 1 { -// t.Fatalf("bad %v", vResp) -// } -// -// // Check for the index -// if respW.Header().Get("X-Nomad-Index") == "" { -// t.Fatalf("missing index") -// } -// if respW.Header().Get("X-Nomad-KnownLeader") != "true" { -// t.Fatalf("missing known leader") -// } -// if respW.Header().Get("X-Nomad-LastContact") == "" { -// t.Fatalf("missing last contact") -// } -// }) -// } -// -// func TestHTTP_PeriodicForce(t *testing.T) { -// t.Parallel() -// httpTest(t, nil, func(s *TestAgent) { -// // Create and register a periodic job. -// job := mock.PeriodicJob() -// args := structs.JobRegisterRequest{ -// Job: job, -// WriteRequest: structs.WriteRequest{ -// Region: "global", -// Namespace: structs.DefaultNamespace, -// }, -// } -// var resp structs.JobRegisterResponse -// if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Make the HTTP request -// req, err := http.NewRequest("POST", "/v1/job/"+job.ID+"/periodic/force", nil) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// respW := httptest.NewRecorder() -// -// // Make the request -// obj, err := s.Server.JobSpecificRequest(respW, req) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Check for the index -// if respW.HeaderMap.Get("X-Nomad-Index") == "" { -// t.Fatalf("missing index") -// } -// -// // Check the response -// r := obj.(structs.PeriodicForceResponse) -// if r.EvalID == "" { -// t.Fatalf("bad: %#v", r) -// } -// }) -// } -// -// func TestHTTP_JobPlan(t *testing.T) { -// t.Parallel() -// httpTest(t, nil, func(s *TestAgent) { -// // Create the job -// job := MockJob() -// args := api.JobPlanRequest{ -// Job: job, -// Diff: true, -// WriteRequest: api.WriteRequest{ -// Region: "global", -// Namespace: api.DefaultNamespace, -// }, -// } -// buf := encodeReq(args) -// -// // Make the HTTP request -// req, err := http.NewRequest("PUT", "/v1/job/"+*job.ID+"/plan", buf) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// respW := httptest.NewRecorder() -// -// // Make the request -// obj, err := s.Server.JobSpecificRequest(respW, req) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Check the response -// plan := obj.(structs.JobPlanResponse) -// if plan.Annotations == nil { -// t.Fatalf("bad: %v", plan) -// } -// -// if plan.Diff == nil { -// t.Fatalf("bad: %v", plan) -// } -// }) -// } -// -// func TestHTTP_JobPlanRegion(t *testing.T) { -// t.Parallel() -// -// cases := []struct { -// Name string -// ConfigRegion string -// APIRegion string -// ExpectedRegion string -// }{ -// { -// Name: "api region takes precedence", -// ConfigRegion: "not-global", -// APIRegion: "north-america", -// ExpectedRegion: "north-america", -// }, -// { -// Name: "config region is set", -// ConfigRegion: "north-america", -// APIRegion: "", -// ExpectedRegion: "north-america", -// }, -// { -// Name: "api region is set", -// ConfigRegion: "", -// APIRegion: "north-america", -// ExpectedRegion: "north-america", -// }, -// { -// Name: "falls back to default if no region is provided", -// ConfigRegion: "", -// APIRegion: "", -// ExpectedRegion: "global", -// }, -// } -// -// for _, tc := range cases { -// t.Run(tc.Name, func(t *testing.T) { -// httpTest(t, func(c *Config) { c.Region = tc.ExpectedRegion }, func(s *TestAgent) { -// // Create the job -// job := MockRegionalJob() -// -// if tc.ConfigRegion == "" { -// job.Region = nil -// } else { -// job.Region = &tc.ConfigRegion -// } -// -// args := api.JobPlanRequest{ -// Job: job, -// Diff: true, -// WriteRequest: api.WriteRequest{ -// Region: tc.APIRegion, -// Namespace: api.DefaultNamespace, -// }, -// } -// buf := encodeReq(args) -// -// // Make the HTTP request -// req, err := http.NewRequest("PUT", "/v1/job/"+*job.ID+"/plan", buf) -// require.NoError(t, err) -// respW := httptest.NewRecorder() -// -// // Make the request -// obj, err := s.Server.JobSpecificRequest(respW, req) -// require.NoError(t, err) -// -// // Check the response -// plan := obj.(structs.JobPlanResponse) -// require.NotNil(t, plan.Annotations) -// require.NotNil(t, plan.Diff) -// }) -// }) -// } -// } -// -// func TestHTTP_JobDispatch(t *testing.T) { -// t.Parallel() -// httpTest(t, nil, func(s *TestAgent) { -// // Create the parameterized job -// job := mock.BatchJob() -// job.ParameterizedJob = &structs.ParameterizedJobConfig{} -// -// args := structs.JobRegisterRequest{ -// Job: job, -// WriteRequest: structs.WriteRequest{ -// Region: "global", -// Namespace: structs.DefaultNamespace, -// }, -// } -// var resp structs.JobRegisterResponse -// if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Make the request -// respW := httptest.NewRecorder() -// args2 := structs.JobDispatchRequest{ -// WriteRequest: structs.WriteRequest{ -// Region: "global", -// Namespace: structs.DefaultNamespace, -// }, -// } -// buf := encodeReq(args2) -// -// // Make the HTTP request -// req2, err := http.NewRequest("PUT", "/v1/job/"+job.ID+"/dispatch", buf) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// respW.Flush() -// -// // Make the request -// obj, err := s.Server.JobSpecificRequest(respW, req2) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Check the response -// dispatch := obj.(structs.JobDispatchResponse) -// if dispatch.EvalID == "" { -// t.Fatalf("bad: %v", dispatch) -// } -// -// if dispatch.DispatchedJobID == "" { -// t.Fatalf("bad: %v", dispatch) -// } -// }) -// } -// -// func TestHTTP_JobRevert(t *testing.T) { -// t.Parallel() -// httpTest(t, nil, func(s *TestAgent) { -// // Create the job and register it twice -// job := mock.Job() -// regReq := structs.JobRegisterRequest{ -// Job: job, -// WriteRequest: structs.WriteRequest{ -// Region: "global", -// Namespace: structs.DefaultNamespace, -// }, -// } -// var regResp structs.JobRegisterResponse -// if err := s.Agent.RPC("Job.Register", ®Req, ®Resp); err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Change the job to get a new version -// job.Datacenters = append(job.Datacenters, "foo") -// if err := s.Agent.RPC("Job.Register", ®Req, ®Resp); err != nil { -// t.Fatalf("err: %v", err) -// } -// -// args := structs.JobRevertRequest{ -// JobID: job.ID, -// JobVersion: 0, -// WriteRequest: structs.WriteRequest{ -// Region: "global", -// Namespace: structs.DefaultNamespace, -// }, -// } -// buf := encodeReq(args) -// -// // Make the HTTP request -// req, err := http.NewRequest("PUT", "/v1/job/"+job.ID+"/revert", buf) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// respW := httptest.NewRecorder() -// -// // Make the request -// obj, err := s.Server.JobSpecificRequest(respW, req) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Check the response -// revertResp := obj.(structs.JobRegisterResponse) -// if revertResp.EvalID == "" { -// t.Fatalf("bad: %v", revertResp) -// } -// -// // Check for the index -// if respW.HeaderMap.Get("X-Nomad-Index") == "" { -// t.Fatalf("missing index") -// } -// }) -// } -// -// func TestHTTP_JobStable(t *testing.T) { -// t.Parallel() -// httpTest(t, nil, func(s *TestAgent) { -// // Create the job and register it twice -// job := mock.Job() -// regReq := structs.JobRegisterRequest{ -// Job: job, -// WriteRequest: structs.WriteRequest{ -// Region: "global", -// Namespace: structs.DefaultNamespace, -// }, -// } -// var regResp structs.JobRegisterResponse -// if err := s.Agent.RPC("Job.Register", ®Req, ®Resp); err != nil { -// t.Fatalf("err: %v", err) -// } -// -// if err := s.Agent.RPC("Job.Register", ®Req, ®Resp); err != nil { -// t.Fatalf("err: %v", err) -// } -// -// args := structs.JobStabilityRequest{ -// JobID: job.ID, -// JobVersion: 0, -// Stable: true, -// WriteRequest: structs.WriteRequest{ -// Region: "global", -// Namespace: structs.DefaultNamespace, -// }, -// } -// buf := encodeReq(args) -// -// // Make the HTTP request -// req, err := http.NewRequest("PUT", "/v1/job/"+job.ID+"/stable", buf) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// respW := httptest.NewRecorder() -// -// // Make the request -// obj, err := s.Server.JobSpecificRequest(respW, req) -// if err != nil { -// t.Fatalf("err: %v", err) -// } -// -// // Check the response -// stableResp := obj.(structs.JobStabilityResponse) -// if stableResp.Index == 0 { -// t.Fatalf("bad: %v", stableResp) -// } -// -// // Check for the index -// if respW.HeaderMap.Get("X-Nomad-Index") == "" { -// t.Fatalf("missing index") -// } -// }) -// } -// -// func TestJobs_ApiJobToStructsJob(t *testing.T) { -// apiJob := &api.Job{ -// Stop: helper.BoolToPtr(true), -// Region: helper.StringToPtr("global"), -// Namespace: helper.StringToPtr("foo"), -// ID: helper.StringToPtr("foo"), -// ParentID: helper.StringToPtr("lol"), -// Name: helper.StringToPtr("name"), -// Type: helper.StringToPtr("service"), -// Priority: helper.IntToPtr(50), -// AllAtOnce: helper.BoolToPtr(true), -// Datacenters: []string{"dc1", "dc2"}, -// Constraints: []*api.Constraint{ -// { -// LTarget: "a", -// RTarget: "b", -// Operand: "c", -// }, -// }, -// Affinities: []*api.Affinity{ -// { -// LTarget: "a", -// RTarget: "b", -// Operand: "c", -// Weight: helper.Int8ToPtr(50), -// }, -// }, -// Update: &api.UpdateStrategy{ -// Stagger: helper.TimeToPtr(1 * time.Second), -// MaxParallel: helper.IntToPtr(5), -// HealthCheck: helper.StringToPtr(structs.UpdateStrategyHealthCheck_Manual), -// MinHealthyTime: helper.TimeToPtr(1 * time.Minute), -// HealthyDeadline: helper.TimeToPtr(3 * time.Minute), -// ProgressDeadline: helper.TimeToPtr(3 * time.Minute), -// AutoRevert: helper.BoolToPtr(false), -// Canary: helper.IntToPtr(1), -// }, -// Spreads: []*api.Spread{ -// { -// Attribute: "${meta.rack}", -// Weight: helper.Int8ToPtr(100), -// SpreadTarget: []*api.SpreadTarget{ -// { -// Value: "r1", -// Percent: 50, -// }, -// }, -// }, -// }, -// Periodic: &api.PeriodicConfig{ -// Enabled: helper.BoolToPtr(true), -// Spec: helper.StringToPtr("spec"), -// SpecType: helper.StringToPtr("cron"), -// ProhibitOverlap: helper.BoolToPtr(true), -// TimeZone: helper.StringToPtr("test zone"), -// }, -// ParameterizedJob: &api.ParameterizedJobConfig{ -// Payload: "payload", -// MetaRequired: []string{"a", "b"}, -// MetaOptional: []string{"c", "d"}, -// }, -// Payload: []byte("payload"), -// Meta: map[string]string{ -// "foo": "bar", -// }, -// TaskGroups: []*api.TaskGroup{ -// { -// Name: helper.StringToPtr("group1"), -// Count: helper.IntToPtr(5), -// Constraints: []*api.Constraint{ -// { -// LTarget: "x", -// RTarget: "y", -// Operand: "z", -// }, -// }, -// Affinities: []*api.Affinity{ -// { -// LTarget: "x", -// RTarget: "y", -// Operand: "z", -// Weight: helper.Int8ToPtr(100), -// }, -// }, -// RestartPolicy: &api.RestartPolicy{ -// Interval: helper.TimeToPtr(1 * time.Second), -// Attempts: helper.IntToPtr(5), -// Delay: helper.TimeToPtr(10 * time.Second), -// Mode: helper.StringToPtr("delay"), -// }, -// ReschedulePolicy: &api.ReschedulePolicy{ -// Interval: helper.TimeToPtr(12 * time.Hour), -// Attempts: helper.IntToPtr(5), -// DelayFunction: helper.StringToPtr("constant"), -// Delay: helper.TimeToPtr(30 * time.Second), -// Unlimited: helper.BoolToPtr(true), -// MaxDelay: helper.TimeToPtr(20 * time.Minute), -// }, -// Migrate: &api.MigrateStrategy{ -// MaxParallel: helper.IntToPtr(12), -// HealthCheck: helper.StringToPtr("task_events"), -// MinHealthyTime: helper.TimeToPtr(12 * time.Hour), -// HealthyDeadline: helper.TimeToPtr(12 * time.Hour), -// }, -// Spreads: []*api.Spread{ -// { -// Attribute: "${node.datacenter}", -// Weight: helper.Int8ToPtr(100), -// SpreadTarget: []*api.SpreadTarget{ -// { -// Value: "dc1", -// Percent: 100, -// }, -// }, -// }, -// }, -// EphemeralDisk: &api.EphemeralDisk{ -// SizeMB: helper.IntToPtr(100), -// Sticky: helper.BoolToPtr(true), -// Migrate: helper.BoolToPtr(true), -// }, -// Update: &api.UpdateStrategy{ -// HealthCheck: helper.StringToPtr(structs.UpdateStrategyHealthCheck_Checks), -// MinHealthyTime: helper.TimeToPtr(2 * time.Minute), -// HealthyDeadline: helper.TimeToPtr(5 * time.Minute), -// ProgressDeadline: helper.TimeToPtr(5 * time.Minute), -// AutoRevert: helper.BoolToPtr(true), -// }, -// Meta: map[string]string{ -// "key": "value", -// }, -// Services: []*api.Service{ -// { -// Name: "groupserviceA", -// Tags: []string{"a", "b"}, -// CanaryTags: []string{"d", "e"}, -// PortLabel: "1234", -// Meta: map[string]string{ -// "servicemeta": "foobar", -// }, -// CheckRestart: &api.CheckRestart{ -// Limit: 4, -// Grace: helper.TimeToPtr(11 * time.Second), -// }, -// Checks: []api.ServiceCheck{ -// { -// Id: "hello", -// Name: "bar", -// Type: "http", -// Command: "foo", -// Args: []string{"a", "b"}, -// Path: "/check", -// Protocol: "http", -// PortLabel: "foo", -// AddressMode: "driver", -// GRPCService: "foo.Bar", -// GRPCUseTLS: true, -// Interval: 4 * time.Second, -// Timeout: 2 * time.Second, -// InitialStatus: "ok", -// CheckRestart: &api.CheckRestart{ -// Limit: 3, -// IgnoreWarnings: true, -// }, -// TaskName: "task1", -// }, -// }, -// Connect: &api.ConsulConnect{ -// Native: false, -// SidecarService: &api.ConsulSidecarService{ -// Tags: []string{"f", "g"}, -// Port: "9000", -// }, -// }, -// }, -// }, -// Tasks: []*api.Task{ -// { -// Name: "task1", -// Leader: true, -// Driver: "docker", -// User: "mary", -// Config: map[string]interface{}{ -// "lol": "code", -// }, -// Env: map[string]string{ -// "hello": "world", -// }, -// Constraints: []*api.Constraint{ -// { -// LTarget: "x", -// RTarget: "y", -// Operand: "z", -// }, -// }, -// Affinities: []*api.Affinity{ -// { -// LTarget: "a", -// RTarget: "b", -// Operand: "c", -// Weight: helper.Int8ToPtr(50), -// }, -// }, -// -// Services: []*api.Service{ -// { -// Id: "id", -// Name: "serviceA", -// Tags: []string{"1", "2"}, -// CanaryTags: []string{"3", "4"}, -// PortLabel: "foo", -// Meta: map[string]string{ -// "servicemeta": "foobar", -// }, -// CheckRestart: &api.CheckRestart{ -// Limit: 4, -// Grace: helper.TimeToPtr(11 * time.Second), -// }, -// Checks: []api.ServiceCheck{ -// { -// Id: "hello", -// Name: "bar", -// Type: "http", -// Command: "foo", -// Args: []string{"a", "b"}, -// Path: "/check", -// Protocol: "http", -// PortLabel: "foo", -// AddressMode: "driver", -// GRPCService: "foo.Bar", -// GRPCUseTLS: true, -// Interval: 4 * time.Second, -// Timeout: 2 * time.Second, -// InitialStatus: "ok", -// CheckRestart: &api.CheckRestart{ -// Limit: 3, -// IgnoreWarnings: true, -// }, -// }, -// { -// Id: "check2id", -// Name: "check2", -// Type: "tcp", -// PortLabel: "foo", -// Interval: 4 * time.Second, -// Timeout: 2 * time.Second, -// }, -// }, -// }, -// }, -// Resources: &api.Resources{ -// CPU: helper.IntToPtr(100), -// MemoryMB: helper.IntToPtr(10), -// Networks: []*api.NetworkResource{ -// { -// IP: "10.10.11.1", -// MBits: helper.IntToPtr(10), -// ReservedPorts: []api.Port{ -// { -// Label: "http", -// Value: 80, -// }, -// }, -// DynamicPorts: []api.Port{ -// { -// Label: "ssh", -// Value: 2000, -// }, -// }, -// }, -// }, -// Devices: []*api.RequestedDevice{ -// { -// Name: "nvidia/gpu", -// Count: helper.Uint64ToPtr(4), -// Constraints: []*api.Constraint{ -// { -// LTarget: "x", -// RTarget: "y", -// Operand: "z", -// }, -// }, -// Affinities: []*api.Affinity{ -// { -// LTarget: "a", -// RTarget: "b", -// Operand: "c", -// Weight: helper.Int8ToPtr(50), -// }, -// }, -// }, -// { -// Name: "gpu", -// Count: nil, -// }, -// }, -// }, -// Meta: map[string]string{ -// "lol": "code", -// }, -// KillTimeout: helper.TimeToPtr(10 * time.Second), -// KillSignal: "SIGQUIT", -// LogConfig: &api.LogConfig{ -// MaxFiles: helper.IntToPtr(10), -// MaxFileSizeMB: helper.IntToPtr(100), -// }, -// Artifacts: []*api.TaskArtifact{ -// { -// GetterSource: helper.StringToPtr("source"), -// GetterOptions: map[string]string{ -// "a": "b", -// }, -// GetterMode: helper.StringToPtr("dir"), -// RelativeDest: helper.StringToPtr("dest"), -// }, -// }, -// Vault: &api.Vault{ -// Policies: []string{"a", "b", "c"}, -// Env: helper.BoolToPtr(true), -// ChangeMode: helper.StringToPtr("c"), -// ChangeSignal: helper.StringToPtr("sighup"), -// }, -// Templates: []*api.Template{ -// { -// SourcePath: helper.StringToPtr("source"), -// DestPath: helper.StringToPtr("dest"), -// EmbeddedTmpl: helper.StringToPtr("embedded"), -// ChangeMode: helper.StringToPtr("change"), -// ChangeSignal: helper.StringToPtr("signal"), -// Splay: helper.TimeToPtr(1 * time.Minute), -// Perms: helper.StringToPtr("666"), -// LeftDelim: helper.StringToPtr("abc"), -// RightDelim: helper.StringToPtr("def"), -// Envvars: helper.BoolToPtr(true), -// VaultGrace: helper.TimeToPtr(3 * time.Second), -// }, -// }, -// DispatchPayload: &api.DispatchPayloadConfig{ -// File: "fileA", -// }, -// }, -// }, -// }, -// }, -// VaultToken: helper.StringToPtr("token"), -// Status: helper.StringToPtr("status"), -// StatusDescription: helper.StringToPtr("status_desc"), -// Version: helper.Uint64ToPtr(10), -// CreateIndex: helper.Uint64ToPtr(1), -// ModifyIndex: helper.Uint64ToPtr(3), -// JobModifyIndex: helper.Uint64ToPtr(5), -// } -// -// expected := &structs.Job{ -// Stop: true, -// Region: "global", -// Namespace: "foo", -// ID: "foo", -// ParentID: "lol", -// Name: "name", -// Type: "service", -// Priority: 50, -// AllAtOnce: true, -// Datacenters: []string{"dc1", "dc2"}, -// Constraints: []*structs.Constraint{ -// { -// LTarget: "a", -// RTarget: "b", -// Operand: "c", -// }, -// }, -// Affinities: []*structs.Affinity{ -// { -// LTarget: "a", -// RTarget: "b", -// Operand: "c", -// Weight: 50, -// }, -// }, -// Spreads: []*structs.Spread{ -// { -// Attribute: "${meta.rack}", -// Weight: 100, -// SpreadTarget: []*structs.SpreadTarget{ -// { -// Value: "r1", -// Percent: 50, -// }, -// }, -// }, -// }, -// Update: structs.UpdateStrategy{ -// Stagger: 1 * time.Second, -// MaxParallel: 5, -// }, -// Periodic: &structs.PeriodicConfig{ -// Enabled: true, -// Spec: "spec", -// SpecType: "cron", -// ProhibitOverlap: true, -// TimeZone: "test zone", -// }, -// ParameterizedJob: &structs.ParameterizedJobConfig{ -// Payload: "payload", -// MetaRequired: []string{"a", "b"}, -// MetaOptional: []string{"c", "d"}, -// }, -// Payload: []byte("payload"), -// Meta: map[string]string{ -// "foo": "bar", -// }, -// TaskGroups: []*structs.TaskGroup{ -// { -// Name: "group1", -// Count: 5, -// Constraints: []*structs.Constraint{ -// { -// LTarget: "x", -// RTarget: "y", -// Operand: "z", -// }, -// }, -// Affinities: []*structs.Affinity{ -// { -// LTarget: "x", -// RTarget: "y", -// Operand: "z", -// Weight: 100, -// }, -// }, -// RestartPolicy: &structs.RestartPolicy{ -// Interval: 1 * time.Second, -// Attempts: 5, -// Delay: 10 * time.Second, -// Mode: "delay", -// }, -// Spreads: []*structs.Spread{ -// { -// Attribute: "${node.datacenter}", -// Weight: 100, -// SpreadTarget: []*structs.SpreadTarget{ -// { -// Value: "dc1", -// Percent: 100, -// }, -// }, -// }, -// }, -// ReschedulePolicy: &structs.ReschedulePolicy{ -// Interval: 12 * time.Hour, -// Attempts: 5, -// DelayFunction: "constant", -// Delay: 30 * time.Second, -// Unlimited: true, -// MaxDelay: 20 * time.Minute, -// }, -// Migrate: &structs.MigrateStrategy{ -// MaxParallel: 12, -// HealthCheck: "task_events", -// MinHealthyTime: 12 * time.Hour, -// HealthyDeadline: 12 * time.Hour, -// }, -// EphemeralDisk: &structs.EphemeralDisk{ -// SizeMB: 100, -// Sticky: true, -// Migrate: true, -// }, -// Update: &structs.UpdateStrategy{ -// Stagger: 1 * time.Second, -// MaxParallel: 5, -// HealthCheck: structs.UpdateStrategyHealthCheck_Checks, -// MinHealthyTime: 2 * time.Minute, -// HealthyDeadline: 5 * time.Minute, -// ProgressDeadline: 5 * time.Minute, -// AutoRevert: true, -// AutoPromote: false, -// Canary: 1, -// }, -// Meta: map[string]string{ -// "key": "value", -// }, -// Services: []*structs.Service{ -// { -// Name: "groupserviceA", -// Tags: []string{"a", "b"}, -// CanaryTags: []string{"d", "e"}, -// PortLabel: "1234", -// AddressMode: "auto", -// Meta: map[string]string{ -// "servicemeta": "foobar", -// }, -// Checks: []*structs.ServiceCheck{ -// { -// Name: "bar", -// Type: "http", -// Command: "foo", -// Args: []string{"a", "b"}, -// Path: "/check", -// Protocol: "http", -// PortLabel: "foo", -// AddressMode: "driver", -// GRPCService: "foo.Bar", -// GRPCUseTLS: true, -// Interval: 4 * time.Second, -// Timeout: 2 * time.Second, -// InitialStatus: "ok", -// CheckRestart: &structs.CheckRestart{ -// Grace: 11 * time.Second, -// Limit: 3, -// IgnoreWarnings: true, -// }, -// TaskName: "task1", -// }, -// }, -// Connect: &structs.ConsulConnect{ -// Native: false, -// SidecarService: &structs.ConsulSidecarService{ -// Tags: []string{"f", "g"}, -// Port: "9000", -// }, -// }, -// }, -// }, -// Tasks: []*structs.Task{ -// { -// Name: "task1", -// Driver: "docker", -// Leader: true, -// User: "mary", -// Config: map[string]interface{}{ -// "lol": "code", -// }, -// Constraints: []*structs.Constraint{ -// { -// LTarget: "x", -// RTarget: "y", -// Operand: "z", -// }, -// }, -// Affinities: []*structs.Affinity{ -// { -// LTarget: "a", -// RTarget: "b", -// Operand: "c", -// Weight: 50, -// }, -// }, -// Env: map[string]string{ -// "hello": "world", -// }, -// Services: []*structs.Service{ -// { -// Name: "serviceA", -// Tags: []string{"1", "2"}, -// CanaryTags: []string{"3", "4"}, -// PortLabel: "foo", -// AddressMode: "auto", -// Meta: map[string]string{ -// "servicemeta": "foobar", -// }, -// Checks: []*structs.ServiceCheck{ -// { -// Name: "bar", -// Type: "http", -// Command: "foo", -// Args: []string{"a", "b"}, -// Path: "/check", -// Protocol: "http", -// PortLabel: "foo", -// AddressMode: "driver", -// Interval: 4 * time.Second, -// Timeout: 2 * time.Second, -// InitialStatus: "ok", -// GRPCService: "foo.Bar", -// GRPCUseTLS: true, -// CheckRestart: &structs.CheckRestart{ -// Limit: 3, -// Grace: 11 * time.Second, -// IgnoreWarnings: true, -// }, -// }, -// { -// Name: "check2", -// Type: "tcp", -// PortLabel: "foo", -// Interval: 4 * time.Second, -// Timeout: 2 * time.Second, -// CheckRestart: &structs.CheckRestart{ -// Limit: 4, -// Grace: 11 * time.Second, -// }, -// }, -// }, -// }, -// }, -// Resources: &structs.Resources{ -// CPU: 100, -// MemoryMB: 10, -// Networks: []*structs.NetworkResource{ -// { -// IP: "10.10.11.1", -// MBits: 10, -// ReservedPorts: []structs.Port{ -// { -// Label: "http", -// Value: 80, -// }, -// }, -// DynamicPorts: []structs.Port{ -// { -// Label: "ssh", -// Value: 2000, -// }, -// }, -// }, -// }, -// Devices: []*structs.RequestedDevice{ -// { -// Name: "nvidia/gpu", -// Count: 4, -// Constraints: []*structs.Constraint{ -// { -// LTarget: "x", -// RTarget: "y", -// Operand: "z", -// }, -// }, -// Affinities: []*structs.Affinity{ -// { -// LTarget: "a", -// RTarget: "b", -// Operand: "c", -// Weight: 50, -// }, -// }, -// }, -// { -// Name: "gpu", -// Count: 1, -// }, -// }, -// }, -// Meta: map[string]string{ -// "lol": "code", -// }, -// KillTimeout: 10 * time.Second, -// KillSignal: "SIGQUIT", -// LogConfig: &structs.LogConfig{ -// MaxFiles: 10, -// MaxFileSizeMB: 100, -// }, -// Artifacts: []*structs.TaskArtifact{ -// { -// GetterSource: "source", -// GetterOptions: map[string]string{ -// "a": "b", -// }, -// GetterMode: "dir", -// RelativeDest: "dest", -// }, -// }, -// Vault: &structs.Vault{ -// Policies: []string{"a", "b", "c"}, -// Env: true, -// ChangeMode: "c", -// ChangeSignal: "sighup", -// }, -// Templates: []*structs.Template{ -// { -// SourcePath: "source", -// DestPath: "dest", -// EmbeddedTmpl: "embedded", -// ChangeMode: "change", -// ChangeSignal: "SIGNAL", -// Splay: 1 * time.Minute, -// Perms: "666", -// LeftDelim: "abc", -// RightDelim: "def", -// Envvars: true, -// VaultGrace: 3 * time.Second, -// }, -// }, -// DispatchPayload: &structs.DispatchPayloadConfig{ -// File: "fileA", -// }, -// }, -// }, -// }, -// }, -// -// VaultToken: "token", -// } -// -// structsJob := ApiJobToStructJob(apiJob) -// -// if diff := pretty.Diff(expected, structsJob); len(diff) > 0 { -// t.Fatalf("bad:\n%s", strings.Join(diff, "\n")) -// } -// -// systemAPIJob := &api.Job{ -// Stop: helper.BoolToPtr(true), -// Region: helper.StringToPtr("global"), -// Namespace: helper.StringToPtr("foo"), -// ID: helper.StringToPtr("foo"), -// ParentID: helper.StringToPtr("lol"), -// Name: helper.StringToPtr("name"), -// Type: helper.StringToPtr("system"), -// Priority: helper.IntToPtr(50), -// AllAtOnce: helper.BoolToPtr(true), -// Datacenters: []string{"dc1", "dc2"}, -// Constraints: []*api.Constraint{ -// { -// LTarget: "a", -// RTarget: "b", -// Operand: "c", -// }, -// }, -// TaskGroups: []*api.TaskGroup{ -// { -// Name: helper.StringToPtr("group1"), -// Count: helper.IntToPtr(5), -// Constraints: []*api.Constraint{ -// { -// LTarget: "x", -// RTarget: "y", -// Operand: "z", -// }, -// }, -// RestartPolicy: &api.RestartPolicy{ -// Interval: helper.TimeToPtr(1 * time.Second), -// Attempts: helper.IntToPtr(5), -// Delay: helper.TimeToPtr(10 * time.Second), -// Mode: helper.StringToPtr("delay"), -// }, -// EphemeralDisk: &api.EphemeralDisk{ -// SizeMB: helper.IntToPtr(100), -// Sticky: helper.BoolToPtr(true), -// Migrate: helper.BoolToPtr(true), -// }, -// Meta: map[string]string{ -// "key": "value", -// }, -// Tasks: []*api.Task{ -// { -// Name: "task1", -// Leader: true, -// Driver: "docker", -// User: "mary", -// Config: map[string]interface{}{ -// "lol": "code", -// }, -// Env: map[string]string{ -// "hello": "world", -// }, -// Constraints: []*api.Constraint{ -// { -// LTarget: "x", -// RTarget: "y", -// Operand: "z", -// }, -// }, -// Resources: &api.Resources{ -// CPU: helper.IntToPtr(100), -// MemoryMB: helper.IntToPtr(10), -// Networks: []*api.NetworkResource{ -// { -// IP: "10.10.11.1", -// MBits: helper.IntToPtr(10), -// ReservedPorts: []api.Port{ -// { -// Label: "http", -// Value: 80, -// }, -// }, -// DynamicPorts: []api.Port{ -// { -// Label: "ssh", -// Value: 2000, -// }, -// }, -// }, -// }, -// }, -// Meta: map[string]string{ -// "lol": "code", -// }, -// KillTimeout: helper.TimeToPtr(10 * time.Second), -// KillSignal: "SIGQUIT", -// LogConfig: &api.LogConfig{ -// MaxFiles: helper.IntToPtr(10), -// MaxFileSizeMB: helper.IntToPtr(100), -// }, -// Artifacts: []*api.TaskArtifact{ -// { -// GetterSource: helper.StringToPtr("source"), -// GetterOptions: map[string]string{ -// "a": "b", -// }, -// GetterMode: helper.StringToPtr("dir"), -// RelativeDest: helper.StringToPtr("dest"), -// }, -// }, -// DispatchPayload: &api.DispatchPayloadConfig{ -// File: "fileA", -// }, -// }, -// }, -// }, -// }, -// Status: helper.StringToPtr("status"), -// StatusDescription: helper.StringToPtr("status_desc"), -// Version: helper.Uint64ToPtr(10), -// CreateIndex: helper.Uint64ToPtr(1), -// ModifyIndex: helper.Uint64ToPtr(3), -// JobModifyIndex: helper.Uint64ToPtr(5), -// } -// -// expectedSystemJob := &structs.Job{ -// Stop: true, -// Region: "global", -// Namespace: "foo", -// ID: "foo", -// ParentID: "lol", -// Name: "name", -// Type: "system", -// Priority: 50, -// AllAtOnce: true, -// Datacenters: []string{"dc1", "dc2"}, -// Constraints: []*structs.Constraint{ -// { -// LTarget: "a", -// RTarget: "b", -// Operand: "c", -// }, -// }, -// TaskGroups: []*structs.TaskGroup{ -// { -// Name: "group1", -// Count: 5, -// Constraints: []*structs.Constraint{ -// { -// LTarget: "x", -// RTarget: "y", -// Operand: "z", -// }, -// }, -// RestartPolicy: &structs.RestartPolicy{ -// Interval: 1 * time.Second, -// Attempts: 5, -// Delay: 10 * time.Second, -// Mode: "delay", -// }, -// EphemeralDisk: &structs.EphemeralDisk{ -// SizeMB: 100, -// Sticky: true, -// Migrate: true, -// }, -// Meta: map[string]string{ -// "key": "value", -// }, -// Tasks: []*structs.Task{ -// { -// Name: "task1", -// Driver: "docker", -// Leader: true, -// User: "mary", -// Config: map[string]interface{}{ -// "lol": "code", -// }, -// Constraints: []*structs.Constraint{ -// { -// LTarget: "x", -// RTarget: "y", -// Operand: "z", -// }, -// }, -// Env: map[string]string{ -// "hello": "world", -// }, -// Resources: &structs.Resources{ -// CPU: 100, -// MemoryMB: 10, -// Networks: []*structs.NetworkResource{ -// { -// IP: "10.10.11.1", -// MBits: 10, -// ReservedPorts: []structs.Port{ -// { -// Label: "http", -// Value: 80, -// }, -// }, -// DynamicPorts: []structs.Port{ -// { -// Label: "ssh", -// Value: 2000, -// }, -// }, -// }, -// }, -// }, -// Meta: map[string]string{ -// "lol": "code", -// }, -// KillTimeout: 10 * time.Second, -// KillSignal: "SIGQUIT", -// LogConfig: &structs.LogConfig{ -// MaxFiles: 10, -// MaxFileSizeMB: 100, -// }, -// Artifacts: []*structs.TaskArtifact{ -// { -// GetterSource: "source", -// GetterOptions: map[string]string{ -// "a": "b", -// }, -// GetterMode: "dir", -// RelativeDest: "dest", -// }, -// }, -// DispatchPayload: &structs.DispatchPayloadConfig{ -// File: "fileA", -// }, -// }, -// }, -// }, -// }, -// } -// -// systemStructsJob := ApiJobToStructJob(systemAPIJob) -// -// if diff := pretty.Diff(expectedSystemJob, systemStructsJob); len(diff) > 0 { -// t.Fatalf("bad:\n%s", strings.Join(diff, "\n")) -// } -// } -// -// func TestJobs_ApiJobToStructsJobUpdate(t *testing.T) { -// apiJob := &api.Job{ -// Update: &api.UpdateStrategy{ -// Stagger: helper.TimeToPtr(1 * time.Second), -// MaxParallel: helper.IntToPtr(5), -// HealthCheck: helper.StringToPtr(structs.UpdateStrategyHealthCheck_Manual), -// MinHealthyTime: helper.TimeToPtr(1 * time.Minute), -// HealthyDeadline: helper.TimeToPtr(3 * time.Minute), -// ProgressDeadline: helper.TimeToPtr(3 * time.Minute), -// AutoRevert: helper.BoolToPtr(false), -// AutoPromote: nil, -// Canary: helper.IntToPtr(1), -// }, -// TaskGroups: []*api.TaskGroup{ -// { -// Update: &api.UpdateStrategy{ -// Canary: helper.IntToPtr(2), -// AutoRevert: helper.BoolToPtr(true), -// }, -// }, { -// Update: &api.UpdateStrategy{ -// Canary: helper.IntToPtr(3), -// AutoPromote: helper.BoolToPtr(true), -// }, -// }, -// }, -// } -// -// structsJob := ApiJobToStructJob(apiJob) -// -// // Update has been moved from job down to the groups -// jobUpdate := structs.UpdateStrategy{ -// Stagger: 1000000000, -// MaxParallel: 5, -// HealthCheck: "", -// MinHealthyTime: 0, -// HealthyDeadline: 0, -// ProgressDeadline: 0, -// AutoRevert: false, -// AutoPromote: false, -// Canary: 0, -// } -// -// // But the groups inherit settings from the job update -// group1 := structs.UpdateStrategy{ -// Stagger: 1000000000, -// MaxParallel: 5, -// HealthCheck: "manual", -// MinHealthyTime: 60000000000, -// HealthyDeadline: 180000000000, -// ProgressDeadline: 180000000000, -// AutoRevert: true, -// AutoPromote: false, -// Canary: 2, -// } -// -// group2 := structs.UpdateStrategy{ -// Stagger: 1000000000, -// MaxParallel: 5, -// HealthCheck: "manual", -// MinHealthyTime: 60000000000, -// HealthyDeadline: 180000000000, -// ProgressDeadline: 180000000000, -// AutoRevert: false, -// AutoPromote: true, -// Canary: 3, -// } -// -// require.Equal(t, jobUpdate, structsJob.Update) -// require.Equal(t, group1, *structsJob.TaskGroups[0].Update) -// require.Equal(t, group2, *structsJob.TaskGroups[1].Update) -// } -// -// // TestHTTP_JobValidate_SystemMigrate asserts that a system job with a migrate -// // stanza fails to validate but does not panic (see #5477). -// func TestHTTP_JobValidate_SystemMigrate(t *testing.T) { -// t.Parallel() -// httpTest(t, nil, func(s *TestAgent) { -// // Create the job -// job := &api.Job{ -// Region: helper.StringToPtr("global"), -// Datacenters: []string{"dc1"}, -// ID: helper.StringToPtr("systemmigrate"), -// Name: helper.StringToPtr("systemmigrate"), -// TaskGroups: []*api.TaskGroup{ -// {Name: helper.StringToPtr("web")}, -// }, -// -// // System job... -// Type: helper.StringToPtr("system"), -// -// // ...with an empty migrate stanza -// Migrate: &api.MigrateStrategy{}, -// } -// -// args := api.JobValidateRequest{ -// Job: job, -// WriteRequest: api.WriteRequest{Region: "global"}, -// } -// buf := encodeReq(args) -// -// // Make the HTTP request -// req, err := http.NewRequest("PUT", "/v1/validate/job", buf) -// require.NoError(t, err) -// respW := httptest.NewRecorder() -// -// // Make the request -// obj, err := s.Server.ValidateJobRequest(respW, req) -// require.NoError(t, err) -// -// // Check the response -// resp := obj.(structs.JobValidateResponse) -// require.Contains(t, resp.Error, `Job type "system" does not allow migrate block`) -// }) -// }