You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by me...@apache.org on 2020/08/18 12:19:06 UTC

[apisix] branch master updated: feature: divide config.yaml into two files config-custom.yaml & confi… (#2023)

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

membphis 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 cf882bd  feature: divide config.yaml into two files config-custom.yaml & confi… (#2023)
cf882bd is described below

commit cf882bda1e36abf00f57cbaeb9a77ddaa7edf8a7
Author: YuanSheng Wang <me...@gmail.com>
AuthorDate: Tue Aug 18 20:18:55 2020 +0800

    feature: divide config.yaml into two files config-custom.yaml & confi… (#2023)
    
    * feature: divide config.yaml into two files config-custom.yaml & config-default.yaml .
    
    fix #1923
---
 .travis/apisix_cli_test.sh                     |  46 +++++--
 .travis/linux_apisix_master_luarocks_runner.sh |   6 +-
 .travis/linux_openresty_mtls_runner.sh         |  17 ++-
 Makefile                                       |   1 +
 apisix/core.lua                                |   5 +-
 apisix/core/config_local.lua                   | 108 ++++++++++++++-
 bin/apisix                                     |  68 +++++++++-
 conf/{config.yaml => config-default.yaml}      |   5 +
 conf/config.yaml                               | 175 +------------------------
 doc/architecture-design.md                     |  26 ++--
 doc/plugin-develop.md                          |   4 +-
 doc/zh-cn/architecture-design.md               |  25 ++--
 doc/zh-cn/plugin-develop.md                    |   4 +-
 t/APISIX.pm                                    |  27 ++--
 t/admin/stream-routes-disable.t                |  23 +---
 t/admin/token.t                                |  20 +--
 t/config-center-yaml/route-service.t           |  19 +--
 t/config-center-yaml/route-upstream.t          |  19 +--
 t/config-center-yaml/route.t                   |  19 +--
 t/core/config-default.t                        |  93 +++++++++++++
 t/core/config.t                                |   7 +-
 t/core/profile.t                               |  14 +-
 t/debug/debug-mode.t                           |  17 +--
 t/discovery/eureka.t                           |  43 +++---
 t/node/rr-balance.t                            |   9 --
 t/node/upstream-node-dns.t                     |  23 +---
 t/plugin/example.t                             |   3 +-
 t/router/radixtree-host-uri.t                  |  20 +--
 t/router/radixtree-host-uri2.t                 |  24 ++--
 t/router/radixtree-uri-host.t                  |  24 ----
 t/router/radixtree-uri-keep-end-slash.t        |  20 +--
 t/router/radixtree-uri-multiple.t              |  22 ----
 t/router/radixtree-uri-priority.t              |  19 ---
 t/router/radixtree-uri-sanity.t                |  28 ----
 34 files changed, 460 insertions(+), 523 deletions(-)

diff --git a/.travis/apisix_cli_test.sh b/.travis/apisix_cli_test.sh
index 020a90a..bfa3ce3 100755
--- a/.travis/apisix_cli_test.sh
+++ b/.travis/apisix_cli_test.sh
@@ -47,7 +47,11 @@ fi
 echo "passed: nginx.conf file contains reuseport configuration"
 
 # check default ssl port
-sed  -i 's/listen_port: 9443/listen_port: 8443/g'  conf/config.yaml
+echo "
+apisix:
+    ssl:
+        listen_port: 8443
+" > conf/config.yaml
 
 make init
 
@@ -66,8 +70,7 @@ fi
 echo "passed: change default ssl port"
 
 # check nameserver imported
-
-sed -i '/dns_resolver:/,+4s/^/#/'  conf/config.yaml
+git checkout conf/config.yaml
 
 make init
 
@@ -82,11 +85,15 @@ do
   fi
 done
 
-sed -i '/dns_resolver:/,+4s/^#//'  conf/config.yaml
 echo "passed: system nameserver imported"
 
 # enable enable_dev_mode
-sed  -i 's/enable_dev_mode: false/enable_dev_mode: true/g'  conf/config.yaml
+git checkout conf/config.yaml
+
+echo "
+apisix:
+    enable_dev_mode: true
+" > conf/config.yaml
 
 make init
 
@@ -102,9 +109,11 @@ if [ $count -ne 0 ]; then
     exit 1
 fi
 
-git checkout conf/config.yaml
+echo "passed: enable enable_dev_mode"
+
+# check whether the 'worker_cpu_affinity' is in nginx.conf
 
-# check whether the 'worker_cpu_affinity' is in nginx.conf .
+git checkout conf/config.yaml
 
 make init
 
@@ -118,8 +127,13 @@ echo "passed: nginx.conf file contains worker_cpu_affinity configuration"
 
 # check admin https enabled
 
-sed  -i 's/\# port_admin: 9180/port_admin: 9180/'  conf/config.yaml
-sed  -i 's/\# https_admin: true/https_admin: true/'  conf/config.yaml
+git checkout conf/config.yaml
+
+echo "
+apisix:
+    port_admin: 9180
+    https_admin: true
+" > conf/config.yaml
 
 make init
 
@@ -137,14 +151,13 @@ if [ ! $code -eq 200 ]; then
     exit 1
 fi
 
+make stop
+
 echo "passed: admin https enabled"
 
 # rollback to the default
 
-make stop
-
-sed  -i 's/port_admin: 9180/\# port_admin: 9180/'  conf/config.yaml
-sed  -i 's/https_admin: true/\# https_admin: true/'  conf/config.yaml
+git checkout conf/config.yaml
 
 make init
 
@@ -174,7 +187,12 @@ echo "passed: worker_shutdown_timeout in nginx.conf is ok"
 
 # check worker processes number is configurable.
 
-sed -i 's/worker_processes: auto/worker_processes: 2/'  conf/config.yaml
+git checkout conf/config.yaml
+
+echo "
+nginx_config:
+    worker_processes: 2
+" > conf/config.yaml
 
 make init
 
diff --git a/.travis/linux_apisix_master_luarocks_runner.sh b/.travis/linux_apisix_master_luarocks_runner.sh
index 7705c97..6b63f3a 100755
--- a/.travis/linux_apisix_master_luarocks_runner.sh
+++ b/.travis/linux_apisix_master_luarocks_runner.sh
@@ -55,6 +55,10 @@ script() {
 
     sudo rm -rf /usr/local/apisix
 
+    # run the test case in an empty folder
+    mkdir tmp && cd tmp
+    cp -r ../utils ./
+
     # install APISIX by shell
     sudo mkdir -p /usr/local/apisix/deps
     sudo PATH=$PATH ./utils/install-apisix.sh install > build.log 2>&1 || (cat build.log && exit 1)
@@ -78,7 +82,7 @@ script() {
     sudo PATH=$PATH apisix stop
 
     # apisix cli test
-    sudo PATH=$PATH .travis/apisix_cli_test.sh
+    # todo: need a more stable way
 
     cat /usr/local/apisix/logs/error.log | grep '\[error\]' > /tmp/error.log | true
     if [ -s /tmp/error.log ]; then
diff --git a/.travis/linux_openresty_mtls_runner.sh b/.travis/linux_openresty_mtls_runner.sh
index 8b7e035..d0a7ceb 100755
--- a/.travis/linux_openresty_mtls_runner.sh
+++ b/.travis/linux_openresty_mtls_runner.sh
@@ -99,12 +99,17 @@ script() {
 
 
     # enable mTLS
-    sed  -i 's/\# port_admin: 9180/port_admin: 9180/'  conf/config.yaml
-    sed  -i 's/\# https_admin: true/https_admin: true/'  conf/config.yaml
-    sed  -i 's/mtls_enable: false/mtls_enable: true/'  conf/config.yaml
-    sed  -i 's#admin_ssl_ca_cert: ""#admin_ssl_ca_cert: "../t/certs/mtls_ca.crt"#'  conf/config.yaml
-    sed  -i 's#admin_ssl_cert_key: ""#admin_ssl_cert_key: "../t/certs/mtls_server.key"#'  conf/config.yaml
-    sed  -i 's#admin_ssl_cert: ""#admin_ssl_cert: "../t/certs/mtls_server.crt"#'  conf/config.yaml
+    echo "
+apisix:
+    port_admin: 9180
+    https_admin: true
+
+    admin_api_mtls:
+        admin_ssl_cert: "../t/certs/mtls_server.crt"
+        admin_ssl_cert_key: "../t/certs/mtls_server.key"
+        admin_ssl_ca_cert: "../t/certs/mtls_ca.crt"
+
+" > conf/config.yaml
 
     ./bin/apisix help
     ./bin/apisix init
diff --git a/Makefile b/Makefile
index edfc950..36a045c 100644
--- a/Makefile
+++ b/Makefile
@@ -120,6 +120,7 @@ install: default
 	$(INSTALL) -d /usr/local/apisix/conf/cert
 	$(INSTALL) conf/mime.types /usr/local/apisix/conf/mime.types
 	$(INSTALL) conf/config.yaml /usr/local/apisix/conf/config.yaml
+	$(INSTALL) conf/config-default.yaml /usr/local/apisix/conf/config-default.yaml
 	$(INSTALL) conf/cert/apisix.* /usr/local/apisix/conf/cert/
 
 	$(INSTALL) -d $(INST_LUADIR)/apisix
diff --git a/apisix/core.lua b/apisix/core.lua
index 1b4ebf4..2d3ca2e 100644
--- a/apisix/core.lua
+++ b/apisix/core.lua
@@ -15,7 +15,10 @@
 -- limitations under the License.
 --
 local log = require("apisix.core.log")
-local local_conf = require("apisix.core.config_local").local_conf()
+local local_conf, err = require("apisix.core.config_local").local_conf()
+if not local_conf then
+    error("failed to parse yaml config: " .. err)
+end
 
 local config_center = local_conf.apisix and local_conf.apisix.config_center
                       or "etcd"
diff --git a/apisix/core/config_local.lua b/apisix/core/config_local.lua
index 403b8ac..42e6ae7 100644
--- a/apisix/core/config_local.lua
+++ b/apisix/core/config_local.lua
@@ -14,19 +14,24 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 --
+
 local log = require("apisix.core.log")
 local profile = require("apisix.core.profile")
 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 _M = {
-    version = 0.2,
-}
+local _M = {}
 
 
 local function read_file(path)
@@ -46,17 +51,106 @@ function _M.clear_cache()
 end
 
 
+local function is_empty_yaml_line(line)
+    return line == '' or string.find(line, '^%s*$') or
+           string.find(line, '^%s*#')
+end
+
+
+local function tab_is_array(t)
+    local count = 0
+    for k,v in pairs(t) do
+        count = count + 1
+    end
+
+    return #t == count
+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 tab_is_array(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 yaml_config, err = read_file(local_conf_path)
-    if type(yaml_config) ~= "string" then
-        return nil, "failed to read config file:" .. err
+    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
     end
 
-    config_data = yaml.parse(yaml_config)
     return config_data
 end
 
diff --git a/bin/apisix b/bin/apisix
index 625f584..9f22884 100755
--- a/bin/apisix
+++ b/bin/apisix
@@ -565,16 +565,76 @@ local function read_file(file_path)
 end
 
 
+local function is_empty_yaml_line(line)
+    return line == '' or string.find(line, '^%s*$') or
+           string.find(line, '^%s*#')
+end
+
+
+local function tab_is_array(t)
+    local count = 0
+    for k,v in pairs(t) do
+        count = count + 1
+    end
+
+    return #t == count
+end
+
+
+local function merge_conf(base, new_tab)
+    for key, val in pairs(new_tab) do
+        if type(val) == "table" then
+            if tab_is_array(val) then
+                base[key] = val
+            else
+                merge_conf(base[key], val)
+            end
+        else
+            base[key] = val
+        end
+    end
+    return base
+end
+
+
 local function read_yaml_conf()
     local profile = require("apisix.core.profile")
     profile.apisix_home = apisix_home .. "/"
-    local local_conf_path = profile:yaml_path("config")
-    local ymal_conf, err = read_file(local_conf_path)
-    if not ymal_conf then
+    local local_conf_path = profile:yaml_path("config-default")
+    local default_conf_yaml, err = read_file(local_conf_path)
+    if not default_conf_yaml then
         return nil, err
     end
 
-    return yaml.parse(ymal_conf)
+    local default_conf = yaml.parse(default_conf_yaml)
+    if not default_conf then
+        return nil, "invalid config-default.yaml file"
+    end
+
+    local_conf_path = profile:yaml_path("config")
+    local user_conf_yaml, err = read_file(local_conf_path)
+    if not user_conf_yaml then
+        return nil, err
+    end
+
+    local is_empty_file = true
+    for line in string.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
+
+        merge_conf(default_conf, user_conf)
+    end
+
+    return default_conf
 end
 
 local function get_openresty_version()
diff --git a/conf/config.yaml b/conf/config-default.yaml
similarity index 98%
copy from conf/config.yaml
copy to conf/config-default.yaml
index 047f775..bbef151 100644
--- a/conf/config.yaml
+++ b/conf/config-default.yaml
@@ -14,6 +14,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+# PLEASE DO NOT UPDATE THIS FILE!
+# If you want to set the specified configuration value, you can set the new
+# value in the conf/config.yaml file.
+#
+
 apisix:
   node_listen: 9080              # APISIX listening port
   enable_admin: true
diff --git a/conf/config.yaml b/conf/config.yaml
index 047f775..d9c060d 100644
--- a/conf/config.yaml
+++ b/conf/config.yaml
@@ -14,171 +14,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-apisix:
-  node_listen: 9080              # APISIX listening port
-  enable_admin: true
-  enable_admin_cors: true         # Admin API support CORS response headers.
-  enable_debug: false
-  enable_dev_mode: false          # Sets nginx worker_processes to 1 if set to true
-  enable_reuseport: true          # Enable nginx SO_REUSEPORT switch if set to true.
-  enable_ipv6: true
-  config_center: etcd             # etcd: use etcd to store the config value
-                                  # yaml: fetch the config value from local yaml file `/your_path/conf/apisix.yaml`
-
-  #proxy_protocol:                 # Proxy Protocol configuration
-  #  listen_http_port: 9181        # The port with proxy protocol for http, it differs from node_listen and port_admin.
-                                   # This port can only receive http request with proxy protocol, but node_listen & port_admin
-                                   # can only receive http request. If you enable proxy protocol, you must use this port to
-                                   # receive http request with proxy protocol
-  #  listen_https_port: 9182       # The port with proxy protocol for https
-  #  enable_tcp_pp: true           # Enable the proxy protocol for tcp proxy, it works for stream_proxy.tcp option
-  #  enable_tcp_pp_to_upstream: true # Enables the proxy protocol to the upstream server
-
-  proxy_cache:                     # Proxy Caching configuration
-    cache_ttl: 10s                 # The default caching time if the upstream does not specify the cache time
-    zones:                         # The parameters of a cache
-    - name: disk_cache_one         # The name of the cache, administrator can be specify
-                                   # which cache to use by name in the admin api
-      memory_size: 50m             # The size of shared memory, it's used to store the cache index
-      disk_size: 1G                # The size of disk, it's used to store the cache data
-      disk_path: "/tmp/disk_cache_one" # The path to store the cache data
-      cache_levels: "1:2"           # The hierarchy levels of a cache
-  #  - name: disk_cache_two
-  #    memory_size: 50m
-  #    disk_size: 1G
-  #    disk_path: "/tmp/disk_cache_two"
-  #    cache_levels: "1:2"
-
-  allow_admin:                  # http://nginx.org/en/docs/http/ngx_http_access_module.html#allow
-    - 127.0.0.0/24              # If we don't set any IP list, then any IP access is allowed by default.
-  #   - "::/64"
-  # port_admin: 9180              # use a separate port
-  # https_admin: true             # enable HTTPS when use a separate port for Admin API.
-                                # Admin API will use conf/apisix_admin_api.crt and conf/apisix_admin_api.key as certificate.
-  admin_api_mtls:               # Depends on `port_admin` and `https_admin`.
-    admin_ssl_cert: ""             # Path of your self-signed server side cert.
-    admin_ssl_cert_key: ""         # Path of your self-signed server side key.
-    admin_ssl_ca_cert: ""          # Path of your self-signed ca cert.The CA is used to sign all admin api callers' certificates.
-
-  # Default token when use API to call for Admin API.
-  # *NOTE*: Highly recommended to modify this value to protect APISIX's Admin API.
-  # Disabling this configuration item means that the Admin API does not
-  # require any authentication.
-  admin_key:
-    -
-      name: "admin"
-      key: edd1c9f034335f136f87ad84b625c8f1
-      role: admin                 # admin: manage all configuration data
-                                  # viewer: only can view configuration data
-    -
-      name: "viewer"
-      key: 4054f7cf07e344346cd3f287985e76a2
-      role: viewer
-
-  delete_uri_tail_slash: false    # delete the '/' at the end of the URI
-  router:
-    http: 'radixtree_uri'         # radixtree_uri: match route by uri(base on radixtree)
-                                  # radixtree_host_uri: match route by host + uri(base on radixtree)
-    ssl: 'radixtree_sni'          # radixtree_sni: match route by SNI(base on radixtree)
-  # stream_proxy:                 # TCP/UDP proxy
-  #   tcp:                        # TCP proxy port list
-  #     - 9100
-  #     - 9101
-  #   udp:                        # UDP proxy port list
-  #     - 9200
-  #     - 9211
-  # dns_resolver:                   # If not set, read from `/etc/resolv.conf`
-  #  - 1.1.1.1
-  #  - 8.8.8.8
-  dns_resolver_valid: 30          # valid time for dns result 30 seconds
-  resolver_timeout: 5             # resolver timeout
-  ssl:
-    enable: true
-    enable_http2: true
-    listen_port: 9443
-    ssl_protocols: "TLSv1.2 TLSv1.3"
-    ssl_ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
-    key_encrypt_salt: "edd1c9f0985e76a2"    #  If not set, will save origin ssl key into etcd.
-                                            #  If set this, must be a string of length 16. And it will encrypt ssl key with AES-128-CBC
-                                            #  !!! So do not change it after saving your ssl, it can't decrypt the ssl keys have be saved if you change !!
-#  discovery: eureka               # service discovery center
-nginx_config:                     # config for render the template to genarate nginx.conf
-  error_log: "logs/error.log"
-  error_log_level: "warn"         # warn,error
-  worker_processes: auto
-  worker_rlimit_nofile: 20480     # the number of files a worker process can open, should be larger than worker_connections
-  worker_shutdown_timeout: 240s     # timeout for a graceful shutdown of worker processes
-  event:
-    worker_connections: 10620
-  http:
-    access_log: "logs/access.log"
-    keepalive_timeout: 60s         # timeout during which a keep-alive client connection will stay open on the server side.
-    client_header_timeout: 60s     # timeout for reading client request header, then 408 (Request Time-out) error is returned to the client
-    client_body_timeout: 60s       # timeout for reading client request body, then 408 (Request Time-out) error is returned to the client
-    send_timeout: 10s              # timeout for transmitting a response to the client.then the connection is closed
-    underscores_in_headers: "on"   # default enables the use of underscores in client request header fields
-    real_ip_header: "X-Real-IP"    # http://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header
-    real_ip_from:                  # http://nginx.org/en/docs/http/ngx_http_realip_module.html#set_real_ip_from
-      - 127.0.0.1
-      - 'unix:'
-    #lua_shared_dicts:              # add custom shared cache to nginx.conf
-    #  ipc_shared_dict: 100m        # custom shared cache, format: `cache-key: cache-size`
-
-etcd:
-  host:                           # it's possible to define multiple etcd hosts addresses of the same etcd cluster.
-    - "http://127.0.0.1:2379"     # multiple etcd address
-  prefix: "/apisix"               # apisix configurations prefix
-  timeout: 30                     # 30 seconds
-  # user: root                     # root username for etcd
-  # password: 5tHkHhYkjr6cQY        # root password for etcd
-#eureka:
-#  host:                           # it's possible to define multiple eureka hosts addresses of the same eureka cluster.
-#    - "http://127.0.0.1:8761"
-#  prefix: "/eureka/"
-#  fetch_interval: 30              # default 30s
-#  weight: 100                     # default weight for node
-#  timeout:
-#    connect: 2000                 # default 2000ms
-#    send: 2000                    # default 2000ms
-#    read: 5000                    # default 5000ms
-
-plugins:                          # plugin list
-  - example-plugin
-  - limit-req
-  - limit-count
-  - limit-conn
-  - key-auth
-  - basic-auth
-  - prometheus
-  - node-status
-  - jwt-auth
-  - zipkin
-  - ip-restriction
-  - grpc-transcode
-  - serverless-pre-function
-  - serverless-post-function
-  - openid-connect
-  - proxy-rewrite
-  - redirect
-  - response-rewrite
-  - fault-injection
-  - udp-logger
-  - wolf-rbac
-  - tcp-logger
-  - kafka-logger
-  - cors
-  - consumer-restriction
-  - syslog
-  - batch-requests
-  - http-logger
-  - skywalking
-  - echo
-  - authz-keycloak
-  - uri-blocker
-  - request-validation
-  - proxy-cache
-  - proxy-mirror
-  - request-id
-
-stream_plugins:
-  - mqtt-proxy
+# If you want to set the specified configuration value, you can set the new
+# in this file. For example if you want to specify the etcd address:
+#
+# etcd:
+#     host:
+#       - "http://127.0.0.1:2379"
+#
diff --git a/doc/architecture-design.md b/doc/architecture-design.md
index d52ba30..a8593b8 100644
--- a/doc/architecture-design.md
+++ b/doc/architecture-design.md
@@ -45,25 +45,27 @@
 
 ## APISIX Config
 
-We can start using APISIX just by modifying `conf/config.yaml` file.
+For example, set the default listening port of APISIX to 8000, and keep other configurations as default. The configuration in `conf/config.yaml` should be like this:
 
 ```yaml
 apisix:
-  node_listen: 9080             # APISIX listening port
+  node_listen: 8000             # APISIX listening port
+```
+
+Set the default listening port of APISIX to 8000, set the `etcd` address to `http://foo:2379`,
+and keep other configurations as default. The configuration in `conf/config.yaml` should be like this:
+
+```yaml
+apisix:
+  node_listen: 8000             # APISIX listening port
 
 etcd:
-  host: "http://127.0.0.1:2379" # etcd address
-  prefix: "apisix"              # apisix configurations prefix
-  timeout: 60
-
-plugins:                        # plugin name list
-  - example-plugin
-  - limit-req
-  - limit-count
-  - ...
+  host: "http://foo:2379"       # etcd address
 ```
 
-*Note* `apisix` will generate `conf/nginx.conf` file automatically, so please *DO NOT EDIT* that file.
+Other default configurations can be found in the `conf/config-default.yaml` file, which is bound to the APISIX source code. **Never** manually modify the `conf/config-default.yaml` file. If you need to customize any configuration, you should update the `conf/config.yaml` file.
+
+**Note** `APISIX` will generate `conf/nginx.conf` file automatically, so please *DO NOT EDIT* `conf/nginx.conf` file too.
 
 [Back to top](#Table-of-contents)
 
diff --git a/doc/plugin-develop.md b/doc/plugin-develop.md
index a85c208..052f298 100644
--- a/doc/plugin-develop.md
+++ b/doc/plugin-develop.md
@@ -56,7 +56,7 @@ Note : if the dependency of some plugin needs to be initialized when Nginx start
 
 ## name and config
 
-determine the name and priority of the plugin, and add to conf/config.yaml. For example, for the key-auth plugin,
+determine the name and priority of the plugin, and add to conf/config-default.yaml. For example, for the key-auth plugin,
  you need to specify the plugin name in the code (the name is the unique identifier of the plugin and cannot be
  duplicate), you can see the code in file "__apisix/plugins/key-auth.lua__" :
 
@@ -74,7 +74,7 @@ determine the name and priority of the plugin, and add to conf/config.yaml. For
 
 Note : The priority of the new plugin cannot be the same as the priority of any existing plugin. In addition, plugins with a high priority value will be executed first. For example, the priority of basic-auth is 2520 and the priority of ip-restriction is 3000. Therefore, the ip-restriction plugin will be executed first, then the basic-auth plugin.
 
-in the "__conf/config.yaml__" configuration file, the enabled plugins (all specified by plugin name) are listed.
+in the "__conf/config-default.yaml__" configuration file, the enabled plugins (all specified by plugin name) are listed.
 
 ```yaml
 plugins:                          # plugin list
diff --git a/doc/zh-cn/architecture-design.md b/doc/zh-cn/architecture-design.md
index 2d696cc..37ff449 100644
--- a/doc/zh-cn/architecture-design.md
+++ b/doc/zh-cn/architecture-design.md
@@ -45,22 +45,27 @@
 
 通过修改本地 `conf/config.yaml` 文件完成对 APISIX 服务本身的基本配置。
 
+比如修改 APISIX 默认监听端口为 8000,其他配置保持默认,在 `conf/config.yaml` 中只需这样配置:
+
 ```yaml
 apisix:
-  node_listen: 9080             # APISIX listening port
+  node_listen: 8000             # APISIX listening port
+```
+
+比如指定 APISIX 默认监听端口为 8000,并且设置 etcd 地址为 `http://foo:2379`,
+其他配置保持默认。在 `conf/config.yaml` 中只需这样配置:
+
+```yaml
+apisix:
+  node_listen: 8000             # APISIX listening port
 
 etcd:
-  host: "http://127.0.0.1:2379" # etcd address
-  prefix: "apisix"              # apisix configurations prefix
-  timeout: 60
-
-plugins:                        # plugin name list
-  - example-plugin
-  - limit-req
-  - limit-count
-  - ...
+  host: "http://foo:2379"       # etcd address
 ```
 
+其他默认配置,可以在 `conf/config-default.yaml` 文件中看到,该文件是与 APISIX 源码强绑定,
+**永远不要**手工修改 `conf/config-default.yaml` 文件。如果需要自定义任何配置,都应在 `conf/config.yaml` 文件中完成。
+
 *注意* 不要手工修改 APISIX 自身的 `conf/nginx.conf` 文件,当服务每次启动时,`apisix`
 会根据 `conf/config.yaml` 配置自动生成新的 `conf/nginx.conf` 并自动启动服务。
 
diff --git a/doc/zh-cn/plugin-develop.md b/doc/zh-cn/plugin-develop.md
index 85083dd..0c5356b 100644
--- a/doc/zh-cn/plugin-develop.md
+++ b/doc/zh-cn/plugin-develop.md
@@ -54,7 +54,7 @@
 
 ## 插件命名与配置
 
-给插件取一个很棒的名字,确定插件的加载优先级,然后在 __conf/config.yaml__ 文件中添加上你的插件名。例如 key-auth 这个插件,
+给插件取一个很棒的名字,确定插件的加载优先级,然后在 __conf/config-default.yaml__ 文件中添加上你的插件名。例如 key-auth 这个插件,
 需要在代码里指定插件名称(名称是插件的唯一标识,不可重名),在 __apisix/plugins/key-auth.lua__ 文件中可以看到:
 
 ```lua
@@ -71,7 +71,7 @@
 
 注:新插件的优先级( priority 属性 )不能与现有插件的优先级相同。另外,优先级( priority )值大的插件,会优先执行,比如 `basic-auth` 的优先级是 2520 ,`ip-restriction` 的优先级是 3000 ,所以在每个阶段,会先执行 `ip-restriction` 插件,再去执行 `basic-auth` 插件。
 
-在 __conf/config.yaml__ 配置文件中,列出了启用的插件(都是以插件名指定的):
+在 __conf/config-default.yaml__ 配置文件中,列出了启用的插件(都是以插件名指定的):
 
 ```yaml
 plugins:                          # plugin list
diff --git a/t/APISIX.pm b/t/APISIX.pm
index d0e7e3b..c12c240 100644
--- a/t/APISIX.pm
+++ b/t/APISIX.pm
@@ -69,20 +69,29 @@ if ($enable_local_dns) {
 }
 
 
-my $yaml_config = read_file("conf/config.yaml");
+my $default_yaml_config = read_file("conf/config-default.yaml");
+my $user_yaml_config = read_file("conf/config.yaml");
 my $ssl_crt = read_file("conf/cert/apisix.crt");
 my $ssl_key = read_file("conf/cert/apisix.key");
 my $test2_crt = read_file("conf/cert/test2.crt");
 my $test2_key = read_file("conf/cert/test2.key");
-$yaml_config =~ s/node_listen: 9080/node_listen: 1984/;
-$yaml_config =~ s/  # stream_proxy:/  stream_proxy:\n    tcp:\n      - 9100/;
-$yaml_config =~ s/admin_key:/disable_admin_key:/;
+$user_yaml_config = <<_EOC_;
+apisix:
+  node_listen: 1984
+  stream_proxy:
+    tcp:
+      - 9100
+  admin_key: null
+_EOC_
 
 my $etcd_enable_auth = $ENV{"ETCD_ENABLE_AUTH"} || "false";
 
 if ($etcd_enable_auth eq "true") {
-    $yaml_config =~ s/  # user:/  user:/;
-    $yaml_config =~ s/  # password:/  password:/;
+    $user_yaml_config .= <<_EOC_;
+etcd:
+  user: root
+  password: 5tHkHhYkjr6cQY
+_EOC_
 }
 
 
@@ -414,15 +423,17 @@ $user_apisix_yaml
 _EOC_
     }
 
-    my $user_yaml_config = $block->yaml_config // $yaml_config;
+    my $yaml_config = $block->yaml_config // $user_yaml_config;
     my $user_debug_config = $block->debug_config // "";
 
     my $user_files = $block->user_files;
     $user_files .= <<_EOC_;
 >>> ../conf/$debug_file
 $user_debug_config
+>>> ../conf/config-default.yaml
+$default_yaml_config
 >>> ../conf/$config_file
-$user_yaml_config
+$yaml_config
 >>> ../conf/cert/apisix.crt
 $ssl_crt
 >>> ../conf/cert/apisix.key
diff --git a/t/admin/stream-routes-disable.t b/t/admin/stream-routes-disable.t
index c363722..7752663 100644
--- a/t/admin/stream-routes-disable.t
+++ b/t/admin/stream-routes-disable.t
@@ -23,25 +23,16 @@ no_root_location();
 no_shuffle();
 log_level("info");
 
-my $apisix_home = $ENV{APISIX_HOME} || cwd();
-
-sub read_file($) {
-    my $infile = shift;
-    open my $in, "$apisix_home/$infile"
-        or die "cannot open $infile for reading: $!";
-    my $data = do { local $/; <$in> };
-    close $in;
-    $data;
-}
-
-my $yaml_config = read_file("conf/config.yaml");
-$yaml_config =~ s/node_listen: 9080/node_listen: 1984/;
-$yaml_config =~ s/admin_key:/disable_admin_key:/;
-
 add_block_preprocessor(sub {
     my ($block) = @_;
 
-    $block->set_value("yaml_config", $yaml_config);
+    my $user_yaml_config = <<_EOC_;
+apisix:
+    node_listen: 1984
+    admin_key: null
+_EOC_
+
+    $block->set_value("yaml_config", $user_yaml_config);
 });
 
 run_tests;
diff --git a/t/admin/token.t b/t/admin/token.t
index e3b8769..22308e1 100644
--- a/t/admin/token.t
+++ b/t/admin/token.t
@@ -23,26 +23,12 @@ no_root_location();
 no_shuffle();
 log_level("info");
 
-my $apisix_home = $ENV{APISIX_HOME} || cwd();
-
-sub read_file($) {
-    my $infile = shift;
-    open my $in, "$apisix_home/$infile"
-        or die "cannot open $infile for reading: $!";
-    my $data = do { local $/; <$in> };
-    close $in;
-    $data;
-}
-
-my $yaml_config = read_file("conf/config.yaml");
-$yaml_config =~ s/node_listen: 9080/node_listen: 1984/;
-
 add_block_preprocessor(sub {
     my ($block) = @_;
 
-    my $user_yaml_config = $block->yaml_config;
-    $user_yaml_config .= <<_EOC_;
-$yaml_config
+    my $user_yaml_config = <<_EOC_;
+apisix:
+  node_listen: 1984
 _EOC_
 
     $block->set_value("yaml_config", $user_yaml_config);
diff --git a/t/config-center-yaml/route-service.t b/t/config-center-yaml/route-service.t
index 7f11fce..8febcd7 100644
--- a/t/config-center-yaml/route-service.t
+++ b/t/config-center-yaml/route-service.t
@@ -21,19 +21,12 @@ log_level('info');
 no_root_location();
 no_shuffle();
 
-sub read_file($) {
-    my $infile = shift;
-    open my $in, $infile
-        or die "cannot open $infile for reading: $!";
-    my $cert = do { local $/; <$in> };
-    close $in;
-    $cert;
-}
-
-our $yaml_config = read_file("conf/config.yaml");
-$yaml_config =~ s/node_listen: 9080/node_listen: 1984/;
-$yaml_config =~ s/config_center: etcd/config_center: yaml/;
-$yaml_config =~ s/enable_admin: true/enable_admin: false/;
+our $yaml_config = <<_EOC_;
+apisix:
+    node_listen: 1984
+    config_center: yaml
+    enable_admin: false
+_EOC_
 
 run_tests();
 
diff --git a/t/config-center-yaml/route-upstream.t b/t/config-center-yaml/route-upstream.t
index 4d3868b..46063a8 100644
--- a/t/config-center-yaml/route-upstream.t
+++ b/t/config-center-yaml/route-upstream.t
@@ -21,19 +21,12 @@ log_level('info');
 no_root_location();
 no_shuffle();
 
-sub read_file($) {
-    my $infile = shift;
-    open my $in, $infile
-        or die "cannot open $infile for reading: $!";
-    my $cert = do { local $/; <$in> };
-    close $in;
-    $cert;
-}
-
-our $yaml_config = read_file("conf/config.yaml");
-$yaml_config =~ s/node_listen: 9080/node_listen: 1984/;
-$yaml_config =~ s/config_center: etcd/config_center: yaml/;
-$yaml_config =~ s/enable_admin: true/enable_admin: false/;
+our $yaml_config = <<_EOC_;
+apisix:
+    node_listen: 1984
+    config_center: yaml
+    enable_admin: false
+_EOC_
 
 run_tests();
 
diff --git a/t/config-center-yaml/route.t b/t/config-center-yaml/route.t
index 4e207fc..1f2c8e9 100644
--- a/t/config-center-yaml/route.t
+++ b/t/config-center-yaml/route.t
@@ -21,19 +21,12 @@ log_level('info');
 no_root_location();
 no_shuffle();
 
-sub read_file($) {
-    my $infile = shift;
-    open my $in, $infile
-        or die "cannot open $infile for reading: $!";
-    my $cert = do { local $/; <$in> };
-    close $in;
-    $cert;
-}
-
-our $yaml_config = read_file("conf/config.yaml");
-$yaml_config =~ s/node_listen: 9080/node_listen: 1984/;
-$yaml_config =~ s/config_center: etcd/config_center: yaml/;
-$yaml_config =~ s/enable_admin: true/enable_admin: false/;
+our $yaml_config = <<_EOC_;
+apisix:
+    node_listen: 1984
+    config_center: yaml
+    enable_admin: false
+_EOC_
 
 run_tests();
 
diff --git a/t/core/config-default.t b/t/core/config-default.t
new file mode 100644
index 0000000..60a4859
--- /dev/null
+++ b/t/core/config-default.t
@@ -0,0 +1,93 @@
+#
+# 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_root_location();
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: sanity
+--- config
+    location /t {
+        content_by_lua_block {
+            local encode_json = require "lib.json_sort" .encode
+            local config = require("apisix.core").config.local_conf()
+
+            ngx.say("node_listen: ", config.apisix.node_listen)
+            ngx.say("stream_proxy: ", encode_json(config.apisix.stream_proxy))
+            ngx.say("admin_key: ", encode_json(config.apisix.admin_key))
+        }
+    }
+--- request
+GET /t
+--- response_body
+node_listen: 1984
+stream_proxy: {"tcp":[9100]}
+admin_key: null
+
+
+
+=== TEST 2: wrong type: expect: number, but got: string
+--- yaml_config
+apisix:
+  node_listen: xxxx
+--- must_die
+--- error_log
+failed to parse yaml config: failed to merge, path[apisix->node_listen] expect: number, but got: string
+
+
+
+=== TEST 3: use `null` means delete
+--- yaml_config
+apisix:
+  admin_key: null
+--- config
+  location /t {
+    content_by_lua_block {
+        local encode_json = require "lib.json_sort" .encode
+        local config = require("apisix.core").config.local_conf()
+
+        ngx.say("admin_key: ", encode_json(config.apisix.admin_key))
+    }
+}
+--- request
+GET /t
+--- response_body
+admin_key: null
+
+
+
+=== TEST 4: use `~` means delete
+--- yaml_config
+apisix:
+  admin_key: ~
+--- config
+  location /t {
+    content_by_lua_block {
+        local encode_json = require "lib.json_sort" .encode
+        local config = require("apisix.core").config.local_conf()
+
+        ngx.say("admin_key: ", encode_json(config.apisix.admin_key))
+    }
+}
+--- request
+GET /t
+--- response_body
+admin_key: null
diff --git a/t/core/config.t b/t/core/config.t
index 4075001..807beac 100644
--- a/t/core/config.t
+++ b/t/core/config.t
@@ -46,7 +46,7 @@ first plugin: "example-plugin"
 --- config
     location /t {
         content_by_lua_block {
-            local encode_json = require "cjson.safe" .encode
+            local encode_json = require "lib.json_sort" .encode
             local config = require("apisix.core").config.local_conf()
 
             ngx.say("etcd host: ", config.etcd.host)
@@ -56,7 +56,8 @@ first plugin: "example-plugin"
     }
 --- yaml_config
 etcd:
-  host: "http://127.0.0.1:2379" # etcd address
+  host:
+    - "http://127.0.0.1:2379" # etcd address
   prefix: "/apisix"             # apisix configurations prefix
   timeout: 1
 
@@ -298,4 +299,4 @@ GET /t
 --- response_body
 etcd host: http://127.0.0.1:2379
 first plugin: "example-plugin"
-seq: {"Flow style":["Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune","Pluto"],"Block style":["Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune","Pluto"]}
+seq: {"Block style":["Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune","Pluto"],"Flow style":["Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune","Pluto"]}
diff --git a/t/core/profile.t b/t/core/profile.t
index 12a1292..5217353 100644
--- a/t/core/profile.t
+++ b/t/core/profile.t
@@ -29,16 +29,8 @@ run_tests;
 __DATA__
 
 === TEST 1: set env "APISIX_PROFILE"
---- config
-    location /t {
-        content_by_lua_block {
-            local profile = require("apisix.core.profile")
-            profile.apisix_home = "./test/"
-            local local_conf_path = profile:yaml_path("config")
-            ngx.say(local_conf_path)
-        }
-    }
 --- request
 GET /t
---- response_body
-./test/conf/config-dev.yaml
+--- must_die
+--- error_log_like eval
+qr/failed to read config file:.*conf/config-default-dev.yaml/
diff --git a/t/debug/debug-mode.t b/t/debug/debug-mode.t
index 9f4f1a9..0b466e6 100644
--- a/t/debug/debug-mode.t
+++ b/t/debug/debug-mode.t
@@ -20,18 +20,11 @@ repeat_each(1);
 no_long_string();
 no_root_location();
 
-sub read_file($) {
-    my $infile = shift;
-    open my $in, $infile
-        or die "cannot open $infile for reading: $!";
-    my $cert = do { local $/; <$in> };
-    close $in;
-    $cert;
-}
-
-our $yaml_config = read_file("conf/config.yaml");
-$yaml_config =~ s/node_listen: 9080/node_listen: 1984/;
-$yaml_config =~ s/enable_debug: false/enable_debug: true/;
+our $yaml_config = <<_EOC_;
+apisix:
+    node_listen: 1984
+    enable_debug: true
+_EOC_
 
 run_tests;
 
diff --git a/t/discovery/eureka.t b/t/discovery/eureka.t
index d39178c..3801e7f 100644
--- a/t/discovery/eureka.t
+++ b/t/discovery/eureka.t
@@ -21,36 +21,23 @@ log_level('info');
 no_root_location();
 no_shuffle();
 
-sub read_file($) {
-    my $infile = shift;
-    open my $in, $infile
-        or die "cannot open $infile for reading: $!";
-    my $cert = do { local $/; <$in> };
-    close $in;
-    $cert;
-}
+our $yaml_config = <<_EOC_;
+apisix:
+  node_listen: 1984
+  config_center: yaml
+  enable_admin: false
+  discovery: eureka
 
-our $yaml_config = read_file("conf/config.yaml");
-$yaml_config =~ s/node_listen: 9080/node_listen: 1984/;
-$yaml_config =~ s/config_center: etcd/config_center: yaml/;
-$yaml_config =~ s/enable_admin: true/enable_admin: false/;
-$yaml_config =~ s/enable_admin: true/enable_admin: false/;
-$yaml_config =~ s/  discovery:/  discovery: eureka #/;
-$yaml_config =~ s/#  discovery:/  discovery: eureka #/;
-$yaml_config =~ s/error_log_level: "warn"/error_log_level: "info"/;
-
-
-$yaml_config .= <<_EOC_;
 eureka:
- host:
-   - "http://127.0.0.1:8761"
- prefix: "/eureka/"
- fetch_interval: 10
- weight: 80
- timeout:
-   connect: 1500
-   send: 1500
-   read: 1500
+  host:
+    - "http://127.0.0.1:8761"
+  prefix: "/eureka/"
+  fetch_interval: 10
+  weight: 80
+  timeout:
+    connect: 1500
+    send: 1500
+    read: 1500
 _EOC_
 
 run_tests();
diff --git a/t/node/rr-balance.t b/t/node/rr-balance.t
index 99b03d8..58a8770 100644
--- a/t/node/rr-balance.t
+++ b/t/node/rr-balance.t
@@ -14,15 +14,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-BEGIN {
-    if ($ENV{TEST_NGINX_CHECK_LEAK}) {
-        $SkipReason = "unavailable for the hup tests";
-
-    } else {
-        $ENV{TEST_NGINX_USE_HUP} = 1;
-        undef $ENV{TEST_NGINX_USE_STAP};
-    }
-}
 
 use t::APISIX 'no_plan';
 
diff --git a/t/node/upstream-node-dns.t b/t/node/upstream-node-dns.t
index d9b6625..ef15dc2 100644
--- a/t/node/upstream-node-dns.t
+++ b/t/node/upstream-node-dns.t
@@ -15,29 +15,18 @@
 # limitations under the License.
 #
 use t::APISIX 'no_plan';
-use Cwd qw(cwd);
 
 repeat_each(1);
 log_level('info');
 no_root_location();
 no_shuffle();
 
-my $apisix_home = $ENV{APISIX_HOME} || cwd();
-
-sub read_file($) {
-    my $infile = shift;
-    open my $in, "$apisix_home/$infile"
-        or die "cannot open $infile for reading: $!";
-    my $data = do { local $/; <$in> };
-    close $in;
-    $data;
-}
-
-my $yaml_config = read_file("conf/config.yaml");
-$yaml_config =~ s/node_listen: 9080/node_listen: 1984/;
-$yaml_config =~ s/enable_heartbeat: true/enable_heartbeat: false/;
-$yaml_config =~ s/admin_key:/disable_admin_key:/;
-$yaml_config =~ s/dns_resolver_valid: 30/dns_resolver_valid: 1/;
+our $yaml_config = <<_EOC_;
+apisix:
+    node_listen: 1984
+    admin_key: ~
+    dns_resolver_valid: 1
+_EOC_
 
 add_block_preprocessor(sub {
     my ($block) = @_;
diff --git a/t/plugin/example.t b/t/plugin/example.t
index 2634509..a1d1560 100644
--- a/t/plugin/example.t
+++ b/t/plugin/example.t
@@ -164,7 +164,8 @@ GET /t
 plugin name: example-plugin priority: 0
 --- yaml_config
 etcd:
-  host: "http://127.0.0.1:2379" # etcd address
+  host:
+    - "http://127.0.0.1:2379" # etcd address
   prefix: "/apisix"             # apisix configurations prefix
   timeout: 1
 
diff --git a/t/router/radixtree-host-uri.t b/t/router/radixtree-host-uri.t
index 6af56e3..d547499 100644
--- a/t/router/radixtree-host-uri.t
+++ b/t/router/radixtree-host-uri.t
@@ -22,19 +22,13 @@ worker_connections(256);
 no_root_location();
 no_shuffle();
 
-sub read_file($) {
-    my $infile = shift;
-    open my $in, $infile
-        or die "cannot open $infile for reading: $!";
-    my $cert = do { local $/; <$in> };
-    close $in;
-    $cert;
-}
-
-our $yaml_config = read_file("conf/config.yaml");
-$yaml_config =~ s/node_listen: 9080/node_listen: 1984/;
-$yaml_config =~ s/http: 'radixtree_uri'/http: 'radixtree_host_uri'/;
-$yaml_config =~ s/admin_key:/disable_admin_key:/;
+our $yaml_config = <<_EOC_;
+apisix:
+    node_listen: 1984
+    router:
+        http: 'radixtree_host_uri'
+    admin_key: null
+_EOC_
 
 run_tests();
 
diff --git a/t/router/radixtree-host-uri2.t b/t/router/radixtree-host-uri2.t
index d24d412..eb49091 100644
--- a/t/router/radixtree-host-uri2.t
+++ b/t/router/radixtree-host-uri2.t
@@ -22,21 +22,15 @@ worker_connections(256);
 no_root_location();
 no_shuffle();
 
-sub read_file($) {
-    my $infile = shift;
-    open my $in, $infile
-        or die "cannot open $infile for reading: $!";
-    my $cert = do { local $/; <$in> };
-    close $in;
-    $cert;
-}
-
-our $yaml_config = read_file("conf/config.yaml");
-$yaml_config =~ s/node_listen: 9080/node_listen: 1984/;
-$yaml_config =~ s/config_center: etcd/config_center: yaml/;
-$yaml_config =~ s/enable_admin: true/enable_admin: false/;
-$yaml_config =~ s/http: 'radixtree_uri'/http: 'radixtree_host_uri'/;
-$yaml_config =~ s/admin_key:/disable_admin_key:/;
+our $yaml_config = <<_EOC_;
+apisix:
+    node_listen: 1984
+    config_center: yaml
+    enable_admin: false
+    router:
+        http: 'radixtree_host_uri'
+    admin_key: null
+_EOC_
 
 run_tests();
 
diff --git a/t/router/radixtree-uri-host.t b/t/router/radixtree-uri-host.t
index 46e6242..c2e7b10 100644
--- a/t/router/radixtree-uri-host.t
+++ b/t/router/radixtree-uri-host.t
@@ -22,19 +22,6 @@ worker_connections(256);
 no_root_location();
 no_shuffle();
 
-sub read_file($) {
-    my $infile = shift;
-    open my $in, $infile
-        or die "cannot open $infile for reading: $!";
-    my $cert = do { local $/; <$in> };
-    close $in;
-    $cert;
-}
-
-our $yaml_config = read_file("conf/config.yaml");
-$yaml_config =~ s/node_listen: 9080/node_listen: 1984/;
-$yaml_config =~ s/admin_key:/disable_admin_key:/;
-
 run_tests();
 
 __DATA__
@@ -65,7 +52,6 @@ __DATA__
             ngx.say(body)
         }
     }
---- yaml_config eval: $::yaml_config
 --- request
 GET /t
 --- response_body
@@ -78,7 +64,6 @@ passed
 === TEST 2: /not_found
 --- request
 GET /not_found
---- yaml_config eval: $::yaml_config
 --- error_code: 404
 --- response_body
 {"error_msg":"failed to match any routes"}
@@ -90,7 +75,6 @@ GET /not_found
 === TEST 3: /not_found
 --- request
 GET /hello
---- yaml_config eval: $::yaml_config
 --- error_code: 404
 --- no_error_log
 [error]
@@ -100,7 +84,6 @@ GET /hello
 === TEST 4: /not_found
 --- request
 GET /hello
---- yaml_config eval: $::yaml_config
 --- more_headers
 Host: not_found.com
 --- error_code: 404
@@ -112,7 +95,6 @@ Host: not_found.com
 === TEST 5: hit routes (www.foo.com)
 --- request
 GET /hello
---- yaml_config eval: $::yaml_config
 --- more_headers
 Host: www.foo.com
 --- response_body
@@ -125,7 +107,6 @@ hello world
 === TEST 6: hit routes (user.foo.com)
 --- request
 GET /hello
---- yaml_config eval: $::yaml_config
 --- more_headers
 Host: user.foo.com
 --- response_body
@@ -161,7 +142,6 @@ hello world
             ngx.say(body)
         }
     }
---- yaml_config eval: $::yaml_config
 --- request
 GET /t
 --- response_body
@@ -174,7 +154,6 @@ passed
 === TEST 8: /not_found
 --- request
 GET /not_found
---- yaml_config eval: $::yaml_config
 --- error_code: 404
 --- response_body
 {"error_msg":"failed to match any routes"}
@@ -186,7 +165,6 @@ GET /not_found
 === TEST 9: /not_found
 --- request
 GET /hello
---- yaml_config eval: $::yaml_config
 --- error_code: 404
 --- no_error_log
 [error]
@@ -196,7 +174,6 @@ GET /hello
 === TEST 10: /not_found
 --- request
 GET /hello
---- yaml_config eval: $::yaml_config
 --- more_headers
 Host: www.foo.com
 --- error_code: 404
@@ -208,7 +185,6 @@ Host: www.foo.com
 === TEST 11: hit routes (foo.com)
 --- request
 GET /hello
---- yaml_config eval: $::yaml_config
 --- more_headers
 Host: foo.com
 --- response_body
diff --git a/t/router/radixtree-uri-keep-end-slash.t b/t/router/radixtree-uri-keep-end-slash.t
index ec6e9ba..46c9b9c 100644
--- a/t/router/radixtree-uri-keep-end-slash.t
+++ b/t/router/radixtree-uri-keep-end-slash.t
@@ -22,20 +22,12 @@ worker_connections(256);
 no_root_location();
 no_shuffle();
 
-sub read_file($) {
-    my $infile = shift;
-    open my $in, $infile
-        or die "cannot open $infile for reading: $!";
-    my $cert = do { local $/; <$in> };
-    close $in;
-    $cert;
-}
-
-our $yaml_config = read_file("conf/config.yaml");
-$yaml_config =~ s/node_listen: 9080/node_listen: 1984/;
-$yaml_config =~ s/enable_heartbeat: true/enable_heartbeat: false/;
-$yaml_config =~ s/admin_key:/disable_admin_key:/;
-$yaml_config =~ s/delete_uri_tail_slash: false/delete_uri_tail_slash: true/;
+our $yaml_config = <<_EOC_;
+apisix:
+    node_listen: 1984
+    delete_uri_tail_slash: true
+    admin_key: null
+_EOC_
 
 run_tests();
 
diff --git a/t/router/radixtree-uri-multiple.t b/t/router/radixtree-uri-multiple.t
index 81508a6..0b8d1f6 100644
--- a/t/router/radixtree-uri-multiple.t
+++ b/t/router/radixtree-uri-multiple.t
@@ -22,19 +22,6 @@ worker_connections(256);
 no_root_location();
 no_shuffle();
 
-sub read_file($) {
-    my $infile = shift;
-    open my $in, $infile
-        or die "cannot open $infile for reading: $!";
-    my $cert = do { local $/; <$in> };
-    close $in;
-    $cert;
-}
-
-our $yaml_config = read_file("conf/config.yaml");
-$yaml_config =~ s/node_listen: 9080/node_listen: 1984/;
-$yaml_config =~ s/admin_key:/disable_admin_key:/;
-
 run_tests();
 
 __DATA__
@@ -63,7 +50,6 @@ __DATA__
             ngx.say(body)
         }
     }
---- yaml_config eval: $::yaml_config
 --- request
 GET /t
 --- response_body
@@ -97,7 +83,6 @@ passed
             ngx.say(body)
         }
     }
---- yaml_config eval: $::yaml_config
 --- request
 GET /t
 --- response_body
@@ -131,7 +116,6 @@ passed
             ngx.say(body)
         }
     }
---- yaml_config eval: $::yaml_config
 --- request
 GET /t
 --- response_body
@@ -144,7 +128,6 @@ passed
 === TEST 4: /not_found
 --- request
 GET /not_found
---- yaml_config eval: $::yaml_config
 --- error_code: 404
 --- response_body
 {"error_msg":"failed to match any routes"}
@@ -156,7 +139,6 @@ GET /not_found
 === TEST 5: hit route 1
 --- request
 GET /server_port
---- yaml_config eval: $::yaml_config
 --- response_body eval
 qr/1980/
 --- no_error_log
@@ -167,7 +149,6 @@ qr/1980/
 === TEST 6: hit route 2
 --- request
 GET /server_port/route2
---- yaml_config eval: $::yaml_config
 --- response_body eval
 qr/1981/
 --- no_error_log
@@ -178,7 +159,6 @@ qr/1981/
 === TEST 7: hit route 3
 --- request
 GET /server_port/hello
---- yaml_config eval: $::yaml_config
 --- response_body eval
 qr/1982/
 --- no_error_log
@@ -201,7 +181,6 @@ qr/1982/
             ngx.say(body)
         }
     }
---- yaml_config eval: $::yaml_config
 --- request
 GET /t
 --- response_body
@@ -226,7 +205,6 @@ passed
             ngx.say(body)
         }
     }
---- yaml_config eval: $::yaml_config
 --- request
 GET /t
 --- response_body
diff --git a/t/router/radixtree-uri-priority.t b/t/router/radixtree-uri-priority.t
index 52eb50a..87b0cf6 100644
--- a/t/router/radixtree-uri-priority.t
+++ b/t/router/radixtree-uri-priority.t
@@ -22,18 +22,6 @@ worker_connections(256);
 no_root_location();
 no_shuffle();
 
-sub read_file($) {
-    my $infile = shift;
-    open my $in, $infile
-        or die "cannot open $infile for reading: $!";
-    my $cert = do { local $/; <$in> };
-    close $in;
-    $cert;
-}
-
-our $yaml_config = read_file("conf/config.yaml");
-$yaml_config =~ s/node_listen: 9080/node_listen: 1984/;
-$yaml_config =~ s/admin_key:/disable_admin_key:/;
 
 run_tests();
 
@@ -63,7 +51,6 @@ __DATA__
             ngx.say(body)
         }
     }
---- yaml_config eval: $::yaml_config
 --- request
 GET /t
 --- response_body
@@ -76,7 +63,6 @@ passed
 === TEST 2: hit routes
 --- request
 GET /server_port/aa
---- yaml_config eval: $::yaml_config
 --- response_body eval
 1980
 --- no_error_log
@@ -108,7 +94,6 @@ GET /server_port/aa
             ngx.say(body)
         }
     }
---- yaml_config eval: $::yaml_config
 --- request
 GET /t
 --- response_body
@@ -121,7 +106,6 @@ passed
 === TEST 4: hit routes
 --- request
 GET /server_port/aa
---- yaml_config eval: $::yaml_config
 --- response_body eval
 1980
 --- no_error_log
@@ -153,7 +137,6 @@ GET /server_port/aa
             ngx.say(body)
         }
     }
---- yaml_config eval: $::yaml_config
 --- request
 GET /t
 --- response_body
@@ -166,7 +149,6 @@ passed
 === TEST 6: hit routes
 --- request
 GET /server_port/aa
---- yaml_config eval: $::yaml_config
 --- response_body eval
 1981
 --- no_error_log
@@ -188,7 +170,6 @@ GET /server_port/aa
             ngx.say(body)
         }
     }
---- yaml_config eval: $::yaml_config
 --- request
 GET /t
 --- response_body
diff --git a/t/router/radixtree-uri-sanity.t b/t/router/radixtree-uri-sanity.t
index 0cd6101..587b385 100644
--- a/t/router/radixtree-uri-sanity.t
+++ b/t/router/radixtree-uri-sanity.t
@@ -22,19 +22,6 @@ worker_connections(256);
 no_root_location();
 no_shuffle();
 
