Skip to content

Commit

Permalink
Merge pull request #575 from nautsio/f-short-ids
Browse files Browse the repository at this point in the history
Allow lookups based on short identifiers
  • Loading branch information
dadgar committed Jan 6, 2016
2 parents d6d969d + 6295108 commit cf3152d
Show file tree
Hide file tree
Showing 48 changed files with 1,230 additions and 64 deletions.
4 changes: 4 additions & 0 deletions api/allocations.go
Expand Up @@ -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
Expand Down
46 changes: 46 additions & 0 deletions api/allocations_test.go
Expand Up @@ -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},
Expand Down
6 changes: 6 additions & 0 deletions api/api.go
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions api/evaluations.go
Expand Up @@ -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
Expand Down
39 changes: 39 additions & 0 deletions api/evaluations_test.go
Expand Up @@ -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()
Expand Down
5 changes: 5 additions & 0 deletions api/jobs.go
Expand Up @@ -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) {
Expand Down
76 changes: 76 additions & 0 deletions api/jobs_test.go
Expand Up @@ -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()
Expand Down
4 changes: 4 additions & 0 deletions api/nodes.go
Expand Up @@ -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
Expand Down
41 changes: 41 additions & 0 deletions api/nodes_test.go
Expand Up @@ -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
Expand Down
53 changes: 52 additions & 1 deletion command/agent/alloc_endpoint_test.go
Expand Up @@ -45,14 +45,65 @@ 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)
}
})
}

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
Expand Down

0 comments on commit cf3152d

Please sign in to comment.