healthcheck-passive-resty-events.t 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. #
  2. # Licensed to the Apache Software Foundation (ASF) under one or more
  3. # contributor license agreements. See the NOTICE file distributed with
  4. # this work for additional information regarding copyright ownership.
  5. # The ASF licenses this file to You under the Apache License, Version 2.0
  6. # (the "License"); you may not use this file except in compliance with
  7. # the License. You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. #
  17. BEGIN {
  18. if ($ENV{TEST_EVENTS_MODULE} ne "lua-resty-events") {
  19. $SkipReason = "Only for lua-resty-events events module";
  20. }
  21. }
  22. use Test::Nginx::Socket::Lua $SkipReason ? (skip_all => $SkipReason) : ();
  23. use t::APISIX 'no_plan';
  24. repeat_each(1);
  25. log_level('info');
  26. no_root_location();
  27. no_shuffle();
  28. worker_connections(256);
  29. run_tests();
  30. __DATA__
  31. === TEST 1: set route(passive)
  32. --- config
  33. location /t {
  34. content_by_lua_block {
  35. local t = require("lib.test_admin").test
  36. local code, body = t('/apisix/admin/routes/1',
  37. ngx.HTTP_PUT,
  38. [[{
  39. "uri": "/server_port",
  40. "upstream": {
  41. "type": "roundrobin",
  42. "nodes": {
  43. "127.0.0.1:1980": 0,
  44. "127.0.0.1:1": 1
  45. },
  46. "retries": 0,
  47. "checks": {
  48. "active": {
  49. "http_path": "/status",
  50. "host": "foo.com",
  51. "healthy": {
  52. "interval": 100,
  53. "successes": 1
  54. },
  55. "unhealthy": {
  56. "interval": 100,
  57. "http_failures": 2
  58. }
  59. },]] .. [[
  60. "passive": {
  61. "healthy": {
  62. "http_statuses": [200, 201],
  63. "successes": 3
  64. },
  65. "unhealthy": {
  66. "http_statuses": [502],
  67. "http_failures": 1,
  68. "tcp_failures": 1
  69. }
  70. }
  71. }
  72. }
  73. }]]
  74. )
  75. if code >= 300 then
  76. ngx.status = code
  77. end
  78. ngx.say(body)
  79. }
  80. }
  81. --- request
  82. GET /t
  83. --- response_body
  84. passed
  85. === TEST 2: hit routes (two healthy nodes)
  86. --- config
  87. location /t {
  88. content_by_lua_block {
  89. ngx.sleep(3) -- wait for sync
  90. local json_sort = require("toolkit.json")
  91. local http = require("resty.http")
  92. local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/server_port"
  93. local httpc = http.new()
  94. -- Since a failed request attempt triggers a passive health check to report
  95. -- a non-health condition, a request is first triggered manually here to
  96. -- trigger a passive health check to refresh the monitoring state of the build
  97. --
  98. -- The reason for this is to avoid delays in event synchronization timing due
  99. -- to non-deterministic asynchronous connections when using lua-resty-events
  100. -- as an events module.
  101. local res, err = httpc:request_uri(uri, {method = "GET", keepalive = false})
  102. if not res then
  103. ngx.say(err)
  104. return
  105. end
  106. ngx.sleep(1) -- Wait for health check unhealthy events sync
  107. local ports_count = {}
  108. for i = 1, 6 do
  109. local res, err = httpc:request_uri(uri, {method = "GET", keepalive = false})
  110. if not res then
  111. ngx.say(err)
  112. return
  113. end
  114. local status = tostring(res.status)
  115. ports_count[status] = (ports_count[status] or 0) + 1
  116. end
  117. ngx.say(json_sort.encode(ports_count))
  118. ngx.exit(200)
  119. }
  120. }
  121. --- request
  122. GET /t
  123. --- response_body
  124. {"200":5,"502":1}
  125. --- error_log
  126. (upstream#/apisix/routes/1) unhealthy HTTP increment (1/1)
  127. --- timeout: 10
  128. === TEST 3: set route(only passive)
  129. --- config
  130. location /t {
  131. content_by_lua_block {
  132. local t = require("lib.test_admin").test
  133. local code, body = t('/apisix/admin/routes/1',
  134. ngx.HTTP_PUT,
  135. [[{
  136. "uri": "/server_port",
  137. "upstream": {
  138. "type": "roundrobin",
  139. "nodes": {
  140. "127.0.0.1:1980": 0,
  141. "127.0.0.1:1": 1
  142. },
  143. "retries": 0,
  144. "checks": {
  145. "passive": {
  146. "healthy": {
  147. "http_statuses": [200, 201],
  148. "successes": 3
  149. },
  150. "unhealthy": {
  151. "http_statuses": [502],
  152. "http_failures": 1,
  153. "tcp_failures": 1
  154. }
  155. }
  156. }
  157. }
  158. }]]
  159. )
  160. if code >= 300 then
  161. ngx.status = code
  162. end
  163. ngx.print(body)
  164. }
  165. }
  166. --- request
  167. GET /t
  168. --- error_code: 400
  169. --- response_body
  170. {"error_msg":"invalid configuration: property \"upstream\" validation failed: property \"checks\" validation failed: object matches none of the required: [\"active\"] or [\"active\",\"passive\"]"}
  171. === TEST 4: set route(only active + active & passive)
  172. --- config
  173. location /t {
  174. content_by_lua_block {
  175. local t = require("lib.test_admin").test
  176. local code, body = t('/apisix/admin/routes/1',
  177. ngx.HTTP_PUT,
  178. [[{
  179. "uri": "/hello",
  180. "upstream": {
  181. "type": "roundrobin",
  182. "nodes": {
  183. "127.0.0.1:1980": 0,
  184. "127.0.0.1:1": 1
  185. },
  186. "retries": 0,
  187. "checks": {
  188. "active": {
  189. "http_path": "/status",
  190. "host": "foo.com",
  191. "healthy": {
  192. "interval": 100,
  193. "successes": 1
  194. },
  195. "unhealthy": {
  196. "interval": 100,
  197. "http_failures": 2
  198. }
  199. }
  200. }
  201. }
  202. }]]
  203. )
  204. if code >= 300 then
  205. ngx.status = code
  206. ngx.say(body)
  207. return
  208. end
  209. local code, body = t('/apisix/admin/routes/2',
  210. ngx.HTTP_PUT,
  211. [[{
  212. "uri": "/hello_",
  213. "upstream": {
  214. "type": "roundrobin",
  215. "nodes": {
  216. "127.0.0.1:1980": 0,
  217. "127.0.0.1:1": 1
  218. },
  219. "retries": 0,
  220. "checks": {
  221. "active": {
  222. "http_path": "/status",
  223. "host": "foo.com",
  224. "healthy": {
  225. "interval": 100,
  226. "successes": 1
  227. },
  228. "unhealthy": {
  229. "interval": 100,
  230. "http_failures": 2
  231. }
  232. },]] .. [[
  233. "passive": {
  234. "healthy": {
  235. "http_statuses": [200, 201],
  236. "successes": 3
  237. },
  238. "unhealthy": {
  239. "http_statuses": [502],
  240. "http_failures": 1,
  241. "tcp_failures": 1
  242. }
  243. }
  244. }
  245. }
  246. }]]
  247. )
  248. if code >= 300 then
  249. ngx.status = code
  250. end
  251. ngx.say(body)
  252. }
  253. }
  254. --- request
  255. GET /t
  256. --- response_body
  257. passed
  258. === TEST 5: only one route should have passive healthcheck
  259. --- config
  260. location /t {
  261. content_by_lua_block {
  262. local t = require("lib.test_admin").test
  263. local json_sort = require("toolkit.json")
  264. local http = require("resty.http")
  265. local uri = "http://127.0.0.1:" .. ngx.var.server_port
  266. local ports_count = {}
  267. local httpc = http.new()
  268. local res, err = httpc:request_uri(uri .. "/hello_")
  269. if not res then
  270. ngx.say(err)
  271. return
  272. end
  273. ngx.say(res.status)
  274. -- only /hello_ has passive healthcheck
  275. local res, err = httpc:request_uri(uri .. "/hello")
  276. if not res then
  277. ngx.say(err)
  278. return
  279. end
  280. ngx.say(res.status)
  281. }
  282. }
  283. --- request
  284. GET /t
  285. --- response_body
  286. 502
  287. 502
  288. --- grep_error_log eval
  289. qr/enabled healthcheck passive/
  290. --- grep_error_log_out
  291. enabled healthcheck passive
  292. === TEST 6: make sure passive healthcheck works (conf is not corrupted by the default value)
  293. --- config
  294. location /t {
  295. content_by_lua_block {
  296. local t = require("lib.test_admin").test
  297. local json_sort = require("toolkit.json")
  298. local http = require("resty.http")
  299. local uri = "http://127.0.0.1:" .. ngx.var.server_port
  300. local httpc = http.new()
  301. local res, err = httpc:request_uri(uri .. "/hello")
  302. if not res then
  303. ngx.say(err)
  304. return
  305. end
  306. ngx.say(res.status)
  307. -- The first time request to /hello_
  308. -- Ensure that the event that triggers the healthchecker to perform
  309. -- add_target has been sent and processed correctly
  310. --
  311. -- Due to the implementation of lua-resty-events, it relies on the kernel and
  312. -- the Nginx event loop to process socket connections.
  313. -- When lua-resty-healthcheck handles passive healthchecks and uses lua-resty-events
  314. -- as the events module, the synchronization of the first event usually occurs
  315. -- before the start of the passive healthcheck. So when the execution finishes and
  316. -- healthchecker tries to record the healthcheck status, it will not be able to find
  317. -- an existing target (because the synchronization event has not finished yet), which
  318. -- will lead to some anomalies that deviate from the original test case, so compatibility
  319. -- operations are performed here.
  320. local res, err = httpc:request_uri(uri .. "/hello_")
  321. if not res then
  322. ngx.say(err)
  323. return
  324. end
  325. ngx.say(res.status)
  326. ngx.sleep(1) -- Wait for health check unhealthy events sync
  327. -- The second time request to /hello_
  328. local res, err = httpc:request_uri(uri .. "/hello_")
  329. if not res then
  330. ngx.say(err)
  331. return
  332. end
  333. ngx.say(res.status)
  334. }
  335. }
  336. --- request
  337. GET /t
  338. --- response_body
  339. 502
  340. 502
  341. 502
  342. --- grep_error_log eval
  343. qr/\[healthcheck\] \([^)]+\) unhealthy HTTP increment/
  344. --- grep_error_log_out
  345. [healthcheck] (upstream#/apisix/routes/2) unhealthy HTTP increment