Adds support for coordinates to client API.

This commit is contained in:
James Phillips 2015-10-15 21:42:09 -07:00
parent 660da92152
commit 787f946f8d
4 changed files with 133 additions and 0 deletions

View File

@ -44,6 +44,12 @@ type QueryOptions struct {
// Token is used to provide a per-request ACL token // Token is used to provide a per-request ACL token
// which overrides the agent's default token. // which overrides the agent's default token.
Token string 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 // WriteOptions are used to parameterize a write
@ -250,6 +256,9 @@ func (r *request) setQueryOptions(q *QueryOptions) {
if q.Token != "" { if q.Token != "" {
r.params.Set("token", 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 // durToMsec converts a duration to a millisecond specified string

View File

@ -127,6 +127,7 @@ func TestSetQueryOptions(t *testing.T) {
WaitIndex: 1000, WaitIndex: 1000,
WaitTime: 100 * time.Second, WaitTime: 100 * time.Second,
Token: "12345", Token: "12345",
Near: "nodex",
} }
r.setQueryOptions(q) r.setQueryOptions(q)
@ -148,6 +149,9 @@ func TestSetQueryOptions(t *testing.T) {
if r.params.Get("token") != "12345" { if r.params.Get("token") != "12345" {
t.Fatalf("bad: %v", r.params) t.Fatalf("bad: %v", r.params)
} }
if r.params.Get("near") != "nodex" {
t.Fatalf("bad: %v", r.params)
}
} }
func TestSetWriteOptions(t *testing.T) { func TestSetWriteOptions(t *testing.T) {

66
api/coordinate.go Normal file
View File

@ -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
api/coordinate_test.go Normal file
View File

@ -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)
})
}