diff --git a/apisix/admin/proto.lua b/apisix/admin/proto.lua index de4d24e23e88..f8133cc80b71 100644 --- a/apisix/admin/proto.lua +++ b/apisix/admin/proto.lua @@ -50,7 +50,7 @@ local function check_proto_used(plugins, deleting, ptype, pid) if type(plugins) == "table" and plugins["grpc-transcode"] and plugins["grpc-transcode"].proto_id and tostring(plugins["grpc-transcode"].proto_id) == deleting then - return false, {error_msg = "can not delete this proto," + return false, {error_msg = "can not delete this proto, " .. ptype .. " [" .. pid .. "] is still using it now"} end diff --git a/apisix/admin/resource.lua b/apisix/admin/resource.lua index bfc6789df0fb..35fe3bba2476 100644 --- a/apisix/admin/resource.lua +++ b/apisix/admin/resource.lua @@ -230,7 +230,7 @@ function _M:put(id, conf, sub_path, args) end -- Keep the unused conf to make the args list consistent with other methods -function _M:delete(id, conf, sub_path) +function _M:delete(id, conf, sub_path, uri_args) if core.table.array_find(self.unsupported_methods, "delete") then return 405, {error_msg = "not supported `DELETE` method for " .. self.kind} end @@ -253,7 +253,7 @@ function _M:delete(id, conf, sub_path) key = key .. "/" .. id - if self.delete_checker then + if self.delete_checker and uri_args.force ~= "true" then local code, err = self.delete_checker(id) if err then return code, err diff --git a/docs/en/latest/admin-api.md b/docs/en/latest/admin-api.md index af2775e7b67b..5096684d01a6 100644 --- a/docs/en/latest/admin-api.md +++ b/docs/en/latest/admin-api.md @@ -103,6 +103,33 @@ deployment: This will find the environment variable `ADMIN_KEY` first, and if it does not exist, it will use `edd1c9f034335f136f87ad84b625c8f1` as the default value. +### Force Delete + +By default, the Admin API checks for references between resources and will refuse to delete resources in use. + +You can make a force deletion by adding the request argument `force=true` to the delete request, for example: + +```bash +$ curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '{ + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" +}' +$ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '{ + "uri": "/*", + "upstream_id": 1 +}' +{"value":{"priority":0,"upstream_id":1,"uri":"/*","create_time":1689038794,"id":"1","status":1,"update_time":1689038916},"key":"/apisix/routes/1"} + +$ curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE +{"error_msg":"can not delete this upstream, route [1] is still using it now"} +$ curl "http://127.0.0.1:9180/apisix/admin/upstreams/1?force=anyvalue" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE +{"error_msg":"can not delete this upstream, route [1] is still using it now"} +$ curl "http://127.0.0.1:9180/apisix/admin/upstreams/1?force=true" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE +{"deleted":"1","key":"/apisix/upstreams/1"} +``` + ## V3 new feature The Admin API has made some breaking changes in V3 version, as well as supporting additional features. diff --git a/docs/zh/latest/admin-api.md b/docs/zh/latest/admin-api.md index 5e623d990794..f2f4d1f81bbc 100644 --- a/docs/zh/latest/admin-api.md +++ b/docs/zh/latest/admin-api.md @@ -105,6 +105,33 @@ deployment: 首先查找环境变量 `ADMIN_KEY`,如果该环境变量不存在,它将使用 `edd1c9f034335f136f87ad84b625c8f1` 作为默认值。 +### 强制删除 {#force-delete} + +默认情况下,Admin API 会检查资源间的引用关系,将会拒绝删除正在使用中的资源。 + +可以通过在删除请求中添加请求参数 `force=true` 来进行强制删除,例如: + +```bash +$ curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '{ + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" +}' +$ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '{ + "uri": "/*", + "upstream_id": 1 +}' +{"value":{"priority":0,"upstream_id":1,"uri":"/*","create_time":1689038794,"id":"1","status":1,"update_time":1689038916},"key":"/apisix/routes/1"} + +$ curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE +{"error_msg":"can not delete this upstream, route [1] is still using it now"} +$ curl "http://127.0.0.1:9180/apisix/admin/upstreams/1?force=anyvalue" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE +{"error_msg":"can not delete this upstream, route [1] is still using it now"} +$ curl "http://127.0.0.1:9180/apisix/admin/upstreams/1?force=true" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE +{"deleted":"1","key":"/apisix/upstreams/1"} +``` + ## v3 版本新功能 {#v3-new-function} 在 APISIX v3 版本中,Admin API 支持了一些不向下兼容的新特性,比如支持新的响应体格式、支持分页查询、支持过滤资源等。 diff --git a/t/admin/consumer-group-force-delete.t b/t/admin/consumer-group-force-delete.t new file mode 100644 index 000000000000..4b2fb2d09a67 --- /dev/null +++ b/t/admin/consumer-group-force-delete.t @@ -0,0 +1,163 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +no_shuffle(); +log_level("info"); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if (!$block->no_error_log && !$block->error_log) { + $block->set_value("no_error_log", "[error]\n[alert]"); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: set consumer_group(id: 1) +--- 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": { + "limit-count": { + "count": 200, + "time_window": 60, + "rejected_code": 503, + "group": "$consumer_group_id" + } + } + }]] + ) + ngx.status = code + ngx.say(body) + } + } +--- error_code: 201 +--- response_body +passed + + + +=== TEST 2: add consumer +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/consumers/1', + ngx.HTTP_PUT, + [[{ + "username": "1", + "plugins": { + "key-auth": { + "key": "auth-one" + } + }, + "group_id": "1" + }]] + ) + if code >= 300 then + ngx.status = code + ngx.print(message) + return + end + ngx.say(message) + } + } +--- response_body +passed + + + +=== TEST 3: delete consumer_group(wrong header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/consumer_groups/1?force=anyvalue', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this consumer group, consumer [1] is still using it now"} + + + +=== TEST 4: delete consumer_group(without force delete header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/consumer_groups/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this consumer group, consumer [1] is still using it now"} + + + +=== TEST 5: delete consumer_group(force delete) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/consumer_groups/1?force=true', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed + + + +=== TEST 6: delete consumer +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/consumers/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed diff --git a/t/admin/plugin-configs-force-delete.t b/t/admin/plugin-configs-force-delete.t new file mode 100644 index 000000000000..7d4f73739e34 --- /dev/null +++ b/t/admin/plugin-configs-force-delete.t @@ -0,0 +1,163 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +no_shuffle(); +log_level("info"); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if (!$block->no_error_log && !$block->error_log) { + $block->set_value("no_error_log", "[error]\n[alert]"); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: set plugin_configs(id: 1) +--- 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": { + "limit-count": { + "count": 2, + "time_window": 60, + "rejected_code": 503 + } + } + }]] + ) + ngx.status = code + ngx.say(body) + } + } +--- error_code: 201 +--- response_body +passed + + + +=== TEST 2: add route +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugin_config_id": 1, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + }, + "uri": "/index.html" + }]] + ) + if code >= 300 then + ngx.status = code + ngx.print(message) + return + end + ngx.say(message) + } + } +--- response_body +passed + + + +=== TEST 3: delete plugin_configs(wrong header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/plugin_configs/1?force=anyvalue', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this plugin config, route [1] is still using it now"} + + + +=== TEST 4: delete plugin_configs(without force delete header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/plugin_configs/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this plugin config, route [1] is still using it now"} + + + +=== TEST 5: delete plugin_configs(force delete) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/plugin_configs/1?force=true', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed + + + +=== TEST 6: delete route +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/routes/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed diff --git a/t/admin/protos-force-delete.t b/t/admin/protos-force-delete.t new file mode 100644 index 000000000000..909128924bfe --- /dev/null +++ b/t/admin/protos-force-delete.t @@ -0,0 +1,175 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +no_shuffle(); +log_level("info"); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if (!$block->no_error_log && !$block->error_log) { + $block->set_value("no_error_log", "[error]\n[alert]"); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: set proto(id: 1) +--- 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; + }" + }]] + ) + ngx.status = code + ngx.say(body) + } + } +--- error_code: 201 +--- response_body +passed + + + +=== TEST 2: add route +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "methods": ["GET"], + "uri": "/grpctest", + "plugins": { + "grpc-transcode": { + "proto_id": "1", + "service": "helloworld.Greeter", + "method": "SayHello" + } + }, + "upstream": { + "scheme": "grpc", + "type": "roundrobin", + "nodes": { + "127.0.0.1:50051": 1 + } + } + }]] + ) + if code >= 300 then + ngx.status = code + ngx.print(message) + return + end + ngx.say(message) + } + } +--- response_body +passed + + + +=== TEST 3: delete proto(wrong header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/protos/1?force=anyvalue', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this proto, route [1] is still using it now"} + + + +=== TEST 4: delete proto(without force delete header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/protos/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this proto, route [1] is still using it now"} + + + +=== TEST 5: delete proto(force delete) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/protos/1?force=true', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed + + + +=== TEST 6: delete route +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/routes/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed diff --git a/t/admin/services-force-delete.t b/t/admin/services-force-delete.t new file mode 100644 index 000000000000..439b44e098ab --- /dev/null +++ b/t/admin/services-force-delete.t @@ -0,0 +1,156 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +no_shuffle(); +log_level("info"); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if (!$block->no_error_log && !$block->error_log) { + $block->set_value("no_error_log", "[error]\n[alert]"); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: set service(id: 1) +--- 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, + [[{ + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" + } + }]] + ) + ngx.status = code + ngx.say(body) + } + } +--- error_code: 201 +--- response_body +passed + + + +=== TEST 2: add route +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "service_id": 1, + "uri": "/index.html" + }]] + ) + if code >= 300 then + ngx.status = code + ngx.print(message) + return + end + ngx.say(message) + } + } +--- response_body +passed + + + +=== TEST 3: delete service(wrong header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/services/1?force=anyvalue', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this service directly, route [1] is still using it now"} + + + +=== TEST 4: delete service(without force delete header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/services/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this service directly, route [1] is still using it now"} + + + +=== TEST 5: delete service(force delete) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/services/1?force=true', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed + + + +=== TEST 6: delete route +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/routes/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed diff --git a/t/admin/upstream-force-delete.t b/t/admin/upstream-force-delete.t new file mode 100644 index 000000000000..6d834b11469a --- /dev/null +++ b/t/admin/upstream-force-delete.t @@ -0,0 +1,154 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +no_shuffle(); +log_level("info"); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if (!$block->no_error_log && !$block->error_log) { + $block->set_value("no_error_log", "[error]\n[alert]"); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: 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, + [[{ + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" + }]] + ) + ngx.status = code + ngx.say(body) + } + } +--- error_code: 201 +--- response_body +passed + + + +=== TEST 2: add route +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "upstream_id": 1, + "uri": "/index.html" + }]] + ) + if code >= 300 then + ngx.status = code + ngx.print(message) + return + end + ngx.say(message) + } + } +--- response_body +passed + + + +=== TEST 3: delete upstream(wrong header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/upstreams/1?force=anyvalue', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this upstream, route [1] is still using it now"} + + + +=== TEST 4: delete upstream(without force delete header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/upstreams/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this upstream, route [1] is still using it now"} + + + +=== TEST 5: delete upstream(force delete) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/upstreams/1?force=true', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed + + + +=== TEST 6: delete route +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/routes/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed