open-nomad/command/agent/job_endpoint_test.go

1360 lines
34 KiB
Go
Raw Normal View History

2015-09-06 01:00:30 +00:00
package agent
2015-09-06 01:20:47 +00:00
import (
"net/http"
"net/http/httptest"
2016-12-02 00:27:22 +00:00
"reflect"
2017-07-06 17:17:19 +00:00
"strings"
2015-09-06 01:20:47 +00:00
"testing"
2017-02-13 23:18:17 +00:00
"time"
2015-09-06 01:20:47 +00:00
2016-12-02 00:27:22 +00:00
"github.com/golang/snappy"
2017-02-13 23:18:17 +00:00
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/helper"
2015-09-06 01:20:47 +00:00
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
2017-07-06 17:17:19 +00:00
"github.com/kr/pretty"
2017-07-01 00:23:34 +00:00
"github.com/stretchr/testify/assert"
2015-09-06 01:20:47 +00:00
)
2015-09-06 19:32:22 +00:00
func TestHTTP_JobsList(t *testing.T) {
httpTest(t, nil, func(s *TestServer) {
for i := 0; i < 3; i++ {
// Create the job
2017-02-22 22:15:22 +00:00
job := mock.Job()
args := structs.JobRegisterRequest{
2015-09-06 19:32:22 +00:00
Job: job,
2017-02-22 22:15:22 +00:00
WriteRequest: structs.WriteRequest{Region: "global"},
2015-09-06 19:32:22 +00:00
}
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/jobs", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
respW := httptest.NewRecorder()
// Make the request
obj, err := s.Server.JobsRequest(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")
}
if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" {
t.Fatalf("missing known leader")
}
if respW.HeaderMap.Get("X-Nomad-LastContact") == "" {
t.Fatalf("missing last contact")
}
// Check the job
j := obj.([]*structs.JobListStub)
if len(j) != 3 {
t.Fatalf("bad: %#v", j)
}
})
}
func TestHTTP_PrefixJobsList(t *testing.T) {
ids := []string{
"aaaaaaaa-e8f7-fd38-c855-ab94ceb89706",
"aabbbbbb-e8f7-fd38-c855-ab94ceb89706",
"aabbcccc-e8f7-fd38-c855-ab94ceb89706",
}
httpTest(t, nil, func(s *TestServer) {
for i := 0; i < 3; i++ {
// Create the job
2017-02-22 22:15:22 +00:00
job := mock.Job()
job.ID = ids[i]
job.TaskGroups[0].Count = 1
args := structs.JobRegisterRequest{
Job: job,
2017-02-22 22:15:22 +00:00
WriteRequest: structs.WriteRequest{Region: "global"},
}
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/jobs?prefix=aabb", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
respW := httptest.NewRecorder()
// Make the request
obj, err := s.Server.JobsRequest(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")
}
if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" {
t.Fatalf("missing known leader")
}
if respW.HeaderMap.Get("X-Nomad-LastContact") == "" {
t.Fatalf("missing last contact")
}
// Check the job
j := obj.([]*structs.JobListStub)
if len(j) != 2 {
t.Fatalf("bad: %#v", j)
}
})
}
2015-09-06 18:47:52 +00:00
func TestHTTP_JobsRegister(t *testing.T) {
httpTest(t, nil, func(s *TestServer) {
// Create the job
job := api.MockJob()
args := api.JobRegisterRequest{
2015-09-06 18:47:52 +00:00
Job: job,
WriteRequest: api.WriteRequest{Region: "global"},
2015-09-06 18:47:52 +00:00
}
buf := encodeReq(args)
// Make the HTTP request
req, err := http.NewRequest("PUT", "/v1/jobs", buf)
if err != nil {
t.Fatalf("err: %v", err)
}
respW := httptest.NewRecorder()
// Make the request
obj, err := s.Server.JobsRequest(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.HeaderMap.Get("X-Nomad-Index") == "" {
t.Fatalf("missing index")
}
// Check the job is registered
getReq := structs.JobSpecificRequest{
JobID: *job.ID,
QueryOptions: structs.QueryOptions{Region: "global"},
2015-09-06 18:47:52 +00:00
}
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_JobsRegister_Defaulting(t *testing.T) {
httpTest(t, nil, func(s *TestServer) {
// Create the job
job := api.MockJob()
// Do not set its priority
job.Priority = nil
args := api.JobRegisterRequest{
Job: job,
WriteRequest: api.WriteRequest{Region: "global"},
}
buf := encodeReq(args)
// Make the HTTP request
req, err := http.NewRequest("PUT", "/v1/jobs", buf)
if err != nil {
t.Fatalf("err: %v", err)
}
respW := httptest.NewRecorder()
// Make the request
obj, err := s.Server.JobsRequest(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.HeaderMap.Get("X-Nomad-Index") == "" {
t.Fatalf("missing index")
}
// Check the job is registered
getReq := structs.JobSpecificRequest{
JobID: *job.ID,
QueryOptions: structs.QueryOptions{Region: "global"},
}
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")
}
if getResp.Job.Priority != 50 {
t.Fatalf("job didn't get defaulted")
}
})
}
2015-09-06 01:43:40 +00:00
func TestHTTP_JobQuery(t *testing.T) {
httpTest(t, nil, func(s *TestServer) {
// Create the job
2017-02-23 00:56:51 +00:00
job := mock.Job()
args := structs.JobRegisterRequest{
2015-09-06 01:43:40 +00:00
Job: job,
2017-02-23 00:56:51 +00:00
WriteRequest: structs.WriteRequest{Region: "global"},
2015-09-06 01:43:40 +00:00
}
var resp structs.JobRegisterResponse
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
t.Fatalf("err: %v", err)
}
// Make the HTTP request
2017-02-23 00:56:51 +00:00
req, err := http.NewRequest("GET", "/v1/job/"+job.ID, nil)
2015-09-06 01:43:40 +00:00
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")
}
if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" {
t.Fatalf("missing known leader")
}
if respW.HeaderMap.Get("X-Nomad-LastContact") == "" {
t.Fatalf("missing last contact")
}
// Check the job
j := obj.(*structs.Job)
2017-02-23 00:56:51 +00:00
if j.ID != job.ID {
2015-09-06 01:43:40 +00:00
t.Fatalf("bad: %#v", j)
}
})
}
2016-12-14 20:50:08 +00:00
func TestHTTP_JobQuery_Payload(t *testing.T) {
2016-12-02 00:27:22 +00:00
httpTest(t, nil, func(s *TestServer) {
// Create the job
job := mock.Job()
2016-12-14 20:50:08 +00:00
// Insert Payload compressed
2016-12-02 00:27:22 +00:00
expected := []byte("hello world")
compressed := snappy.Encode(nil, expected)
2016-12-14 20:50:08 +00:00
job.Payload = compressed
2016-12-02 00:27:22 +00:00
2016-12-19 00:54:12 +00:00
// 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)
2016-12-02 00:27:22 +00:00
}
// 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.HeaderMap.Get("X-Nomad-Index") == "" {
t.Fatalf("missing index")
}
if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" {
t.Fatalf("missing known leader")
}
if respW.HeaderMap.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)
}
2016-12-14 20:50:08 +00:00
// Check the payload is decompressed
if !reflect.DeepEqual(j.Payload, expected) {
t.Fatalf("Payload not decompressed properly; got %#v; want %#v", j.Payload, expected)
2016-12-02 00:27:22 +00:00
}
})
}
2015-09-06 02:08:47 +00:00
func TestHTTP_JobUpdate(t *testing.T) {
httpTest(t, nil, func(s *TestServer) {
// Create the job
job := api.MockJob()
args := api.JobRegisterRequest{
2015-09-06 02:08:47 +00:00
Job: job,
WriteRequest: api.WriteRequest{Region: "global"},
2015-09-06 02:08:47 +00:00
}
buf := encodeReq(args)
// Make the HTTP request
req, err := http.NewRequest("PUT", "/v1/job/"+*job.ID, buf)
2015-09-06 02:08:47 +00:00
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.HeaderMap.Get("X-Nomad-Index") == "" {
t.Fatalf("missing index")
}
// Check the job is registered
getReq := structs.JobSpecificRequest{
JobID: *job.ID,
QueryOptions: structs.QueryOptions{Region: "global"},
2015-09-06 02:08:47 +00:00
}
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")
}
})
}
2015-09-06 01:20:47 +00:00
func TestHTTP_JobDelete(t *testing.T) {
httpTest(t, nil, func(s *TestServer) {
// Create the job
2017-02-23 00:56:51 +00:00
job := mock.Job()
args := structs.JobRegisterRequest{
2015-09-06 01:20:47 +00:00
Job: job,
2017-02-23 00:56:51 +00:00
WriteRequest: structs.WriteRequest{Region: "global"},
2015-09-06 01:20:47 +00:00
}
var resp structs.JobRegisterResponse
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
t.Fatalf("err: %v", err)
}
2017-04-15 03:54:30 +00:00
// Make the HTTP request to do a soft delete
2017-02-23 00:56:51 +00:00
req, err := http.NewRequest("DELETE", "/v1/job/"+job.ID, nil)
2015-09-06 01:20:47 +00:00
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)
}
2015-09-06 01:43:40 +00:00
// Check for the index
if respW.HeaderMap.Get("X-Nomad-Index") == "" {
t.Fatalf("missing index")
}
2017-04-15 03:54:30 +00:00
// Check the job is still queryable
getReq1 := structs.JobSpecificRequest{
JobID: job.ID,
QueryOptions: structs.QueryOptions{Region: "global"},
}
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.HeaderMap.Get("X-Nomad-Index") == "" {
t.Fatalf("missing index")
}
2015-09-06 01:20:47 +00:00
// Check the job is gone
2017-04-15 03:54:30 +00:00
getReq2 := structs.JobSpecificRequest{
2017-02-23 00:56:51 +00:00
JobID: job.ID,
QueryOptions: structs.QueryOptions{Region: "global"},
2015-09-06 01:20:47 +00:00
}
2017-04-15 03:54:30 +00:00
var getResp2 structs.SingleJobResponse
if err := s.Agent.RPC("Job.GetJob", &getReq2, &getResp2); err != nil {
2015-09-06 01:20:47 +00:00
t.Fatalf("err: %v", err)
}
2017-04-15 03:54:30 +00:00
if getResp2.Job != nil {
2015-09-06 02:08:47 +00:00
t.Fatalf("job still exists")
}
2015-09-06 01:20:47 +00:00
})
}
func TestHTTP_JobForceEvaluate(t *testing.T) {
httpTest(t, nil, func(s *TestServer) {
// Create the job
2017-02-23 00:56:51 +00:00
job := mock.Job()
args := structs.JobRegisterRequest{
Job: job,
2017-02-23 00:56:51 +00:00
WriteRequest: structs.WriteRequest{Region: "global"},
}
var resp structs.JobRegisterResponse
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
t.Fatalf("err: %v", err)
}
// Make the HTTP request
2017-02-23 00:56:51 +00:00
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.HeaderMap.Get("X-Nomad-Index") == "" {
t.Fatalf("missing index")
}
})
}
2015-09-06 19:32:22 +00:00
2015-09-06 21:15:29 +00:00
func TestHTTP_JobEvaluations(t *testing.T) {
2015-09-06 19:32:22 +00:00
httpTest(t, nil, func(s *TestServer) {
// Create the job
2017-02-23 00:56:51 +00:00
job := mock.Job()
args := structs.JobRegisterRequest{
2015-09-06 19:32:22 +00:00
Job: job,
2017-02-23 00:56:51 +00:00
WriteRequest: structs.WriteRequest{Region: "global"},
2015-09-06 19:32:22 +00:00
}
var resp structs.JobRegisterResponse
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
t.Fatalf("err: %v", err)
}
// Make the HTTP request
2017-02-23 00:56:51 +00:00
req, err := http.NewRequest("GET", "/v1/job/"+job.ID+"/evaluations", nil)
2015-09-06 19:32:22 +00:00
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
2016-08-16 23:12:37 +00:00
if len(evals) < 0 || evals[idx].ID != resp.EvalID {
2015-09-06 19:32:22 +00:00
t.Fatalf("bad: %v", evals)
}
// Check for the index
if respW.HeaderMap.Get("X-Nomad-Index") == "" {
t.Fatalf("missing index")
}
if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" {
t.Fatalf("missing known leader")
}
if respW.HeaderMap.Get("X-Nomad-LastContact") == "" {
t.Fatalf("missing last contact")
}
})
}
2015-09-06 21:15:29 +00:00
func TestHTTP_JobAllocations(t *testing.T) {
2015-09-06 19:32:22 +00:00
httpTest(t, nil, func(s *TestServer) {
// Create the job
alloc1 := mock.Alloc()
2015-09-06 19:32:22 +00:00
args := structs.JobRegisterRequest{
Job: alloc1.Job,
WriteRequest: structs.WriteRequest{Region: "global"},
2015-09-06 19:32:22 +00:00
}
var resp structs.JobRegisterResponse
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
t.Fatalf("err: %v", err)
}
// Directly manipulate the state
state := s.Agent.server.State()
2015-09-07 03:47:42 +00:00
err := state.UpsertAllocs(1000, []*structs.Allocation{alloc1})
2015-09-06 19:32:22 +00:00
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)
2015-09-06 19:32:22 +00:00
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
2015-09-06 23:18:25 +00:00
allocs := obj.([]*structs.AllocListStub)
2015-09-06 19:32:22 +00:00
if len(allocs) != 1 && allocs[0].ID != alloc1.ID {
t.Fatalf("bad: %v", allocs)
}
2017-04-13 23:55:21 +00:00
// Check for the index
if respW.HeaderMap.Get("X-Nomad-Index") == "" {
t.Fatalf("missing index")
}
if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" {
t.Fatalf("missing known leader")
}
if respW.HeaderMap.Get("X-Nomad-LastContact") == "" {
t.Fatalf("missing last contact")
}
})
}
2017-07-01 00:23:34 +00:00
func TestHTTP_JobDeployments(t *testing.T) {
assert := assert.New(t)
httpTest(t, nil, func(s *TestServer) {
// Create the job
j := mock.Job()
args := structs.JobRegisterRequest{
Job: j,
WriteRequest: structs.WriteRequest{Region: "global"},
}
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
assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment")
2017-07-01 00:23:34 +00:00
// 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.HeaderMap.Get("X-Nomad-Index"), "missing index")
assert.Equal("true", respW.HeaderMap.Get("X-Nomad-KnownLeader"), "missing known leader")
assert.NotZero(respW.HeaderMap.Get("X-Nomad-LastContact"), "missing last contact")
})
}
func TestHTTP_JobDeployment(t *testing.T) {
assert := assert.New(t)
httpTest(t, nil, func(s *TestServer) {
// Create the job
j := mock.Job()
args := structs.JobRegisterRequest{
Job: j,
WriteRequest: structs.WriteRequest{Region: "global"},
}
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
assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment")
2017-07-01 00:23:34 +00:00
// 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.HeaderMap.Get("X-Nomad-Index"), "missing index")
assert.Equal("true", respW.HeaderMap.Get("X-Nomad-KnownLeader"), "missing known leader")
assert.NotZero(respW.HeaderMap.Get("X-Nomad-LastContact"), "missing last contact")
})
}
2017-04-13 23:55:21 +00:00
func TestHTTP_JobVersions(t *testing.T) {
httpTest(t, nil, func(s *TestServer) {
// Create the job
job := mock.Job()
args := structs.JobRegisterRequest{
Job: job,
WriteRequest: structs.WriteRequest{Region: "global"},
}
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"},
}
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)
2017-04-13 23:55:21 +00:00
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
2017-04-13 23:55:21 +00:00
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)
}
2015-09-06 19:32:22 +00:00
// Check for the index
if respW.HeaderMap.Get("X-Nomad-Index") == "" {
t.Fatalf("missing index")
}
if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" {
t.Fatalf("missing known leader")
}
if respW.HeaderMap.Get("X-Nomad-LastContact") == "" {
t.Fatalf("missing last contact")
}
})
}
2016-01-19 19:09:36 +00:00
func TestHTTP_PeriodicForce(t *testing.T) {
httpTest(t, nil, func(s *TestServer) {
// Create and register a periodic job.
2017-02-23 00:56:51 +00:00
job := mock.PeriodicJob()
args := structs.JobRegisterRequest{
2016-01-19 19:09:36 +00:00
Job: job,
2017-02-23 00:56:51 +00:00
WriteRequest: structs.WriteRequest{Region: "global"},
2016-01-19 19:09:36 +00:00
}
var resp structs.JobRegisterResponse
if err := s.Agent.RPC("Job.Register", &args, &resp); err != nil {
t.Fatalf("err: %v", err)
}
// Make the HTTP request
2017-02-23 00:56:51 +00:00
req, err := http.NewRequest("POST", "/v1/job/"+job.ID+"/periodic/force", nil)
2016-01-19 19:09:36 +00:00
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) {
httpTest(t, nil, func(s *TestServer) {
// Create the job
job := api.MockJob()
args := api.JobPlanRequest{
Job: job,
Diff: true,
WriteRequest: api.WriteRequest{Region: "global"},
}
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)
2016-05-16 19:49:18 +00:00
if plan.Annotations == nil {
t.Fatalf("bad: %v", plan)
}
if plan.Diff == nil {
t.Fatalf("bad: %v", plan)
}
})
}
2016-12-02 00:27:22 +00:00
func TestHTTP_JobDispatch(t *testing.T) {
httpTest(t, nil, func(s *TestServer) {
// Create the parameterized job
2017-02-23 00:56:51 +00:00
job := mock.Job()
job.Type = "batch"
job.ParameterizedJob = &structs.ParameterizedJobConfig{}
2016-12-02 00:27:22 +00:00
2017-02-23 00:56:51 +00:00
args := structs.JobRegisterRequest{
2016-12-02 00:27:22 +00:00
Job: job,
2017-02-23 00:56:51 +00:00
WriteRequest: structs.WriteRequest{Region: "global"},
2016-12-02 00:27:22 +00:00
}
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"},
}
buf := encodeReq(args2)
// Make the HTTP request
2017-02-23 00:56:51 +00:00
req2, err := http.NewRequest("PUT", "/v1/job/"+job.ID+"/dispatch", buf)
2016-12-02 00:27:22 +00:00
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)
}
})
}
2017-02-13 23:18:17 +00:00
2017-04-19 20:24:06 +00:00
func TestHTTP_JobRevert(t *testing.T) {
httpTest(t, nil, func(s *TestServer) {
// Create the job and register it twice
job := mock.Job()
regReq := structs.JobRegisterRequest{
Job: job,
WriteRequest: structs.WriteRequest{Region: "global"},
}
var regResp structs.JobRegisterResponse
if err := s.Agent.RPC("Job.Register", &regReq, &regResp); err != nil {
t.Fatalf("err: %v", err)
}
// Change the job to get a new version
job.Datacenters = append(job.Datacenters, "foo")
2017-04-19 20:24:06 +00:00
if err := s.Agent.RPC("Job.Register", &regReq, &regResp); err != nil {
t.Fatalf("err: %v", err)
}
args := structs.JobRevertRequest{
JobID: job.ID,
JobVersion: 0,
WriteRequest: structs.WriteRequest{Region: "global"},
}
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")
}
})
}
2017-07-06 19:49:13 +00:00
func TestHTTP_JobStable(t *testing.T) {
httpTest(t, nil, func(s *TestServer) {
// Create the job and register it twice
job := mock.Job()
regReq := structs.JobRegisterRequest{
Job: job,
WriteRequest: structs.WriteRequest{Region: "global"},
}
var regResp structs.JobRegisterResponse
if err := s.Agent.RPC("Job.Register", &regReq, &regResp); err != nil {
t.Fatalf("err: %v", err)
}
if err := s.Agent.RPC("Job.Register", &regReq, &regResp); err != nil {
t.Fatalf("err: %v", err)
}
args := structs.JobStabilityRequest{
JobID: job.ID,
JobVersion: 0,
Stable: true,
WriteRequest: structs.WriteRequest{Region: "global"},
}
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)
2017-07-07 20:55:39 +00:00
if stableResp.Index == 0 {
2017-07-06 19:49:13 +00:00
t.Fatalf("bad: %v", stableResp)
}
// Check for the index
if respW.HeaderMap.Get("X-Nomad-Index") == "" {
t.Fatalf("missing index")
}
})
}
2017-02-13 23:18:17 +00:00
func TestJobs_ApiJobToStructsJob(t *testing.T) {
apiJob := &api.Job{
2017-04-15 03:54:30 +00:00
Stop: helper.BoolToPtr(true),
2017-02-13 23:18:17 +00:00
Region: helper.StringToPtr("global"),
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",
},
},
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),
AutoRevert: helper.BoolToPtr(false),
Canary: helper.IntToPtr(1),
2017-02-13 23:18:17 +00:00
},
Periodic: &api.PeriodicConfig{
Enabled: helper.BoolToPtr(true),
Spec: helper.StringToPtr("spec"),
SpecType: helper.StringToPtr("cron"),
ProhibitOverlap: helper.BoolToPtr(true),
2017-02-21 00:43:28 +00:00
TimeZone: helper.StringToPtr("test zone"),
2017-02-13 23:18:17 +00:00
},
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",
},
},
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),
},
Update: &api.UpdateStrategy{
HealthCheck: helper.StringToPtr(structs.UpdateStrategyHealthCheck_Checks),
MinHealthyTime: helper.TimeToPtr(2 * time.Minute),
HealthyDeadline: helper.TimeToPtr(5 * time.Minute),
AutoRevert: helper.BoolToPtr(true),
},
2017-02-13 23:18:17 +00:00
Meta: map[string]string{
"key": "value",
},
Tasks: []*api.Task{
{
Name: "task1",
2017-02-21 00:43:28 +00:00
Leader: true,
2017-02-13 23:18:17 +00:00
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",
},
},
2017-03-01 23:30:01 +00:00
Services: []*api.Service{
2017-02-13 23:18:17 +00:00
{
Id: "id",
Name: "serviceA",
Tags: []string{"1", "2"},
PortLabel: "foo",
Checks: []api.ServiceCheck{
{
Id: "hello",
Name: "bar",
Type: "http",
Command: "foo",
Args: []string{"a", "b"},
Path: "/check",
Protocol: "http",
PortLabel: "foo",
Interval: 4 * time.Second,
Timeout: 2 * time.Second,
InitialStatus: "ok",
},
},
},
},
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),
LogConfig: &api.LogConfig{
MaxFiles: helper.IntToPtr(10),
MaxFileSizeMB: helper.IntToPtr(100),
},
Artifacts: []*api.TaskArtifact{
{
GetterSource: helper.StringToPtr("source"),
GetterOptions: map[string]string{
"a": "b",
},
2017-07-06 17:17:19 +00:00
GetterMode: helper.StringToPtr("dir"),
2017-02-13 23:18:17 +00:00
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"),
2017-02-21 00:43:28 +00:00
LeftDelim: helper.StringToPtr("abc"),
RightDelim: helper.StringToPtr("def"),
2017-02-13 23:18:17 +00:00
},
},
DispatchPayload: &api.DispatchPayloadConfig{
File: "fileA",
},
},
},
},
},
VaultToken: helper.StringToPtr("token"),
Status: helper.StringToPtr("status"),
StatusDescription: helper.StringToPtr("status_desc"),
2017-04-15 03:54:30 +00:00
Version: helper.Uint64ToPtr(10),
2017-02-13 23:18:17 +00:00
CreateIndex: helper.Uint64ToPtr(1),
ModifyIndex: helper.Uint64ToPtr(3),
JobModifyIndex: helper.Uint64ToPtr(5),
}
expected := &structs.Job{
2017-04-15 03:54:30 +00:00
Stop: true,
2017-02-13 23:18:17 +00:00
Region: "global",
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",
},
},
Update: structs.UpdateStrategy{
Stagger: 1 * time.Second,
MaxParallel: 5,
},
Periodic: &structs.PeriodicConfig{
Enabled: true,
Spec: "spec",
SpecType: "cron",
ProhibitOverlap: true,
2017-02-21 00:43:28 +00:00
TimeZone: "test zone",
2017-02-13 23:18:17 +00:00
},
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",
},
},
RestartPolicy: &structs.RestartPolicy{
Interval: 1 * time.Second,
Attempts: 5,
Delay: 10 * time.Second,
Mode: "delay",
},
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,
AutoRevert: true,
Canary: 1,
},
2017-02-13 23:18:17 +00:00
Meta: map[string]string{
"key": "value",
},
Tasks: []*structs.Task{
{
Name: "task1",
Driver: "docker",
2017-02-21 00:43:28 +00:00
Leader: true,
2017-02-13 23:18:17 +00:00
User: "mary",
Config: map[string]interface{}{
"lol": "code",
},
Constraints: []*structs.Constraint{
{
LTarget: "x",
RTarget: "y",
Operand: "z",
},
},
Env: map[string]string{
"hello": "world",
},
Services: []*structs.Service{
&structs.Service{
Name: "serviceA",
Tags: []string{"1", "2"},
PortLabel: "foo",
AddressMode: "auto",
2017-02-13 23:18:17 +00:00
Checks: []*structs.ServiceCheck{
&structs.ServiceCheck{
Name: "bar",
Type: "http",
Command: "foo",
Args: []string{"a", "b"},
Path: "/check",
Protocol: "http",
PortLabel: "foo",
Interval: 4 * time.Second,
Timeout: 2 * time.Second,
InitialStatus: "ok",
},
},
},
},
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,
LogConfig: &structs.LogConfig{
MaxFiles: 10,
MaxFileSizeMB: 100,
},
Artifacts: []*structs.TaskArtifact{
{
GetterSource: "source",
GetterOptions: map[string]string{
"a": "b",
},
2017-07-06 17:17:19 +00:00
GetterMode: "dir",
2017-02-13 23:18:17 +00:00
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",
2017-02-21 00:43:28 +00:00
LeftDelim: "abc",
RightDelim: "def",
2017-02-13 23:18:17 +00:00
},
},
DispatchPayload: &structs.DispatchPayloadConfig{
File: "fileA",
},
},
},
},
},
2017-04-15 03:54:30 +00:00
VaultToken: "token",
2017-02-13 23:18:17 +00:00
}
structsJob := ApiJobToStructJob(apiJob)
2017-02-13 23:18:17 +00:00
2017-07-06 17:17:19 +00:00
if diff := pretty.Diff(expected, structsJob); len(diff) > 0 {
t.Fatalf("bad:\n%s", strings.Join(diff, "\n"))
2017-02-13 23:18:17 +00:00
}
}