From de105a0a4ec78d1e247a7acaf3b61d1874641be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Pi=C3=B1a?= Date: Mon, 19 Apr 2021 12:01:26 -0700 Subject: [PATCH] Return not ready for connection reason Currently, we use ReadyForConnections in server tests to wait for the server to be ready. However, when this fails we don't get a clue about why it failed. This change adds a new unexported method called readyForConnections that returns an error describing which check failed. The exported ReadyForConnections version works exactly as before. The unexported version gets used in internal tests only. --- server/accounts_test.go | 8 +++--- server/mqtt_test.go | 4 +-- server/routes_test.go | 4 +-- server/server.go | 59 ++++++++++++++++++++++++++++------------- server/server_test.go | 8 +++--- server/service_test.go | 5 ++-- 6 files changed, 55 insertions(+), 33 deletions(-) diff --git a/server/accounts_test.go b/server/accounts_test.go index 7d0d6438..64db8b0c 100644 --- a/server/accounts_test.go +++ b/server/accounts_test.go @@ -214,8 +214,8 @@ func TestAccountIsolationExportImport(t *testing.T) { s := opTrustBasicSetup() defer s.Shutdown() go s.Start() - if !s.ReadyForConnections(5 * time.Second) { - t.Fatal("failed to be ready for connections") + if err := s.readyForConnections(5 * time.Second); err != nil { + t.Fatal(err) } buildMemAccResolver(s) @@ -1689,8 +1689,8 @@ func TestAccountRequestReplyTrackLatency(t *testing.T) { // Run server in Go routine. We need this one running for internal sending of msgs. go s.Start() // Wait for accept loop(s) to be started - if !s.ReadyForConnections(10 * time.Second) { - panic("Unable to start NATS Server in Go Routine") + if err := s.readyForConnections(10 * time.Second); err != nil { + t.Fatal(err) } cfoo, crFoo, _ := newClientForServer(s) diff --git a/server/mqtt_test.go b/server/mqtt_test.go index c2ad5469..2ede9b1d 100644 --- a/server/mqtt_test.go +++ b/server/mqtt_test.go @@ -216,8 +216,8 @@ func testMQTTRunServer(t testing.TB, o *Options) *Server { l := &DummyLogger{} s.SetLogger(l, true, true) go s.Start() - if !s.ReadyForConnections(3 * time.Second) { - t.Fatal("Unable to start server") + if err := s.readyForConnections(3 * time.Second); err != nil { + t.Fatal(err) } return s } diff --git a/server/routes_test.go b/server/routes_test.go index f6fd1761..e5ffafee 100644 --- a/server/routes_test.go +++ b/server/routes_test.go @@ -1342,8 +1342,8 @@ func TestRouteIPResolutionAndRouteToSelf(t *testing.T) { go func() { s.Start() }() - if !s.ReadyForConnections(time.Second) { - t.Fatalf("Failed to start server") + if err := s.readyForConnections(time.Second); err != nil { + t.Fatal(err) } select { diff --git a/server/server.go b/server/server.go index 4c5e3c05..a4a25357 100644 --- a/server/server.go +++ b/server/server.go @@ -2682,28 +2682,51 @@ func (s *Server) ProfilerAddr() *net.TCPAddr { return s.profiler.Addr().(*net.TCPAddr) } +func (s *Server) readyForConnections(d time.Duration) error { + // Snapshot server options. + opts := s.getOpts() + + chk := make(map[string]bool) + end := time.Now().Add(d) + for time.Now().Before(end) { + s.mu.Lock() + chk["server"] = s.listener != nil + chk["route"] = (opts.Cluster.Port == 0 || s.routeListener != nil) + chk["gateway"] = (opts.Gateway.Name == "" || s.gatewayListener != nil) + chk["leafNode"] = (opts.LeafNode.Port == 0 || s.leafNodeListener != nil) + chk["websocket"] = (opts.Websocket.Port == 0 || s.websocket.listener != nil) + s.mu.Unlock() + + var numOK int + for _, ok := range chk { + if ok { + numOK++ + } + } + if numOK == len(chk) { + return nil + } + time.Sleep(25 * time.Millisecond) + } + + failed := make([]string, 0, len(chk)) + for name, ok := range chk { + if !ok { + failed = append(failed, name) + } + } + + return fmt.Errorf( + "failed to be ready for connections after %s: %s", + d, strings.Join(failed, ", "), + ) +} + // ReadyForConnections returns `true` if the server is ready to accept clients // and, if routing is enabled, route connections. If after the duration // `dur` the server is still not ready, returns `false`. func (s *Server) ReadyForConnections(dur time.Duration) bool { - // Snapshot server options. - opts := s.getOpts() - - end := time.Now().Add(dur) - for time.Now().Before(end) { - s.mu.Lock() - ok := s.listener != nil && - (opts.Cluster.Port == 0 || s.routeListener != nil) && - (opts.Gateway.Name == "" || s.gatewayListener != nil) && - (opts.LeafNode.Port == 0 || s.leafNodeListener != nil) && - (opts.Websocket.Port == 0 || s.websocket.listener != nil) - s.mu.Unlock() - if ok { - return true - } - time.Sleep(25 * time.Millisecond) - } - return false + return s.readyForConnections(dur) == nil } // Quick utility to function to tell if the server supports headers. diff --git a/server/server_test.go b/server/server_test.go index f4eb6f1b..dfe76673 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -84,8 +84,8 @@ func RunServer(opts *Options) *Server { go s.Start() // Wait for accept loop(s) to be started - if !s.ReadyForConnections(10 * time.Second) { - panic("Unable to start NATS Server in Go Routine") + if err := s.readyForConnections(10 * time.Second); err != nil { + panic(err) } return s } @@ -1481,8 +1481,8 @@ func TestInsecureSkipVerifyWarning(t *testing.T) { s.Start() wg.Done() }() - if !s.ReadyForConnections(time.Second) { - t.Fatal("Unable to start the server") + if err := s.readyForConnections(time.Second); err != nil { + t.Fatal(err) } select { case w := <-l.warn: diff --git a/server/service_test.go b/server/service_test.go index 8811c185..93f2727e 100644 --- a/server/service_test.go +++ b/server/service_test.go @@ -16,7 +16,6 @@ package server import ( - "errors" "testing" "time" ) @@ -31,8 +30,8 @@ func TestRun(t *testing.T) { errC <- Run(s) }() go func() { - if !s.ReadyForConnections(time.Second) { - started <- errors.New("failed to start in time") + if err := s.readyForConnections(time.Second); err != nil { + started <- err return } s.Shutdown()