2015-09-06 00:06:05 +00:00
|
|
|
package agent
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net/http"
|
2015-09-06 01:00:30 +00:00
|
|
|
"strings"
|
2015-09-06 01:20:47 +00:00
|
|
|
|
2016-11-29 00:05:56 +00:00
|
|
|
"github.com/golang/snappy"
|
2015-09-06 01:20:47 +00:00
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
2015-09-06 00:06:05 +00:00
|
|
|
)
|
|
|
|
|
2015-09-06 01:00:30 +00:00
|
|
|
func (s *HTTPServer) JobsRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
|
|
|
switch req.Method {
|
|
|
|
case "GET":
|
|
|
|
return s.jobListRequest(resp, req)
|
|
|
|
case "PUT", "POST":
|
2015-09-06 18:47:52 +00:00
|
|
|
return s.jobUpdate(resp, req, "")
|
2015-09-06 01:00:30 +00:00
|
|
|
default:
|
|
|
|
return nil, CodedError(405, ErrInvalidMethod)
|
2015-09-06 00:06:05 +00:00
|
|
|
}
|
2015-09-06 01:00:30 +00:00
|
|
|
}
|
2015-09-06 00:06:05 +00:00
|
|
|
|
2015-09-06 01:00:30 +00:00
|
|
|
func (s *HTTPServer) jobListRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
2015-09-06 19:32:22 +00:00
|
|
|
args := structs.JobListRequest{}
|
|
|
|
if s.parse(resp, req, &args.Region, &args.QueryOptions) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var out structs.JobListResponse
|
|
|
|
if err := s.agent.RPC("Job.List", &args, &out); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
setMeta(resp, &out.QueryMeta)
|
2015-09-07 17:03:10 +00:00
|
|
|
if out.Jobs == nil {
|
|
|
|
out.Jobs = make([]*structs.JobListStub, 0)
|
|
|
|
}
|
2015-09-06 19:32:22 +00:00
|
|
|
return out.Jobs, nil
|
2015-09-06 01:00:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *HTTPServer) JobSpecificRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
|
|
|
path := strings.TrimPrefix(req.URL.Path, "/v1/job/")
|
|
|
|
switch {
|
|
|
|
case strings.HasSuffix(path, "/evaluate"):
|
|
|
|
jobName := strings.TrimSuffix(path, "/evaluate")
|
|
|
|
return s.jobForceEvaluate(resp, req, jobName)
|
|
|
|
case strings.HasSuffix(path, "/allocations"):
|
|
|
|
jobName := strings.TrimSuffix(path, "/allocations")
|
|
|
|
return s.jobAllocations(resp, req, jobName)
|
|
|
|
case strings.HasSuffix(path, "/evaluations"):
|
|
|
|
jobName := strings.TrimSuffix(path, "/evaluations")
|
|
|
|
return s.jobEvaluations(resp, req, jobName)
|
2016-01-19 19:09:36 +00:00
|
|
|
case strings.HasSuffix(path, "/periodic/force"):
|
|
|
|
jobName := strings.TrimSuffix(path, "/periodic/force")
|
|
|
|
return s.periodicForceRequest(resp, req, jobName)
|
2016-05-05 18:21:58 +00:00
|
|
|
case strings.HasSuffix(path, "/plan"):
|
|
|
|
jobName := strings.TrimSuffix(path, "/plan")
|
|
|
|
return s.jobPlan(resp, req, jobName)
|
2016-07-18 23:51:47 +00:00
|
|
|
case strings.HasSuffix(path, "/summary"):
|
|
|
|
jobName := strings.TrimSuffix(path, "/summary")
|
|
|
|
return s.jobSummaryRequest(resp, req, jobName)
|
2016-11-26 02:04:55 +00:00
|
|
|
case strings.HasSuffix(path, "/dispatch"):
|
|
|
|
jobName := strings.TrimSuffix(path, "/dispatch")
|
|
|
|
return s.jobDispatchRequest(resp, req, jobName)
|
2015-09-06 01:00:30 +00:00
|
|
|
default:
|
|
|
|
return s.jobCRUD(resp, req, path)
|
2015-09-06 00:06:05 +00:00
|
|
|
}
|
2015-09-06 01:00:30 +00:00
|
|
|
}
|
2015-09-06 00:06:05 +00:00
|
|
|
|
2015-09-06 01:00:30 +00:00
|
|
|
func (s *HTTPServer) jobForceEvaluate(resp http.ResponseWriter, req *http.Request,
|
|
|
|
jobName string) (interface{}, error) {
|
|
|
|
if req.Method != "PUT" && req.Method != "POST" {
|
|
|
|
return nil, CodedError(405, ErrInvalidMethod)
|
2015-09-06 00:06:05 +00:00
|
|
|
}
|
2015-09-06 18:50:37 +00:00
|
|
|
args := structs.JobEvaluateRequest{
|
|
|
|
JobID: jobName,
|
|
|
|
}
|
|
|
|
s.parseRegion(req, &args.Region)
|
|
|
|
|
|
|
|
var out structs.JobRegisterResponse
|
|
|
|
if err := s.agent.RPC("Job.Evaluate", &args, &out); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
setIndex(resp, out.Index)
|
|
|
|
return out, nil
|
2015-09-06 00:06:05 +00:00
|
|
|
}
|
|
|
|
|
2016-05-05 18:21:58 +00:00
|
|
|
func (s *HTTPServer) jobPlan(resp http.ResponseWriter, req *http.Request,
|
|
|
|
jobName string) (interface{}, error) {
|
|
|
|
if req.Method != "PUT" && req.Method != "POST" {
|
|
|
|
return nil, CodedError(405, ErrInvalidMethod)
|
|
|
|
}
|
|
|
|
|
|
|
|
var args structs.JobPlanRequest
|
|
|
|
if err := decodeBody(req, &args); err != nil {
|
|
|
|
return nil, CodedError(400, err.Error())
|
|
|
|
}
|
|
|
|
if args.Job == nil {
|
|
|
|
return nil, CodedError(400, "Job must be specified")
|
|
|
|
}
|
|
|
|
if jobName != "" && args.Job.ID != jobName {
|
|
|
|
return nil, CodedError(400, "Job ID does not match")
|
|
|
|
}
|
|
|
|
s.parseRegion(req, &args.Region)
|
|
|
|
|
|
|
|
var out structs.JobPlanResponse
|
|
|
|
if err := s.agent.RPC("Job.Plan", &args, &out); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-05-12 01:51:48 +00:00
|
|
|
setIndex(resp, out.Index)
|
2016-05-05 18:21:58 +00:00
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
2016-01-19 19:09:36 +00:00
|
|
|
func (s *HTTPServer) periodicForceRequest(resp http.ResponseWriter, req *http.Request,
|
|
|
|
jobName string) (interface{}, error) {
|
|
|
|
if req.Method != "PUT" && req.Method != "POST" {
|
|
|
|
return nil, CodedError(405, ErrInvalidMethod)
|
|
|
|
}
|
|
|
|
|
|
|
|
args := structs.PeriodicForceRequest{
|
|
|
|
JobID: jobName,
|
|
|
|
}
|
|
|
|
s.parseRegion(req, &args.Region)
|
|
|
|
|
|
|
|
var out structs.PeriodicForceResponse
|
|
|
|
if err := s.agent.RPC("Periodic.Force", &args, &out); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
setIndex(resp, out.Index)
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
2015-09-06 01:00:30 +00:00
|
|
|
func (s *HTTPServer) jobAllocations(resp http.ResponseWriter, req *http.Request,
|
|
|
|
jobName string) (interface{}, error) {
|
|
|
|
if req.Method != "GET" {
|
|
|
|
return nil, CodedError(405, ErrInvalidMethod)
|
2015-09-06 00:06:05 +00:00
|
|
|
}
|
2015-09-06 19:32:22 +00:00
|
|
|
args := structs.JobSpecificRequest{
|
|
|
|
JobID: jobName,
|
|
|
|
}
|
|
|
|
if s.parse(resp, req, &args.Region, &args.QueryOptions) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var out structs.JobAllocationsResponse
|
|
|
|
if err := s.agent.RPC("Job.Allocations", &args, &out); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
setMeta(resp, &out.QueryMeta)
|
2015-09-07 17:03:10 +00:00
|
|
|
if out.Allocations == nil {
|
|
|
|
out.Allocations = make([]*structs.AllocListStub, 0)
|
|
|
|
}
|
2015-09-06 19:32:22 +00:00
|
|
|
return out.Allocations, nil
|
2015-09-06 01:00:30 +00:00
|
|
|
}
|
2015-09-06 00:06:05 +00:00
|
|
|
|
2015-09-06 01:00:30 +00:00
|
|
|
func (s *HTTPServer) jobEvaluations(resp http.ResponseWriter, req *http.Request,
|
|
|
|
jobName string) (interface{}, error) {
|
|
|
|
if req.Method != "GET" {
|
|
|
|
return nil, CodedError(405, ErrInvalidMethod)
|
2015-09-06 00:06:05 +00:00
|
|
|
}
|
2015-09-06 19:32:22 +00:00
|
|
|
args := structs.JobSpecificRequest{
|
|
|
|
JobID: jobName,
|
|
|
|
}
|
|
|
|
if s.parse(resp, req, &args.Region, &args.QueryOptions) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var out structs.JobEvaluationsResponse
|
|
|
|
if err := s.agent.RPC("Job.Evaluations", &args, &out); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
setMeta(resp, &out.QueryMeta)
|
2015-09-07 17:03:10 +00:00
|
|
|
if out.Evaluations == nil {
|
|
|
|
out.Evaluations = make([]*structs.Evaluation, 0)
|
|
|
|
}
|
2015-09-06 19:32:22 +00:00
|
|
|
return out.Evaluations, nil
|
2015-09-06 01:00:30 +00:00
|
|
|
}
|
2015-09-06 00:06:05 +00:00
|
|
|
|
2015-09-06 01:00:30 +00:00
|
|
|
func (s *HTTPServer) jobCRUD(resp http.ResponseWriter, req *http.Request,
|
|
|
|
jobName string) (interface{}, error) {
|
|
|
|
switch req.Method {
|
|
|
|
case "GET":
|
|
|
|
return s.jobQuery(resp, req, jobName)
|
|
|
|
case "PUT", "POST":
|
|
|
|
return s.jobUpdate(resp, req, jobName)
|
|
|
|
case "DELETE":
|
|
|
|
return s.jobDelete(resp, req, jobName)
|
|
|
|
default:
|
|
|
|
return nil, CodedError(405, ErrInvalidMethod)
|
2015-09-06 00:06:05 +00:00
|
|
|
}
|
2015-09-06 01:00:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *HTTPServer) jobQuery(resp http.ResponseWriter, req *http.Request,
|
|
|
|
jobName string) (interface{}, error) {
|
2015-09-06 01:43:40 +00:00
|
|
|
args := structs.JobSpecificRequest{
|
|
|
|
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 {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
setMeta(resp, &out.QueryMeta)
|
|
|
|
if out.Job == nil {
|
|
|
|
return nil, CodedError(404, "job not found")
|
|
|
|
}
|
2016-11-29 00:05:56 +00:00
|
|
|
|
|
|
|
// Decode the input data if there is any
|
|
|
|
job := out.Job
|
|
|
|
if len(job.InputData) != 0 {
|
|
|
|
decoded, err := snappy.Decode(nil, out.Job.InputData)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
job = job.Copy()
|
|
|
|
job.InputData = decoded
|
|
|
|
}
|
|
|
|
|
|
|
|
return job, nil
|
2015-09-06 01:00:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *HTTPServer) jobUpdate(resp http.ResponseWriter, req *http.Request,
|
|
|
|
jobName string) (interface{}, error) {
|
2015-09-06 02:08:47 +00:00
|
|
|
var args structs.JobRegisterRequest
|
2015-09-23 03:01:57 +00:00
|
|
|
if err := decodeBody(req, &args); err != nil {
|
2015-09-06 02:08:47 +00:00
|
|
|
return nil, CodedError(400, err.Error())
|
|
|
|
}
|
2015-09-21 00:38:26 +00:00
|
|
|
if args.Job == nil {
|
|
|
|
return nil, CodedError(400, "Job must be specified")
|
|
|
|
}
|
2015-09-06 18:47:52 +00:00
|
|
|
if jobName != "" && args.Job.ID != jobName {
|
2015-09-06 02:08:47 +00:00
|
|
|
return nil, CodedError(400, "Job ID does not match")
|
|
|
|
}
|
|
|
|
s.parseRegion(req, &args.Region)
|
|
|
|
|
|
|
|
var out structs.JobRegisterResponse
|
|
|
|
if err := s.agent.RPC("Job.Register", &args, &out); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
setIndex(resp, out.Index)
|
|
|
|
return out, nil
|
2015-09-06 01:00:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *HTTPServer) jobDelete(resp http.ResponseWriter, req *http.Request,
|
|
|
|
jobName string) (interface{}, error) {
|
2015-09-06 01:20:47 +00:00
|
|
|
args := structs.JobDeregisterRequest{
|
|
|
|
JobID: jobName,
|
|
|
|
}
|
|
|
|
s.parseRegion(req, &args.Region)
|
|
|
|
|
|
|
|
var out structs.JobDeregisterResponse
|
|
|
|
if err := s.agent.RPC("Job.Deregister", &args, &out); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-09-06 01:43:40 +00:00
|
|
|
setIndex(resp, out.Index)
|
2015-09-06 01:20:47 +00:00
|
|
|
return out, nil
|
2015-09-06 00:06:05 +00:00
|
|
|
}
|
2016-07-18 23:51:47 +00:00
|
|
|
|
|
|
|
func (s *HTTPServer) jobSummaryRequest(resp http.ResponseWriter, req *http.Request, name string) (interface{}, error) {
|
|
|
|
args := structs.JobSummaryRequest{
|
|
|
|
JobID: name,
|
|
|
|
}
|
|
|
|
if s.parse(resp, req, &args.Region, &args.QueryOptions) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2016-07-21 21:43:21 +00:00
|
|
|
var out structs.JobSummaryResponse
|
2016-07-25 21:33:39 +00:00
|
|
|
if err := s.agent.RPC("Job.Summary", &args, &out); err != nil {
|
2016-07-18 23:51:47 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
setMeta(resp, &out.QueryMeta)
|
|
|
|
if out.JobSummary == nil {
|
|
|
|
return nil, CodedError(404, "job not found")
|
|
|
|
}
|
|
|
|
setIndex(resp, out.Index)
|
|
|
|
return out.JobSummary, nil
|
|
|
|
}
|
2016-11-26 02:04:55 +00:00
|
|
|
|
|
|
|
func (s *HTTPServer) jobDispatchRequest(resp http.ResponseWriter, req *http.Request, name string) (interface{}, error) {
|
|
|
|
if req.Method != "PUT" && req.Method != "POST" {
|
|
|
|
return nil, CodedError(405, ErrInvalidMethod)
|
|
|
|
}
|
|
|
|
args := structs.JobDispatchRequest{
|
|
|
|
JobID: name,
|
|
|
|
}
|
|
|
|
if err := decodeBody(req, &args); err != nil {
|
|
|
|
return nil, CodedError(400, err.Error())
|
|
|
|
}
|
|
|
|
s.parseRegion(req, &args.Region)
|
|
|
|
|
|
|
|
var out structs.JobDispatchResponse
|
|
|
|
if err := s.agent.RPC("Job.Dispatch", &args, &out); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
setIndex(resp, out.Index)
|
|
|
|
return out, nil
|
|
|
|
}
|