mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
Merge pull request #575 from nautsio/f-short-ids
Allow lookups based on short identifiers
This commit is contained in:
@@ -26,6 +26,10 @@ func (a *Allocations) List(q *QueryOptions) ([]*AllocationListStub, *QueryMeta,
|
||||
return resp, qm, nil
|
||||
}
|
||||
|
||||
func (a *Allocations) PrefixList(prefix string) ([]*AllocationListStub, *QueryMeta, error) {
|
||||
return a.List(&QueryOptions{Prefix: prefix})
|
||||
}
|
||||
|
||||
// Info is used to retrieve a single allocation.
|
||||
func (a *Allocations) Info(allocID string, q *QueryOptions) (*Allocation, *QueryMeta, error) {
|
||||
var resp Allocation
|
||||
|
||||
@@ -52,6 +52,52 @@ func TestAllocations_List(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllocations_PrefixList(t *testing.T) {
|
||||
c, s := makeClient(t, nil, nil)
|
||||
defer s.Stop()
|
||||
a := c.Allocations()
|
||||
|
||||
// Querying when no allocs exist returns nothing
|
||||
allocs, qm, err := a.PrefixList("")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if qm.LastIndex != 0 {
|
||||
t.Fatalf("bad index: %d", qm.LastIndex)
|
||||
}
|
||||
if n := len(allocs); n != 0 {
|
||||
t.Fatalf("expected 0 allocs, got: %d", n)
|
||||
}
|
||||
|
||||
// TODO: do something that causes an allocation to actually happen
|
||||
// so we can query for them.
|
||||
return
|
||||
|
||||
job := &Job{
|
||||
ID: "job1",
|
||||
Name: "Job #1",
|
||||
Type: JobTypeService,
|
||||
}
|
||||
eval, _, err := c.Jobs().Register(job, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// List the allocations by prefix
|
||||
allocs, qm, err = a.PrefixList("foobar")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if qm.LastIndex == 0 {
|
||||
t.Fatalf("bad index: %d", qm.LastIndex)
|
||||
}
|
||||
|
||||
// Check that we got the allocation back
|
||||
if len(allocs) == 0 || allocs[0].EvalID != eval {
|
||||
t.Fatalf("bad: %#v", allocs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllocations_CreateIndexSort(t *testing.T) {
|
||||
allocs := []*AllocationListStub{
|
||||
&AllocationListStub{CreateIndex: 2},
|
||||
|
||||
@@ -31,6 +31,9 @@ type QueryOptions struct {
|
||||
// WaitTime is used to bound the duration of a wait.
|
||||
// Defaults to that of the Config, but can be overriden.
|
||||
WaitTime time.Duration
|
||||
|
||||
// If set, used as prefix for resource list searches
|
||||
Prefix string
|
||||
}
|
||||
|
||||
// WriteOptions are used to parameterize a write
|
||||
@@ -150,6 +153,9 @@ func (r *request) setQueryOptions(q *QueryOptions) {
|
||||
if q.WaitTime != 0 {
|
||||
r.params.Set("wait", durToMsec(q.WaitTime))
|
||||
}
|
||||
if q.Prefix != "" {
|
||||
r.params.Set("prefix", q.Prefix)
|
||||
}
|
||||
}
|
||||
|
||||
// durToMsec converts a duration to a millisecond specified string
|
||||
|
||||
@@ -26,6 +26,10 @@ func (e *Evaluations) List(q *QueryOptions) ([]*Evaluation, *QueryMeta, error) {
|
||||
return resp, qm, nil
|
||||
}
|
||||
|
||||
func (e *Evaluations) PrefixList(prefix string) ([]*Evaluation, *QueryMeta, error) {
|
||||
return e.List(&QueryOptions{Prefix: prefix})
|
||||
}
|
||||
|
||||
// Info is used to query a single evaluation by its ID.
|
||||
func (e *Evaluations) Info(evalID string, q *QueryOptions) (*Evaluation, *QueryMeta, error) {
|
||||
var resp Evaluation
|
||||
|
||||
@@ -46,6 +46,45 @@ func TestEvaluations_List(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvaluations_PrefixList(t *testing.T) {
|
||||
c, s := makeClient(t, nil, nil)
|
||||
defer s.Stop()
|
||||
e := c.Evaluations()
|
||||
|
||||
// Listing when nothing exists returns empty
|
||||
result, qm, err := e.PrefixList("abcdef")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if qm.LastIndex != 0 {
|
||||
t.Fatalf("bad index: %d", qm.LastIndex)
|
||||
}
|
||||
if n := len(result); n != 0 {
|
||||
t.Fatalf("expected 0 evaluations, got: %d", n)
|
||||
}
|
||||
|
||||
// Register a job. This will create an evaluation.
|
||||
jobs := c.Jobs()
|
||||
job := testJob()
|
||||
evalID, wm, err := jobs.Register(job, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
assertWriteMeta(t, wm)
|
||||
|
||||
// Check the evaluations again
|
||||
result, qm, err = e.PrefixList(evalID[:4])
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
assertQueryMeta(t, qm)
|
||||
|
||||
// Check if we have the right list
|
||||
if len(result) != 1 || result[0].ID != evalID {
|
||||
t.Fatalf("bad: %#v", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvaluations_Info(t *testing.T) {
|
||||
c, s := makeClient(t, nil, nil)
|
||||
defer s.Stop()
|
||||
|
||||
@@ -47,6 +47,11 @@ func (j *Jobs) List(q *QueryOptions) ([]*JobListStub, *QueryMeta, error) {
|
||||
return resp, qm, nil
|
||||
}
|
||||
|
||||
// PrefixList is used to list all existing jobs that match the prefix.
|
||||
func (j *Jobs) PrefixList(prefix string) ([]*JobListStub, *QueryMeta, error) {
|
||||
return j.List(&QueryOptions{Prefix: prefix})
|
||||
}
|
||||
|
||||
// Info is used to retrieve information about a particular
|
||||
// job given its unique ID.
|
||||
func (j *Jobs) Info(jobID string, q *QueryOptions) (*Job, *QueryMeta, error) {
|
||||
|
||||
@@ -81,6 +81,82 @@ func TestJobs_Info(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobs_PrefixList(t *testing.T) {
|
||||
c, s := makeClient(t, nil, nil)
|
||||
defer s.Stop()
|
||||
jobs := c.Jobs()
|
||||
|
||||
// Listing when nothing exists returns empty
|
||||
results, qm, err := jobs.PrefixList("dummy")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if qm.LastIndex != 0 {
|
||||
t.Fatalf("bad index: %d", qm.LastIndex)
|
||||
}
|
||||
if n := len(results); n != 0 {
|
||||
t.Fatalf("expected 0 jobs, got: %d", n)
|
||||
}
|
||||
|
||||
// Register the job
|
||||
job := testJob()
|
||||
_, wm, err := jobs.Register(job, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
assertWriteMeta(t, wm)
|
||||
|
||||
// Query the job again and ensure it exists
|
||||
// Listing when nothing exists returns empty
|
||||
results, qm, err = jobs.PrefixList(job.ID[:1])
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Check if we have the right list
|
||||
if len(results) != 1 || results[0].ID != job.ID {
|
||||
t.Fatalf("bad: %#v", results)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobs_List(t *testing.T) {
|
||||
c, s := makeClient(t, nil, nil)
|
||||
defer s.Stop()
|
||||
jobs := c.Jobs()
|
||||
|
||||
// Listing when nothing exists returns empty
|
||||
results, qm, err := jobs.List(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if qm.LastIndex != 0 {
|
||||
t.Fatalf("bad index: %d", qm.LastIndex)
|
||||
}
|
||||
if n := len(results); n != 0 {
|
||||
t.Fatalf("expected 0 jobs, got: %d", n)
|
||||
}
|
||||
|
||||
// Register the job
|
||||
job := testJob()
|
||||
_, wm, err := jobs.Register(job, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
assertWriteMeta(t, wm)
|
||||
|
||||
// Query the job again and ensure it exists
|
||||
// Listing when nothing exists returns empty
|
||||
results, qm, err = jobs.List(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Check if we have the right list
|
||||
if len(results) != 1 || results[0].ID != job.ID {
|
||||
t.Fatalf("bad: %#v", results)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobs_Allocations(t *testing.T) {
|
||||
c, s := makeClient(t, nil, nil)
|
||||
defer s.Stop()
|
||||
|
||||
@@ -26,6 +26,10 @@ func (n *Nodes) List(q *QueryOptions) ([]*NodeListStub, *QueryMeta, error) {
|
||||
return resp, qm, nil
|
||||
}
|
||||
|
||||
func (n *Nodes) PrefixList(prefix string) ([]*NodeListStub, *QueryMeta, error) {
|
||||
return n.List(&QueryOptions{Prefix: prefix})
|
||||
}
|
||||
|
||||
// Info is used to query a specific node by its ID.
|
||||
func (n *Nodes) Info(nodeID string, q *QueryOptions) (*Node, *QueryMeta, error) {
|
||||
var resp Node
|
||||
|
||||
@@ -38,6 +38,47 @@ func TestNodes_List(t *testing.T) {
|
||||
assertQueryMeta(t, qm)
|
||||
}
|
||||
|
||||
func TestNodes_PrefixList(t *testing.T) {
|
||||
c, s := makeClient(t, nil, func(c *testutil.TestServerConfig) {
|
||||
c.DevMode = true
|
||||
})
|
||||
defer s.Stop()
|
||||
nodes := c.Nodes()
|
||||
|
||||
var qm *QueryMeta
|
||||
var out []*NodeListStub
|
||||
var err error
|
||||
|
||||
// Get the node ID
|
||||
var nodeID, dc string
|
||||
testutil.WaitForResult(func() (bool, error) {
|
||||
out, _, err := nodes.List(nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if n := len(out); n != 1 {
|
||||
return false, fmt.Errorf("expected 1 node, got: %d", n)
|
||||
}
|
||||
nodeID = out[0].ID
|
||||
dc = out[0].Datacenter
|
||||
return true, nil
|
||||
}, func(err error) {
|
||||
t.Fatalf("err: %s", err)
|
||||
})
|
||||
|
||||
// Find node based on four character prefix
|
||||
out, qm, err = nodes.PrefixList(nodeID[:4])
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if n := len(out); n != 1 {
|
||||
t.Fatalf("expected 1 node, got: %d ", n)
|
||||
}
|
||||
|
||||
// Check that we got valid QueryMeta.
|
||||
assertQueryMeta(t, qm)
|
||||
}
|
||||
|
||||
func TestNodes_Info(t *testing.T) {
|
||||
c, s := makeClient(t, nil, func(c *testutil.TestServerConfig) {
|
||||
c.DevMode = true
|
||||
|
||||
@@ -45,7 +45,7 @@ func TestHTTP_AllocsList(t *testing.T) {
|
||||
t.Fatalf("missing last contact")
|
||||
}
|
||||
|
||||
// Check the job
|
||||
// Check the alloc
|
||||
n := obj.([]*structs.AllocListStub)
|
||||
if len(n) != 2 {
|
||||
t.Fatalf("bad: %#v", n)
|
||||
@@ -53,6 +53,57 @@ func TestHTTP_AllocsList(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestHTTP_AllocsPrefixList(t *testing.T) {
|
||||
httpTest(t, nil, func(s *TestServer) {
|
||||
// Directly manipulate the state
|
||||
state := s.Agent.server.State()
|
||||
alloc1 := mock.Alloc()
|
||||
alloc1.ID = "aaaaaaaa-e8f7-fd38-c855-ab94ceb89706"
|
||||
alloc2 := mock.Alloc()
|
||||
alloc2.ID = "aaabbbbb-e8f7-fd38-c855-ab94ceb89706"
|
||||
err := state.UpsertAllocs(1000,
|
||||
[]*structs.Allocation{alloc1, alloc2})
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Make the HTTP request
|
||||
req, err := http.NewRequest("GET", "/v1/allocations?prefix=aaab", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
respW := httptest.NewRecorder()
|
||||
|
||||
// Make the request
|
||||
obj, err := s.Server.AllocsRequest(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 alloc
|
||||
n := obj.([]*structs.AllocListStub)
|
||||
if len(n) != 1 {
|
||||
t.Fatalf("bad: %#v", n)
|
||||
}
|
||||
|
||||
// Check the identifier
|
||||
if n[0].ID != alloc2.ID {
|
||||
t.Fatalf("expected alloc ID: %v, Actual: %v", alloc2.ID, n[0].ID)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestHTTP_AllocQuery(t *testing.T) {
|
||||
httpTest(t, nil, func(s *TestServer) {
|
||||
// Directly manipulate the state
|
||||
|
||||
@@ -45,7 +45,7 @@ func TestHTTP_EvalList(t *testing.T) {
|
||||
t.Fatalf("missing last contact")
|
||||
}
|
||||
|
||||
// Check the job
|
||||
// Check the eval
|
||||
e := obj.([]*structs.Evaluation)
|
||||
if len(e) != 2 {
|
||||
t.Fatalf("bad: %#v", e)
|
||||
@@ -53,6 +53,57 @@ func TestHTTP_EvalList(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestHTTP_EvalPrefixList(t *testing.T) {
|
||||
httpTest(t, nil, func(s *TestServer) {
|
||||
// Directly manipulate the state
|
||||
state := s.Agent.server.State()
|
||||
eval1 := mock.Eval()
|
||||
eval1.ID = "aaabbbbb-e8f7-fd38-c855-ab94ceb89706"
|
||||
eval2 := mock.Eval()
|
||||
eval2.ID = "aaabbbbb-e8f7-fd38-c855-ab94ceb89706"
|
||||
err := state.UpsertEvals(1000,
|
||||
[]*structs.Evaluation{eval1, eval2})
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Make the HTTP request
|
||||
req, err := http.NewRequest("GET", "/v1/evaluations?prefix=aaab", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
respW := httptest.NewRecorder()
|
||||
|
||||
// Make the request
|
||||
obj, err := s.Server.EvalsRequest(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 eval
|
||||
e := obj.([]*structs.Evaluation)
|
||||
if len(e) != 1 {
|
||||
t.Fatalf("bad: %#v", e)
|
||||
}
|
||||
|
||||
// Check the identifier
|
||||
if e[0].ID != eval2.ID {
|
||||
t.Fatalf("expected eval ID: %v, Actual: %v", eval2.ID, e[0].ID)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestHTTP_EvalAllocations(t *testing.T) {
|
||||
httpTest(t, nil, func(s *TestServer) {
|
||||
// Directly manipulate the state
|
||||
|
||||
@@ -258,6 +258,14 @@ func parseConsistency(req *http.Request, b *structs.QueryOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
// parsePrefix is used to parse the ?prefix query param
|
||||
func parsePrefix(req *http.Request, b *structs.QueryOptions) {
|
||||
query := req.URL.Query()
|
||||
if prefix := query.Get("prefix"); prefix != "" {
|
||||
b.Prefix = prefix
|
||||
}
|
||||
}
|
||||
|
||||
// parseRegion is used to parse the ?region query param
|
||||
func (s *HTTPServer) parseRegion(req *http.Request, r *string) {
|
||||
if other := req.URL.Query().Get("region"); other != "" {
|
||||
@@ -271,5 +279,6 @@ func (s *HTTPServer) parseRegion(req *http.Request, r *string) {
|
||||
func (s *HTTPServer) parse(resp http.ResponseWriter, req *http.Request, r *string, b *structs.QueryOptions) bool {
|
||||
s.parseRegion(req, r)
|
||||
parseConsistency(req, b)
|
||||
parsePrefix(req, b)
|
||||
return parseWait(resp, req, b)
|
||||
}
|
||||
|
||||
@@ -56,6 +56,59 @@ func TestHTTP_JobsList(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
job := mock.Job()
|
||||
job.ID = ids[i]
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestHTTP_JobsRegister(t *testing.T) {
|
||||
httpTest(t, nil, func(s *TestServer) {
|
||||
// Create the job
|
||||
|
||||
@@ -48,7 +48,7 @@ func TestHTTP_NodesList(t *testing.T) {
|
||||
t.Fatalf("missing last contact")
|
||||
}
|
||||
|
||||
// Check the job
|
||||
// Check the nodes
|
||||
n := obj.([]*structs.NodeListStub)
|
||||
if len(n) < 3 { // Maybe 4 including client
|
||||
t.Fatalf("bad: %#v", n)
|
||||
@@ -56,6 +56,55 @@ func TestHTTP_NodesList(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestHTTP_NodesPrefixList(t *testing.T) {
|
||||
httpTest(t, nil, func(s *TestServer) {
|
||||
ids := []string{"aaaaa", "aaaab", "aaabb", "aabbb", "abbbb", "bbbbb"}
|
||||
for i := 0; i < 5; i++ {
|
||||
// Create the node
|
||||
node := mock.Node()
|
||||
node.ID = ids[i]
|
||||
args := structs.NodeRegisterRequest{
|
||||
Node: node,
|
||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
||||
}
|
||||
var resp structs.NodeUpdateResponse
|
||||
if err := s.Agent.RPC("Node.Register", &args, &resp); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Make the HTTP request
|
||||
req, err := http.NewRequest("GET", "/v1/nodes?prefix=aaa", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
respW := httptest.NewRecorder()
|
||||
|
||||
// Make the request
|
||||
obj, err := s.Server.NodesRequest(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 nodes
|
||||
n := obj.([]*structs.NodeListStub)
|
||||
if len(n) != 3 {
|
||||
t.Fatalf("bad: %#v", n)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestHTTP_NodeForceEval(t *testing.T) {
|
||||
httpTest(t, nil, func(s *TestServer) {
|
||||
// Create the node
|
||||
|
||||
@@ -68,8 +68,37 @@ func (c *AllocStatusCommand) Run(args []string) int {
|
||||
// Query the allocation info
|
||||
alloc, _, err := client.Allocations().Info(allocID, nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error querying allocation: %s", err))
|
||||
return 1
|
||||
allocs, _, err := client.Allocations().PrefixList(allocID)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error querying allocation: %v", err))
|
||||
return 1
|
||||
}
|
||||
if len(allocs) == 0 {
|
||||
c.Ui.Error(fmt.Sprintf("No allocation(s) with prefix or id %q found", allocID))
|
||||
return 1
|
||||
}
|
||||
if len(allocs) > 1 {
|
||||
// Format the allocs
|
||||
out := make([]string, len(allocs)+1)
|
||||
out[0] = "ID|EvalID|JobID|TaskGroup|DesiredStatus|ClientStatus"
|
||||
for i, alloc := range allocs {
|
||||
out[i+1] = fmt.Sprintf("%s|%s|%s|%s|%s|%s",
|
||||
alloc.ID,
|
||||
alloc.EvalID,
|
||||
alloc.JobID,
|
||||
alloc.TaskGroup,
|
||||
alloc.DesiredStatus,
|
||||
alloc.ClientStatus)
|
||||
}
|
||||
c.Ui.Output(fmt.Sprintf("Please disambiguate the desired allocation\n\n%s", formatList(out)))
|
||||
return 0
|
||||
}
|
||||
// Prefix lookup matched a single allocation
|
||||
alloc, _, err = client.Allocations().Info(allocs[0].ID, nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error querying allocation: %s", err))
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// Format the allocation data
|
||||
|
||||
@@ -34,12 +34,13 @@ func TestAllocStatusCommand_Fails(t *testing.T) {
|
||||
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error querying allocation") {
|
||||
t.Fatalf("expected failed query error, got: %s", out)
|
||||
}
|
||||
ui.ErrorWriter.Reset()
|
||||
|
||||
// Fails on missing alloc
|
||||
if code := cmd.Run([]string{"-address=" + url, "26470238-5CF2-438F-8772-DC67CFB0705C"}); code != 1 {
|
||||
t.Fatalf("expected exit 1, got: %d", code)
|
||||
}
|
||||
if out := ui.ErrorWriter.String(); !strings.Contains(out, "not found") {
|
||||
if out := ui.ErrorWriter.String(); !strings.Contains(out, "No allocation(s) with prefix or id") {
|
||||
t.Fatalf("expected not found error, got: %s", out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ func TestEvalMonitorCommand_Fails(t *testing.T) {
|
||||
if code := cmd.Run([]string{"-address=" + url, "3E55C771-76FC-423B-BCED-3E5314F433B1"}); code != 1 {
|
||||
t.Fatalf("expect exit 1, got: %d", code)
|
||||
}
|
||||
if out := ui.ErrorWriter.String(); !strings.Contains(out, "not found") {
|
||||
if out := ui.ErrorWriter.String(); !strings.Contains(out, "No evaluation(s) with prefix or id") {
|
||||
t.Fatalf("expect not found error, got: %s", out)
|
||||
}
|
||||
ui.ErrorWriter.Reset()
|
||||
|
||||
@@ -182,8 +182,35 @@ func (m *monitor) monitor(evalID string) int {
|
||||
// Query the evaluation
|
||||
eval, _, err := m.client.Evaluations().Info(evalID, nil)
|
||||
if err != nil {
|
||||
m.ui.Error(fmt.Sprintf("Error reading evaluation: %s", err))
|
||||
return 1
|
||||
evals, _, err := m.client.Evaluations().PrefixList(evalID)
|
||||
if err != nil {
|
||||
m.ui.Error(fmt.Sprintf("Error reading evaluation: %s", err))
|
||||
return 1
|
||||
}
|
||||
if len(evals) == 0 {
|
||||
m.ui.Error(fmt.Sprintf("No evaluation(s) with prefix or id %q found", evalID))
|
||||
return 1
|
||||
}
|
||||
if len(evals) > 1 {
|
||||
// Format the evaluations
|
||||
out := make([]string, len(evals)+1)
|
||||
out[0] = "ID|Priority|Type|TriggeredBy|Status"
|
||||
for i, eval := range evals {
|
||||
out[i+1] = fmt.Sprintf("%s|%d|%s|%s|%s",
|
||||
eval.ID,
|
||||
eval.Priority,
|
||||
eval.Type,
|
||||
eval.TriggeredBy,
|
||||
eval.Status)
|
||||
}
|
||||
m.ui.Output(fmt.Sprintf("Please disambiguate the desired evaluation\n\n%s", formatList(out)))
|
||||
return 0
|
||||
}
|
||||
// Prefix lookup matched a single evaluation
|
||||
eval, _, err = m.client.Evaluations().Info(evals[0].ID, nil)
|
||||
if err != nil {
|
||||
m.ui.Error(fmt.Sprintf("Error reading evaluation: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
// Create the new eval state.
|
||||
@@ -196,7 +223,7 @@ func (m *monitor) monitor(evalID string) int {
|
||||
state.index = eval.CreateIndex
|
||||
|
||||
// Query the allocations associated with the evaluation
|
||||
allocs, _, err := m.client.Evaluations().Allocations(evalID, nil)
|
||||
allocs, _, err := m.client.Evaluations().Allocations(eval.ID, nil)
|
||||
if err != nil {
|
||||
m.ui.Error(fmt.Sprintf("Error reading allocations: %s", err))
|
||||
return 1
|
||||
|
||||
@@ -276,6 +276,52 @@ func TestMonitor_Monitor(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMonitor_MonitorWithPrefix(t *testing.T) {
|
||||
srv, client, _ := testServer(t, nil)
|
||||
defer srv.Stop()
|
||||
|
||||
// Create the monitor
|
||||
ui := new(cli.MockUi)
|
||||
mon := newMonitor(ui, client)
|
||||
|
||||
// Submit a job - this creates a new evaluation we can monitor
|
||||
job := testJob("job1")
|
||||
evalID, _, err := client.Jobs().Register(job, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Start monitoring the eval
|
||||
var code int
|
||||
doneCh := make(chan struct{})
|
||||
go func() {
|
||||
defer close(doneCh)
|
||||
code = mon.monitor(evalID[:4])
|
||||
}()
|
||||
|
||||
// Wait for completion
|
||||
select {
|
||||
case <-doneCh:
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatalf("eval monitor took too long")
|
||||
}
|
||||
|
||||
// Check the return code. We should get exit code 2 as there
|
||||
// would be a scheduling problem on the test server (no clients).
|
||||
if code != 2 {
|
||||
t.Fatalf("expect exit 2, got: %d", code)
|
||||
}
|
||||
|
||||
// Check the output
|
||||
out := ui.OutputWriter.String()
|
||||
if !strings.Contains(out, evalID) {
|
||||
t.Fatalf("missing eval\n\n%s", out)
|
||||
}
|
||||
if !strings.Contains(out, "finished with status") {
|
||||
t.Fatalf("missing final status\n\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMonitor_DumpAllocStatus(t *testing.T) {
|
||||
ui := new(cli.MockUi)
|
||||
|
||||
|
||||
@@ -68,8 +68,48 @@ func (c *NodeDrainCommand) Run(args []string) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Check if node exists
|
||||
node, _, err := client.Nodes().Info(nodeID, nil)
|
||||
if err != nil {
|
||||
// Exact lookup failed, try with prefix based search
|
||||
nodes, _, err := client.Nodes().PrefixList(nodeID)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error toggling drain mode: %s", err))
|
||||
return 1
|
||||
}
|
||||
// Return error if no nodes are found
|
||||
if len(nodes) == 0 {
|
||||
c.Ui.Error(fmt.Sprintf("No node(s) with prefix or id %q found", nodeID))
|
||||
return 1
|
||||
}
|
||||
if len(nodes) > 1 {
|
||||
// Format the nodes list that matches the prefix so that the user
|
||||
// can create a more specific request
|
||||
out := make([]string, len(nodes)+1)
|
||||
out[0] = "ID|DC|Name|Class|Drain|Status"
|
||||
for i, node := range nodes {
|
||||
out[i+1] = fmt.Sprintf("%s|%s|%s|%s|%v|%s",
|
||||
node.ID,
|
||||
node.Datacenter,
|
||||
node.Name,
|
||||
node.NodeClass,
|
||||
node.Drain,
|
||||
node.Status)
|
||||
}
|
||||
// Dump the output
|
||||
c.Ui.Output(fmt.Sprintf("Please disambiguate the desired node\n\n%s", formatList(out)))
|
||||
return 0
|
||||
}
|
||||
// Prefix lookup matched a single node
|
||||
node, _, err = client.Nodes().Info(nodes[0].ID, nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error toggling drain mode: %s", err))
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle node draining
|
||||
if _, err := client.Nodes().ToggleDrain(nodeID, enable, nil); err != nil {
|
||||
if _, err := client.Nodes().ToggleDrain(node.ID, enable, nil); err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error toggling drain mode: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestNodeDrainCommand_Fails(t *testing.T) {
|
||||
if code := cmd.Run([]string{"-address=" + url, "-enable", "nope"}); code != 1 {
|
||||
t.Fatalf("expected exit 1, got: %d", code)
|
||||
}
|
||||
if out := ui.ErrorWriter.String(); !strings.Contains(out, "not found") {
|
||||
if out := ui.ErrorWriter.String(); !strings.Contains(out, "No node(s) with prefix or id") {
|
||||
t.Fatalf("expected not exist error, got: %s", out)
|
||||
}
|
||||
ui.ErrorWriter.Reset()
|
||||
|
||||
@@ -100,8 +100,41 @@ func (c *NodeStatusCommand) Run(args []string) int {
|
||||
nodeID := args[0]
|
||||
node, _, err := client.Nodes().Info(nodeID, nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error querying node info: %s", err))
|
||||
return 1
|
||||
// Exact lookup failed, try with prefix based search
|
||||
nodes, _, err := client.Nodes().PrefixList(nodeID)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error querying node info: %s", err))
|
||||
return 1
|
||||
}
|
||||
// Return error if no nodes are found
|
||||
if len(nodes) == 0 {
|
||||
c.Ui.Error(fmt.Sprintf("No node(s) with prefix %q found", nodeID))
|
||||
return 1
|
||||
}
|
||||
if len(nodes) > 1 {
|
||||
// Format the nodes list that matches the prefix so that the user
|
||||
// can create a more specific request
|
||||
out := make([]string, len(nodes)+1)
|
||||
out[0] = "ID|DC|Name|Class|Drain|Status"
|
||||
for i, node := range nodes {
|
||||
out[i+1] = fmt.Sprintf("%s|%s|%s|%s|%v|%s",
|
||||
node.ID,
|
||||
node.Datacenter,
|
||||
node.Name,
|
||||
node.NodeClass,
|
||||
node.Drain,
|
||||
node.Status)
|
||||
}
|
||||
// Dump the output
|
||||
c.Ui.Output(fmt.Sprintf("Please disambiguate the desired node\n\n%s", formatList(out)))
|
||||
return 0
|
||||
}
|
||||
// Prefix lookup matched a single node
|
||||
node, _, err = client.Nodes().Info(nodes[0].ID, nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error querying node info: %s", err))
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
m := node.Attributes
|
||||
@@ -132,7 +165,7 @@ func (c *NodeStatusCommand) Run(args []string) int {
|
||||
var allocs []string
|
||||
if !short {
|
||||
// Query the node allocations
|
||||
nodeAllocs, _, err := client.Nodes().Allocations(nodeID, nil)
|
||||
nodeAllocs, _, err := client.Nodes().Allocations(node.ID, nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error querying node allocations: %s", err))
|
||||
return 1
|
||||
|
||||
@@ -74,6 +74,19 @@ func TestNodeStatusCommand_Run(t *testing.T) {
|
||||
if strings.Contains(out, "Allocations") {
|
||||
t.Fatalf("should not dump allocations")
|
||||
}
|
||||
|
||||
// Query a single node based on prefix
|
||||
if code := cmd.Run([]string{"-address=" + url, nodeID[:4]}); code != 0 {
|
||||
t.Fatalf("expected exit 0, got: %d", code)
|
||||
}
|
||||
out = ui.OutputWriter.String()
|
||||
if !strings.Contains(out, "mynode") {
|
||||
t.Fatalf("expect to find mynode, got: %s", out)
|
||||
}
|
||||
if !strings.Contains(out, "Allocations") {
|
||||
t.Fatalf("expected allocations, got: %s", out)
|
||||
}
|
||||
ui.OutputWriter.Reset()
|
||||
}
|
||||
|
||||
func TestNodeStatusCommand_Fails(t *testing.T) {
|
||||
@@ -99,12 +112,13 @@ func TestNodeStatusCommand_Fails(t *testing.T) {
|
||||
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error querying node status") {
|
||||
t.Fatalf("expected failed query error, got: %s", out)
|
||||
}
|
||||
ui.ErrorWriter.Reset()
|
||||
|
||||
// Fails on non-existent node
|
||||
if code := cmd.Run([]string{"-address=" + url, "nope"}); code != 1 {
|
||||
t.Fatalf("expected exit 1, got: %d", code)
|
||||
}
|
||||
if out := ui.ErrorWriter.String(); !strings.Contains(out, "not found") {
|
||||
if out := ui.ErrorWriter.String(); !strings.Contains(out, "No node(s) with prefix") {
|
||||
t.Fatalf("expected not found error, got: %s", out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,8 +95,34 @@ func (c *StatusCommand) Run(args []string) int {
|
||||
jobID := args[0]
|
||||
job, _, err := client.Jobs().Info(jobID, nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error querying job: %s", err))
|
||||
return 1
|
||||
jobs, _, err := client.Jobs().PrefixList(jobID)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error querying job: %s", err))
|
||||
return 1
|
||||
}
|
||||
if len(jobs) == 0 {
|
||||
c.Ui.Error(fmt.Sprintf("No job(s) with prefix or id %q found", jobID))
|
||||
return 1
|
||||
}
|
||||
if len(jobs) > 1 {
|
||||
out := make([]string, len(jobs)+1)
|
||||
out[0] = "ID|Type|Priority|Status"
|
||||
for i, job := range jobs {
|
||||
out[i+1] = fmt.Sprintf("%s|%s|%d|%s",
|
||||
job.ID,
|
||||
job.Type,
|
||||
job.Priority,
|
||||
job.Status)
|
||||
}
|
||||
c.Ui.Output(fmt.Sprintf("Please disambiguate the desired job\n\n%s", formatList(out)))
|
||||
return 0
|
||||
}
|
||||
// Prefix lookup matched a single job
|
||||
job, _, err = client.Jobs().Info(jobs[0].ID, nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error querying job: %s", err))
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// Check if it is periodic
|
||||
@@ -129,14 +155,14 @@ func (c *StatusCommand) Run(args []string) int {
|
||||
var evals, allocs []string
|
||||
|
||||
// Query the evaluations
|
||||
jobEvals, _, err := client.Jobs().Evaluations(jobID, nil)
|
||||
jobEvals, _, err := client.Jobs().Evaluations(job.ID, nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error querying job evaluations: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
// Query the allocations
|
||||
jobAllocs, _, err := client.Jobs().Allocations(jobID, nil)
|
||||
jobAllocs, _, err := client.Jobs().Allocations(job.ID, nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error querying job allocations: %s", err))
|
||||
return 1
|
||||
|
||||
@@ -31,11 +31,11 @@ func TestStatusCommand_Run(t *testing.T) {
|
||||
}
|
||||
|
||||
// Register two jobs
|
||||
job1 := testJob("job1")
|
||||
job1 := testJob("job1_sfx")
|
||||
if _, _, err := client.Jobs().Register(job1, nil); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
job2 := testJob("job2")
|
||||
job2 := testJob("job2_sfx")
|
||||
if _, _, err := client.Jobs().Register(job2, nil); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
@@ -45,18 +45,18 @@ func TestStatusCommand_Run(t *testing.T) {
|
||||
t.Fatalf("expected exit 0, got: %d", code)
|
||||
}
|
||||
out := ui.OutputWriter.String()
|
||||
if !strings.Contains(out, "job1") || !strings.Contains(out, "job2") {
|
||||
t.Fatalf("expected job1 and job2, got: %s", out)
|
||||
if !strings.Contains(out, "job1_sfx") || !strings.Contains(out, "job2_sfx") {
|
||||
t.Fatalf("expected job1_sfx and job2_sfx, got: %s", out)
|
||||
}
|
||||
ui.OutputWriter.Reset()
|
||||
|
||||
// Query a single job
|
||||
if code := cmd.Run([]string{"-address=" + url, "job2"}); code != 0 {
|
||||
if code := cmd.Run([]string{"-address=" + url, "job2_sfx"}); code != 0 {
|
||||
t.Fatalf("expected exit 0, got: %d", code)
|
||||
}
|
||||
out = ui.OutputWriter.String()
|
||||
if strings.Contains(out, "job1") || !strings.Contains(out, "job2") {
|
||||
t.Fatalf("expected only job2, got: %s", out)
|
||||
if strings.Contains(out, "job1_sfx") || !strings.Contains(out, "job2_sfx") {
|
||||
t.Fatalf("expected only job2_sfx, got: %s", out)
|
||||
}
|
||||
if !strings.Contains(out, "Evaluations") {
|
||||
t.Fatalf("should dump evaluations")
|
||||
@@ -66,6 +66,26 @@ func TestStatusCommand_Run(t *testing.T) {
|
||||
}
|
||||
ui.OutputWriter.Reset()
|
||||
|
||||
// Query jobs with prefix match
|
||||
if code := cmd.Run([]string{"-address=" + url, "job"}); code != 0 {
|
||||
t.Fatalf("expected exit 0, got: %d", code)
|
||||
}
|
||||
out = ui.OutputWriter.String()
|
||||
if !strings.Contains(out, "job1_sfx") || !strings.Contains(out, "job2_sfx") {
|
||||
t.Fatalf("expected job1_sfx and job2_sfx, got: %s", out)
|
||||
}
|
||||
ui.OutputWriter.Reset()
|
||||
|
||||
// Query a single job with prefix match
|
||||
if code := cmd.Run([]string{"-address=" + url, "job1"}); code != 0 {
|
||||
t.Fatalf("expected exit 0, got: %d", code)
|
||||
}
|
||||
out = ui.OutputWriter.String()
|
||||
if !strings.Contains(out, "job1_sfx") || strings.Contains(out, "job2_sfx") {
|
||||
t.Fatalf("expected only job1_sfx, got: %s", out)
|
||||
}
|
||||
ui.OutputWriter.Reset()
|
||||
|
||||
// Query in short view mode
|
||||
if code := cmd.Run([]string{"-address=" + url, "-short", "job2"}); code != 0 {
|
||||
t.Fatalf("expected exit 0, got: %d", code)
|
||||
|
||||
@@ -65,13 +65,40 @@ func (c *StopCommand) Run(args []string) int {
|
||||
}
|
||||
|
||||
// Check if the job exists
|
||||
if _, _, err := client.Jobs().Info(jobID, nil); err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error deregistering job: %s", err))
|
||||
return 1
|
||||
job, _, err := client.Jobs().Info(jobID, nil)
|
||||
if err != nil {
|
||||
jobs, _, err := client.Jobs().PrefixList(jobID)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error deregistering job: %s", err))
|
||||
return 1
|
||||
}
|
||||
if len(jobs) == 0 {
|
||||
c.Ui.Error(fmt.Sprintf("No job(s) with prefix or id %q found", jobID))
|
||||
return 1
|
||||
}
|
||||
if len(jobs) > 1 {
|
||||
out := make([]string, len(jobs)+1)
|
||||
out[0] = "ID|Type|Priority|Status"
|
||||
for i, job := range jobs {
|
||||
out[i+1] = fmt.Sprintf("%s|%s|%d|%s",
|
||||
job.ID,
|
||||
job.Type,
|
||||
job.Priority,
|
||||
job.Status)
|
||||
}
|
||||
c.Ui.Output(fmt.Sprintf("Please disambiguate the desired job\n\n%s", formatList(out)))
|
||||
return 0
|
||||
}
|
||||
// Prefix lookup matched a single job
|
||||
job, _, err = client.Jobs().Info(jobs[0].ID, nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error deregistering job: %s", err))
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke the stop
|
||||
evalID, _, err := client.Jobs().Deregister(jobID, nil)
|
||||
evalID, _, err := client.Jobs().Deregister(job.ID, nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error deregistering job: %s", err))
|
||||
return 1
|
||||
|
||||
@@ -31,7 +31,7 @@ func TestStopCommand_Fails(t *testing.T) {
|
||||
if code := cmd.Run([]string{"-address=" + url, "nope"}); code != 1 {
|
||||
t.Fatalf("expect exit 1, got: %d", code)
|
||||
}
|
||||
if out := ui.ErrorWriter.String(); !strings.Contains(out, "not found") {
|
||||
if out := ui.ErrorWriter.String(); !strings.Contains(out, "No job(s) with prefix or id") {
|
||||
t.Fatalf("expect not found error, got: %s", out)
|
||||
}
|
||||
ui.ErrorWriter.Reset()
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/armon/go-metrics"
|
||||
"github.com/hashicorp/go-memdb"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
"github.com/hashicorp/nomad/nomad/watch"
|
||||
)
|
||||
@@ -31,7 +32,12 @@ func (a *Alloc) List(args *structs.AllocListRequest, reply *structs.AllocListRes
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iter, err := snap.Allocs()
|
||||
var iter memdb.ResultIterator
|
||||
if prefix := args.QueryOptions.Prefix; prefix != "" {
|
||||
iter, err = snap.AllocsByIDPrefix(prefix)
|
||||
} else {
|
||||
iter, err = snap.Allocs()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func TestAllocEndpoint_List(t *testing.T) {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Lookup the jobs
|
||||
// Lookup the allocations
|
||||
get := &structs.AllocListRequest{
|
||||
QueryOptions: structs.QueryOptions{Region: "global"},
|
||||
}
|
||||
@@ -43,6 +43,26 @@ func TestAllocEndpoint_List(t *testing.T) {
|
||||
if resp.Allocations[0].ID != alloc.ID {
|
||||
t.Fatalf("bad: %#v", resp.Allocations[0])
|
||||
}
|
||||
|
||||
// Lookup the allocations by prefix
|
||||
get = &structs.AllocListRequest{
|
||||
QueryOptions: structs.QueryOptions{Region: "global", Prefix: alloc.ID[:4]},
|
||||
}
|
||||
|
||||
var resp2 structs.AllocListResponse
|
||||
if err := msgpackrpc.CallWithCodec(codec, "Alloc.List", get, &resp2); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp2.Index != 1000 {
|
||||
t.Fatalf("Bad index: %d %d", resp2.Index, 1000)
|
||||
}
|
||||
|
||||
if len(resp2.Allocations) != 1 {
|
||||
t.Fatalf("bad: %#v", resp2.Allocations)
|
||||
}
|
||||
if resp2.Allocations[0].ID != alloc.ID {
|
||||
t.Fatalf("bad: %#v", resp2.Allocations[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllocEndpoint_List_Blocking(t *testing.T) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/armon/go-metrics"
|
||||
"github.com/hashicorp/go-memdb"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
"github.com/hashicorp/nomad/nomad/watch"
|
||||
)
|
||||
@@ -239,7 +240,12 @@ func (e *Eval) List(args *structs.EvalListRequest,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iter, err := snap.Evals()
|
||||
var iter memdb.ResultIterator
|
||||
if prefix := args.QueryOptions.Prefix; prefix != "" {
|
||||
iter, err = snap.EvalsByIDPrefix(prefix)
|
||||
} else {
|
||||
iter, err = snap.Evals()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -391,7 +391,9 @@ func TestEvalEndpoint_List(t *testing.T) {
|
||||
|
||||
// Create the register request
|
||||
eval1 := mock.Eval()
|
||||
eval1.ID = "aaaaaaaa-3350-4b4b-d185-0e1992ed43e9"
|
||||
eval2 := mock.Eval()
|
||||
eval2.ID = "aaaabbbb-3350-4b4b-d185-0e1992ed43e9"
|
||||
s1.fsm.State().UpsertEvals(1000, []*structs.Evaluation{eval1, eval2})
|
||||
|
||||
// Lookup the eval
|
||||
@@ -409,6 +411,23 @@ func TestEvalEndpoint_List(t *testing.T) {
|
||||
if len(resp.Evaluations) != 2 {
|
||||
t.Fatalf("bad: %#v", resp.Evaluations)
|
||||
}
|
||||
|
||||
// Lookup the eval by prefix
|
||||
get = &structs.EvalListRequest{
|
||||
QueryOptions: structs.QueryOptions{Region: "global", Prefix: "aaaabb"},
|
||||
}
|
||||
var resp2 structs.EvalListResponse
|
||||
if err := msgpackrpc.CallWithCodec(codec, "Eval.List", get, &resp2); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp2.Index != 1000 {
|
||||
t.Fatalf("Bad index: %d %d", resp2.Index, 1000)
|
||||
}
|
||||
|
||||
if len(resp2.Evaluations) != 1 {
|
||||
t.Fatalf("bad: %#v", resp2.Evaluations)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestEvalEndpoint_List_Blocking(t *testing.T) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/armon/go-metrics"
|
||||
"github.com/hashicorp/go-memdb"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
"github.com/hashicorp/nomad/nomad/watch"
|
||||
)
|
||||
@@ -293,7 +294,12 @@ func (j *Job) List(args *structs.JobListRequest,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iter, err := snap.Jobs()
|
||||
var iter memdb.ResultIterator
|
||||
if prefix := args.QueryOptions.Prefix; prefix != "" {
|
||||
iter, err = snap.JobsByIDPrefix(prefix)
|
||||
} else {
|
||||
iter, err = snap.Jobs()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -683,6 +683,25 @@ func TestJobEndpoint_ListJobs(t *testing.T) {
|
||||
if resp2.Jobs[0].ID != job.ID {
|
||||
t.Fatalf("bad: %#v", resp2.Jobs[0])
|
||||
}
|
||||
|
||||
// Lookup the jobs by prefix
|
||||
get = &structs.JobListRequest{
|
||||
QueryOptions: structs.QueryOptions{Region: "global", Prefix: resp2.Jobs[0].ID[:4]},
|
||||
}
|
||||
var resp3 structs.JobListResponse
|
||||
if err := msgpackrpc.CallWithCodec(codec, "Job.List", get, &resp3); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp3.Index != 1000 {
|
||||
t.Fatalf("Bad index: %d %d", resp3.Index, 1000)
|
||||
}
|
||||
|
||||
if len(resp3.Jobs) != 1 {
|
||||
t.Fatalf("bad: %#v", resp3.Jobs)
|
||||
}
|
||||
if resp3.Jobs[0].ID != job.ID {
|
||||
t.Fatalf("bad: %#v", resp3.Jobs[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobEndpoint_ListJobs_Blocking(t *testing.T) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/armon/go-metrics"
|
||||
"github.com/hashicorp/go-memdb"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
"github.com/hashicorp/nomad/nomad/watch"
|
||||
)
|
||||
@@ -424,7 +425,12 @@ func (n *Node) List(args *structs.NodeListRequest,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iter, err := snap.Nodes()
|
||||
var iter memdb.ResultIterator
|
||||
if prefix := args.QueryOptions.Prefix; prefix != "" {
|
||||
iter, err = snap.NodesByIDPrefix(prefix)
|
||||
} else {
|
||||
iter, err = snap.Nodes()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -879,6 +879,25 @@ func TestClientEndpoint_ListNodes(t *testing.T) {
|
||||
if resp2.Nodes[0].ID != node.ID {
|
||||
t.Fatalf("bad: %#v", resp2.Nodes[0])
|
||||
}
|
||||
|
||||
// Lookup the node with prefix
|
||||
get = &structs.NodeListRequest{
|
||||
QueryOptions: structs.QueryOptions{Region: "global", Prefix: node.ID[:4]},
|
||||
}
|
||||
var resp3 structs.NodeListResponse
|
||||
if err := msgpackrpc.CallWithCodec(codec, "Node.List", get, &resp3); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp3.Index != resp.Index {
|
||||
t.Fatalf("Bad index: %d %d", resp3.Index, resp2.Index)
|
||||
}
|
||||
|
||||
if len(resp3.Nodes) != 1 {
|
||||
t.Fatalf("bad: %#v", resp3.Nodes)
|
||||
}
|
||||
if resp3.Nodes[0].ID != node.ID {
|
||||
t.Fatalf("bad: %#v", resp3.Nodes[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientEndpoint_ListNodes_Blocking(t *testing.T) {
|
||||
|
||||
@@ -252,6 +252,18 @@ func (s *StateStore) NodeByID(nodeID string) (*structs.Node, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NodesByIDPrefix is used to lookup nodes by prefix
|
||||
func (s *StateStore) NodesByIDPrefix(nodeID string) (memdb.ResultIterator, error) {
|
||||
txn := s.db.Txn(false)
|
||||
|
||||
iter, err := txn.Get("nodes", "id_prefix", nodeID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("node lookup failed: %v", err)
|
||||
}
|
||||
|
||||
return iter, nil
|
||||
}
|
||||
|
||||
// Nodes returns an iterator over all the nodes
|
||||
func (s *StateStore) Nodes() (memdb.ResultIterator, error) {
|
||||
txn := s.db.Txn(false)
|
||||
@@ -347,6 +359,18 @@ func (s *StateStore) JobByID(id string) (*structs.Job, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// JobsByIDPrefix is used to lookup a job by prefix
|
||||
func (s *StateStore) JobsByIDPrefix(id string) (memdb.ResultIterator, error) {
|
||||
txn := s.db.Txn(false)
|
||||
|
||||
iter, err := txn.Get("jobs", "id_prefix", id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("job lookup failed: %v", err)
|
||||
}
|
||||
|
||||
return iter, nil
|
||||
}
|
||||
|
||||
// Jobs returns an iterator over all the jobs
|
||||
func (s *StateStore) Jobs() (memdb.ResultIterator, error) {
|
||||
txn := s.db.Txn(false)
|
||||
@@ -607,6 +631,18 @@ func (s *StateStore) EvalByID(id string) (*structs.Evaluation, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// EvalsByIDPrefix is used to lookup evaluations by prefix
|
||||
func (s *StateStore) EvalsByIDPrefix(id string) (memdb.ResultIterator, error) {
|
||||
txn := s.db.Txn(false)
|
||||
|
||||
iter, err := txn.Get("evals", "id_prefix", id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("eval lookup failed: %v", err)
|
||||
}
|
||||
|
||||
return iter, nil
|
||||
}
|
||||
|
||||
// EvalsByJob returns all the evaluations by job id
|
||||
func (s *StateStore) EvalsByJob(jobID string) ([]*structs.Evaluation, error) {
|
||||
txn := s.db.Txn(false)
|
||||
@@ -756,6 +792,18 @@ func (s *StateStore) AllocByID(id string) (*structs.Allocation, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// AllocsByIDPrefix is used to lookup allocs by prefix
|
||||
func (s *StateStore) AllocsByIDPrefix(id string) (memdb.ResultIterator, error) {
|
||||
txn := s.db.Txn(false)
|
||||
|
||||
iter, err := txn.Get("allocs", "id_prefix", id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("alloc lookup failed: %v", err)
|
||||
}
|
||||
|
||||
return iter, nil
|
||||
}
|
||||
|
||||
// AllocsByNode returns all the allocations by node
|
||||
func (s *StateStore) AllocsByNode(node string) ([]*structs.Allocation, error) {
|
||||
txn := s.db.Txn(false)
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-memdb"
|
||||
"github.com/hashicorp/nomad/nomad/mock"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
"github.com/hashicorp/nomad/nomad/watch"
|
||||
@@ -216,6 +217,77 @@ func TestStateStore_Nodes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateStore_NodesByIDPrefix(t *testing.T) {
|
||||
state := testStateStore(t)
|
||||
node := mock.Node()
|
||||
|
||||
node.ID = "11111111-662e-d0ab-d1c9-3e434af7bdb4"
|
||||
err := state.UpsertNode(1000, node)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
iter, err := state.NodesByIDPrefix(node.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
gatherNodes := func(iter memdb.ResultIterator) []*structs.Node {
|
||||
var nodes []*structs.Node
|
||||
for {
|
||||
raw := iter.Next()
|
||||
if raw == nil {
|
||||
break
|
||||
}
|
||||
node := raw.(*structs.Node)
|
||||
nodes = append(nodes, node)
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
nodes := gatherNodes(iter)
|
||||
if len(nodes) != 1 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
iter, err = state.NodesByIDPrefix("11")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
nodes = gatherNodes(iter)
|
||||
if len(nodes) != 1 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
node = mock.Node()
|
||||
node.ID = "11222222-662e-d0ab-d1c9-3e434af7bdb4"
|
||||
err = state.UpsertNode(1001, node)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
iter, err = state.NodesByIDPrefix("11")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
nodes = gatherNodes(iter)
|
||||
if len(nodes) != 2 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
iter, err = state.NodesByIDPrefix("111")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
nodes = gatherNodes(iter)
|
||||
if len(nodes) != 1 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateStore_RestoreNode(t *testing.T) {
|
||||
state := testStateStore(t)
|
||||
node := mock.Node()
|
||||
@@ -405,6 +477,76 @@ func TestStateStore_Jobs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateStore_JobsByIDPrefix(t *testing.T) {
|
||||
state := testStateStore(t)
|
||||
job := mock.Job()
|
||||
|
||||
job.ID = "redis"
|
||||
err := state.UpsertJob(1000, job)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
iter, err := state.JobsByIDPrefix(job.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
gatherJobs := func(iter memdb.ResultIterator) []*structs.Job {
|
||||
var jobs []*structs.Job
|
||||
for {
|
||||
raw := iter.Next()
|
||||
if raw == nil {
|
||||
break
|
||||
}
|
||||
jobs = append(jobs, raw.(*structs.Job))
|
||||
}
|
||||
return jobs
|
||||
}
|
||||
|
||||
jobs := gatherJobs(iter)
|
||||
if len(jobs) != 1 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
iter, err = state.JobsByIDPrefix("re")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
jobs = gatherJobs(iter)
|
||||
if len(jobs) != 1 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
job = mock.Job()
|
||||
job.ID = "riak"
|
||||
err = state.UpsertJob(1001, job)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
iter, err = state.JobsByIDPrefix("r")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
jobs = gatherJobs(iter)
|
||||
if len(jobs) != 2 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
iter, err = state.JobsByIDPrefix("ri")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
jobs = gatherJobs(iter)
|
||||
if len(jobs) != 1 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateStore_JobsByPeriodic(t *testing.T) {
|
||||
state := testStateStore(t)
|
||||
var periodic, nonPeriodic []*structs.Job
|
||||
@@ -444,9 +586,6 @@ func TestStateStore_JobsByPeriodic(t *testing.T) {
|
||||
}
|
||||
|
||||
iter, err = state.JobsByPeriodic(false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
var outNonPeriodic []*structs.Job
|
||||
for {
|
||||
@@ -1142,6 +1281,74 @@ func TestStateStore_Evals(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateStore_EvalsByIDPrefix(t *testing.T) {
|
||||
state := testStateStore(t)
|
||||
var evals []*structs.Evaluation
|
||||
|
||||
ids := []string{
|
||||
"aaaaaaaa-7bfb-395d-eb95-0685af2176b2",
|
||||
"aaaaaaab-7bfb-395d-eb95-0685af2176b2",
|
||||
"aaaaaabb-7bfb-395d-eb95-0685af2176b2",
|
||||
"aaaaabbb-7bfb-395d-eb95-0685af2176b2",
|
||||
"aaaabbbb-7bfb-395d-eb95-0685af2176b2",
|
||||
"aaabbbbb-7bfb-395d-eb95-0685af2176b2",
|
||||
"aabbbbbb-7bfb-395d-eb95-0685af2176b2",
|
||||
"abbbbbbb-7bfb-395d-eb95-0685af2176b2",
|
||||
"bbbbbbbb-7bfb-395d-eb95-0685af2176b2",
|
||||
}
|
||||
for i := 0; i < 9; i++ {
|
||||
eval := mock.Eval()
|
||||
eval.ID = ids[i]
|
||||
evals = append(evals, eval)
|
||||
}
|
||||
|
||||
err := state.UpsertEvals(1000, evals)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
iter, err := state.EvalsByIDPrefix("aaaa")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
gatherEvals := func(iter memdb.ResultIterator) []*structs.Evaluation {
|
||||
var evals []*structs.Evaluation
|
||||
for {
|
||||
raw := iter.Next()
|
||||
if raw == nil {
|
||||
break
|
||||
}
|
||||
evals = append(evals, raw.(*structs.Evaluation))
|
||||
}
|
||||
return evals
|
||||
}
|
||||
|
||||
out := gatherEvals(iter)
|
||||
if len(out) != 5 {
|
||||
t.Fatalf("bad: expected five evaluations, got: %#v", out)
|
||||
}
|
||||
|
||||
sort.Sort(EvalIDSort(evals))
|
||||
|
||||
for index, eval := range out {
|
||||
if ids[index] != eval.ID {
|
||||
t.Fatalf("bad: got unexpected id: %s", eval.ID)
|
||||
}
|
||||
}
|
||||
|
||||
iter, err = state.EvalsByIDPrefix("b-a7bfb")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
out = gatherEvals(iter)
|
||||
if len(out) != 0 {
|
||||
t.Fatalf("bad: unexpected zero evaluations, got: %#v", out)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestStateStore_RestoreEval(t *testing.T) {
|
||||
state := testStateStore(t)
|
||||
eval := mock.Eval()
|
||||
@@ -1402,6 +1609,73 @@ func TestStateStore_AllocsByJob(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateStore_AllocsByIDPrefix(t *testing.T) {
|
||||
state := testStateStore(t)
|
||||
var allocs []*structs.Allocation
|
||||
|
||||
ids := []string{
|
||||
"aaaaaaaa-7bfb-395d-eb95-0685af2176b2",
|
||||
"aaaaaaab-7bfb-395d-eb95-0685af2176b2",
|
||||
"aaaaaabb-7bfb-395d-eb95-0685af2176b2",
|
||||
"aaaaabbb-7bfb-395d-eb95-0685af2176b2",
|
||||
"aaaabbbb-7bfb-395d-eb95-0685af2176b2",
|
||||
"aaabbbbb-7bfb-395d-eb95-0685af2176b2",
|
||||
"aabbbbbb-7bfb-395d-eb95-0685af2176b2",
|
||||
"abbbbbbb-7bfb-395d-eb95-0685af2176b2",
|
||||
"bbbbbbbb-7bfb-395d-eb95-0685af2176b2",
|
||||
}
|
||||
for i := 0; i < 9; i++ {
|
||||
alloc := mock.Alloc()
|
||||
alloc.ID = ids[i]
|
||||
allocs = append(allocs, alloc)
|
||||
}
|
||||
|
||||
err := state.UpsertAllocs(1000, allocs)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
iter, err := state.AllocsByIDPrefix("aaaa")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
gatherAllocs := func(iter memdb.ResultIterator) []*structs.Allocation {
|
||||
var allocs []*structs.Allocation
|
||||
for {
|
||||
raw := iter.Next()
|
||||
if raw == nil {
|
||||
break
|
||||
}
|
||||
allocs = append(allocs, raw.(*structs.Allocation))
|
||||
}
|
||||
return allocs
|
||||
}
|
||||
|
||||
out := gatherAllocs(iter)
|
||||
if len(out) != 5 {
|
||||
t.Fatalf("bad: expected five allocations, got: %#v", out)
|
||||
}
|
||||
|
||||
sort.Sort(AllocIDSort(allocs))
|
||||
|
||||
for index, alloc := range out {
|
||||
if ids[index] != alloc.ID {
|
||||
t.Fatalf("bad: got unexpected id: %s", alloc.ID)
|
||||
}
|
||||
}
|
||||
|
||||
iter, err = state.AllocsByIDPrefix("b-a7bfb")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
out = gatherAllocs(iter)
|
||||
if len(out) != 0 {
|
||||
t.Fatalf("bad: unexpected zero allocations, got: %#v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateStore_Allocs(t *testing.T) {
|
||||
state := testStateStore(t)
|
||||
var allocs []*structs.Allocation
|
||||
|
||||
@@ -71,6 +71,9 @@ type QueryOptions struct {
|
||||
// If set, any follower can service the request. Results
|
||||
// may be arbitrarily stale.
|
||||
AllowStale bool
|
||||
|
||||
// If set, used as prefix for resource list searches
|
||||
Prefix string
|
||||
}
|
||||
|
||||
func (q QueryOptions) RequestRegion() string {
|
||||
|
||||
@@ -19,8 +19,9 @@ current state of its tasks.
|
||||
nomad alloc-status [options] <allocation>
|
||||
```
|
||||
|
||||
An allocation ID must be provided. This specific allocation will be queried
|
||||
and detailed information for it will be dumped.
|
||||
An allocation ID or prefix must be provided. If there is an exact match, the
|
||||
full details of the allocation will be displayed. Otherwise, a list of matching
|
||||
allocations and information will be displayed.
|
||||
|
||||
## General Options
|
||||
|
||||
|
||||
@@ -20,10 +20,12 @@ reaches a terminal state.
|
||||
nomad eval-monitor [options] <eval>
|
||||
```
|
||||
|
||||
The eval-monitor command requires a single argument, specifying the
|
||||
evaluation ID to monitor. An interactive monitoring session will be
|
||||
started in the terminal. It is safe to exit the monitor at any time
|
||||
using ctrl+c.
|
||||
An evaluation ID or prefix must be provided. If there is an exact match, the
|
||||
the evaluation will be monitored. Otherwise, a list of matching evaluations and
|
||||
information will be displayed.
|
||||
|
||||
An interactive monitoring session will be started in the terminal. It is safe
|
||||
to exit the monitor at any time using ctrl+c.
|
||||
|
||||
The command will exit when the given evaluation reaches a terminal
|
||||
state (completed or failed). Exit code 0 is returned on successful
|
||||
|
||||
@@ -21,9 +21,12 @@ nicely by providing the current drain status of a given node.
|
||||
nomad node-drain [options] <node>
|
||||
```
|
||||
|
||||
This command expects exactly one argument to specify the node ID to enable or
|
||||
disable drain mode for. It is also required to pass one of `-enable` or
|
||||
`-disable`, depending on which operation is desired.
|
||||
A node ID or prefix must be provided. If there is an exact match, the
|
||||
drain mode will be adjusted for that node. Otherwise, a list of matching
|
||||
nodes and information will be displayed.
|
||||
|
||||
It is also required to pass one of `-enable` or `-disable`, depending on which
|
||||
operation is desired.
|
||||
|
||||
## General Options
|
||||
|
||||
|
||||
@@ -20,9 +20,11 @@ nomad node-status [options] [node]
|
||||
|
||||
If no node ID is passed, then the command will enter "list mode" and dump a
|
||||
high-level list of all known nodes. This list output contains less information
|
||||
but is a good way to get a bird's-eye view of things. If a node ID is specified,
|
||||
then that particular node will be queried, and detailed information will be
|
||||
displayed.
|
||||
but is a good way to get a bird's-eye view of things.
|
||||
|
||||
If there is an exact match based on the provided node ID or prefix, then that
|
||||
particular node will be queried, and detailed information will be displayed.
|
||||
Otherwise, a list of matching nodes and information will be displayed.
|
||||
|
||||
## General Options
|
||||
|
||||
@@ -50,7 +52,7 @@ Single-node view in short mode:
|
||||
$ nomad node-status -short 1f3f03ea-a420-b64b-c73b-51290ed7f481
|
||||
ID = 1f3f03ea-a420-b64b-c73b-51290ed7f481
|
||||
Name = node2
|
||||
Class =
|
||||
Class =
|
||||
Datacenter = dc1
|
||||
Drain = false
|
||||
Status = ready
|
||||
@@ -62,7 +64,7 @@ Full output for a single node:
|
||||
$ nomad node-status 1f3f03ea-a420-b64b-c73b-51290ed7f481
|
||||
ID = 1f3f03ea-a420-b64b-c73b-51290ed7f481
|
||||
Name = node2
|
||||
Class =
|
||||
Class =
|
||||
Datacenter = dc1
|
||||
Drain = false
|
||||
Status = ready
|
||||
|
||||
@@ -16,10 +16,13 @@ The `status` command displays status information for jobs.
|
||||
nomad status [options] [job]
|
||||
```
|
||||
|
||||
This command accepts an optional job ID as the sole argument. If the job ID is
|
||||
provided, information about the specific job is queried and displayed. If the ID
|
||||
is omitted, the command lists out all of the existing jobs and a few of the most
|
||||
useful status fields for each.
|
||||
This command accepts an optional job ID or prefix as the sole argument. If there
|
||||
is an exact match based on the provided job ID or prefix, then information about
|
||||
the specific job is queried and displayed. Otherwise, a list of matching jobs and
|
||||
information will be displayed.
|
||||
|
||||
If the ID is omitted, the command lists out all of the existing jobs and a few of
|
||||
the most useful status fields for each.
|
||||
|
||||
## General Options
|
||||
|
||||
|
||||
@@ -17,8 +17,10 @@ to cancel all of the running allocations.
|
||||
nomad stop [options] <job>
|
||||
```
|
||||
|
||||
The stop command requires a single argument, specifying the job ID to
|
||||
cancel.
|
||||
The stop command requires a single argument, specifying the job ID or prefix to
|
||||
cancel. If there is an exact match based on the provided job ID or prefix, then
|
||||
the job will be cancelled. Otherwise, a list of matching jobs and information
|
||||
will be displayed.
|
||||
|
||||
Upon successful deregistration, an interactive monitor session will start to
|
||||
display log lines as the job unwinds its allocations and completes shutting
|
||||
|
||||
@@ -28,7 +28,14 @@ be specified using the `?region=` query parameter.
|
||||
|
||||
<dt>Parameters</dt>
|
||||
<dd>
|
||||
None
|
||||
<ul>
|
||||
<li>
|
||||
<span class="param">prefix</span>
|
||||
<span class="param-flags">optional</span>
|
||||
<span class="param-flags">even-length</span>
|
||||
Filter allocations based on an identifier prefix.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
<dt>Blocking Queries</dt>
|
||||
|
||||
@@ -28,7 +28,14 @@ be specified using the `?region=` query parameter.
|
||||
|
||||
<dt>Parameters</dt>
|
||||
<dd>
|
||||
None
|
||||
<ul>
|
||||
<li>
|
||||
<span class="param">prefix</span>
|
||||
<span class="param-flags">optional</span>
|
||||
<span class="param-flags">even-length</span>
|
||||
Filter evaluations based on an identifier prefix.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
<dt>Blocking Queries</dt>
|
||||
|
||||
@@ -28,7 +28,13 @@ another region can be specified using the `?region=` query parameter.
|
||||
|
||||
<dt>Parameters</dt>
|
||||
<dd>
|
||||
None
|
||||
<ul>
|
||||
<li>
|
||||
<span class="param">prefix</span>
|
||||
<span class="param-flags">optional</span>
|
||||
Filter jobs based on an identifier prefix.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
<dt>Blocking Queries</dt>
|
||||
|
||||
@@ -28,7 +28,13 @@ be specified using the `?region=` query parameter.
|
||||
|
||||
<dt>Parameters</dt>
|
||||
<dd>
|
||||
None
|
||||
<ul>
|
||||
<li>
|
||||
<span class="param">prefix</span>
|
||||
<span class="param-flags">optional</span>
|
||||
Filter nodes based on an identifier prefix.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
<dt>Blocking Queries</dt>
|
||||
|
||||
Reference in New Issue
Block a user