Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow lookups based on short identifiers #575

Merged
merged 17 commits into from
Jan 6, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions api/allocations.go
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is also added in the structs. Once this PR is done I will take a look at removing the api specific structs and using those in the structs package as specified in issue #329 .

}

// 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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also test that you get the right ID

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
Loading