diff --git a/apisix/core.lua b/apisix/core.lua index 0ef6ae9bb259..4b5bc2b99b9d 100644 --- a/apisix/core.lua +++ b/apisix/core.lua @@ -49,5 +49,6 @@ return { etcd = require("apisix.core.etcd"), tablepool = require("tablepool"), resolver = require("apisix.core.resolver"), + os = require("apisix.core.os"), empty_tab = {}, } diff --git a/apisix/core/os.lua b/apisix/core/os.lua new file mode 100644 index 000000000000..b8476ecad5cf --- /dev/null +++ b/apisix/core/os.lua @@ -0,0 +1,55 @@ +-- +-- 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. +-- +local ffi = require("ffi") +local ffi_str = ffi.string +local ffi_errno = ffi.errno +local C = ffi.C +local tostring = tostring +local type = type + + +local _M = {} + + +ffi.cdef[[ + int setenv(const char *name, const char *value, int overwrite); + char *strerror(int errnum); +]] + + +local function err() + return ffi_str(C.strerror(ffi_errno())) +end + + +-- setenv sets the value of the environment variable +function _M.setenv(name, value) + local tv = type(value) + if type(name) ~= "string" or (tv ~= "string" and tv ~= "number") then + return false, "invalid argument" + end + + value = tostring(value) + local ok = C.setenv(name, value, 1) == 0 + if not ok then + return false, err() + end + return true +end + + +return _M diff --git a/apisix/plugins/ext-plugin/init.lua b/apisix/plugins/ext-plugin/init.lua index 9f83bf4e4f3c..c227be0a9444 100644 --- a/apisix/plugins/ext-plugin/init.lua +++ b/apisix/plugins/ext-plugin/init.lua @@ -547,13 +547,20 @@ local function create_lrucache() end +local function must_set(env, value) + local ok, err = core.os.setenv(env, value) + if not ok then + error(str_format("failed to set %s: %s", env, err), 2) + end +end + + local function spawn_proc(cmd) + must_set("APISIX_CONF_EXPIRE_TIME", helper.get_conf_token_cache_time()) + must_set("APISIX_LISTEN_ADDRESS", helper.get_path()) + local opt = { merge_stderr = true, - environ = { - "APISIX_CONF_EXPIRE_TIME=" .. helper.get_conf_token_cache_time(), - "APISIX_LISTEN_ADDRESS=" .. helper.get_path(), - }, } local proc, err = ngx_pipe.spawn(cmd, opt) if not proc then diff --git a/docs/en/latest/external-plugin.md b/docs/en/latest/external-plugin.md index 0eea264c3032..318e38e80a3c 100644 --- a/docs/en/latest/external-plugin.md +++ b/docs/en/latest/external-plugin.md @@ -63,8 +63,7 @@ ext-plugin: Then APISIX will manage the runner as its subprocess. -Note: APISIX can't manage the runner on the Mac. It is fine, we can run the runner by ourselves -during development. +Note: APISIX can't manage the runner on the Mac in `v2.6`. During development, we want to run the runner separately so that we can restart it without restarting APISIX first. @@ -89,3 +88,18 @@ ext-plugin: In the prod environment, `path_for_test` should not be used and the unix socket path will be generated dynamically. + +## FAQ + +### When managing by APISIX, the runner can't access my environment variable + +Since `v2.7`, APISIX can pass environment to the runner. + +However, Nginx will hide all environment variables by default. So you need to +declare your variable first in the `conf/config.yaml`: + +```yaml +nginx_config: + envs: + - MY_ENV_VAR +``` diff --git a/t/core/os.t b/t/core/os.t new file mode 100644 index 000000000000..dff6c8b3c191 --- /dev/null +++ b/t/core/os.t @@ -0,0 +1,72 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->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: setenv +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + + core.os.setenv("TEST", "A") + ngx.say(os.getenv("TEST")) + core.os.setenv("TEST", 1) + ngx.say(os.getenv("TEST")) + } + } +--- response_body +A +1 + + + +=== TEST 2: setenv, bad arguments +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + + for _, c in ipairs({ + {name = "A"}, + {value = "A"}, + {name = 1, value = "A"}, + }) do + local ok = core.os.setenv(c.name, c.value) + ngx.say(ok) + end + } + } +--- response_body +false +false +false diff --git a/t/plugin/ext-plugin/runner.sh b/t/plugin/ext-plugin/runner.sh index 5b8785288f3a..056aa4fb0520 100755 --- a/t/plugin/ext-plugin/runner.sh +++ b/t/plugin/ext-plugin/runner.sh @@ -18,5 +18,6 @@ echo "LISTEN $APISIX_LISTEN_ADDRESS" echo "EXPIRE $APISIX_CONF_EXPIRE_TIME" +echo "MY_ENV_VAR $MY_ENV_VAR" sleep "$1" exit 111 diff --git a/t/plugin/ext-plugin/sanity.t b/t/plugin/ext-plugin/sanity.t index 905215e93221..ac21eeb81b31 100644 --- a/t/plugin/ext-plugin/sanity.t +++ b/t/plugin/ext-plugin/sanity.t @@ -337,3 +337,17 @@ GET /hello --- error_code: 503 --- error_log failed to receive RPC_PREPARE_CONF: bad request + + + +=== TEST 12: runner can access the environment variable +--- main_config +env MY_ENV_VAR=foo; +--- ext_plugin_cmd +["t/plugin/ext-plugin/runner.sh", "3600"] +--- config + location /t { + return 200; + } +--- error_log +MY_ENV_VAR foo