This is an automated email from the ASF dual-hosted git repository.
nic-6443 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 b1d91f194 fix(consul): one invalid node should not discard the
remaining nodes of the service (#13513)
b1d91f194 is described below
commit b1d91f194cb7c69d0c1f83899b44f7c39d6a1e96
Author: Nic <[email protected]>
AuthorDate: Fri Jun 12 10:50:38 2026 +0800
fix(consul): one invalid node should not discard the remaining nodes of the
service (#13513)
---
apisix/discovery/consul/client.lua | 5 +-
t/discovery/consul-malformed-node.t | 157 ++++++++++++++++++++++++++++++++++++
2 files changed, 161 insertions(+), 1 deletion(-)
diff --git a/apisix/discovery/consul/client.lua
b/apisix/discovery/consul/client.lua
index 9bac9655d..c5afd20e8 100644
--- a/apisix/discovery/consul/client.lua
+++ b/apisix/discovery/consul/client.lua
@@ -394,7 +394,9 @@ function _M.fetch_services_from_server(consul_server,
options)
local nodes_uniq = {}
for _, node in ipairs(result.body) do
if not node.Service then
- goto CONTINUE
+ log.warn("invalid consul service entry without Service
field, ",
+ "skip it: ", json_delay_encode(node))
+ goto CONTINUE_NODE
end
local svc_address, svc_port = node.Service.Address,
node.Service.Port
@@ -422,6 +424,7 @@ function _M.fetch_services_from_server(consul_server,
options)
core.table.insert(nodes, n)
nodes_uniq[service_id] = true
end
+ :: CONTINUE_NODE ::
end
if nodes then
diff --git a/t/discovery/consul-malformed-node.t
b/t/discovery/consul-malformed-node.t
new file mode 100644
index 000000000..476c3a7f8
--- /dev/null
+++ b/t/discovery/consul-malformed-node.t
@@ -0,0 +1,157 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+log_level('info');
+no_root_location();
+no_shuffle();
+
+
+add_block_preprocessor(sub {
+ my ($block) = @_;
+
+ # mock consul server: /v1/health/service/service_a returns one malformed
+ # entry (without the Service field) followed by two valid entries
+ my $http_config = $block->http_config // <<_EOC_;
+
+ server {
+ listen 18506;
+
+ location /v1/catalog/services {
+ content_by_lua_block {
+ ngx.header["X-Consul-Index"] = "1"
+ ngx.header.content_type = "application/json"
+ ngx.say('{"service_a":[]}')
+ }
+ }
+
+ location /v1/health/state/any {
+ content_by_lua_block {
+ ngx.header["X-Consul-Index"] = "1"
+ ngx.header.content_type = "application/json"
+ ngx.say('[]')
+ }
+ }
+
+ location /v1/health/service/service_a {
+ content_by_lua_block {
+ ngx.header["X-Consul-Index"] = "1"
+ ngx.header.content_type = "application/json"
+ ngx.say('['
+ ..
'{"Node":{"Node":"reclaimed-node","Address":"127.0.0.1"},"Checks":[]},'
+ .. '{"Node":{"Node":"node1","Address":"127.0.0.1"},'
+ .. '"Service":{"ID":"service_a1","Service":"service_a",'
+ .. '"Address":"127.0.0.1","Port":30511},"Checks":[]},'
+ .. '{"Node":{"Node":"node2","Address":"127.0.0.1"},'
+ .. '"Service":{"ID":"service_a2","Service":"service_a",'
+ .. '"Address":"127.0.0.1","Port":30512},"Checks":[]}'
+ .. ']')
+ }
+ }
+ }
+
+ server {
+ listen 30511;
+
+ location /hello {
+ content_by_lua_block {
+ ngx.say("server 1")
+ }
+ }
+ }
+ server {
+ listen 30512;
+
+ location /hello {
+ content_by_lua_block {
+ ngx.say("server 2")
+ }
+ }
+ }
+_EOC_
+
+ $block->set_value("http_config", $http_config);
+});
+
+our $yaml_config = <<_EOC_;
+apisix:
+ node_listen: 1984
+deployment:
+ role: data_plane
+ role_data_plane:
+ config_provider: yaml
+discovery:
+ consul:
+ servers:
+ - "http://127.0.0.1:18506"
+ timeout:
+ connect: 1000
+ read: 1000
+ wait: 60
+ weight: 1
+ fetch_interval: 1
+ keepalive: true
+_EOC_
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: one malformed health service entry should not discard the
remaining valid nodes
+--- yaml_config eval: $::yaml_config
+--- apisix_yaml
+routes:
+ -
+ uri: /hello
+ upstream:
+ service_name: service_a
+ discovery_type: consul
+ type: roundrobin
+#END
+--- config
+ location /t {
+ content_by_lua_block {
+ ngx.sleep(2)
+
+ local http = require("resty.http")
+ local bodies = {}
+ for i = 1, 2 do
+ local httpc = http.new()
+ local res, err =
httpc:request_uri("http://127.0.0.1:1984/hello")
+ if not res then
+ ngx.say("request failed: ", err)
+ return
+ end
+ if res.status ~= 200 then
+ ngx.say("unexpected status: ", res.status)
+ return
+ end
+ table.insert(bodies, res.body)
+ end
+ table.sort(bodies)
+ ngx.print(table.concat(bodies))
+ }
+ }
+--- timeout: 5
+--- request
+GET /t
+--- response_body
+server 1
+server 2
+--- error_log
+invalid consul service entry without Service field