healthcheck_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package healthcheck
  14. import (
  15. "encoding/json"
  16. "net"
  17. "net/http"
  18. "net/http/httptest"
  19. "testing"
  20. "time"
  21. "k8s.io/apimachinery/pkg/types"
  22. "k8s.io/apimachinery/pkg/util/clock"
  23. "k8s.io/apimachinery/pkg/util/sets"
  24. "github.com/davecgh/go-spew/spew"
  25. )
  26. type fakeListener struct {
  27. openPorts sets.String
  28. }
  29. func newFakeListener() *fakeListener {
  30. return &fakeListener{
  31. openPorts: sets.String{},
  32. }
  33. }
  34. func (fake *fakeListener) hasPort(addr string) bool {
  35. return fake.openPorts.Has(addr)
  36. }
  37. func (fake *fakeListener) Listen(addr string) (net.Listener, error) {
  38. fake.openPorts.Insert(addr)
  39. return &fakeNetListener{
  40. parent: fake,
  41. addr: addr,
  42. }, nil
  43. }
  44. type fakeNetListener struct {
  45. parent *fakeListener
  46. addr string
  47. }
  48. func (fake *fakeNetListener) Accept() (net.Conn, error) {
  49. // Not implemented
  50. return nil, nil
  51. }
  52. func (fake *fakeNetListener) Close() error {
  53. fake.parent.openPorts.Delete(fake.addr)
  54. return nil
  55. }
  56. func (fake *fakeNetListener) Addr() net.Addr {
  57. // Not implemented
  58. return nil
  59. }
  60. type fakeHTTPServerFactory struct{}
  61. func newFakeHTTPServerFactory() *fakeHTTPServerFactory {
  62. return &fakeHTTPServerFactory{}
  63. }
  64. func (fake *fakeHTTPServerFactory) New(addr string, handler http.Handler) HTTPServer {
  65. return &fakeHTTPServer{
  66. addr: addr,
  67. handler: handler,
  68. }
  69. }
  70. type fakeHTTPServer struct {
  71. addr string
  72. handler http.Handler
  73. }
  74. func (fake *fakeHTTPServer) Serve(listener net.Listener) error {
  75. return nil // Cause the goroutine to return
  76. }
  77. func mknsn(ns, name string) types.NamespacedName {
  78. return types.NamespacedName{
  79. Namespace: ns,
  80. Name: name,
  81. }
  82. }
  83. type hcPayload struct {
  84. Service struct {
  85. Namespace string
  86. Name string
  87. }
  88. LocalEndpoints int
  89. }
  90. type healthzPayload struct {
  91. LastUpdated string
  92. CurrentTime string
  93. }
  94. func TestServer(t *testing.T) {
  95. listener := newFakeListener()
  96. httpFactory := newFakeHTTPServerFactory()
  97. hcsi := NewServer("hostname", nil, listener, httpFactory)
  98. hcs := hcsi.(*server)
  99. if len(hcs.services) != 0 {
  100. t.Errorf("expected 0 services, got %d", len(hcs.services))
  101. }
  102. // sync nothing
  103. hcs.SyncServices(nil)
  104. if len(hcs.services) != 0 {
  105. t.Errorf("expected 0 services, got %d", len(hcs.services))
  106. }
  107. hcs.SyncEndpoints(nil)
  108. if len(hcs.services) != 0 {
  109. t.Errorf("expected 0 services, got %d", len(hcs.services))
  110. }
  111. // sync unknown endpoints, should be dropped
  112. hcs.SyncEndpoints(map[types.NamespacedName]int{mknsn("a", "b"): 93})
  113. if len(hcs.services) != 0 {
  114. t.Errorf("expected 0 services, got %d", len(hcs.services))
  115. }
  116. // sync a real service
  117. nsn := mknsn("a", "b")
  118. hcs.SyncServices(map[types.NamespacedName]uint16{nsn: 9376})
  119. if len(hcs.services) != 1 {
  120. t.Errorf("expected 1 service, got %d", len(hcs.services))
  121. }
  122. if hcs.services[nsn].endpoints != 0 {
  123. t.Errorf("expected 0 endpoints, got %d", hcs.services[nsn].endpoints)
  124. }
  125. if len(listener.openPorts) != 1 {
  126. t.Errorf("expected 1 open port, got %d\n%s", len(listener.openPorts), spew.Sdump(listener.openPorts))
  127. }
  128. if !listener.hasPort(":9376") {
  129. t.Errorf("expected port :9376 to be open\n%s", spew.Sdump(listener.openPorts))
  130. }
  131. // test the handler
  132. testHandler(hcs, nsn, http.StatusServiceUnavailable, 0, t)
  133. // sync an endpoint
  134. hcs.SyncEndpoints(map[types.NamespacedName]int{nsn: 18})
  135. if len(hcs.services) != 1 {
  136. t.Errorf("expected 1 service, got %d", len(hcs.services))
  137. }
  138. if hcs.services[nsn].endpoints != 18 {
  139. t.Errorf("expected 18 endpoints, got %d", hcs.services[nsn].endpoints)
  140. }
  141. // test the handler
  142. testHandler(hcs, nsn, http.StatusOK, 18, t)
  143. // sync zero endpoints
  144. hcs.SyncEndpoints(map[types.NamespacedName]int{nsn: 0})
  145. if len(hcs.services) != 1 {
  146. t.Errorf("expected 1 service, got %d", len(hcs.services))
  147. }
  148. if hcs.services[nsn].endpoints != 0 {
  149. t.Errorf("expected 0 endpoints, got %d", hcs.services[nsn].endpoints)
  150. }
  151. // test the handler
  152. testHandler(hcs, nsn, http.StatusServiceUnavailable, 0, t)
  153. // put the endpoint back
  154. hcs.SyncEndpoints(map[types.NamespacedName]int{nsn: 11})
  155. if len(hcs.services) != 1 {
  156. t.Errorf("expected 1 service, got %d", len(hcs.services))
  157. }
  158. if hcs.services[nsn].endpoints != 11 {
  159. t.Errorf("expected 18 endpoints, got %d", hcs.services[nsn].endpoints)
  160. }
  161. // sync nil endpoints
  162. hcs.SyncEndpoints(nil)
  163. if len(hcs.services) != 1 {
  164. t.Errorf("expected 1 service, got %d", len(hcs.services))
  165. }
  166. if hcs.services[nsn].endpoints != 0 {
  167. t.Errorf("expected 0 endpoints, got %d", hcs.services[nsn].endpoints)
  168. }
  169. // test the handler
  170. testHandler(hcs, nsn, http.StatusServiceUnavailable, 0, t)
  171. // put the endpoint back
  172. hcs.SyncEndpoints(map[types.NamespacedName]int{nsn: 18})
  173. if len(hcs.services) != 1 {
  174. t.Errorf("expected 1 service, got %d", len(hcs.services))
  175. }
  176. if hcs.services[nsn].endpoints != 18 {
  177. t.Errorf("expected 18 endpoints, got %d", hcs.services[nsn].endpoints)
  178. }
  179. // delete the service
  180. hcs.SyncServices(nil)
  181. if len(hcs.services) != 0 {
  182. t.Errorf("expected 0 services, got %d", len(hcs.services))
  183. }
  184. // sync multiple services
  185. nsn1 := mknsn("a", "b")
  186. nsn2 := mknsn("c", "d")
  187. nsn3 := mknsn("e", "f")
  188. nsn4 := mknsn("g", "h")
  189. hcs.SyncServices(map[types.NamespacedName]uint16{
  190. nsn1: 9376,
  191. nsn2: 12909,
  192. nsn3: 11113,
  193. })
  194. if len(hcs.services) != 3 {
  195. t.Errorf("expected 3 service, got %d", len(hcs.services))
  196. }
  197. if hcs.services[nsn1].endpoints != 0 {
  198. t.Errorf("expected 0 endpoints, got %d", hcs.services[nsn1].endpoints)
  199. }
  200. if hcs.services[nsn2].endpoints != 0 {
  201. t.Errorf("expected 0 endpoints, got %d", hcs.services[nsn2].endpoints)
  202. }
  203. if hcs.services[nsn3].endpoints != 0 {
  204. t.Errorf("expected 0 endpoints, got %d", hcs.services[nsn3].endpoints)
  205. }
  206. if len(listener.openPorts) != 3 {
  207. t.Errorf("expected 3 open ports, got %d\n%s", len(listener.openPorts), spew.Sdump(listener.openPorts))
  208. }
  209. // test the handlers
  210. testHandler(hcs, nsn1, http.StatusServiceUnavailable, 0, t)
  211. testHandler(hcs, nsn2, http.StatusServiceUnavailable, 0, t)
  212. testHandler(hcs, nsn3, http.StatusServiceUnavailable, 0, t)
  213. // sync endpoints
  214. hcs.SyncEndpoints(map[types.NamespacedName]int{
  215. nsn1: 9,
  216. nsn2: 3,
  217. nsn3: 7,
  218. })
  219. if len(hcs.services) != 3 {
  220. t.Errorf("expected 3 services, got %d", len(hcs.services))
  221. }
  222. if hcs.services[nsn1].endpoints != 9 {
  223. t.Errorf("expected 9 endpoints, got %d", hcs.services[nsn1].endpoints)
  224. }
  225. if hcs.services[nsn2].endpoints != 3 {
  226. t.Errorf("expected 3 endpoints, got %d", hcs.services[nsn2].endpoints)
  227. }
  228. if hcs.services[nsn3].endpoints != 7 {
  229. t.Errorf("expected 7 endpoints, got %d", hcs.services[nsn3].endpoints)
  230. }
  231. // test the handlers
  232. testHandler(hcs, nsn1, http.StatusOK, 9, t)
  233. testHandler(hcs, nsn2, http.StatusOK, 3, t)
  234. testHandler(hcs, nsn3, http.StatusOK, 7, t)
  235. // sync new services
  236. hcs.SyncServices(map[types.NamespacedName]uint16{
  237. //nsn1: 9376, // remove it
  238. nsn2: 12909, // leave it
  239. nsn3: 11114, // change it
  240. nsn4: 11878, // add it
  241. })
  242. if len(hcs.services) != 3 {
  243. t.Errorf("expected 3 service, got %d", len(hcs.services))
  244. }
  245. if hcs.services[nsn2].endpoints != 3 {
  246. t.Errorf("expected 3 endpoints, got %d", hcs.services[nsn2].endpoints)
  247. }
  248. if hcs.services[nsn3].endpoints != 0 {
  249. t.Errorf("expected 0 endpoints, got %d", hcs.services[nsn3].endpoints)
  250. }
  251. if hcs.services[nsn4].endpoints != 0 {
  252. t.Errorf("expected 0 endpoints, got %d", hcs.services[nsn4].endpoints)
  253. }
  254. // test the handlers
  255. testHandler(hcs, nsn2, http.StatusOK, 3, t)
  256. testHandler(hcs, nsn3, http.StatusServiceUnavailable, 0, t)
  257. testHandler(hcs, nsn4, http.StatusServiceUnavailable, 0, t)
  258. // sync endpoints
  259. hcs.SyncEndpoints(map[types.NamespacedName]int{
  260. nsn1: 9,
  261. nsn2: 3,
  262. nsn3: 7,
  263. nsn4: 6,
  264. })
  265. if len(hcs.services) != 3 {
  266. t.Errorf("expected 3 services, got %d", len(hcs.services))
  267. }
  268. if hcs.services[nsn2].endpoints != 3 {
  269. t.Errorf("expected 3 endpoints, got %d", hcs.services[nsn2].endpoints)
  270. }
  271. if hcs.services[nsn3].endpoints != 7 {
  272. t.Errorf("expected 7 endpoints, got %d", hcs.services[nsn3].endpoints)
  273. }
  274. if hcs.services[nsn4].endpoints != 6 {
  275. t.Errorf("expected 6 endpoints, got %d", hcs.services[nsn4].endpoints)
  276. }
  277. // test the handlers
  278. testHandler(hcs, nsn2, http.StatusOK, 3, t)
  279. testHandler(hcs, nsn3, http.StatusOK, 7, t)
  280. testHandler(hcs, nsn4, http.StatusOK, 6, t)
  281. // sync endpoints, missing nsn2
  282. hcs.SyncEndpoints(map[types.NamespacedName]int{
  283. nsn3: 7,
  284. nsn4: 6,
  285. })
  286. if len(hcs.services) != 3 {
  287. t.Errorf("expected 3 services, got %d", len(hcs.services))
  288. }
  289. if hcs.services[nsn2].endpoints != 0 {
  290. t.Errorf("expected 0 endpoints, got %d", hcs.services[nsn2].endpoints)
  291. }
  292. if hcs.services[nsn3].endpoints != 7 {
  293. t.Errorf("expected 7 endpoints, got %d", hcs.services[nsn3].endpoints)
  294. }
  295. if hcs.services[nsn4].endpoints != 6 {
  296. t.Errorf("expected 6 endpoints, got %d", hcs.services[nsn4].endpoints)
  297. }
  298. // test the handlers
  299. testHandler(hcs, nsn2, http.StatusServiceUnavailable, 0, t)
  300. testHandler(hcs, nsn3, http.StatusOK, 7, t)
  301. testHandler(hcs, nsn4, http.StatusOK, 6, t)
  302. }
  303. func testHandler(hcs *server, nsn types.NamespacedName, status int, endpoints int, t *testing.T) {
  304. handler := hcs.services[nsn].server.(*fakeHTTPServer).handler
  305. req, err := http.NewRequest("GET", "/healthz", nil)
  306. if err != nil {
  307. t.Fatal(err)
  308. }
  309. resp := httptest.NewRecorder()
  310. handler.ServeHTTP(resp, req)
  311. if resp.Code != status {
  312. t.Errorf("expected status code %v, got %v", status, resp.Code)
  313. }
  314. var payload hcPayload
  315. if err := json.Unmarshal(resp.Body.Bytes(), &payload); err != nil {
  316. t.Fatal(err)
  317. }
  318. if payload.Service.Name != nsn.Name || payload.Service.Namespace != nsn.Namespace {
  319. t.Errorf("expected payload name %q, got %v", nsn.String(), payload.Service)
  320. }
  321. if payload.LocalEndpoints != endpoints {
  322. t.Errorf("expected %d endpoints, got %d", endpoints, payload.LocalEndpoints)
  323. }
  324. }
  325. func TestHealthzServer(t *testing.T) {
  326. listener := newFakeListener()
  327. httpFactory := newFakeHTTPServerFactory()
  328. fakeClock := clock.NewFakeClock(time.Now())
  329. hs := newHealthzServer(listener, httpFactory, fakeClock, "127.0.0.1:10256", 10*time.Second, nil, nil)
  330. server := hs.httpFactory.New(hs.addr, healthzHandler{hs: hs})
  331. // Should return 200 "OK" by default.
  332. testHealthzHandler(server, http.StatusOK, t)
  333. // Should return 503 "ServiceUnavailable" if exceed max no respond duration.
  334. hs.UpdateTimestamp()
  335. fakeClock.Step(25 * time.Second)
  336. testHealthzHandler(server, http.StatusServiceUnavailable, t)
  337. // Should return 200 "OK" if timestamp is valid.
  338. hs.UpdateTimestamp()
  339. fakeClock.Step(5 * time.Second)
  340. testHealthzHandler(server, http.StatusOK, t)
  341. }
  342. func testHealthzHandler(server HTTPServer, status int, t *testing.T) {
  343. handler := server.(*fakeHTTPServer).handler
  344. req, err := http.NewRequest("GET", "/healthz", nil)
  345. if err != nil {
  346. t.Fatal(err)
  347. }
  348. resp := httptest.NewRecorder()
  349. handler.ServeHTTP(resp, req)
  350. if resp.Code != status {
  351. t.Errorf("expected status code %v, got %v", status, resp.Code)
  352. }
  353. var payload healthzPayload
  354. if err := json.Unmarshal(resp.Body.Bytes(), &payload); err != nil {
  355. t.Fatal(err)
  356. }
  357. }