This is an automated email from the ASF dual-hosted git repository.

monkeydluffy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git


The following commit(s) were added to refs/heads/master by this push:
     new 809ba09b2 feat: Upstream status report (#9151)
809ba09b2 is described below

commit 809ba09b26ddd62e0efa612f85e90d1aa938ce02
Author: jinhua luo <[email protected]>
AuthorDate: Thu Mar 30 15:29:44 2023 +0800

    feat: Upstream status report (#9151)
---
 apisix/control/v1.lua                           | 156 +++++++++++++++++++----
 apisix/plugins/prometheus/exporter.lua          |  14 +++
 docs/assets/images/health_check_status_page.png | Bin 0 -> 23044 bytes
 docs/en/latest/control-api.md                   | 159 +++++++++++-------------
 docs/en/latest/plugins/prometheus.md            |  14 +++
 docs/zh/latest/control-api.md                   | 159 +++++++++++-------------
 docs/zh/latest/plugins/prometheus.md            |  11 ++
 rockspec/apisix-master-0.rockspec               |   2 +-
 t/control/healthcheck.t                         | 114 ++++++-----------
 t/discovery/consul.t                            |  10 +-
 t/discovery/consul_kv.t                         |  24 ++--
 11 files changed, 377 insertions(+), 286 deletions(-)

diff --git a/apisix/control/v1.lua b/apisix/control/v1.lua
index fd031a473..3143ae594 100644
--- a/apisix/control/v1.lua
+++ b/apisix/control/v1.lua
@@ -14,6 +14,7 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 --
+local require = require
 local core = require("apisix.core")
 local plugin = require("apisix.plugin")
 local get_routes = require("apisix.router").http_routes
@@ -22,6 +23,7 @@ local upstream_mod = require("apisix.upstream")
 local get_upstreams = upstream_mod.upstreams
 local collectgarbage = collectgarbage
 local ipairs = ipairs
+local pcall = pcall
 local str_format = string.format
 local ngx_var = ngx.var
 
@@ -62,52 +64,137 @@ function _M.schema()
 end
 
 
-local function extra_checker_info(value, src_type)
-    local checker = value.checker
-    local upstream = value.checker_upstream
-    local host = upstream.checks and upstream.checks.active and 
upstream.checks.active.host
-    local port = upstream.checks and upstream.checks.active and 
upstream.checks.active.port
-    local nodes = upstream.nodes
-    local healthy_nodes = core.table.new(#nodes, 0)
-    for _, node in ipairs(nodes) do
-        local ok = checker:get_target_status(node.host, port or node.port, 
host)
-        if ok then
-            core.table.insert(healthy_nodes, node)
-        end
+local healthcheck
+local function extra_checker_info(value)
+    if not healthcheck then
+        healthcheck = require("resty.healthcheck")
     end
 
-    local conf = value.value
+    local name = upstream_mod.get_healthchecker_name(value)
+    local nodes, err = healthcheck.get_target_list(name, 
"upstream-healthcheck")
+    if err then
+        core.log.error("healthcheck.get_target_list failed: ", err)
+    end
     return {
-        name = upstream_mod.get_healthchecker_name(value),
-        src_id = conf.id,
-        src_type = src_type,
+        name = value.key,
         nodes = nodes,
-        healthy_nodes = healthy_nodes,
     }
 end
 
 
-local function iter_and_add_healthcheck_info(infos, values, src_type)
+local function get_checker_type(checks)
+    if checks.active and checks.active.type then
+        return checks.active.type
+    elseif checks.passive and checks.passive.type then
+        return checks.passive.type
+    end
+end
+
+
+local function iter_and_add_healthcheck_info(infos, values)
     if not values then
         return
     end
 
     for _, value in core.config_util.iterate_values(values) do
-        if value.checker then
-            core.table.insert(infos, extra_checker_info(value, src_type))
+        local checks = value.value.checks or (value.value.upstream and 
value.value.upstream.checks)
+        if checks then
+            local info = extra_checker_info(value)
+            info.type = get_checker_type(checks)
+            core.table.insert(infos, info)
         end
     end
 end
 
 
-function _M.get_health_checkers()
+local HTML_TEMPLATE = [[
+<html xmlns="http://www.w3.org/1999/xhtml";>
+<head>
+  <title>APISIX upstream check status</title>
+</head>
+<body>
+<h1>APISIX upstream check status</h1>
+<table style="background-color:white" cellspacing="0" cellpadding="3" 
border="1">
+  <tr bgcolor="#C0C0C0">
+    <th>Index</th>
+    <th>Upstream</th>
+    <th>Check type</th>
+    <th>Host</th>
+    <th>Status</th>
+    <th>Success counts</th>
+    <th>TCP Failures</th>
+    <th>HTTP Failures</th>
+    <th>TIMEOUT Failures</th>
+  </tr>
+{% local i = 0 %}
+{% for _, stat in ipairs(stats) do %}
+{% for _, node in ipairs(stat.nodes) do %}
+{% i = i + 1 %}
+  {% if node.status == "healthy" then %}
+  <tr>
+  {% else %}
+  <tr bgcolor="#FF0000">
+  {% end %}
+    <td>{* i *}</td>
+    <td>{* stat.name *}</td>
+    <td>{* stat.type *}</td>
+    <td>{* node.ip .. ":" .. node.port *}</td>
+    <td>{* node.status *}</td>
+    <td>{* node.counter.success *}</td>
+    <td>{* node.counter.tcp_failure *}</td>
+    <td>{* node.counter.http_failure *}</td>
+    <td>{* node.counter.timeout_failure *}</td>
+  </tr>
+{% end %}
+{% end %}
+</table>
+</body>
+</html>
+]]
+
+local html_render
+
+local function try_render_html(data)
+    if not html_render then
+        local template = require("resty.template")
+        html_render = template.compile(HTML_TEMPLATE)
+    end
+    local accept = ngx_var.http_accept
+    if accept and accept:find("text/html") then
+        local ok, out = pcall(html_render, data)
+        if not ok then
+            local err = str_format("HTML template rendering: %s", out)
+            core.log.error(err)
+            return nil, err
+        end
+        return out
+    end
+end
+
+
+local function _get_health_checkers()
     local infos = {}
     local routes = get_routes()
-    iter_and_add_healthcheck_info(infos, routes, "routes")
+    iter_and_add_healthcheck_info(infos, routes)
     local services = get_services()
-    iter_and_add_healthcheck_info(infos, services, "services")
+    iter_and_add_healthcheck_info(infos, services)
     local upstreams = get_upstreams()
-    iter_and_add_healthcheck_info(infos, upstreams, "upstreams")
+    iter_and_add_healthcheck_info(infos, upstreams)
+    return infos
+end
+
+
+function _M.get_health_checkers()
+    local infos = _get_health_checkers()
+    local out, err = try_render_html({stats=infos})
+    if out then
+        core.response.set_header("Content-Type", "text/html")
+        return 200, out
+    end
+    if err then
+        return 503, {error_msg = err}
+    end
+
     return 200, infos
 end
 
@@ -119,11 +206,15 @@ local function iter_and_find_healthcheck_info(values, 
src_type, src_id)
 
     for _, value in core.config_util.iterate_values(values) do
         if value.value.id == src_id then
-            if not value.checker then
+            local checks = value.value.checks or
+                (value.value.upstream and value.value.upstream.checks)
+            if not checks then
                 return nil, str_format("no checker for %s[%s]", src_type, 
src_id)
             end
 
-            return extra_checker_info(value, src_type)
+            local info = extra_checker_info(value)
+            info.type = get_checker_type(checks)
+            return info
         end
     end
 
@@ -155,6 +246,16 @@ function _M.get_health_checker()
     if not info then
         return 404, {error_msg = err}
     end
+
+    local out, err = try_render_html({stats={info}})
+    if out then
+        core.response.set_header("Content-Type", "text/html")
+        return 200, out
+    end
+    if err then
+        return 503, {error_msg = err}
+    end
+
     return 200, info
 end
 
@@ -372,5 +473,6 @@ return {
         methods = {"GET"},
         uris = {"/plugin_metadata/*"},
         handler = _M.dump_plugin_metadata,
-    }
+    },
+    get_health_checkers = _get_health_checkers,
 }
diff --git a/apisix/plugins/prometheus/exporter.lua 
b/apisix/plugins/prometheus/exporter.lua
index 45ff94c3f..1cb4a534c 100644
--- a/apisix/plugins/prometheus/exporter.lua
+++ b/apisix/plugins/prometheus/exporter.lua
@@ -17,6 +17,7 @@
 local base_prometheus = require("prometheus")
 local core      = require("apisix.core")
 local plugin    = require("apisix.plugin")
+local control   = require("apisix.control.v1")
 local ipairs    = ipairs
 local pairs     = pairs
 local ngx       = ngx
@@ -158,6 +159,10 @@ function _M.http_init(prometheus_enabled_in_stream)
             "The free space of each nginx shared DICT since APISIX start",
             {"name"})
 
