diff --git a/.github/workflows/centos7-ci.yml b/.github/workflows/centos7-ci.yml index 60ea0a15db7c..dc08b0fd384e 100644 --- a/.github/workflows/centos7-ci.yml +++ b/.github/workflows/centos7-ci.yml @@ -69,6 +69,10 @@ jobs: echo "type=last" >>$GITHUB_OUTPUT fi + - name: Free disk space + run: | + bash ./ci/free_disk_space.sh + - name: Linux launch common services run: | make ci-env-up project_compose_ci=ci/pod/docker-compose.common.yml @@ -153,8 +157,6 @@ jobs: - if: ${{ steps.cache-images.outputs.cache-hit != 'true' }} name: Save docker images run: | - # free disk space - bash ./ci/free_disk_space.sh echo "start backing up, $(date)" bash ./ci/backup-docker-images.sh ${{ steps.test_env.outputs.type }} echo "backup done, $(date)" diff --git a/.github/workflows/fips.yml b/.github/workflows/fips.yml index 854417503cfd..5b2a004d72b2 100644 --- a/.github/workflows/fips.yml +++ b/.github/workflows/fips.yml @@ -85,6 +85,10 @@ jobs: echo "type=last" >>$GITHUB_OUTPUT fi + - name: Free disk space + run: | + bash ./ci/free_disk_space.sh + - name: Linux launch common services run: | make ci-env-up project_compose_ci=ci/pod/docker-compose.common.yml @@ -145,8 +149,6 @@ jobs: - if: ${{ steps.cache-images.outputs.cache-hit != 'true' }} name: Save docker images run: | - # free disk space - bash ./ci/free_disk_space.sh echo "start backing up, $(date)" bash ./ci/backup-docker-images.sh ${{ steps.test_env.outputs.type }} echo "backup done, $(date)" diff --git a/.github/workflows/gm-cron.yaml b/.github/workflows/gm-cron.yaml index 9acb63dbbd59..cc3e0d81d482 100644 --- a/.github/workflows/gm-cron.yaml +++ b/.github/workflows/gm-cron.yaml @@ -81,6 +81,10 @@ jobs: echo "type=last" >>$GITHUB_OUTPUT fi + - name: Free disk space + run: | + bash ./ci/free_disk_space.sh + - name: Linux launch common services run: | make ci-env-up project_compose_ci=ci/pod/docker-compose.common.yml @@ -160,8 +164,6 @@ jobs: - if: ${{ steps.cache-images.outputs.cache-hit != 'true' }} name: Save docker images run: | - # free disk space - bash ./ci/free_disk_space.sh echo "start backing up, $(date)" bash ./ci/backup-docker-images.sh ${{ steps.test_env.outputs.type }} echo "backup done, $(date)" diff --git a/CHANGELOG.md b/CHANGELOG.md index e2fd69414cf9..2fa43de447c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ title: Changelog ## Table of Contents +- [3.2.2](#322) - [3.2.1](#321) - [3.2.0](#320) - [3.1.0](#310) @@ -68,6 +69,28 @@ title: Changelog - [0.7.0](#070) - [0.6.0](#060) +## 3.2.2 + +### Bugfix + +- Fix the issue where the upstream key cannot be load balanced through `mqtt_client_id` [#9450](https://github.com/apache/apisix/pull/9450) +- Fix `hold_body_chunk` cannot be used in multiple plugins [#9266](https://github.com/apache/apisix/pull/9266) +- Fix domain nodes can cause health check leaks [#9090](https://github.com/apache/apisix/pull/9090) +- The upstream referenced by the traffic-split plugin can be deleted, causing the plugin to fail [#9044](https://github.com/apache/apisix/pull/9044) +- Fix `allow_origins_by_regex` is not nil, and other domain names will also be verified [#9028](https://github.com/apache/apisix/pull/9028) +- When configuring domain names in the traffic-split plugin, they will only be resolved once. [#9332](https://github.com/apache/apisix/pull/9332) +- The syslog plugin has an error in the log encoding format, making the plugin unusable [#9425](https://github.com/apache/apisix/pull/9425) +- Fix `http-logger` is missing the default request path `/`, causing the log push to fail [#9472](https://github.com/apache/apisix/pull/9472) +- When using Wolf-rbac, the consumer cannot use other plugins at the same time [#9298](https://github.com/apache/apisix/pull/9298) +- The incorrect log format of the `splunk-hec-logging` plugin causes the request to fail to be pushed [#9478](https://github.com/apache/apisix/pull/9478) +- Fix `opentelemetry` and `grpc-transcode` plugins cannot be used at the same time [#9606](https://github.com/apache/apisix/pull/9606) +- When adding a header `key: value` in the `response-rewrite` plugin, the value cannot be configured as one character [#9372](https://github.com/apache/apisix/pull/9372) +- Fix etcd enable keepalive [#9420](https://github.com/apache/apisix/pull/9420) +- Use a single long http connection to watch all resources for etcd [#9456](https://github.com/apache/apisix/pull/9456) +- Fix authentication bypass in the jwt-auth plugin [#9837](https://github.com/apache/apisix/pull/9837) +- Fix `update_count` is reset once updated, cause cache key conflict [#9811](https://github.com/apache/apisix/pull/9811) +- Fix(consumer): etcd connection failed during startup and still works [#9077](https://github.com/apache/apisix/pull/9077) + ## 3.2.1 ### Bugfix diff --git a/Makefile b/Makefile index abe654407bc7..fb572714a156 100644 --- a/Makefile +++ b/Makefile @@ -337,9 +337,6 @@ install: runtime $(ENV_INSTALL) -d $(ENV_INST_LUADIR)/apisix/plugins/serverless $(ENV_INSTALL) apisix/plugins/serverless/*.lua $(ENV_INST_LUADIR)/apisix/plugins/serverless/ - $(ENV_INSTALL) -d $(ENV_INST_LUADIR)/apisix/plugins/slslog - $(ENV_INSTALL) apisix/plugins/slslog/*.lua $(ENV_INST_LUADIR)/apisix/plugins/slslog/ - $(ENV_INSTALL) -d $(ENV_INST_LUADIR)/apisix/plugins/syslog $(ENV_INSTALL) apisix/plugins/syslog/*.lua $(ENV_INST_LUADIR)/apisix/plugins/syslog/ diff --git a/apisix/admin/upstreams.lua b/apisix/admin/upstreams.lua index a85d24d8d9e1..687e09cd49e5 100644 --- a/apisix/admin/upstreams.lua +++ b/apisix/admin/upstreams.lua @@ -15,13 +15,17 @@ -- limitations under the License. -- local core = require("apisix.core") +local config_util = require("apisix.core.config_util") +local router = require("apisix.router") local get_routes = require("apisix.router").http_routes local get_services = require("apisix.http.service").services +local get_plugin_configs = require("apisix.plugin_config").plugin_configs +local get_consumers = require("apisix.consumer").consumers +local get_consumer_groups = require("apisix.consumer_group").consumer_groups local apisix_upstream = require("apisix.upstream") local resource = require("apisix.admin.resource") local tostring = tostring local ipairs = ipairs -local type = type local function check_conf(id, conf, need_id) @@ -34,31 +38,96 @@ local function check_conf(id, conf, need_id) end -local function delete_checker(id) - local routes, routes_ver = get_routes() - if routes_ver and routes then - for _, route in ipairs(routes) do - if type(route) == "table" and route.value - and route.value.upstream_id - and tostring(route.value.upstream_id) == id then - return 400, {error_msg = "can not delete this upstream," - .. " route [" .. route.value.id +local function up_id_in_plugins(plugins, up_id) + if plugins and plugins["traffic-split"] + and plugins["traffic-split"].rules then + + for _, rule in ipairs(plugins["traffic-split"].rules) do + local plugin_upstreams = rule.weighted_upstreams + for _, plugin_upstream in ipairs(plugin_upstreams) do + if plugin_upstream.upstream_id + and tostring(plugin_upstream.upstream_id) == up_id then + return true + end + end + end + + return false + end +end + + +local function check_resources_reference(resources, up_id, + only_check_plugin, resources_name) + if resources then + for _, resource in config_util.iterate_values(resources) do + if resource and resource.value then + if up_id_in_plugins(resource.value.plugins, up_id) then + return {error_msg = "can not delete this upstream," + .. " plugin in " + .. resources_name .. " [" + .. resource.value.id + .. "] is still using it now"} + end + + if not only_check_plugin and resource.value.upstream_id + and tostring(resource.value.upstream_id) == up_id then + return {error_msg = "can not delete this upstream, " + .. resources_name .. " [" .. resource.value.id .. "] is still using it now"} + end end end end +end + + +local function delete_checker(id) + local routes = get_routes() + local err_msg = check_resources_reference(routes, id, false, "route") + if err_msg then + return 400, err_msg + end local services, services_ver = get_services() core.log.info("services: ", core.json.delay_encode(services, true)) core.log.info("services_ver: ", services_ver) - if services_ver and services then - for _, service in ipairs(services) do - if type(service) == "table" and service.value - and service.value.upstream_id - and tostring(service.value.upstream_id) == id then - return 400, {error_msg = "can not delete this upstream," - .. " service [" .. service.value.id - .. "] is still using it now"} + local err_msg = check_resources_reference(services, id, false, "service") + if err_msg then + return 400, err_msg + end + + local plugin_configs = get_plugin_configs() + local err_msg = check_resources_reference(plugin_configs, id, true, "plugin_config") + if err_msg then + return 400, err_msg + end + + local consumers = get_consumers() + local err_msg = check_resources_reference(consumers, id, true, "consumer") + if err_msg then + return 400, err_msg + end + + local consumer_groups = get_consumer_groups() + local err_msg = check_resources_reference(consumer_groups, id, true, "consumer_group") + if err_msg then + return 400, err_msg + end + + -- TODO: Refactor router.global_rules and then refactor the following code + local global_rules = router.global_rules + if global_rules and global_rules.values + and #global_rules.values > 0 then + + for _, global_rule in config_util.iterate_values(global_rules.values) do + if global_rule and global_rule.value + and global_rule.value.plugins + and up_id_in_plugins(global_rule.value.plugins, id) then + return 400, {error_msg = "can not delete this upstream," + .. " plugin in global_rule [" + .. global_rule.value.id + .. "] is still using it now"} end end end diff --git a/apisix/cli/snippet.lua b/apisix/cli/snippet.lua index ca7ed3f7c59a..95069a0ab263 100644 --- a/apisix/cli/snippet.lua +++ b/apisix/cli/snippet.lua @@ -28,6 +28,9 @@ upstream apisix_conf_backend { local conf_server = require("apisix.conf_server") conf_server.balancer() } + keepalive 320; + keepalive_requests 1000; + keepalive_timeout 60s; } {% if trusted_ca_cert then %} diff --git a/apisix/consumer.lua b/apisix/consumer.lua index f463a92f6484..83d2aea3ab34 100644 --- a/apisix/consumer.lua +++ b/apisix/consumer.lua @@ -137,13 +137,12 @@ end function _M.init_worker() local err - local config = core.config.new() local cfg = { automatic = true, item_schema = core.schema.consumer, checker = check_consumer, } - if config.type ~= "etcd" then + if core.config.type ~= "etcd" then cfg.filter = filter end diff --git a/apisix/consumer_group.lua b/apisix/consumer_group.lua index 8c17bdd715e5..3be59ec923f9 100644 --- a/apisix/consumer_group.lua +++ b/apisix/consumer_group.lua @@ -39,6 +39,14 @@ function _M.init_worker() end +function _M.consumer_groups() + if not consumer_groups then + return nil, nil + end + return consumer_groups.values, consumer_groups.conf_version +end + + function _M.get(id) return consumer_groups:get(id) end diff --git a/apisix/core/config_etcd.lua b/apisix/core/config_etcd.lua index 4946cc5c22c8..ecb76270452d 100644 --- a/apisix/core/config_etcd.lua +++ b/apisix/core/config_etcd.lua @@ -27,6 +27,10 @@ local json = require("apisix.core.json") local etcd_apisix = require("apisix.core.etcd") local core_str = require("apisix.core.string") local new_tab = require("table.new") +local inspect = require("inspect") +local errlog = require("ngx.errlog") +local log_level = errlog.get_sys_filter_level() +local NGX_INFO = ngx.INFO local check_schema = require("apisix.core.schema").check local exiting = ngx.worker.exiting local insert_tab = table.insert @@ -43,9 +47,14 @@ local xpcall = xpcall local debug = debug local string = string local error = error +local pairs = pairs +local next = next +local assert = assert local rand = math.random local constants = require("apisix.constants") local health_check = require("resty.etcd.health_check") +local semaphore = require("ngx.semaphore") +local tablex = require("pl.tablex") local is_http = ngx.config.subsystem == "http" @@ -58,6 +67,7 @@ if not is_http then end local created_obj = {} local loaded_configuration = {} +local watch_ctx local _M = { @@ -75,6 +85,208 @@ local mt = { } +local get_etcd +do + local etcd_cli + + function get_etcd() + if etcd_cli ~= nil then + return etcd_cli + end + + local _, err + etcd_cli, _, err = etcd_apisix.get_etcd_syncer() + return etcd_cli, err + end +end + + +local function cancel_watch(http_cli) + local res, err = watch_ctx.cli:watchcancel(http_cli) + if res == 1 then + log.info("cancel watch connection success") + else + log.error("cancel watch failed: ", err) + end +end + + +-- append res to the queue and notify pending watchers +local function produce_res(res, err) + if log_level >= NGX_INFO then + log.info("append res: ", inspect(res), ", err: ", inspect(err)) + end + insert_tab(watch_ctx.res, {res=res, err=err}) + for _, sema in pairs(watch_ctx.sema) do + sema:post() + end + table.clear(watch_ctx.sema) +end + + +local function run_watch(premature) + if premature then + return + end + + local local_conf, err = config_local.local_conf() + if not local_conf then + error("no local conf: " .. err) + end + watch_ctx.prefix = local_conf.etcd.prefix .. "/" + + watch_ctx.cli, err = get_etcd() + if not watch_ctx.cli then + error("failed to create etcd instance: " .. string(err)) + end + + local rev = 0 + if loaded_configuration then + local _, res = next(loaded_configuration) + if res then + rev = tonumber(res.headers["X-Etcd-Index"]) + assert(rev > 0, 'invalid res.headers["X-Etcd-Index"]') + end + end + + if rev == 0 then + while true do + local res, err = watch_ctx.cli:get(watch_ctx.prefix) + if not res then + log.error("etcd get: ", err) + ngx_sleep(3) + else + watch_ctx.rev = tonumber(res.body.header.revision) + break + end + end + end + + watch_ctx.rev = rev + 1 + watch_ctx.started = true + + log.warn("main etcd watcher started, revision=", watch_ctx.rev) + for _, sema in pairs(watch_ctx.wait_init) do + sema:post() + end + watch_ctx.wait_init = nil + + local opts = {} + opts.timeout = 50 -- second + opts.need_cancel = true + + ::restart_watch:: + while true do + opts.start_revision = watch_ctx.rev + log.info("restart watchdir: start_revision=", opts.start_revision) + local res_func, err, http_cli = watch_ctx.cli:watchdir(watch_ctx.prefix, opts) + if not res_func then + log.error("watchdir: ", err) + ngx_sleep(3) + goto restart_watch + end + + ::watch_event:: + while true do + local res, err = res_func() + if log_level >= NGX_INFO then + log.info("res_func: ", inspect(res)) + end + + if not res then + if err ~= "closed" and + err ~= "timeout" and + err ~= "broken pipe" + then + log.error("wait watch event: ", err) + end + cancel_watch(http_cli) + break + end + + if res.error then + log.error("wait watch event: ", inspect(res.error)) + cancel_watch(http_cli) + break + end + + if res.result.created then + goto watch_event + end + + if res.result.canceled then + log.warn("watch canceled by etcd, res: ", inspect(res)) + if res.result.compact_revision then + watch_ctx.rev = tonumber(res.result.compact_revision) + log.warn("etcd compacted, compact_revision=", watch_ctx.rev) + produce_res(nil, "compacted") + end + cancel_watch(http_cli) + break + end + + -- cleanup + local min_idx = 0 + for _, idx in pairs(watch_ctx.idx) do + if (min_idx == 0) or (idx < min_idx) then + min_idx = idx + end + end + + for i = 1, min_idx - 1 do + watch_ctx.res[i] = false + end + + if min_idx > 100 then + for k, idx in pairs(watch_ctx.idx) do + watch_ctx.idx[k] = idx - min_idx + 1 + end + -- trim the res table + for i = 1, min_idx - 1 do + table.remove(watch_ctx.res, 1) + end + end + + local rev = tonumber(res.result.header.revision) + if rev > watch_ctx.rev then + watch_ctx.rev = rev + 1 + end + produce_res(res) + end + end +end + + +local function init_watch_ctx(key) + if not watch_ctx then + watch_ctx = { + idx = {}, + res = {}, + sema = {}, + wait_init = {}, + started = false, + } + ngx_timer_at(0, run_watch) + end + + if watch_ctx.started == false then + -- wait until the main watcher is started + local sema, err = semaphore.new() + if not sema then + error(err) + end + watch_ctx.wait_init[key] = sema + while true do + local ok, err = sema:wait(60) + if ok then + break + end + log.error("wait main watcher to start, key: ", key, ", err: ", err) + end + end +end + + local function getkey(etcd_cli, key) if not etcd_cli then return nil, "not inited" @@ -157,45 +369,67 @@ local function flush_watching_streams(self) end -local function http_waitdir(etcd_cli, key, modified_index, timeout) - local opts = {} - opts.start_revision = modified_index - opts.timeout = timeout - opts.need_cancel = true - local res_func, func_err, http_cli = etcd_cli:watchdir(key, opts) - if not res_func then - return nil, func_err +local function http_waitdir(self, etcd_cli, key, modified_index, timeout) + if not watch_ctx.idx[key] then + watch_ctx.idx[key] = 1 end - -- in etcd v3, the 1st res of watch is watch info, useless to us. - -- try twice to skip create info - local res, err = res_func() - if not res or not res.result or not res.result.events then - res, err = res_func() - end + ::iterate_events:: + for i = watch_ctx.idx[key], #watch_ctx.res do + watch_ctx.idx[key] = i + 1 - if http_cli then - local res_cancel, err_cancel = etcd_cli:watchcancel(http_cli) - if res_cancel == 1 then - log.info("cancel watch connection success") - else - log.error("cancel watch failed: ", err_cancel) + local item = watch_ctx.res[i] + if item == false then + goto iterate_events + end + + local res, err = item.res, item.err + if err then + return res, err + end + + -- ignore res with revision smaller then self.prev_index + if tonumber(res.result.header.revision) > self.prev_index then + local res2 + for _, evt in ipairs(res.result.events) do + if evt.kv.key:find(key) == 1 then + if not res2 then + res2 = tablex.deepcopy(res) + table.clear(res2.result.events) + end + insert_tab(res2.result.events, evt) + end + end + + if res2 then + if log_level >= NGX_INFO then + log.info("http_waitdir: ", inspect(res2)) + end + return res2 + end end end - if not res then - return nil, err + -- if no events, wait via semaphore + if not self.watch_sema then + local sema, err = semaphore.new() + if not sema then + error(err) + end + self.watch_sema = sema end - if type(res.result) ~= "table" then - err = "failed to wait etcd dir" - if res.error and res.error.message then - err = err .. ": " .. res.error.message + watch_ctx.sema[key] = self.watch_sema + local ok, err = self.watch_sema:wait(timeout or 60) + watch_ctx.sema[key] = nil + if ok then + goto iterate_events + else + if err ~= "timeout" then + log.error("wait watch event, key=", key, ", err: ", err) end return nil, err end - - return res, err end @@ -213,7 +447,7 @@ local function waitdir(self) if etcd_cli.use_grpc then res, err = grpc_waitdir(self, etcd_cli, key, modified_index, timeout) else - res, err = http_waitdir(etcd_cli, key, modified_index, timeout) + res, err = http_waitdir(self, etcd_cli, key, modified_index, timeout) end if not res then @@ -359,6 +593,10 @@ local function sync_data(self) return nil, "missing 'key' arguments" end + if not self.etcd_cli.use_grpc then + init_watch_ctx(self.key) + end + if self.need_reload then flush_watching_streams(self) @@ -555,22 +793,6 @@ function _M.getkey(self, key) end -local get_etcd -do - local etcd_cli - - function get_etcd() - if etcd_cli ~= nil then - return etcd_cli - end - - local _, err - etcd_cli, _, err = etcd_apisix.get_etcd_syncer() - return etcd_cli, err - end -end - - local function _automatic_fetch(premature, self) if premature then return diff --git a/apisix/core/etcd.lua b/apisix/core/etcd.lua index 99e4bbb6b7ab..b52517cd40b5 100644 --- a/apisix/core/etcd.lua +++ b/apisix/core/etcd.lua @@ -372,9 +372,7 @@ do return nil, nil, err end - if tmp_etcd_cli.use_grpc then - etcd_cli = tmp_etcd_cli - end + etcd_cli = tmp_etcd_cli return tmp_etcd_cli, prefix end diff --git a/apisix/core/pubsub.lua b/apisix/core/pubsub.lua index 25ac46f13eeb..18bb887001c1 100644 --- a/apisix/core/pubsub.lua +++ b/apisix/core/pubsub.lua @@ -185,9 +185,10 @@ function _M.wait(self) end -- recovery of stored pb_store - pb.state(pb_state) + local pb_old_state = pb.state(pb_state) local data, err = pb.decode("PubSubReq", raw_data) + pb.state(pb_old_state) if not data then log.error("pubsub server receives undecodable data, err: ", err) send_error(ws, 0, "wrong command") diff --git a/apisix/core/response.lua b/apisix/core/response.lua index b934d94b93af..cfbac1467341 100644 --- a/apisix/core/response.lua +++ b/apisix/core/response.lua @@ -177,14 +177,19 @@ end function _M.hold_body_chunk(ctx, hold_the_copy) local body_buffer local chunk, eof = arg[1], arg[2] + + if not ctx._body_buffer then + ctx._body_buffer = {} + end + if type(chunk) == "string" and chunk ~= "" then - body_buffer = ctx._body_buffer + body_buffer = ctx._body_buffer[ctx._plugin_name] if not body_buffer then body_buffer = { chunk, n = 1 } - ctx._body_buffer = body_buffer + ctx._body_buffer[ctx._plugin_name] = body_buffer else local n = body_buffer.n + 1 body_buffer.n = n @@ -193,13 +198,13 @@ function _M.hold_body_chunk(ctx, hold_the_copy) end if eof then - body_buffer = ctx._body_buffer + body_buffer = ctx._body_buffer[ctx._plugin_name] if not body_buffer then return chunk end body_buffer = concat_tab(body_buffer, "", 1, body_buffer.n) - ctx._body_buffer = nil + ctx._body_buffer[ctx._plugin_name] = nil return body_buffer end diff --git a/apisix/core/version.lua b/apisix/core/version.lua index dc852603f9a4..4e7d2a9d438a 100644 --- a/apisix/core/version.lua +++ b/apisix/core/version.lua @@ -20,5 +20,5 @@ -- @module core.version return { - VERSION = "3.2.1" + VERSION = "3.2.2" } diff --git a/apisix/init.lua b/apisix/init.lua index 388af426effe..aef5b7ebeb23 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -223,9 +223,15 @@ local function parse_domain_in_route(route) -- don't modify the modifiedIndex to avoid plugin cache miss because of DNS resolve result -- has changed - -- Here we copy the whole route instead of part of it, - -- so that we can avoid going back from route.value to route during copying. - route.dns_value = core.table.deepcopy(route).value + local parent = route.value.upstream.parent + if parent then + route.value.upstream.parent = nil + end + route.dns_value = core.table.deepcopy(route.value) + if parent then + route.value.upstream.parent = parent + route.dns_value.upstream.parent = parent + end route.dns_value.upstream.nodes = new_nodes core.log.info("parse route which contain domain: ", core.json.delay_encode(route, true)) diff --git a/apisix/plugin.lua b/apisix/plugin.lua index 3b5cecac6ded..a7d55db7375c 100644 --- a/apisix/plugin.lua +++ b/apisix/plugin.lua @@ -1091,7 +1091,9 @@ function _M.run_plugin(phase, plugins, api_ctx) end plugin_run = true + api_ctx._plugin_name = plugins[i]["name"] local code, body = phase_func(conf, api_ctx) + api_ctx._plugin_name = nil if code or body then if is_http then if code >= 400 then @@ -1126,7 +1128,9 @@ function _M.run_plugin(phase, plugins, api_ctx) local conf = plugins[i + 1] if phase_func and meta_filter(api_ctx, plugins[i]["name"], conf) then plugin_run = true + api_ctx._plugin_name = plugins[i]["name"] phase_func(conf, api_ctx) + api_ctx._plugin_name = nil end end diff --git a/apisix/plugin_config.lua b/apisix/plugin_config.lua index cc5a6ff38456..88b17d4b83ac 100644 --- a/apisix/plugin_config.lua +++ b/apisix/plugin_config.lua @@ -40,6 +40,14 @@ function _M.init_worker() end +function _M.plugin_configs() + if not plugin_configs then + return nil, nil + end + return plugin_configs.values, plugin_configs.conf_version +end + + function _M.get(id) return plugin_configs:get(id) end @@ -70,8 +78,7 @@ function _M.merge(route_conf, plugin_config) end end - route_conf.update_count = route_conf.update_count + 1 - route_conf.modifiedIndex = route_conf.orig_modifiedIndex .. "#" .. route_conf.update_count + route_conf.modifiedIndex = route_conf.orig_modifiedIndex .. "#" .. plugin_config.modifiedIndex route_conf.prev_plugin_config_ver = plugin_config.modifiedIndex return route_conf diff --git a/apisix/plugins/cors.lua b/apisix/plugins/cors.lua index f0d911f5de92..4f0bfa5d37aa 100644 --- a/apisix/plugins/cors.lua +++ b/apisix/plugins/cors.lua @@ -190,10 +190,6 @@ local function set_cors_headers(conf, ctx) end core.response.set_header("Access-Control-Allow-Origin", ctx.cors_allow_origins) - if ctx.cors_allow_origins ~= "*" then - core.response.add_header("Vary", "Origin") - end - core.response.set_header("Access-Control-Allow-Methods", allow_methods) core.response.set_header("Access-Control-Max-Age", conf.max_age) core.response.set_header("Access-Control-Expose-Headers", conf.expose_headers) @@ -241,10 +237,6 @@ local function process_with_allow_origins(allow_origins, ctx, req_origin, end local function process_with_allow_origins_by_regex(conf, ctx, req_origin) - if conf.allow_origins_by_regex == nil then - return - end - if not conf.allow_origins_by_regex_rules_concat then local allow_origins_by_regex_rules = {} for i, re_rule in ipairs(conf.allow_origins_by_regex) do @@ -297,17 +289,21 @@ end function _M.header_filter(conf, ctx) local req_origin = ctx.original_request_origin - -- Try allow_origins first, if mismatched, try allow_origins_by_regex. + -- If allow_origins_by_regex is not nil, should be matched to it only local allow_origins - allow_origins = process_with_allow_origins(conf.allow_origins, ctx, req_origin) - if not match_origins(req_origin, allow_origins) then + if conf.allow_origins_by_regex == nil then + allow_origins = process_with_allow_origins(conf.allow_origins, ctx, req_origin) + else allow_origins = process_with_allow_origins_by_regex(conf, ctx, req_origin) end - if not allow_origins then + if not match_origins(req_origin, allow_origins) then allow_origins = process_with_allow_origins_by_metadata( conf.allow_origins_by_metadata, ctx, req_origin ) end + if conf.allow_origins ~= "*" then + core.response.add_header("Vary", "Origin") + end if allow_origins then ctx.cors_allow_origins = allow_origins set_cors_headers(conf, ctx) diff --git a/apisix/plugins/grpc-transcode/proto.lua b/apisix/plugins/grpc-transcode/proto.lua index 1c9b7718fdc1..347ec39eae17 100644 --- a/apisix/plugins/grpc-transcode/proto.lua +++ b/apisix/plugins/grpc-transcode/proto.lua @@ -99,7 +99,7 @@ end local function compile_proto(content) -- clear pb state - pb.state(nil) + local old_pb_state = pb.state(nil) local compiled, err = compile_proto_text(content) if not compiled then @@ -110,7 +110,7 @@ local function compile_proto(content) end -- fetch pb state - compiled.pb_state = pb.state(nil) + compiled.pb_state = pb.state(old_pb_state) return compiled end diff --git a/apisix/plugins/grpc-transcode/request.lua b/apisix/plugins/grpc-transcode/request.lua index 88d2fcfb9ec8..934a1c95657c 100644 --- a/apisix/plugins/grpc-transcode/request.lua +++ b/apisix/plugins/grpc-transcode/request.lua @@ -36,10 +36,12 @@ return function (proto, service, method, pb_option, deadline, default_values) req_read_body() + local pb_old_state = pb.state(proto.pb_state) util.set_options(proto, pb_option) local map_message = util.map_message(m.input_type, default_values or {}) local ok, encoded = pcall(pb.encode, m.input_type, map_message) + pb.state(pb_old_state) if not ok or not encoded then return false, "failed to encode request data to protobuf", 400 diff --git a/apisix/plugins/grpc-transcode/response.lua b/apisix/plugins/grpc-transcode/response.lua index dee267b77c1f..9dd6780f049d 100644 --- a/apisix/plugins/grpc-transcode/response.lua +++ b/apisix/plugins/grpc-transcode/response.lua @@ -25,7 +25,7 @@ local ipairs = ipairs local pcall = pcall -local function handle_error_response(status_detail_type) +local function handle_error_response(status_detail_type, proto) local err_msg local grpc_status = ngx.header["grpc-status-details-bin"] @@ -58,7 +58,9 @@ local function handle_error_response(status_detail_type) if status_detail_type and details then local decoded_details = {} for _, detail in ipairs(details) do + local pb_old_state = pb.state(proto.pb_state) local ok, err_or_value = pcall(pb.decode, status_detail_type, detail.value) + pb.state(pb_old_state) if not ok then err_msg = "failed to call pb.decode to decode details in " .. "grpc-status-details-bin" @@ -99,7 +101,7 @@ return function(ctx, proto, service, method, pb_option, show_status_in_body, sta -- handle error response after the last response chunk if ngx.status >= 300 and show_status_in_body then - return handle_error_response(status_detail_type) + return handle_error_response(status_detail_type, proto) end -- when body has already been read by other plugin @@ -118,10 +120,12 @@ return function(ctx, proto, service, method, pb_option, show_status_in_body, sta buffer = string.sub(buffer, 6) end + local pb_old_state = pb.state(proto.pb_state) util.set_options(proto, pb_option) local err_msg local decoded = pb.decode(m.output_type, buffer) + pb.state(pb_old_state) if not decoded then err_msg = "failed to decode response data by protobuf" ngx.arg[1] = err_msg diff --git a/apisix/plugins/grpc-transcode/util.lua b/apisix/plugins/grpc-transcode/util.lua index 4a27c1d3b9df..a95cb8202041 100644 --- a/apisix/plugins/grpc-transcode/util.lua +++ b/apisix/plugins/grpc-transcode/util.lua @@ -48,8 +48,6 @@ function _M.find_method(proto, service, method) return nil end - -- restore pb state - pb.state(proto.pb_state) return res end diff --git a/apisix/plugins/http-logger.lua b/apisix/plugins/http-logger.lua index 399d50da1eed..5dd52240f8df 100644 --- a/apisix/plugins/http-logger.lua +++ b/apisix/plugins/http-logger.lua @@ -122,7 +122,7 @@ local function send_http_data(conf, log_message) local httpc_res, httpc_err = httpc:request({ method = "POST", - path = url_decoded.path, + path = #url_decoded.path ~= 0 and url_decoded.path or "/", query = url_decoded.query, body = log_message, headers = { diff --git a/apisix/plugins/response-rewrite.lua b/apisix/plugins/response-rewrite.lua index 4c3487da0686..20458c4fcf82 100644 --- a/apisix/plugins/response-rewrite.lua +++ b/apisix/plugins/response-rewrite.lua @@ -58,7 +58,7 @@ local schema = { items = { type = "string", -- "Set-Cookie: =; Max-Age=" - pattern = "^[^:]+:[^:]+[^/]$" + pattern = "^[^:]+:[^:]*[^/]$" } }, set = { diff --git a/apisix/plugins/sls-logger.lua b/apisix/plugins/sls-logger.lua index 6c2415ac6d29..de2fbae67ffa 100644 --- a/apisix/plugins/sls-logger.lua +++ b/apisix/plugins/sls-logger.lua @@ -21,7 +21,7 @@ local bp_manager_mod = require("apisix.utils.batch-processor-manager") local plugin_name = "sls-logger" local ngx = ngx -local rf5424 = require("apisix.plugins.slslog.rfc5424") +local rf5424 = require("apisix.utils.rfc5424") local tcp = ngx.socket.tcp local tostring = tostring local ipairs = ipairs @@ -138,9 +138,14 @@ function _M.log(conf, ctx) return end - local rf5424_data = rf5424.encode("SYSLOG", "INFO", ctx.var.host,"apisix", - ctx.var.pid, conf.project, conf.logstore, - conf.access_key_id, conf.access_key_secret, json_str) + local structured_data = { + {name = "project", value = conf.project}, + {name = "logstore", value = conf.logstore}, + {name = "access-key-id", value = conf.access_key_id}, + {name = "access-key-secret", value = conf.access_key_secret}, + } + local rf5424_data = rf5424.encode("SYSLOG", "INFO", ctx.var.host, "apisix", + ctx.var.pid, json_str, structured_data) local process_context = { data = rf5424_data, diff --git a/apisix/plugins/splunk-hec-logging.lua b/apisix/plugins/splunk-hec-logging.lua index 8de8be6eca9b..d7c97107e620 100644 --- a/apisix/plugins/splunk-hec-logging.lua +++ b/apisix/plugins/splunk-hec-logging.lua @@ -21,6 +21,9 @@ local ngx_now = ngx.now local http = require("resty.http") local log_util = require("apisix.utils.log-util") local bp_manager_mod = require("apisix.utils.batch-processor-manager") +local table_insert = core.table.insert +local table_concat = core.table.concat +local ipairs = ipairs local DEFAULT_SPLUNK_HEC_ENTRY_SOURCE = "apache-apisix-splunk-hec-logging" @@ -127,10 +130,15 @@ local function send_to_splunk(conf, entries) local http_new = http.new() http_new:set_timeout(conf.endpoint.timeout * 1000) + local t = {} + for _, e in ipairs(entries) do + table_insert(t, core.json.encode(e)) + end + local res, err = http_new:request_uri(conf.endpoint.uri, { ssl_verify = conf.ssl_verify, method = "POST", - body = core.json.encode(entries), + body = table_concat(t), headers = request_headers, }) diff --git a/apisix/plugins/syslog/init.lua b/apisix/plugins/syslog/init.lua index 24f2f62ab812..0ab34f8054e9 100644 --- a/apisix/plugins/syslog/init.lua +++ b/apisix/plugins/syslog/init.lua @@ -18,6 +18,10 @@ local core = require("apisix.core") local bp_manager_mod = require("apisix.utils.batch-processor-manager") local logger_socket = require("resty.logger.socket") +local rfc5424 = require("apisix.utils.rfc5424") +local ipairs = ipairs +local table_insert = core.table.insert +local table_concat = core.table.concat local batch_processor_manager = bp_manager_mod.new("sys logger") @@ -63,7 +67,8 @@ local function send_syslog_data(conf, log_message, api_ctx) end -- reuse the logger object - local ok, err = logger:log(core.json.encode(log_message)) + local ok, err = logger:log(log_message) + if not ok then res = false err_msg = "failed to log message" .. err @@ -75,28 +80,32 @@ end -- called in log phase of APISIX function _M.push_entry(conf, ctx, entry) - if batch_processor_manager:add_entry(conf, entry) then + local json_str, err = core.json.encode(entry) + if not json_str then + core.log.error('error occurred while encoding the data: ', err) + return + end + + local rfc5424_data = rfc5424.encode("SYSLOG", "INFO", ctx.var.host, + "apisix", ctx.var.pid, json_str) + + if batch_processor_manager:add_entry(conf, rfc5424_data) then return end -- Generate a function to be executed by the batch processor local cp_ctx = core.table.clone(ctx) - local func = function(entries, batch_max_size) - local data, err - if batch_max_size == 1 then - data, err = core.json.encode(entries[1]) -- encode as single {} - else - data, err = core.json.encode(entries) -- encode as array [{}] - end - - if not data then - return false, 'error occurred while encoding the data: ' .. err + local func = function(entries) + local items = {} + for _, e in ipairs(entries) do + table_insert(items, e) + core.log.debug("buffered logs:", e) end - return send_syslog_data(conf, data, cp_ctx) + return send_syslog_data(conf, table_concat(items), cp_ctx) end - batch_processor_manager:add_entry_to_new_processor(conf, entry, ctx, func) + batch_processor_manager:add_entry_to_new_processor(conf, rfc5424_data, ctx, func) end diff --git a/apisix/plugins/traffic-split.lua b/apisix/plugins/traffic-split.lua index 38e272b7be66..18a6be6d0136 100644 --- a/apisix/plugins/traffic-split.lua +++ b/apisix/plugins/traffic-split.lua @@ -125,7 +125,7 @@ end local function parse_domain_for_node(node) - local host = node.host + local host = node.domain or node.host if not ipmatcher.parse_ipv4(host) and not ipmatcher.parse_ipv6(host) then diff --git a/apisix/plugins/wolf-rbac.lua b/apisix/plugins/wolf-rbac.lua index 62cb7b04fbba..154fde41a4ad 100644 --- a/apisix/plugins/wolf-rbac.lua +++ b/apisix/plugins/wolf-rbac.lua @@ -266,13 +266,13 @@ function _M.rewrite(conf, ctx) local consumers = consumer.consumers_kv(plugin_name, consumer_conf, "appid") core.log.info("------ consumers: ", core.json.delay_encode(consumers)) - local consumer = consumers[appid] - if not consumer then + local cur_consumer = consumers[appid] + if not cur_consumer then core.log.error("consumer [", appid, "] not found") return 401, fail_response("Invalid appid in rbac token") end - core.log.info("consumer: ", core.json.delay_encode(consumer)) - local server = consumer.auth_conf.server + core.log.info("consumer: ", core.json.delay_encode(cur_consumer)) + local server = cur_consumer.auth_conf.server local res = check_url_permission(server, appid, action, url, client_ip, wolf_token) @@ -287,7 +287,7 @@ function _M.rewrite(conf, ctx) local userId = userInfo.id username = userInfo.username nickname = userInfo.nickname or userInfo.username - local prefix = consumer.auth_conf.header_prefix or '' + local prefix = cur_consumer.auth_conf.header_prefix or '' core.response.set_header(prefix .. "UserId", userId) core.response.set_header(prefix .. "Username", username) core.response.set_header(prefix .. "Nickname", ngx.escape_uri(nickname)) @@ -303,6 +303,7 @@ function _M.rewrite(conf, ctx) ") failed, res: ",core.json.delay_encode(res)) return res.status, fail_response(res.err, { username = username, nickname = nickname }) end + consumer.attach_consumer(ctx, cur_consumer, consumer_conf) core.log.info("wolf-rbac check permission passed") end diff --git a/apisix/router.lua b/apisix/router.lua index 2fd14917c299..d308128113a2 100644 --- a/apisix/router.lua +++ b/apisix/router.lua @@ -29,7 +29,6 @@ local _M = {version = 0.3} local function filter(route) route.orig_modifiedIndex = route.modifiedIndex - route.update_count = 0 route.has_domain = false if not route.value then diff --git a/apisix/schema_def.lua b/apisix/schema_def.lua index f7b117af93da..280680729d50 100644 --- a/apisix/schema_def.lua +++ b/apisix/schema_def.lua @@ -508,7 +508,7 @@ local upstream_schema = { _M.upstream_hash_vars_schema = { type = "string", pattern = [[^((uri|server_name|server_addr|request_uri|remote_port]] - .. [[|remote_addr|query_string|host|hostname)]] + .. [[|remote_addr|query_string|host|hostname|mqtt_client_id)]] .. [[|arg_[0-9a-zA-z_-]+)$]], } diff --git a/apisix/plugins/slslog/rfc5424.lua b/apisix/utils/rfc5424.lua similarity index 83% rename from apisix/plugins/slslog/rfc5424.lua rename to apisix/utils/rfc5424.lua index 5d09a58a5165..e046194bf349 100644 --- a/apisix/plugins/slslog/rfc5424.lua +++ b/apisix/utils/rfc5424.lua @@ -79,12 +79,13 @@ local Severity = { } local log_util = require("apisix.utils.log-util") - +local ipairs = ipairs +local str_format = string.format local _M = { version = 0.1 } -function _M.encode(facility, severity, hostname, appname, pid, project, - logstore, access_key_id, access_key_secret, msg) + +function _M.encode(facility, severity, hostname, appname, pid, msg, structured_data) local pri = (Facility[facility] * 8 + Severity[severity]) local t = log_util.get_rfc3339_zulu_timestamp() if not hostname then @@ -95,10 +96,19 @@ function _M.encode(facility, severity, hostname, appname, pid, project, appname = "-" end - return "<" .. pri .. ">1 " .. t .. " " .. hostname .. " " .. appname .. " " .. pid - .. " - [logservice project=\"" .. project .. "\" logstore=\"" .. logstore - .. "\" access-key-id=\"" .. access_key_id .. "\" access-key-secret=\"" - .. access_key_secret .. "\"] " .. msg .. "\n" + local structured_data_str = "-" + + if structured_data then + structured_data_str = "[logservice" + for _, sd_param in ipairs(structured_data) do + structured_data_str = structured_data_str .. " " .. sd_param.name + .. "=\"" .. sd_param.value .. "\"" + end + structured_data_str = structured_data_str .. "]" + end + + return str_format("<%d>1 %s %s %s %d - %s %s\n", pri, t, hostname, + appname, pid, structured_data_str, msg) end return _M diff --git a/ci/init-plugin-test-service.sh b/ci/init-plugin-test-service.sh index 881f466c710d..ae86dcde2c85 100755 --- a/ci/init-plugin-test-service.sh +++ b/ci/init-plugin-test-service.sh @@ -23,7 +23,7 @@ after() { # prepare openwhisk env docker pull openwhisk/action-nodejs-v14:nightly - docker run --rm -d --name openwhisk -p 3233:3233 -p 3232:3232 -v /var/run/docker.sock:/var/run/docker.sock openwhisk/standalone:nightly + docker run --rm -d --name openwhisk -p 3233:3233 -p 3232:3232 -v /var/run/docker.sock:/var/run/docker.sock openwhisk/standalone:20a7b1c docker exec -i openwhisk waitready docker exec -i openwhisk bash -c "wsk package create pkg" docker exec -i openwhisk bash -c "wsk action update /guest/pkg/testpkg <(echo 'function main(args){return {\"hello\": \"world\"}}') --kind nodejs:14" diff --git a/ci/linux_openresty_runner.sh b/ci/linux_openresty_runner.sh index 2cdc87b218f3..877248913368 100755 --- a/ci/linux_openresty_runner.sh +++ b/ci/linux_openresty_runner.sh @@ -18,5 +18,5 @@ export OPENRESTY_VERSION=source -export TEST_CI_USE_GRPC=true +#export TEST_CI_USE_GRPC=true . ./ci/linux_openresty_common_runner.sh diff --git a/ci/pod/docker-compose.plugin.yml b/ci/pod/docker-compose.plugin.yml index e7878ab61b81..a811aa07676b 100644 --- a/ci/pod/docker-compose.plugin.yml +++ b/ci/pod/docker-compose.plugin.yml @@ -319,9 +319,24 @@ services: ports: - '8888:8080' + vector: + image: timberio/vector:0.29.1-debian + container_name: vector + volumes: + - ./ci/pod/vector:/etc/vector/ + - ./t/certs:/certs + ports: + - '3000:3000' + - '43000:43000' + - '5140:5140' + - '5150:5150/udp' + networks: + vector_net: + networks: apisix_net: kafka_net: skywalk_net: rocketmq_net: opa_net: + vector_net: diff --git a/ci/pod/openfunction/build-function-image.sh b/ci/pod/openfunction/build-function-image.sh index fc4a4945ed3e..b397791b556e 100755 --- a/ci/pod/openfunction/build-function-image.sh +++ b/ci/pod/openfunction/build-function-image.sh @@ -23,6 +23,6 @@ if [ ! -f "./pack" ]; then fi # please update function-example/*/hello.go if you want to update function -./pack build test-uri-image --path ./ci/pod/openfunction/function-example/test-uri --builder openfunction/builder-go:v2.4.0-1.17 --env FUNC_NAME="HelloWorld" --env FUNC_CLEAR_SOURCE=true --env FUNC_GOPROXY="https://goproxy.cn" -./pack build test-body-image --path ./ci/pod/openfunction/function-example/test-body --builder openfunction/builder-go:v2.4.0-1.17 --env FUNC_NAME="HelloWorld" --env FUNC_CLEAR_SOURCE=true --env FUNC_GOPROXY="https://goproxy.cn" -./pack build test-header-image --path ./ci/pod/openfunction/function-example/test-header --builder openfunction/builder-go:v2.4.0-1.17 --env FUNC_NAME="HelloWorld" --env FUNC_CLEAR_SOURCE=true --env FUNC_GOPROXY="https://goproxy.cn" +./pack build test-uri-image --path ./ci/pod/openfunction/function-example/test-uri --builder openfunction/builder-go:v2.4.0-1.17 --env FUNC_NAME="HelloWorld" --env FUNC_CLEAR_SOURCE=true +./pack build test-body-image --path ./ci/pod/openfunction/function-example/test-body --builder openfunction/builder-go:v2.4.0-1.17 --env FUNC_NAME="HelloWorld" --env FUNC_CLEAR_SOURCE=true +./pack build test-header-image --path ./ci/pod/openfunction/function-example/test-header --builder openfunction/builder-go:v2.4.0-1.17 --env FUNC_NAME="HelloWorld" --env FUNC_CLEAR_SOURCE=true diff --git a/ci/pod/vector/vector.toml b/ci/pod/vector/vector.toml new file mode 100644 index 000000000000..953f30746c05 --- /dev/null +++ b/ci/pod/vector/vector.toml @@ -0,0 +1,75 @@ +# +# 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. + +[sources.log-from-tcp] +type = "socket" +address = "0.0.0.0:3000" +host_key = "host" +mode = "tcp" +port_key = "port" +shutdown_timeout_secs = 30 +socket_file_mode = 511 + +[sources.log-from-tls] +type = "socket" +address = "0.0.0.0:43000" +host_key = "host" +mode = "tcp" +port_key = "port" +tls.enabled = true +tls.verify = true +tls.ca_file = "/certs/vector_logs_ca.crt" +tls.crt_file = "/certs/vector_logs_server.crt" +tls.key_file = "/certs/vector_logs_server.key" + +[sources.log-from-syslog-tcp] +type = "syslog" +address = "0.0.0.0:5140" +mode = "tcp" + +[sources.log-from-syslog-udp] +type = "syslog" +address = "0.0.0.0:5150" +mode = "udp" + +[sinks.log-2-console] +inputs = [ "log-from-tcp", "log-from-tls", "log-from-syslog-tcp", "log-from-syslog-udp" ] +type = "console" +encoding.codec = "json" + +[sinks.log-2-tcp-file] +inputs = [ "log-from-tcp" ] +type = "file" +encoding.codec = "text" +path = "/etc/vector/tcp.log" + +[sinks.tls-log-2-file] +inputs = [ "log-from-tls" ] +type = "file" +encoding.codec = "json" +path = "/etc/vector/tls-datas.log" + +[sinks.log-2-syslog-tcp-file] +inputs = [ "log-from-syslog-tcp" ] +type = "file" +encoding.codec = "text" +path = "/etc/vector/syslog-tcp.log" + +[sinks.log-2-syslog-udp-file] +inputs = [ "log-from-syslog-udp" ] +type = "file" +encoding.codec = "text" +path = "/etc/vector/syslog-udp.log" diff --git a/docs/en/latest/building-apisix.md b/docs/en/latest/building-apisix.md index a4ce33b2f135..c3752504da4b 100644 --- a/docs/en/latest/building-apisix.md +++ b/docs/en/latest/building-apisix.md @@ -52,7 +52,7 @@ curl https://raw.githubusercontent.com/apache/apisix/master/utils/install-depend Then, create a directory and set the environment variable `APISIX_VERSION`: ```shell -APISIX_VERSION='3.2.1' +APISIX_VERSION='3.2.2' mkdir apisix-${APISIX_VERSION} ``` diff --git a/docs/en/latest/config.json b/docs/en/latest/config.json index ee946a1e9919..d43ea2a06350 100644 --- a/docs/en/latest/config.json +++ b/docs/en/latest/config.json @@ -1,5 +1,5 @@ { - "version": "3.2.1", + "version": "3.2.2", "sidebar": [ { "type": "doc", diff --git a/docs/en/latest/control-api.md b/docs/en/latest/control-api.md index c6944f2b5421..b59fb7aa5b19 100644 --- a/docs/en/latest/control-api.md +++ b/docs/en/latest/control-api.md @@ -221,7 +221,6 @@ Returns all configured [Routes](./terminology/route.md): ```json [ { - "update_count": 0, "value": { "priority": 0, "uris": [ @@ -260,7 +259,6 @@ Returns the Route with the specified `route_id`: ```json { - "update_count": 0, "value": { "priority": 0, "uris": [ diff --git a/docs/en/latest/plugins/cors.md b/docs/en/latest/plugins/cors.md index fcdafe854b88..321d6f3cadfe 100644 --- a/docs/en/latest/plugins/cors.md +++ b/docs/en/latest/plugins/cors.md @@ -40,7 +40,7 @@ The `cors` Plugins lets you enable [CORS](https://developer.mozilla.org/en-US/do | expose_headers | string | False | "*" | Headers in the response allowed when accessing a cross-origin resource. Use `,` to add multiple headers. If `allow_credential` is set to `false`, you can enable CORS for all response headers by using `*`. If `allow_credential` is set to `true`, you can forcefully allow CORS on all response headers by using `**` but it will pose some security issues. | | max_age | integer | False | 5 | Maximum time in seconds the result is cached. If the time is within this limit, the browser will check the cached result. Set to `-1` to disable caching. Note that the maximum value is browser dependent. See [Access-Control-Max-Age](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age#Directives) for more details. | | allow_credential | boolean | False | false | When set to `true`, allows requests to include credentials like cookies. According to CORS specification, if you set this to `true`, you cannot use '*' to allow all for the other attributes. | -| allow_origins_by_regex | array | False | nil | Regex to match with origin for enabling CORS. For example, `[".*\.test.com"]` can match all subdomain of `test.com`. | +| allow_origins_by_regex | array | False | nil | Regex to match with origin for enabling CORS. For example, `[".*\.test.com"]` can match all subdomain of `test.com`. When set to specified range, only domains in this range will be allowed, no matter what `allow_origins` is. | | allow_origins_by_metadata | array | False | nil | Origins to enable CORS referenced from `allow_origins` set in the Plugin metadata. For example, if `"allow_origins": {"EXAMPLE": "https://example.com"}` is set in the Plugin metadata, then `["EXAMPLE"]` can be used to allow CORS on the origin `https://example.com`. | :::info IMPORTANT diff --git a/docs/zh/latest/CHANGELOG.md b/docs/zh/latest/CHANGELOG.md index 118bb9584d54..7c681a50bc3e 100644 --- a/docs/zh/latest/CHANGELOG.md +++ b/docs/zh/latest/CHANGELOG.md @@ -23,6 +23,7 @@ title: CHANGELOG ## Table of Contents +- [3.2.2](#322) - [3.2.1](#321) - [3.2.0](#320) - [3.1.0](#310) @@ -68,6 +69,28 @@ title: CHANGELOG - [0.7.0](#070) - [0.6.0](#060) +## 3.2.2 + +### Bugfix + +- 修复 upstream key 无法通过 `mqtt_client_id` 负载均衡 [#9450](https://github.com/apache/apisix/pull/9450) +- hold_body_chunk 不能在多个插件中使用 [#9266](https://github.com/apache/apisix/pull/9266) +- 使用 domain nodes 会导致健康检查器泄露 [#9090](https://github.com/apache/apisix/pull/9090) +- traffic-split 插件引用的上游可以被删掉,导致插件异常 [#9044](https://github.com/apache/apisix/pull/9044) +- `allow_origins_by_regex` 不为 nil,使用其他域名也会被验证 [#9028](https://github.com/apache/apisix/pull/9028) +- traffic-split 插件配置域名时,只会被解析一次。 [#9332](https://github.com/apache/apisix/pull/9332) +- syslog 插件存在错误的日志编码格式导致该插件不可用 [#9425](https://github.com/apache/apisix/pull/9425) +- http-logger 缺少默认请求 path / 导致日志推送失败 [#9472](https://github.com/apache/apisix/pull/9472) +- consumer 使用 Wolf-rbac 时,无法同时使用其他插件 [#9298](https://github.com/apache/apisix/pull/9298) +- splunk-hec-logging 错误的日志格式导致请求无法被推送, [#9478](https://github.com/apache/apisix/pull/9478) +- opentelemetry 和 grpc-transcode 插件不能同时使用 [#9606](https://github.com/apache/apisix/pull/9606) +- response-rewrite 增加 header `key: value` 时,value 不能配置为 1 个字符 [#9372](https://github.com/apache/apisix/pull/9372) +- etcd_cli 启用 keepalive [#9420](https://github.com/apache/apisix/pull/9420) +- 单个 HTTP 长连接 watch 全部 etcd 资源 [#9456](https://github.com/apache/apisix/pull/9456) +- 修复 jwt 插件绕过认证的安全问题 [#9837](https://github.com/apache/apisix/pull/9837) +- 修复更新 `update_count` 后,导致 lru 缓存 `key` 冲突 [#9811](https://github.com/apache/apisix/pull/9811) +- 修复 consumer 在 etcd 启动时连接失败,仍然可以运行 [#9077](https://github.com/apache/apisix/pull/9077) + ## 3.2.1 ### Bugfix diff --git a/docs/zh/latest/building-apisix.md b/docs/zh/latest/building-apisix.md index 96290ad10847..67506197d5e1 100644 --- a/docs/zh/latest/building-apisix.md +++ b/docs/zh/latest/building-apisix.md @@ -53,7 +53,7 @@ curl https://raw.githubusercontent.com/apache/apisix/master/utils/install-depend 然后,创建一个目录并设置环境变量 `APISIX_VERSION`: ```shell -APISIX_VERSION='3.2.1' +APISIX_VERSION='3.2.2' mkdir apisix-${APISIX_VERSION} ``` diff --git a/docs/zh/latest/config.json b/docs/zh/latest/config.json index 2952fa9cc296..d5fa9ea5c31b 100644 --- a/docs/zh/latest/config.json +++ b/docs/zh/latest/config.json @@ -1,5 +1,5 @@ { - "version": "3.2.1", + "version": "3.2.2", "sidebar": [ { "type": "doc", diff --git a/docs/zh/latest/plugins/cors.md b/docs/zh/latest/plugins/cors.md index b08ba9b85940..153e76622e44 100644 --- a/docs/zh/latest/plugins/cors.md +++ b/docs/zh/latest/plugins/cors.md @@ -40,7 +40,7 @@ description: 本文介绍了 Apache APISIX cors 插件的基本信息及使用 | expose_headers | string | 否 | "*" | 允许跨域访问时响应方携带哪些非 `CORS 规范` 以外的 Header。如果你有多个 Header,请使用 `,` 分割。当 `allow_credential` 为 `false` 时,可以使用 `*` 来表示允许任意 Header。你也可以在启用了 `allow_credential` 后使用 `**` 强制允许任意 Header,但请注意这样存在安全隐患。 | | max_age | integer | 否 | 5 | 浏览器缓存 CORS 结果的最大时间,单位为秒。在这个时间范围内,浏览器会复用上一次的检查结果,`-1` 表示不缓存。请注意各个浏览器允许的最大时间不同,详情请参考 [Access-Control-Max-Age - MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age#directives)。 | | allow_credential | boolean | 否 | false | 是否允许跨域访问的请求方携带凭据(如 Cookie 等)。根据 CORS 规范,如果设置该选项为 `true`,那么将不能在其他属性中使用 `*`。 | -| allow_origins_by_regex | array | 否 | nil | 使用正则表达式数组来匹配允许跨域访问的 Origin,如 `[".*\.test.com"]` 可以匹配任何 `test.com` 的子域名 `*`。 | +| allow_origins_by_regex | array | 否 | nil | 使用正则表达式数组来匹配允许跨域访问的 Origin,如 `[".*\.test.com"]` 可以匹配任何 `test.com` 的子域名 `*`。如果 `allow_origins_by_regex` 属性已经指定,则会忽略 `allow_origins` 属性。 | | allow_origins_by_metadata | array | 否 | nil | 通过引用插件元数据的 `allow_origins` 配置允许跨域访问的 Origin。比如当插件元数据为 `"allow_origins": {"EXAMPLE": "https://example.com"}` 时,配置 `["EXAMPLE"]` 将允许 Origin `https://example.com` 的访问。 | :::info IMPORTANT diff --git a/rockspec/apisix-3.2.2-0.rockspec b/rockspec/apisix-3.2.2-0.rockspec new file mode 100644 index 000000000000..738df084518d --- /dev/null +++ b/rockspec/apisix-3.2.2-0.rockspec @@ -0,0 +1,103 @@ +-- +-- 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. +-- + +package = "apisix" +version = "3.2.2-0" +supported_platforms = {"linux", "macosx"} + +source = { + url = "git://github.com/apache/apisix", + branch = "3.2.2", +} + +description = { + summary = "Apache APISIX is a cloud-native microservices API gateway, delivering the ultimate performance, security, open source and scalable platform for all your APIs and microservices.", + homepage = "https://github.com/apache/apisix", + license = "Apache License 2.0", +} + +dependencies = { + "lua-resty-ctxdump = 0.1-0", + "api7-lua-resty-dns-client = 7.0.1", + "lua-resty-template = 2.0", + "lua-resty-etcd = 1.10.4", + "api7-lua-resty-http = 0.2.0", + "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", + "api7-lua-resty-jwt = 0.2.5", + "lua-resty-hmac-ffi = 0.05", + "lua-resty-cookie = 0.1.0", + "lua-resty-session = 3.10", + "opentracing-openresty = 0.1", + "lua-resty-radixtree = 2.8.2", + "lua-protobuf = 0.4.1", + "lua-resty-openidc = 1.7.5", + "luafilesystem = 1.7.0-2", + "api7-lua-tinyyaml = 0.4.2", + "nginx-lua-prometheus = 0.20220527", + "jsonschema = 0.9.8", + "lua-resty-ipmatcher = 0.6.1", + "lua-resty-kafka = 0.20-0", + "lua-resty-logger-socket = 2.0.1-0", + "skywalking-nginx-lua = 0.6.0", + "base64 = 1.5-2", + "binaryheap = 0.4", + "api7-dkjson = 0.1.1", + "resty-redis-cluster = 1.02-4", + "lua-resty-expr = 1.3.2", + "graphql = 0.0.2", + "argparse = 0.7.1-1", + "luasocket = 3.1.0-1", + "luasec = 0.9-1", + "lua-resty-consul = 0.3-2", + "penlight = 1.9.2-1", + "ext-plugin-proto = 0.6.0", + "casbin = 1.41.5", + "api7-snowflake = 2.0-1", + "inspect == 3.1.1", + "lualdap = 1.2.6-1", + "lua-resty-rocketmq = 0.3.0-0", + "opentelemetry-lua = 0.2-3", + "net-url = 0.9-1", + "xml2lua = 1.5-2", + "nanoid = 0.1-1", + "lua-resty-mediador = 0.1.2-1", + "lua-resty-ldap = 0.1.0-0" +} + +build = { + type = "make", + build_variables = { + CFLAGS="$(CFLAGS)", + LIBFLAG="$(LIBFLAG)", + LUA_LIBDIR="$(LUA_LIBDIR)", + LUA_BINDIR="$(LUA_BINDIR)", + LUA_INCDIR="$(LUA_INCDIR)", + LUA="$(LUA)", + OPENSSL_INCDIR="$(OPENSSL_INCDIR)", + OPENSSL_LIBDIR="$(OPENSSL_LIBDIR)", + }, + install_variables = { + ENV_INST_PREFIX="$(PREFIX)", + ENV_INST_BINDIR="$(BINDIR)", + ENV_INST_LIBDIR="$(LIBDIR)", + ENV_INST_LUADIR="$(LUADIR)", + ENV_INST_CONFDIR="$(CONFDIR)", + }, +} diff --git a/rockspec/apisix-master-0.rockspec b/rockspec/apisix-master-0.rockspec index 63c852e1bf39..9342c584afd1 100644 --- a/rockspec/apisix-master-0.rockspec +++ b/rockspec/apisix-master-0.rockspec @@ -40,7 +40,7 @@ dependencies = { "lua-resty-ngxvar = 0.5.2", "lua-resty-jit-uuid = 0.0.7", "lua-resty-healthcheck-api7 = 2.2.2", - "api7-lua-resty-jwt = 0.2.4", + "api7-lua-resty-jwt = 0.2.5", "lua-resty-hmac-ffi = 0.05", "lua-resty-cookie = 0.1.0", "lua-resty-session = 3.10", diff --git a/t/admin/routes4.t b/t/admin/routes4.t index 3c799be8be90..ba9ff44142e3 100644 --- a/t/admin/routes4.t +++ b/t/admin/routes4.t @@ -611,7 +611,7 @@ failed to read request body: request size 1678025 is greater than the maximum si "methods": ["GET", "GET"], "upstream": { "nodes": { - "httpbin.org:8080": 1, + "apisix.com:8080": 1, "test.com:8080": 1 }, "type": "roundrobin", diff --git a/t/admin/upstream3.t b/t/admin/upstream3.t index e40e24e99b4a..335bbfae36db 100644 --- a/t/admin/upstream3.t +++ b/t/admin/upstream3.t @@ -442,7 +442,7 @@ passed } --- error_code: 400 --- response_body -{"error_msg":"invalid configuration: failed to match pattern \"^((uri|server_name|server_addr|request_uri|remote_port|remote_addr|query_string|host|hostname)|arg_[0-9a-zA-z_-]+)$\" with \"not_support\""} +{"error_msg":"invalid configuration: failed to match pattern \"^((uri|server_name|server_addr|request_uri|remote_port|remote_addr|query_string|host|hostname|mqtt_client_id)|arg_[0-9a-zA-z_-]+)$\" with \"not_support\""} @@ -521,7 +521,7 @@ passed } --- error_code: 400 --- response_body -{"error_msg":"invalid configuration: failed to match pattern \"^((uri|server_name|server_addr|request_uri|remote_port|remote_addr|query_string|host|hostname)|arg_[0-9a-zA-z_-]+)$\" with \"not_support\""} +{"error_msg":"invalid configuration: failed to match pattern \"^((uri|server_name|server_addr|request_uri|remote_port|remote_addr|query_string|host|hostname|mqtt_client_id)|arg_[0-9a-zA-z_-]+)$\" with \"not_support\""} diff --git a/t/admin/upstream4.t b/t/admin/upstream4.t index 99c840f944f1..b657edc6e731 100644 --- a/t/admin/upstream4.t +++ b/t/admin/upstream4.t @@ -205,7 +205,7 @@ passed ngx.HTTP_PUT, [[{ "nodes": { - "httpbin.org:8080": 1, + "apisix.com:8080": 1, "test.com:8080": 1 }, "type": "roundrobin", diff --git a/t/admin/upstream5.t b/t/admin/upstream5.t index 9fd59bfe7e86..f589a415d470 100644 --- a/t/admin/upstream5.t +++ b/t/admin/upstream5.t @@ -111,3 +111,489 @@ passed } --- response_body passed + + + +=== TEST 4: prepare upstream +--- 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, [[{ + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }]]) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + + } + } +--- response_body +passed + + + +=== TEST 5: prepare route +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t("/apisix/admin/routes/1", ngx.HTTP_PUT, [[{ + "plugins": { + "traffic-split": { + "rules": [ + { + "weighted_upstreams": [ + { + "upstream_id": 1, + "weight": 1 + }, + { + "weight": 1 + } + ] + } + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]]) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + + } + } +--- response_body +passed + + + +=== TEST 6: delete upstream when plugin in route still refer it +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t("/apisix/admin/upstreams/1", ngx.HTTP_DELETE) + if code >= 300 then + ngx.status = code + end + ngx.print(body) + } + } +--- error_code: 400 +--- response_body +{"error_msg":"can not delete this upstream, plugin in route [1] is still using it now"} + + + +=== TEST 7: delete route +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t("/apisix/admin/routes/1", ngx.HTTP_DELETE) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 8: prepare service +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t("/apisix/admin/services/1", ngx.HTTP_PUT, [[{ + "plugins": { + "traffic-split": { + "rules": [ + { + "weighted_upstreams": [ + { + "upstream_id": 1, + "weight": 1 + }, + { + "weight": 1 + } + ] + } + ] + } + } + }]]) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 9: delete upstream when plugin in service still refer it +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t("/apisix/admin/upstreams/1", ngx.HTTP_DELETE) + if code >= 300 then + ngx.status = code + end + ngx.print(body) + } + } +--- error_code: 400 +--- response_body +{"error_msg":"can not delete this upstream, plugin in service [1] is still using it now"} + + + +=== TEST 10: delete service +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t("/apisix/admin/services/1", ngx.HTTP_DELETE) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 11: prepare global_rule +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + + local code, body = t("/apisix/admin/global_rules/1", ngx.HTTP_PUT, [[{ + "plugins": { + "traffic-split": { + "rules": [ + { + "weighted_upstreams": [ + { + "upstream_id": 1, + "weight": 1 + }, + { + "weight": 1 + } + ] + } + ] + } + } + }]]) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 12: delete upstream when plugin in global_rule still refer it +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t("/apisix/admin/upstreams/1", ngx.HTTP_DELETE) + if code >= 300 then + ngx.status = code + end + ngx.print(body) + } + } +--- error_code: 400 +--- response_body +{"error_msg":"can not delete this upstream, plugin in global_rule [1] is still using it now"} + + + +=== TEST 13: delete global_rule +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t("/apisix/admin/global_rules/1", ngx.HTTP_DELETE) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 14: prepare plugin_config +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + + local code, body = t("/apisix/admin/plugin_configs/1", ngx.HTTP_PUT, [[{ + "plugins": { + "traffic-split": { + "rules": [ + { + "weighted_upstreams": [ + { + "upstream_id": 1, + "weight": 1 + }, + { + "weight": 1 + } + ] + } + ] + } + } + }]]) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 15: delete upstream when plugin in plugin_config still refer it +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t("/apisix/admin/upstreams/1", ngx.HTTP_DELETE) + if code >= 300 then + ngx.status = code + end + ngx.print(body) + } + } +--- error_code: 400 +--- response_body +{"error_msg":"can not delete this upstream, plugin in plugin_config [1] is still using it now"} + + + +=== TEST 16: delete plugin_config +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t("/apisix/admin/plugin_configs/1", ngx.HTTP_DELETE) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 17: prepare consumer +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + + local code, body = t("/apisix/admin/consumers", ngx.HTTP_PUT, [[{ + "username": "test", + "plugins": { + "key-auth": { + "key": "auth-one" + }, + "traffic-split": { + "rules": [ + { + "weighted_upstreams": [ + { + "upstream_id": 1, + "weight": 1 + }, + { + "weight": 1 + } + ] + } + ] + } + } + }]]) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 18: delete upstream when plugin in consumer still refer it +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t("/apisix/admin/upstreams/1", ngx.HTTP_DELETE) + if code >= 300 then + ngx.status = code + end + ngx.print(body) + } + } +--- error_code: 400 +--- response_body +{"error_msg":"can not delete this upstream, plugin in consumer [test] is still using it now"} + + + +=== TEST 19: delete consumer +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t("/apisix/admin/consumers/test", ngx.HTTP_DELETE) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 20: prepare consumer_group +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + + local code, body = t("/apisix/admin/consumer_groups/1", ngx.HTTP_PUT, [[{ + "plugins": { + "key-auth": { + "key": "auth-one" + }, + "traffic-split": { + "rules": [ + { + "weighted_upstreams": [ + { + "upstream_id": 1, + "weight": 1 + }, + { + "weight": 1 + } + ] + } + ] + } + } + }]]) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 21: delete upstream when plugin in consumer_group still refer it +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t("/apisix/admin/upstreams/1", ngx.HTTP_DELETE) + if code >= 300 then + ngx.status = code + end + ngx.print(body) + } + } +--- error_code: 400 +--- response_body +{"error_msg":"can not delete this upstream, plugin in consumer_group [1] is still using it now"} + + + +=== TEST 22: delete consumer_group +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t("/apisix/admin/consumer_groups/1", ngx.HTTP_DELETE) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 23: delete upstream +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t("/apisix/admin/upstreams/1", ngx.HTTP_DELETE) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed diff --git a/t/certs/vector_logs_ca.crt b/t/certs/vector_logs_ca.crt new file mode 100644 index 000000000000..42e3659f61e0 --- /dev/null +++ b/t/certs/vector_logs_ca.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIUa34rzhtYT21pC8NwNIYf3phFciQwDQYJKoZIhvcNAQEL +BQAwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMzA0MjQxMzE2NDNaFw0yMzA1 +MjQxMzE2NDNaMEUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw +HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQC84FXe/8ofZB2rj5TPHasXdiBEbTCv04ti/lV92WOC +MrHLKibI2+kI3YRQXaR5/1F2bXfROUhdRgkB8NOSM4WbaD1mtr/7mW2Tatxplxsp +1s0zmHHl5v0VYwBahcUs6nlSe19dgfrj4s0Wn7p4E7iSq/UDAs+We/dQowusQTVs +Q2ZhjDlFY22CV/oyCYsNq3ORRgwZRm9cmVmUUF7GX70yjT1KvLkFjc7y1vwi8XJY +ADhw/hjtEzAOkxdUai84+jyhpQYQWMOgrlP1DXnZw1bNKqo6NTkMzfNCS+ul5PMs +Noyxcw1iyGW6Bm81LANsnMM7BLhPQATShmW7O83WUJ4vAgMBAAGjUzBRMB0GA1Ud +DgQWBBRdFCb//WETC8mDxg/75e+RoVNoDjAfBgNVHSMEGDAWgBRdFCb//WETC8mD +xg/75e+RoVNoDjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQC8 +cKOagO+SVJBMzJppm4uGdSM6TJ2wbkn6K5/eOtZmYdKtW6fkAC9tf0DR7dVP1DUk +24lS+atR1Oe7SukxJyd+NafCZ61uf+zrMC3wgBGnufrbPWaDDVxi6c3I0I+WNaCk +DHHY+9UtjvSboWKG1yuEExPN6aDeytbpscG1DNi7l96Ac3Yzs007SFljA7NBrf65 +So9SZYSdJVC/JrOnfK2HZPeAqvoyUO5JsCh02q5AskxTqfBGy6VUVQO5mN8bxYHV +GG5XD46rpwQYNT2bWWRF5d0bRv7ecNkCoupm6hCQROg4FZHGPnqHGqDTcgCLZ59e +8rHh2gsDMMNYvSMTi+0N +-----END CERTIFICATE----- diff --git a/t/certs/vector_logs_ca.key b/t/certs/vector_logs_ca.key new file mode 100644 index 000000000000..e36b69354b13 --- /dev/null +++ b/t/certs/vector_logs_ca.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAvOBV3v/KH2Qdq4+Uzx2rF3YgRG0wr9OLYv5VfdljgjKxyyom +yNvpCN2EUF2kef9Rdm130TlIXUYJAfDTkjOFm2g9Zra/+5ltk2rcaZcbKdbNM5hx +5eb9FWMAWoXFLOp5UntfXYH64+LNFp+6eBO4kqv1AwLPlnv3UKMLrEE1bENmYYw5 +RWNtglf6MgmLDatzkUYMGUZvXJlZlFBexl+9Mo09Sry5BY3O8tb8IvFyWAA4cP4Y +7RMwDpMXVGovOPo8oaUGEFjDoK5T9Q152cNWzSqqOjU5DM3zQkvrpeTzLDaMsXMN +YshlugZvNSwDbJzDOwS4T0AE0oZluzvN1lCeLwIDAQABAoIBADM7ou9fcQM80/OC +efoIcS1nBG+rMqau+kM6/BOsERrzB1k1sNmRFVArTkXCcOgKwp0eKn8dS6zJX44g +NjOVOCukhetDrSXhQ2DWfr1BmMOrmXPiaRrUolfXx/PGD2sUmx4tivvBUz3Xeowl +fZ4us0VN0aMkcwy9yaMc5wCtm4Em+uMrUIvWSAl3ji09oG4NNBQHUsEWJoRMZ/AG +GQowc7Ga850ybZlza1uWh29a3bbQqEwHExJwiCISv25PJ/xQLqH65biB4MU+ym17 +Ou/MDn9cYndxBal/XI4R7HbeIjMgw2XxwXiiDOuKAn5TlCzHmySRXFj1BoT8xoXa +vTXVlAkCgYEA+nc2GiainyW0MAASX53Ue5zsFh4T2CaA4TTHeXEK22rL1Sz3LsbX +ymBqCcNwbcSTYUzBsf6YzSsPLUwIzBGEN0p5Ywts5KtWavAxllBj2MOTP4yQfLvh +AxOq94hqrDLMs/g7LkFrfspYMCXmegGjjXGuqirKbigXkFVQkvOUcwUCgYEAwQy8 +kl2+deq1OD9rJId596nDx6JVLBt7/VP4dOOaS2/YQeFnUdM1xMM+zovEZb3UZMpp +8yhRE7hB7Fm0688yd+F7GpFC49LyVTitZcaIV7nMnXjJQQ27WyiAZoRKHt1gP4io +OCZAaOEJRbGJcWR3sSPHfX93R+xEtFNAexb/eqMCgYEA8NDV7+bdzO7PhKdNAyoZ +NpD2XX2lztmWaPH6KMWLjtPsD5cgQpVkvWxeB+0lmCS9H3xRb/Y+rGWOPhsxCiR9 +Xzv34kcF+AbVHBS9WK0Kk0vXs+5Ord9mxTKP21gKWG6vawpsvFiiJlIe4IxQQVZ6 +DnETYwGpiKh7n4an5eLVBJECgYEAnviuEJnBzbiJotgWku49MgVKg4raOIgpgmMz +po4G8TgZDadgPbGABZgCkHPoNyArVxSYSvRYT7TcFJWKtuTY2n+DsE0OmC2OAT+7 +CqSCgjsulD5y/G8iad7gXYtyvhfuumL+o75cLAGkcQ/R7t6c8fJUxLPCtieKLDSi +VLqLh6ECgYAlk8O5Rz2bSje4F+b0PZLAGjQRytpTjrgVpxwJ4bBXYeqhfd+3Fi8s +OraFx+kj/YOOFkk5uu75/fbccEY1DG0nmWUR0pjHM+QndB4rpkSxtp+pfVo2nRn0 +pAY8ep+TFRLwmy7ZXpOFPYlGPwx+rjSm9vk9EJYjxZE8YYldiBBKHw== +-----END RSA PRIVATE KEY----- diff --git a/t/certs/vector_logs_server.crt b/t/certs/vector_logs_server.crt new file mode 100644 index 000000000000..95bb51bec4d2 --- /dev/null +++ b/t/certs/vector_logs_server.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDETCCAfkCFEynFsv9L6bzyJJVmjoaKuCoYZwkMA0GCSqGSIb3DQEBCwUAMEUx +CzAJBgNVBAYTAlVTMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl +cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjMwNDI0MTMxNzAwWhcNMjQwNDIzMTMx +NzAwWjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE +CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAtGvdwIHuk6k3vphBnGIdtlZ/6ImHSVBsNHz5y6E9X31a88EH +wtnxT5Ang8K6Y4pQt+LsjhI0NdUY2skiKDnGpo2IkaFAn9nERQ1GJstIHr7ltal6 +ureV4n/Na/T6n6GPnwD4+P86XvpIwFtJZujYr2tUl4qm/t1P7zHjB/UsF9G6H/aN +oCsDkG3a7+b8uWAZLkyHS4RLF3pG6pDWns8/vC/P9nTT7o3Ha2DV7TPaY0hlsXf6 +0/SCSm7EonnVVwhnKyy5Z0FsCXClg7weN4ZKPb+ypF0o0/LLqw481lbSfAu5kpjE +r/rHpsQonRbQrcrD9xovXmw2vdk/2jJn6wpFQwIDAQABMA0GCSqGSIb3DQEBCwUA +A4IBAQBv9e8gsD75iySf+pam11JUujjL0gpqdzY72CKo4abYX5NZhMiBs6OCKicz +EedR/EgRY+26RMThKC0zSy3hOO6SKPw03FLsV2B8ooDzaOa4l3F/E6NQ5yNDoK+K +lT1G85fW3bQWtNoB8aa/r1/eExZy3kZF8GSl+/BvwLtOwtGXMO0Y1URo81Dl0da+ +F2yv6ZGziEYIWYTUK3kxOpe0Sl4wHz33olWoli2qpYlSndUUIWoVYJr4gtH/xTEV +GHxdOhxcfyMNi6ceYG4HGWyKRFR9TJAU+PRBxHI8UUpg+BG3/DQmfA5+7xgAws37 +dEVsm725hta8vPUSMSAdRrArBlh+ +-----END CERTIFICATE----- diff --git a/t/certs/vector_logs_server.key b/t/certs/vector_logs_server.key new file mode 100644 index 000000000000..54035d4fa9a6 --- /dev/null +++ b/t/certs/vector_logs_server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAtGvdwIHuk6k3vphBnGIdtlZ/6ImHSVBsNHz5y6E9X31a88EH +wtnxT5Ang8K6Y4pQt+LsjhI0NdUY2skiKDnGpo2IkaFAn9nERQ1GJstIHr7ltal6 +ureV4n/Na/T6n6GPnwD4+P86XvpIwFtJZujYr2tUl4qm/t1P7zHjB/UsF9G6H/aN +oCsDkG3a7+b8uWAZLkyHS4RLF3pG6pDWns8/vC/P9nTT7o3Ha2DV7TPaY0hlsXf6 +0/SCSm7EonnVVwhnKyy5Z0FsCXClg7weN4ZKPb+ypF0o0/LLqw481lbSfAu5kpjE +r/rHpsQonRbQrcrD9xovXmw2vdk/2jJn6wpFQwIDAQABAoIBAQCB24lV/6759Le8 +pNXEexIrpQKXGjWXXR0kgjdAiyMjUZRfETZG1prKy1TFjyiccHc8g0YD07JkdKZZ +Ap9lGICUbBY5yzg6VYDguncdgP69smSfZgaB0ZU92wK9iyvALYazyP1qKjmXFsm6 +OXoRadJcIAJYuGEN27imzt87YQmFciXj63lW4usR7rPpacW004VeWqGfXTnckJd6 +TYFq0xmdhnGxDxOlf6fs5zOEw17NrGlYxQVtdst8sGmpAPMEM7DzvDsjfEPxDuXl +hQJE8Zk8jK3Xwrnc03NWisZ4QVhgxeR7PVcraFo623qiI/CzH9YqUqMCtIMAqz/T +COXXl9JxAoGBAOosUC72SM7ZRshneHHszEaZDvfLINdKGUKCDvYlLEmVFqE5iRFy +SomVci2jtrlGH1gJAWfwkT09JVgtGosRIA0MS82HseLN/QIa01dAmmiZqM/CLbcn +mpb0CQDkm0Bbz6fokQkFB/sBA5Kj3kOKRydCLp2S0Ugs50cKXDHP5fuVAoGBAMU8 +9rIvmNdweGTiYjHYLJBkzu7eL+5RB2zVSMuZtVivaDTfleilbRlcrBBIaM0urv2W +UtROB9ack2Ijn/BF+tHkBRVWpaZFdHJ8qMfz2bBDgf6za/LBcvuT35i7ibPT+zfg +UFXtArmGwPq3AZdWBwIKyN8rM7253WDnUlkN7Ed3AoGBAMPAR0b6meJPvtvHoueZ +Cyn4yIpbQxi02GjAT8FzUZIxDrm3Xt02rRhV1RxRvm0iMRFmdcZtUvveIVmUWpvl +tOUzYiptREZT6yvXQNOvLWRDDtqdd5mjgZauaNhWQXGLTgsOXi8sBX/NWS87zJCp +BtHKgS03jbrHzo2UG32ITLgBAoGAJRoardoWPjCB9ThAkG/BskfERVq2WXYUl3xn +fSUk39HfIFMOt/ymUScFluqIDFDDyiAE5Lro7o31i3h4FZKUY/conaL29hgKl56r +gTF1uZp5UZgerkOFhZ2Dag+dD57ImvIvKnqzEIMwufjC69za5J9yucg+q2nTIu9g +pi/gSnECgYEAhfJ5uq1qa+g23np02ED5ttqmyrMRGGInx3mr2QgJDTum6FujpYCM +PwJhMwKJZXcf3eUlECSJPa+9UGI53d+JDlQdwq9Pi726KFtrBiS4t9aSyZSpkoWk +SVdYGaOMtokDKRJibazXjpGFJQy9tAMgtqptS3kL03IuJc643y+lMFc= +-----END RSA PRIVATE KEY----- diff --git a/t/config-center-yaml/plugin-configs.t b/t/config-center-yaml/plugin-configs.t index 2958b13785fa..f10c3651ad45 100644 --- a/t/config-center-yaml/plugin-configs.t +++ b/t/config-center-yaml/plugin-configs.t @@ -115,7 +115,7 @@ world --- response_headers in: out --- error_log eval -qr/conf_version: \d+#1,/ +qr/conf_version: \d+#\d+,/ diff --git a/t/config-center-yaml/route-upstream.t b/t/config-center-yaml/route-upstream.t index 53b9d51d3fb2..d5353a449332 100644 --- a/t/config-center-yaml/route-upstream.t +++ b/t/config-center-yaml/route-upstream.t @@ -140,17 +140,17 @@ hello world routes: - id: 1 - uri: /get + uri: /hello upstream_id: 1 upstreams: - id: 1 nodes: - "httpbin.org:80": 1 + "test.com:1980": 1 type: roundrobin #END --- request -GET /get +GET /hello --- error_code: 200 @@ -161,19 +161,19 @@ GET /get routes: - id: 1 - uri: /get + uri: /hello upstream_id: 1 upstreams: - id: 1 nodes: - "httpbin.org:80": 1 + "test.com:1980": 1 type: chash hash_on: header key: "$aaa" #END --- request -GET /get +GET /hello --- error_code: 502 --- error_log invalid configuration: failed to match pattern diff --git a/t/core/config_etcd.t b/t/core/config_etcd.t index 380b82522138..666d001b272b 100644 --- a/t/core/config_etcd.t +++ b/t/core/config_etcd.t @@ -266,7 +266,6 @@ qr/etcd auth failed/ etcd auth failed etcd auth failed etcd auth failed -etcd auth failed diff --git a/t/core/etcd-sync.t b/t/core/etcd-sync.t index e74ae19ec710..aef5e23619a9 100644 --- a/t/core/etcd-sync.t +++ b/t/core/etcd-sync.t @@ -22,65 +22,7 @@ run_tests; __DATA__ -=== TEST 1: minus timeout to watch repeatedly ---- yaml_config -deployment: - role: traditional - role_traditional: - config_provider: etcd - etcd: - # this test requires the HTTP long pull as the gRPC stream is shared and can't change - # default timeout in the fly - use_grpc: false - admin: - admin_key: null ---- config - location /t { - content_by_lua_block { - local core = require("apisix.core") - local t = require("lib.test_admin").test - - local consumers, _ = core.config.new("/consumers", { - automatic = true, - item_schema = core.schema.consumer, - timeout = 0.2 - }) - - ngx.sleep(0.6) - local idx = consumers.prev_index - - local code, body = t('/apisix/admin/consumers', - ngx.HTTP_PUT, - [[{ - "username": "jobs", - "plugins": { - "basic-auth": { - "username": "jobs", - "password": "123456" - } - } - }]]) - - ngx.sleep(2) - local new_idx = consumers.prev_index - core.log.info("idx:", idx, " new_idx: ", new_idx) - if new_idx > idx then - ngx.say("prev_index updated") - else - ngx.say("prev_index not update") - end - } - } ---- request -GET /t ---- response_body -prev_index updated ---- error_log eval -qr/(create watch stream for key|cancel watch connection success)/ - - - -=== TEST 2: using default timeout +=== TEST 1: using default timeout --- config location /t { content_by_lua_block { @@ -126,7 +68,7 @@ waitdir key -=== TEST 3: no update +=== TEST 2: no update --- config location /t { content_by_lua_block { @@ -162,7 +104,7 @@ prev_index not update -=== TEST 4: bad plugin configuration (validated via incremental sync) +=== TEST 3: bad plugin configuration (validated via incremental sync) --- config location /t { content_by_lua_block { @@ -182,7 +124,7 @@ property "uri" validation failed -=== TEST 5: bad plugin configuration (validated via full sync) +=== TEST 4: bad plugin configuration (validated via full sync) --- config location /t { content_by_lua_block { @@ -196,7 +138,7 @@ property "uri" validation failed -=== TEST 6: bad plugin configuration (validated without sync during start) +=== TEST 5: bad plugin configuration (validated without sync during start) --- extra_yaml_config disable_sync_configuration_during_start: true --- config diff --git a/t/core/etcd.t b/t/core/etcd.t index eaa425ec6b15..c0715de7b06b 100644 --- a/t/core/etcd.t +++ b/t/core/etcd.t @@ -401,35 +401,3 @@ qr/init_by_lua:\d+: \S+/ init_by_lua:12: ab init_by_lua:19: 200 init_by_lua:26: 404 - - - -=== TEST 8: error handling in server_version ---- yaml_config -deployment: - role: traditional - role_traditional: - config_provider: etcd - etcd: - host: - - "http://127.0.0.1:2379" - prefix: "/apisix" ---- config - location /t { - content_by_lua_block { - local etcd_lib = require("resty.etcd") - -- the mock won't take effect when using gRPC because the connection will be cached - etcd_lib.new = function() - return nil, "ouch" - end - local etcd = require("apisix.core.etcd") - local res, err = etcd.server_version() - ngx.say(err) - } - } ---- request -GET /t ---- response_body -ouch ---- error_log -failed to get server_info from etcd diff --git a/t/core/response.t b/t/core/response.t index c7cb92a56d56..dc748a7ad025 100644 --- a/t/core/response.t +++ b/t/core/response.t @@ -188,6 +188,7 @@ aaa: } body_filter_by_lua_block { local core = require("apisix.core") + ngx.ctx._plugin_name = "test" local final_body = core.response.hold_body_chunk(ngx.ctx) if not final_body then return diff --git a/t/node/consumer-plugin2.t b/t/node/consumer-plugin2.t index 64c3869bce56..d48387c179fa 100644 --- a/t/node/consumer-plugin2.t +++ b/t/node/consumer-plugin2.t @@ -435,3 +435,34 @@ apikey: auth-jack {"message":"Your IP address is not allowed"} hello world hello world + + + +=== TEST 10: consumer should work if the etcd connection failed during starting +--- extra_init_by_lua +local etcd_apisix = require("apisix.core.etcd") +etcd_apisix.get_etcd_syncer = function () + return nil, "", "ouch" +end +--- config + location /t { + content_by_lua_block { + local http = require "resty.http" + local httpc = http.new() + local uri = "http://127.0.0.1:" .. ngx.var.server_port + .. "/hello" + local headers = { + ["Authorization"] = "Basic Zm9vOmJhbGE=" + } + local res, err = httpc:request_uri(uri, {headers = headers}) + if not res then + ngx.say(err) + return + end + ngx.print(res.body) + } + } +--- response_body +hello world +--- error_log +failed to fetch data from etcd diff --git a/t/node/healthcheck-leak-bugfix.t b/t/node/healthcheck-leak-bugfix.t new file mode 100644 index 000000000000..d3ada8c171f7 --- /dev/null +++ b/t/node/healthcheck-leak-bugfix.t @@ -0,0 +1,112 @@ +# +# 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('warn'); +no_root_location(); +no_shuffle(); + +run_tests(); + +__DATA__ + +=== TEST 1: ensure the old check is cleared after configuration updated +--- extra_init_worker_by_lua + local healthcheck = require("resty.healthcheck") + local new = healthcheck.new + healthcheck.new = function(...) + local obj = new(...) + local clear = obj.clear + obj.clear = function(...) + ngx.log(ngx.WARN, "clear checker") + return clear(...) + end + return obj + end + +--- extra_init_by_lua + local utils = require("apisix.core.utils") + local count = 0 + utils.dns_parse = function (domain) -- mock: DNS parser + count = count + 1 + if domain == "test1.com" then + return {address = "127.0.0." .. count} + end + if domain == "test2.com" then + return {address = "127.0.0." .. count+100} + end + + error("unknown domain: " .. domain) + end + +--- config +location /t { + content_by_lua_block { + local cfg = [[{ + "upstream": { + "nodes": { + "test1.com:1980": 1, + "test2.com:1980": 1 + }, + "type": "roundrobin", + "checks":{ + "active":{ + "healthy":{ + "http_statuses":[ + 200, + 302 + ], + "interval":1, + "successes":2 + }, + "http_path":"/hello", + "timeout":1, + "type":"http", + "unhealthy":{ + "http_failures":5, + "http_statuses":[ + 429, + 404, + 500, + 501, + 502, + 503, + 504, + 505 + ], + "interval":1, + "tcp_failures":2, + "timeouts":3 + } + } + } + }, + "uri": "/hello" + }]] + local t = require("lib.test_admin").test + assert(t('/apisix/admin/routes/1', ngx.HTTP_PUT, cfg) < 300) + t('/hello', ngx.HTTP_GET) + assert(t('/apisix/admin/routes/1', ngx.HTTP_PUT, cfg) < 300) + ngx.sleep(1) + } +} + +--- request +GET /t +--- error_log +clear checker diff --git a/t/node/merge-route.t b/t/node/merge-route.t index 474e08abbd13..b09b272cf795 100644 --- a/t/node/merge-route.t +++ b/t/node/merge-route.t @@ -235,10 +235,10 @@ qr/merge_service_route.*"time_window":60/] ngx.HTTP_PUT, [[{ "upstream": { - "scheme": "https", + "scheme": "http", "type": "roundrobin", "nodes": { - "httpbin.org:443": 1 + "test.com:1980": 1 } } }]] @@ -266,10 +266,10 @@ passed ngx.HTTP_PUT, [[{ "uri": "/fake", - "host": "httpbin.org", + "host": "test.com", "plugins": { "proxy-rewrite": { - "uri": "/get" + "uri": "/echo" } }, "service_id": "1" @@ -293,10 +293,9 @@ passed --- request GET /fake --- more_headers -host: httpbin.org ---- response_body eval -qr/"Host": "httpbin.org"/ ---- timeout: 5 +host: test.com +--- response_headers +host: test.com @@ -304,7 +303,7 @@ qr/"Host": "httpbin.org"/ --- request GET /fake --- more_headers -host: httpbin.orgxxx +host: test.comxxx --- error_code: 404 diff --git a/t/node/plugin-configs.t b/t/node/plugin-configs.t index 11f9601030ad..f601ae86d773 100644 --- a/t/node/plugin-configs.t +++ b/t/node/plugin-configs.t @@ -113,10 +113,10 @@ __DATA__ hello world --- grep_error_log eval -qr/conf_version: \d+#\d/ +qr/conf_version: \d+#\d+/ --- grep_error_log_out eval -qr/conf_version: \d+#1 -conf_version: \d+#2 +qr/conf_version: \d+#\d+ +conf_version: \d+#\d+ / diff --git a/t/node/route-domain.t b/t/node/route-domain.t index 316f8c166c46..6fb376b83d5e 100644 --- a/t/node/route-domain.t +++ b/t/node/route-domain.t @@ -115,8 +115,7 @@ passed --- request GET /echo --- response_headers -Host: test.com ---- timeout: 10 +host: test.com @@ -157,8 +156,7 @@ passed --- request GET /echo --- response_headers -Host: test.com:1980 ---- timeout: 10 +host: test.com:1980 diff --git a/t/node/upstream-domain.t b/t/node/upstream-domain.t index 35dccbb5ee04..24048467f390 100644 --- a/t/node/upstream-domain.t +++ b/t/node/upstream-domain.t @@ -110,7 +110,7 @@ qr/dns resolver domain: foo.com to \d+.\d+.\d+.\d+/ ngx.HTTP_PUT, [[{ "nodes": { - "httpbin.orgx:80": 0 + "test.comx:80": 0 }, "type": "roundrobin", "desc": "new upstream" @@ -150,8 +150,8 @@ GET /t status: 503 status: 503 --- error_log -failed to parse domain: httpbin.orgx -failed to parse domain: httpbin.orgx +failed to parse domain: test.comx +failed to parse domain: test.comx --- timeout: 10 diff --git a/t/node/upstream.t b/t/node/upstream.t index b5d3acf2a121..c99b10aecad6 100644 --- a/t/node/upstream.t +++ b/t/node/upstream.t @@ -205,7 +205,7 @@ GET /t ngx.HTTP_PUT, [[{ "nodes": { - "httpbin.org:80": 1 + "test.com:1980": 1 }, "type": "roundrobin", "desc": "new upstream", @@ -234,7 +234,7 @@ passed local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, [[{ - "uri": "/get", + "uri": "/echo", "upstream_id": "1" }]] ) @@ -254,9 +254,9 @@ passed === TEST 12: hit route --- request -GET /get ---- response_body eval -qr/"Host": "httpbin.org"/ +GET /echo +--- response_headers +host: test.com:1980 @@ -274,7 +274,7 @@ qr/"Host": "httpbin.org"/ "type": "roundrobin", "desc": "new upstream", "pass_host": "rewrite", - "upstream_host": "httpbin.org" + "upstream_host": "test.com" }]] ) @@ -299,7 +299,7 @@ passed local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, [[{ - "uri": "/uri", + "uri": "/echo", "upstream_id": "1" }]] ) @@ -319,9 +319,9 @@ passed === TEST 15: hit route --- request -GET /uri ---- response_body eval -qr/host: httpbin.org/ +GET /echo +--- response_headers +host: test.com diff --git a/t/plugin/authz-casdoor.t b/t/plugin/authz-casdoor.t index 926d3daef97a..25fab487a29d 100644 --- a/t/plugin/authz-casdoor.t +++ b/t/plugin/authz-casdoor.t @@ -129,12 +129,15 @@ done "endpoint_addr":"]] .. fake_uri .. [[", "client_id":"7ceb9b7fda4a9061ec1c", "client_secret":"3416238e1edf915eac08b8fe345b2b95cdba7e04" + }, + "proxy-rewrite": { + "uri": "/echo" } }, "upstream": { "type": "roundrobin", "nodes": { - "httpbin.org:80": 1 + "test.com:1980": 1 } } }]] @@ -466,12 +469,15 @@ apisix: "endpoint_addr": "http://127.0.0.1:10420", "client_id":"7ceb9b7fda4a9061ec1c", "client_secret":"3416238e1edf915eac08b8fe345b2b95cdba7e04" + }, + "proxy-rewrite": { + "uri": "/echo" } }, "upstream": { "type": "roundrobin", "nodes": { - "httpbin.org:80": 1 + "test.com:1980": 1 } } }]] diff --git a/t/plugin/cors2.t b/t/plugin/cors2.t index 67f6d6baa2fb..1bc223bd3e8b 100644 --- a/t/plugin/cors2.t +++ b/t/plugin/cors2.t @@ -89,3 +89,88 @@ __DATA__ } --- response_body done + + + +=== TEST 2: set route ( regex specified and allow_origins is default value ) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "cors": { + "allow_origins": "*", + "allow_methods": "GET,POST", + "allow_headers": "request-h", + "expose_headers": "expose-h", + "max_age": 10, + "allow_origins_by_regex":[".*\\.domain.com"] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 3: regex specified not match +--- request +GET /hello HTTP/1.1 +--- more_headers +Origin: http://sub.test.com +resp-vary: Via +--- response_headers +Access-Control-Allow-Origin: +Access-Control-Allow-Methods: +Access-Control-Allow-Headers: +Access-Control-Expose-Headers: +Access-Control-Max-Age: +Access-Control-Allow-Credentials: + + + +=== TEST 4: regex no origin specified +--- request +GET /hello HTTP/1.1 +--- response_headers +Access-Control-Allow-Origin: +Access-Control-Allow-Methods: +Access-Control-Allow-Headers: +Access-Control-Expose-Headers: +Access-Control-Max-Age: +Access-Control-Allow-Credentials: + + + +=== TEST 5: regex specified match +--- request +GET /hello HTTP/1.1 +--- more_headers +Origin: http://sub.domain.com +resp-vary: Via +--- response_headers +Access-Control-Allow-Origin: http://sub.domain.com +Vary: Via +Access-Control-Allow-Methods: GET,POST +Access-Control-Allow-Headers: request-h +Access-Control-Expose-Headers: expose-h +Access-Control-Max-Age: 10 diff --git a/t/plugin/cors3.t b/t/plugin/cors3.t index 92210a1a3093..ae68dec3f549 100644 --- a/t/plugin/cors3.t +++ b/t/plugin/cors3.t @@ -163,7 +163,7 @@ Origin: http://foo.example.org hello world --- response_headers Access-Control-Allow-Origin: -Vary: +Vary: Origin Access-Control-Allow-Methods: Access-Control-Allow-Headers: Access-Control-Expose-Headers: @@ -254,7 +254,7 @@ Origin: http://foo.example.org hello world --- response_headers Access-Control-Allow-Origin: -Vary: +Vary: Origin Access-Control-Allow-Methods: Access-Control-Allow-Headers: Access-Control-Expose-Headers: diff --git a/t/plugin/error-log-logger-skywalking.t b/t/plugin/error-log-logger-skywalking.t index ffebf1cf4fa5..edb5003c0988 100644 --- a/t/plugin/error-log-logger-skywalking.t +++ b/t/plugin/error-log-logger-skywalking.t @@ -118,8 +118,8 @@ qr/Batch Processor\[error-log-logger\] failed to process entries: error while se --- request GET /tg --- response_body ---- error_log eval -qr/.*\[\{\"body\":\{\"text\":\{\"text\":\".*this is an error message for test.*\"\}\},\"endpoint\":\"\",\"service\":\"APISIX\",\"serviceInstance\":\"instance\".*/ +--- error_log +this is an error message for test --- wait: 5 diff --git a/t/plugin/grpc-transcode2.t b/t/plugin/grpc-transcode2.t index 0f7cc8d0ba99..da91d3ceb265 100644 --- a/t/plugin/grpc-transcode2.t +++ b/t/plugin/grpc-transcode2.t @@ -486,7 +486,7 @@ GET /grpc_plus?a=1&b=2 --- response_body eval qr/\{"result":3\}/ --- error_log eval -qr/request log: \{.*body":\"\\u0000\\u0000\\u0000\\u0000\\u0002\\b\\u0003\\u0000\\u0000\\u0000\\u0000\\u0002\\b\\u0003"/ +qr/request log: \{.*body":\"\\u0000\\u0000\\u0000\\u0000\\u0002\\b\\u0003"/ diff --git a/t/plugin/http-logger2.t b/t/plugin/http-logger2.t index 09572439c775..12ee8b437d78 100644 --- a/t/plugin/http-logger2.t +++ b/t/plugin/http-logger2.t @@ -88,6 +88,12 @@ add_block_preprocessor(sub { end } } + + location / { + content_by_lua_block { + ngx.log(ngx.WARN, "test http logger for root path") + } + } } _EOC_ @@ -305,3 +311,49 @@ test-http-logger-request --- error_log received Authorization header: [nil] --- wait: 1.5 + + + +=== TEST 10: add default path +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "http-logger": { + "uri": "http://127.0.0.1:12001", + "batch_max_size": 1, + "max_retry_count": 1, + "retry_delay": 2, + "buffer_duration": 2, + "inactive_timeout": 2 + } + }, + "upstream": { + "nodes": { + "127.0.0.1:12001": 1 + }, + "type": "roundrobin" + }, + "uri": "/http-logger/test" + }]]) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 11: hit +--- request +GET /http-logger/test +--- error_log +test http logger for root path diff --git a/t/plugin/jwt-auth3.t b/t/plugin/jwt-auth3.t index 7daf6cf164a3..3ab089a974c6 100755 --- a/t/plugin/jwt-auth3.t +++ b/t/plugin/jwt-auth3.t @@ -280,11 +280,11 @@ hello: world }, "upstream": { "nodes": { - "httpbin.org:80": 1 + "test.com:1980": 1 }, "type": "roundrobin" }, - "uri": "/get" + "uri": "/hello" }]] ) @@ -299,11 +299,11 @@ hello: world === TEST 12: verify (in cookie) with hiding credentials --- request -GET /get +GET /hello --- more_headers Cookie: hello=world; jwt-cookie=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTg3OTMxODU0MX0.fNtFJnNmJgzbiYmGB0Yjvm-l6A6M4jRV1l4mnVFSYjs; foo=bar ---- response_body eval -qr/hello=world; foo=bar/ +--- response_body +hello world diff --git a/t/plugin/opentelemetry-bugfix-pb-state.t b/t/plugin/opentelemetry-bugfix-pb-state.t new file mode 100644 index 000000000000..b6f2e1052e24 --- /dev/null +++ b/t/plugin/opentelemetry-bugfix-pb-state.t @@ -0,0 +1,192 @@ +# +# 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'; + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->extra_yaml_config) { + my $extra_yaml_config = <<_EOC_; +plugins: + - example-plugin + - key-auth + - opentelemetry +plugin_attr: + opentelemetry: + batch_span_processor: + max_export_batch_size: 1 + inactive_timeout: 0.5 +_EOC_ + $block->set_value("extra_yaml_config", $extra_yaml_config); + } + + + if (!$block->extra_init_by_lua) { + my $extra_init_by_lua = <<_EOC_; +-- mock exporter http client +local client = require("opentelemetry.trace.exporter.http_client") +client.do_request = function() + ngx.log(ngx.INFO, "opentelemetry export span") + return "ok" +end +local ctx_new = require("opentelemetry.context").new +require("opentelemetry.context").new = function (...) + local ctx = ctx_new(...) + local current = ctx.current + ctx.current = function (...) + ngx.log(ngx.INFO, "opentelemetry context current") + return current(...) + end + return ctx +end +_EOC_ + + $block->set_value("extra_init_by_lua", $extra_init_by_lua); + } + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + $block; +}); + +run_tests; + +__DATA__ + +=== TEST 1: set additional_attributes with match +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "name": "route_name", + "plugins": { + "opentelemetry": { + "sampler": { + "name": "always_on" + }, + "additional_header_prefix_attributes": [ + "x-my-header-*" + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 2: opentelemetry expands headers +--- extra_init_by_lua + local otlp = require("opentelemetry.trace.exporter.otlp") + local orig_export_spans = otlp.export_spans + otlp.export_spans = function(self, spans) + if (#spans ~= 1) then + ngx.log(ngx.ERR, "unexpected spans length: ", #spans) + return + end + + local attributes_names = {} + local attributes = {} + local span = spans[1] + for _, attribute in ipairs(span.attributes) do + if attribute.key == "hostname" then + -- remove any randomness + goto skip + end + table.insert(attributes_names, attribute.key) + attributes[attribute.key] = attribute.value.string_value or "" + ::skip:: + end + table.sort(attributes_names) + for _, attribute in ipairs(attributes_names) do + ngx.log(ngx.INFO, "attribute " .. attribute .. ": \"" .. attributes[attribute] .. "\"") + end + + ngx.log(ngx.INFO, "opentelemetry export span") + return orig_export_spans(self, spans) + end +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/protos/1', + ngx.HTTP_PUT, + [[{ + "content" : "syntax = \"proto3\"; + package helloworld; + service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply) {} + } + message HelloRequest { + string name = 1; + } + message HelloReply { + string message = 1; + }" + }]] + ) + + if code >= 300 then + ngx.status = code + end + local http = require "resty.http" + local httpc = http.new() + local uri1 = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello" + local headers = { + ["x-my-header-name"] = "william", + ["x-my-header-nick"] = "bill", + } + local res, err = httpc:request_uri(uri1, {method = "GET", headers = headers}) + if not res then + ngx.say(err) + return + end + ngx.status = res.status + } + } +--- wait: 1 +--- error_code: 200 +--- no_error_log +type 'opentelemetry.proto.trace.v1.TracesData' does not exists +--- grep_error_log eval +qr/attribute .+?:.[^,]*/ +--- grep_error_log_out +attribute route: "route_name" +attribute service: "" +attribute x-my-header-name: "william" +attribute x-my-header-nick: "bill" diff --git a/t/plugin/proxy-mirror.t b/t/plugin/proxy-mirror.t index 9c58f4bac98e..031737d50a13 100644 --- a/t/plugin/proxy-mirror.t +++ b/t/plugin/proxy-mirror.t @@ -705,8 +705,8 @@ passed [[{ "plugins": { "proxy-mirror": { - "host": "http://httpbin.org", - "path": "/get" + "host": "http://test.com:1980", + "path": "/hello" } }, "upstream": { @@ -736,7 +736,7 @@ GET /hello --- response_body hello world --- error_log_like eval -qr/http:\/\/httpbin\.org is resolved to: http:\/\/((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}/ +qr/http:\/\/test\.com is resolved to: http:\/\/((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}/ diff --git a/t/plugin/response-rewrite.t b/t/plugin/response-rewrite.t index def0c61db219..2e4dcf4eb324 100644 --- a/t/plugin/response-rewrite.t +++ b/t/plugin/response-rewrite.t @@ -696,3 +696,40 @@ passed --- request GET /hello --- response_body + + + +=== TEST 27: test add header with one word +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "response-rewrite": { + "headers": { + "add": [ + "X-Server-test:a" + ] + } + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uris": ["/hello"] + }]] + ) + + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed diff --git a/t/plugin/sls-logger.t b/t/plugin/sls-logger.t index a56d6121f876..9e668e1bf3b0 100644 --- a/t/plugin/sls-logger.t +++ b/t/plugin/sls-logger.t @@ -173,16 +173,20 @@ hello world end math.randomseed(os.time()) - local rfc5424 = require("apisix.plugins.slslog.rfc5424") + local rfc5424 = require("apisix.utils.rfc5424") local m = 0 -- because the millisecond value obtained by `ngx.now` may be `0` -- it is executed multiple times to ensure the accuracy of the test for i = 1, 5 do ngx.sleep(string.format("%0.3f", math.random())) + local structured_data = { + {name = "project", value = "apisix.apache.org"}, + {name = "logstore", value = "apisix.apache.org"}, + {name = "access-key-id", value = "apisix.sls.logger"}, + {name = "access-key-secret", value = "BD274822-96AA-4DA6-90EC-15940FB24444"} + } local log_entry = rfc5424.encode("SYSLOG", "INFO", "localhost", "apisix", - 123456, "apisix.apache.org", "apisix.apache.log", - "apisix.sls.logger", "BD274822-96AA-4DA6-90EC-15940FB24444", - "hello world") + 123456, "hello world", structured_data) m = get_syslog_timestamp_millisecond(log_entry) + m end @@ -226,15 +230,13 @@ passed === TEST 9: access --- extra_init_by_lua local json = require("toolkit.json") - local rfc5424 = require("apisix.plugins.slslog.rfc5424") + local rfc5424 = require("apisix.utils.rfc5424") local old_f = rfc5424.encode - rfc5424.encode = function(facility, severity, hostname, appname, pid, project, - logstore, access_key_id, access_key_secret, msg) + rfc5424.encode = function(facility, severity, hostname, appname, pid, msg, structured_data) local r = json.decode(msg) assert(r.client_ip == "127.0.0.1", r.client_ip) assert(r.host == "localhost", r.host) - return old_f(facility, severity, hostname, appname, pid, project, - logstore, access_key_id, access_key_secret, msg) + return old_f(facility, severity, hostname, appname, pid, msg, structured_data) end --- request GET /hello @@ -372,14 +374,12 @@ passed === TEST 13: access --- extra_init_by_lua local json = require("toolkit.json") - local rfc5424 = require("apisix.plugins.slslog.rfc5424") + local rfc5424 = require("apisix.utils.rfc5424") local old_f = rfc5424.encode - rfc5424.encode = function(facility, severity, hostname, appname, pid, project, - logstore, access_key_id, access_key_secret, msg) + rfc5424.encode = function(facility, severity, hostname, appname, pid, msg, structured_data) local r = json.decode(msg) assert(r.vip == "127.0.0.1", r.vip) - return old_f(facility, severity, hostname, appname, pid, project, - logstore, access_key_id, access_key_secret, msg) + return old_f(facility, severity, hostname, appname, pid, msg, structured_data) end --- request GET /hello diff --git a/t/plugin/splunk-hec-logging.t b/t/plugin/splunk-hec-logging.t index 78d6e9689ebd..58fba496aa1f 100644 --- a/t/plugin/splunk-hec-logging.t +++ b/t/plugin/splunk-hec-logging.t @@ -287,9 +287,9 @@ passed local data = ngx.req.get_body_data() ngx.log(ngx.WARN, data) data = decode(data) - assert(data[1].event.client_ip == "127.0.0.1") - assert(data[1].source == "apache-apisix-splunk-hec-logging") - assert(data[1].host == core.utils.gethostname()) + assert(data.event.client_ip == "127.0.0.1") + assert(data.source == "apache-apisix-splunk-hec-logging") + assert(data.host == core.utils.gethostname()) ngx.say('{}') end --- request @@ -361,9 +361,9 @@ passed local data = ngx.req.get_body_data() ngx.log(ngx.WARN, data) data = decode(data) - assert(data[1].event.vip == "127.0.0.1") - assert(data[1].source == "apache-apisix-splunk-hec-logging") - assert(data[1].host == core.utils.gethostname()) + assert(data.event.vip == "127.0.0.1") + assert(data.source == "apache-apisix-splunk-hec-logging") + assert(data.host == core.utils.gethostname()) ngx.say('{}') end --- request @@ -375,3 +375,50 @@ hello world the mock backend is hit --- no_error_log [error] + + + +=== TEST 11: set route test batched data +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', ngx.HTTP_PUT, { + uri = "/hello", + upstream = { + type = "roundrobin", + nodes = { + ["127.0.0.1:1980"] = 1 + } + }, + plugins = { + ["splunk-hec-logging"] = { + endpoint = { + uri = "http://127.0.0.1:18088/services/collector", + token = "BD274822-96AA-4DA6-90EC-18940FB2414C" + }, + batch_max_size = 3, + inactive_timeout = 1 + } + } + }) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 12: hit +--- pipelined_requests eval +["GET /hello", "GET /hello", "GET /hello"] +--- wait: 2 +--- response_body eval +["hello world\n", "hello world\n", "hello world\n"] +--- no_error_log +[error] diff --git a/t/plugin/syslog.t b/t/plugin/syslog.t index a8795bc24be6..5e13aa301c56 100644 --- a/t/plugin/syslog.t +++ b/t/plugin/syslog.t @@ -31,7 +31,7 @@ __DATA__ local plugin = require("apisix.plugins.syslog") local ok, err = plugin.check_schema({ host = "127.0.0.1", - port = 3000, + port = 5140, }) if not ok then ngx.say(err) @@ -73,7 +73,7 @@ done local plugin = require("apisix.plugins.syslog") local ok, err = plugin.check_schema({ host = "127.0.0.1", - port = "3000", + port = "5140", }) if not ok then ngx.say(err) @@ -100,7 +100,7 @@ done "plugins": { "syslog": { "host" : "127.0.0.1", - "port" : 5044 + "port" : 5140 } }, "upstream": { @@ -142,7 +142,7 @@ hello world local logger_socket = require("resty.logger.socket") local logger, err = logger_socket:new({ host = "127.0.0.1", - port = 5044, + port = 5140, flush_limit = 100, }) @@ -183,7 +183,7 @@ done "plugins": { "syslog": { "host" : "127.0.0.1", - "port" : 5044, + "port" : 5140, "flush_limit" : 1, "inactive_timeout": 1 } @@ -236,7 +236,15 @@ unlock with key route#1 -=== TEST 8: check plugin configuration updating +=== TEST 8: check log +--- exec +tail -n 1 ci/pod/vector/syslog-tcp.log +--- response_body eval +qr/.*apisix_latency.*/ + + + +=== TEST 9: check plugin configuration updating --- config location /t { content_by_lua_block { @@ -327,7 +335,7 @@ sending a batch logs to 127.0.0.1:5045 -=== TEST 9: add log format +=== TEST 10: add log format --- config location /t { content_by_lua_block { @@ -355,7 +363,7 @@ passed -=== TEST 10: Add route and Enable Syslog Plugin, batch_max_size=1 +=== TEST 11: Add route and Enable Syslog Plugin, batch_max_size=1 --- config location /t { content_by_lua_block { @@ -369,7 +377,7 @@ passed "disable": false, "flush_limit": 1, "host" : "127.0.0.1", - "port" : 5050 + "port" : 5140 } }, "upstream": { @@ -394,7 +402,7 @@ passed -=== TEST 11: hit route and report sys logger +=== TEST 12: hit route and report sys logger --- extra_init_by_lua local syslog = require("apisix.plugins.syslog.init") local json = require("apisix.core.json") @@ -416,7 +424,15 @@ qr/syslog-log-format.*\{.*"upstream":"127.0.0.1:\d+"/ -=== TEST 12: log format in plugin +=== TEST 13: check log +--- exec +tail -n 1 ci/pod/vector/syslog-tcp.log +--- response_body eval +qr/.*\"host\":\"localhost\".*/ + + + +=== TEST 14: log format in plugin --- config location /t { content_by_lua_block { @@ -432,7 +448,7 @@ qr/syslog-log-format.*\{.*"upstream":"127.0.0.1:\d+"/ "vip": "$remote_addr" }, "host" : "127.0.0.1", - "port" : 5050 + "port" : 5140 } }, "upstream": { @@ -461,7 +477,7 @@ passed -=== TEST 13: access +=== TEST 15: access --- extra_init_by_lua local syslog = require("apisix.plugins.syslog.init") local json = require("apisix.core.json") @@ -481,3 +497,65 @@ hello world [error] --- error_log push_entry is called with data + + + +=== TEST 16: check log +--- exec +tail -n 1 ci/pod/vector/syslog-tcp.log +--- response_body eval +qr/.*vip.*/ + + + +=== TEST 17: test udp mode +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "syslog": { + "batch_max_size": 1, + "disable": false, + "flush_limit": 1, + "host" : "127.0.0.1", + "port" : 5150, + "sock_type": "udp" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 18: hit +--- request +GET /hello + + + +=== TEST 19: check log +--- exec +tail -n 1 ci/pod/vector/syslog-udp.log +--- response_body eval +qr/.*upstream.*/ diff --git a/t/plugin/tcp-logger.t b/t/plugin/tcp-logger.t index 0d15b5692d5f..3ef774f813d1 100644 --- a/t/plugin/tcp-logger.t +++ b/t/plugin/tcp-logger.t @@ -97,7 +97,7 @@ done "plugins": { "tcp-logger": { "host": "127.0.0.1", - "port": 5044, + "port": 3000, "tls": false } }, @@ -134,6 +134,7 @@ hello world === TEST 6: error log +--- log_level: error --- config location /t { content_by_lua_block { @@ -192,7 +193,7 @@ failed to connect to TCP server: host[312.0.0.1] port[2000] "plugins": { "tcp-logger": { "host": "127.0.0.1", - "port": 5044, + "port": 3000, "tls": false, "batch_max_size": 1 } @@ -226,7 +227,7 @@ failed to connect to TCP server: host[312.0.0.1] port[2000] "plugins": { "tcp-logger": { "host": "127.0.0.1", - "port": 5045, + "port": 43000, "tls": false, "batch_max_size": 1 } @@ -269,8 +270,8 @@ passedopentracing --- grep_error_log eval qr/sending a batch logs to 127.0.0.1:(\d+)/ --- grep_error_log_out -sending a batch logs to 127.0.0.1:5044 -sending a batch logs to 127.0.0.1:5045 +sending a batch logs to 127.0.0.1:3000 +sending a batch logs to 127.0.0.1:43000 @@ -312,7 +313,7 @@ GET /t "plugins": { "tcp-logger": { "host": "127.0.0.1", - "port": 8125, + "port": 3000, "tls": false, "batch_max_size": 1, "inactive_timeout": 1 @@ -338,6 +339,7 @@ GET /t ngx.HTTP_PUT, [[{ "log_format": { + "case name": "plugin_metadata", "host": "$host", "@timestamp": "$time_iso8601", "client_ip": "$remote_addr" @@ -349,7 +351,15 @@ GET /t ngx.say(body) return end - ngx.say(body) + + local code, _, _ = t("/hello", "GET") + if code >= 300 then + ngx.status = code + ngx.say("fail") + return + end + + ngx.say("passed") } } --- request @@ -359,47 +369,38 @@ passed -=== TEST 10: access ---- stream_conf_enable ---- extra_stream_config - server { - listen 8125; - content_by_lua_block { - local decode = require("toolkit.json").decode - ngx.log(ngx.WARN, "the mock backend is hit") +=== TEST 10: log format in plugin_metadata +--- exec +tail -n 1 ci/pod/vector/tcp.log +--- response_body eval +qr/.*plugin_metadata.*/ - local sock, err = ngx.req.socket(true) - if not sock then - ngx.log(ngx.ERR, "failed to get the request socket: ", err) - return - end - local data, err = sock:receive('*a') - if not data then - if err and err ~= "closed" then - ngx.log(ngx.ERR, "socket error, returning: ", err) - end - return - end +=== TEST 11: remove tcp logger metadata +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + + local code, body = t('/apisix/admin/plugin_metadata/tcp-logger', + ngx.HTTP_PUT, + [[{ + "log_format": {} + }]] + ) - data = decode(data) - assert(data.client_ip == "127.0.0.1") + ngx.say(body) } } --- request -GET /hello +GET /t --- response_body -hello world ---- wait: 2 ---- error_log -the mock backend is hit ---- no_error_log -[error] +passed -=== TEST 11: log format in plugin +=== TEST 12: log format in plugin --- config location /t { content_by_lua_block { @@ -410,9 +411,10 @@ the mock backend is hit "plugins": { "tcp-logger": { "host": "127.0.0.1", - "port": 8125, + "port": 3000, "tls": false, "log_format": { + "case name": "logger format in plugin", "vip": "$remote_addr" }, "batch_max_size": 1, @@ -435,50 +437,84 @@ the mock backend is hit return end - ngx.say(body) + local code, _, body2 = t("/hello", "GET") + if code >= 300 then + ngx.status = code + ngx.say("fail") + return + end + + ngx.say("passed") } } --- request GET /t +--- wait: 0.5 --- response_body passed -=== TEST 12: access ---- stream_conf_enable ---- extra_stream_config - server { - listen 8125; +=== TEST 13: check tcp log +--- exec +tail -n 1 ci/pod/vector/tcp.log +--- response_body eval +qr/.*logger format in plugin.*/ + + + +=== TEST 14: true tcp log with tls +--- config + location /t { content_by_lua_block { - local decode = require("toolkit.json").decode - ngx.log(ngx.WARN, "the mock backend is hit") + local t = require("lib.test_admin").test + local code, body1 = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "tcp-logger": { + "host": "127.0.0.1", + "port": 43000, + "tls": true, + "batch_max_size": 1 + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1982": 1 + }, + "type": "roundrobin" + }, + "uri": "/opentracing" + }]] + ) - local sock, err = ngx.req.socket(true) - if not sock then - ngx.log(ngx.ERR, "failed to get the request socket: ", err) + if code >= 300 then + ngx.status = code + ngx.say("fail") return end - local data, err = sock:receive('*a') - - if not data then - if err and err ~= "closed" then - ngx.log(ngx.ERR, "socket error, returning: ", err) - end + local code, _, body2 = t("/opentracing", "GET") + if code >= 300 then + ngx.status = code + ngx.say("fail") return end - data = decode(data) - assert(data.vip == "127.0.0.1") + ngx.print(body2) } } --- request -GET /hello +GET /t +--- wait: 0.5 --- response_body -hello world ---- wait: 2 ---- error_log -the mock backend is hit ---- no_error_log -[error] +opentracing + + + +=== TEST 15: check tls log +--- exec +tail -n 1 ci/pod/vector/tls-datas.log +--- response_body eval +qr/.*route_id.*1.*/ diff --git a/t/plugin/traffic-split.t b/t/plugin/traffic-split.t index f79152fd8f21..4a56988e83c6 100644 --- a/t/plugin/traffic-split.t +++ b/t/plugin/traffic-split.t @@ -710,11 +710,12 @@ passed === TEST 19: domain name resolved successfully ---- request -GET / ---- error_code: 200 ---- error_log eval -qr/dns resolver domain: www.apiseven.com to \d+.\d+.\d+.\d+/ +--- pipelined_requests eval +["GET /", "GET /"] +--- error_code eval +[200, 200] +--- error_log_like eval +qr/(dns resolver domain: www.apiseven.com to \d+.\d+.\d+.\d+){2}/ diff --git a/t/plugin/udp-logger.t b/t/plugin/udp-logger.t index 288c3e5124a5..934b47c68ee3 100644 --- a/t/plugin/udp-logger.t +++ b/t/plugin/udp-logger.t @@ -365,7 +365,7 @@ passed local decode = require("toolkit.json").decode ngx.log(ngx.WARN, "the mock backend is hit") - local sock, err = ngx.req.socket(true) + local sock, err = ngx.req.socket() if not sock then ngx.log(ngx.ERR, "failed to get the request socket: ", err) return @@ -451,7 +451,7 @@ passed local decode = require("toolkit.json").decode ngx.log(ngx.WARN, "the mock backend is hit") - local sock, err = ngx.req.socket(true) + local sock, err = ngx.req.socket() if not sock then ngx.log(ngx.ERR, "failed to get the request socket: ", err) return diff --git a/t/plugin/wolf-rbac.t b/t/plugin/wolf-rbac.t index 34c7068ca56f..8136e3df6bc0 100644 --- a/t/plugin/wolf-rbac.t +++ b/t/plugin/wolf-rbac.t @@ -685,3 +685,53 @@ passed ngx.status = code } } + + + +=== TEST 36: add consumer with echo plugin +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers', + ngx.HTTP_PUT, + [[{ + "username": "wolf_rbac_with_other_plugins", + "plugins": { + "wolf-rbac": { + "appid": "wolf-rbac-app", + "server": "http://127.0.0.1:1982" + }, + "echo": { + "body": "consumer merge echo plugins\n" + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 37: verify echo plugin in consumer +--- request +GET /hello +--- more_headers +Authorization: V1#wolf-rbac-app#wolf-rbac-token +--- response_headers +X-UserId: 100 +X-Username: admin +X-Nickname: administrator +--- response_body +consumer merge echo plugins +--- no_error_log +[error] diff --git a/t/stream-plugin/mqtt-proxy2.t b/t/stream-plugin/mqtt-proxy2.t index 8c797a353d77..35211878e330 100644 --- a/t/stream-plugin/mqtt-proxy2.t +++ b/t/stream-plugin/mqtt-proxy2.t @@ -75,3 +75,110 @@ passed --- error_log failed to parse domain: loc, error: --- timeout: 10 + + + +=== TEST 3: set upstream(id: 1) +--- 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, + [[{ + "type": "chash", + "key": "mqtt_client_id", + "nodes": [ + { + "host": "0.0.0.0", + "port": 1995, + "weight": 1 + }, + { + "host": "127.0.0.1", + "port": 1995, + "weight": 1 + } + ] + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 4: balance with mqtt_client_id +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/stream_routes/1', + ngx.HTTP_PUT, + [[{ + "remote_addr": "127.0.0.1", + "server_port": 1985, + "plugins": { + "mqtt-proxy": { + "protocol_name": "MQTT", + "protocol_level": 5 + } + }, + "upstream_id": 1 + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 5: hit route with empty id +--- stream_request eval +"\x10\x0d\x00\x04\x4d\x51\x54\x54\x05\x02\x00\x3c\x00\x00\x00" +--- stream_response +hello world +--- grep_error_log eval +qr/(mqtt client id: \w+|proxy request to \S+)/ +--- grep_error_log_out +proxy request to 127.0.0.1:1995 + + + +=== TEST 6: hit route with different client id, part 1 +--- stream_request eval +"\x10\x0e\x00\x04\x4d\x51\x54\x54\x05\x02\x00\x3c\x00\x00\x01\x66" +--- stream_response +hello world +--- grep_error_log eval +qr/(mqtt client id: \w+|proxy request to \S+)/ +--- grep_error_log_out +mqtt client id: f +proxy request to 0.0.0.0:1995 + + + +=== TEST 7: hit route with different client id, part 2 +--- stream_request eval +"\x10\x0e\x00\x04\x4d\x51\x54\x54\x05\x02\x00\x3c\x00\x00\x01\x67" +--- stream_response +hello world +--- grep_error_log eval +qr/(mqtt client id: \w+|proxy request to \S+)/ +--- grep_error_log_out +mqtt client id: g +proxy request to 127.0.0.1:1995 diff --git a/t/utils/rfc5424.t b/t/utils/rfc5424.t new file mode 100644 index 000000000000..06051e627cc4 --- /dev/null +++ b/t/utils/rfc5424.t @@ -0,0 +1,83 @@ +# +# 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(); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!defined $block->request) { + $block->set_value("request", "GET /t"); + } + +}); + +run_tests(); + +__DATA__ + +=== TEST 1: Compatibility testing +--- config + location /t { + content_by_lua_block { + local rfc5424 = require("apisix.utils.rfc5424") + local structured_data = { + {name = "project", value = "apisix.apache.org"}, + {name = "logstore", value = "apisix.apache.org"}, + {name = "access-key-id", value = "apisix.sls.logger"}, + {name = "access-key-secret", value = "BD274822-96AA-4DA6-90EC-15940FB24444"} + } + local data = rfc5424.encode("SYSLOG", "INFO", "localhost", "apisix", + 123456, "hello world", structured_data) + ngx.say(data) + } + } +--- response_body eval +qr/<46>1.*localhost apisix 123456 - \[logservice project=\"apisix\.apache\.org\" logstore=\"apisix\.apache\.org\" access-key-id=\"apisix\.sls\.logger\" access-key-secret=\"BD274822-96AA-4DA6-90EC-15940FB24444\"\] hello world/ + + + +=== TEST 2: No structured data test +--- config + location /t { + content_by_lua_block { + local rfc5424 = require("apisix.utils.rfc5424") + local data = rfc5424.encode("SYSLOG", "INFO", "localhost", "apisix", + 123456, "hello world") + ngx.say(data) + } + } +--- response_body eval +qr/<46>1.*localhost apisix 123456 - - hello world/ + + + +=== TEST 3: No host and appname test +--- config + location /t { + content_by_lua_block { + local rfc5424 = require("apisix.utils.rfc5424") + local data = rfc5424.encode("SYSLOG", "INFO", nil, nil, + 123456, "hello world") + ngx.say(data) + } + } +--- response_body eval +qr/<46>1.*- - 123456 - - hello world/ diff --git a/t/xrpc/pingpong2.t b/t/xrpc/pingpong2.t index fc77fa1482df..7365929ac326 100644 --- a/t/xrpc/pingpong2.t +++ b/t/xrpc/pingpong2.t @@ -497,7 +497,7 @@ passed --- stream_conf_enable --- wait: 0.5 --- error_log eval -qr/message received:.*\"client_ip\\"\:\\"127.0.0.1\\"/ +qr/message received:.*\"client_ip\"\:\"127.0.0.1\"/ @@ -556,7 +556,7 @@ passed --- stream_conf_enable --- wait: 0.5 --- error_log eval -qr/message received:.*\"client_ip\\"\:\\"127.0.0.1\\"/ +qr/message received:.*\"client_ip\"\:\"127.0.0.1\"/ diff --git a/t/xrpc/pingpong3.t b/t/xrpc/pingpong3.t index c6d98810d656..da16e626b9e4 100644 --- a/t/xrpc/pingpong3.t +++ b/t/xrpc/pingpong3.t @@ -190,4 +190,4 @@ passed --- stream_conf_enable --- wait: 0.5 --- error_log eval -qr/message received:.*\"rpc_time\\"\:(0.\d+|0)\}\"/ +qr/message received:.*\"rpc_time\"\:(0.\d+|0)\}/ diff --git a/t/xrpc/redis2.t b/t/xrpc/redis2.t index 65ca9829c616..076a406b4f4f 100644 --- a/t/xrpc/redis2.t +++ b/t/xrpc/redis2.t @@ -16,6 +16,8 @@ # use t::APISIX; +log_level("warn"); + my $nginx_binary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; my $version = eval { `$nginx_binary -V 2>&1` }; @@ -117,7 +119,7 @@ passed { name = "syslog", filter = { - {"rpc_time", ">=", 0.01}, + {"rpc_time", ">=", 0.001}, }, conf = { host = "127.0.0.1", @@ -149,6 +151,7 @@ passed === TEST 3: verify the data received by the log server +--- stream_conf_enable --- config location /t { content_by_lua_block { @@ -190,11 +193,10 @@ passed hmset animals: OK hmget animals: barkmeow ping: pong ---- stream_conf_enable --- wait: 1 --- grep_error_log eval -qr/message received:.*\"redis_cmd_line\\"\:[^,]+/ +qr/message received:.*\"redis_cmd_line\":[^}|^,]+/ --- grep_error_log_out eval -[qr/message received:.*\"redis_cmd_line\\"\:\\\"hmset animals dog bark cat meow\\\"/, -qr/message received:.*\"redis_cmd_line\\"\:\\\"hmget animals dog cat\\\"/, -qr/message received:.*\"redis_cmd_line\\"\:\\\"ping\\\"/] +qr{message received:.*\"redis_cmd_line\":\"hmset animals dog bark cat meow\"(?s).* +message received:.*\"redis_cmd_line\":\"hmget animals dog cat\"(?s).* +message received:.*\"redis_cmd_line\":\"ping\"}