diff --git a/apisix/discovery/nacos/init.lua b/apisix/discovery/nacos/init.lua index 2e06f5553493..b014d0feff36 100644 --- a/apisix/discovery/nacos/init.lua +++ b/apisix/discovery/nacos/init.lua @@ -39,6 +39,8 @@ local auth_path = 'auth/login' local instance_list_path = 'ns/instance/list?healthyOnly=true&serviceName=' local default_namespace_id = "public" local default_group_name = "DEFAULT_GROUP" +local access_key +local secret_key local events local events_list @@ -53,6 +55,7 @@ local function discovery_nacos_callback(data, event, source, pid) ", application: ", core.json.encode(applications, true)) end + local function request(request_uri, path, body, method, basic_auth) local url = request_uri .. path log.info('request url:', url) @@ -127,6 +130,7 @@ local function get_token_param(base_uri, username, password) return '&accessToken=' .. data.accessToken end + local function get_namespace_param(namespace_id) local param = '' if namespace_id then @@ -136,6 +140,7 @@ local function get_namespace_param(namespace_id) return param end + local function get_group_name_param(group_name) local param = '' if group_name then @@ -145,6 +150,22 @@ local function get_group_name_param(group_name) return param end + +local function get_signed_param(group_name, service_name) + local param = '' + if access_key ~= '' and secret_key ~= '' then + local str_to_sign = ngx.now() * 1000 .. '@@' .. group_name .. '@@' .. service_name + local args = { + ak = access_key, + data = str_to_sign, + signature = ngx.encode_base64(ngx.hmac_sha1(secret_key, str_to_sign)) + } + param = '&' .. ngx.encode_args(args) + end + return param +end + + local function get_base_uri() local host = local_conf.discovery.nacos.host -- TODO Add health check to get healthy nodes. @@ -257,6 +278,7 @@ local function is_grpc(scheme) return false end + local function fetch_full_registry(premature) if premature then return @@ -286,8 +308,10 @@ local function fetch_full_registry(premature) local scheme = service_info.scheme or '' local namespace_param = get_namespace_param(service_info.namespace_id) local group_name_param = get_group_name_param(service_info.group_name) + local signature_param = get_signed_param(service_info.group_name, service_info.service_name) local query_path = instance_list_path .. service_info.service_name .. token_param .. namespace_param .. group_name_param + .. signature_param data, err = get_url(base_uri, query_path) if err then log.error('get_url:', query_path, ' err:', err) @@ -385,6 +409,8 @@ function _M.init_worker() log.info('default_weight:', default_weight) local fetch_interval = local_conf.discovery.nacos.fetch_interval log.info('fetch_interval:', fetch_interval) + access_key = local_conf.discovery.nacos.access_key + secret_key = local_conf.discovery.nacos.secret_key ngx_timer_at(0, fetch_full_registry) ngx_timer_every(fetch_interval, fetch_full_registry) end diff --git a/apisix/discovery/nacos/schema.lua b/apisix/discovery/nacos/schema.lua index 40c1ba77cc10..294048736e58 100644 --- a/apisix/discovery/nacos/schema.lua +++ b/apisix/discovery/nacos/schema.lua @@ -52,6 +52,8 @@ return { read = 5000, } }, + access_key = {type = 'string', default = ''}, + secret_key = {type = 'string', default = ''}, }, required = {'host'} } diff --git a/conf/config-default.yaml b/conf/config-default.yaml index b435533ca02d..4e257f4d4335 100755 --- a/conf/config-default.yaml +++ b/conf/config-default.yaml @@ -308,6 +308,8 @@ nginx_config: # Config for render the template to generate n # connect: 2000 # Default 2000ms # send: 2000 # Default 2000ms # read: 5000 # Default 5000ms +# access_key: "" # Nacos AccessKey ID in Alibaba Cloud, notice that it's for Nacos instances on Microservices Engine (MSE) +# secret_key: "" # Nacos AccessKey Secret in Alibaba Cloud, notice that it's for Nacos instances on Microservices Engine (MSE) # consul_kv: # Consul KV # servers: # Consul KV address(es) # - "http://127.0.0.1:8500" diff --git a/t/discovery/nacos3.t b/t/discovery/nacos3.t new file mode 100644 index 000000000000..c71259386c7e --- /dev/null +++ b/t/discovery/nacos3.t @@ -0,0 +1,638 @@ +# +# 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. +# + +# we can't use mse nacos to test, access_key and secret_key won't affect the open source nacos +use t::APISIX 'no_plan'; + +workers(4); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } +}); + +our $yaml_config = <<_EOC_; +apisix: + node_listen: 1984 +deployment: + role: data_plane + role_data_plane: + config_provider: yaml +discovery: + nacos: + host: + - "http://127.0.0.1:8858" + prefix: "/nacos/v1/" + fetch_interval: 1 + weight: 1 + timeout: + connect: 2000 + send: 2000 + read: 5000 + access_key: "my_access_key" + secret_key: "my_secret_key" + +_EOC_ + +run_tests(); + +__DATA__ + +=== TEST 1: error service_name +--- yaml_config eval: $::yaml_config +--- apisix_yaml +routes: + - + uri: /hello + upstream: + service_name: APISIX-NACOS-DEMO + discovery_type: nacos + type: roundrobin + +#END +--- request +GET /hello +--- error_code: 503 +--- error_log +no valid upstream node + + + +=== TEST 2: error namespace_id +--- yaml_config eval: $::yaml_config +--- apisix_yaml +routes: + - + uri: /hello + upstream: + service_name: APISIX-NACOS-DEMO + discovery_type: nacos + type: roundrobin + discovery_args: + namespace_id: err_ns +#END +--- request +GET /hello +--- error_code: 503 +--- error_log +no valid upstream node + + + +=== TEST 3: error group_name +--- yaml_config eval: $::yaml_config +--- apisix_yaml +routes: + - + uri: /hello + upstream: + service_name: APISIX-NACOS-DEMO + discovery_type: nacos + type: roundrobin + discovery_args: + group_name: err_group_name +#END +--- request +GET /hello +--- error_code: 503 +--- error_log +no valid upstream node + + + +=== TEST 4: error namespace_id and error group_name +--- yaml_config eval: $::yaml_config +--- apisix_yaml +routes: + - + uri: /hello + upstream: + service_name: APISIX-NACOS-DEMO + discovery_type: nacos + type: roundrobin + discovery_args: + namespace_id: err_ns + group_name: err_group_name +#END +--- request +GET /hello +--- error_code: 503 +--- error_log +no valid upstream node + + + +=== TEST 5: error group_name and correct namespace_id +--- yaml_config eval: $::yaml_config +--- apisix_yaml +routes: + - + uri: /hello + upstream: + service_name: APISIX-NACOS-DEMO + discovery_type: nacos + type: roundrobin + discovery_args: + namespace_id: test_ns + group_name: err_group_name +#END +--- request +GET /hello +--- error_code: 503 +--- error_log +no valid upstream node + + + +=== TEST 6: error namespace_id and correct group_name +--- yaml_config eval: $::yaml_config +--- apisix_yaml +routes: + - + uri: /hello + upstream: + service_name: APISIX-NACOS-DEMO + discovery_type: nacos + type: roundrobin + discovery_args: + namespace_id: err_ns + group_name: test_group +#END +--- request +GET /hello +--- error_code: 503 +--- error_log +no valid upstream node + + + +=== TEST 7: get APISIX-NACOS info from NACOS - configured in services +--- yaml_config eval: $::yaml_config +--- apisix_yaml +routes: + - + uri: /hello + service_id: 1 +services: + - + id: 1 + upstream: + service_name: APISIX-NACOS + discovery_type: nacos + type: roundrobin +#END +--- pipelined_requests eval +[ + "GET /hello", + "GET /hello", +] +--- response_body_like eval +[ + qr/server [1-2]/, + qr/server [1-2]/, +] + + + +=== TEST 8: get APISIX-NACOS info from NACOS - configured in services with group_name +--- yaml_config eval: $::yaml_config +--- apisix_yaml +routes: + - + uri: /hello + service_id: 1 +services: + - + id: 1 + upstream: + service_name: APISIX-NACOS + discovery_type: nacos + type: roundrobin + discovery_args: + group_name: test_group +#END +--- pipelined_requests eval +[ + "GET /hello", + "GET /hello", +] +--- response_body_like eval +[ + qr/server [1-2]/, + qr/server [1-2]/, +] + + + +=== TEST 9: get APISIX-NACOS info from NACOS - configured in services with namespace_id +--- yaml_config eval: $::yaml_config +--- apisix_yaml +routes: + - + uri: /hello + service_id: 1 +services: + - + id: 1 + upstream: + service_name: APISIX-NACOS + discovery_type: nacos + type: roundrobin + discovery_args: + namespace_id: test_ns +#END +--- pipelined_requests eval +[ + "GET /hello", + "GET /hello", +] +--- response_body_like eval +[ + qr/server [1-2]/, + qr/server [1-2]/, +] + + + +=== TEST 10: get APISIX-NACOS info from NACOS - configured in services with group_name and namespace_id +--- yaml_config eval: $::yaml_config +--- apisix_yaml +routes: + - + uri: /hello + service_id: 1 +services: + - + id: 1 + upstream: + service_name: APISIX-NACOS + discovery_type: nacos + type: roundrobin + discovery_args: + group_name: test_group + namespace_id: test_ns +#END +--- pipelined_requests eval +[ + "GET /hello", + "GET /hello", +] +--- response_body_like eval +[ + qr/server [1-2]/, + qr/server [1-2]/, +] + + + +=== TEST 11: get APISIX-NACOS info from NACOS - configured in upstreams +--- yaml_config eval: $::yaml_config +--- apisix_yaml +routes: + - + uri: /hello + upstream: + service_name: APISIX-NACOS + discovery_type: nacos + type: roundrobin +#END +--- pipelined_requests eval +[ + "GET /hello", + "GET /hello", +] +--- response_body_like eval +[ + qr/server [1-2]/, + qr/server [1-2]/, +] +--- no_error_log +[error, error] + + + +=== TEST 12: get APISIX-NACOS info from NACOS - configured in upstreams with namespace_id +--- yaml_config eval: $::yaml_config +--- apisix_yaml +routes: + - + uri: /hello + upstream: + service_name: APISIX-NACOS + discovery_type: nacos + type: roundrobin + discovery_args: + namespace_id: test_ns +#END +--- pipelined_requests eval +[ + "GET /hello", + "GET /hello", +] +--- response_body_like eval +[ + qr/server [1-2]/, + qr/server [1-2]/, +] + + + +=== TEST 13: get APISIX-NACOS info from NACOS - configured in upstreams with group_name +--- yaml_config eval: $::yaml_config +--- apisix_yaml +routes: + - + uri: /hello + upstream: + service_name: APISIX-NACOS + discovery_type: nacos + type: roundrobin + discovery_args: + group_name: test_group +#END +--- pipelined_requests eval +[ + "GET /hello", + "GET /hello", +] +--- response_body_like eval +[ + qr/server [1-2]/, + qr/server [1-2]/, +] + + + +=== TEST 14: get APISIX-NACOS info from NACOS - configured in upstreams with namespace_id and group_name +--- yaml_config eval: $::yaml_config +--- apisix_yaml +routes: + - + uri: /hello + upstream: + service_name: APISIX-NACOS + discovery_type: nacos + type: roundrobin + discovery_args: + namespace_id: test_ns + group_name: test_group +#END +--- pipelined_requests eval +[ + "GET /hello", + "GET /hello", +] +--- response_body_like eval +[ + qr/server [1-2]/, + qr/server [1-2]/, +] + + + +=== TEST 15: get APISIX-NACOS info from NACOS - configured in upstreams + etcd +--- extra_yaml_config +discovery: + nacos: + host: + - "http://127.0.0.1:8858" + fetch_interval: 1 + access_key: "my_access_key" + secret_key: "my_secret_key" +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/upstreams/1', + ngx.HTTP_PUT, + [[{ + "service_name": "APISIX-NACOS", + "discovery_type": "nacos", + "type": "roundrobin" + }]] + ) + + if code >= 300 then + ngx.status = code + ngx.say(body) + return + end + + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/hello", + "upstream_id": 1 + }]] + ) + + if code >= 300 then + ngx.status = code + end + + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 16: same namespace_id and service_name, different group_name +--- extra_yaml_config +discovery: + nacos: + host: + - "http://127.0.0.1:8858" + fetch_interval: 1 + access_key: "my_access_key" + secret_key: "my_secret_key" +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + + -- use nacos-service5 + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/hello", + "upstream": { + "service_name": "APISIX-NACOS", + "discovery_type": "nacos", + "type": "roundrobin", + "discovery_args": { + "namespace_id": "test_ns", + "group_name": "test_group" + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + + -- use nacos-service6 + local code, body = t('/apisix/admin/routes/2', + ngx.HTTP_PUT, + [[{ + "uri": "/hello1", + "upstream": { + "service_name": "APISIX-NACOS", + "discovery_type": "nacos", + "type": "roundrobin", + "discovery_args": { + "namespace_id": "test_ns", + "group_name": "test_group2" + } + }, + "plugins": { + "proxy-rewrite": { + "uri": "/hello" + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + + ngx.sleep(1.5) + + local http = require "resty.http" + local httpc = http.new() + local uri1 = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello" + local res, err = httpc:request_uri(uri1, { method = "GET"}) + if err then + ngx.log(ngx.ERR, err) + ngx.status = res.status + return + end + ngx.say(res.body) + + local uri2 = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello1" + res, err = httpc:request_uri(uri2, { method = "GET"}) + if err then + ngx.log(ngx.ERR, err) + ngx.status = res.status + return + end + ngx.say(res.body) + } + } +--- request +GET /t +--- response_body +server 1 +server 3 + + + +=== TEST 17: same group_name and service_name, different namespace_id +--- extra_yaml_config +discovery: + nacos: + host: + - "http://127.0.0.1:8858" + fetch_interval: 1 + access_key: "my_access_key" + secret_key: "my_secret_key" +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + + -- use nacos-service5 + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/hello", + "upstream": { + "service_name": "APISIX-NACOS", + "discovery_type": "nacos", + "type": "roundrobin", + "discovery_args": { + "namespace_id": "test_ns", + "group_name": "test_group" + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + + -- use nacos-service7 + local code, body = t('/apisix/admin/routes/2', + ngx.HTTP_PUT, + [[{ + "uri": "/hello1", + "upstream": { + "service_name": "APISIX-NACOS", + "discovery_type": "nacos", + "type": "roundrobin", + "discovery_args": { + "namespace_id": "test_ns2", + "group_name": "test_group" + } + }, + "plugins": { + "proxy-rewrite": { + "uri": "/hello" + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + + ngx.sleep(1.5) + + local http = require "resty.http" + local httpc = http.new() + local uri1 = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello" + local res, err = httpc:request_uri(uri1, { method = "GET"}) + if err then + ngx.log(ngx.ERR, err) + ngx.status = res.status + return + end + ngx.say(res.body) + + local uri2 = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello1" + res, err = httpc:request_uri(uri2, { method = "GET"}) + if err then + ngx.log(ngx.ERR, err) + ngx.status = res.status + return + end + ngx.say(res.body) + } + } +--- request +GET /t +--- response_body +server 1 +server 4