+    metrics.upstream_status = prometheus:gauge("upstream_status",
+            "Upstream status from health check",
+            {"name", "ip", "port"})
+
     -- per service
 
     -- The consumer label indicates the name of consumer corresponds to the
@@ -458,6 +463,15 @@ local function collect(ctx, stream_only)
 
     metrics.node_info:set(1, gen_arr(hostname))
 
+    -- update upstream_status metrics
+    local stats = control.get_health_checkers()
+    for _, stat in ipairs(stats) do
+        for _, node in ipairs(stat.nodes) do
+            metrics.upstream_status:set((node.status == "healthy") and 1 or 0,
+                gen_arr(stat.name, node.ip, node.port))
+        end
+    end
+
     core.response.set_header("content_type", "text/plain")
     return 200, core.table.concat(prometheus:metric_data())
 end
diff --git a/docs/assets/images/health_check_status_page.png 
b/docs/assets/images/health_check_status_page.png
new file mode 100644
index 000000000..ed4aebead
Binary files /dev/null and b/docs/assets/images/health_check_status_page.png 
differ
diff --git a/docs/en/latest/control-api.md b/docs/en/latest/control-api.md
index c6944f2b5..a068d4411 100644
--- a/docs/en/latest/control-api.md
+++ b/docs/en/latest/control-api.md
@@ -98,71 +98,50 @@ Returns a [health check](./tutorials/health-check.md) of 
the APISIX instance.
 
 ```json
 [
-    {
-        "healthy_nodes": [
-            {
-                "host": "127.0.0.1",
-                "port": 1980,
-                "priority": 0,
-                "weight": 1
-            }
-        ],
-        "name": "upstream#/upstreams/1",
-        "nodes": [
-            {
-                "host": "127.0.0.1",
-                "port": 1980,
-                "priority": 0,
-                "weight": 1
-            },
-            {
-                "host": "127.0.0.2",
-                "port": 1988,
-                "priority": 0,
-                "weight": 1
-            }
-        ],
-        "src_id": "1",
-        "src_type": "upstreams"
-    },
-    {
-        "healthy_nodes": [
-            {
-                "host": "127.0.0.1",
-                "port": 1980,
-                "priority": 0,
-                "weight": 1
-            }
-        ],
-        "name": "upstream#/routes/1",
-        "nodes": [
-            {
-                "host": "127.0.0.1",
-                "port": 1980,
-                "priority": 0,
-                "weight": 1
-            },
-            {
-                "host": "127.0.0.1",
-                "port": 1988,
-                "priority": 0,
-                "weight": 1
-            }
-        ],
-        "src_id": "1",
-        "src_type": "routes"
-    }
+  {
+    "nodes": [
+      {
+        "ip": "52.86.68.46",
+        "counter": {
+          "http_failure": 0,
+          "success": 0,
+          "timeout_failure": 0,
+          "tcp_failure": 0
+        },
+        "port": 80,
+        "status": "healthy"
+      },
+      {
+        "ip": "100.24.156.8",
+        "counter": {
+          "http_failure": 5,
+          "success": 0,
+          "timeout_failure": 0,
+          "tcp_failure": 0
+        },
+        "port": 80,
+        "status": "unhealthy"
+      }
+    ],
+    "name": "/apisix/routes/1",
+    "type": "http"
+  }
 ]
+
 ```
 
 Each of the returned objects contain the following fields:
 
-* src_type: where the health checker is reporting from. Value is one of  
`["routes", "services", "upstreams"]`.
-* src_id: id of the object creating the health checker. For example, if an 
Upstream
-object with id `1` creates a health checker, the `src_type` is `upstreams` and 
the `src_id` is `1`.
-* name: name of the health checker.
+* name: resource id, where the health checker is reporting from.
+* type: health check type: `["http", "https", "tcp"]`.
 * nodes: target nodes of the health checker.
-* healthy_nodes: healthy nodes discovered by the health checker.
+* nodes[i].ip: ip address.
+* nodes[i].port: port number.
+* nodes[i].status: health check result: `["healthy", "unhealthy", 
"mostly_healthy", "mostly_unhealthy"]`.
+* nodes[i].counter.success: success health check count.
+* nodes[i].counter.http_failure: http failures count.
+* nodes[i].counter.tcp_failure: tcp connect/read/write failures count.
+* nodes[i].counter.timeout_failure: timeout count.
 
 You can also use `/v1/healthcheck/$src_type/$src_id` to get the health status 
of specific nodes.
 
@@ -170,40 +149,50 @@ For example, `GET /v1/healthcheck/upstreams/1` returns:
 
 ```json
 {
-    "healthy_nodes": [
-        {
-            "host": "127.0.0.1",
-            "port": 1980,
-            "priority": 0,
-            "weight": 1
-        }
-    ],
-    "name": "upstream#/upstreams/1",
-    "nodes": [
-        {
-            "host": "127.0.0.1",
-            "port": 1980,
-            "priority": 0,
-            "weight": 1
-        },
-        {
-            "host": "127.0.0.2",
-            "port": 1988,
-            "priority": 0,
-            "weight": 1
-        }
-    ],
-    "src_id": "1",
-    "src_type": "upstreams"
+  "nodes": [
+    {
+      "ip": "52.86.68.46",
+      "counter": {
+        "http_failure": 0,
+        "success": 2,
+        "timeout_failure": 0,
+        "tcp_failure": 0
+      },
+      "port": 80,
+      "status": "healthy"
+    },
+    {
+      "ip": "100.24.156.8",
+      "counter": {
+        "http_failure": 5,
+        "success": 0,
+        "timeout_failure": 0,
+        "tcp_failure": 0
+      },
+      "port": 80,
+      "status": "unhealthy"
+    }
+  ],
+  "type": "http"
+  "name": "/apisix/routes/1"
 }
+
 ```
 
 :::note
 
