Skip to content

Commit

Permalink
Adds support for coordinates to client API.
Browse files Browse the repository at this point in the history
  • Loading branch information
James Phillips committed Oct 23, 2015
1 parent 660da92 commit 787f946
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 0 deletions.
9 changes: 9 additions & 0 deletions api/api.go
Expand Up @@ -44,6 +44,12 @@ type QueryOptions struct {
// Token is used to provide a per-request ACL token
// which overrides the agent's default token.
Token string

// Near is used to provide a node name that will sort the results
// in ascending order based on the estimated round trip time from
// that node. Setting this to "_agent" will use the agent's node
// for the sort.
Near string
}

// WriteOptions are used to parameterize a write
Expand Down Expand Up @@ -250,6 +256,9 @@ func (r *request) setQueryOptions(q *QueryOptions) {
if q.Token != "" {
r.params.Set("token", q.Token)
}
if q.Near != "" {
r.params.Set("near", q.Near)
}
}

// durToMsec converts a duration to a millisecond specified string
Expand Down
4 changes: 4 additions & 0 deletions api/api_test.go
Expand Up @@ -127,6 +127,7 @@ func TestSetQueryOptions(t *testing.T) {
WaitIndex: 1000,
WaitTime: 100 * time.Second,
Token: "12345",
Near: "nodex",
}
r.setQueryOptions(q)

Expand All @@ -148,6 +149,9 @@ func TestSetQueryOptions(t *testing.T) {
if r.params.Get("token") != "12345" {
t.Fatalf("bad: %v", r.params)
}
if r.params.Get("near") != "nodex" {
t.Fatalf("bad: %v", r.params)
}
}

func TestSetWriteOptions(t *testing.T) {
Expand Down
66 changes: 66 additions & 0 deletions api/coordinate.go
@@ -0,0 +1,66 @@
package api

import (
"github.com/hashicorp/serf/coordinate"
)

// CoordinateEntry represents a node and its associated network coordinate.
type CoordinateEntry struct {
Node string
Coord *coordinate.Coordinate
}

// CoordinateDatacenterMap represents a datacenter and its associated WAN
// nodes and their associates coordinates.
type CoordinateDatacenterMap struct {
Datacenter string
Coordinates []CoordinateEntry
}

// Coordinate can be used to query the coordinate endpoints
type Coordinate struct {
c *Client
}

// Coordinate returns a handle to the coordinate endpoints
func (c *Client) Coordinate() *Coordinate {
return &Coordinate{c}
}

// Datacenters is used to return the coordinates of all the servers in the WAN
// pool.
func (c *Coordinate) Datacenters() ([]*CoordinateDatacenterMap, error) {
r := c.c.newRequest("GET", "/v1/coordinate/datacenters")
_, resp, err := requireOK(c.c.doRequest(r))
if err != nil {
return nil, err
}
defer resp.Body.Close()

var out []*CoordinateDatacenterMap
if err := decodeBody(resp, &out); err != nil {
return nil, err
}
return out, nil
}

// Nodes is used to return the coordinates of all the nodes in the LAN pool.
func (c *Coordinate) Nodes(q *QueryOptions) ([]*CoordinateEntry, *QueryMeta, error) {
r := c.c.newRequest("GET", "/v1/coordinate/nodes")
r.setQueryOptions(q)
rtt, resp, err := requireOK(c.c.doRequest(r))
if err != nil {
return nil, nil, err
}
defer resp.Body.Close()

qm := &QueryMeta{}
parseQueryMeta(resp, qm)
qm.RequestTime = rtt

var out []*CoordinateEntry
if err := decodeBody(resp, &out); err != nil {
return nil, nil, err
}
return out, qm, nil
}
54 changes: 54 additions & 0 deletions api/coordinate_test.go
@@ -0,0 +1,54 @@
package api

import (
"fmt"
"testing"

"github.com/hashicorp/consul/testutil"
)

func TestCoordinate_Datacenters(t *testing.T) {
t.Parallel()
c, s := makeClient(t)
defer s.Stop()

coordinate := c.Coordinate()

testutil.WaitForResult(func() (bool, error) {
datacenters, err := coordinate.Datacenters()
if err != nil {
return false, err
}

if len(datacenters) == 0 {
return false, fmt.Errorf("Bad: %v", datacenters)
}

return true, nil
}, func(err error) {
t.Fatalf("err: %s", err)
})
}

func TestCoordinate_Nodes(t *testing.T) {
t.Parallel()
c, s := makeClient(t)
defer s.Stop()

coordinate := c.Coordinate()

testutil.WaitForResult(func() (bool, error) {
_, _, err := coordinate.Nodes(nil)
if err != nil {
return false, err
}

// There's not a good way to populate coordinates without
// waiting for them to calculate and update, so the best
// we can do is call the endpoint and make sure we don't
// get an error.
return true, nil
}, func(err error) {
t.Fatalf("err: %s", err)
})
}

0 comments on commit 787f946

Please sign in to comment.