diff --git a/apisix/plugins/fault-injection.lua b/apisix/plugins/fault-injection.lua index ac2846db7f80e..eb699c33a6b9c 100644 --- a/apisix/plugins/fault-injection.lua +++ b/apisix/plugins/fault-injection.lua @@ -35,7 +35,10 @@ local schema = { percentage = {type = "integer", minimum = 0, maximum = 100}, vars = { type = "array", - maxItems = 20 + maxItems = 20, + item = { + type = "array", + }, } }, required = {"http_status"}, @@ -47,7 +50,10 @@ local schema = { percentage = {type = "integer", minimum = 0, maximum = 100}, vars = { type = "array", - maxItems = 20 + maxItems = 20, + item = { + type = "array", + }, } }, required = {"duration"}, diff --git a/apisix/plugins/response-rewrite.lua b/apisix/plugins/response-rewrite.lua index 2614fcb25ab35..b3c35ff3302d5 100644 --- a/apisix/plugins/response-rewrite.lua +++ b/apisix/plugins/response-rewrite.lua @@ -47,12 +47,6 @@ local schema = { }, vars = { type = "array", - items = { - description = "Nginx builtin variable name and value", - type = "array", - maxItems = 4, - minItems = 2, - }, }, }, minProperties = 1, diff --git a/apisix/plugins/traffic-split.lua b/apisix/plugins/traffic-split.lua index c070865c716ba..764e99b3d1909 100644 --- a/apisix/plugins/traffic-split.lua +++ b/apisix/plugins/traffic-split.lua @@ -33,48 +33,6 @@ local lrucache = core.lrucache.new({ local vars_schema = { type = "array", - items = { - type = "array", - items = { - { - type = "string", - minLength = 1, - maxLength = 100 - }, - { - type = "string", - minLength = 1, - maxLength = 2 - } - }, - additionalItems = { - anyOf = { - {type = "string"}, - {type = "number"}, - {type = "boolean"}, - { - type = "array", - items = { - anyOf = { - { - type = "string", - minLength = 1, maxLength = 100 - }, - { - type = "number" - }, - { - type = "boolean" - } - } - }, - uniqueItems = true - } - } - }, - minItems = 0, - maxItems = 10 - } } @@ -154,6 +112,19 @@ function _M.check_schema(conf) return false, err end + if conf.rules then + for _, rule in ipairs(conf.rules) do + if rule.match then + for _, m in ipairs(rule.match) do + local ok, err = expr.new(m.vars) + if not ok then + return false, "failed to validate the 'vars' expression: " .. err + end + end + end + end + end + return true end diff --git a/apisix/schema_def.lua b/apisix/schema_def.lua index 9ae7669928ba3..6f620f3e955fe 100644 --- a/apisix/schema_def.lua +++ b/apisix/schema_def.lua @@ -462,12 +462,6 @@ _M.route = { }, vars = { type = "array", - items = { - description = "Nginx builtin variable name and value", - type = "array", - maxItems = 4, - minItems = 2, - }, }, filter_func = { type = "string", diff --git a/rockspec/apisix-master-0.rockspec b/rockspec/apisix-master-0.rockspec index d7bc0ddb4109e..20327c19e8766 100644 --- a/rockspec/apisix-master-0.rockspec +++ b/rockspec/apisix-master-0.rockspec @@ -44,7 +44,7 @@ dependencies = { "lua-resty-cookie = 0.1.0", "lua-resty-session = 2.24", "opentracing-openresty = 0.1", - "lua-resty-radixtree = 2.6.1", + "lua-resty-radixtree = 2.7.0", "lua-protobuf = 0.3.1", "lua-resty-openidc = 1.7.2-1", "luafilesystem = 1.7.0-2", @@ -59,7 +59,7 @@ dependencies = { "binaryheap = 0.4", "dkjson = 2.5-2", "resty-redis-cluster = 1.02-4", - "lua-resty-expr = 1.1.0", + "lua-resty-expr = 1.2.0", "graphql = 0.0.2", "argparse = 0.7.1-1", "luasocket = 3.0rc1-2", diff --git a/t/plugin/fault-injection.t b/t/plugin/fault-injection.t index d0469f51ee371..566bdd6abd200 100644 --- a/t/plugin/fault-injection.t +++ b/t/plugin/fault-injection.t @@ -802,7 +802,7 @@ done http_status = 403, body = "Fault Injection!\n", vars = { - {"arg_name","==","jack"} + {"arg_name","!=","jack"} } }, delay = { @@ -828,7 +828,7 @@ done --- request GET /t --- response_body -invalid expression +invalid operator '!=' done --- error_log eval qr/failed to create vars expression:.*/ diff --git a/t/plugin/fault-injection2.t b/t/plugin/fault-injection2.t new file mode 100644 index 0000000000000..841c9566320ff --- /dev/null +++ b/t/plugin/fault-injection2.t @@ -0,0 +1,97 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +repeat_each(1); +log_level('info'); +no_root_location(); +no_shuffle(); +master_on(); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if (!$block->error_log && !$block->no_error_log) { + $block->set_value("no_error_log", "[error]"); + } +}); + +run_tests(); + +__DATA__ + +=== TEST 1: vars rule with ! (set) +--- 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": { + "fault-injection": { + "abort": { + "http_status": 403, + "body": "Fault Injection!\n", + "vars": [ + [ + "!AND", + ["arg_name","==","jack"], + ["arg_age","!","<",18] + ] + ] + } + } + }, + "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: vars rule with ! (hit) +--- request +GET /hello?name=jack&age=17 +--- error_code: 403 +--- response_body +Fault Injection! + + + +=== TEST 3: vars rule with ! (miss) +--- request +GET /hello?name=jack&age=18 +--- response_body +hello world diff --git a/t/plugin/response-rewrite.t b/t/plugin/response-rewrite.t index 90e231be8eb89..b4141b51a0ac0 100644 --- a/t/plugin/response-rewrite.t +++ b/t/plugin/response-rewrite.t @@ -560,7 +560,7 @@ done --- request GET /t --- response_body -property "vars" validation failed: failed to validate item 1: expect array to have at least 2 items +failed to validate the 'vars' expression: rule too short --- no_error_log [error] diff --git a/t/plugin/traffic-split.t b/t/plugin/traffic-split.t index dc58c60170e62..66f1f19ed9b88 100644 --- a/t/plugin/traffic-split.t +++ b/t/plugin/traffic-split.t @@ -172,7 +172,7 @@ done --- request GET /t --- response_body eval -qr/property "rules" validation failed:.* failed to validate item 2: wrong type: expected string, got number/ +qr/failed to validate the 'vars' expression: invalid operator '123'/ --- no_error_log [error] diff --git a/t/plugin/traffic-split2.t b/t/plugin/traffic-split2.t new file mode 100644 index 0000000000000..655885a4f04a2 --- /dev/null +++ b/t/plugin/traffic-split2.t @@ -0,0 +1,105 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +repeat_each(1); +log_level('info'); +no_root_location(); +no_shuffle(); +master_on(); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if (!$block->error_log && !$block->no_error_log) { + $block->set_value("no_error_log", "[error]"); + } +}); + +run_tests(); + +__DATA__ + +=== TEST 1: vars rule with ! (set) +--- 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": "/server_port", + "plugins": { + "traffic-split": { + "rules": [ + { + "match": [ + { + "vars": [ + ["!AND", + ["arg_name", "==", "jack"], + ["arg_age", "!", "<", "18"] + ] + ] + } + ], + "weighted_upstreams": [ + { + "upstream": {"name": "upstream_A", "type": "roundrobin", "nodes": {"127.0.0.1:1981":1}}, + "weight": 1 + } + ] + } + ] + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + } + }]=] + ) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 2: vars rule with ! (hit) +--- request +GET /server_port?name=jack&age=17 +--- response_body chomp +1981 + + + +=== TEST 3: vars rule with ! (miss) +--- request +GET /server_port?name=jack&age=18 +--- response_body chomp +1980 diff --git a/t/router/radixtree-uri-vars.t b/t/router/radixtree-uri-vars.t index 870fae24eb8ba..235a439eb6865 100644 --- a/t/router/radixtree-uri-vars.t +++ b/t/router/radixtree-uri-vars.t @@ -320,3 +320,66 @@ User-Agent: ios hello world --- no_error_log [error] + + + +=== TEST 16: vars rule with logical operator (set) +--- 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, + [=[{ + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello", + "vars": [ + "!OR", + ["http_user_agent", "==", "ios"], + ["http_demo", "==", "test"] + ] + }]=] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 17: vars rule with logical operator (hit) +--- request +GET /hello +--- more_headers +User-Agent: android +demo: prod +--- response_body +hello world +--- no_error_log +[error] + + + +=== TEST 18: vars rule with logical operator (miss) +--- request +GET /hello +--- more_headers +User-Agent: ios +demo: prod +--- error_code: 404 +--- no_error_log +[error]