You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by su...@apache.org on 2025/06/10 08:45:55 UTC

(apisix) branch master updated: feat: add warning for data plane writing to etcd (#12241)

This is an automated email from the ASF dual-hosted git repository.

sunyi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git


The following commit(s) were added to refs/heads/master by this push:
     new 9c3f5adf1 feat: add warning for data plane writing to etcd (#12241)
9c3f5adf1 is described below

commit 9c3f5adf156b70c86aadd8e1d9237ae3f9b2d816
Author: litesun <su...@apache.org>
AuthorDate: Tue Jun 10 16:45:49 2025 +0800

    feat: add warning for data plane writing to etcd (#12241)
---
 apisix/core/etcd.lua |  111 +++++
 t/core/etcd-write.t  | 1107 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1218 insertions(+)

diff --git a/apisix/core/etcd.lua b/apisix/core/etcd.lua
index e3785091b..3caa2f1cc 100644
--- a/apisix/core/etcd.lua
+++ b/apisix/core/etcd.lua
@@ -22,6 +22,8 @@
 local require           = require
 local fetch_local_conf  = require("apisix.core.config_local").local_conf
 local array_mt          = require("apisix.core.json").array_mt
+local log               = require("apisix.core.log")
+local try_read_attr     = require("apisix.core.table").try_read_attr
 local v3_adapter        = require("apisix.admin.v3_adapter")
 local etcd              = require("resty.etcd")
 local clone_tab         = require("table.clone")
@@ -37,6 +39,81 @@ local ngx_get_phase     = ngx.get_phase
 local _M = {}
 
 
+local NOT_ALLOW_WRITE_ETCD_WARN = 'Data plane role should not write to etcd. ' ..
+    'This operation will be deprecated in future releases.'
+
+local function is_data_plane()
+    local local_conf, err = fetch_local_conf()
+    if not local_conf then
+        return nil, err
+    end
+
+    local role = try_read_attr(local_conf, "deployment", "role")
+    if role == "data_plane" then
+      return true
+    end
+
+    return false
+end
+
+
+
+local function disable_write_if_data_plane()
+    local data_plane, err = is_data_plane()
+    if err then
+        log.error("failed to check data plane role: ", err)
+        return true, err
+    end
+
+    if data_plane then
+        -- current only warn, will be return false in future releases
+        -- to block etcd write
+        log.warn(NOT_ALLOW_WRITE_ETCD_WARN)
+        return false
+    end
+
+    return false, nil
+end
+
+
+local function wrap_etcd_client(etcd_cli)
+    -- note: methods txn can read and write, don't use txn to write when data plane role
+    local methods_to_wrap = {
+        "set",
+        "setnx",
+        "setx",
+        "delete",
+        "rmdir",
+        "grant",
+        "revoke",
+        "keepalive"
+    }
+
+    local original_methods = {}
+    for _, method in ipairs(methods_to_wrap) do
+        if not etcd_cli[method] then
+            log.error("method ", method, " not found in etcd client")
+            return nil, "method " .. method .. " not found in etcd client"
+        end
+
+        original_methods[method] = etcd_cli[method]
+    end
+
+    for _, method in ipairs(methods_to_wrap) do
+        etcd_cli[method] = function(self, ...)
+            local disable, err = disable_write_if_data_plane()
+            if disable then
+                return nil, err
+            end
+
+            return original_methods[method](self, ...)
+        end
+    end
+
+    return etcd_cli
+end
+
+
 local function _new(etcd_conf)
     local prefix = etcd_conf.prefix
     etcd_conf.http_host = etcd_conf.host
@@ -67,6 +144,8 @@ local function _new(etcd_conf)
         return nil, nil, err
     end
 
+    etcd_cli = wrap_etcd_client(etcd_cli)
+
     return etcd_cli, prefix
 end
 
@@ -337,6 +416,12 @@ end
 
 
 local function set(key, value, ttl)
+    local disable, err = disable_write_if_data_plane()
+    if disable then
+        return nil, err
+    end
+
+
     local etcd_cli, prefix, err = get_etcd_cli()
     if not etcd_cli then
         return nil, err
@@ -386,6 +471,11 @@ _M.set = set
 
 
 function _M.atomic_set(key, value, ttl, mod_revision)
+    local disable, err = disable_write_if_data_plane()
+    if disable then
+        return nil, err
+    end
+
     local etcd_cli, prefix, err = get_etcd_cli()
     if not etcd_cli then
         return nil, err
@@ -443,7 +533,13 @@ function _M.atomic_set(key, value, ttl, mod_revision)
 end
 
 
+
 function _M.push(key, value, ttl)
+    local disable, err = disable_write_if_data_plane()
+    if disable then
+        return nil, err
+    end
+
     local etcd_cli, _, err = get_etcd_cli()
     if not etcd_cli then
         return nil, err
@@ -476,6 +572,11 @@ end
 
 
 function _M.delete(key)
+    local disable, err = disable_write_if_data_plane()
+    if disable then
+        return nil, err
+    end
+
     local etcd_cli, prefix, err = get_etcd_cli()
     if not etcd_cli then
         return nil, err
@@ -502,6 +603,11 @@ function _M.delete(key)
 end
 
 function _M.rmdir(key, opts)
+    local disable, err = disable_write_if_data_plane()
+    if disable then
+        return nil, err
+    end
+
     local etcd_cli, prefix, err = get_etcd_cli()
     if not etcd_cli then
         return nil, err
@@ -548,6 +654,11 @@ end
 
 
 function _M.keepalive(id)
+    local disable, err = disable_write_if_data_plane()
+    if disable then
+        return nil, err
+    end
+
     local etcd_cli, _, err = get_etcd_cli()
     if not etcd_cli then
         return nil, err
diff --git a/t/core/etcd-write.t b/t/core/etcd-write.t
new file mode 100644
index 000000000..457cb76ed
--- /dev/null
+++ b/t/core/etcd-write.t
@@ -0,0 +1,1107 @@
+#
+# 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();
+log_level("info");
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: add serverless-pre-function with etcd delete
+--- 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": {
+                        "serverless-pre-function": {
+                            "phase": "rewrite",
+                            "functions" : [
+                                "return function() local etcd_cli = require(\"apisix.core.etcd\").new() etcd_cli:delete(\"/test-key\") end"
+                            ]
+                        }
+                    },
+                    "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
+
+
+
+=== TEST 2: should show warn when serverless-pre-function try to write to etcd with cli delete
+--- yaml_config
+deployment:
+  role: data_plane
+  role_data_plane:
+    config_provider: etcd
+  etcd:
+    host:
+      - "http://127.0.0.1:2379"
+    prefix: "/apisix"
+    tls:
+      verify: false
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/hello')
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- error_log eval
+qr/Data plane role should not write to etcd. This operation will be deprecated in future releases./
+
+
+
+=== TEST 3: add serverless-pre-function with etcd cli grant
+--- 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": {
+                        "serverless-pre-function": {
+                            "phase": "rewrite",
+                            "functions" : [
+                                "return function() local etcd_cli = require(\"apisix.core.etcd\").new() etcd_cli:grant(10) end"
+                            ]
+                        }
+                    },
+                    "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
+
+
+
+=== TEST 4: should show warn when serverless-pre-function try to write to etcd with cli grant
+--- yaml_config
+deployment:
+  role: data_plane
+  role_data_plane:
+    config_provider: etcd
+  etcd:
+    host:
+      - "http://127.0.0.1:2379"
+    prefix: "/apisix"
+    tls:
+      verify: false
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/hello')
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- error_log eval
+qr/Data plane role should not write to etcd. This operation will be deprecated in future releases./
+
+
+
+=== TEST 5: add serverless-pre-function with etcd cli setnx
+--- 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": {
+                        "serverless-pre-function": {
+                            "phase": "rewrite",
+                            "functions" : [
+                                "return function() local etcd_cli = require(\"apisix.core.etcd\").new() etcd_cli:setnx(\"/test-key\", {value = \"hello from serverless\"}) end"
+                            ]
+                        }
+                    },
+                    "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
+
+
+
+=== TEST 6: should show warn when serverless-pre-function try to write to etcd with cli setnx
+--- yaml_config
+deployment:
+  role: data_plane
+  role_data_plane:
+    config_provider: etcd
+  etcd:
+    host:
+      - "http://127.0.0.1:2379"
+    prefix: "/apisix"
+    tls:
+      verify: false
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/hello')
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- error_log eval
+qr/Data plane role should not write to etcd. This operation will be deprecated in future releases./
+
+
+
+=== TEST 7: add serverless-pre-function with etcd cli 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": {
+                        "serverless-pre-function": {
+                            "phase": "rewrite",
+                            "functions" : [
+                                "return function() local etcd_cli = require(\"apisix.core.etcd\").new() etcd_cli:set(\"/test-key\", {value = \"hello from serverless\"}) end"
+                            ]
+                        }
+                    },
+                    "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
+
+
+
+=== TEST 8: should show warn when serverless-pre-function try to write to etcd with cli set
+--- yaml_config
+deployment:
+  role: data_plane
+  role_data_plane:
+    config_provider: etcd
+  etcd:
+    host:
+      - "http://127.0.0.1:2379"
+    prefix: "/apisix"
+    tls:
+      verify: false
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/hello')
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- error_log eval
+qr/Data plane role should not write to etcd. This operation will be deprecated in future releases./
+
+
+
+=== TEST 9: add serverless-pre-function with etcd cli setx
+--- 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": {
+                        "serverless-pre-function": {
+                            "phase": "rewrite",
+                            "functions" : [
+                                "return function() local etcd_cli = require(\"apisix.core.etcd\").new() etcd_cli:setx(\"/test-key\", {value = \"hello from serverless\"}) end"
+                            ]
+                        }
+                    },
+                    "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
+
+
+
+=== TEST 10: should show warn when serverless-pre-function try to write to etcd with cli setx
+--- yaml_config
+deployment:
+  role: data_plane
+  role_data_plane:
+    config_provider: etcd
+  etcd:
+    host:
+      - "http://127.0.0.1:2379"
+    prefix: "/apisix"
+    tls:
+      verify: false
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/hello')
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- error_log eval
+qr/Data plane role should not write to etcd. This operation will be deprecated in future releases./
+
+
+
+=== TEST 11: add serverless-pre-function with etcd cli rmdir
+--- 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": {
+                        "serverless-pre-function": {
+                            "phase": "rewrite",
+                            "functions" : [
+                                "return function() local etcd_cli = require(\"apisix.core.etcd\").new() etcd_cli:rmdir(\"/test-key\") end"
+                            ]
+                        }
+                    },
+                    "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
+
+
+
+=== TEST 12: should show warn when serverless-pre-function try to write to etcd with cli rmdir
+--- yaml_config
+deployment:
+  role: data_plane
+  role_data_plane:
+    config_provider: etcd
+  etcd:
+    host:
+      - "http://127.0.0.1:2379"
+    prefix: "/apisix"
+    tls:
+      verify: false
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/hello')
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- error_log eval
+qr/Data plane role should not write to etcd. This operation will be deprecated in future releases./
+
+
+
+=== TEST 13: add serverless-pre-function with etcd cli revoke
+--- 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": {
+                        "serverless-pre-function": {
+                            "phase": "rewrite",
+                            "functions" : [
+                                "return function() local etcd_cli = require(\"apisix.core.etcd\").new() etcd_cli:revoke(123) end"
+                            ]
+                        }
+                    },
+                    "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
+
+
+
+=== TEST 14: should show warn when serverless-pre-function try to write to etcd with cli revoke
+--- yaml_config
+deployment:
+  role: data_plane
+  role_data_plane:
+    config_provider: etcd
+  etcd:
+    host:
+      - "http://127.0.0.1:2379"
+    prefix: "/apisix"
+    tls:
+      verify: false
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/hello')
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- error_log eval
+qr/Data plane role should not write to etcd. This operation will be deprecated in future releases./
+
+
+
+=== TEST 15: add serverless-pre-function with etcd cli keepalive
+--- 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": {
+                        "serverless-pre-function": {
+                            "phase": "rewrite",
+                            "functions" : [
+                                "return function() local etcd_cli = require(\"apisix.core.etcd\").new() etcd_cli:keepalive(123) end"
+                            ]
+                        }
+                    },
+                    "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
+
+
+
+=== TEST 16: should show warn when serverless-pre-function try to write to etcd with cli keepalive
+--- yaml_config
+deployment:
+  role: data_plane
+  role_data_plane:
+    config_provider: etcd
+  etcd:
+    host:
+      - "http://127.0.0.1:2379"
+    prefix: "/apisix"
+    tls:
+      verify: false
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/hello')
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- error_log eval
+qr/Data plane role should not write to etcd. This operation will be deprecated in future releases./
+
+
+
+=== TEST 17: add serverless-pre-function with etcd cli get
+--- 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": {
+                        "serverless-pre-function": {
+                            "phase": "rewrite",
+                            "functions" : [
+                                "return function() local etcd_cli = require(\"apisix.core.etcd\").new() etcd_cli:get(\"/my-test-key\") end"
+                            ]
+                        }
+                    },
+                    "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: should not show warn when serverless-pre-function try to read from etcd with cli get
+--- yaml_config
+deployment:
+  role: data_plane
+  role_data_plane:
+    config_provider: etcd
+  etcd:
+    host:
+      - "http://127.0.0.1:2379"
+    prefix: "/apisix"
+    tls:
+      verify: false
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t("/hello")
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- no_error_log
+Data plane role should not write to etcd. This operation will be deprecated in future releases.
+
+
+
+=== TEST 19: add serverless-pre-function with etcd function 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": {
+                        "serverless-pre-function": {
+                            "phase": "rewrite",
+                            "functions" : [
+                                "return function() local core = require(\"apisix.core\") core.etcd.set(\"/my-test-key\", {value = \"hello from serverless\"}) end"
+                            ]
+                        }
+                    },
+                    "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 20: should show warn when serverless-pre-function try to write to etcd with function set
+--- yaml_config
+deployment:
+  role: data_plane
+  role_data_plane:
+    config_provider: etcd
+  etcd:
+    host:
+      - "http://127.0.0.1:2379"
+    prefix: "/apisix"
+    tls:
+      verify: false
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/hello')
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- error_log eval
+qr/Data plane role should not write to etcd. This operation will be deprecated in future releases./
+
+
+
+=== TEST 21: add serverless-pre-function with etcd function atomic_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": {
+                        "serverless-pre-function": {
+                            "phase": "rewrite",
+                            "functions" : [
+                                "return function() local core = require(\"apisix.core\") core.etcd.atomic_set(\"/my-test-key\", {value = \"hello from serverless\"}) end"
+                            ]
+                        }
+                    },
+                    "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 22: should show warn when serverless-pre-function try to write to etcd with function atomic_set
+--- yaml_config
+deployment:
+  role: data_plane
+  role_data_plane:
+    config_provider: etcd
+  etcd:
+    host:
+      - "http://127.0.0.1:2379"
+    prefix: "/apisix"
+    tls:
+      verify: false
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/hello')
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- error_log eval
+qr/Data plane role should not write to etcd. This operation will be deprecated in future releases./
+
+
+
+=== TEST 23: add serverless-pre-function with etcd function push
+--- 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": {
+                        "serverless-pre-function": {
+                            "phase": "rewrite",
+                            "functions" : [
+                                "return function() local core = require(\"apisix.core\") core.etcd.push(\"/my-test-key\", {value = \"hello from serverless\"}) end"
+                            ]
+                        }
+                    },
+                    "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 24: should show warn when serverless-pre-function try to write to etcd with function push
+--- yaml_config
+deployment:
+  role: data_plane
+  role_data_plane:
+    config_provider: etcd
+  etcd:
+    host:
+      - "http://127.0.0.1:2379"
+    prefix: "/apisix"
+    tls:
+      verify: false
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/hello')
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- error_log eval
+qr/Data plane role should not write to etcd. This operation will be deprecated in future releases./
+
+
+
+=== TEST 25: add serverless-pre-function with etcd function delete
+--- 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": {
+                        "serverless-pre-function": {
+                            "phase": "rewrite",
+                            "functions" : [
+                                "return function() local core = require(\"apisix.core\") core.etcd.delete(\"/my-test-key\") end"
+                            ]
+                        }
+                    },
+                    "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 26: should show warn when serverless-pre-function try to write to etcd with function delete
+--- yaml_config
+deployment:
+  role: data_plane
+  role_data_plane:
+    config_provider: etcd
+  etcd:
+    host:
+      - "http://127.0.0.1:2379"
+    prefix: "/apisix"
+    tls:
+      verify: false
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/hello')
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- error_log eval
+qr/Data plane role should not write to etcd. This operation will be deprecated in future releases./
+
+
+
+=== TEST 27: add serverless-pre-function with etcd function rmdir
+--- 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": {
+                        "serverless-pre-function": {
+                            "phase": "rewrite",
+                            "functions" : [
+                                "return function() local core = require(\"apisix.core\") core.etcd.rmdir(\"/my-test-key\") end"
+                            ]
+                        }
+                    },
+                    "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 28: should show warn when serverless-pre-function try to write to etcd with function rmdir
+--- yaml_config
+deployment:
+  role: data_plane
+  role_data_plane:
+    config_provider: etcd
+  etcd:
+    host:
+      - "http://127.0.0.1:2379"
+    prefix: "/apisix"
+    tls:
+      verify: false
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/hello')
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- error_log eval
+qr/Data plane role should not write to etcd. This operation will be deprecated in future releases./
+
+
+
+=== TEST 29: add serverless-pre-function with etcd function keepalive
+--- 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": {
+                        "serverless-pre-function": {
+                            "phase": "rewrite",
+                            "functions" : [
+                                "return function() local core = require(\"apisix.core\") core.etcd.keepalive(123) end"
+                            ]
+                        }
+                    },
+                    "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 30: should show warn when serverless-pre-function try to write to etcd with function keepalive
+--- yaml_config
+deployment:
+  role: data_plane
+  role_data_plane:
+    config_provider: etcd
+  etcd:
+    host:
+      - "http://127.0.0.1:2379"
+    prefix: "/apisix"
+    tls:
+      verify: false
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/hello')
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- error_log eval
+qr/Data plane role should not write to etcd. This operation will be deprecated in future releases./
+
+
+
+=== TEST 31: add serverless-pre-function with etcd function get
+--- 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": {
+                        "serverless-pre-function": {
+                            "phase": "rewrite",
+                            "functions" : [
+                                "return function() local core = require(\"apisix.core\") core.etcd.get(\"/my-test-key\") end"
+                            ]
+                        }
+                    },
+                    "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 32: should not show warn when serverless-pre-function try to read from etcd with function get
+--- yaml_config
+deployment:
+  role: data_plane
+  role_data_plane:
+    config_provider: etcd
+  etcd:
+    host:
+      - "http://127.0.0.1:2379"
+    prefix: "/apisix"
+    tls:
+      verify: false
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t("/hello")
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- no_error_log
+Data plane role should not write to etcd. This operation will be deprecated in future releases.
+
+
+
+=== TEST 33: should not warn when not data_plane
+--- yaml_config
+deployment:
+  role: control_plane
+  role_control_plane:
+    config_provider: etcd
+  etcd:
+    host:
+      - "http://127.0.0.1:2379"
+    prefix: "/apisix"
+    tls:
+      verify: false
+--- config
+    location /t {
+        content_by_lua_block {
+            local etcd = require("apisix.core.etcd")
+            etcd.set("foo", "bar")
+            etcd.delete("foo")
+        }
+    }
+--- request
+GET /t
+--- no_error_log
+Data plane role should not write to etcd. This operation will be deprecated in future releases.