client: support modifying the servers list

This commit is contained in:
Ryan Uber 2015-09-24 18:51:17 -07:00
parent fce6a5f7c7
commit 6f15160f5c
2 changed files with 74 additions and 5 deletions

View File

@ -74,6 +74,9 @@ type Client struct {
lastRPCTime time.Time
lastServerLock sync.Mutex
servers []string
serverLock sync.RWMutex
connPool *nomad.ConnPool
lastHeartbeat time.Time
@ -128,6 +131,9 @@ func NewClient(cfg *config.Config) (*Client, error) {
return nil, fmt.Errorf("driver setup failed: %v", err)
}
// Set up the known servers list
c.SetServers(c.config.Servers)
// Start the client!
go c.run()
return c, nil
@ -213,13 +219,12 @@ func (c *Client) pickServer() (net.Addr, error) {
}
// Bail if we can't find any servers
if len(c.config.Servers) == 0 {
servers := c.Servers()
if len(servers) == 0 {
return nil, fmt.Errorf("no known servers")
}
// Copy the list of servers and shuffle
servers := make([]string, len(c.config.Servers))
copy(servers, c.config.Servers)
// Shuffle so we don't always use the same server
shuffleStrings(servers)
// Try to resolve each server
@ -237,6 +242,26 @@ func (c *Client) pickServer() (net.Addr, error) {
return nil, fmt.Errorf("failed to resolve any servers")
}
// Servers is used to return the current known servers list. When an agent
// is first started, this list comes directly from configuration files.
func (c *Client) Servers() []string {
c.serverLock.RLock()
defer c.serverLock.RUnlock()
return c.servers
}
// SetServers is used to modify the known servers list. This avoids forcing
// a config rollout + rolling restart and enables auto-join features. The
// full set of servers is passed to support adding and/or removing servers.
func (c *Client) SetServers(servers []string) {
c.serverLock.Lock()
defer c.serverLock.Unlock()
if servers == nil {
servers = make([]string, 0)
}
c.servers = servers
}
// Stats is used to return statistics for debugging and insight
// for various sub-systems
func (c *Client) Stats() map[string]map[string]string {
@ -249,7 +274,7 @@ func (c *Client) Stats() map[string]map[string]string {
stats := map[string]map[string]string{
"client": map[string]string{
"known_servers": toString(uint64(len(c.config.Servers))),
"known_servers": toString(uint64(len(c.Servers()))),
"num_allocations": toString(uint64(numAllocs)),
"last_heartbeat": fmt.Sprintf("%v", time.Since(c.lastHeartbeat)),
"heartbeat_ttl": fmt.Sprintf("%v", c.heartbeatTTL),

View File

@ -6,6 +6,7 @@ import (
"net"
"os"
"path/filepath"
"reflect"
"sync/atomic"
"testing"
"time"
@ -395,3 +396,46 @@ func TestClient_Init(t *testing.T) {
t.Fatalf("err: %s", err)
}
}
func TestClient_SetServers(t *testing.T) {
client := testClient(t, nil)
// Sets an empty list
client.SetServers(nil)
if client.servers == nil {
t.Fatalf("should not be nil")
}
// Set the initial servers list
expect := []string{"foo"}
client.SetServers(expect)
if !reflect.DeepEqual(client.servers, expect) {
t.Fatalf("expect %v, got %v", expect, client.servers)
}
// Add a server
expect = []string{"foo", "bar"}
client.SetServers(expect)
if !reflect.DeepEqual(client.servers, expect) {
t.Fatalf("expect %v, got %v", expect, client.servers)
}
// Remove a server
expect = []string{"bar"}
client.SetServers(expect)
if !reflect.DeepEqual(client.servers, expect) {
t.Fatalf("expect %v, got %v", expect, client.servers)
}
// Add and remove a server
expect = []string{"baz", "zip"}
client.SetServers(expect)
if !reflect.DeepEqual(client.servers, expect) {
t.Fatalf("expect %v, got %v", expect, client.servers)
}
// Query the servers list
if servers := client.Servers(); !reflect.DeepEqual(servers, expect) {
t.Fatalf("expect %v, got %v", expect, servers)
}
}