-As APISIX uses multiple-process architecture, if the process never handles the 
request of a specific upstream, then the upstream's health check information 
will not appear on the process. This may result in the health check API can't 
get all data during testing.
+Only when one upstream is satisfied by the conditions below,
+its status is shown in the result list:
+
+* The upstream is configured with a health checker
+* The upstream has served requests in any worker process
 
 :::
 
+If you use browser to access the control API URL, then you will get the HTML 
output:
+
+![Health Check Status 
Page](https://raw.githubusercontent.com/apache/apisix/master/docs/assets/images/health_check_status_page.png)
+
 ### POST /v1/gc
 
 Introduced in [v2.8](https://github.com/apache/apisix/releases/tag/2.8).
diff --git a/docs/en/latest/plugins/prometheus.md 
b/docs/en/latest/plugins/prometheus.md
index eb9ca6c83..9f93f9a24 100644
--- a/docs/en/latest/plugins/prometheus.md
+++ b/docs/en/latest/plugins/prometheus.md
@@ -235,6 +235,16 @@ The following metrics are exported by the `prometheus` 
Plugin:
 - Info: Information about the APISIX node.
 - Shared dict: The capacity and free space of all nginx.shared.DICT in APISIX.
 
+- `apisix_upstream_status`: Health check result status of upstream nodes. A 
value of `1` represents healthy and `0` represents unhealthy.
+
+  The available attributes are:
+
+  | Name         | Description                                                 
                                                                  |
+  
|--------------|-------------------------------------------------------------------------------------------------------------------------------|
+  | name         | resource id where the upstream node is attached to, e.g. 
`/apisix/routes/1`, `/apisix/upstreams/1`.                                      
                                      |
+  | ip        | ip address of the node.                          |
+  | port  | port number of the node.                               |
+
 Here are the original metrics from APISIX:
 
 ```shell
@@ -323,6 +333,10 @@ 
apisix_shared_dict_free_space_bytes{name="balancer-ewma-locks"} 10412032
 apisix_shared_dict_free_space_bytes{name="discovery"} 1032192
 apisix_shared_dict_free_space_bytes{name="etcd-cluster-health-check"} 10412032
 ...
+# HELP apisix_upstream_status Upstream status from health check
+# TYPE apisix_upstream_status gauge
+apisix_upstream_status{name="/apisix/routes/1",ip="100.24.156.8",port="80"} 0
+apisix_upstream_status{name="/apisix/routes/1",ip="52.86.68.46",port="80"} 1
 ```
 
 ## Disable Plugin
diff --git a/docs/zh/latest/control-api.md b/docs/zh/latest/control-api.md
index f257789e7..eeb61b5b8 100644
--- a/docs/zh/latest/control-api.md
+++ b/docs/zh/latest/control-api.md
@@ -96,70 +96,50 @@ APISIX 中一些插件添加了自己的 control API。如果你对他们感兴
 
 ```json
 [
-    {
-        "healthy_nodes": [
-            {
-                "host": "127.0.0.1",
-                "port": 1980,
-                "priority": 0,
-                "weight": 1
-            }
-        ],
-        "name": "upstream#/upstreams/1",
-        "nodes": [
-            {
-                "host": "127.0.0.1",
-                "port": 1980,
-                "priority": 0,
-                "weight": 1
-            },
-            {
-                "host": "127.0.0.2",
-                "port": 1988,
-                "priority": 0,
-                "weight": 1
-            }
-        ],
-        "src_id": "1",
-        "src_type": "upstreams"
-    },
-    {
-        "healthy_nodes": [
-            {
-                "host": "127.0.0.1",
-                "port": 1980,
-                "priority": 0,
-                "weight": 1
-            }
-        ],
-        "name": "upstream#/routes/1",
-        "nodes": [
-            {
-                "host": "127.0.0.1",
-                "port": 1980,
-                "priority": 0,
-                "weight": 1
-            },
-            {
-                "host": "127.0.0.1",
-                "port": 1988,
-                "priority": 0,
-                "weight": 1
-            }
-        ],
-        "src_id": "1",
-        "src_type": "routes"
-    }
+  {
+    "nodes": [
+      {
+        "ip": "52.86.68.46",
+        "counter": {
+          "http_failure": 0,
+          "success": 0,
+          "timeout_failure": 0,
+          "tcp_failure": 0
+        },
+        "port": 80,
+        "status": "healthy"
+      },
+      {
+        "ip": "100.24.156.8",
+        "counter": {
+          "http_failure": 5,
+          "success": 0,
+          "timeout_failure": 0,
+          "tcp_failure": 0
+        },
+        "port": 80,
+        "status": "unhealthy"
+      }
+    ],
+    "name": "/apisix/routes/1",
+    "type": "http"
+  }
 ]
+
 ```
 
 每个 entry 包含以下字段:
 
-* src_type:表示 health checker 的来源。值是 `[routes,services,upstreams]` 其中之一
-* src_id:表示创建 health checker 的对象的 id。例如,假设 id 为 1 的 Upstream 对象创建了一个 health 
checker,那么 `src_type` 就是 `upstreams`,`src_id` 就是 1
-* name:表示 health checker 的名称
-* nodes:health checker 的目标节点
-* healthy_nodes:表示 health checker 检测到的健康节点
+* name: 资源 ID,健康检查的报告对象。
+* type: 健康检查类型,取值为 `["http", "https", "tcp"]`。
+* nodes: 检查节点列表。
+* nodes[i].ip: IP 地址。
+* nodes[i].port: 端口。
+* nodes[i].status: 状态:`["healthy", "unhealthy", "mostly_healthy", 
"mostly_unhealthy"]`。
+* nodes[i].counter.success: 成功计数器。
+* nodes[i].counter.http_failure: HTTP 访问失败计数器。
+* nodes[i].counter.tcp_failure: TCP 连接或读写的失败计数器。
+* nodes[i].counter.timeout_failure: 超时计数器。
 
 用户也可以通过 `/v1/healthcheck/$src_type/$src_id` 来获取指定 health checker 的状态。
 
@@ -167,40 +147,49 @@ APISIX 中一些插件添加了自己的 control API。如果你对他们感兴
 
 ```json
 {
-    "healthy_nodes": [
-        {
-            "host": "127.0.0.1",
-            "port": 1980,
-            "priority": 0,
-            "weight": 1
-        }
-    ],
-    "name": "upstream#/upstreams/1",
-    "nodes": [
-        {
-            "host": "127.0.0.1",
-            "port": 1980,
-            "priority": 0,
-            "weight": 1
-        },
-        {
-            "host": "127.0.0.2",
-            "port": 1988,
-            "priority": 0,
-            "weight": 1
-        }
-    ],
-    "src_id": "1",
-    "src_type": "upstreams"
+  "nodes": [
+    {
+      "ip": "52.86.68.46",
+      "counter": {
+        "http_failure": 0,
+        "success": 2,
+        "timeout_failure": 0,
+        "tcp_failure": 0
+      },
+      "port": 80,
+      "status": "healthy"
+    },
+    {
+      "ip": "100.24.156.8",
+      "counter": {
+        "http_failure": 5,
+        "success": 0,
+        "timeout_failure": 0,
+        "tcp_failure": 0
+      },
+      "port": 80,
+      "status": "unhealthy"
+    }
+  ],
+  "type": "http"
+  "name": "/apisix/routes/1"
 }
+
 ```
 
 :::note
 
-由于 APISIX 采用多进程架构,如果该进程从来没有处理特定上游的请求,则上游的健康检查信息不会出现在该进程上。这可能会导致健康检查 API 
在测试期间无法获取所有数据。
+只有一个上游满足以下条件时,它的健康检查状态才会出现在结果里面:
+
+* 上游配置了健康检查。
+* 上游在任何一个 worker 进程处理过客户端请求。
 
 :::
 
+如果你使用浏览器访问该 API,你将得到一个网页:
+
+![Health Check Status 
Page](https://raw.githubusercontent.com/apache/apisix/master/docs/assets/images/health_check_status_page.png)
+
 ### POST /v1/gc
 
 引入自 2.8 版本
diff --git a/docs/zh/latest/plugins/prometheus.md 
b/docs/zh/latest/plugins/prometheus.md
index 8afc3fe65..b63d7f970 100644
--- a/docs/zh/latest/plugins/prometheus.md
+++ b/docs/zh/latest/plugins/prometheus.md
@@ -208,6 +208,13 @@ scrape_configs:
 
 - Info: 当前 APISIX 节点信息。
 - Shared dict: APISIX 中所有共享内存的容量以及剩余可用空间。
+- `apisix_upstream_status`: 上游健康检查的节点状态,`1` 表示健康,`0` 表示不健康。属性如下所示:
+
+  | 名称         | 描述                                                            
                                                       |
+  
|--------------|-------------------------------------------------------------------------------------------------------------------------------|
+  | name         | 上游所依附的资源 ID,例如 `/apisix/routes/1`, `/apisix/upstreams/1`.   
                                                                         |
+  | ip        | 上游节点的 IP 地址。                          |
+  | port  | 上游节点的端口号。                               |
 
 以下是 APISIX 的原始的指标数据集:
 
@@ -297,6 +304,10 @@ 
apisix_shared_dict_free_space_bytes{name="balancer-ewma-locks"} 10412032
 apisix_shared_dict_free_space_bytes{name="discovery"} 1032192
 apisix_shared_dict_free_space_bytes{name="etcd-cluster-health-check"} 10412032
 ...
+# HELP apisix_upstream_status Upstream status from health check
+# TYPE apisix_upstream_status gauge
+apisix_upstream_status{name="/apisix/routes/1",ip="100.24.156.8",port="80"} 0
+apisix_upstream_status{name="/apisix/routes/1",ip="52.86.68.46",port="80"} 1
 ```
 
 ## 禁用插件
diff --git a/rockspec/apisix-master-0.rockspec 
b/rockspec/apisix-master-0.rockspec
index 8a73fd59c..f11c57c3d 100644
--- a/rockspec/apisix-master-0.rockspec
+++ b/rockspec/apisix-master-0.rockspec
@@ -39,7 +39,7 @@ dependencies = {
     "lua-resty-balancer = 0.04",
     "lua-resty-ngxvar = 0.5.2",
     "lua-resty-jit-uuid = 0.0.7",
-    "lua-resty-healthcheck-api7 = 2.2.2",
+    "lua-resty-healthcheck-api7 = 2.2.3",
     "api7-lua-resty-jwt = 0.2.4",
     "lua-resty-hmac-ffi = 0.05",
     "lua-resty-cookie = 0.1.0",
diff --git a/t/control/healthcheck.t b/t/control/healthcheck.t
index 3c9cefff8..5d40e9707 100644
--- a/t/control/healthcheck.t
+++ b/t/control/healthcheck.t
@@ -67,6 +67,7 @@ upstreams:
 --- config
     location /t {
         content_by_lua_block {
+            local core = require("apisix.core")
             local json = require("toolkit.json")
             local t = require("lib.test_admin")
             local http = require "resty.http"
@@ -76,21 +77,44 @@ upstreams:
 
             ngx.sleep(2.2)
 
-            local code, body, res = t.test('/v1/healthcheck',
+            local _, _, res = t.test('/v1/healthcheck',
                 ngx.HTTP_GET)
             res = json.decode(res)
+            assert(#res == 1, "invalid number of results")
             table.sort(res[1].nodes, function(a, b)
-                return a.host < b.host
+                return a.ip < b.ip
             end)
-            ngx.say(json.encode(res))
+            ngx.say(core.json.stably_encode(res[1].nodes))
 
-            local code, body, res = t.test('/v1/healthcheck/upstreams/1',
+            local _, _, res = t.test('/v1/healthcheck/upstreams/1',
                 ngx.HTTP_GET)
             res = json.decode(res)
             table.sort(res.nodes, function(a, b)
-                return a.host < b.host
+                return a.ip < b.ip
             end)
-            ngx.say(json.encode(res))
+            ngx.say(core.json.stably_encode(res.nodes))
+
+            local _, _, res = t.test('/v1/healthcheck/upstreams/1',
+                ngx.HTTP_GET, nil, nil, {["Accept"] = "text/html"})
+            local xml2lua = require("xml2lua")
+            local xmlhandler = require("xmlhandler.tree")
+            local handler = xmlhandler:new()
+            local parser = xml2lua.parser(handler)
+            parser.parse(parser, res)
+            local matches = 0
+            for _, td in ipairs(handler.root.html.body.table.tr) do
+                if td.td then
+                    if td.td[4] == "127.0.0.2:1988" then
+                        assert(td.td[5] == "unhealthy", "127.0.0.2:1988 is not 
unhealthy")
+                        matches = matches + 1
+                    end
+                    if td.td[4] == "127.0.0.1:1980" then
+                        assert(td.td[5] == "healthy", "127.0.0.1:1980 is not 
healthy")
+                        matches = matches + 1
+                    end
+                end
+            end
+            assert(matches == 2, "unexpected html")
         }
     }
 --- grep_error_log eval
@@ -99,8 +123,8 @@ qr/unhealthy TCP increment \(.+\) for '[^']+'/
 unhealthy TCP increment (1/2) for '(127.0.0.2:1988)'
 unhealthy TCP increment (2/2) for '(127.0.0.2:1988)'
 --- response_body
-[{"healthy_nodes":[{"host":"127.0.0.1","port":1980,"priority":0,"weight":1}],"name":"upstream#/upstreams/1","nodes":[{"host":"127.0.0.1","port":1980,"priority":0,"weight":1},{"host":"127.0.0.2","port":1988,"priority":0,"weight":1}],"src_id":"1","src_type":"upstreams"}]
-{"healthy_nodes":[{"host":"127.0.0.1","port":1980,"priority":0,"weight":1}],"name":"upstream#/upstreams/1","nodes":[{"host":"127.0.0.1","port":1980,"priority":0,"weight":1},{"host":"127.0.0.2","port":1988,"priority":0,"weight":1}],"src_id":"1","src_type":"upstreams"}
+[{"counter":{"http_failure":0,"success":0,"tcp_failure":0,"timeout_failure":0},"ip":"127.0.0.1","port":1980,"status":"healthy"},{"counter":{"http_failure":0,"success":0,"tcp_failure":2,"timeout_failure":0},"ip":"127.0.0.2","port":1988,"status":"unhealthy"}]
+[{"counter":{"http_failure":0,"success":0,"tcp_failure":0,"timeout_failure":0},"ip":"127.0.0.1","port":1980,"status":"healthy"},{"counter":{"http_failure":0,"success":0,"tcp_failure":2,"timeout_failure":0},"ip":"127.0.0.2","port":1988,"status":"unhealthy"}]
 
 
 
@@ -169,8 +193,8 @@ qr/unhealthy TCP increment \(.+\) for '[^']+'/
 unhealthy TCP increment (1/2) for '127.0.0.1(127.0.0.1:1988)'
 unhealthy TCP increment (2/2) for '127.0.0.1(127.0.0.1:1988)'
 --- response_body
-[{"healthy_nodes":[{"host":"127.0.0.1","port":1980,"priority":0,"weight":1}],"name":"upstream#/routes/1","nodes":[{"host":"127.0.0.1","port":1980,"priority":0,"weight":1},{"host":"127.0.0.1","port":1988,"priority":0,"weight":1}],"src_id":"1","src_type":"routes"}]
-{"healthy_nodes":[{"host":"127.0.0.1","port":1980,"priority":0,"weight":1}],"name":"upstream#/routes/1","nodes":[{"host":"127.0.0.1","port":1980,"priority":0,"weight":1},{"host":"127.0.0.1","port":1988,"priority":0,"weight":1}],"src_id":"1","src_type":"routes"}
+[{"name":"/routes/1","nodes":[{"counter":{"http_failure":0,"success":0,"tcp_failure":0,"timeout_failure":0},"hostname":"127.0.0.1","ip":"127.0.0.1","port":1980,"status":"healthy"},{"counter":{"http_failure":0,"success":0,"tcp_failure":2,"timeout_failure":0},"hostname":"127.0.0.1","ip":"127.0.0.1","port":1988,"status":"unhealthy"}],"type":"http"}]
+{"name":"/routes/1","nodes":[{"counter":{"http_failure":0,"success":0,"tcp_failure":0,"timeout_failure":0},"hostname":"127.0.0.1","ip":"127.0.0.1","port":1980,"status":"healthy"},{"counter":{"http_failure":0,"success":0,"tcp_failure":2,"timeout_failure":0},"hostname":"127.0.0.1","ip":"127.0.0.1","port":1988,"status":"unhealthy"}],"type":"http"}
 
 
 
@@ -244,8 +268,8 @@ qr/unhealthy TCP increment \(.+\) for '[^']+'/
 unhealthy TCP increment (1/2) for '127.0.0.1(127.0.0.1:1988)'
 unhealthy TCP increment (2/2) for '127.0.0.1(127.0.0.1:1988)'
 --- response_body
-[{"healthy_nodes":{},"name":"upstream#/services/1","nodes":[{"host":"127.0.0.1","port":1980,"priority":0,"weight":1},{"host":"127.0.0.1","port":1988,"priority":0,"weight":1}],"src_id":"1","src_type":"services"}]
-{"healthy_nodes":{},"name":"upstream#/services/1","nodes":[{"host":"127.0.0.1","port":1980,"priority":0,"weight":1},{"host":"127.0.0.1","port":1988,"priority":0,"weight":1}],"src_id":"1","src_type":"services"}
+[{"name":"/services/1","nodes":[{"counter":{"http_failure":0,"success":0,"tcp_failure":2,"timeout_failure":0},"hostname":"127.0.0.1","ip":"127.0.0.1","port":1988,"status":"unhealthy"}],"type":"http"}]
+{"name":"/services/1","nodes":[{"counter":{"http_failure":0,"success":0,"tcp_failure":2,"timeout_failure":0},"hostname":"127.0.0.1","ip":"127.0.0.1","port":1988,"status":"unhealthy"}],"type":"http"}
 
 
 
@@ -279,69 +303,3 @@ GET /v1/healthcheck/route/1
 --- error_code: 400
 --- response_body
 {"error_msg":"invalid src type route"}
-
-
-
-=== TEST 7: default health status
---- yaml_config
-apisix:
-    node_listen: 1984
-deployment:
-    role: data_plane
-    role_data_plane:
-        config_provider: yaml
---- apisix_yaml
-routes:
-  -
-    uris:
-        - /hello
-    upstream_id: 1
-upstreams:
-    - nodes:
-        "127.0.0.1:1988": 1
-        "127.0.0.2:1980": 1
-      type: chash
-      id: 1
-      key: "uri"
-      checks:
-        active:
-            http_path: "/status"
-            healthy:
-                interval: 1
-                successes: 1
-            unhealthy:
-                interval: 1
-                http_failures: 1
-#END
---- config
-    location /t {
-        content_by_lua_block {
-            local json = require("toolkit.json")
-            local t = require("lib.test_admin")
-
-            -- not hit
-            local code, body, res = t.test('/v1/healthcheck',
-                ngx.HTTP_GET)
-            ngx.print(res)
-
-            -- hit, but no enough to mark node to unhealthy
-            local http = require "resty.http"
-            local uri = "http://127.0.0.1:"; .. ngx.var.server_port .. "/hello"
-            local httpc = http.new()
-            local res, err = httpc:request_uri(uri, {method = "GET"})
-            local code, body, res = t.test('/v1/healthcheck',
-                ngx.HTTP_GET)
-            res = json.decode(res)
-            table.sort(res[1].healthy_nodes, function(a, b)
-                return a.host < b.host
-            end)
-            ngx.say(json.encode(res[1].healthy_nodes))
-        }
-    }
---- grep_error_log eval
-qr/unhealthy TCP increment \(.+\) for '[^']+'/
---- grep_error_log_out
-unhealthy TCP increment (1/2) for '(127.0.0.1:1988)'
---- response_body
-{}
-[{"host":"127.0.0.1","port":1988,"priority":0,"weight":1},{"host":"127.0.0.2","port":1980,"priority":0,"weight":1}]
diff --git a/t/discovery/consul.t b/t/discovery/consul.t
index 39c5ab287..cf97e0ce8 100644
--- a/t/discovery/consul.t
+++ b/t/discovery/consul.t
@@ -558,6 +558,9 @@ upstreams:
             table.sort(nodes, function(a, b)
                 return a.port < b.port
             end)
+            for _, node in ipairs(nodes) do
+                node.counter = nil
+            end
             ngx.say(json.encode(nodes))
 
             local code, body, res = t.test('/v1/healthcheck/upstreams/1',
@@ -567,12 +570,15 @@ upstreams:
             table.sort(nodes, function(a, b)
                 return a.port < b.port
             end)
+            for _, node in ipairs(nodes) do
+                node.counter = nil
+            end
             ngx.say(json.encode(nodes))
         }
     }
 --- request
 GET /thc
 --- response_body
-[{"host":"127.0.0.1","port":30513,"priority":0,"weight":1},{"host":"127.0.0.1","port":30514,"priority":0,"weight":1}]
-[{"host":"127.0.0.1","port":30513,"priority":0,"weight":1},{"host":"127.0.0.1","port":30514,"priority":0,"weight":1}]
+[{"ip":"127.0.0.1","port":30513,"status":"healthy"},{"ip":"127.0.0.1","port":30514,"status":"healthy"}]
+[{"ip":"127.0.0.1","port":30513,"status":"healthy"},{"ip":"127.0.0.1","port":30514,"status":"healthy"}]
 --- ignore_error_log
diff --git a/t/discovery/consul_kv.t b/t/discovery/consul_kv.t
index da557d1d0..9363f768d 100644
--- a/t/discovery/consul_kv.t
+++ b/t/discovery/consul_kv.t
@@ -425,25 +425,33 @@ upstreams:
             local code, body, res = t.test('/v1/healthcheck',
                 ngx.HTTP_GET)
             res = json.decode(res)
-            table.sort(res[1].nodes, function(a, b)
-                return a.host < b.host
+            local nodes = res[1].nodes
+            table.sort(nodes, function(a, b)
+                return a.ip < b.ip
             end)
-            ngx.say(json.encode(res))
+            for _, node in ipairs(nodes) do
+                node.counter = nil
+            end
+            ngx.say(json.encode(nodes))
 
             local code, body, res = t.test('/v1/healthcheck/upstreams/1',
                 ngx.HTTP_GET)
             res = json.decode(res)
-            table.sort(res.nodes, function(a, b)
-                return a.host < b.host
+            local nodes = res.nodes
+            table.sort(nodes, function(a, b)
+                return a.ip < b.ip
             end)
-            ngx.say(json.encode(res))
+            for _, node in ipairs(nodes) do
+                node.counter = nil
+            end
+            ngx.say(json.encode(nodes))
         }
     }
 --- request
 GET /thc
 --- response_body
-[{"healthy_nodes":[{"host":"127.0.0.1","port":30511,"priority":0,"weight":1}],"name":"upstream#/upstreams/1","nodes":[{"host":"127.0.0.1","port":30511,"priority":0,"weight":1},{"host":"127.0.0.2","port":1988,"priority":0,"weight":1}],"src_id":"1","src_type":"upstreams"}]
-{"healthy_nodes":[{"host":"127.0.0.1","port":30511,"priority":0,"weight":1}],"name":"upstream#/upstreams/1","nodes":[{"host":"127.0.0.1","port":30511,"priority":0,"weight":1},{"host":"127.0.0.2","port":1988,"priority":0,"weight":1}],"src_id":"1","src_type":"upstreams"}
+[{"ip":"127.0.0.1","port":30511,"status":"healthy"},{"ip":"127.0.0.2","port":1988,"status":"unhealthy"}]
+[{"ip":"127.0.0.1","port":30511,"status":"healthy"},{"ip":"127.0.0.2","port":1988,"status":"unhealthy"}]
 --- ignore_error_log
 
 


Reply via email to