node_backend_test.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package ipnlocal
  4. import (
  5. "context"
  6. "errors"
  7. "testing"
  8. "time"
  9. "tailscale.com/tailcfg"
  10. "tailscale.com/tstest"
  11. "tailscale.com/types/netmap"
  12. "tailscale.com/types/ptr"
  13. "tailscale.com/util/eventbus"
  14. )
  15. func TestNodeBackendReadiness(t *testing.T) {
  16. nb := newNodeBackend(t.Context(), tstest.WhileTestRunningLogger(t), eventbus.New())
  17. // The node backend is not ready until [nodeBackend.ready] is called,
  18. // and [nodeBackend.Wait] should fail with [context.DeadlineExceeded].
  19. ctx, cancelCtx := context.WithTimeout(context.Background(), 100*time.Millisecond)
  20. defer cancelCtx()
  21. if err := nb.Wait(ctx); err != ctx.Err() {
  22. t.Fatalf("Wait: got %v; want %v", err, ctx.Err())
  23. }
  24. // Start a goroutine to wait for the node backend to become ready.
  25. waitDone := make(chan struct{})
  26. go func() {
  27. if err := nb.Wait(context.Background()); err != nil {
  28. t.Errorf("Wait: got %v; want nil", err)
  29. }
  30. close(waitDone)
  31. }()
  32. // Call [nodeBackend.ready] to indicate that the node backend is now ready.
  33. go nb.ready()
  34. // Once the backend is called, [nodeBackend.Wait] should return immediately without error.
  35. if err := nb.Wait(context.Background()); err != nil {
  36. t.Fatalf("Wait: got %v; want nil", err)
  37. }
  38. // And any pending waiters should also be unblocked.
  39. <-waitDone
  40. }
  41. func TestNodeBackendShutdown(t *testing.T) {
  42. nb := newNodeBackend(t.Context(), tstest.WhileTestRunningLogger(t), eventbus.New())
  43. shutdownCause := errors.New("test shutdown")
  44. // Start a goroutine to wait for the node backend to become ready.
  45. // This test expects it to block until the node backend shuts down
  46. // and then return the specified shutdown cause.
  47. waitDone := make(chan struct{})
  48. go func() {
  49. if err := nb.Wait(context.Background()); err != shutdownCause {
  50. t.Errorf("Wait: got %v; want %v", err, shutdownCause)
  51. }
  52. close(waitDone)
  53. }()
  54. // Call [nodeBackend.shutdown] to indicate that the node backend is shutting down.
  55. nb.shutdown(shutdownCause)
  56. // Calling it again is fine, but should not change the shutdown cause.
  57. nb.shutdown(errors.New("test shutdown again"))
  58. // After shutdown, [nodeBackend.Wait] should return with the specified shutdown cause.
  59. if err := nb.Wait(context.Background()); err != shutdownCause {
  60. t.Fatalf("Wait: got %v; want %v", err, shutdownCause)
  61. }
  62. // The context associated with the node backend should also be cancelled
  63. // and its cancellation cause should match the shutdown cause.
  64. if err := nb.Context().Err(); !errors.Is(err, context.Canceled) {
  65. t.Fatalf("Context.Err: got %v; want %v", err, context.Canceled)
  66. }
  67. if cause := context.Cause(nb.Context()); cause != shutdownCause {
  68. t.Fatalf("Cause: got %v; want %v", cause, shutdownCause)
  69. }
  70. // And any pending waiters should also be unblocked.
  71. <-waitDone
  72. }
  73. func TestNodeBackendReadyAfterShutdown(t *testing.T) {
  74. nb := newNodeBackend(t.Context(), tstest.WhileTestRunningLogger(t), eventbus.New())
  75. shutdownCause := errors.New("test shutdown")
  76. nb.shutdown(shutdownCause)
  77. nb.ready() // Calling ready after shutdown is a no-op, but should not panic, etc.
  78. if err := nb.Wait(context.Background()); err != shutdownCause {
  79. t.Fatalf("Wait: got %v; want %v", err, shutdownCause)
  80. }
  81. }
  82. func TestNodeBackendParentContextCancellation(t *testing.T) {
  83. ctx, cancelCtx := context.WithCancel(context.Background())
  84. nb := newNodeBackend(ctx, tstest.WhileTestRunningLogger(t), eventbus.New())
  85. cancelCtx()
  86. // Cancelling the parent context should cause [nodeBackend.Wait]
  87. // to return with [context.Canceled].
  88. if err := nb.Wait(context.Background()); !errors.Is(err, context.Canceled) {
  89. t.Fatalf("Wait: got %v; want %v", err, context.Canceled)
  90. }
  91. // And the node backend's context should also be cancelled.
  92. if err := nb.Context().Err(); !errors.Is(err, context.Canceled) {
  93. t.Fatalf("Context.Err: got %v; want %v", err, context.Canceled)
  94. }
  95. }
  96. func TestNodeBackendConcurrentReadyAndShutdown(t *testing.T) {
  97. nb := newNodeBackend(t.Context(), tstest.WhileTestRunningLogger(t), eventbus.New())
  98. // Calling [nodeBackend.ready] and [nodeBackend.shutdown] concurrently
  99. // should not cause issues, and [nodeBackend.Wait] should unblock,
  100. // but the result of [nodeBackend.Wait] is intentionally undefined.
  101. go nb.ready()
  102. go nb.shutdown(errors.New("test shutdown"))
  103. nb.Wait(context.Background())
  104. }
  105. func TestNodeBackendReachability(t *testing.T) {
  106. for _, tc := range []struct {
  107. name string
  108. // Cap sets [tailcfg.NodeAttrClientSideReachability] on the self
  109. // node.
  110. //
  111. // When disabled, the client relies on the control plane sending
  112. // an accurate peer.Online flag. When enabled, the client
  113. // ignores peer.Online and determines whether it can reach the
  114. // peer node.
  115. cap bool
  116. peer tailcfg.Node
  117. want bool
  118. }{
  119. {
  120. name: "disabled/offline",
  121. cap: false,
  122. peer: tailcfg.Node{
  123. Online: ptr.To(false),
  124. },
  125. want: false,
  126. },
  127. {
  128. name: "disabled/online",
  129. cap: false,
  130. peer: tailcfg.Node{
  131. Online: ptr.To(true),
  132. },
  133. want: true,
  134. },
  135. {
  136. name: "enabled/offline",
  137. cap: true,
  138. peer: tailcfg.Node{
  139. Online: ptr.To(false),
  140. },
  141. want: true,
  142. },
  143. {
  144. name: "enabled/online",
  145. cap: true,
  146. peer: tailcfg.Node{
  147. Online: ptr.To(true),
  148. },
  149. want: true,
  150. },
  151. } {
  152. t.Run(tc.name, func(t *testing.T) {
  153. nb := newNodeBackend(t.Context(), tstest.WhileTestRunningLogger(t), eventbus.New())
  154. nb.netMap = &netmap.NetworkMap{}
  155. if tc.cap {
  156. nb.netMap.AllCaps.Make()
  157. nb.netMap.AllCaps.Add(tailcfg.NodeAttrClientSideReachability)
  158. }
  159. got := nb.PeerIsReachable(t.Context(), tc.peer.View())
  160. if got != tc.want {
  161. t.Errorf("got %v, want %v", got, tc.want)
  162. }
  163. })
  164. }
  165. }