-sub read_file($) {
-    my $infile = shift;
-    open my $in, $infile
-        or die "cannot open $infile for reading: $!";
-    my $cert = do { local $/; <$in> };
-    close $in;
-    $cert;
-}
-
-our $yaml_config = read_file("conf/config.yaml");
-$yaml_config =~ s/node_listen: 9080/node_listen: 1984/;
-$yaml_config =~ s/admin_key:/disable_admin_key:/;
-
 run_tests();
 
 __DATA__
@@ -65,7 +52,6 @@ __DATA__
             ngx.say(body)
         }
     }
---- yaml_config eval: $::yaml_config
 --- request
 GET /t
 --- response_body
@@ -78,7 +64,6 @@ passed
 === TEST 2: /not_found
 --- request
 GET /not_found
---- yaml_config eval: $::yaml_config
 --- error_code: 404
 --- response_body
 {"error_msg":"failed to match any routes"}
@@ -90,7 +75,6 @@ GET /not_found
 === TEST 3: /not_found
 --- request
 GET /hello
---- yaml_config eval: $::yaml_config
 --- error_code: 404
 --- response_body
 {"error_msg":"failed to match any routes"}
@@ -102,7 +86,6 @@ GET /hello
 === TEST 4: /not_found
 --- request
 GET /hello
