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

wenming 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 85a84fb  fix: avoid unnecessary type check and support var in 
local_conf (#2990)
85a84fb is described below

commit 85a84fb191efd6a511aa2380cd15bb30362ae300
Author: 罗泽轩 <[email protected]>
AuthorDate: Thu Dec 10 15:48:47 2020 +0800

    fix: avoid unnecessary type check and support var in local_conf (#2990)
    
    Also merge bundle of repeated code.
    
    Signed-off-by: spacewander <[email protected]>
---
 .travis/apisix_cli_test.sh   | 114 ++++++++++++++++++++++++++++++++++++++++++
 apisix/cli/file.lua          |  99 +++++++++++++++++++++++++++++++------
 apisix/cli/ops.lua           |  25 ++++++++++
 apisix/core/config_local.lua | 115 +++----------------------------------------
 conf/config.yaml             |   2 +
 doc/how-to-build.md          |   3 ++
 t/core/config.t              |  42 ++++++++++++++++
 7 files changed, 277 insertions(+), 123 deletions(-)

diff --git a/.travis/apisix_cli_test.sh b/.travis/apisix_cli_test.sh
index 5590ecf..b793284 100755
--- a/.travis/apisix_cli_test.sh
+++ b/.travis/apisix_cli_test.sh
@@ -166,6 +166,12 @@ if ! grep "env TEST_bar;" conf/nginx.conf > /dev/null; then
     exit 1
 fi
 
+out=$(make init 2>&1 || true)
+if ! echo "$out" | grep "can't find environment variable"; then
+    echo "failed: failed to resolve variables"
+    exit 1
+fi
+
 echo "passed: resolve variables"
 
 echo '
@@ -211,6 +217,114 @@ fi
 
 echo "passed: resolve variables wrapped with whitespace"
 
+# support environment variables in local_conf
+echo '
+etcd:
+    host:
+        - "http://${{ETCD_HOST}}:${{ETCD_PORT}}";
+' > conf/config.yaml
+
+ETCD_HOST=127.0.0.1 ETCD_PORT=2379 make init
+
+if ! grep "env ETCD_HOST=127.0.0.1;" conf/nginx.conf > /dev/null; then
+    echo "failed: support environment variables in local_conf"
+    exit 1
+fi
+
+# don't override user's envs configuration
+echo '
+etcd:
+    host:
+        - "http://${{ETCD_HOST}}:${{ETCD_PORT}}";
+nginx_config:
+    envs:
+        - ETCD_HOST
+' > conf/config.yaml
+
+ETCD_HOST=127.0.0.1 ETCD_PORT=2379 make init
+
+if grep "env ETCD_HOST=127.0.0.1;" conf/nginx.conf > /dev/null; then
+    echo "failed: support environment variables in local_conf"
+    exit 1
+fi
+
+if ! grep "env ETCD_HOST;" conf/nginx.conf > /dev/null; then
+    echo "failed: support environment variables in local_conf"
+    exit 1
+fi
+
+echo '
+etcd:
+    host:
+        - "http://${{ETCD_HOST}}:${{ETCD_PORT}}";
+nginx_config:
+    envs:
+        - ETCD_HOST=1.1.1.1
+' > conf/config.yaml
+
+ETCD_HOST=127.0.0.1 ETCD_PORT=2379 make init
+
+if grep "env ETCD_HOST=127.0.0.1;" conf/nginx.conf > /dev/null; then
+    echo "failed: support environment variables in local_conf"
+    exit 1
+fi
+
+if ! grep "env ETCD_HOST=1.1.1.1;" conf/nginx.conf > /dev/null; then
+    echo "failed: support environment variables in local_conf"
+    exit 1
+fi
+
+echo "pass: support environment variables in local_conf"
+
+# support merging worker_processes
+echo '
+nginx_config:
+    worker_processes: 1
+' > conf/config.yaml
+
+make init
+
+if ! grep "worker_processes 1;" conf/nginx.conf > /dev/null; then
+    echo "failed: failed to merge worker_processes"
+    exit 1
+fi
+
+echo '
+nginx_config:
+    worker_processes: ${{nproc}}
+' > conf/config.yaml
+
+nproc=1 make init
+
+if ! grep "worker_processes 1;" conf/nginx.conf > /dev/null; then
+    echo "failed: failed to merge worker_processes"
+    exit 1
+fi
+
+echo '
+nginx_config:
+    worker_processes: true
+' > conf/config.yaml
+
+out=$(make init 2>&1 || true)
+if ! echo "$out" | grep 'path\[nginx_config->worker_processes\] expect'; then
+    echo "failed: failed to merge worker_processes"
+    exit 1
+fi
+
+echo '
+nginx_config:
+    worker_processes: ${{nproc}}
+' > conf/config.yaml
+
+out=$(nproc=false make init 2>&1 || true)
+if ! echo "$out" | grep 'path\[nginx_config->worker_processes\] expect'; then
+    echo "failed: failed to merge worker_processes"
+    exit 1
+fi
+
+echo "passed: merge worker_processes"
+
 # check nameserver imported
 git checkout conf/config.yaml
 
diff --git a/apisix/cli/file.lua b/apisix/cli/file.lua
index 0bc604d..892e335 100644
--- a/apisix/cli/file.lua
+++ b/apisix/cli/file.lua
@@ -22,11 +22,19 @@ local util = require("apisix.cli.util")
 local pairs = pairs
 local type = type
 local tonumber = tonumber
+local getmetatable = getmetatable
 local getenv = os.getenv
 local str_gmatch = string.gmatch
 local str_find = string.find
+local str_sub = string.sub
 
 local _M = {}
+local exported_vars
+
+
+function _M.get_exported_vars()
+    return exported_vars
+end
 
 
 local function is_empty_yaml_line(line)
@@ -47,24 +55,37 @@ end
 local function resolve_conf_var(conf)
     for key, val in pairs(conf) do
         if type(val) == "table" then
-            resolve_conf_var(val)
+            local ok, err = resolve_conf_var(val)
+            if not ok then
+                return nil, err
+            end
 
         elseif type(val) == "string" then
+            local err
             local var_used = false
             -- we use '${{var}}' because '$var' and '${var}' are taken
             -- by Nginx
             local new_val = val:gsub("%$%{%{%s*([%w_]+)%s*%}%}", function(var)
                 local v = getenv(var)
                 if v then
+                    if not exported_vars then
+                        exported_vars = {}
+                    end
+
+                    exported_vars[var] = v
                     var_used = true
                     return v
                 end
 
-                util.die("failed to handle configuration: ",
-                         "can't find environment variable ",
-                         var, "\n")
+                err = "failed to handle configuration: " ..
+                      "can't find environment variable " .. var
+                return ""
             end)
 
+            if err then
+                return nil, err
+            end
+
             if var_used then
                 if tonumber(new_val) ~= nil then
                     new_val = tonumber(new_val)
@@ -78,22 +99,62 @@ local function resolve_conf_var(conf)
             conf[key] = new_val
         end
     end
+
+    return true
+end
+
+
+local function tinyyaml_type(t)
+    local mt = getmetatable(t)
+    if mt then
+        return mt.__type
+    end
 end
 
 
-local function merge_conf(base, new_tab)
+local function merge_conf(base, new_tab, ppath)
+    ppath = ppath or ""
+
     for key, val in pairs(new_tab) do
         if type(val) == "table" then
-            if tab_is_array(val) then
-                base[key] = val
-            elseif base[key] == nil then
+            if tinyyaml_type(val) == "null" then
+                base[key] = nil
+
+            elseif tab_is_array(val) then
                 base[key] = val
+
             else
-                merge_conf(base[key], val)
-            end
+                if base[key] == nil then
+                    base[key] = {}
+                end
 
+                local ok, err = merge_conf(
+                    base[key],
+                    val,
+                    ppath == "" and key or ppath .. "->" .. key
+                )
+                if not ok then
+                    return nil, err
+                end
+            end
         else
-            base[key] = val
+            local type_val = type(val)
+
+            if base[key] == nil then
+                base[key] = val
+            elseif type(base[key]) ~= type_val then
+                if (ppath == "nginx_config" or str_sub(ppath, 1, 14) == 
"nginx_config->") and
+                    (type_val == "number" or type_val == "string")
+                then
+                    base[key] = val
+                else
+                    local path = ppath == "" and key or ppath .. "->" .. key
+                    return nil, "failed to merge, path[" .. path ..  "] 
expect: " ..
+                                type(base[key]) .. ", but got: " .. type_val
+                end
+            else
+                base[key] = val
+            end
         end
     end
 
@@ -102,7 +163,10 @@ end
 
 
 function _M.read_yaml_conf(apisix_home)
-    profile.apisix_home = apisix_home .. "/"
+    if apisix_home then
+        profile.apisix_home = apisix_home .. "/"
+    end
+
     local local_conf_path = profile:yaml_path("config-default")
     local default_conf_yaml, err = util.read_file(local_conf_path)
     if not default_conf_yaml then
@@ -134,8 +198,15 @@ function _M.read_yaml_conf(apisix_home)
             return nil, "invalid config.yaml file"
         end
 
-        resolve_conf_var(user_conf)
-        merge_conf(default_conf, user_conf)
+        local ok, err = resolve_conf_var(user_conf)
+        if not ok then
+            return nil, err
+        end
+
+        ok, err = merge_conf(default_conf, user_conf)
+        if not ok then
+            return nil, err
+        end
     end
 
     return default_conf
diff --git a/apisix/cli/ops.lua b/apisix/cli/ops.lua
index 046df7f..f2523a5 100644
--- a/apisix/cli/ops.lua
+++ b/apisix/cli/ops.lua
@@ -38,6 +38,7 @@ local floor = math.floor
 local str_find = string.find
 local str_sub = string.sub
 
+
 local _M = {}
 
 
@@ -300,6 +301,30 @@ Please modify "admin_key" in conf/config.yaml .
         sys_conf["worker_processes"] = floor(tonumber(env_worker_processes))
     end
 
+    local exported_vars = file.get_exported_vars()
+    if exported_vars then
+        if not sys_conf["envs"] then
+            sys_conf["envs"]= {}
+        end
+        for _, cfg_env in ipairs(sys_conf["envs"]) do
+            local cfg_name
+            local from = str_find(cfg_env, "=", 1, true)
+            if from then
+                cfg_name = str_sub(cfg_env, 1, from - 1)
+            else
+                cfg_name = cfg_env
+            end
+
+            exported_vars[cfg_name] = false
+        end
+
+        for name, value in pairs(exported_vars) do
+            if value then
+                table_insert(sys_conf["envs"], name .. "=" .. value)
+            end
+        end
+    end
+
     local conf_render = template.compile(ngx_tpl)
     local ngxconf = conf_render(sys_conf)
 
diff --git a/apisix/core/config_local.lua b/apisix/core/config_local.lua
index aa9252f..d17255b 100644
--- a/apisix/core/config_local.lua
+++ b/apisix/core/config_local.lua
@@ -15,134 +15,31 @@
 -- limitations under the License.
 --
 
-local log = require("apisix.core.log")
-local profile = require("apisix.core.profile")
-local table = require("apisix.core.table")
-local yaml = require("tinyyaml")
-
-local io_open = io.open
-local type = type
-local str_gmatch = string.gmatch
-local string = string
-local pairs = pairs
-local getmetatable = getmetatable
-
-
-local local_default_conf_path = profile:yaml_path("config-default")
-local local_conf_path = profile:yaml_path("config")
-local config_data
+local file = require("apisix.cli.file")
 
 
 local _M = {}
 
 
-local function read_file(path)
-    local file, err = io_open(path, "rb")   -- read as binary mode
-    if not file then
-        log.error("failed to read config file:" .. path, ", error info:", err)
-        return nil, err
-    end
+local config_data
 
-    local content = file:read("*a") -- `*a` reads the whole file
-    file:close()
-    return content
-end
 
 function _M.clear_cache()
     config_data = nil
 end
 
 
-local function is_empty_yaml_line(line)
-    return line == '' or string.find(line, '^%s*$') or
-           string.find(line, '^%s*#')
-end
-
-
-local function tinyyaml_type(t)
-    local mt = getmetatable(t)
-    if mt then
-        log.debug("table type: ", mt.__type)
-        return mt.__type
-    end
-end
-
-
-local function merge_conf(base, new_tab, ppath)
-    ppath = ppath or ""
-
-    for key, val in pairs(new_tab) do
-        if type(val) == "table" then
-            if tinyyaml_type(val) == "null" then
-                base[key] = nil
-
-            elseif table.isarray(val) then
-                base[key] = val
-
-            else
-                if base[key] == nil then
-                    base[key] = {}
-                end
-
-                local ok, err = merge_conf(
-                    base[key],
-                    val,
-                    ppath == "" and key or ppath .. "->" .. key
-                )
-                if not ok then
-                    return nil, err
-                end
-            end
-        else
-            if base[key] == nil then
-                base[key] = val
-            elseif type(base[key]) ~= type(val) then
-                return false, "failed to merge, path[" ..
-                              (ppath == "" and key or ppath .. "->" .. key) ..
-                              "] expect: " ..
-                              type(base[key]) .. ", but got: " .. type(val)
-            else
-                base[key] = val
-            end
-        end
-    end
-
-    return base
-end
-
-
 function _M.local_conf(force)
     if not force and config_data then
         return config_data
     end
 
-    local default_conf_yaml, err = read_file(local_default_conf_path)
-    if type(default_conf_yaml) ~= "string" then
-        return nil, "failed to read config-default file:" .. err
-    end
-    config_data = yaml.parse(default_conf_yaml)
-
-    local user_conf_yaml = read_file(local_conf_path) or ""
-    local is_empty_file = true
-    for line in str_gmatch(user_conf_yaml .. '\n', '(.-)\r?\n') do
-        if not is_empty_yaml_line(line) then
-            is_empty_file = false
-            break
-        end
-    end
-
-    if not is_empty_file then
-        local user_conf = yaml.parse(user_conf_yaml)
-        if not user_conf then
-            return nil, "invalid config.yaml file"
-        end
-
-        config_data, err = merge_conf(config_data, user_conf)
-        if err then
-            return nil, err
-        end
+    local default_conf, err = file.read_yaml_conf()
+    if not default_conf then
+        return nil, err
     end
 
+    config_data = default_conf
     return config_data
 end
 
diff --git a/conf/config.yaml b/conf/config.yaml
index 335998b..3249e66 100644
--- a/conf/config.yaml
+++ b/conf/config.yaml
@@ -27,6 +27,8 @@
 #     host:
 #       - "http://${{ETCD_HOST}}:2379";
 #
+# And then run `export ETCD_HOST=$your_host` before `make init`.
+#
 # If the configured environment variable can't be found, an error will be 
thrown.
 apisix:
   admin_key:
diff --git a/doc/how-to-build.md b/doc/how-to-build.md
index e30fa3f..013d764 100644
--- a/doc/how-to-build.md
+++ b/doc/how-to-build.md
@@ -106,6 +106,9 @@ Makefile rules:
     license-check:    Check Lua source code for Apache License
 ```
 
+Environment variable can be used to configure APISIX. Please take a look at 
`conf/config.yaml` to
+see how to do it.
+
 ## 4. Test
 
 1. Install perl's package manager `cpanminus` first
diff --git a/t/core/config.t b/t/core/config.t
index 115f4da..0e1bc74 100644
--- a/t/core/config.t
+++ b/t/core/config.t
@@ -300,3 +300,45 @@ GET /t
 etcd host: http://127.0.0.1:2379
 first plugin: "example-plugin"
 seq: {"Block 
style":["Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune","Pluto"],"Flow
 
style":["Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune","Pluto"]}
+
+
+
+=== TEST 3: allow environment variable
+--- config
+    location /t {
+        content_by_lua_block {
+            local config = require("apisix.core").config.local_conf()
+
+            ngx.say(config.apisix.id)
+        }
+    }
+--- main_config
+env AID=3;
+--- yaml_config
+#nginx_config:
+    #env: AID=3
+apisix:
+    id: ${{ AID }}
+--- request
+GET /t
+--- response_body
+3
+
+
+
+=== TEST 4: allow integer worker processes
+--- config
+    location /t {
+        content_by_lua_block {
+            local config = require("apisix.core").config.local_conf()
+
+            ngx.say(config.nginx_config.worker_processes)
+        }
+    }
+--- extra_yaml_config
+nginx_config:
+    worker_processes: 1 
+--- request
+GET /t
+--- response_body
+1

Reply via email to