From f554b87b41a07d50c19526626ef01ad98362d637 Mon Sep 17 00:00:00 2001 From: wklken Date: Wed, 6 Nov 2024 16:47:56 +0800 Subject: [PATCH 1/9] feat(plugins/bk-traffic-label.lua): add new plugin --- src/apisix/plugins/bk-traffic-label.lua | 151 +++++++++++++ src/apisix/tests/test-bk-traffic-label.lua | 249 +++++++++++++++++++++ 2 files changed, 400 insertions(+) create mode 100644 src/apisix/plugins/bk-traffic-label.lua create mode 100644 src/apisix/tests/test-bk-traffic-label.lua diff --git a/src/apisix/plugins/bk-traffic-label.lua b/src/apisix/plugins/bk-traffic-label.lua new file mode 100644 index 0000000..5cfb382 --- /dev/null +++ b/src/apisix/plugins/bk-traffic-label.lua @@ -0,0 +1,151 @@ +-- +-- TencentBlueKing is pleased to support the open source community by making +-- 蓝鲸智云 - API 网关(BlueKing - APIGateway) available. +-- Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. +-- Licensed under the MIT License (the "License"); you may not use this file except +-- in compliance with the License. You may obtain a copy of the License at +-- +-- http://opensource.org/licenses/MIT +-- +-- Unless required by applicable law or agreed to in writing, software distributed under +-- the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +-- either express or implied. See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- We undertake not to change the open source license (MIT license) applicable +-- to the current version of the project delivered to anyone in the future. +-- + +-- this plugin is impls based on the doc of api7 traffic-label +-- link: https://docs.api7.ai/hub/traffic-label/ + +local core = require("apisix.core") +local expr = require("resty.expr.v1") +local pairs = pairs +local ipairs = ipairs + +local match_schema = { + type = "array", +} + +local actions_schema = { + type = "array", + items = { + type = "object", + properties = { + set_headers = { + type = "object", + additionalProperties = { + type = "string" + } + }, + weight = { + description = "percentage of all matched which would do the actions", + type = "integer", + default = 1, + minimum = 0 + } + } + }, + minItems = 1, + maxItems = 20 +} + +local schema = { + type = "object", + properties = { + rules = { + type = "array", + items = { + type = "object", + properties = { + match = match_schema, + actions = actions_schema + }, + } + } + }, +} + +local plugin_name = "bk-traffic-label" + +local _M = { + version = 0.1, + priority = 967, + name = plugin_name, + schema = schema +} + +function _M.check_schema(conf) + -- Validate the configuration schema + local ok, err = core.schema.check(schema, conf) + if not ok then + return false, err + end + + if conf.rules then + for _, rule in ipairs(conf.rules) do + if rule.match then + -- Validate the match expression + local _, err = expr.new(rule.match) + if err then + core.log.error("failed to validate the 'match' expression: ", err) + return false, "failed to validate the 'match' expression: " .. err + end + end + -- Calculate total weight of all actions + local total_weight = 0 + for _, action in ipairs(rule.actions) do + total_weight = total_weight + (action.weight or 1) + end + -- Normalize the weight of each action + for _, action in ipairs(rule.actions) do + action.weight = (action.weight or 1) / total_weight + end + end + end + + return true +end + +local function apply_actions(actions, ctx) + -- Generate a random number between 0 and 1 + local random_weight = math.random() + local current_weight = 0 + + for _, action in ipairs(actions) do + current_weight = current_weight + action.weight + -- Apply the action if the random number falls within the current weight range + if random_weight <= current_weight then + if action.set_headers then + -- Set the specified headers + for k, v in pairs(action.set_headers) do + core.request.set_header(ctx, k, v) + end + end + break + end + end +end + +function _M.access(conf, ctx) + if not conf or not conf.rules then + return + end + + for _, rule in ipairs(conf.rules) do + if rule.match then + -- Evaluate the match expression + local ex, _ = expr.new(rule.match) + local match_passed = ex:eval(ctx.var) + if match_passed then + -- Apply the actions if the match condition is met + apply_actions(rule.actions, ctx) + end + end + end + + return +end + +return _M diff --git a/src/apisix/tests/test-bk-traffic-label.lua b/src/apisix/tests/test-bk-traffic-label.lua new file mode 100644 index 0000000..3da437a --- /dev/null +++ b/src/apisix/tests/test-bk-traffic-label.lua @@ -0,0 +1,249 @@ +local plugin = require("apisix.plugins.bk-traffic-label") + +describe( + "bk-traffic-label", function() + + local ctx + local conf + + before_each( + function() + ctx = { + var = { + uri = "/foo", + host = "example.com", + remote_addr = "127.0.0.1" + }, + headers = {} + } + end + ) + + context( + "1 ruls: 1 match 1 action", function() + before_each( + function() + conf = { + rules = { + { + match = { + {"uri", "==", "/foo"} + }, + actions = { + { + set_headers = { + ["X-Test-Header"] = "test" + } + } + } + } + } + } + end + ) + + it( + "match hit set_headers", function() + plugin.check_schema(conf) + + plugin.access(conf, ctx) + assert.is_equal(ctx.headers["X-Test-Header"], "test") + end + ) + + it( + "match miss do nothing", function() + plugin.check_schema(conf) + + ctx.var.uri = "/bar" + plugin.access(conf, ctx) + assert.is_nil(ctx.headers["X-Test-Header"]) + end + ) + end + ) + + context( + "1 ruls: 1 match 2 actions, with weight", function() + before_each( + function() + conf = { + rules = { + { + match = { + {"uri", "==", "/foo"} + }, + actions = { + { + set_headers = { + ["X-Test-Header-1"] = "test1" + }, + weight = 0.5 + }, + { + set_headers = { + ["X-Test-Header-2"] = "test2" + }, + weight = 0.5 + } + } + } + } + } + end + ) + + it( + "multiple-actions with weight", function() + plugin.check_schema(conf) + + math.randomseed(os.time()) + plugin.access(conf, ctx) + assert.is_true(ctx.headers["X-Test-Header-1"] == "test1" or ctx.headers["X-Test-Header-2"] == "test2") + end + ) + end + ) + + context( + "1 ruls: 1 match 2 actions, one with weight 0", function() + before_each( + function() + conf = { + rules = { + { + match = { + {"uri", "==", "/foo"} + }, + actions = { + { + set_headers = { + ["X-Test-Header-1"] = "test1" + }, + weight = 0 + }, + { + set_headers = { + ["X-Test-Header-2"] = "test2" + }, + weight = 1 + } + } + } + } + } + end + ) + + it( + "only the action with non-zero weight is applied", function() + plugin.check_schema(conf) + + plugin.access(conf, ctx) + assert.is_nil(ctx.headers["X-Test-Header-1"]) + assert.is_equal(ctx.headers["X-Test-Header-2"], "test2") + end + ) + end + ) + + context( + "1 ruls: 1 match 2 actions, one with weight 0, another weight no set_headers", function() + before_each( + function() + conf = { + rules = { + { + match = { + {"uri", "==", "/foo"} + }, + actions = { + { + set_headers = { + ["X-Test-Header-1"] = "test1" + }, + weight = 0 + }, + { + weight = 1 + } + } + } + } + } + end + ) + + it( + "only the action with non-zero weight is applied, but do nothing", function() + plugin.check_schema(conf) + + plugin.access(conf, ctx) + assert.is_nil(ctx.headers["X-Test-Header-1"]) + assert.is_nil(ctx.headers["X-Test-Header-2"]) + -- assert.is_equal(ctx.headers["X-Test-Header-2"], "test2") + end + ) + end + ) + + context( + "2 rules", function() + + before_each( + function() + conf = { + rules = { + { + match = { + {"uri", "==", "/foo"} + }, + actions = { + { + set_headers = { + ["X-Test-Header-1"] = "test1" + } + } + } + }, + { + match = { + {"host", "==", "example.com"} + }, + actions = { + { + set_headers = { + ["X-Test-Header-2"] = "test2" + } + } + } + } + } + } + end + ) + + it( + "multiple matches, all hit", function() + plugin.check_schema(conf) + + plugin.access(conf, ctx) + assert.is_equal(ctx.headers["X-Test-Header-1"], "test1") + assert.is_equal(ctx.headers["X-Test-Header-2"], "test2") + end + ) + + it( + "multiple matches, only hit one", function() + plugin.check_schema(conf) + + ctx.var.uri = "/bar" + plugin.access(conf, ctx) + assert.is_nil(ctx.headers["X-Test-Header-1"]) + assert.is_equal(ctx.headers["X-Test-Header-2"], "test2") + end + ) + end + ) + end +) From e0d9c904a4efa2bd4ab6ef4e162cdd1a99e1cf46 Mon Sep 17 00:00:00 2001 From: wklken Date: Wed, 6 Nov 2024 16:49:49 +0800 Subject: [PATCH 2/9] feat(priority): update --- src/apisix/plugins/README.md | 1 + src/apisix/plugins/bk-traffic-label.lua | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/apisix/plugins/README.md b/src/apisix/plugins/README.md index ecfa077..33a859c 100644 --- a/src/apisix/plugins/README.md +++ b/src/apisix/plugins/README.md @@ -56,6 +56,7 @@ proxy 预处理:17000 ~ 17500 +- bk-traffic-label # priority: 17460 - bk-delete-sensitive # priority: 17450 - bk-delete-cookie # priority: 17440 - bk-proxy-rewrite # priority: 17430 # 该插件供 operator 进行后端地址转换使用 diff --git a/src/apisix/plugins/bk-traffic-label.lua b/src/apisix/plugins/bk-traffic-label.lua index 5cfb382..991f241 100644 --- a/src/apisix/plugins/bk-traffic-label.lua +++ b/src/apisix/plugins/bk-traffic-label.lua @@ -71,7 +71,7 @@ local plugin_name = "bk-traffic-label" local _M = { version = 0.1, - priority = 967, + priority = 17460, name = plugin_name, schema = schema } From 3d76800b26425f959460213248de7d2df75f398e Mon Sep 17 00:00:00 2001 From: wklken Date: Wed, 6 Nov 2024 17:41:28 +0800 Subject: [PATCH 3/9] style(plugins/bk-traffic-label.lua): fix lint --- src/apisix/plugins/bk-traffic-label.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/apisix/plugins/bk-traffic-label.lua b/src/apisix/plugins/bk-traffic-label.lua index 991f241..902822d 100644 --- a/src/apisix/plugins/bk-traffic-label.lua +++ b/src/apisix/plugins/bk-traffic-label.lua @@ -87,8 +87,8 @@ function _M.check_schema(conf) for _, rule in ipairs(conf.rules) do if rule.match then -- Validate the match expression - local _, err = expr.new(rule.match) - if err then + local _, err2 = expr.new(rule.match) + if err2 then core.log.error("failed to validate the 'match' expression: ", err) return false, "failed to validate the 'match' expression: " .. err end From c6aa2dfb7aa14d69e171b1b3541ba819a2075077 Mon Sep 17 00:00:00 2001 From: wklken Date: Thu, 7 Nov 2024 14:38:03 +0800 Subject: [PATCH 4/9] test(bk-traffic-label): add more cases --- src/apisix/plugins/bk-traffic-label.lua | 16 +- src/apisix/t/bk-traffic-label.t | 368 +++++++++++++++++++++ src/apisix/tests/test-bk-traffic-label.lua | 82 ++++- 3 files changed, 460 insertions(+), 6 deletions(-) create mode 100644 src/apisix/t/bk-traffic-label.t diff --git a/src/apisix/plugins/bk-traffic-label.lua b/src/apisix/plugins/bk-traffic-label.lua index 902822d..c0f9450 100644 --- a/src/apisix/plugins/bk-traffic-label.lua +++ b/src/apisix/plugins/bk-traffic-label.lua @@ -65,6 +65,7 @@ local schema = { } } }, + required = {"rules"}, } local plugin_name = "bk-traffic-label" @@ -99,8 +100,15 @@ function _M.check_schema(conf) total_weight = total_weight + (action.weight or 1) end -- Normalize the weight of each action - for _, action in ipairs(rule.actions) do - action.weight = (action.weight or 1) / total_weight + local accumulated_weight = 0 + for i, action in ipairs(rule.actions) do + if i == #rule.actions then + -- Assign the remaining weight to the last action to ensure the total is 100 + action.weight = 100 - accumulated_weight + else + action.weight = math.floor((action.weight or 1) / total_weight * 100) + accumulated_weight = accumulated_weight + action.weight + end end end end @@ -109,8 +117,8 @@ function _M.check_schema(conf) end local function apply_actions(actions, ctx) - -- Generate a random number between 0 and 1 - local random_weight = math.random() + -- Generate a random number between 0 and 100 + local random_weight = math.random(0, 100) local current_weight = 0 for _, action in ipairs(actions) do diff --git a/src/apisix/t/bk-traffic-label.t b/src/apisix/t/bk-traffic-label.t new file mode 100644 index 0000000..cee148e --- /dev/null +++ b/src/apisix/t/bk-traffic-label.t @@ -0,0 +1,368 @@ +# +# TencentBlueKing is pleased to support the open source community by making +# 蓝鲸智云 - API 网关(BlueKing - APIGateway) available. +# Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. +# Licensed under the MIT License (the "License"); you may not use this file except +# in compliance with the License. You may obtain a copy of the License at +# +# http://opensource.org/licenses/MIT +# +# Unless required by applicable law or agreed to in writing, software distributed under +# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific language governing permissions and +# limitations under the License. +# +# We undertake not to change the open source license (MIT license) applicable +# to the current version of the project delivered to anyone in the future. +# + +BEGIN { + if ($ENV{TEST_NGINX_CHECK_LEAK}) { + $SkipReason = "unavailable for the hup tests"; + + } else { + $ENV{TEST_NGINX_USE_HUP} = 1; + undef $ENV{TEST_NGINX_USE_STAP}; + } +} + +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_shuffle(); +no_root_location(); +run_tests; + +__DATA__ + +=== TEST 1: sanity +--- config + location /t { + content_by_lua_block { + local plugin = require("apisix.plugins.bk-traffic-label") + local ok, err = plugin.check_schema({ + rules = { + { + match = {{"arg_env", "==", "prod"}}, + actions = { + {set_headers = {["X-Test-Header"] = "test"}} + } + } + } + }) + if not ok then + ngx.say(err) + end + + ngx.say("done") + } + } +--- request +GET /t +--- response_body +done + +=== TEST 2: match hit set_headers +--- 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": { + "bk-proxy-rewrite": { + "uri": "/uri/plugin_proxy_rewrite" + }, + "bk-traffic-label": { + "rules": [ + { + "match": [ + ["arg_env", "==", "prod"] + ], + "actions": [ + {"set_headers": {"X-Test-Header": "test"}} + ] + } + ] + } + }, + "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: match hit set_headers +--- request +GET /hello?env=prod +--- response_body +uri: /uri/plugin_proxy_rewrite +host: localhost +x-real-ip: 127.0.0.1 +x-test-header: test + + +=== TEST 4: match miss do nothing +--- request +GET /hello?env=dev +--- response_body +uri: /uri/plugin_proxy_rewrite +host: localhost +x-real-ip: 127.0.0.1 + +=== TEST 5: multiple-actions with weight +--- 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": { + "bk-proxy-rewrite": { + "uri": "/uri/plugin_proxy_rewrite" + }, + "bk-traffic-label": { + "rules": [ + { + "match": [ + ["arg_env", "==", "prod"] + ], + "actions": [ + {"set_headers": {"X-Test-Header": "test"}, "weight": 1}, + {"set_headers": {"X-Test-Header": "test"}, "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) + } + } +--- request +GET /t +--- response_body +passed + +=== TEST 6: match hit set_headers +--- request +GET /hello?env=prod +--- response_body +uri: /uri/plugin_proxy_rewrite +host: localhost +x-real-ip: 127.0.0.1 +x-test-header: test + +=== TEST 7: only the action with non-zero weight is applied +--- 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": { + "bk-proxy-rewrite": { + "uri": "/uri/plugin_proxy_rewrite" + }, + "bk-traffic-label": { + "rules": [ + { + "match": [ + ["arg_env", "==", "prod"] + ], + "actions": [ + {"set_headers": {"X-Test-Header": "test1"}, "weight": 0}, + {"set_headers": {"X-Test-Header": "test2"}, "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) + } + } +--- request +GET /t +--- response_body +passed + +=== TEST 6: match hit set_headers +--- request +GET /hello?env=prod +--- response_body +uri: /uri/plugin_proxy_rewrite +host: localhost +x-real-ip: 127.0.0.1 +x-test-header: test2 + +=== TEST 7: only the action with non-zero weight is applied, but do nothing +--- 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": { + "bk-proxy-rewrite": { + "uri": "/uri/plugin_proxy_rewrite" + }, + "bk-traffic-label": { + "rules": [ + { + "match": [ + ["arg_env", "==", "prod"] + ], + "actions": [ + {"set_headers": {"X-Test-Header-1": "test1"}, "weight": 0}, + {"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) + } + } +--- request +GET /t +--- response_body +passed + +=== TEST 8: match hit set_headers +--- request +GET /hello?env=prod +--- response_body +uri: /uri/plugin_proxy_rewrite +host: localhost +x-real-ip: 127.0.0.1 + +=== TEST 9: multiple matches, all hit +--- 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": { + "bk-proxy-rewrite": { + "uri": "/uri/plugin_proxy_rewrite" + }, + "bk-traffic-label": { + "rules": [ + { + "match": [ + ["arg_env", "==", "prod"] + ], + "actions": [ + {"set_headers": {"X-Test-Header-1": "test1"}} + ] + }, + { + "match": [ + ["arg_type", "==", "foo"] + ], + "actions": [ + {"set_headers": {"X-Test-Header-2": "test2"}} + ] + } + ] + } + }, + "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 10: multiple matches, only hit one +--- request +GET /hello?env=dev&type=foo +--- response_body +uri: /uri/plugin_proxy_rewrite +host: localhost +x-real-ip: 127.0.0.1 +x-test-header-2: test2 + +=== TEST 10: multiple matches, hit both +--- request +GET /hello?env=prod&type=foo +--- response_body +uri: /uri/plugin_proxy_rewrite +host: localhost +x-real-ip: 127.0.0.1 +x-test-header-1: test1 +x-test-header-2: test2 diff --git a/src/apisix/tests/test-bk-traffic-label.lua b/src/apisix/tests/test-bk-traffic-label.lua index 3da437a..efa45c4 100644 --- a/src/apisix/tests/test-bk-traffic-label.lua +++ b/src/apisix/tests/test-bk-traffic-label.lua @@ -19,6 +19,83 @@ describe( end ) + context( + "schema validation", function() + it( + "invalid schema: empty", function() + conf = {} + local ok, err = plugin.check_schema(conf) + assert.is_false(ok) + assert.is_not_nil(err) + end + ) + + it( + "valid schema", function() + conf = { + rules = { + { + match = { + {"uri", "==", "/foo"} + }, + actions = { + { + set_headers = { + ["X-Test-Header"] = "test" + } + } + } + } + } + } + local ok, err = plugin.check_schema(conf) + assert.is_true(ok) + assert.is_nil(err) + end + ) + + it( + "valid schema with 1 match and 3 actions with same weight", function() + conf = { + rules = { + { + match = { + {"uri", "==", "/foo"} + }, + actions = { + { + set_headers = { + ["X-Test-Header-1"] = "test1" + }, + weight = 1 + }, + { + set_headers = { + ["X-Test-Header-2"] = "test2" + }, + weight = 1 + }, + { + set_headers = { + ["X-Test-Header-3"] = "test3" + }, + weight = 1 + } + } + } + } + } + local ok, err = plugin.check_schema(conf) + assert.is_true(ok) + assert.is_nil(err) + assert.is_equal(conf.rules[1].actions[1].weight, 33) + assert.is_equal(conf.rules[1].actions[2].weight, 33) + assert.is_equal(conf.rules[1].actions[3].weight, 34) + end + ) + end + ) + context( "1 ruls: 1 match 1 action", function() before_each( @@ -78,13 +155,13 @@ describe( set_headers = { ["X-Test-Header-1"] = "test1" }, - weight = 0.5 + weight = 1 }, { set_headers = { ["X-Test-Header-2"] = "test2" }, - weight = 0.5 + weight = 1 } } } @@ -245,5 +322,6 @@ describe( ) end ) + end ) From 7e666783877e67364d6e3dcb7a58251ee573fe76 Mon Sep 17 00:00:00 2001 From: wklken Date: Thu, 7 Nov 2024 16:55:25 +0800 Subject: [PATCH 5/9] feat(makefile): update test-nginx to support run specific case --- src/apisix/Makefile | 4 ++- src/apisix/ci/run-test-nginx.sh | 7 ++++- src/apisix/t/bk-00.t | 54 +++++++++++++++++++++++++++++++++ src/apisix/t/bk-traffic-label.t | 1 + 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 src/apisix/t/bk-00.t diff --git a/src/apisix/Makefile b/src/apisix/Makefile index 087c7b5..8973287 100644 --- a/src/apisix/Makefile +++ b/src/apisix/Makefile @@ -20,12 +20,14 @@ test-busted: -v ${ROOT_DIR}/plugins:/bkgateway/apisix/plugins \ apisix-test-busted "/run-test-busted.sh" +# make test-nginx +# make test-nginx CASE_FILE=bk-traffic-label.t .PHONY: test-nginx test-nginx: @docker run --rm ${RUN_WITH_IT} \ -v ${ROOT_DIR}/t:/bkgateway/t/ \ -v ${ROOT_DIR}/plugins:/bkgateway/apisix/plugins \ - apisix-test-nginx "/run-test-nginx.sh" + apisix-test-nginx "/run-test-nginx.sh" $(if $(CASE_FILE),$(CASE_FILE)) .PHONY: apisix-test-images apisix-test-images: apisix-test-busted apisix-test-nginx diff --git a/src/apisix/ci/run-test-nginx.sh b/src/apisix/ci/run-test-nginx.sh index 2dd280d..7e966b7 100755 --- a/src/apisix/ci/run-test-nginx.sh +++ b/src/apisix/ci/run-test-nginx.sh @@ -71,5 +71,10 @@ export OPENRESTY_PREFIX="/usr/local/openresty-debug" export APISIX_MAIN="https://raw.githubusercontent.com/apache/incubator-apisix/master/rockspec/apisix-master-0.rockspec" export PATH=$OPENRESTY_PREFIX/nginx/sbin:$OPENRESTY_PREFIX/luajit/bin:$OPENRESTY_PREFIX/bin:$PATH -FLUSH_ETCD=1 prove --timer -Itest-nginx/lib -I./ t/bk-*.t +if [ -n "$1" ]; then + CASE_FILE=$1 + FLUSH_ETCD=1 prove --timer -Itest-nginx/lib -I./ t/bk-00.t t/$CASE_FILE +else + FLUSH_ETCD=1 prove --timer -Itest-nginx/lib -I./ t/bk-*.t +fi diff --git a/src/apisix/t/bk-00.t b/src/apisix/t/bk-00.t new file mode 100644 index 0000000..bbeb0f0 --- /dev/null +++ b/src/apisix/t/bk-00.t @@ -0,0 +1,54 @@ +# +# TencentBlueKing is pleased to support the open source community by making +# 蓝鲸智云 - API 网关(BlueKing - APIGateway) available. +# Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. +# Licensed under the MIT License (the "License"); you may not use this file except +# in compliance with the License. You may obtain a copy of the License at +# +# http://opensource.org/licenses/MIT +# +# Unless required by applicable law or agreed to in writing, software distributed under +# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific language governing permissions and +# limitations under the License. +# +# We undertake not to change the open source license (MIT license) applicable +# to the current version of the project delivered to anyone in the future. +# + +# NOTE: this file should be ran as the first test file +# otherwise other test files would fail + +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +no_shuffle(); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!defined $block->request) { + $block->set_value("request", "GET /t"); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: sanity +--- config + location /t { + content_by_lua_block { + local ok = true + if not ok then + ngx.say(err) + end + + ngx.say("done") + } + } +--- response_body +done diff --git a/src/apisix/t/bk-traffic-label.t b/src/apisix/t/bk-traffic-label.t index cee148e..378e490 100644 --- a/src/apisix/t/bk-traffic-label.t +++ b/src/apisix/t/bk-traffic-label.t @@ -1,4 +1,5 @@ # + # TencentBlueKing is pleased to support the open source community by making # 蓝鲸智云 - API 网关(BlueKing - APIGateway) available. # Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. From b2a71e07b512a6a83f456d10a9dfd96c329839c4 Mon Sep 17 00:00:00 2001 From: wklken Date: Thu, 7 Nov 2024 18:06:57 +0800 Subject: [PATCH 6/9] fix(bk-traffic-label): bug when weight is 0 --- src/apisix/plugins/bk-traffic-label.lua | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/apisix/plugins/bk-traffic-label.lua b/src/apisix/plugins/bk-traffic-label.lua index c0f9450..1c6d40f 100644 --- a/src/apisix/plugins/bk-traffic-label.lua +++ b/src/apisix/plugins/bk-traffic-label.lua @@ -94,11 +94,16 @@ function _M.check_schema(conf) return false, "failed to validate the 'match' expression: " .. err end end - -- Calculate total weight of all actions + + -- Calculate total weight of all actions and preprocess actions to set default weight local total_weight = 0 for _, action in ipairs(rule.actions) do - total_weight = total_weight + (action.weight or 1) + if action.weight == nil or action.weight <= 0 then + action.weight = 1 + end + total_weight = total_weight + action.weight end + -- Normalize the weight of each action local accumulated_weight = 0 for i, action in ipairs(rule.actions) do @@ -106,7 +111,7 @@ function _M.check_schema(conf) -- Assign the remaining weight to the last action to ensure the total is 100 action.weight = 100 - accumulated_weight else - action.weight = math.floor((action.weight or 1) / total_weight * 100) + action.weight = math.floor(action.weight / total_weight * 100) accumulated_weight = accumulated_weight + action.weight end end From 6954909645fed2fb0b3a9433c6479cd960901565 Mon Sep 17 00:00:00 2001 From: wklken Date: Thu, 7 Nov 2024 19:14:53 +0800 Subject: [PATCH 7/9] fix(plugins/bk-traffic-label.lua): if the weight = 0, should not change to 1 --- src/apisix/plugins/bk-traffic-label.lua | 2 +- src/apisix/t/bk-traffic-label.t | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/apisix/plugins/bk-traffic-label.lua b/src/apisix/plugins/bk-traffic-label.lua index 1c6d40f..6b00b4a 100644 --- a/src/apisix/plugins/bk-traffic-label.lua +++ b/src/apisix/plugins/bk-traffic-label.lua @@ -98,7 +98,7 @@ function _M.check_schema(conf) -- Calculate total weight of all actions and preprocess actions to set default weight local total_weight = 0 for _, action in ipairs(rule.actions) do - if action.weight == nil or action.weight <= 0 then + if action.weight == nil or action.weight < 0 then action.weight = 1 end total_weight = total_weight + action.weight diff --git a/src/apisix/t/bk-traffic-label.t b/src/apisix/t/bk-traffic-label.t index 378e490..bcc8784 100644 --- a/src/apisix/t/bk-traffic-label.t +++ b/src/apisix/t/bk-traffic-label.t @@ -231,7 +231,7 @@ GET /t --- response_body passed -=== TEST 6: match hit set_headers +=== TEST 8: match hit set_headers --- request GET /hello?env=prod --- response_body @@ -240,7 +240,7 @@ host: localhost x-real-ip: 127.0.0.1 x-test-header: test2 -=== TEST 7: only the action with non-zero weight is applied, but do nothing +=== TEST 9: only the action with non-zero weight is applied, but do nothing --- config location /t { content_by_lua_block { @@ -287,7 +287,7 @@ GET /t --- response_body passed -=== TEST 8: match hit set_headers +=== TEST 10: match hit set_headers --- request GET /hello?env=prod --- response_body @@ -295,7 +295,7 @@ uri: /uri/plugin_proxy_rewrite host: localhost x-real-ip: 127.0.0.1 -=== TEST 9: multiple matches, all hit +=== TEST 11: multiple matches, all hit --- config location /t { content_by_lua_block { @@ -349,7 +349,7 @@ GET /t --- response_body passed -=== TEST 10: multiple matches, only hit one +=== TEST 12: multiple matches, only hit one --- request GET /hello?env=dev&type=foo --- response_body @@ -358,7 +358,7 @@ host: localhost x-real-ip: 127.0.0.1 x-test-header-2: test2 -=== TEST 10: multiple matches, hit both +=== TEST 13: multiple matches, hit both --- request GET /hello?env=prod&type=foo --- response_body From f81fe7e0a2b2ffb6085f6abda8a87a7364720b6f Mon Sep 17 00:00:00 2001 From: wklken Date: Fri, 15 Nov 2024 11:25:48 +0800 Subject: [PATCH 8/9] fix(plugins/bk-traffic-label.lua): typo --- src/apisix/plugins/bk-traffic-label.lua | 4 ++-- src/apisix/tests/test-bk-traffic-label.lua | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/apisix/plugins/bk-traffic-label.lua b/src/apisix/plugins/bk-traffic-label.lua index 6b00b4a..6462bd2 100644 --- a/src/apisix/plugins/bk-traffic-label.lua +++ b/src/apisix/plugins/bk-traffic-label.lua @@ -90,8 +90,8 @@ function _M.check_schema(conf) -- Validate the match expression local _, err2 = expr.new(rule.match) if err2 then - core.log.error("failed to validate the 'match' expression: ", err) - return false, "failed to validate the 'match' expression: " .. err + core.log.error("failed to validate the 'match' expression: ", err2) + return false, "failed to validate the 'match' expression: " .. err2 end end diff --git a/src/apisix/tests/test-bk-traffic-label.lua b/src/apisix/tests/test-bk-traffic-label.lua index efa45c4..20e5346 100644 --- a/src/apisix/tests/test-bk-traffic-label.lua +++ b/src/apisix/tests/test-bk-traffic-label.lua @@ -97,7 +97,7 @@ describe( ) context( - "1 ruls: 1 match 1 action", function() + "1 rules: 1 match 1 action", function() before_each( function() conf = { @@ -141,7 +141,7 @@ describe( ) context( - "1 ruls: 1 match 2 actions, with weight", function() + "1 rules: 1 match 2 actions, with weight", function() before_each( function() conf = { @@ -183,7 +183,7 @@ describe( ) context( - "1 ruls: 1 match 2 actions, one with weight 0", function() + "1 rules: 1 match 2 actions, one with weight 0", function() before_each( function() conf = { @@ -225,7 +225,7 @@ describe( ) context( - "1 ruls: 1 match 2 actions, one with weight 0, another weight no set_headers", function() + "1 rules: 1 match 2 actions, one with weight 0, another weight no set_headers", function() before_each( function() conf = { From 3aad0e87e7a31f61251a4bf0c13bde4c0ae7e282 Mon Sep 17 00:00:00 2001 From: wklken Date: Fri, 22 Nov 2024 11:35:40 +0800 Subject: [PATCH 9/9] fix(plugins/bk-traffic-label.lua): remove weight normalized, use raw weight --- src/apisix/plugins/bk-traffic-label.lua | 23 ++++++---------------- src/apisix/tests/test-bk-traffic-label.lua | 7 ++++--- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/apisix/plugins/bk-traffic-label.lua b/src/apisix/plugins/bk-traffic-label.lua index 6462bd2..d85e719 100644 --- a/src/apisix/plugins/bk-traffic-label.lua +++ b/src/apisix/plugins/bk-traffic-label.lua @@ -103,30 +103,19 @@ function _M.check_schema(conf) end total_weight = total_weight + action.weight end - - -- Normalize the weight of each action - local accumulated_weight = 0 - for i, action in ipairs(rule.actions) do - if i == #rule.actions then - -- Assign the remaining weight to the last action to ensure the total is 100 - action.weight = 100 - accumulated_weight - else - action.weight = math.floor(action.weight / total_weight * 100) - accumulated_weight = accumulated_weight + action.weight - end - end + rule.total_weight = total_weight end end return true end -local function apply_actions(actions, ctx) - -- Generate a random number between 0 and 100 - local random_weight = math.random(0, 100) +local function apply_actions(rule, ctx) + -- Generate a random number between 1 and total_weight, [1, total_weight] + local random_weight = math.random(1, rule.total_weight) local current_weight = 0 - for _, action in ipairs(actions) do + for _, action in ipairs(rule.actions) do current_weight = current_weight + action.weight -- Apply the action if the random number falls within the current weight range if random_weight <= current_weight then @@ -153,7 +142,7 @@ function _M.access(conf, ctx) local match_passed = ex:eval(ctx.var) if match_passed then -- Apply the actions if the match condition is met - apply_actions(rule.actions, ctx) + apply_actions(rule, ctx) end end end diff --git a/src/apisix/tests/test-bk-traffic-label.lua b/src/apisix/tests/test-bk-traffic-label.lua index 20e5346..c93e52b 100644 --- a/src/apisix/tests/test-bk-traffic-label.lua +++ b/src/apisix/tests/test-bk-traffic-label.lua @@ -88,9 +88,10 @@ describe( local ok, err = plugin.check_schema(conf) assert.is_true(ok) assert.is_nil(err) - assert.is_equal(conf.rules[1].actions[1].weight, 33) - assert.is_equal(conf.rules[1].actions[2].weight, 33) - assert.is_equal(conf.rules[1].actions[3].weight, 34) + assert.is_equal(conf.rules[1].actions[1].weight, 1) + assert.is_equal(conf.rules[1].actions[2].weight, 1) + assert.is_equal(conf.rules[1].actions[3].weight, 1) + assert.is_equal(conf.rules[1].total_weight, 3) end ) end