diff --git a/nomad/alloc_endpoint.go b/nomad/alloc_endpoint.go new file mode 100644 index 000000000..7d811adab --- /dev/null +++ b/nomad/alloc_endpoint.go @@ -0,0 +1,51 @@ +package nomad + +import ( + "time" + + "github.com/armon/go-metrics" + "github.com/hashicorp/nomad/nomad/structs" +) + +// Alloc endpoint is used for manipulating allocations +type Alloc struct { + srv *Server +} + +// List is used to list the allocations in the system +func (a *Alloc) List(args *structs.AllocListRequest, reply *structs.AllocListResponse) error { + if done, err := a.srv.forward("Alloc.List", args, args, reply); done { + return err + } + defer metrics.MeasureSince([]string{"nomad", "alloc", "list"}, time.Now()) + + // Capture all the allocations + snap, err := a.srv.fsm.State().Snapshot() + if err != nil { + return err + } + iter, err := snap.Allocs() + if err != nil { + return err + } + + for { + raw := iter.Next() + if raw == nil { + break + } + alloc := raw.(*structs.Allocation) + reply.Allocations = append(reply.Allocations, alloc.Stub()) + } + + // Use the last index that affected the jobs table + index, err := snap.GetIndex("allocs") + if err != nil { + return err + } + reply.Index = index + + // Set the query response + a.srv.setQueryMeta(&reply.QueryMeta) + return nil +} diff --git a/nomad/alloc_endpoint_test.go b/nomad/alloc_endpoint_test.go new file mode 100644 index 000000000..314b9d897 --- /dev/null +++ b/nomad/alloc_endpoint_test.go @@ -0,0 +1,44 @@ +package nomad + +import ( + "testing" + + "github.com/hashicorp/net-rpc-msgpackrpc" + "github.com/hashicorp/nomad/nomad/mock" + "github.com/hashicorp/nomad/nomad/structs" + "github.com/hashicorp/nomad/testutil" +) + +func TestAllocEndpoint_List(t *testing.T) { + s1 := testServer(t, nil) + defer s1.Shutdown() + codec := rpcClient(t, s1) + testutil.WaitForLeader(t, s1.RPC) + + // Create the register request + alloc := mock.Alloc() + state := s1.fsm.State() + err := state.UpdateAllocations(1000, []*structs.Allocation{alloc}) + if err != nil { + t.Fatalf("err: %v", err) + } + + // Lookup the jobs + get := &structs.AllocListRequest{ + QueryOptions: structs.QueryOptions{Region: "region1"}, + } + var resp structs.AllocListResponse + if err := msgpackrpc.CallWithCodec(codec, "Alloc.List", get, &resp); err != nil { + t.Fatalf("err: %v", err) + } + if resp.Index != 1000 { + t.Fatalf("Bad index: %d %d", resp.Index, 1000) + } + + if len(resp.Allocations) != 1 { + t.Fatalf("bad: %#v", resp.Allocations) + } + if resp.Allocations[0].ID != alloc.ID { + t.Fatalf("bad: %#v", resp.Allocations[0]) + } +} diff --git a/nomad/client_endpoint.go b/nomad/client_endpoint.go index 3a4338716..3a362ec80 100644 --- a/nomad/client_endpoint.go +++ b/nomad/client_endpoint.go @@ -364,18 +364,7 @@ func (c *ClientEndpoint) List(args *structs.NodeListRequest, break } node := raw.(*structs.Node) - - stub := &structs.NodeListStub{ - ID: node.ID, - Datacenter: node.Datacenter, - Name: node.Name, - NodeClass: node.NodeClass, - Status: node.Status, - StatusDescription: node.StatusDescription, - CreateIndex: node.CreateIndex, - ModifyIndex: node.ModifyIndex, - } - reply.Nodes = append(reply.Nodes, stub) + reply.Nodes = append(reply.Nodes, node.Stub()) } // Use the last index that affected the jobs table diff --git a/nomad/job_endpoint.go b/nomad/job_endpoint.go index 89898ed75..86c0e87e4 100644 --- a/nomad/job_endpoint.go +++ b/nomad/job_endpoint.go @@ -245,18 +245,7 @@ func (j *Job) List(args *structs.JobListRequest, break } job := raw.(*structs.Job) - - stub := &structs.JobListStub{ - ID: job.ID, - Name: job.Name, - Type: job.Type, - Priority: job.Priority, - Status: job.Status, - StatusDescription: job.StatusDescription, - CreateIndex: job.CreateIndex, - ModifyIndex: job.ModifyIndex, - } - reply.Jobs = append(reply.Jobs, stub) + reply.Jobs = append(reply.Jobs, job.Stub()) } // Use the last index that affected the jobs table diff --git a/nomad/server.go b/nomad/server.go index a70628ec1..5ea554b9d 100644 --- a/nomad/server.go +++ b/nomad/server.go @@ -132,6 +132,7 @@ type endpoints struct { Job *Job Eval *Eval Plan *Plan + Alloc *Alloc } // NewServer is used to construct a new Nomad server from the @@ -341,6 +342,7 @@ func (s *Server) setupRPC(tlsWrap tlsutil.DCWrapper) error { s.endpoints.Job = &Job{s} s.endpoints.Eval = &Eval{s} s.endpoints.Plan = &Plan{s} + s.endpoints.Alloc = &Alloc{s} // Register the handlers s.rpcServer.Register(s.endpoints.Status) @@ -348,6 +350,7 @@ func (s *Server) setupRPC(tlsWrap tlsutil.DCWrapper) error { s.rpcServer.Register(s.endpoints.Job) s.rpcServer.Register(s.endpoints.Eval) s.rpcServer.Register(s.endpoints.Plan) + s.rpcServer.Register(s.endpoints.Alloc) list, err := net.ListenTCP("tcp", s.config.RPCAddr) if err != nil { diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index 16dd2cffd..bb831c3af 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -233,6 +233,11 @@ type AllocUpdateRequest struct { WriteRequest } +// AllocListRequest is used to request a list of allocations +type AllocListRequest struct { + QueryOptions +} + // GenericRequest is used to request where no // specific information is needed. type GenericRequest struct { @@ -295,19 +300,6 @@ type SingleNodeResponse struct { QueryMeta } -// NodeListStub is used to return a subset of job information -// for the job list -type NodeListStub struct { - ID string - Datacenter string - Name string - NodeClass string - Status string - StatusDescription string - CreateIndex uint64 - ModifyIndex uint64 -} - // JobListResponse is used for a list request type NodeListResponse struct { Nodes []*NodeListStub @@ -320,19 +312,6 @@ type SingleJobResponse struct { QueryMeta } -// JobListStub is used to return a subset of job information -// for the job list -type JobListStub struct { - ID string - Name string - Type string - Priority int - Status string - StatusDescription string - CreateIndex uint64 - ModifyIndex uint64 -} - // JobListResponse is used for a list request type JobListResponse struct { Jobs []*JobListStub @@ -370,6 +349,12 @@ type PlanResponse struct { WriteMeta } +// AllocListResponse is used for a list request +type AllocListResponse struct { + Allocations []*AllocListStub + QueryMeta +} + const ( NodeStatusInit = "initializing" NodeStatusReady = "ready" @@ -467,6 +452,33 @@ func (n *Node) TerminalStatus() bool { } } +// Stub returns a summarized version of the node +func (n *Node) Stub() *NodeListStub { + return &NodeListStub{ + ID: n.ID, + Datacenter: n.Datacenter, + Name: n.Name, + NodeClass: n.NodeClass, + Status: n.Status, + StatusDescription: n.StatusDescription, + CreateIndex: n.CreateIndex, + ModifyIndex: n.ModifyIndex, + } +} + +// NodeListStub is used to return a subset of job information +// for the job list +type NodeListStub struct { + ID string + Datacenter string + Name string + NodeClass string + Status string + StatusDescription string + CreateIndex uint64 + ModifyIndex uint64 +} + // Resources is used to define the resources available // on a client type Resources struct { @@ -658,6 +670,33 @@ func (j *Job) LookupTaskGroup(name string) *TaskGroup { return nil } +// Stub is used to return a summary of the job +func (j *Job) Stub() *JobListStub { + return &JobListStub{ + ID: j.ID, + Name: j.Name, + Type: j.Type, + Priority: j.Priority, + Status: j.Status, + StatusDescription: j.StatusDescription, + CreateIndex: j.CreateIndex, + ModifyIndex: j.ModifyIndex, + } +} + +// JobListStub is used to return a subset of job information +// for the job list +type JobListStub struct { + ID string + Name string + Type string + Priority int + Status string + StatusDescription string + CreateIndex uint64 + ModifyIndex uint64 +} + // TaskGroup is an atomic unit of placement. Each task group belongs to // a job and may contain any number of tasks. A task group support running // in many replicas using the same configuration.. @@ -791,6 +830,40 @@ func (a *Allocation) TerminalStatus() bool { } } +// Stub returns a list stub for the allocation +func (a *Allocation) Stub() *AllocListStub { + return &AllocListStub{ + ID: a.ID, + EvalID: a.EvalID, + Name: a.Name, + NodeID: a.NodeID, + JobID: a.JobID, + TaskGroup: a.TaskGroup, + DesiredStatus: a.DesiredStatus, + DesiredDescription: a.DesiredDescription, + ClientStatus: a.ClientStatus, + ClientDescription: a.ClientDescription, + CreateIndex: a.CreateIndex, + ModifyIndex: a.ModifyIndex, + } +} + +// AllocListStub is used to return a subset of alloc information +type AllocListStub struct { + ID string + EvalID string + Name string + NodeID string + JobID string + TaskGroup string + DesiredStatus string + DesiredDescription string + ClientStatus string + ClientDescription string + CreateIndex uint64 + ModifyIndex uint64 +} + // AllocMetric is used to track various metrics while attempting // to make an allocation. These are used to debug a job, or to better // understand the pressure within the system.