diff --git a/command/agent/agent_endpoint_test.go b/command/agent/agent_endpoint_test.go index b72024c71..af2eade98 100644 --- a/command/agent/agent_endpoint_test.go +++ b/command/agent/agent_endpoint_test.go @@ -13,7 +13,8 @@ import ( ) func TestHTTP_AgentSelf(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Make the HTTP request req, err := http.NewRequest("GET", "/v1/agent/self", nil) if err != nil { @@ -39,7 +40,9 @@ func TestHTTP_AgentSelf(t *testing.T) { } func TestHTTP_AgentJoin(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + // TODO(alexdadgar) + // t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Determine the join address member := s.Agent.Server().LocalMember() addr := fmt.Sprintf("%s:%d", member.Addr, member.Port) @@ -70,7 +73,8 @@ func TestHTTP_AgentJoin(t *testing.T) { } func TestHTTP_AgentMembers(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Make the HTTP request req, err := http.NewRequest("GET", "/v1/agent/members", nil) if err != nil { @@ -93,7 +97,8 @@ func TestHTTP_AgentMembers(t *testing.T) { } func TestHTTP_AgentForceLeave(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Make the HTTP request req, err := http.NewRequest("PUT", "/v1/agent/force-leave?node=foo", nil) if err != nil { @@ -110,7 +115,8 @@ func TestHTTP_AgentForceLeave(t *testing.T) { } func TestHTTP_AgentSetServers(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Establish a baseline number of servers req, err := http.NewRequest("GET", "/v1/agent/servers", nil) if err != nil { @@ -183,11 +189,13 @@ func TestHTTP_AgentSetServers(t *testing.T) { } func TestHTTP_AgentListKeys(t *testing.T) { + t.Parallel() + key1 := "HS5lJ+XuTlYKWaeGYyG+/A==" httpTest(t, func(c *Config) { c.Server.EncryptKey = key1 - }, func(s *TestServer) { + }, func(s *TestAgent) { req, err := http.NewRequest("GET", "/v1/agent/keyring/list", nil) if err != nil { t.Fatalf("err: %s", err) @@ -206,12 +214,15 @@ func TestHTTP_AgentListKeys(t *testing.T) { } func TestHTTP_AgentInstallKey(t *testing.T) { + // TODO(alexdadgar) + // t.Parallel() + key1 := "HS5lJ+XuTlYKWaeGYyG+/A==" key2 := "wH1Bn9hlJ0emgWB1JttVRA==" httpTest(t, func(c *Config) { c.Server.EncryptKey = key1 - }, func(s *TestServer) { + }, func(s *TestAgent) { b, err := json.Marshal(&structs.KeyringRequest{Key: key2}) if err != nil { t.Fatalf("err: %v", err) @@ -244,12 +255,15 @@ func TestHTTP_AgentInstallKey(t *testing.T) { } func TestHTTP_AgentRemoveKey(t *testing.T) { + // TODO(alexdadgar) + // t.Parallel() + key1 := "HS5lJ+XuTlYKWaeGYyG+/A==" key2 := "wH1Bn9hlJ0emgWB1JttVRA==" httpTest(t, func(c *Config) { c.Server.EncryptKey = key1 - }, func(s *TestServer) { + }, func(s *TestAgent) { b, err := json.Marshal(&structs.KeyringRequest{Key: key2}) if err != nil { t.Fatalf("err: %v", err) diff --git a/command/agent/agent_test.go b/command/agent/agent_test.go index 75ebf286e..524bdef23 100644 --- a/command/agent/agent_test.go +++ b/command/agent/agent_test.go @@ -12,7 +12,6 @@ import ( "time" "github.com/hashicorp/nomad/helper" - "github.com/hashicorp/nomad/nomad" sconfig "github.com/hashicorp/nomad/nomad/structs/config" ) @@ -38,61 +37,9 @@ func tmpDir(t testing.TB) string { return dir } -func makeAgent(t testing.TB, cb func(*Config)) (string, *Agent) { - dir := tmpDir(t) - conf := DevConfig() - - // Customize the server configuration - config := nomad.DefaultConfig() - conf.NomadConfig = config - - // Set the data_dir - conf.DataDir = dir - conf.NomadConfig.DataDir = dir - - // Bind and set ports - conf.BindAddr = "127.0.0.1" - conf.Ports = &Ports{ - HTTP: getPort(), - RPC: getPort(), - Serf: getPort(), - } - conf.NodeName = fmt.Sprintf("Node %d", conf.Ports.RPC) - conf.Consul = sconfig.DefaultConsulConfig() - conf.Vault.Enabled = new(bool) - - // Tighten the Serf timing - config.SerfConfig.MemberlistConfig.SuspicionMult = 2 - config.SerfConfig.MemberlistConfig.RetransmitMult = 2 - config.SerfConfig.MemberlistConfig.ProbeTimeout = 50 * time.Millisecond - config.SerfConfig.MemberlistConfig.ProbeInterval = 100 * time.Millisecond - config.SerfConfig.MemberlistConfig.GossipInterval = 100 * time.Millisecond - - // Tighten the Raft timing - config.RaftConfig.LeaderLeaseTimeout = 20 * time.Millisecond - config.RaftConfig.HeartbeatTimeout = 40 * time.Millisecond - config.RaftConfig.ElectionTimeout = 40 * time.Millisecond - config.RaftConfig.StartAsLeader = true - config.RaftTimeout = 500 * time.Millisecond - - if cb != nil { - cb(conf) - } - - if err := conf.normalizeAddrs(); err != nil { - t.Fatalf("error normalizing config: %v", err) - } - agent, err := NewAgent(conf, os.Stderr) - if err != nil { - os.RemoveAll(dir) - t.Fatalf("err: %v", err) - } - return dir, agent -} - func TestAgent_RPCPing(t *testing.T) { - dir, agent := makeAgent(t, nil) - defer os.RemoveAll(dir) + t.Parallel() + agent := NewTestAgent(t.Name(), nil) defer agent.Shutdown() var out struct{} @@ -102,6 +49,7 @@ func TestAgent_RPCPing(t *testing.T) { } func TestAgent_ServerConfig(t *testing.T) { + t.Parallel() conf := DefaultConfig() conf.DevMode = true // allow localhost for advertise addrs a := &Agent{config: conf} @@ -320,6 +268,7 @@ func TestAgent_ServerConfig(t *testing.T) { } func TestAgent_ClientConfig(t *testing.T) { + t.Parallel() conf := DefaultConfig() conf.Client.Enabled = true @@ -365,6 +314,7 @@ func TestAgent_ClientConfig(t *testing.T) { // TestAgent_HTTPCheck asserts Agent.agentHTTPCheck properly alters the HTTP // API health check depending on configuration. func TestAgent_HTTPCheck(t *testing.T) { + t.Parallel() logger := log.New(ioutil.Discard, "", 0) if testing.Verbose() { logger = log.New(os.Stdout, "[TestAgent_HTTPCheck] ", log.Lshortfile) @@ -455,6 +405,7 @@ func TestAgent_HTTPCheck(t *testing.T) { } func TestAgent_ConsulSupportsTLSSkipVerify(t *testing.T) { + t.Parallel() assertSupport := func(expected bool, blob string) { self := map[string]map[string]interface{}{} if err := json.Unmarshal([]byte("{"+blob+"}"), &self); err != nil { @@ -561,6 +512,7 @@ func TestAgent_ConsulSupportsTLSSkipVerify(t *testing.T) { // TestAgent_HTTPCheckPath asserts clients and servers use different endpoints // for healthchecks. func TestAgent_HTTPCheckPath(t *testing.T) { + t.Parallel() // Agent.agentHTTPCheck only needs a config and logger a := &Agent{ config: DevConfig(), diff --git a/command/agent/alloc_endpoint_test.go b/command/agent/alloc_endpoint_test.go index 9012945ea..652eab5a7 100644 --- a/command/agent/alloc_endpoint_test.go +++ b/command/agent/alloc_endpoint_test.go @@ -13,7 +13,8 @@ import ( ) func TestHTTP_AllocsList(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Directly manipulate the state state := s.Agent.server.State() alloc1 := mock.Alloc() @@ -59,7 +60,8 @@ func TestHTTP_AllocsList(t *testing.T) { } func TestHTTP_AllocsPrefixList(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Directly manipulate the state state := s.Agent.server.State() @@ -118,7 +120,8 @@ func TestHTTP_AllocsPrefixList(t *testing.T) { } func TestHTTP_AllocQuery(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Directly manipulate the state state := s.Agent.server.State() alloc := mock.Alloc() @@ -164,7 +167,8 @@ func TestHTTP_AllocQuery(t *testing.T) { } func TestHTTP_AllocQuery_Payload(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Directly manipulate the state state := s.Agent.server.State() alloc := mock.Alloc() @@ -220,7 +224,8 @@ func TestHTTP_AllocQuery_Payload(t *testing.T) { } func TestHTTP_AllocStats(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Make the HTTP request req, err := http.NewRequest("GET", "/v1/client/allocation/123/foo", nil) if err != nil { @@ -237,7 +242,8 @@ func TestHTTP_AllocStats(t *testing.T) { } func TestHTTP_AllocSnapshot(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Make the HTTP request req, err := http.NewRequest("GET", "/v1/client/allocation/123/snapshot", nil) if err != nil { @@ -254,7 +260,8 @@ func TestHTTP_AllocSnapshot(t *testing.T) { } func TestHTTP_AllocGC(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Make the HTTP request req, err := http.NewRequest("GET", "/v1/client/allocation/123/gc", nil) if err != nil { @@ -271,7 +278,8 @@ func TestHTTP_AllocGC(t *testing.T) { } func TestHTTP_AllocAllGC(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Make the HTTP request req, err := http.NewRequest("GET", "/v1/client/gc", nil) if err != nil { diff --git a/command/agent/command_test.go b/command/agent/command_test.go index 342390913..9ef099a35 100644 --- a/command/agent/command_test.go +++ b/command/agent/command_test.go @@ -12,10 +12,12 @@ import ( ) func TestCommand_Implements(t *testing.T) { + t.Parallel() var _ cli.Command = &Command{} } func TestCommand_Args(t *testing.T) { + t.Parallel() tmpDir, err := ioutil.TempDir("", "nomad") if err != nil { t.Fatalf("err: %s", err) @@ -75,9 +77,10 @@ func TestCommand_Args(t *testing.T) { } } +// TODO Why is this failing func TestRetryJoin(t *testing.T) { - dir, agent := makeAgent(t, nil) - defer os.RemoveAll(dir) + t.Parallel() + agent := NewTestAgent(t.Name(), nil) defer agent.Shutdown() doneCh := make(chan struct{}) @@ -97,14 +100,11 @@ func TestRetryJoin(t *testing.T) { }, } - serfAddr := fmt.Sprintf( - "%s:%d", - agent.config.BindAddr, - agent.config.Ports.Serf) + serfAddr := agent.Config.normalizedAddrs.Serf args := []string{ "-dev", - "-node", fmt.Sprintf(`"Node %d"`, getPort()), + "-node", "foo", "-retry-join", serfAddr, "-retry-interval", "1s", } diff --git a/command/agent/config_parse_test.go b/command/agent/config_parse_test.go index 8caaaf4a0..a677f62c7 100644 --- a/command/agent/config_parse_test.go +++ b/command/agent/config_parse_test.go @@ -13,6 +13,7 @@ import ( ) func TestConfig_Parse(t *testing.T) { + t.Parallel() cases := []struct { File string Result *Config diff --git a/command/agent/deployment_endpoint_test.go b/command/agent/deployment_endpoint_test.go index e6dd7f06b..8c17369c5 100644 --- a/command/agent/deployment_endpoint_test.go +++ b/command/agent/deployment_endpoint_test.go @@ -11,8 +11,9 @@ import ( ) func TestHTTP_DeploymentList(t *testing.T) { + t.Parallel() assert := assert.New(t) - httpTest(t, nil, func(s *TestServer) { + httpTest(t, nil, func(s *TestAgent) { // Directly manipulate the state state := s.Agent.server.State() d1 := mock.Deployment() @@ -41,8 +42,9 @@ func TestHTTP_DeploymentList(t *testing.T) { } func TestHTTP_DeploymentPrefixList(t *testing.T) { + t.Parallel() assert := assert.New(t) - httpTest(t, nil, func(s *TestServer) { + httpTest(t, nil, func(s *TestAgent) { // Directly manipulate the state state := s.Agent.server.State() d1 := mock.Deployment() @@ -74,8 +76,9 @@ func TestHTTP_DeploymentPrefixList(t *testing.T) { } func TestHTTP_DeploymentAllocations(t *testing.T) { + t.Parallel() assert := assert.New(t) - httpTest(t, nil, func(s *TestServer) { + httpTest(t, nil, func(s *TestAgent) { // Directly manipulate the state state := s.Agent.server.State() j := mock.Job() @@ -112,8 +115,9 @@ func TestHTTP_DeploymentAllocations(t *testing.T) { } func TestHTTP_DeploymentQuery(t *testing.T) { + t.Parallel() assert := assert.New(t) - httpTest(t, nil, func(s *TestServer) { + httpTest(t, nil, func(s *TestAgent) { // Directly manipulate the state state := s.Agent.server.State() d := mock.Deployment() @@ -140,8 +144,9 @@ func TestHTTP_DeploymentQuery(t *testing.T) { } func TestHTTP_DeploymentPause(t *testing.T) { + t.Parallel() assert := assert.New(t) - httpTest(t, nil, func(s *TestServer) { + httpTest(t, nil, func(s *TestAgent) { // Directly manipulate the state state := s.Agent.server.State() j := mock.Job() @@ -177,8 +182,9 @@ func TestHTTP_DeploymentPause(t *testing.T) { } func TestHTTP_DeploymentPromote(t *testing.T) { + t.Parallel() assert := assert.New(t) - httpTest(t, nil, func(s *TestServer) { + httpTest(t, nil, func(s *TestAgent) { // Directly manipulate the state state := s.Agent.server.State() j := mock.Job() @@ -214,8 +220,9 @@ func TestHTTP_DeploymentPromote(t *testing.T) { } func TestHTTP_DeploymentAllocHealth(t *testing.T) { + t.Parallel() assert := assert.New(t) - httpTest(t, nil, func(s *TestServer) { + httpTest(t, nil, func(s *TestAgent) { // Directly manipulate the state state := s.Agent.server.State() j := mock.Job() @@ -255,8 +262,9 @@ func TestHTTP_DeploymentAllocHealth(t *testing.T) { } func TestHTTP_DeploymentFail(t *testing.T) { + t.Parallel() assert := assert.New(t) - httpTest(t, nil, func(s *TestServer) { + httpTest(t, nil, func(s *TestAgent) { // Directly manipulate the state state := s.Agent.server.State() j := mock.Job() diff --git a/command/agent/eval_endpoint_test.go b/command/agent/eval_endpoint_test.go index 4cf284438..c81ff489b 100644 --- a/command/agent/eval_endpoint_test.go +++ b/command/agent/eval_endpoint_test.go @@ -10,7 +10,8 @@ import ( ) func TestHTTP_EvalList(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Directly manipulate the state state := s.Agent.server.State() eval1 := mock.Eval() @@ -54,7 +55,8 @@ func TestHTTP_EvalList(t *testing.T) { } func TestHTTP_EvalPrefixList(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Directly manipulate the state state := s.Agent.server.State() eval1 := mock.Eval() @@ -105,7 +107,8 @@ func TestHTTP_EvalPrefixList(t *testing.T) { } func TestHTTP_EvalAllocations(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Directly manipulate the state state := s.Agent.server.State() alloc1 := mock.Alloc() @@ -153,7 +156,8 @@ func TestHTTP_EvalAllocations(t *testing.T) { } func TestHTTP_EvalQuery(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Directly manipulate the state state := s.Agent.server.State() eval := mock.Eval() diff --git a/command/agent/fs_endpoint_test.go b/command/agent/fs_endpoint_test.go index 2feee179c..ba461ef6d 100644 --- a/command/agent/fs_endpoint_test.go +++ b/command/agent/fs_endpoint_test.go @@ -25,7 +25,8 @@ import ( ) func TestAllocDirFS_List_MissingParams(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { req, err := http.NewRequest("GET", "/v1/client/fs/ls/", nil) if err != nil { t.Fatalf("err: %v", err) @@ -40,7 +41,8 @@ func TestAllocDirFS_List_MissingParams(t *testing.T) { } func TestAllocDirFS_Stat_MissingParams(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { req, err := http.NewRequest("GET", "/v1/client/fs/stat/", nil) if err != nil { t.Fatalf("err: %v", err) @@ -67,7 +69,8 @@ func TestAllocDirFS_Stat_MissingParams(t *testing.T) { } func TestAllocDirFS_ReadAt_MissingParams(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { req, err := http.NewRequest("GET", "/v1/client/fs/readat/", nil) if err != nil { t.Fatalf("err: %v", err) @@ -500,7 +503,8 @@ func TestStreamFramer_Order_PlainText(t *testing.T) { } func TestHTTP_Stream_MissingParams(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { req, err := http.NewRequest("GET", "/v1/client/fs/stream/", nil) if err != nil { t.Fatalf("err: %v", err) @@ -560,7 +564,8 @@ func (n nopWriteCloser) Close() error { } func TestHTTP_Stream_NoFile(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Get a temp alloc dir ad := tempAllocDir(t) defer os.RemoveAll(ad.AllocDir) @@ -576,7 +581,8 @@ func TestHTTP_Stream_NoFile(t *testing.T) { } func TestHTTP_Stream_Modify(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Get a temp alloc dir ad := tempAllocDir(t) defer os.RemoveAll(ad.AllocDir) @@ -651,7 +657,8 @@ func TestHTTP_Stream_Modify(t *testing.T) { } func TestHTTP_Stream_Truncate(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Get a temp alloc dir ad := tempAllocDir(t) defer os.RemoveAll(ad.AllocDir) @@ -760,7 +767,8 @@ func TestHTTP_Stream_Truncate(t *testing.T) { } func TestHTTP_Stream_Delete(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Get a temp alloc dir ad := tempAllocDir(t) defer os.RemoveAll(ad.AllocDir) @@ -842,7 +850,8 @@ func TestHTTP_Stream_Delete(t *testing.T) { } func TestHTTP_Logs_NoFollow(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Get a temp alloc dir and create the log dir ad := tempAllocDir(t) defer os.RemoveAll(ad.AllocDir) @@ -923,7 +932,8 @@ func TestHTTP_Logs_NoFollow(t *testing.T) { } func TestHTTP_Logs_Follow(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Get a temp alloc dir and create the log dir ad := tempAllocDir(t) defer os.RemoveAll(ad.AllocDir) @@ -1029,7 +1039,7 @@ func BenchmarkHTTP_Logs_Follow(t *testing.B) { runtime.MemProfileRate = 1 s := makeHTTPServer(t, nil) - defer s.Cleanup() + defer s.Shutdown() testutil.WaitForLeader(t, s.Agent.RPC) // Get a temp alloc dir and create the log dir diff --git a/command/agent/http.go b/command/agent/http.go index d79250ff8..3891ef37f 100644 --- a/command/agent/http.go +++ b/command/agent/http.go @@ -35,7 +35,7 @@ type HTTPServer struct { mux *http.ServeMux listener net.Listener logger *log.Logger - addr string + Addr string } // NewHTTPServer starts new HTTP server over the agent @@ -76,7 +76,7 @@ func NewHTTPServer(agent *Agent, config *Config) (*HTTPServer, error) { mux: mux, listener: ln, logger: agent.logger, - addr: ln.Addr().String(), + Addr: ln.Addr().String(), } srv.registerHandlers(config.EnableDebug) @@ -97,7 +97,7 @@ func newScadaHttp(agent *Agent, list net.Listener) *HTTPServer { mux: mux, listener: list, logger: agent.logger, - addr: scadaHTTPAddr, + Addr: scadaHTTPAddr, } srv.registerHandlers(false) // Never allow debug for SCADA diff --git a/command/agent/http_test.go b/command/agent/http_test.go index 90c4eb9bb..8b6616b97 100644 --- a/command/agent/http_test.go +++ b/command/agent/http_test.go @@ -12,7 +12,6 @@ import ( "net/http" "net/http/httptest" "net/url" - "os" "strconv" "testing" "time" @@ -23,41 +22,17 @@ import ( "github.com/hashicorp/nomad/testutil" ) -type TestServer struct { - T testing.TB - Dir string - Agent *Agent - Server *HTTPServer -} - -func (s *TestServer) Cleanup() { - s.Server.Shutdown() - s.Agent.Shutdown() - os.RemoveAll(s.Dir) -} - // makeHTTPServer returns a test server whose logs will be written to // the passed writer. If the writer is nil, the logs are written to stderr. -func makeHTTPServer(t testing.TB, cb func(c *Config)) *TestServer { - dir, agent := makeAgent(t, cb) - srv, err := NewHTTPServer(agent, agent.config) - if err != nil { - t.Fatalf("err: %v", err) - } - s := &TestServer{ - T: t, - Dir: dir, - Agent: agent, - Server: srv, - } - return s +func makeHTTPServer(t testing.TB, cb func(c *Config)) *TestAgent { + return NewTestAgent(t.Name(), cb) } func BenchmarkHTTPRequests(b *testing.B) { s := makeHTTPServer(b, func(c *Config) { c.Client.Enabled = false }) - defer s.Cleanup() + defer s.Shutdown() job := mock.Job() var allocs []*structs.Allocation @@ -85,6 +60,7 @@ func BenchmarkHTTPRequests(b *testing.B) { } func TestSetIndex(t *testing.T) { + t.Parallel() resp := httptest.NewRecorder() setIndex(resp, 1000) header := resp.Header().Get("X-Nomad-Index") @@ -98,6 +74,7 @@ func TestSetIndex(t *testing.T) { } func TestSetKnownLeader(t *testing.T) { + t.Parallel() resp := httptest.NewRecorder() setKnownLeader(resp, true) header := resp.Header().Get("X-Nomad-KnownLeader") @@ -113,6 +90,7 @@ func TestSetKnownLeader(t *testing.T) { } func TestSetLastContact(t *testing.T) { + t.Parallel() resp := httptest.NewRecorder() setLastContact(resp, 123456*time.Microsecond) header := resp.Header().Get("X-Nomad-LastContact") @@ -122,6 +100,7 @@ func TestSetLastContact(t *testing.T) { } func TestSetMeta(t *testing.T) { + t.Parallel() meta := structs.QueryMeta{ Index: 1000, KnownLeader: true, @@ -144,9 +123,10 @@ func TestSetMeta(t *testing.T) { } func TestSetHeaders(t *testing.T) { + t.Parallel() s := makeHTTPServer(t, nil) s.Agent.config.HTTPAPIResponseHeaders = map[string]string{"foo": "bar"} - defer s.Cleanup() + defer s.Shutdown() resp := httptest.NewRecorder() handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) { @@ -164,8 +144,9 @@ func TestSetHeaders(t *testing.T) { } func TestContentTypeIsJSON(t *testing.T) { + t.Parallel() s := makeHTTPServer(t, nil) - defer s.Cleanup() + defer s.Shutdown() resp := httptest.NewRecorder() @@ -184,20 +165,23 @@ func TestContentTypeIsJSON(t *testing.T) { } func TestPrettyPrint(t *testing.T) { + t.Parallel() testPrettyPrint("pretty=1", true, t) } func TestPrettyPrintOff(t *testing.T) { + t.Parallel() testPrettyPrint("pretty=0", false, t) } func TestPrettyPrintBare(t *testing.T) { + t.Parallel() testPrettyPrint("pretty", true, t) } func testPrettyPrint(pretty string, prettyFmt bool, t *testing.T) { s := makeHTTPServer(t, nil) - defer s.Cleanup() + defer s.Shutdown() r := &structs.Job{Name: "foo"} @@ -228,6 +212,7 @@ func testPrettyPrint(pretty string, prettyFmt bool, t *testing.T) { } func TestParseWait(t *testing.T) { + t.Parallel() resp := httptest.NewRecorder() var b structs.QueryOptions @@ -250,6 +235,7 @@ func TestParseWait(t *testing.T) { } func TestParseWait_InvalidTime(t *testing.T) { + t.Parallel() resp := httptest.NewRecorder() var b structs.QueryOptions @@ -269,6 +255,7 @@ func TestParseWait_InvalidTime(t *testing.T) { } func TestParseWait_InvalidIndex(t *testing.T) { + t.Parallel() resp := httptest.NewRecorder() var b structs.QueryOptions @@ -288,6 +275,7 @@ func TestParseWait_InvalidIndex(t *testing.T) { } func TestParseConsistency(t *testing.T) { + t.Parallel() var b structs.QueryOptions req, err := http.NewRequest("GET", @@ -315,8 +303,9 @@ func TestParseConsistency(t *testing.T) { } func TestParseRegion(t *testing.T) { + t.Parallel() s := makeHTTPServer(t, nil) - defer s.Cleanup() + defer s.Shutdown() req, err := http.NewRequest("GET", "/v1/jobs?region=foo", nil) @@ -345,6 +334,7 @@ func TestParseRegion(t *testing.T) { // TestHTTP_VerifyHTTPSClient asserts that a client certificate signed by the // appropriate CA is required when VerifyHTTPSClient=true. func TestHTTP_VerifyHTTPSClient(t *testing.T) { + t.Parallel() const ( cafile = "../../helper/tlsutil/testdata/ca.pem" foocert = "../../helper/tlsutil/testdata/nomad-foo.pem" @@ -360,7 +350,7 @@ func TestHTTP_VerifyHTTPSClient(t *testing.T) { KeyFile: fookey, } }) - defer s.Cleanup() + defer s.Shutdown() reqURL := fmt.Sprintf("https://%s/v1/agent/self", s.Agent.config.AdvertiseAddrs.HTTP) @@ -492,9 +482,9 @@ func getIndex(t *testing.T, resp *httptest.ResponseRecorder) uint64 { return uint64(val) } -func httpTest(t testing.TB, cb func(c *Config), f func(srv *TestServer)) { +func httpTest(t testing.TB, cb func(c *Config), f func(srv *TestAgent)) { s := makeHTTPServer(t, cb) - defer s.Cleanup() + defer s.Shutdown() testutil.WaitForLeader(t, s.Agent.RPC) f(s) } diff --git a/command/agent/job_endpoint_test.go b/command/agent/job_endpoint_test.go index 173da2acb..4ff4b0e36 100644 --- a/command/agent/job_endpoint_test.go +++ b/command/agent/job_endpoint_test.go @@ -18,7 +18,8 @@ import ( ) func TestHTTP_JobsList(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { for i := 0; i < 3; i++ { // Create the job job := mock.Job() @@ -70,7 +71,8 @@ func TestHTTP_PrefixJobsList(t *testing.T) { "aabbbbbb-e8f7-fd38-c855-ab94ceb89706", "aabbcccc-e8f7-fd38-c855-ab94ceb89706", } - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { for i := 0; i < 3; i++ { // Create the job job := mock.Job() @@ -119,7 +121,8 @@ func TestHTTP_PrefixJobsList(t *testing.T) { } func TestHTTP_JobsRegister(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the job job := api.MockJob() args := api.JobRegisterRequest{ @@ -169,7 +172,8 @@ func TestHTTP_JobsRegister(t *testing.T) { } func TestHTTP_JobsRegister_Defaulting(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the job job := api.MockJob() @@ -226,7 +230,8 @@ func TestHTTP_JobsRegister_Defaulting(t *testing.T) { } func TestHTTP_JobQuery(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the job job := mock.Job() args := structs.JobRegisterRequest{ @@ -271,7 +276,8 @@ func TestHTTP_JobQuery(t *testing.T) { } func TestHTTP_JobQuery_Payload(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the job job := mock.Job() @@ -324,7 +330,8 @@ func TestHTTP_JobQuery_Payload(t *testing.T) { } func TestHTTP_JobUpdate(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the job job := api.MockJob() args := api.JobRegisterRequest{ @@ -374,7 +381,8 @@ func TestHTTP_JobUpdate(t *testing.T) { } func TestHTTP_JobDelete(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the job job := mock.Job() args := structs.JobRegisterRequest{ @@ -466,7 +474,8 @@ func TestHTTP_JobDelete(t *testing.T) { } func TestHTTP_JobForceEvaluate(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the job job := mock.Job() args := structs.JobRegisterRequest{ @@ -505,7 +514,8 @@ func TestHTTP_JobForceEvaluate(t *testing.T) { } func TestHTTP_JobEvaluations(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the job job := mock.Job() args := structs.JobRegisterRequest{ @@ -552,7 +562,8 @@ func TestHTTP_JobEvaluations(t *testing.T) { } func TestHTTP_JobAllocations(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the job alloc1 := mock.Alloc() args := structs.JobRegisterRequest{ @@ -605,7 +616,8 @@ func TestHTTP_JobAllocations(t *testing.T) { func TestHTTP_JobDeployments(t *testing.T) { assert := assert.New(t) - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the job j := mock.Job() args := structs.JobRegisterRequest{ @@ -643,7 +655,8 @@ func TestHTTP_JobDeployments(t *testing.T) { func TestHTTP_JobDeployment(t *testing.T) { assert := assert.New(t) - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the job j := mock.Job() args := structs.JobRegisterRequest{ @@ -680,7 +693,8 @@ func TestHTTP_JobDeployment(t *testing.T) { } func TestHTTP_JobVersions(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the job job := mock.Job() args := structs.JobRegisterRequest{ @@ -751,7 +765,8 @@ func TestHTTP_JobVersions(t *testing.T) { } func TestHTTP_PeriodicForce(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create and register a periodic job. job := mock.PeriodicJob() args := structs.JobRegisterRequest{ @@ -790,7 +805,8 @@ func TestHTTP_PeriodicForce(t *testing.T) { } func TestHTTP_JobPlan(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the job job := api.MockJob() args := api.JobPlanRequest{ @@ -826,7 +842,8 @@ func TestHTTP_JobPlan(t *testing.T) { } func TestHTTP_JobDispatch(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the parameterized job job := mock.Job() job.Type = "batch" @@ -874,7 +891,8 @@ func TestHTTP_JobDispatch(t *testing.T) { } func TestHTTP_JobRevert(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the job and register it twice job := mock.Job() regReq := structs.JobRegisterRequest{ @@ -926,7 +944,8 @@ func TestHTTP_JobRevert(t *testing.T) { } func TestHTTP_JobStable(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the job and register it twice job := mock.Job() regReq := structs.JobRegisterRequest{ diff --git a/command/agent/keyring_test.go b/command/agent/keyring_test.go index 475dc6223..a194e3ac2 100644 --- a/command/agent/keyring_test.go +++ b/command/agent/keyring_test.go @@ -9,11 +9,11 @@ import ( ) func TestAgent_LoadKeyrings(t *testing.T) { + t.Parallel() key := "tbLJg26ZJyJ9pK3qhc9jig==" // Should be no configured keyring file by default - dir1, agent1 := makeAgent(t, nil) - defer os.RemoveAll(dir1) + agent1 := NewTestAgent(t.Name(), nil) defer agent1.Shutdown() c := agent1.server.GetConfig() @@ -24,14 +24,12 @@ func TestAgent_LoadKeyrings(t *testing.T) { t.Fatalf("keyring should not be loaded") } - // Server should auto-load LAN and WAN keyring files - dir2, agent2 := makeAgent(t, func(c *Config) { - file := filepath.Join(c.DataDir, serfKeyring) - if err := initKeyring(file, key); err != nil { - t.Fatalf("err: %s", err) - } - }) - defer os.RemoveAll(dir2) + // Server should auto-load WAN keyring files + agent2 := &TestAgent{ + Name: t.Name() + "2", + Key: key, + } + agent2.Start() defer agent2.Shutdown() c = agent2.server.GetConfig() @@ -44,6 +42,7 @@ func TestAgent_LoadKeyrings(t *testing.T) { } func TestAgent_InitKeyring(t *testing.T) { + t.Parallel() key1 := "tbLJg26ZJyJ9pK3qhc9jig==" key2 := "4leC33rgtXKIVUr9Nr0snQ==" expected := fmt.Sprintf(`["%s"]`, key1) diff --git a/command/agent/log_levels_test.go b/command/agent/log_levels_test.go index 0a3753a1c..a3e863465 100644 --- a/command/agent/log_levels_test.go +++ b/command/agent/log_levels_test.go @@ -7,6 +7,7 @@ import ( ) func TestLevelFilter(t *testing.T) { + t.Parallel() filt := LevelFilter() filt.Levels = []logutils.LogLevel{"TRACE", "DEBUG", "INFO", "WARN", "ERR"} diff --git a/command/agent/log_writer_test.go b/command/agent/log_writer_test.go index 47e446d22..19c23c573 100644 --- a/command/agent/log_writer_test.go +++ b/command/agent/log_writer_test.go @@ -13,6 +13,7 @@ func (m *MockLogHandler) HandleLog(l string) { } func TestLogWriter(t *testing.T) { + t.Parallel() h := &MockLogHandler{} w := NewLogWriter(4) diff --git a/command/agent/node_endpoint_test.go b/command/agent/node_endpoint_test.go index a9fe426bf..391c10b9d 100644 --- a/command/agent/node_endpoint_test.go +++ b/command/agent/node_endpoint_test.go @@ -10,7 +10,8 @@ import ( ) func TestHTTP_NodesList(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { for i := 0; i < 3; i++ { // Create the node node := mock.Node() @@ -57,7 +58,8 @@ func TestHTTP_NodesList(t *testing.T) { } func TestHTTP_NodesPrefixList(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { ids := []string{ "12345678-abcd-efab-cdef-123456789abc", "12345678-aaaa-efab-cdef-123456789abc", @@ -113,7 +115,8 @@ func TestHTTP_NodesPrefixList(t *testing.T) { } func TestHTTP_NodeForceEval(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the node node := mock.Node() args := structs.NodeRegisterRequest{ @@ -164,7 +167,8 @@ func TestHTTP_NodeForceEval(t *testing.T) { } func TestHTTP_NodeAllocations(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the job node := mock.Node() args := structs.NodeRegisterRequest{ @@ -221,7 +225,8 @@ func TestHTTP_NodeAllocations(t *testing.T) { } func TestHTTP_NodeDrain(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the node node := mock.Node() args := structs.NodeRegisterRequest{ @@ -272,7 +277,8 @@ func TestHTTP_NodeDrain(t *testing.T) { } func TestHTTP_NodeQuery(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Create the job node := mock.Node() args := structs.NodeRegisterRequest{ diff --git a/command/agent/operator_endpoint_test.go b/command/agent/operator_endpoint_test.go index 5b5de7d82..b0e4dd651 100644 --- a/command/agent/operator_endpoint_test.go +++ b/command/agent/operator_endpoint_test.go @@ -11,7 +11,8 @@ import ( ) func TestHTTP_OperatorRaftConfiguration(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { body := bytes.NewBuffer(nil) req, err := http.NewRequest("GET", "/v1/operator/raft/configuration", body) if err != nil { @@ -39,7 +40,8 @@ func TestHTTP_OperatorRaftConfiguration(t *testing.T) { } func TestHTTP_OperatorRaftPeer(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { body := bytes.NewBuffer(nil) req, err := http.NewRequest("DELETE", "/v1/operator/raft/peer?address=nope", body) if err != nil { diff --git a/command/agent/region_endpoint_test.go b/command/agent/region_endpoint_test.go index 006c7a159..2549c6a9f 100644 --- a/command/agent/region_endpoint_test.go +++ b/command/agent/region_endpoint_test.go @@ -7,7 +7,8 @@ import ( ) func TestHTTP_RegionList(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Make the HTTP request req, err := http.NewRequest("GET", "/v1/regions", nil) if err != nil { diff --git a/command/agent/stats_endpoint_test.go b/command/agent/stats_endpoint_test.go index 0ce18aa1e..cc3c612e5 100644 --- a/command/agent/stats_endpoint_test.go +++ b/command/agent/stats_endpoint_test.go @@ -7,7 +7,8 @@ import ( ) func TestClientStatsRequest(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { req, err := http.NewRequest("GET", "/v1/client/stats/?since=foo", nil) if err != nil { t.Fatalf("err: %v", err) diff --git a/command/agent/status_endpoint_test.go b/command/agent/status_endpoint_test.go index 2b14f56a0..2dbe39cfd 100644 --- a/command/agent/status_endpoint_test.go +++ b/command/agent/status_endpoint_test.go @@ -7,7 +7,8 @@ import ( ) func TestHTTP_StatusLeader(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Make the HTTP request req, err := http.NewRequest("GET", "/v1/status/leader", nil) if err != nil { @@ -29,7 +30,8 @@ func TestHTTP_StatusLeader(t *testing.T) { } func TestHTTP_StatusPeers(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Make the HTTP request req, err := http.NewRequest("GET", "/v1/status/peers", nil) if err != nil { diff --git a/command/agent/syslog_test.go b/command/agent/syslog_test.go index 0990a081d..39ee0eee4 100644 --- a/command/agent/syslog_test.go +++ b/command/agent/syslog_test.go @@ -10,6 +10,7 @@ import ( ) func TestSyslogFilter(t *testing.T) { + t.Parallel() if runtime.GOOS == "windows" { t.Skip("Syslog not supported on Windows") } diff --git a/command/agent/system_endpoint_test.go b/command/agent/system_endpoint_test.go index fa186a477..f45c17c2c 100644 --- a/command/agent/system_endpoint_test.go +++ b/command/agent/system_endpoint_test.go @@ -7,7 +7,8 @@ import ( ) func TestHTTP_SystemGarbageCollect(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Make the HTTP request req, err := http.NewRequest("PUT", "/v1/system/gc", nil) if err != nil { @@ -23,7 +24,8 @@ func TestHTTP_SystemGarbageCollect(t *testing.T) { } func TestHTTP_ReconcileJobSummaries(t *testing.T) { - httpTest(t, nil, func(s *TestServer) { + t.Parallel() + httpTest(t, nil, func(s *TestAgent) { // Make the HTTP request req, err := http.NewRequest("PUT", "/v1/system/reconcile/summaries", nil) if err != nil { diff --git a/command/agent/testagent.go b/command/agent/testagent.go new file mode 100644 index 000000000..0ec4ea1bc --- /dev/null +++ b/command/agent/testagent.go @@ -0,0 +1,281 @@ +package agent + +import ( + "fmt" + "io" + "io/ioutil" + "math/rand" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "runtime" + "strings" + "time" + + "github.com/hashicorp/nomad/api" + "github.com/hashicorp/nomad/nomad" + "github.com/hashicorp/nomad/nomad/structs" + sconfig "github.com/hashicorp/nomad/nomad/structs/config" + "github.com/hashicorp/nomad/testutil" +) + +func init() { + rand.Seed(time.Now().UnixNano()) // seed random number generator +} + +// TempDir defines the base dir for temporary directories. +var TempDir = os.TempDir() + +// TestAgent encapsulates an Agent with a default configuration and +// startup procedure suitable for testing. It panics if there are errors +// during creation or startup instead of returning errors. It manages a +// temporary data directory which is removed after shutdown. +type TestAgent struct { + // Name is an optional name of the agent. + Name string + + // ConfigCallback is an optional callback that allows modification of the + // configuration before the agent is started. + ConfigCallback func(*Config) + + // Config is the agent configuration. If Config is nil then + // TestConfig() is used. If Config.DataDir is set then it is + // the callers responsibility to clean up the data directory. + // Otherwise, a temporary data directory is created and removed + // when Shutdown() is called. + Config *Config + + // LogOutput is the sink for the logs. If nil, logs are written + // to os.Stderr. + LogOutput io.Writer + + // DataDir is the data directory which is used when Config.DataDir + // is not set. It is created automatically and removed when + // Shutdown() is called. + DataDir string + + // Key is the optional encryption key for the keyring. + Key string + + // Server is a reference to the started HTTP endpoint. + // It is valid after Start(). + Server *HTTPServer + + // Agent is the embedded Nomad agent. + // It is valid after Start(). + *Agent +} + +// NewTestAgent returns a started agent with the given name and +// configuration. It panics if the agent could not be started. The +// caller should call Shutdown() to stop the agent and remove temporary +// directories. +func NewTestAgent(name string, configCallback func(*Config)) *TestAgent { + a := &TestAgent{Name: name, ConfigCallback: configCallback} + a.Start() + return a +} + +// Start starts a test agent. It panics if the agent could not be started. +func (a *TestAgent) Start() *TestAgent { + if a.Agent != nil { + panic("TestAgent already started") + } + if a.Config == nil { + a.Config = a.config() + } + if a.Config.DataDir == "" { + name := "agent" + if a.Name != "" { + name = a.Name + "-agent" + } + name = strings.Replace(name, "/", "_", -1) + d, err := ioutil.TempDir(TempDir, name) + if err != nil { + panic(fmt.Sprintf("Error creating data dir %s: %s", filepath.Join(TempDir, name), err)) + } + a.DataDir = d + a.Config.DataDir = d + a.Config.NomadConfig.DataDir = d + } + + for i := 10; i >= 0; i-- { + pickRandomPorts(a.Config) + a.Config.NodeName = fmt.Sprintf("Node %d", a.Config.Ports.RPC) + + // write the keyring + if a.Key != "" { + writeKey := func(key, filename string) { + path := filepath.Join(a.Config.DataDir, filename) + if err := initKeyring(path, key); err != nil { + panic(fmt.Sprintf("Error creating keyring %s: %s", path, err)) + } + } + writeKey(a.Key, serfKeyring) + } + + // we need the err var in the next exit condition + if agent, err := a.start(); err == nil { + a.Agent = agent + break + } else if i == 0 { + fmt.Println(a.Name, "Error starting agent:", err) + runtime.Goexit() + } else { + if agent != nil { + agent.Shutdown() + } + wait := time.Duration(rand.Int31n(2000)) * time.Millisecond + fmt.Println(a.Name, "retrying in", wait) + time.Sleep(wait) + } + + // Clean out the data dir if we are responsible for it before we + // try again, since the old ports may have gotten written to + // the data dir, such as in the Raft configuration. + if a.DataDir != "" { + if err := os.RemoveAll(a.DataDir); err != nil { + fmt.Println(a.Name, "Error resetting data dir:", err) + runtime.Goexit() + } + } + } + + if a.Config.NomadConfig.Bootstrap && a.Config.Server.Enabled { + testutil.WaitForResult(func() (bool, error) { + args := &structs.GenericRequest{} + var leader string + err := a.RPC("Status.Leader", args, &leader) + return leader != "", err + }, func(err error) { + panic(fmt.Sprintf("failed to find leader: %v", err)) + }) + } else { + testutil.WaitForResult(func() (bool, error) { + req, _ := http.NewRequest("GET", "/v1/agent/self", nil) + resp := httptest.NewRecorder() + _, err := a.Server.AgentSelfRequest(resp, req) + return err == nil && resp.Code == 200, err + }, func(err error) { + panic(fmt.Sprintf("failed OK response: %v", err)) + }) + } + return a +} + +func (a *TestAgent) start() (*Agent, error) { + if a.LogOutput == nil { + a.LogOutput = os.Stderr + } + + agent, err := NewAgent(a.Config, a.LogOutput) + if err != nil { + return nil, err + } + + // Setup the HTTP server + http, err := NewHTTPServer(agent, a.Config) + if err != nil { + return agent, err + } + + a.Server = http + return agent, nil +} + +// Shutdown stops the agent and removes the data directory if it is +// managed by the test agent. +func (a *TestAgent) Shutdown() error { + defer func() { + if a.DataDir != "" { + os.RemoveAll(a.DataDir) + } + }() + + // shutdown agent before endpoints + a.Server.Shutdown() + return a.Agent.Shutdown() +} + +func (a *TestAgent) HTTPAddr() string { + if a.Server == nil { + return "" + } + return a.Server.Addr +} + +func (a *TestAgent) Client() *api.Client { + conf := api.DefaultConfig() + conf.Address = a.HTTPAddr() + c, err := api.NewClient(conf) + if err != nil { + panic(fmt.Sprintf("Error creating consul API client: %s", err)) + } + return c +} + +// FivePorts returns the first port number of a block of +// five random ports. +func FivePorts() int { + return 1030 + int(rand.Int31n(6440))*5 +} + +// pickRandomPorts selects random ports from fixed size random blocks of +// ports. This does not eliminate the chance for port conflict but +// reduces it significanltly with little overhead. Furthermore, asking +// the kernel for a random port by binding to port 0 prolongs the test +// execution (in our case +20sec) while also not fully eliminating the +// chance of port conflicts for concurrently executed test binaries. +// Instead of relying on one set of ports to be sufficient we retry +// starting the agent with different ports on port conflict. +func pickRandomPorts(c *Config) { + port := FivePorts() + c.Ports.HTTP = port + 1 + c.Ports.RPC = port + 2 + c.Ports.Serf = port + 3 + + if err := c.normalizeAddrs(); err != nil { + panic(fmt.Sprintf("error normalizing config: %v", err)) + } +} + +// TestConfig returns a unique default configuration for testing an +// agent. +func (a *TestAgent) config() *Config { + conf := DevConfig() + + // Customize the server configuration + config := nomad.DefaultConfig() + conf.NomadConfig = config + + // Bind and set ports + conf.BindAddr = "127.0.0.1" + + conf.Consul = sconfig.DefaultConsulConfig() + conf.Vault.Enabled = new(bool) + + // Tighten the Serf timing + config.SerfConfig.MemberlistConfig.SuspicionMult = 2 + config.SerfConfig.MemberlistConfig.RetransmitMult = 2 + config.SerfConfig.MemberlistConfig.ProbeTimeout = 50 * time.Millisecond + config.SerfConfig.MemberlistConfig.ProbeInterval = 100 * time.Millisecond + config.SerfConfig.MemberlistConfig.GossipInterval = 100 * time.Millisecond + + // Tighten the Raft timing + config.RaftConfig.LeaderLeaseTimeout = 20 * time.Millisecond + config.RaftConfig.HeartbeatTimeout = 40 * time.Millisecond + config.RaftConfig.ElectionTimeout = 40 * time.Millisecond + config.RaftConfig.StartAsLeader = true + config.RaftTimeout = 500 * time.Millisecond + + // Bootstrap ourselves + config.Bootstrap = true + config.BootstrapExpect = 1 + + if a.ConfigCallback != nil { + a.ConfigCallback(conf) + } + + return conf +} diff --git a/scripts/test.sh b/scripts/test.sh index 7a3803ded..814504c22 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -21,5 +21,5 @@ go build -i -tags "$GOTEST_TAGS" -o $TEMPDIR/nomad || exit 1 echo "--> Running tests" GOBIN="`which go`" sudo -E PATH=$TEMPDIR:$PATH -E GOPATH=$GOPATH -E NOMAD_TEST_RKT=1 \ - $GOBIN test -tags "$GOTEST_TAGS" ${GOTEST_FLAGS:--cover -timeout=900s} $($GOBIN list ./... | grep -v /vendor/) + $GOBIN test -tags "$GOTEST_TAGS" ${GOTEST_FLAGS:--cover -timeout=900s -v -parallel 16} $($GOBIN list ./... | grep -v /vendor/)