mirror of https://github.com/status-im/consul.git
Merge pull request #881 from hashicorp/f-testharness
Move test harness into testutil
This commit is contained in:
commit
9715f3b270
|
@ -17,7 +17,7 @@ func TestACL_CreateDestroy(t *testing.T) {
|
||||||
t.SkipNow()
|
t.SkipNow()
|
||||||
}
|
}
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
c.config.Token = CONSUL_ROOT
|
c.config.Token = CONSUL_ROOT
|
||||||
acl := c.ACL()
|
acl := c.ACL()
|
||||||
|
@ -65,7 +65,7 @@ func TestACL_CloneDestroy(t *testing.T) {
|
||||||
t.SkipNow()
|
t.SkipNow()
|
||||||
}
|
}
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
c.config.Token = CONSUL_ROOT
|
c.config.Token = CONSUL_ROOT
|
||||||
acl := c.ACL()
|
acl := c.ACL()
|
||||||
|
@ -98,7 +98,7 @@ func TestACL_Info(t *testing.T) {
|
||||||
t.SkipNow()
|
t.SkipNow()
|
||||||
}
|
}
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
c.config.Token = CONSUL_ROOT
|
c.config.Token = CONSUL_ROOT
|
||||||
acl := c.ACL()
|
acl := c.ACL()
|
||||||
|
@ -125,7 +125,7 @@ func TestACL_List(t *testing.T) {
|
||||||
t.SkipNow()
|
t.SkipNow()
|
||||||
}
|
}
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
c.config.Token = CONSUL_ROOT
|
c.config.Token = CONSUL_ROOT
|
||||||
acl := c.ACL()
|
acl := c.ACL()
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
|
|
||||||
func TestAgent_Self(t *testing.T) {
|
func TestAgent_Self(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
agent := c.Agent()
|
agent := c.Agent()
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ func TestAgent_Self(t *testing.T) {
|
||||||
|
|
||||||
func TestAgent_Members(t *testing.T) {
|
func TestAgent_Members(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
agent := c.Agent()
|
agent := c.Agent()
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ func TestAgent_Members(t *testing.T) {
|
||||||
|
|
||||||
func TestAgent_Services(t *testing.T) {
|
func TestAgent_Services(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
agent := c.Agent()
|
agent := c.Agent()
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ func TestAgent_Services(t *testing.T) {
|
||||||
|
|
||||||
func TestAgent_ServiceAddress(t *testing.T) {
|
func TestAgent_ServiceAddress(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
agent := c.Agent()
|
agent := c.Agent()
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ func TestAgent_ServiceAddress(t *testing.T) {
|
||||||
|
|
||||||
func TestAgent_Services_MultipleChecks(t *testing.T) {
|
func TestAgent_Services_MultipleChecks(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
agent := c.Agent()
|
agent := c.Agent()
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ func TestAgent_Services_MultipleChecks(t *testing.T) {
|
||||||
|
|
||||||
func TestAgent_SetTTLStatus(t *testing.T) {
|
func TestAgent_SetTTLStatus(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
agent := c.Agent()
|
agent := c.Agent()
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ func TestAgent_SetTTLStatus(t *testing.T) {
|
||||||
|
|
||||||
func TestAgent_Checks(t *testing.T) {
|
func TestAgent_Checks(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
agent := c.Agent()
|
agent := c.Agent()
|
||||||
|
|
||||||
|
@ -235,7 +235,7 @@ func TestAgent_Checks(t *testing.T) {
|
||||||
|
|
||||||
func TestAgent_Checks_serviceBound(t *testing.T) {
|
func TestAgent_Checks_serviceBound(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
agent := c.Agent()
|
agent := c.Agent()
|
||||||
|
|
||||||
|
@ -273,7 +273,7 @@ func TestAgent_Checks_serviceBound(t *testing.T) {
|
||||||
|
|
||||||
func TestAgent_Join(t *testing.T) {
|
func TestAgent_Join(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
agent := c.Agent()
|
agent := c.Agent()
|
||||||
|
|
||||||
|
@ -292,7 +292,7 @@ func TestAgent_Join(t *testing.T) {
|
||||||
|
|
||||||
func TestAgent_ForceLeave(t *testing.T) {
|
func TestAgent_ForceLeave(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
agent := c.Agent()
|
agent := c.Agent()
|
||||||
|
|
||||||
|
@ -305,7 +305,7 @@ func TestAgent_ForceLeave(t *testing.T) {
|
||||||
|
|
||||||
func TestServiceMaintenance(t *testing.T) {
|
func TestServiceMaintenance(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
agent := c.Agent()
|
agent := c.Agent()
|
||||||
|
|
||||||
|
@ -359,7 +359,7 @@ func TestServiceMaintenance(t *testing.T) {
|
||||||
|
|
||||||
func TestNodeMaintenance(t *testing.T) {
|
func TestNodeMaintenance(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
agent := c.Agent()
|
agent := c.Agent()
|
||||||
|
|
||||||
|
|
170
api/api_test.go
170
api/api_test.go
|
@ -2,12 +2,10 @@ package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
crand "crypto/rand"
|
crand "crypto/rand"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -16,131 +14,26 @@ import (
|
||||||
"github.com/hashicorp/consul/testutil"
|
"github.com/hashicorp/consul/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testServer struct {
|
|
||||||
pid int
|
|
||||||
dataDir string
|
|
||||||
configFile string
|
|
||||||
}
|
|
||||||
|
|
||||||
type testPortConfig struct {
|
|
||||||
DNS int `json:"dns,omitempty"`
|
|
||||||
HTTP int `json:"http,omitempty"`
|
|
||||||
RPC int `json:"rpc,omitempty"`
|
|
||||||
SerfLan int `json:"serf_lan,omitempty"`
|
|
||||||
SerfWan int `json:"serf_wan,omitempty"`
|
|
||||||
Server int `json:"server,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type testAddressConfig struct {
|
|
||||||
HTTP string `json:"http,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type testServerConfig struct {
|
|
||||||
Bootstrap bool `json:"bootstrap,omitempty"`
|
|
||||||
Server bool `json:"server,omitempty"`
|
|
||||||
DataDir string `json:"data_dir,omitempty"`
|
|
||||||
LogLevel string `json:"log_level,omitempty"`
|
|
||||||
Addresses *testAddressConfig `json:"addresses,omitempty"`
|
|
||||||
Ports testPortConfig `json:"ports,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Callback functions for modifying config
|
|
||||||
type configCallback func(c *Config)
|
type configCallback func(c *Config)
|
||||||
type serverConfigCallback func(c *testServerConfig)
|
|
||||||
|
|
||||||
func defaultConfig() *testServerConfig {
|
func makeClient(t *testing.T) (*Client, *testutil.TestServer) {
|
||||||
return &testServerConfig{
|
return makeClientWithConfig(t, nil, nil)
|
||||||
Bootstrap: true,
|
|
||||||
Server: true,
|
|
||||||
LogLevel: "debug",
|
|
||||||
Ports: testPortConfig{
|
|
||||||
DNS: 19000,
|
|
||||||
HTTP: 18800,
|
|
||||||
RPC: 18600,
|
|
||||||
SerfLan: 18200,
|
|
||||||
SerfWan: 18400,
|
|
||||||
Server: 18000,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *testServer) stop() {
|
func makeClientWithConfig(
|
||||||
defer os.RemoveAll(s.dataDir)
|
t *testing.T,
|
||||||
defer os.RemoveAll(s.configFile)
|
cb1 configCallback,
|
||||||
|
cb2 testutil.ServerConfigCallback) (*Client, *testutil.TestServer) {
|
||||||
|
|
||||||
cmd := exec.Command("kill", "-9", fmt.Sprintf("%d", s.pid))
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTestServer(t *testing.T) *testServer {
|
|
||||||
return newTestServerWithConfig(t, func(c *testServerConfig) {})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTestServerWithConfig(t *testing.T, cb serverConfigCallback) *testServer {
|
|
||||||
if path, err := exec.LookPath("consul"); err != nil || path == "" {
|
|
||||||
t.Log("consul not found on $PATH, skipping")
|
|
||||||
t.SkipNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
pidFile, err := ioutil.TempFile("", "consul")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
pidFile.Close()
|
|
||||||
os.Remove(pidFile.Name())
|
|
||||||
|
|
||||||
dataDir, err := ioutil.TempDir("", "consul")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
configFile, err := ioutil.TempFile("", "consul")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
consulConfig := defaultConfig()
|
|
||||||
consulConfig.DataDir = dataDir
|
|
||||||
|
|
||||||
cb(consulConfig)
|
|
||||||
|
|
||||||
configContent, err := json.Marshal(consulConfig)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := configFile.Write(configContent); err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
configFile.Close()
|
|
||||||
|
|
||||||
// Start the server
|
|
||||||
cmd := exec.Command("consul", "agent", "-config-file", configFile.Name())
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
if err := cmd.Start(); err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &testServer{
|
|
||||||
pid: cmd.Process.Pid,
|
|
||||||
dataDir: dataDir,
|
|
||||||
configFile: configFile.Name(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeClient(t *testing.T) (*Client, *testServer) {
|
|
||||||
return makeClientWithConfig(t, func(c *Config) {
|
|
||||||
c.Address = "127.0.0.1:18800"
|
|
||||||
}, func(c *testServerConfig) {})
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeClientWithConfig(t *testing.T, cb1 configCallback, cb2 serverConfigCallback) (*Client, *testServer) {
|
|
||||||
// Make client config
|
// Make client config
|
||||||
conf := DefaultConfig()
|
conf := DefaultConfig()
|
||||||
cb1(conf)
|
if cb1 != nil {
|
||||||
|
cb1(conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create server
|
||||||
|
server := testutil.NewTestServerConfig(t, cb2)
|
||||||
|
conf.Address = server.HTTPAddr
|
||||||
|
|
||||||
// Create client
|
// Create client
|
||||||
client, err := NewClient(conf)
|
client, err := NewClient(conf)
|
||||||
|
@ -148,31 +41,6 @@ func makeClientWithConfig(t *testing.T, cb1 configCallback, cb2 serverConfigCall
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create server
|
|
||||||
server := newTestServerWithConfig(t, cb2)
|
|
||||||
|
|
||||||
// Allow the server some time to start, and verify we have a leader.
|
|
||||||
testutil.WaitForResult(func() (bool, error) {
|
|
||||||
req := client.newRequest("GET", "/v1/catalog/nodes")
|
|
||||||
_, resp, err := client.doRequest(req)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
resp.Body.Close()
|
|
||||||
|
|
||||||
// Ensure we have a leader and a node registeration
|
|
||||||
if leader := resp.Header.Get("X-Consul-KnownLeader"); leader != "true" {
|
|
||||||
return false, fmt.Errorf("Consul leader status: %#v", leader)
|
|
||||||
}
|
|
||||||
if resp.Header.Get("X-Consul-Index") == "0" {
|
|
||||||
return false, fmt.Errorf("Consul index is 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
}, func(err error) {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
})
|
|
||||||
|
|
||||||
return client, server
|
return client, server
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +105,7 @@ func TestDefaultConfig_env(t *testing.T) {
|
||||||
|
|
||||||
func TestSetQueryOptions(t *testing.T) {
|
func TestSetQueryOptions(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
r := c.newRequest("GET", "/v1/kv/foo")
|
r := c.newRequest("GET", "/v1/kv/foo")
|
||||||
q := &QueryOptions{
|
q := &QueryOptions{
|
||||||
|
@ -272,7 +140,7 @@ func TestSetQueryOptions(t *testing.T) {
|
||||||
|
|
||||||
func TestSetWriteOptions(t *testing.T) {
|
func TestSetWriteOptions(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
r := c.newRequest("GET", "/v1/kv/foo")
|
r := c.newRequest("GET", "/v1/kv/foo")
|
||||||
q := &WriteOptions{
|
q := &WriteOptions{
|
||||||
|
@ -291,7 +159,7 @@ func TestSetWriteOptions(t *testing.T) {
|
||||||
|
|
||||||
func TestRequestToHTTP(t *testing.T) {
|
func TestRequestToHTTP(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
r := c.newRequest("DELETE", "/v1/kv/foo")
|
r := c.newRequest("DELETE", "/v1/kv/foo")
|
||||||
q := &QueryOptions{
|
q := &QueryOptions{
|
||||||
|
@ -349,12 +217,12 @@ func TestAPI_UnixSocket(t *testing.T) {
|
||||||
|
|
||||||
c, s := makeClientWithConfig(t, func(c *Config) {
|
c, s := makeClientWithConfig(t, func(c *Config) {
|
||||||
c.Address = "unix://" + socket
|
c.Address = "unix://" + socket
|
||||||
}, func(c *testServerConfig) {
|
}, func(c *testutil.TestServerConfig) {
|
||||||
c.Addresses = &testAddressConfig{
|
c.Addresses = &testutil.TestAddressConfig{
|
||||||
HTTP: "unix://" + socket,
|
HTTP: "unix://" + socket,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
agent := c.Agent()
|
agent := c.Agent()
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
func TestCatalog_Datacenters(t *testing.T) {
|
func TestCatalog_Datacenters(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
catalog := c.Catalog()
|
catalog := c.Catalog()
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ func TestCatalog_Datacenters(t *testing.T) {
|
||||||
|
|
||||||
func TestCatalog_Nodes(t *testing.T) {
|
func TestCatalog_Nodes(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
catalog := c.Catalog()
|
catalog := c.Catalog()
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ func TestCatalog_Nodes(t *testing.T) {
|
||||||
|
|
||||||
func TestCatalog_Services(t *testing.T) {
|
func TestCatalog_Services(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
catalog := c.Catalog()
|
catalog := c.Catalog()
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ func TestCatalog_Services(t *testing.T) {
|
||||||
|
|
||||||
func TestCatalog_Service(t *testing.T) {
|
func TestCatalog_Service(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
catalog := c.Catalog()
|
catalog := c.Catalog()
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ func TestCatalog_Service(t *testing.T) {
|
||||||
|
|
||||||
func TestCatalog_Node(t *testing.T) {
|
func TestCatalog_Node(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
catalog := c.Catalog()
|
catalog := c.Catalog()
|
||||||
name, _ := c.Agent().NodeName()
|
name, _ := c.Agent().NodeName()
|
||||||
|
@ -135,7 +135,7 @@ func TestCatalog_Node(t *testing.T) {
|
||||||
|
|
||||||
func TestCatalog_Registration(t *testing.T) {
|
func TestCatalog_Registration(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
catalog := c.Catalog()
|
catalog := c.Catalog()
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
|
|
||||||
func TestEvent_FireList(t *testing.T) {
|
func TestEvent_FireList(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
event := c.Event()
|
event := c.Event()
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
func TestHealth_Node(t *testing.T) {
|
func TestHealth_Node(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
agent := c.Agent()
|
agent := c.Agent()
|
||||||
health := c.Health()
|
health := c.Health()
|
||||||
|
@ -39,7 +39,7 @@ func TestHealth_Node(t *testing.T) {
|
||||||
|
|
||||||
func TestHealth_Checks(t *testing.T) {
|
func TestHealth_Checks(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
agent := c.Agent()
|
agent := c.Agent()
|
||||||
health := c.Health()
|
health := c.Health()
|
||||||
|
@ -75,7 +75,7 @@ func TestHealth_Checks(t *testing.T) {
|
||||||
|
|
||||||
func TestHealth_Service(t *testing.T) {
|
func TestHealth_Service(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
health := c.Health()
|
health := c.Health()
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ func TestHealth_Service(t *testing.T) {
|
||||||
|
|
||||||
func TestHealth_State(t *testing.T) {
|
func TestHealth_State(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
health := c.Health()
|
health := c.Health()
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
func TestClientPutGetDelete(t *testing.T) {
|
func TestClientPutGetDelete(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
kv := c.KV()
|
kv := c.KV()
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ func TestClientPutGetDelete(t *testing.T) {
|
||||||
|
|
||||||
func TestClient_List_DeleteRecurse(t *testing.T) {
|
func TestClient_List_DeleteRecurse(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
kv := c.KV()
|
kv := c.KV()
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ func TestClient_List_DeleteRecurse(t *testing.T) {
|
||||||
|
|
||||||
func TestClient_DeleteCAS(t *testing.T) {
|
func TestClient_DeleteCAS(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
kv := c.KV()
|
kv := c.KV()
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ func TestClient_DeleteCAS(t *testing.T) {
|
||||||
|
|
||||||
func TestClient_CAS(t *testing.T) {
|
func TestClient_CAS(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
kv := c.KV()
|
kv := c.KV()
|
||||||
|
|
||||||
|
@ -211,7 +211,7 @@ func TestClient_CAS(t *testing.T) {
|
||||||
|
|
||||||
func TestClient_WatchGet(t *testing.T) {
|
func TestClient_WatchGet(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
kv := c.KV()
|
kv := c.KV()
|
||||||
|
|
||||||
|
@ -262,7 +262,7 @@ func TestClient_WatchGet(t *testing.T) {
|
||||||
|
|
||||||
func TestClient_WatchList(t *testing.T) {
|
func TestClient_WatchList(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
kv := c.KV()
|
kv := c.KV()
|
||||||
|
|
||||||
|
@ -315,7 +315,7 @@ func TestClient_WatchList(t *testing.T) {
|
||||||
|
|
||||||
func TestClient_Keys_DeleteRecurse(t *testing.T) {
|
func TestClient_Keys_DeleteRecurse(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
kv := c.KV()
|
kv := c.KV()
|
||||||
|
|
||||||
|
@ -364,7 +364,7 @@ func TestClient_Keys_DeleteRecurse(t *testing.T) {
|
||||||
|
|
||||||
func TestClient_AcquireRelease(t *testing.T) {
|
func TestClient_AcquireRelease(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
session := c.Session()
|
session := c.Session()
|
||||||
kv := c.KV()
|
kv := c.KV()
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
func TestLock_LockUnlock(t *testing.T) {
|
func TestLock_LockUnlock(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
lock, err := c.LockKey("test/lock")
|
lock, err := c.LockKey("test/lock")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -66,7 +66,7 @@ func TestLock_LockUnlock(t *testing.T) {
|
||||||
|
|
||||||
func TestLock_ForceInvalidate(t *testing.T) {
|
func TestLock_ForceInvalidate(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
lock, err := c.LockKey("test/lock")
|
lock, err := c.LockKey("test/lock")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -100,7 +100,7 @@ func TestLock_ForceInvalidate(t *testing.T) {
|
||||||
|
|
||||||
func TestLock_DeleteKey(t *testing.T) {
|
func TestLock_DeleteKey(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
lock, err := c.LockKey("test/lock")
|
lock, err := c.LockKey("test/lock")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -133,7 +133,7 @@ func TestLock_DeleteKey(t *testing.T) {
|
||||||
|
|
||||||
func TestLock_Contend(t *testing.T) {
|
func TestLock_Contend(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
acquired := make([]bool, 3)
|
acquired := make([]bool, 3)
|
||||||
|
@ -185,7 +185,7 @@ func TestLock_Contend(t *testing.T) {
|
||||||
|
|
||||||
func TestLock_Destroy(t *testing.T) {
|
func TestLock_Destroy(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
lock, err := c.LockKey("test/lock")
|
lock, err := c.LockKey("test/lock")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -253,7 +253,7 @@ func TestLock_Destroy(t *testing.T) {
|
||||||
|
|
||||||
func TestLock_Conflict(t *testing.T) {
|
func TestLock_Conflict(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
sema, err := c.SemaphorePrefix("test/lock/", 2)
|
sema, err := c.SemaphorePrefix("test/lock/", 2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
func TestSemaphore_AcquireRelease(t *testing.T) {
|
func TestSemaphore_AcquireRelease(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
sema, err := c.SemaphorePrefix("test/semaphore", 2)
|
sema, err := c.SemaphorePrefix("test/semaphore", 2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -66,7 +66,7 @@ func TestSemaphore_AcquireRelease(t *testing.T) {
|
||||||
|
|
||||||
func TestSemaphore_ForceInvalidate(t *testing.T) {
|
func TestSemaphore_ForceInvalidate(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
sema, err := c.SemaphorePrefix("test/semaphore", 2)
|
sema, err := c.SemaphorePrefix("test/semaphore", 2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -100,7 +100,7 @@ func TestSemaphore_ForceInvalidate(t *testing.T) {
|
||||||
|
|
||||||
func TestSemaphore_DeleteKey(t *testing.T) {
|
func TestSemaphore_DeleteKey(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
sema, err := c.SemaphorePrefix("test/semaphore", 2)
|
sema, err := c.SemaphorePrefix("test/semaphore", 2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -133,7 +133,7 @@ func TestSemaphore_DeleteKey(t *testing.T) {
|
||||||
|
|
||||||
func TestSemaphore_Contend(t *testing.T) {
|
func TestSemaphore_Contend(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
acquired := make([]bool, 4)
|
acquired := make([]bool, 4)
|
||||||
|
@ -185,7 +185,7 @@ func TestSemaphore_Contend(t *testing.T) {
|
||||||
|
|
||||||
func TestSemaphore_BadLimit(t *testing.T) {
|
func TestSemaphore_BadLimit(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
sema, err := c.SemaphorePrefix("test/semaphore", 0)
|
sema, err := c.SemaphorePrefix("test/semaphore", 0)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -215,7 +215,7 @@ func TestSemaphore_BadLimit(t *testing.T) {
|
||||||
|
|
||||||
func TestSemaphore_Destroy(t *testing.T) {
|
func TestSemaphore_Destroy(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
sema, err := c.SemaphorePrefix("test/semaphore", 2)
|
sema, err := c.SemaphorePrefix("test/semaphore", 2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -270,7 +270,7 @@ func TestSemaphore_Destroy(t *testing.T) {
|
||||||
|
|
||||||
func TestSemaphore_Conflict(t *testing.T) {
|
func TestSemaphore_Conflict(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
lock, err := c.LockKey("test/sema/.lock")
|
lock, err := c.LockKey("test/sema/.lock")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
|
|
||||||
func TestSession_CreateDestroy(t *testing.T) {
|
func TestSession_CreateDestroy(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
session := c.Session()
|
session := c.Session()
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ func TestSession_CreateDestroy(t *testing.T) {
|
||||||
|
|
||||||
func TestSession_CreateRenewDestroy(t *testing.T) {
|
func TestSession_CreateRenewDestroy(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
session := c.Session()
|
session := c.Session()
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ func TestSession_CreateRenewDestroy(t *testing.T) {
|
||||||
|
|
||||||
func TestSession_Info(t *testing.T) {
|
func TestSession_Info(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
session := c.Session()
|
session := c.Session()
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ func TestSession_Info(t *testing.T) {
|
||||||
|
|
||||||
func TestSession_Node(t *testing.T) {
|
func TestSession_Node(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
session := c.Session()
|
session := c.Session()
|
||||||
|
|
||||||
|
@ -172,7 +172,7 @@ func TestSession_Node(t *testing.T) {
|
||||||
|
|
||||||
func TestSession_List(t *testing.T) {
|
func TestSession_List(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
session := c.Session()
|
session := c.Session()
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
|
|
||||||
func TestStatusLeader(t *testing.T) {
|
func TestStatusLeader(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
status := c.Status()
|
status := c.Status()
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ func TestStatusLeader(t *testing.T) {
|
||||||
|
|
||||||
func TestStatusPeers(t *testing.T) {
|
func TestStatusPeers(t *testing.T) {
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
defer s.stop()
|
defer s.Stop()
|
||||||
|
|
||||||
status := c.Status()
|
status := c.Status()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
Consul Testing Utilities
|
||||||
|
========================
|
||||||
|
|
||||||
|
This package provides some generic helpers to facilitate testing in Consul.
|
||||||
|
|
||||||
|
TestServer
|
||||||
|
==========
|
||||||
|
|
||||||
|
TestServer is a harness for managing Consul agents and initializing them with
|
||||||
|
test data. Using it, you can form test clusters, create services, add health
|
||||||
|
checks, manipulate the K/V store, etc. This test harness is completely decoupled
|
||||||
|
from Consul's core and API client, meaning it can be easily imported and used in
|
||||||
|
external unit tests for various applications. It works by invoking the Consul
|
||||||
|
CLI, which means it is a requirement to have Consul installed in the `$PATH`.
|
||||||
|
|
||||||
|
Following is some example usage:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/consul/testutil"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(t *testing.T) {
|
||||||
|
// Create a server
|
||||||
|
srv1 := testutil.NewTestServer(t)
|
||||||
|
defer srv1.Stop()
|
||||||
|
|
||||||
|
// Create a secondary server
|
||||||
|
srv2 := testutil.NewTestServer(t)
|
||||||
|
defer srv2.Stop()
|
||||||
|
|
||||||
|
// Join the servers together
|
||||||
|
srv1.JoinLAN(srv2.LANAddr)
|
||||||
|
|
||||||
|
// Create a test key/value pair
|
||||||
|
srv1.SetKV("foo", []byte("bar"))
|
||||||
|
|
||||||
|
// Create lots of test key/value pairs
|
||||||
|
srv1.PopulateKV(map[string][]byte{
|
||||||
|
"bar": []byte("123"),
|
||||||
|
"baz": []byte("456"),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create a service
|
||||||
|
srv1.AddService("redis", "passing", []string{"master"})
|
||||||
|
|
||||||
|
// Create a service check
|
||||||
|
srv1.AddCheck("service:redis", "redis", "passing")
|
||||||
|
|
||||||
|
// Create a node check
|
||||||
|
srv1.AddCheck("mem", "", "critical")
|
||||||
|
|
||||||
|
// The HTTPAddr field contains the address of the Consul
|
||||||
|
// API on the new test server instance.
|
||||||
|
println(srv1.HTTPAddr)
|
||||||
|
}
|
||||||
|
```
|
|
@ -0,0 +1,434 @@
|
||||||
|
package testutil
|
||||||
|
|
||||||
|
// TestServer is a test helper. It uses a fork/exec model to create
|
||||||
|
// a test Consul server instance in the background and initialize it
|
||||||
|
// with some data and/or services. The test server can then be used
|
||||||
|
// to run a unit test, and offers an easy API to tear itself down
|
||||||
|
// when the test has completed. The only prerequisite is to have a consul
|
||||||
|
// binary available on the $PATH.
|
||||||
|
//
|
||||||
|
// This package does not use Consul's official API client. This is
|
||||||
|
// because we use TestServer to test the API client, which would
|
||||||
|
// otherwise cause an import cycle.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// offset is used to atomically increment the port numbers.
|
||||||
|
var offset uint64
|
||||||
|
|
||||||
|
// TestPortConfig configures the various ports used for services
|
||||||
|
// provided by the Consul server.
|
||||||
|
type TestPortConfig struct {
|
||||||
|
DNS int `json:"dns,omitempty"`
|
||||||
|
HTTP int `json:"http,omitempty"`
|
||||||
|
RPC int `json:"rpc,omitempty"`
|
||||||
|
SerfLan int `json:"serf_lan,omitempty"`
|
||||||
|
SerfWan int `json:"serf_wan,omitempty"`
|
||||||
|
Server int `json:"server,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAddressConfig contains the bind addresses for various
|
||||||
|
// components of the Consul server.
|
||||||
|
type TestAddressConfig struct {
|
||||||
|
HTTP string `json:"http,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestServerConfig is the main server configuration struct.
|
||||||
|
type TestServerConfig struct {
|
||||||
|
NodeName string `json:"node_name"`
|
||||||
|
Bootstrap bool `json:"bootstrap,omitempty"`
|
||||||
|
Server bool `json:"server,omitempty"`
|
||||||
|
DataDir string `json:"data_dir,omitempty"`
|
||||||
|
Datacenter string `json:"datacenter,omitempty"`
|
||||||
|
DisableCheckpoint bool `json:"disable_update_check"`
|
||||||
|
LogLevel string `json:"log_level,omitempty"`
|
||||||
|
Bind string `json:"bind_addr,omitempty"`
|
||||||
|
Addresses *TestAddressConfig `json:"addresses,omitempty"`
|
||||||
|
Ports *TestPortConfig `json:"ports,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerConfigCallback is a function interface which can be
|
||||||
|
// passed to NewTestServerConfig to modify the server config.
|
||||||
|
type ServerConfigCallback func(c *TestServerConfig)
|
||||||
|
|
||||||
|
// defaultServerConfig returns a new TestServerConfig struct
|
||||||
|
// with all of the listen ports incremented by one.
|
||||||
|
func defaultServerConfig() *TestServerConfig {
|
||||||
|
idx := int(atomic.AddUint64(&offset, 1))
|
||||||
|
|
||||||
|
return &TestServerConfig{
|
||||||
|
NodeName: fmt.Sprintf("node%d", idx),
|
||||||
|
DisableCheckpoint: true,
|
||||||
|
Bootstrap: true,
|
||||||
|
Server: true,
|
||||||
|
LogLevel: "debug",
|
||||||
|
Bind: "127.0.0.1",
|
||||||
|
Addresses: &TestAddressConfig{},
|
||||||
|
Ports: &TestPortConfig{
|
||||||
|
DNS: 20000 + idx,
|
||||||
|
HTTP: 21000 + idx,
|
||||||
|
RPC: 22000 + idx,
|
||||||
|
SerfLan: 23000 + idx,
|
||||||
|
SerfWan: 24000 + idx,
|
||||||
|
Server: 25000 + idx,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestService is used to serialize a service definition.
|
||||||
|
type TestService struct {
|
||||||
|
ID string `json:",omitempty"`
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
Tags []string `json:",omitempty"`
|
||||||
|
Address string `json:",omitempty"`
|
||||||
|
Port int `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCheck is used to serialize a check definition.
|
||||||
|
type TestCheck struct {
|
||||||
|
ID string `json:",omitempty"`
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
ServiceID string `json:",omitempty"`
|
||||||
|
TTL string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestKVResponse is what we use to decode KV data.
|
||||||
|
type TestKVResponse struct {
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestServer is the main server wrapper struct.
|
||||||
|
type TestServer struct {
|
||||||
|
PID int
|
||||||
|
Config *TestServerConfig
|
||||||
|
t *testing.T
|
||||||
|
|
||||||
|
HTTPAddr string
|
||||||
|
LANAddr string
|
||||||
|
WANAddr string
|
||||||
|
|
||||||
|
HttpClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTestServer is an easy helper method to create a new Consul
|
||||||
|
// test server with the most basic configuration.
|
||||||
|
func NewTestServer(t *testing.T) *TestServer {
|
||||||
|
return NewTestServerConfig(t, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTestServerConfig creates a new TestServer, and makes a call to
|
||||||
|
// an optional callback function to modify the configuration.
|
||||||
|
func NewTestServerConfig(t *testing.T, cb ServerConfigCallback) *TestServer {
|
||||||
|
if path, err := exec.LookPath("consul"); err != nil || path == "" {
|
||||||
|
t.Skip("consul not found on $PATH, skipping")
|
||||||
|
}
|
||||||
|
|
||||||
|
dataDir, err := ioutil.TempDir("", "consul")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
configFile, err := ioutil.TempFile("", "consul")
|
||||||
|
if err != nil {
|
||||||
|
defer os.RemoveAll(dataDir)
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(configFile.Name())
|
||||||
|
|
||||||
|
consulConfig := defaultServerConfig()
|
||||||
|
consulConfig.DataDir = dataDir
|
||||||
|
|
||||||
|
if cb != nil {
|
||||||
|
cb(consulConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
configContent, err := json.Marshal(consulConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := configFile.Write(configContent); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
configFile.Close()
|
||||||
|
|
||||||
|
// Start the server
|
||||||
|
cmd := exec.Command("consul", "agent", "-config-file", configFile.Name())
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpAddr string
|
||||||
|
var client *http.Client
|
||||||
|
if strings.HasPrefix(consulConfig.Addresses.HTTP, "unix://") {
|
||||||
|
httpAddr = consulConfig.Addresses.HTTP
|
||||||
|
client = &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
Dial: func(_, _ string) (net.Conn, error) {
|
||||||
|
return net.Dial("unix", httpAddr[7:])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
httpAddr = fmt.Sprintf("127.0.0.1:%d", consulConfig.Ports.HTTP)
|
||||||
|
client = http.DefaultClient
|
||||||
|
}
|
||||||
|
|
||||||
|
server := &TestServer{
|
||||||
|
Config: consulConfig,
|
||||||
|
PID: cmd.Process.Pid,
|
||||||
|
t: t,
|
||||||
|
|
||||||
|
HTTPAddr: httpAddr,
|
||||||
|
LANAddr: fmt.Sprintf("127.0.0.1:%d", consulConfig.Ports.SerfLan),
|
||||||
|
WANAddr: fmt.Sprintf("127.0.0.1:%d", consulConfig.Ports.SerfWan),
|
||||||
|
|
||||||
|
HttpClient: client,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the server to be ready
|
||||||
|
server.waitForLeader()
|
||||||
|
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop stops the test Consul server, and removes the Consul data
|
||||||
|
// directory once we are done.
|
||||||
|
func (s *TestServer) Stop() {
|
||||||
|
defer os.RemoveAll(s.Config.DataDir)
|
||||||
|
|
||||||
|
cmd := exec.Command("kill", "-9", fmt.Sprintf("%d", s.PID))
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
s.t.Errorf("err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// waitForLeader waits for the Consul server's HTTP API to become
|
||||||
|
// available, and then waits for a known leader and an index of
|
||||||
|
// 1 or more to be observed to confirm leader election is done.
|
||||||
|
func (s *TestServer) waitForLeader() {
|
||||||
|
WaitForResult(func() (bool, error) {
|
||||||
|
// Query the API and check the status code
|
||||||
|
resp, err := s.HttpClient.Get(s.url("/v1/catalog/nodes"))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if err := s.requireOK(resp); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we have a leader and a node registeration
|
||||||
|
if leader := resp.Header.Get("X-Consul-KnownLeader"); leader != "true" {
|
||||||
|
fmt.Println(leader)
|
||||||
|
return false, fmt.Errorf("Consul leader status: %#v", leader)
|
||||||
|
}
|
||||||
|
if resp.Header.Get("X-Consul-Index") == "0" {
|
||||||
|
return false, fmt.Errorf("Consul index is 0")
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}, func(err error) {
|
||||||
|
defer s.Stop()
|
||||||
|
s.t.Fatalf("err: %s", err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// url is a helper function which takes a relative URL and
|
||||||
|
// makes it into a proper URL against the local Consul server.
|
||||||
|
func (s *TestServer) url(path string) string {
|
||||||
|
return fmt.Sprintf("http://127.0.0.1:%d%s", s.Config.Ports.HTTP, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// requireOK checks the HTTP response code and ensures it is acceptable.
|
||||||
|
func (s *TestServer) requireOK(resp *http.Response) error {
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return fmt.Errorf("Bad status code: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// put performs a new HTTP PUT request.
|
||||||
|
func (s *TestServer) put(path string, body io.Reader) *http.Response {
|
||||||
|
req, err := http.NewRequest("PUT", s.url(path), body)
|
||||||
|
if err != nil {
|
||||||
|
s.t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
resp, err := s.HttpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
s.t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
if err := s.requireOK(resp); err != nil {
|
||||||
|
defer resp.Body.Close()
|
||||||
|
s.t.Fatal(err)
|
||||||
|
}
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
// get performs a new HTTP GET request.
|
||||||
|
func (s *TestServer) get(path string) *http.Response {
|
||||||
|
resp, err := s.HttpClient.Get(s.url(path))
|
||||||
|
if err != nil {
|
||||||
|
s.t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
if err := s.requireOK(resp); err != nil {
|
||||||
|
defer resp.Body.Close()
|
||||||
|
s.t.Fatal(err)
|
||||||
|
}
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodePayload returns a new io.Reader wrapping the encoded contents
|
||||||
|
// of the payload, suitable for passing directly to a new request.
|
||||||
|
func (s *TestServer) encodePayload(payload interface{}) io.Reader {
|
||||||
|
var encoded bytes.Buffer
|
||||||
|
enc := json.NewEncoder(&encoded)
|
||||||
|
if err := enc.Encode(payload); err != nil {
|
||||||
|
s.t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
return &encoded
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinLAN is used to join nodes within the same datacenter.
|
||||||
|
func (s *TestServer) JoinLAN(addr string) {
|
||||||
|
resp := s.get("/v1/agent/join/" + addr)
|
||||||
|
resp.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinWAN is used to join remote datacenters together.
|
||||||
|
func (s *TestServer) JoinWAN(addr string) {
|
||||||
|
resp := s.get("/v1/agent/join/" + addr + "?wan=1")
|
||||||
|
resp.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetKV sets an individual key in the K/V store.
|
||||||
|
func (s *TestServer) SetKV(key string, val []byte) {
|
||||||
|
resp := s.put("/v1/kv/"+key, bytes.NewBuffer(val))
|
||||||
|
resp.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKV retrieves a single key and returns its value
|
||||||
|
func (s *TestServer) GetKV(key string) []byte {
|
||||||
|
resp := s.get("/v1/kv/" + key)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
raw, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
s.t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []*TestKVResponse
|
||||||
|
if err := json.Unmarshal(raw, &result); err != nil {
|
||||||
|
s.t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
if len(result) < 1 {
|
||||||
|
s.t.Fatalf("key does not exist: %s", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := base64.StdEncoding.DecodeString(result[0].Value)
|
||||||
|
if err != nil {
|
||||||
|
s.t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PopulateKV fills the Consul KV with data from a generic map.
|
||||||
|
func (s *TestServer) PopulateKV(data map[string][]byte) {
|
||||||
|
for k, v := range data {
|
||||||
|
s.SetKV(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListKV returns a list of keys present in the KV store. This will list all
|
||||||
|
// keys under the given prefix recursively and return them as a slice.
|
||||||
|
func (s *TestServer) ListKV(prefix string) []string {
|
||||||
|
resp := s.get("/v1/kv/" + prefix + "?keys")
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
raw, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
s.t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []string
|
||||||
|
if err := json.Unmarshal(raw, &result); err != nil {
|
||||||
|
s.t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddService adds a new service to the Consul instance. It also
|
||||||
|
// automatically adds a health check with the given status, which
|
||||||
|
// can be one of "passing", "warning", or "critical".
|
||||||
|
func (s *TestServer) AddService(name, status string, tags []string) {
|
||||||
|
svc := &TestService{
|
||||||
|
Name: name,
|
||||||
|
Tags: tags,
|
||||||
|
}
|
||||||
|
payload := s.encodePayload(svc)
|
||||||
|
s.put("/v1/agent/service/register", payload)
|
||||||
|
|
||||||
|
chkName := "service:" + name
|
||||||
|
chk := &TestCheck{
|
||||||
|
Name: chkName,
|
||||||
|
ServiceID: name,
|
||||||
|
TTL: "10m",
|
||||||
|
}
|
||||||
|
payload = s.encodePayload(chk)
|
||||||
|
s.put("/v1/agent/check/register", payload)
|
||||||
|
|
||||||
|
switch status {
|
||||||
|
case "passing":
|
||||||
|
s.put("/v1/agent/check/pass/"+chkName, nil)
|
||||||
|
case "warning":
|
||||||
|
s.put("/v1/agent/check/warn/"+chkName, nil)
|
||||||
|
case "critical":
|
||||||
|
s.put("/v1/agent/check/fail/"+chkName, nil)
|
||||||
|
default:
|
||||||
|
s.t.Fatalf("Unrecognized status: %s", status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCheck adds a check to the Consul instance. If the serviceID is
|
||||||
|
// left empty (""), then the check will be associated with the node.
|
||||||
|
// The check status may be "passing", "warning", or "critical".
|
||||||
|
func (s *TestServer) AddCheck(name, serviceID, status string) {
|
||||||
|
chk := &TestCheck{
|
||||||
|
ID: name,
|
||||||
|
Name: name,
|
||||||
|
TTL: "10m",
|
||||||
|
}
|
||||||
|
if serviceID != "" {
|
||||||
|
chk.ServiceID = serviceID
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := s.encodePayload(chk)
|
||||||
|
s.put("/v1/agent/check/register", payload)
|
||||||
|
|
||||||
|
switch status {
|
||||||
|
case "passing":
|
||||||
|
s.put("/v1/agent/check/pass/"+name, nil)
|
||||||
|
case "warning":
|
||||||
|
s.put("/v1/agent/check/warn/"+name, nil)
|
||||||
|
case "critical":
|
||||||
|
s.put("/v1/agent/check/fail/"+name, nil)
|
||||||
|
default:
|
||||||
|
s.t.Fatalf("Unrecognized status: %s", status)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue