diff --git a/apisix/admin/global_rules.lua b/apisix/admin/global_rules.lua index e9bb0057de0e..4835c8bcc7d5 100644 --- a/apisix/admin/global_rules.lua +++ b/apisix/admin/global_rules.lua @@ -18,6 +18,8 @@ local core = require("apisix.core") local schema_plugin = require("apisix.admin.plugins").check_schema local type = type local tostring = tostring +local local_conf = require("apisix.core.config_local").local_conf() +local etcd_version = local_conf.etcd.version or "v2" local _M = { @@ -83,7 +85,7 @@ function _M.get(id) if id then key = key .. "/" .. id end - local res, err = core.etcd.get(key) + local res, err = core.etcd.readdir(key) if not res then core.log.error("failed to get global rule[", key, "]: ", err) return 500, {error_msg = err} @@ -134,7 +136,7 @@ function _M.patch(id, conf, sub_path) core.log.info("key: ", key, " old value: ", core.json.delay_encode(res_old, true)) - local node_value = res_old.body.node.value + local node_value = etcd_version == "v3" and res_old.body.kvs[1].value or res_old.body.node.value if sub_path and sub_path ~= "" then local code, err, node_val = core.table.patch(node_value, sub_path, conf) diff --git a/apisix/admin/routes.lua b/apisix/admin/routes.lua index d9174356b5b8..1d3f1a84e6f6 100644 --- a/apisix/admin/routes.lua +++ b/apisix/admin/routes.lua @@ -21,6 +21,8 @@ local tostring = tostring local type = type local loadstring = loadstring +local local_conf = require("apisix.core.config_local").local_conf() +local etcd_version = local_conf.etcd.version or "v2" local _M = { version = 0.2, @@ -86,6 +88,13 @@ local function check_conf(id, conf, need_id) .. "upstream id [" .. upstream_id .. "], " .. "response code: " .. res.status} end + + -- etcd v3 would not return `Key not found anymore` + if etcd_version == "v3" and not res.body.kvs then + return nil, {error_msg = "failed to fetch upstream info by " + .. "upstream id [" .. upstream_id .. "], " + .. "response code: " .. 404} + end end local service_id = conf.service_id @@ -103,6 +112,13 @@ local function check_conf(id, conf, need_id) .. "service id [" .. service_id .. "], " .. "response code: " .. res.status} end + + -- etcd v3 would not return `Key not found anymore` + if etcd_version == "v3" and not res.body.kvs then + return nil, {error_msg = "failed to fetch service info by " + .. "service id [" .. service_id .. "], " + .. "response code: " .. 404} + end end if conf.plugins then @@ -228,7 +244,7 @@ function _M.patch(id, conf, sub_path, args) core.log.info("key: ", key, " old value: ", core.json.delay_encode(res_old, true)) - local node_value = res_old.body.node.value + local node_value = core.config.getkv(res_old).value if sub_path and sub_path ~= "" then local code, err, node_val = core.table.patch(node_value, sub_path, conf) diff --git a/apisix/admin/services.lua b/apisix/admin/services.lua index cbce7d150278..62ac911ec63b 100644 --- a/apisix/admin/services.lua +++ b/apisix/admin/services.lua @@ -22,6 +22,8 @@ local tostring = tostring local ipairs = ipairs local type = type +local local_conf = require("apisix.core.config_local").local_conf() +local etcd_version = local_conf.etcd.version or "v2" local _M = { version = 0.3, @@ -82,6 +84,13 @@ local function check_conf(id, conf, need_id) .. "upstream id [" .. upstream_id .. "], " .. "response code: " .. res.status} end + + -- etcd v3 would not return `Key not found anymore` + if etcd_version == "v3" and not res.body.kvs then + return nil, {error_msg = "failed to fetch upstream info by " + .. "upstream id [" .. upstream_id .. "], " + .. "response code: " .. 404} + end end if conf.plugins then @@ -205,7 +214,7 @@ function _M.patch(id, conf, sub_path) core.log.info("key: ", key, " old value: ", core.json.delay_encode(res_old, true)) - local node_value = res_old.body.node.value + local node_value = core.config.getkv(res_old).value if sub_path and sub_path ~= "" then local code, err, node_val = core.table.patch(node_value, sub_path, conf) diff --git a/apisix/core.lua b/apisix/core.lua index 051dae36af75..cfdc960cffc6 100644 --- a/apisix/core.lua +++ b/apisix/core.lua @@ -19,6 +19,8 @@ local local_conf = require("apisix.core.config_local").local_conf() local config_center = local_conf.apisix and local_conf.apisix.config_center or "etcd" +local etcd_version = local_conf.etcd.version == "v3" and "_v3" or "" +config_center = config_center == "etcd" and config_center .. etcd_version or config_center log.info("use config_center: ", config_center) return { @@ -35,7 +37,7 @@ return { timer = require("apisix.core.timer"), id = require("apisix.core.id"), utils = require("apisix.core.utils"), - etcd = require("apisix.core.etcd"), + etcd = require("apisix.core.etcd" .. etcd_version), http = require("apisix.core.http"), tablepool= require("tablepool"), } diff --git a/apisix/core/config_etcd.lua b/apisix/core/config_etcd.lua index 6e616b08f906..d7d39a9ea857 100644 --- a/apisix/core/config_etcd.lua +++ b/apisix/core/config_etcd.lua @@ -142,7 +142,7 @@ local function sync_data(self) if not res then return false, err end - + -- node to kvs, headers seems move to under body local dir_res, headers = res.body.node, res.headers log.debug("readdir key: ", self.key, " res: ", json.delay_encode(dir_res)) @@ -153,7 +153,7 @@ local function sync_data(self) if not dir_res.dir then return false, self.key .. " is not a dir" end - + -- nodes to ? if not dir_res.nodes then dir_res.nodes = {} end @@ -227,6 +227,7 @@ local function sync_data(self) local dir_res, err = waitdir(self.etcd_cli, self.key, self.prev_index + 1, self.timeout) log.info("waitdir key: ", self.key, " prev_index: ", self.prev_index + 1) + --log.error(ii, ": waitdir key: ", self.key, " prev_index: ", self.prev_index + 1) log.info("res: ", json.delay_encode(dir_res, true)) if err == "timeout" then if key_res and key_res.headers then @@ -485,6 +486,17 @@ function _M.fetch_created_obj(key) return created_obj[key] end +function _M.getkv(res) + if type(res) ~= "table" then + log.error("fail to get kv" .. json.encode(res)) + end + + if res.body then + res = res.body + end + return res.nodes +end + local function read_etcd_version(etcd_cli) if not etcd_cli then diff --git a/apisix/core/config_etcd_v3.lua b/apisix/core/config_etcd_v3.lua new file mode 100644 index 000000000000..44a55a6a481a --- /dev/null +++ b/apisix/core/config_etcd_v3.lua @@ -0,0 +1,551 @@ +-- +-- 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. +-- +local config_local = require("apisix.core.config_local") +local log = require("apisix.core.log") +local json = require("apisix.core.json") +local etcd = require("resty.etcd") +local new_tab = require("table.new") +local clone_tab = require("table.clone") +local check_schema = require("apisix.core.schema").check +local exiting = ngx.worker.exiting +local insert_tab = table.insert +local type = type +local ipairs = ipairs +local setmetatable = setmetatable +local ngx_sleep = ngx.sleep +local ngx_timer_at = ngx.timer.at +local ngx_time = ngx.time +local sub_str = string.sub +local tostring = tostring +local tonumber = tonumber +local pcall = pcall +local created_obj = {} + + +local _M = { + version = 0.3, + local_conf = config_local.local_conf, + clear_local_cache = config_local.clear_cache, +} + +local mt = { + __index = _M, + __tostring = function(self) + return " etcd key: " .. self.key + end +} + + +local function getkey(etcd_cli, key) + if not etcd_cli then + return nil, "not inited" + end + + local res, err = etcd_cli:get(key) + if not res then + -- log.error("failed to get key from etcd: ", err) + return nil, err + end + + if type(res.body) ~= "table" then + return nil, "failed to get key from etcd" + end + + return res +end + +local function readdir(etcd_cli, key, opts) + if not etcd_cli then + return nil, nil, "not inited" + end + + local res, err = etcd_cli:readdir(key, opts) + if not res then + -- log.error("failed to get key from etcd: ", err) + return nil, nil, err + end + + if type(res.body) ~= "table" then + return nil, "failed to read etcd dir" + end + + return res, err +end + +local function watchdir(etcd_cli, key, opts) + if not etcd_cli then + return nil, nil, "not inited" + end + + local res_fun, err = etcd_cli:watchdir(key, opts) + res_fun() -- skip create info + local res, err = res_fun() + + if not res then + -- log.error("failed to get key from etcd: ", err) + return nil, err + end + + if type(res.result) ~= "table" then + return nil, "failed to read etcd dir" + end + + return res +end + + +local function short_key(self, str) + return sub_str(str, #self.key + 2) +end + + +function _M.upgrade_version(self, new_ver) + new_ver = tonumber(new_ver) + if not new_ver then + return + end + + local pre_index = self.prev_index + if not pre_index then + self.prev_index = new_ver + return + end + + if new_ver <= pre_index then + return + end + + self.prev_index = new_ver + return +end + + +local function sync_data(self) + if not self.key then + return nil, "missing 'key' arguments" + end + + if self.need_reload then + local res, err = readdir(self.etcd_cli, self.key) + if not res then + return false, err + end + if res.body.error then + return res, err + end + + local dir_res, header = res.body, res.body.header + log.debug("readdir key: ", self.key, " res: ", + json.delay_encode(dir_res)) + if not dir_res then + return false, err + end + + -- if not dir_res.dir then + -- return false, self.key .. " is not a dir" + -- end + + if not dir_res.kvs then + dir_res.kvs = {} + -- not return k-v initially, not like v2 + dir_res.kvs.key = self.key + end + + if self.values then + for i, val in ipairs(self.values) do + if val and val.clean_handlers then + for _, clean_handler in ipairs(val.clean_handlers) do + clean_handler(val) + end + val.clean_handlers = nil + end + end + + self.values = nil + self.values_hash = nil + end + + self.values = new_tab(#dir_res.kvs, 0) + self.values_hash = new_tab(0, #dir_res.kvs) + + local changed = false + + for _, item in ipairs(dir_res.kvs) do + -- init create kv without value, like dir in v2 + -- since kvs contains multi kv pair, check inside loop + local type_value = type(item.value) + if type_value == "string" or type_value == "table" then + local key = short_key(self, item.key) + local data_valid = true + if type(item.value) ~= "table" then + data_valid = false + log.error("invalid item data of [", self.key .. "/" .. key, + "], val: ", tostring(item.value), + ", it shoud be a object") + end + + if data_valid and self.item_schema then + data_valid, err = check_schema(self.item_schema, item.value) + if not data_valid then + log.error("failed to check item data of [", self.key, + "] err:", err, " ,val: ", json.encode(item.value)) + end + end + + if data_valid then + changed = true + insert_tab(self.values, item) + self.values_hash[key] = #self.values + item.value.id = key + item.clean_handlers = {} + + if self.filter then + self.filter(item) + end + end + end + + self:upgrade_version(item.mod_revision) + end + + + if header then + self:upgrade_version(header.revision) + end + + if changed then + self.conf_version = self.conf_version + 1 + end + + self.need_reload = false + return true + end + + -- for fetch the etcd index + local key_res, _ = getkey(self.etcd_cli, self.key) + + -- get the first change + local dir_res, err = watchdir(self.etcd_cli, self.key, {start_revision = self.prev_index + 1, timeout = self.timeout, progress_notify = true}) + log.info("watchdir key: ", self.key, " prev_index: ", self.prev_index + 1) + log.info("res: ", json.delay_encode(dir_res, true)) + -- TODO: would be effected when compact in v3 + if err == "timeout" then + if key_res and key_res.body.header then + local key_index = key_res.body.header.revision + local key_idx = key_index and tonumber(key_index) or 0 + if key_idx and key_idx > self.prev_index then + -- Avoid the index to exceed 1000 by updating other keys + -- that will causing a full reload + self:upgrade_version(key_index) + end + end + end + + if not dir_res then + return false, err + end + + local res = dir_res.result.events[1].kv + -- TODO: would be effected when compact in v3 + --[[ + local err_msg = dir_res.body.message + if err_msg then + if err_msg == "The event in requested index is outdated and cleared" + and dir_res.body.errorCode == 401 then + self.need_reload = true + log.warn("watchdir [", self.key, "] err: ", err_msg, + ", need to fully reload") + return false + end + return false, err + end + ]]-- + + if not res then + if err == "The event in requested index is outdated and cleared" then + self.need_reload = true + log.warn("watchdir [", self.key, "] err: ", err, + ", need to fully reload") + return false + end + + return false, err + end + + local key = short_key(self, res.key) + if res.value and type(res.value) ~= "table" then + self:upgrade_version(res.mod_revision) + return false, "invalid item data of [" .. self.key .. "/" .. key + .. "], val: " .. tostring(res.value) + .. ", it shoud be a object" + end + + if res.value and self.item_schema then + local ok, err = check_schema(self.item_schema, res.value) + if not ok then + self:upgrade_version(res.mod_revision) + + return false, "failed to check item data of [" + .. self.key .. "] err:" .. err + end + end + + self:upgrade_version(res.mod_revision) + -- no dir + --[[ + if res.dir then + if res.value then + return false, "todo: support for parsing `dir` response " + .. "structures. " .. json.encode(res) + end + return false + end + ]]-- + + if self.filter then + self.filter(res) + end + -- ?clean_handlers + local pre_index = self.values_hash[key] + if pre_index then + local pre_val = self.values[pre_index] + if pre_val and pre_val.clean_handlers then + for _, clean_handler in ipairs(pre_val.clean_handlers) do + clean_handler(pre_val) + end + pre_val.clean_handlers = nil + end + + if res.value then + res.value.id = key + self.values[pre_index] = res + res.clean_handlers = {} + + else + self.sync_times = self.sync_times + 1 + self.values[pre_index] = false + end + + elseif res.value then + res.clean_handlers = {} + insert_tab(self.values, res) + self.values_hash[key] = #self.values + res.value.id = key + end + + -- avoid space waste + -- todo: need to cover this path, it is important. + if self.sync_times > 100 then + local count = 0 + for i = 1, #self.values do + local val = self.values[i] + self.values[i] = nil + if val then + count = count + 1 + self.values[count] = val + end + end + + for i = 1, count do + key = short_key(self, self.values[i].key) + self.values_hash[key] = i + end + self.sync_times = 0 + end + + self.conf_version = self.conf_version + 1 + return self.values +end + + +function _M.get(self, key) + if not self.values_hash then + return + end + + local arr_idx = self.values_hash[tostring(key)] + if not arr_idx then + return nil + end + + return self.values[arr_idx] +end + + +function _M.getkey(self, key) + if not self.running then + return nil, "stoped" + end + + return getkey(self.etcd_cli, key) +end + + +local function _automatic_fetch(premature, self) + if premature then + return + end + + local i = 0 + while not exiting() and self.running and i <= 32 do + i = i + 1 + local ok, ok2, err = pcall(sync_data, self) + if not ok then + err = ok2 + log.error("failed to fetch data from etcd: ", err, ", ", + tostring(self)) + ngx_sleep(3) + break + + elseif not ok2 and err then + if err ~= "timeout" and err ~= "Key not found" + and self.last_err ~= err then + log.error("failed to fetch data from etcd: ", err, ", ", + tostring(self)) + end + + if err ~= self.last_err then + self.last_err = err + self.last_err_time = ngx_time() + else + if ngx_time() - self.last_err_time >= 30 then + self.last_err = nil + end + end + ngx_sleep(0.5) + + elseif not ok2 then + ngx_sleep(0.05) + end + end + + if not exiting() and self.running then + ngx_timer_at(0, _automatic_fetch, self) + end +end + + +function _M.new(key, opts) + local local_conf, err = config_local.local_conf() + if not local_conf then + return nil, err + end + + local etcd_conf = clone_tab(local_conf.etcd) + local prefix = etcd_conf.prefix + etcd_conf.http_host = etcd_conf.host + etcd_conf.host = nil + etcd_conf.prefix = nil + etcd_conf.protocol = etcd_conf.version + etcd_conf.version = nil + etcd_conf.api_prefix = "/v3" + + local etcd_cli + etcd_cli, err = etcd.new(etcd_conf) + if not etcd_cli then + return nil, err + end + + local automatic = opts and opts.automatic + local item_schema = opts and opts.item_schema + local filter_fun = opts and opts.filter + local timeout = opts and opts.timeout + + local obj = setmetatable({ + etcd_cli = etcd_cli, + key = key and prefix .. key, + automatic = automatic, + item_schema = item_schema, + sync_times = 0, + running = true, + conf_version = 0, + values = nil, + need_reload = true, + routes_hash = nil, + prev_index = nil, + last_err = nil, + last_err_time = nil, + timeout = timeout, + filter = filter_fun, + }, mt) + + if automatic then + if not key then + return nil, "missing `key` argument" + end + + ngx_timer_at(0, _automatic_fetch, obj) + end + + if key then + created_obj[key] = obj + end + + return obj +end + + +function _M.close(self) + self.running = false +end + + +function _M.fetch_created_obj(key) + return created_obj[key] +end + +function _M.getkv(res) + if type(res) ~= "table" then + log.error("fail to get kv" .. json.encode(res)) + end + + if res.body then + res = res.body + end + return res.kvs[1] +end + + +local function read_etcd_version(etcd_cli) + if not etcd_cli then + return nil, "not inited" + end + + local data, err = etcd_cli:version() + if not data then + return nil, err + end + + local body = data.body + if type(body) ~= "table" then + return nil, "failed to read response body when try to fetch etcd " + .. "version" + end + + return body +end + +function _M.server_version(self) + if not self.running then + return nil, "stoped" + end + + return read_etcd_version(self.etcd_cli) +end + + +return _M diff --git a/apisix/core/etcd.lua b/apisix/core/etcd.lua index 818c99b95d1c..bdd56052d978 100644 --- a/apisix/core/etcd.lua +++ b/apisix/core/etcd.lua @@ -53,7 +53,16 @@ function _M.get(key) return etcd_cli:get(prefix .. key) end +function _M.readdir(key) + local etcd_cli, prefix, err = new() + if not etcd_cli then + return nil, err + end + + return etcd_cli:readdir(prefix .. key) +end +-- ttl to timeout in opts function _M.set(key, value, ttl) local etcd_cli, prefix, err = new() if not etcd_cli then diff --git a/apisix/core/etcd_v3.lua b/apisix/core/etcd_v3.lua new file mode 100644 index 000000000000..e8b78ddd26e6 --- /dev/null +++ b/apisix/core/etcd_v3.lua @@ -0,0 +1,131 @@ +-- +-- 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. +-- +local fetch_local_conf = require("apisix.core.config_local").local_conf +local etcd = require("resty.etcd") +local clone_tab = require("table.clone") + +local _M = {version = 0.1} + + +local function new() + local local_conf, err = fetch_local_conf() + if not local_conf then + return nil, nil, err + end + + local etcd_conf = clone_tab(local_conf.etcd) + local prefix = etcd_conf.prefix + etcd_conf.http_host = etcd_conf.host + etcd_conf.host = nil + etcd_conf.prefix = nil + etcd_conf.protocol = etcd_conf.version + etcd_conf.version = nil + etcd_conf.api_prefix = "/v3" + + local etcd_cli + etcd_cli, err = etcd.new(etcd_conf) + if not etcd_cli then + return nil, nil, err + end + + return etcd_cli, prefix +end +_M.new = new + + +function _M.get(key, opts) + local etcd_cli, prefix, err = new() + if not etcd_cli then + return nil, err + end + + return etcd_cli:get(prefix .. key, opts) +end + +function _M.readdir(key, opts) + local etcd_cli, prefix, err = new() + if not etcd_cli then + return nil, err + end + + local res, err = etcd_cli:readdir(prefix .. key, opts) + if not res then + return nil, err + end + if res.body.kvs then + for i=#res.body.kvs,1,-1 do + local type_value = type(res.body.kvs[i].value) + if type_value ~= "string" and type_value ~= "table" then + table.remove(res.body.kvs, i) + end + end + end + return res, err +end + + +function _M.set(key, value, opts) + local etcd_cli, prefix, err = new() + if not etcd_cli then + return nil, err + end + + return etcd_cli:set(prefix .. key, value, opts) +end + + +function _M.push(key, value, opts) + local etcd_cli, prefix, err = new() + if not etcd_cli then + return nil, err + end + -- use pseudo_key (a key that might never be used) to get current revision, to simulate push in v2 + local pseudo_key = "/5tHkHhY/kjr6cQY" + + local res, err = etcd_cli:set(pseudo_key, nil, {prev_kv = true}) + if not res then + return nil, err + end + + local index = res.body.prev_kv.mod_revision + index = string.format("%020d", index) + + return etcd_cli:set(prefix .. key .. "/" .. index, value, opts) +end + + +function _M.delete(key, opts) + local etcd_cli, prefix, err = new() + if not etcd_cli then + return nil, err + end + + return etcd_cli:delete(prefix .. key, opts) +end + + +function _M.server_version() + local etcd_cli, err = new() + if not etcd_cli then + return nil, err + end + + return etcd_cli:version() +end + + +return _M diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua index 8b2841ce8040..9a45c2bddf24 100644 --- a/apisix/http/router/radixtree_host_uri.lua +++ b/apisix/http/router/radixtree_host_uri.lua @@ -125,7 +125,6 @@ local function create_radixtree_router(routes) }) end end - only_uri_router = router.new(only_uri_routes) return true end diff --git a/apisix/plugins/prometheus/exporter.lua b/apisix/plugins/prometheus/exporter.lua index 7e3d63eae7da..4d5433be7097 100644 --- a/apisix/plugins/prometheus/exporter.lua +++ b/apisix/plugins/prometheus/exporter.lua @@ -33,6 +33,7 @@ local get_upstreams = require("apisix.upstream").upstreams local clear_tab = core.table.clear local get_stream_routes = router.stream_routes local get_protos = require("apisix.plugins.grpc-transcode.proto").protos +local local_conf = require("apisix.core.config_local").local_conf() @@ -274,11 +275,21 @@ function _M.collect() end local res, _ = config:getkey("/routes") - if res and res.headers then - clear_tab(key_values) - -- global max - key_values[1] = "x_etcd_index" - metrics.etcd_modify_indexes:set(res.headers["X-Etcd-Index"], key_values) + --TODO: refactor for multiversion etcd + if local_conf.etcd.version == "v3" then + if res and res.body.header then + clear_tab(key_values) + -- global max + key_values[1] = "x_etcd_index" + metrics.etcd_modify_indexes:set(res.body.header.version, key_values) + end + else + if res and res.headers then + clear_tab(key_values) + -- global max + key_values[1] = "x_etcd_index" + metrics.etcd_modify_indexes:set(res.headers["X-Etcd-Index"], key_values) + end end core.response.set_header("content_type", "text/plain") diff --git a/bin/apisix b/bin/apisix index 762c326a460c..810b053281a9 100755 --- a/bin/apisix +++ b/bin/apisix @@ -796,35 +796,51 @@ local function init_etcd(show_output) local host_count = #(yaml_conf.etcd.host) - -- check whether the user has enabled etcd v2 protocol - for index, host in ipairs(yaml_conf.etcd.host) do - uri = host .. "/v2/keys" - local cmd = "curl -i -m ".. timeout * 2 .. " -o /dev/null -s -w %{http_code} " .. uri - local res = excute_cmd(cmd) - if res == "404" then - io.stderr:write(string.format("failed: please make sure that you have enabled the v2 protocol of etcd on %s.\n", host)) - return + -- check whether the user has enabled etcd v2 protocol for v2 API + local etcd_version = etcd_conf.version or "v2" + if etcd_version == "v2" then + for index, host in ipairs(yaml_conf.etcd.host) do + uri = host .. "/v2/keys" + local cmd = "curl -i -m ".. timeout * 2 .. " -o /dev/null -s -w %{http_code} " .. uri + local res = excute_cmd(cmd) + if res == "404" then + io.stderr:write(string.format("failed: please make sure that you have enabled the v2 protocol of etcd on %s.\n", host)) + return + end end end + local base64 = require("base64") local etcd_ok = false for index, host in ipairs(yaml_conf.etcd.host) do local is_success = true - uri = host .. "/v2/keys" .. (etcd_conf.prefix or "") + local cmd for _, dir_name in ipairs({"/routes", "/upstreams", "/services", "/plugins", "/consumers", "/node_status", "/ssl", "/global_rules", "/stream_routes", "/proto"}) do - local cmd = "curl " .. uri .. dir_name - .. "?prev_exist=false -X PUT -d dir=true " - .. "--connect-timeout " .. timeout - .. " --max-time " .. timeout * 2 .. " --retry 1 2>&1" + if etcd_version == "v3" then + uri = host .. "/v3/kv/put" + local key = (etcd_conf.prefix or "") .. dir_name .. "/" + local post_json = '{"value":"' .. base64.encode("null") .. '", "key":"' .. base64.encode(key) .. '"}' + cmd = "curl " .. uri + .. " -X POST -d '" .. post_json + .. "' --connect-timeout " .. timeout + .. " --max-time " .. timeout * 2 .. " --retry 1 2>&1" + -- cmd = "etcdctl " .. uri + else + uri = host .. "/v2/keys" .. (etcd_conf.prefix or "") + cmd = "curl " .. uri .. dir_name + .. "?prev_exist=false -X PUT -d dir=true " + .. "--connect-timeout " .. timeout + .. " --max-time " .. timeout * 2 .. " --retry 1 2>&1" + end local res = excute_cmd(cmd) - if not res:find("index", 1, true) - and not res:find("createdIndex", 1, true) then + if (etcd_version == "v2" and not res:find("index", 1, true) and not res:find("createdIndex", 1, true)) + or (etcd_version == "v3" and not res:find("revision", 1, true)) then is_success = false if (index == host_count) then error(cmd .. "\n" .. res) diff --git a/conf/config.yaml b/conf/config.yaml index 20ba92504631..5735e9bee20a 100644 --- a/conf/config.yaml +++ b/conf/config.yaml @@ -128,6 +128,7 @@ etcd: - "http://127.0.0.1:2379" # multiple etcd address prefix: "/apisix" # apisix configurations prefix timeout: 30 # 30 seconds + version: "v2" # etcd version # user: root # root username for etcd # password: 5tHkHhYkjr6cQY # root password for etcd #eureka: diff --git a/rockspec/apisix-master-0.rockspec b/rockspec/apisix-master-0.rockspec index 9261b2135f63..7912dc57f039 100644 --- a/rockspec/apisix-master-0.rockspec +++ b/rockspec/apisix-master-0.rockspec @@ -52,6 +52,7 @@ dependencies = { "lua-resty-kafka = 0.07", "lua-resty-logger-socket = 2.0-0", "skywalking-nginx-lua-plugin = 1.0-0", + "lbase64 = 20120820-1", } build = { diff --git a/t/admin/consumers.t b/t/admin/consumers.t index 458e396b63df..f04b7ce8a176 100644 --- a/t/admin/consumers.t +++ b/t/admin/consumers.t @@ -38,13 +38,10 @@ __DATA__ "desc": "new consumer" }]], [[{ - "node": { - "value": { - "username": "jack", - "desc": "new consumer" - } - }, - "action": "set" + "value": { + "username": "jack", + "desc": "new consumer" + } }]] ) @@ -78,18 +75,15 @@ passed } }]], [[{ - "node": { - "value": { - "username": "jack", - "desc": "new consumer", - "plugins": { - "key-auth": { - "key": "auth-one" - } + "value": { + "username": "jack", + "desc": "new consumer", + "plugins": { + "key-auth": { + "key": "auth-one" } } - }, - "action": "set" + } }]] ) @@ -115,18 +109,15 @@ passed ngx.HTTP_GET, nil, [[{ - "node": { - "value": { - "username": "jack", - "desc": "new consumer", - "plugins": { - "key-auth": { - "key": "auth-one" - } + "value": { + "username": "jack", + "desc": "new consumer", + "plugins": { + "key-auth": { + "key": "auth-one" } } - }, - "action": "get" + } }]] ) @@ -152,9 +143,8 @@ passed local code, body = t('/apisix/admin/consumers/jack', ngx.HTTP_DELETE, nil, - [[{"action": "delete"}]] + nil ) - ngx.status = code ngx.say(body) } @@ -176,9 +166,7 @@ passed local code = t('/apisix/admin/consumers/not_found', ngx.HTTP_DELETE, nil, - [[{ - "action": "delete" - }]] + nil ) ngx.say("[delete] code: ", code) } @@ -203,12 +191,9 @@ GET /t "id":"jack" }]], [[{ - "node": { - "value": { - "id": "jack" - } - }, - "action": "set" + "value": { + "id": "jack" + } }]] ) diff --git a/t/admin/delete-service.t b/t/admin/delete-service.t index 2deb44ac8753..e817fee53c05 100644 --- a/t/admin/delete-service.t +++ b/t/admin/delete-service.t @@ -39,21 +39,17 @@ __DATA__ } }]], [[{ - "node": { - "value": { - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" - } - }, - "key": "/apisix/services/1" + "value": { + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" + } }, - "action": "set" + "key": "/apisix/services/1" }]] ) - ngx.status = code ngx.say(body) } @@ -72,21 +68,18 @@ passed location /t { content_by_lua_block { local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/routes/1', + local code, body, res = t('/apisix/admin/routes/1', ngx.HTTP_PUT, [[{ "service_id": 1, "uri": "/index.html" }]], [[{ - "node": { - "value": { - "service_id": 1, - "uri": "/index.html" - }, - "key": "/apisix/routes/1" + "value": { + "service_id": 1, + "uri": "/index.html" }, - "action": "set" + "key": "/apisix/routes/1" }]] ) @@ -102,7 +95,6 @@ passed [error] - === TEST 3: delete service(id: 1) --- config location /t { @@ -112,7 +104,7 @@ passed local code, message = t('/apisix/admin/services/1', ngx.HTTP_DELETE, nil, - [[{"action": "delete"}]] + nil ) ngx.print("[delete] code: ", code, " message: ", message) } @@ -135,7 +127,7 @@ GET /t local code, message = t('/apisix/admin/routes/1', ngx.HTTP_DELETE, nil, - [[{"action": "delete"}]] + nil ) ngx.say("[delete] code: ", code, " message: ", message) } @@ -158,7 +150,7 @@ GET /t local code, message = t('/apisix/admin/services/1', ngx.HTTP_DELETE, nil, - [[{"action": "delete"}]] + nil ) ngx.say("[delete] code: ", code, " message: ", message) } diff --git a/t/admin/delete-upstream.t b/t/admin/delete-upstream.t index 4b7384d8a6ba..b9dd6ef3bc5d 100644 --- a/t/admin/delete-upstream.t +++ b/t/admin/delete-upstream.t @@ -37,16 +37,13 @@ __DATA__ "type": "roundrobin" }]], [[{ - "node": { - "value": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" + "value": { + "nodes": { + "127.0.0.1:8080": 1 }, - "key": "/apisix/upstreams/1" + "type": "roundrobin" }, - "action": "set" + "key": "/apisix/upstreams/1" }]] ) @@ -74,13 +71,10 @@ passed "upstream_id": 1 }]], [[{ - "node": { - "value": { - "upstream_id": 1 - }, - "key": "/apisix/services/1" + "value": { + "upstream_id": 1 }, - "action": "set" + "key": "/apisix/services/1" }]] ) @@ -109,14 +103,11 @@ passed "uri": "/index.html" }]], [[{ - "node": { - "value": { - "upstream_id": 1, - "uri": "/index.html" - }, - "key": "/apisix/routes/1" + "value": { + "upstream_id": 1, + "uri": "/index.html" }, - "action": "set" + "key": "/apisix/routes/1" }]] ) @@ -142,7 +133,7 @@ passed local code, message = t('/apisix/admin/upstreams/1', ngx.HTTP_DELETE, nil, - [[{"action": "delete"}]] + nil ) ngx.print("[delete] code: ", code, " message: ", message) } @@ -165,7 +156,7 @@ GET /t local code, message = t('/apisix/admin/routes/1', ngx.HTTP_DELETE, nil, - [[{"action": "delete"}]] + nil ) ngx.say("[delete] code: ", code, " message: ", message) } @@ -188,7 +179,7 @@ GET /t local code, message = t('/apisix/admin/services/1', ngx.HTTP_DELETE, nil, - [[{"action": "delete"}]] + nil ) ngx.say("[delete] code: ", code, " message: ", message) } @@ -211,7 +202,7 @@ GET /t local code, message = t('/apisix/admin/upstreams/1', ngx.HTTP_DELETE, nil, - [[{"action": "delete"}]] + nil ) ngx.say("[delete] code: ", code, " message: ", message) } diff --git a/t/admin/global-rules.t b/t/admin/global-rules.t index 0babd93f8201..ce6dbf082dd3 100644 --- a/t/admin/global-rules.t +++ b/t/admin/global-rules.t @@ -44,20 +44,17 @@ __DATA__ } }]], [[{ - "node": { - "value": { - "plugins": { - "limit-count": { - "count": 2, - "time_window": 60, - "rejected_code": 503, - "key": "remote_addr" - } + "value": { + "plugins": { + "limit-count": { + "count": 2, + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr" } - }, - "key": "/apisix/global_rules/1" + } }, - "action": "set" + "key": "/apisix/global_rules/1" }]] ) @@ -83,20 +80,17 @@ passed ngx.HTTP_GET, nil, [[{ - "node": { - "value": { - "plugins": { - "limit-count": { - "count": 2, - "time_window": 60, - "rejected_code": 503, - "key": "remote_addr" - } + "value": { + "plugins": { + "limit-count": { + "count": 2, + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr" } - }, - "key": "/apisix/global_rules/1" + } }, - "action": "get" + "key": "/apisix/global_rules/1" }]] ) @@ -122,30 +116,20 @@ passed ngx.HTTP_GET, nil, [[{ - "node": { - "dir": true, - "nodes": [ - { - "key": "/apisix/global_rules/1", - "value": { - "plugins": { - "limit-count": { + "key": "/apisix/global_rules/1", + "value": { + "plugins": { + "limit-count": { "time_window": 60, "policy": "local", "count": 2, "key": "remote_addr", "rejected_code": 503 - } - } } } - ], - "key": "/apisix/global_rules" - }, - "action": "get" + } }]] ) - ngx.status = code ngx.say(body) } @@ -176,20 +160,17 @@ passed } }}]], [[{ - "node": { - "value": { - "plugins": { - "limit-count": { - "count": 3, - "time_window": 60, - "rejected_code": 503, - "key": "remote_addr" - } + "value": { + "plugins": { + "limit-count": { + "count": 3, + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr" } - }, - "key": "/apisix/global_rules/1" + } }, - "action": "set" + "key": "/apisix/global_rules/1" }]] ) @@ -222,20 +203,17 @@ passed } }]], [[{ - "node": { - "value": { - "plugins": { - "limit-count": { - "count": 3, - "time_window": 60, - "rejected_code": 503, - "key": "remote_addr" - } + "value": { + "plugins": { + "limit-count": { + "count": 3, + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr" } - }, - "key": "/apisix/global_rules/1" + } }, - "action": "set" + "key": "/apisix/global_rules/1" }]] ) @@ -260,9 +238,7 @@ passed local code, message = t('/apisix/admin/global_rules/1', ngx.HTTP_DELETE, nil, - [[{ - "action": "delete" - }]] + nil ) ngx.say("[delete] code: ", code, " message: ", message) } @@ -284,9 +260,7 @@ GET /t local code = t('/apisix/admin/global_rules/1', ngx.HTTP_DELETE, nil, - [[{ - "action": "delete" - }]] + nil ) ngx.say("[delete] code: ", code) } diff --git a/t/admin/health-check.t b/t/admin/health-check.t index 698d69d11b92..c7c0e1c783fb 100644 --- a/t/admin/health-check.t +++ b/t/admin/health-check.t @@ -78,7 +78,7 @@ __DATA__ } } }]]) - exp_data.node.value.upstream.checks = req_data.upstream.checks + exp_data.value.upstream.checks = req_data.upstream.checks local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, @@ -125,7 +125,7 @@ passed } } }]]) - exp_data.node.value.upstream.checks = req_data.upstream.checks + exp_data.value.upstream.checks = req_data.upstream.checks local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, @@ -341,7 +341,7 @@ GET /t "req_headers": ["User-Agent: curl/7.29.0"] } }]]) - exp_data.node.value.upstream.checks = req_data.upstream.checks + exp_data.value.upstream.checks = req_data.upstream.checks local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, @@ -379,7 +379,7 @@ passed "req_headers": ["User-Agent: curl/7.29.0", "Accept: */*"] } }]]) - exp_data.node.value.upstream.checks = req_data.upstream.checks + exp_data.value.upstream.checks = req_data.upstream.checks local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, @@ -417,7 +417,7 @@ passed "req_headers": ["User-Agent: curl/7.29.0", 2233] } }]]) - exp_data.node.value.upstream.checks = req_data.upstream.checks + exp_data.value.upstream.checks = req_data.upstream.checks local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, @@ -457,7 +457,7 @@ GET /t } } }]]) - exp_data.node.value.upstream.checks = req_data.upstream.checks + exp_data.value.upstream.checks = req_data.upstream.checks local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, @@ -500,7 +500,7 @@ GET /t } } }]]) - exp_data.node.value.upstream.checks = req_data.upstream.checks + exp_data.value.upstream.checks = req_data.upstream.checks local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, diff --git a/t/admin/routes-array-nodes.t b/t/admin/routes-array-nodes.t index c9b141883a28..e210646f17a6 100644 --- a/t/admin/routes-array-nodes.t +++ b/t/admin/routes-array-nodes.t @@ -47,25 +47,22 @@ __DATA__ "uri": "/index.html" }]], [[{ - "node": { - "value": { - "methods": [ - "GET" - ], - "uri": "/index.html", - "desc": "new route", - "upstream": { - "nodes": [{ - "host": "127.0.0.1", - "port": 8080, - "weight": 1 - }], - "type": "roundrobin" - } - }, - "key": "/apisix/routes/1" + "value": { + "methods": [ + "GET" + ], + "uri": "/index.html", + "desc": "new route", + "upstream": { + "nodes": [{ + "host": "127.0.0.1", + "port": 8080, + "weight": 1 + }], + "type": "roundrobin" + } }, - "action": "set" + "key": "/apisix/routes/1" }]] ) @@ -91,25 +88,22 @@ passed ngx.HTTP_GET, nil, [[{ - "node": { - "value": { - "methods": [ - "GET" - ], - "uri": "/index.html", - "desc": "new route", - "upstream": { - "nodes": [{ - "host": "127.0.0.1", - "port": 8080, - "weight": 1 - }], - "type": "roundrobin" - } - }, - "key": "/apisix/routes/1" + "value": { + "methods": [ + "GET" + ], + "uri": "/index.html", + "desc": "new route", + "upstream": { + "nodes": [{ + "host": "127.0.0.1", + "port": 8080, + "weight": 1 + }], + "type": "roundrobin" + } }, - "action": "get" + "key": "/apisix/routes/1" }]] ) diff --git a/t/admin/routes.t b/t/admin/routes.t index 0afdcd0e6b9a..36b2f0b62a8a 100644 --- a/t/admin/routes.t +++ b/t/admin/routes.t @@ -45,23 +45,20 @@ __DATA__ "uri": "/index.html" }]], [[{ - "node": { - "value": { - "methods": [ - "GET" - ], - "uri": "/index.html", - "desc": "new route", - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" - } - }, - "key": "/apisix/routes/1" + "value": { + "methods": [ + "GET" + ], + "uri": "/index.html", + "desc": "new route", + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" + } }, - "action": "set" + "key": "/apisix/routes/1" }]] ) @@ -87,23 +84,20 @@ passed ngx.HTTP_GET, nil, [[{ - "node": { - "value": { - "methods": [ - "GET" - ], - "uri": "/index.html", - "desc": "new route", - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" - } - }, - "key": "/apisix/routes/1" + "value": { + "methods": [ + "GET" + ], + "uri": "/index.html", + "desc": "new route", + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" + } }, - "action": "get" + "key": "/apisix/routes/1" }]] ) @@ -128,9 +122,7 @@ passed local code, message = t('/apisix/admin/routes/1', ngx.HTTP_DELETE, nil, - [[{ - "action": "delete" - }]] + nil ) ngx.say("[delete] code: ", code, " message: ", message) } @@ -152,9 +144,7 @@ GET /t local code = t('/apisix/admin/routes/not_found', ngx.HTTP_DELETE, nil, - [[{ - "action": "delete" - }]] + nil ) ngx.say("[delete] code: ", code) } @@ -186,21 +176,18 @@ GET /t "uri": "/index.html" }]], [[{ - "node": { - "value": { - "methods": [ - "GET" - ], - "uri": "/index.html", - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" - } + "value": { + "methods": [ + "GET" + ], + "uri": "/index.html", + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" } - }, - "action": "create" + } }]] ) @@ -212,7 +199,7 @@ GET /t ngx.say("[push] code: ", code, " message: ", message) - local id = string.sub(res.node.key, #"/apisix/routes/" + 1) + local id = string.sub(res.key, #"/apisix/routes/" + 1) code, message = t('/apisix/admin/routes/' .. id, ngx.HTTP_DELETE, nil, @@ -251,18 +238,15 @@ GET /t "uri": "/index.html" }]], [[{ - "node": { - "value": { - "uri": "/index.html", - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" - } + "value": { + "uri": "/index.html", + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" } - }, - "action": "set" + } }]] ) @@ -304,20 +288,17 @@ GET /t "uri": "/index.html" }]], [[{ - "node": { - "value": { - "uri": "/index.html", - "plugins": { - "limit-count": { - "count": 2, - "time_window": 60, - "rejected_code": 503, - "key": "remote_addr" - } + "value": { + "uri": "/index.html", + "plugins": { + "limit-count": { + "count": 2, + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr" } } - }, - "action": "set" + } }]] ) @@ -724,20 +705,17 @@ GET /t "uri": "/index.html" }]], [[{ - "node": { - "value": { - "host": "*.foo.com", - "uri": "/index.html", - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" - } - }, - "key": "/apisix/routes/1" + "value": { + "host": "*.foo.com", + "uri": "/index.html", + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" + } }, - "action": "set" + "key": "/apisix/routes/1" }]] ) @@ -838,20 +816,17 @@ GET /t "uri": "/index.html" }]], [[{ - "node": { - "value": { - "remote_addr": "127.0.0.1", - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" + "value": { + "remote_addr": "127.0.0.1", + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 }, - "uri": "/index.html" + "type": "roundrobin" }, - "key": "/apisix/routes/1" + "uri": "/index.html" }, - "action": "set" + "key": "/apisix/routes/1" }]] ) @@ -888,20 +863,17 @@ passed "uri": "/index.html" }]], [[{ - "node": { - "value": { - "remote_addr": "127.0.0.0/24", - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" + "value": { + "remote_addr": "127.0.0.0/24", + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 }, - "uri": "/index.html" + "type": "roundrobin" }, - "key": "/apisix/routes/1" + "uri": "/index.html" }, - "action": "set" + "key": "/apisix/routes/1" }]] ) @@ -963,15 +935,15 @@ GET /t local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, [[{ - "methods": ["GET", "POST", "PUT", "DELETE", "PATCH", - "HEAD", "OPTIONS", "CONNECT", "TRACE"], - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" + "methods": ["GET", "POST", "PUT", "DELETE", "PATCH", + "HEAD", "OPTIONS", "CONNECT", "TRACE"], + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 }, - "uri": "/index.html" + "type": "roundrobin" + }, + "uri": "/index.html" }]] ) @@ -999,13 +971,10 @@ passed "uri": "/patch_test" }]], [[{ - "node": { - "value": { - "uri": "/patch_test" - }, - "key": "/apisix/routes/1" + "value": { + "uri": "/patch_test" }, - "action": "set" + "key": "/apisix/routes/1" }]] ) @@ -1040,23 +1009,20 @@ passed "desc": "new route" }]], [[{ - "node": { - "value": { - "methods": [ - "GET" - ], - "uri": "/patch_test", - "desc": "new route", - "upstream": { - "nodes": { - "127.0.0.2:8080": 1 - }, - "type": "roundrobin" - } - }, - "key": "/apisix/routes/1" + "value": { + "methods": [ + "GET" + ], + "uri": "/patch_test", + "desc": "new route", + "upstream": { + "nodes": { + "127.0.0.2:8080": 1 + }, + "type": "roundrobin" + } }, - "action": "set" + "key": "/apisix/routes/1" }]] ) @@ -1084,13 +1050,10 @@ passed "methods": ["GET", "DELETE", "PATCH", "POST", "PUT"] }]], [[{ - "node": { - "value": { - "methods": ["GET", "DELETE", "PATCH", "POST", "PUT"] - }, - "key": "/apisix/routes/1" + "value": { + "methods": ["GET", "DELETE", "PATCH", "POST", "PUT"] }, - "action": "set" + "key": "/apisix/routes/1" }]] ) @@ -1118,13 +1081,10 @@ passed "methods": ["GET", "POST"] }]], [[{ - "node": { - "value": { - "methods": ["GET", "POST"] - }, - "key": "/apisix/routes/1" + "value": { + "methods": ["GET", "POST"] }, - "action": "set" + "key": "/apisix/routes/1" }]] ) @@ -1150,15 +1110,12 @@ passed ngx.HTTP_PATCH, '["POST"]', [[{ - "node": { - "value": { - "methods": [ - "POST" - ] - }, - "key": "/apisix/routes/1" + "value": { + "methods": [ + "POST" + ] }, - "action": "set" + "key": "/apisix/routes/1" }]] ) @@ -1184,13 +1141,10 @@ passed ngx.HTTP_PATCH, '"/patch_uri_test"', [[{ - "node": { - "value": { - "uri": "/patch_uri_test" - }, - "key": "/apisix/routes/1" + "value": { + "uri": "/patch_uri_test" }, - "action": "set" + "key": "/apisix/routes/1" }]] ) @@ -1226,23 +1180,20 @@ passed "uri": "/index.html" }]], [[{ - "node": { - "value": { - "methods": [ - "GET" - ], - "uri": "/index.html", - "desc": "new route", - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" - } - }, - "key": "/apisix/routes/1" + "value": { + "methods": [ + "GET" + ], + "uri": "/index.html", + "desc": "new route", + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" + } }, - "action": "set" + "key": "/apisix/routes/1" }]] ) @@ -1278,10 +1229,8 @@ passed "desc": "new route" }]], [[{ - "node": { - "value": { - "hosts": ["foo.com", "*.bar.com"] - } + "value": { + "hosts": ["foo.com", "*.bar.com"] } }]] ) @@ -1353,10 +1302,8 @@ GET /t "desc": "new route" }]], [[{ - "node": { - "value": { - "remote_addrs": ["127.0.0.1", "192.0.0.1/8", "::1", "fe80::/32"] - } + "value": { + "remote_addrs": ["127.0.0.1", "192.0.0.1/8", "::1", "fe80::/32"] } }]] ) @@ -1393,10 +1340,8 @@ passed "desc": "new route" }]=], [=[{ - "node": { - "value": { - "vars": [["arg_name", "==", "json"], ["arg_age", ">", 18]] - } + "value": { + "vars": [["arg_name", "==", "json"], ["arg_age", ">", 18]] } }]=] ) @@ -1432,10 +1377,8 @@ passed } }]=], [=[{ - "node": { - "value": { - "filter_func": "function(vars) return vars.arg_name == 'json' end" - } + "value": { + "filter_func": "function(vars) return vars.arg_name == 'json' end" } }]=] ) @@ -1545,12 +1488,10 @@ location /t { ngx.HTTP_GET, nil, [[{ - "node": { - "value": { - "uri": "/index.html" - }, - "key": "/apisix/routes/1" - } + "value": { + "uri": "/index.html" + }, + "key": "/apisix/routes/1" }]] ) @@ -1600,7 +1541,7 @@ location /t { }, "uri": "/index.html" }]], - [[{"action": "create"}]] + nil ) if code >= 300 then @@ -1687,13 +1628,10 @@ GET /t "uri": "/index.html" }]], [[{ - "node": { - "value": { - "priority": 0 - }, - "key": "/apisix/routes/1" + "value": { + "priority": 0 }, - "action": "set" + "key": "/apisix/routes/1" }]] ) @@ -1730,13 +1668,10 @@ passed "priority": 1 }]], [[{ - "node": { - "value": { - "priority": 1 - }, - "key": "/apisix/routes/1" + "value": { + "priority": 1 }, - "action": "set" + "key": "/apisix/routes/1" }]] ) @@ -1902,13 +1837,10 @@ passed "uri": "/index.html" }]], [[{ - "node": { - "value": { - "name": "test name" - }, - "key": "/apisix/routes/1" + "value": { + "name": "test name" }, - "action": "set" + "key": "/apisix/routes/1" }]] ) diff --git a/t/admin/services.t b/t/admin/services.t index 17c1b28caf55..2236dd4c196e 100644 --- a/t/admin/services.t +++ b/t/admin/services.t @@ -43,19 +43,16 @@ __DATA__ "desc": "new service" }]], [[{ - "node": { - "value": { - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" + "value": { + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 }, - "desc": "new service" + "type": "roundrobin" }, - "key": "/apisix/services/1" + "desc": "new service" }, - "action": "set" + "key": "/apisix/services/1" }]] ) @@ -81,19 +78,16 @@ passed ngx.HTTP_GET, nil, [[{ - "node": { - "value": { - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" + "value": { + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 }, - "desc": "new service" + "type": "roundrobin" }, - "key": "/apisix/services/1" + "desc": "new service" }, - "action": "get" + "key": "/apisix/services/1" }]] ) @@ -118,9 +112,7 @@ passed local code, message = t('/apisix/admin/services/1', ngx.HTTP_DELETE, nil, - [[{ - "action": "delete" - }]] + nil ) ngx.say("[delete] code: ", code, " message: ", message) } @@ -142,9 +134,7 @@ GET /t local code = t('/apisix/admin/services/not_found', ngx.HTTP_DELETE, nil, - [[{ - "action": "delete" - }]] + nil ) ngx.say("[delete] code: ", code) @@ -175,17 +165,14 @@ GET /t } }]], [[{ - "node": { - "value": { - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" - } + "value": { + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" } - }, - "action": "create" + } }]] ) @@ -197,13 +184,12 @@ GET /t ngx.say("[push] code: ", code, " message: ", message) - local id = string.sub(res.node.key, #"/apisix/services/" + 1) + -- to compatible with etcd v3 + local id = string.sub(res.key, #"/apisix/services/" + 1) code, message = t('/apisix/admin/services/' .. id, ngx.HTTP_DELETE, nil, - [[{ - "action": "delete" - }]] + nil ) ngx.say("[delete] code: ", code, " message: ", message) } @@ -235,17 +221,14 @@ GET /t } }]], [[{ - "node": { - "value": { - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" - } + "value": { + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" } - }, - "action": "set" + } }]] ) @@ -286,19 +269,16 @@ GET /t } }]], [[{ - "node": { - "value": { - "plugins": { - "limit-count": { - "count": 2, - "time_window": 60, - "rejected_code": 503, - "key": "remote_addr" - } + "value": { + "plugins": { + "limit-count": { + "count": 2, + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr" } } - }, - "action": "set" + } }]] ) @@ -417,13 +397,10 @@ GET /t "plugins": {} }]], [[{ - "node": { - "value": { - "plugins": {} - }, - "key": "/apisix/services/1" + "value": { + "plugins": {} }, - "action": "set" + "key": "/apisix/services/1" }]] ) @@ -645,19 +622,16 @@ GET /t "desc": "new 20 service" }]], [[{ - "node": { - "value": { - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" + "value": { + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 }, - "desc": "new 20 service" + "type": "roundrobin" }, - "key": "/apisix/services/1" + "desc": "new 20 service" }, - "action": "set" + "key": "/apisix/services/1" }]] ) @@ -685,19 +659,16 @@ passed "desc": "new 19 service" }]], [[{ - "node": { - "value": { - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" + "value": { + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 }, - "desc": "new 19 service" + "type": "roundrobin" }, - "key": "/apisix/services/1" + "desc": "new 19 service" }, - "action": "set" + "key": "/apisix/services/1" }]] ) @@ -731,16 +702,14 @@ passed } }]], [[{ - "node": { - "value": { - "upstream": { - "nodes": { - "127.0.0.1:8080": 1, - "127.0.0.1:8081": 3, - "127.0.0.1:8082": 4 - }, - "type": "roundrobin" - } + "value": { + "upstream": { + "nodes": { + "127.0.0.1:8080": 1, + "127.0.0.1:8081": 3, + "127.0.0.1:8082": 4 + }, + "type": "roundrobin" } } }]] @@ -776,19 +745,16 @@ passed "desc": "new 22 service" }]], [[{ - "node": { - "value": { - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" + "value": { + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 }, - "desc": "new 22 service" + "type": "roundrobin" }, - "key": "/apisix/services/1" + "desc": "new 22 service" }, - "action": "set" + "key": "/apisix/services/1" }]] ) @@ -814,19 +780,16 @@ passed ngx.HTTP_PATCH, '"new 23 service"', [[{ - "node": { - "value": { - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" + "value": { + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 }, - "desc": "new 23 service" + "type": "roundrobin" }, - "key": "/apisix/services/1" + "desc": "new 23 service" }, - "action": "set" + "key": "/apisix/services/1" }]] ) @@ -858,15 +821,13 @@ passed "type": "roundrobin" }]], [[{ - "node": { - "value": { - "upstream": { - "nodes": { - "127.0.0.2:8081": 3, - "127.0.0.3:8082": 4 - }, - "type": "roundrobin" - } + "value": { + "upstream": { + "nodes": { + "127.0.0.2:8081": 3, + "127.0.0.3:8082": 4 + }, + "type": "roundrobin" } } }]] @@ -1028,19 +989,16 @@ GET /t "name": "test service name" }]], [[{ - "node": { - "value": { - "upstream": { - "nodes": { - "127.0.0.1:8080": 1 - }, - "type": "roundrobin" + "value": { + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 }, - "name": "test service name" + "type": "roundrobin" }, - "key": "/apisix/services/1" + "name": "test service name" }, - "action": "set" + "key": "/apisix/services/1" }]] ) diff --git a/t/core/etcd-auth-fail.t b/t/core/etcd-auth-fail.t index dfeaffee178f..c7e8e711f810 100644 --- a/t/core/etcd-auth-fail.t +++ b/t/core/etcd-auth-fail.t @@ -15,10 +15,13 @@ # limitations under the License. # BEGIN { - $ENV{"ETCD_ENABLE_AUTH"} = "false" + $ENV{"ETCD_ENABLE_AUTH"} = "false"; + # $ENV{"ETCDCTL_API"} = "2" } -use t::APISIX 'no_plan'; +use t::APISIX; + +plan(skip_all => "etcd is too old, skip v3 protocol"); repeat_each(1); no_long_string(); @@ -28,13 +31,13 @@ log_level("info"); # Authentication is enabled at etcd and credentials are set system('etcdctl --endpoints="http://127.0.0.1:2379" -u root:5tHkHhYkjr6cQY user add root:5tHkHhYkjr6cQY'); system('etcdctl --endpoints="http://127.0.0.1:2379" -u root:5tHkHhYkjr6cQY auth enable'); -system('etcdctl --endpoints="http://127.0.0.1:2379" -u root:5tHkHhYkjr6cQY role revoke --path "/*" -rw guest'); +system('etcdctl --endpoints="http://127.0.0.1:2379" -u root:5tHkHhYkjr6cQY role revoke guest -path "/*" -readwrite'); run_tests; # Authentication is disabled at etcd & guest access is granted system('etcdctl --endpoints="http://127.0.0.1:2379" -u root:5tHkHhYkjr6cQY auth disable'); -system('etcdctl --endpoints="http://127.0.0.1:2379" -u root:5tHkHhYkjr6cQY role grant --path "/*" -rw guest'); +system('etcdctl --endpoints="http://127.0.0.1:2379" -u root:5tHkHhYkjr6cQY role grant guest -path "/*" -readwrite'); __DATA__ diff --git a/t/core/etcd-auth.t b/t/core/etcd-auth.t index 3051a68ffbde..3753ca850d2b 100644 --- a/t/core/etcd-auth.t +++ b/t/core/etcd-auth.t @@ -15,26 +15,30 @@ # limitations under the License. # BEGIN { - $ENV{"ETCD_ENABLE_AUTH"} = "true" + $ENV{"ETCD_ENABLE_AUTH"} = "true"; + # $ENV{"ETCDCTL_API"} = "2" } -use t::APISIX 'no_plan'; +use t::APISIX; repeat_each(1); no_long_string(); no_root_location(); log_level("info"); +plan(skip_all => "etcd is too old, skip v3 protocol"); + # Authentication is enabled at etcd and credentials are set system('etcdctl --endpoints="http://127.0.0.1:2379" -u root:5tHkHhYkjr6cQY user add root:5tHkHhYkjr6cQY'); system('etcdctl --endpoints="http://127.0.0.1:2379" -u root:5tHkHhYkjr6cQY auth enable'); -system('etcdctl --endpoints="http://127.0.0.1:2379" -u root:5tHkHhYkjr6cQY role revoke --path "/*" -rw guest'); +system('etcdctl --endpoints="http://127.0.0.1:2379" -u root:5tHkHhYkjr6cQY role revoke guest -path "/*" -readwrite'); run_tests; # Authentication is disabled at etcd & guest access is granted system('etcdctl --endpoints="http://127.0.0.1:2379" -u root:5tHkHhYkjr6cQY auth disable'); -system('etcdctl --endpoints="http://127.0.0.1:2379" -u root:5tHkHhYkjr6cQY role grant --path "/*" -rw guest'); +system('etcdctl --endpoints="http://127.0.0.1:2379" -u root:5tHkHhYkjr6cQY role grant guest -path "/*" -readwrite'); + __DATA__ @@ -48,7 +52,7 @@ __DATA__ core.etcd.set(key, val) local res, err = core.etcd.get(key) ngx.say(res.body.node.value) - core.etcd.delete(val) + core.etcd.delete(key) } } --- request diff --git a/t/core/v3/etcd-auth-v3-fail.t b/t/core/v3/etcd-auth-v3-fail.t new file mode 100644 index 000000000000..491552a6e41d --- /dev/null +++ b/t/core/v3/etcd-auth-v3-fail.t @@ -0,0 +1,80 @@ +# +# 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. +# +BEGIN { + $ENV{"ETCD_ENABLE_AUTH"} = "false"; + $ENV{"ETCDCTL_API"} = "3" +} + +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +log_level("info"); + +add_block_preprocessor(sub { + my ($block) = @_; + + my $init_by_lua_block = <<_EOC_; + fetch_local_conf = require("apisix.core.config_local").local_conf + + function check_val(res) + local local_conf, err = fetch_local_conf() + if not local_conf then + return nil, nil, err + end + ver = local_conf.etcd.version + + if ver == "v3" then + ngx.say(res.body.kvs[1].value) + else + ngx.say(res.body.node.value) + end + end +_EOC_ + $block->set_value("init_by_lua_block", $init_by_lua_block); +}); + +# Authentication is enabled at etcd and credentials are set +system('etcdctl --endpoints="http://127.0.0.1:2379" --user root:5tHkHhYkjr6cQY user add root:5tHkHhYkjr6cQY'); +system('etcdctl --endpoints="http://127.0.0.1:2379" --user root:5tHkHhYkjr6cQY auth enable'); + +run_tests; + +# Authentication is disabled at etcd & guest access is granted +system('etcdctl --endpoints="http://127.0.0.1:2379" --user root:5tHkHhYkjr6cQY auth disable'); + + +__DATA__ + +=== TEST 1: Set and Get a value pass with authentication +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local key = "/test_key" + local val = "test_value" + local res, err = core.etcd.set(key, val) + ngx.say(res.body.error) + } + } +--- request +GET /t +--- response_body +etcdserver: user name is empty +--- no_error_log +[error] diff --git a/t/core/v3/etcd-auth-v3.t b/t/core/v3/etcd-auth-v3.t new file mode 100644 index 000000000000..9a3b79cf67f3 --- /dev/null +++ b/t/core/v3/etcd-auth-v3.t @@ -0,0 +1,82 @@ +# +# 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. +# +BEGIN { + $ENV{"ETCD_ENABLE_AUTH"} = "true"; + $ENV{"ETCDCTL_API"} = "3" +} + +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +log_level("info"); + +add_block_preprocessor(sub { + my ($block) = @_; + + my $init_by_lua_block = <<_EOC_; + fetch_local_conf = require("apisix.core.config_local").local_conf + + function check_val(res) + local local_conf, err = fetch_local_conf() + if not local_conf then + return nil, nil, err + end + ver = local_conf.etcd.version + + if ver == "v3" then + ngx.say(res.body.kvs[1].value) + else + ngx.say(res.body.node.value) + end + end +_EOC_ + $block->set_value("init_by_lua_block", $init_by_lua_block); +}); + +# Authentication is enabled at etcd and credentials are set +system('etcdctl --endpoints="http://127.0.0.1:2379" --user root:5tHkHhYkjr6cQY user add root:5tHkHhYkjr6cQY'); +system('etcdctl --endpoints="http://127.0.0.1:2379" --user root:5tHkHhYkjr6cQY auth enable'); + +run_tests; + +# Authentication is disabled at etcd & guest access is granted +system('etcdctl --endpoints="http://127.0.0.1:2379" --user root:5tHkHhYkjr6cQY auth disable'); + + +__DATA__ + +=== TEST 1: Set and Get a value pass with authentication +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local key = "/test_key" + local val = "test_value" + core.etcd.set(key, val) + local res, err = core.etcd.get(key) + check_val(res) + core.etcd.delete(key) + } + } +--- request +GET /t +--- response_body +test_value +--- no_error_log +[error] diff --git a/t/etcd.t b/t/etcd.t new file mode 100644 index 000000000000..5dd9370cd5ac --- /dev/null +++ b/t/etcd.t @@ -0,0 +1,87 @@ +# +# 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); +no_long_string(); +no_root_location(); +no_shuffle(); +log_level("info"); + +run_tests; + +__DATA__ + +=== TEST 42: set route with ttl +--- config +location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local core = require("apisix.core") + -- set + local code, body, res = t('/apisix/admin/routes/1?ttl=1', + ngx.HTTP_PUT, + [[{ + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" + }, + "uri": "/index.html" + }]] + ) + + ngx.say("code: ", code) + ngx.say(body) + + -- get + code, body = t('/apisix/admin/routes/1?ttl=1', + ngx.HTTP_GET, + nil, + [[{ + "value": { + "uri": "/index.html" + }, + "key": "/apisix/routes/1" + }]] + ) + + ngx.say("code: ", code) + ngx.say(body) + + ngx.sleep(2) + + -- get again + code, body, res = t('/apisix/admin/routes/1', ngx.HTTP_GET) + + ngx.say("code: ", code) + ngx.say("message: ", core.json.decode(body).message) + } +} +--- request +GET /t +--- response_body +code: 200 +passed +code: 200 +passed +code: 404 +message: Key not found +--- no_error_log +[error] +--- timeout: 5 \ No newline at end of file diff --git a/t/lib/test_admin.lua b/t/lib/test_admin.lua index dc245c3bb7b7..1ae13340dcbc 100644 --- a/t/lib/test_admin.lua +++ b/t/lib/test_admin.lua @@ -20,6 +20,8 @@ local aes = require "resty.aes" local ngx_encode_base64 = ngx.encode_base64 local str_find = string.find local dir_names = {} +local local_conf = require("apisix.core.config_local").local_conf() +local etcd_version = local_conf.etcd.version or "v2" local _M = {} @@ -143,6 +145,7 @@ function _M.test(uri, method, body, pattern, headers) end local httpc = http.new() + local key = uri -- https://github.com/ledgetech/lua-resty-http uri = ngx.var.scheme .. "://" .. ngx.var.server_addr .. ":" .. ngx.var.server_port .. uri @@ -163,11 +166,45 @@ function _M.test(uri, method, body, pattern, headers) return res.status, res.body end - if pattern == nil then - return res.status, "passed", res.body + local res_data = json.decode(res.body) + + if etcd_version == "v3" then + -- set would not return kv anymore in etcd v3 + --if method == "PATCH" then + -- need `res.body` for admin/plugins-reload.t + -- return 200, "passed", res_data + --end + + -- v3 return num of deleted k-v + if method == "DELETE" then + if not res_data.deleted then + return 404, "Key not found", res_data + else + return 200, "passed", res_data + end + end + -- to achieve compatibility with v2 in test file, we need some tricky selection + -- Since in v3, all k-v pairs are under key `kvs`, and multiple pairs would under `kvs` + -- While in v2, if not as dir pairs are under key `node` + -- but if as dir, pairs would be unber key `nodes` inside `node` + -- and multiple pairs would be under `nodes` + if not res_data.kvs then + return 404, "Key not found, " .. key, res_data + end + if pattern == nil then + return res.status, "passed", res.body + end + res_data = res_data.kvs + res_data = #res_data == 1 and res_data[1] or res_data + else + if pattern == nil then + return res.status, "passed", res.body + end + res_data = res_data.node and res_data.node or res_data + res_data = res_data.nodes and res_data.nodes or res_data + res_data = #res_data == 1 and res_data[1] or res_data end - local res_data = json.decode(res.body) local ok, err = _M.comp_tab(pattern, res_data) if not ok then return 500, "failed, " .. err, res_data diff --git a/t/node/invalid-route.t b/t/node/invalid-route.t index 74dbb77f075d..4d1e3f8f1e8b 100644 --- a/t/node/invalid-route.t +++ b/t/node/invalid-route.t @@ -47,8 +47,6 @@ GET /t qr/\[error\].*/ --- grep_error_log_out eval qr{invalid item data of \[/apisix/routes/1\], val: mexxxxxxxxxxxxxxx, it shoud be a object} ---- response_body_like eval -qr/"value":"mexxxxxxxxxxxxxxx"/ diff --git a/t/node/invalid-service.t b/t/node/invalid-service.t index 0b7423284cea..5b43ae2a9422 100644 --- a/t/node/invalid-service.t +++ b/t/node/invalid-service.t @@ -48,8 +48,6 @@ GET /t qr/\[error\].*/ --- grep_error_log_out eval qr{invalid item data of \[/apisix/services/1\], val: mexxxxxxxxxxxxxxx, it shoud be a object} ---- response_body_like eval -qr/"value":"mexxxxxxxxxxxxxxx"/ @@ -89,8 +87,6 @@ qr{invalid item data of \[/apisix/services/1\], val: mexxxxxxxxxxxxxxx, it shoud } --- request GET /t ---- response_body_like eval -qr/"nodes":\{"127.0.0.1:1980":1\}/ --- grep_error_log eval qr/\[error\].*/ --- grep_error_log_out eval diff --git a/t/node/invalid-upstream.t b/t/node/invalid-upstream.t index ee8edc2a04b2..82adce07ea6e 100644 --- a/t/node/invalid-upstream.t +++ b/t/node/invalid-upstream.t @@ -47,8 +47,6 @@ GET /t qr/\[error\].*/ --- grep_error_log_out eval qr{invalid item data of \[/apisix/upstreams/1\], val: mexxxxxxxxxxxxxxx, it shoud be a object} ---- response_body_like eval -qr/"value":"mexxxxxxxxxxxxxxx"/ @@ -136,8 +134,6 @@ GET /t } --- request GET /t ---- response_body_like eval -qr/"nodes":\{"127.0.0.1:1980":1\}/ --- no_error_log [error]