---- yaml_config eval: $::yaml_config
 --- more_headers
 Host: not_found.com
 --- error_code: 404
@@ -116,7 +99,6 @@ Host: not_found.com
 === TEST 5: hit routes
 --- request
 GET /hello
---- yaml_config eval: $::yaml_config
 --- more_headers
 Host: foo.com
 --- response_body
@@ -151,7 +133,6 @@ hello world
             ngx.say(body)
         }
     }
---- yaml_config eval: $::yaml_config
 --- request
 GET /t
 --- response_body
@@ -164,7 +145,6 @@ passed
 === TEST 7: /not_found
 --- request
 GET /hello
---- yaml_config eval: $::yaml_config
 --- error_code: 404
 --- response_body
 {"error_msg":"failed to match any routes"}
@@ -176,7 +156,6 @@ GET /hello
 === TEST 8: hit routes
 --- request
 GET /server_port
---- yaml_config eval: $::yaml_config
 --- more_headers
 Host: anydomain.com
 --- response_body_like eval
@@ -211,7 +190,6 @@ qr/1981/
             ngx.say(body)
         }
     }
---- yaml_config eval: $::yaml_config
 --- request
 GET /t
 --- response_body
@@ -224,7 +202,6 @@ passed
 === TEST 10: /not_found
 --- request
 GET /hello2
---- yaml_config eval: $::yaml_config
 --- error_code: 404
 --- response_body
 {"error_msg":"failed to match any routes"}
@@ -236,7 +213,6 @@ GET /hello2
 === TEST 11: hit routes
 --- request
 GET /hello
---- yaml_config eval: $::yaml_config
 --- more_headers
 Host: anydomain.com
 --- response_body
@@ -261,7 +237,6 @@ hello world
             ngx.say(body)
         }
     }
---- yaml_config eval: $::yaml_config
 --- request
 GET /t
 --- response_body
@@ -295,7 +270,6 @@ passed
             ngx.say(body)
         }
     }
---- yaml_config eval: $::yaml_config
 --- request
 GET /t
 --- response_body
@@ -308,7 +282,6 @@ passed
 === TEST 14: hit route with /hello
 --- request
 GET /hello
---- yaml_config eval: $::yaml_config
 --- response_body
 hello world
 --- no_error_log
@@ -319,7 +292,6 @@ hello world
 === TEST 15: miss route
 --- request
 GET /hello/
---- yaml_config eval: $::yaml_config
 --- error_code: 404
 --- no_error_log
 [error]