| #!/usr/bin/env lua |
| |
| -- |
| -- 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 script_path = debug.getinfo(1).source:sub(2) |
| |
| local function trim(s) |
| return (s:gsub("^%s*(.-)%s*$", "%1")) |
| end |
| |
| local function excute_cmd(cmd) |
| local t = io.popen(cmd) |
| local data = t:read("*all") |
| t:close() |
| return data |
| end |
| |
| local pwd = trim(excute_cmd("pwd")) |
| if not pwd then |
| error("failed to fetch current path") |
| end |
| |
| excute_cmd("install -d -m 777 /tmp/apisix_cores/") |
| |
| local apisix_home = "/usr/local/apisix" |
| if script_path:sub(1, 17) == '/usr/local/apisix' or script_path:sub(1, 4) == '/bin' then |
| package.cpath = "/usr/local/apisix/deps/lib64/lua/5.1/?.so;" |
| .. "/usr/local/apisix/deps/lib/lua/5.1/?.so;" |
| .. package.cpath |
| |
| package.path = "/usr/local/apisix/deps/share/lua/5.1/apisix/lua/?.lua;" |
| .. "/usr/local/apisix/deps/share/lua/5.1/?.lua;" |
| .. "/usr/share/lua/5.1/apisix/lua/?.lua;" |
| .. "/usr/local/share/lua/5.1/apisix/lua/?.lua;" |
| .. package.path |
| |
| else |
| apisix_home = pwd |
| package.cpath = pwd .. "/deps/lib64/lua/5.1/?.so;" |
| .. package.cpath |
| |
| package.path = pwd .. "/lua/?.lua;" |
| .. pwd .. "/deps/share/lua/5.1/?.lua;" |
| .. package.path |
| end |
| -- print("apisix_home: ", apisix_home) |
| |
| do |
| -- skip luajit environment |
| local ok = pcall(require, "table.new") |
| if not ok then |
| local ok, json = pcall(require, "cjson") |
| if ok and json then |
| io.stderr:write("please remove the cjson library in Lua, it may " |
| .. "conflict with the cjson library in openresty. " |
| .. "\n luarocks remove cjson\n") |
| return |
| end |
| end |
| end |
| |
| local yaml = require("tinyyaml") |
| local template = require("resty.template") |
| |
| local ngx_tpl = [=[ |
| master_process on; |
| |
| worker_processes {* worker_processes *}; |
| {% if os_name == "Linux" then %} |
| worker_cpu_affinity auto; |
| {% end %} |
| |
| error_log {* error_log *} {* error_log_level or "error" *}; |
| pid logs/nginx.pid; |
| |
| worker_rlimit_nofile 20480; |
| |
| events { |
| accept_mutex off; |
| worker_connections {* event.worker_connections *}; |
| } |
| |
| worker_rlimit_core 500M; |
| working_directory /tmp/apisix_cores/; |
| |
| worker_shutdown_timeout 3; |
| |
| {% if stream_proxy then %} |
| stream { |
| lua_package_path "$prefix/deps/share/lua/5.1/?.lua;]=] |
| .. [=[{*apisix_lua_home*}/lua/?.lua;;{*lua_path*};"; |
| lua_package_cpath "$prefix/deps/lib64/lua/5.1/?.so;]=] |
| .. [=[$prefix/deps/lib/lua/5.1/?.so;;]=] |
| .. [=[{*lua_cpath*};"; |
| lua_socket_log_errors off; |
| |
| upstream apisix_backend { |
| server 127.0.0.1:80; |
| balancer_by_lua_block { |
| apisix.stream_balancer_phase() |
| } |
| } |
| |
| init_by_lua_block { |
| require "resty.core" |
| apisix = require("apisix") |
| apisix.stream_init() |
| } |
| |
| init_worker_by_lua_block { |
| apisix.stream_init_worker() |
| } |
| |
| server { |
| {% for _, port in ipairs(stream_proxy.tcp or {}) do %} |
| listen {*port*}; |
| {% end %} |
| {% for _, port in ipairs(stream_proxy.udp or {}) do %} |
| listen {*port*} udp; |
| {% end %} |
| |
| preread_by_lua_block { |
| apisix.stream_preread_phase() |
| } |
| |
| proxy_pass apisix_backend; |
| |
| log_by_lua_block { |
| apisix.stream_log_phase() |
| } |
| } |
| } |
| {% end %} |
| |
| http { |
| lua_package_path "$prefix/deps/share/lua/5.1/?.lua;]=] |
| .. [=[{*apisix_lua_home*}/lua/?.lua;;{*lua_path*};"; |
| lua_package_cpath "$prefix/deps/lib64/lua/5.1/?.so;]=] |
| .. [=[$prefix/deps/lib/lua/5.1/?.so;;]=] |
| .. [=[{*lua_cpath*};"; |
| |
| lua_shared_dict plugin-limit-req 10m; |
| lua_shared_dict plugin-limit-count 10m; |
| lua_shared_dict prometheus-metrics 10m; |
| lua_shared_dict plugin-limit-conn 10m; |
| lua_shared_dict upstream-healthcheck 10m; |
| lua_shared_dict worker-events 10m; |
| |
| # for openid-connect plugin |
| lua_shared_dict discovery 1m; # cache for discovery metadata documents |
| lua_shared_dict jwks 1m; # cache for JWKs |
| lua_shared_dict introspection 10m; # cache for JWT verification results |
| |
| lua_ssl_verify_depth 5; |
| ssl_session_timeout 86400; |
| |
| lua_socket_log_errors off; |
| |
| resolver {% for _, dns_addr in ipairs(dns_resolver or {}) do %} {*dns_addr*} {% end %} ipv6=off; |
| resolver_timeout 5; |
| |
| lua_http10_buffering off; |
| |
| lua_regex_match_limit 100000; |
| lua_regex_cache_max_entries 8192; |
| |
| log_format main '$remote_addr - $remote_user [$time_local] $http_host "$request" $status $body_bytes_sent $request_time "$http_referer" "$http_user_agent" $upstream_addr $upstream_status $upstream_response_time'; |
| |
| access_log {* http.access_log *} main buffer=32768 flush=3; |
| open_file_cache max=1000 inactive=60; |
| client_max_body_size 0; |
| keepalive_timeout {* http.keepalive_timeout *}; |
| client_header_timeout {* http.client_header_timeout *}; |
| client_body_timeout {* http.client_body_timeout *}; |
| send_timeout {* http.send_timeout *}; |
| |
| server_tokens off; |
| more_set_headers 'Server: APISIX web server'; |
| |
| include mime.types; |
| |
| real_ip_header {* real_ip_header *}; |
| {% for _, real_ip in ipairs(real_ip_from) do %} |
| set_real_ip_from {*real_ip*}; |
| {% end %} |
| |
| upstream apisix_backend { |
| server 0.0.0.1; |
| balancer_by_lua_block { |
| apisix.http_balancer_phase() |
| } |
| |
| keepalive 320; |
| } |
| |
| init_by_lua_block { |
| require "resty.core" |
| apisix = require("apisix") |
| apisix.http_init() |
| } |
| |
| init_worker_by_lua_block { |
| apisix.http_init_worker() |
| } |
| |
| {% if enable_admin and port_admin then %} |
| server { |
| listen {* port_admin *}; |
| |
| location /apisix/admin { |
| {%if allow_admin then%} |
| {% for _, allow_ip in ipairs(allow_admin) do %} |
| allow {*allow_ip*}; |
| {% end %} |
| deny all; |
| {%end%} |
| |
| content_by_lua_block { |
| apisix.http_admin() |
| } |
| } |
| |
| location /apisix/dashboard { |
| {%if allow_admin then%} |
| {% for _, allow_ip in ipairs(allow_admin) do %} |
| allow {*allow_ip*}; |
| {% end %} |
| deny all; |
| {%end%} |
| |
| alias dashboard/; |
| |
| try_files $uri $uri/index.html /index.html; |
| } |
| |
| location /robots.txt { |
| return 200 'User-agent: *\nDisallow: /'; |
| } |
| } |
| {% end %} |
| |
| server { |
| listen {* node_listen *}; |
| {% if ssl.enable then %} |
| listen {* ssl.listen_port *} ssl {% if ssl.enable_http2 then %} http2 {% end %}; |
| {% end %} |
| |
| {% if enable_ipv6 then %} |
| listen [::]:{* node_listen *}; |
| {% if ssl.enable then %} |
| listen [::]:{* node_ssl_listen *} ssl {% if ssl.enable_http2 then %} http2 {% end %}; |
| {% end %} |
| {% end %} {% -- if enable_ipv6 %} |
| |
| ssl_certificate cert/apisix.crt; |
| ssl_certificate_key cert/apisix.key; |
| ssl_session_cache shared:SSL:1m; |
| |
| ssl_protocols {* ssl.ssl_protocols *}; |
| ssl_ciphers {* ssl.ssl_ciphers *}; |
| ssl_prefer_server_ciphers on; |
| |
| {% if with_module_status then %} |
| location = /apisix/nginx_status { |
| allow 127.0.0.0/24; |
| deny all; |
| access_log off; |
| stub_status; |
| } |
| {% end %} |
| |
| {% if enable_admin and not port_admin then %} |
| location /apisix/admin { |
| {%if allow_admin then%} |
| {% for _, allow_ip in ipairs(allow_admin) do %} |
| allow {*allow_ip*}; |
| {% end %} |
| deny all; |
| {%end%} |
| |
| content_by_lua_block { |
| apisix.http_admin() |
| } |
| } |
| |
| location /apisix/dashboard { |
| {%if allow_admin then%} |
| {% for _, allow_ip in ipairs(allow_admin) do %} |
| allow {*allow_ip*}; |
| {% end %} |
| deny all; |
| {%end%} |
| |
| alias dashboard/; |
| |
| try_files $uri $uri/index.html /index.html; |
| } |
| {% end %} |
| |
| ssl_certificate_by_lua_block { |
| apisix.http_ssl_phase() |
| } |
| |
| location / { |
| set $upstream_scheme 'http'; |
| set $upstream_host $host; |
| set $upstream_upgrade ''; |
| set $upstream_connection ''; |
| set $upstream_uri ''; |
| |
| access_by_lua_block { |
| apisix.http_access_phase() |
| } |
| |
| proxy_http_version 1.1; |
| proxy_set_header Host $upstream_host; |
| proxy_set_header Upgrade $upstream_upgrade; |
| proxy_set_header Connection $upstream_connection; |
| proxy_set_header X-Real-IP $remote_addr; |
| proxy_pass_header Server; |
| proxy_pass_header Date; |
| proxy_pass $upstream_scheme://apisix_backend$upstream_uri; |
| |
| header_filter_by_lua_block { |
| apisix.http_header_filter_phase() |
| } |
| |
| body_filter_by_lua_block { |
| apisix.http_body_filter_phase() |
| } |
| |
| log_by_lua_block { |
| apisix.http_log_phase() |
| } |
| } |
| |
| location @grpc_pass { |
| |
| access_by_lua_block { |
| apisix.grpc_access_phase() |
| } |
| |
| grpc_set_header Content-Type application/grpc; |
| grpc_socket_keepalive on; |
| grpc_pass grpc://apisix_backend; |
| |
| header_filter_by_lua_block { |
| apisix.http_header_filter_phase() |
| } |
| |
| body_filter_by_lua_block { |
| apisix.http_body_filter_phase() |
| } |
| |
| log_by_lua_block { |
| apisix.http_log_phase() |
| } |
| } |
| } |
| } |
| ]=] |
| |
| local function write_file(file_path, data) |
| local file = io.open(file_path, "w+") |
| if not file then |
| return false, "failed to open file: " .. file_path |
| end |
| |
| file:write(data) |
| file:close() |
| return true |
| end |
| |
| local function read_file(file_path) |
| local file = io.open(file_path, "rb") |
| if not file then |
| return false, "failed to open file: " .. file_path |
| end |
| |
| local data = file:read("*all") |
| file:close() |
| return data |
| end |
| |
| local function exec(command) |
| local t= io.popen(command) |
| local res = t:read("*all") |
| t:close() |
| return trim(res) |
| end |
| |
| local function read_yaml_conf() |
| local ymal_conf, err = read_file(apisix_home .. "/conf/config.yaml") |
| if not ymal_conf then |
| return nil, err |
| end |
| |
| return yaml.parse(ymal_conf) |
| end |
| |
| local function get_openresty_version() |
| local str = "nginx version: openresty/" |
| local ret = excute_cmd("openresty -v 2>&1") |
| local pos = string.find(ret,str) |
| if pos then |
| return string.sub(ret, pos + string.len(str)) |
| end |
| |
| str = "nginx version: nginx/" |
| ret = excute_cmd("openresty -v 2>&1") |
| pos = string.find(ret, str) |
| if pos then |
| return string.sub(ret, pos + string.len(str)) |
| end |
| |
| return nil |
| end |
| |
| local function split(self, sep) |
| local sep, fields = sep or ":", {} |
| local pattern = string.format("([^%s]+)", sep) |
| self:gsub(pattern, function(c) fields[#fields + 1] = c end) |
| return fields |
| end |
| |
| local function check_or_version(cur_ver_s, need_ver_s) |
| local cur_vers = split(cur_ver_s, [[.]]) |
| local need_vers = split(need_ver_s, [[.]]) |
| local len = math.max(#cur_vers, #need_vers) |
| |
| for i = 1, len do |
| local cur_ver = tonumber(cur_vers[i]) or 0 |
| local need_ver = tonumber(need_vers[i]) or 0 |
| if cur_ver > need_ver then |
| return true |
| end |
| |
| if cur_ver < need_ver then |
| return false |
| end |
| end |
| |
| return true |
| end |
| |
| local _M = {version = 0.1} |
| |
| function _M.help() |
| print([[ |
| Usage: apisix [action] <argument> |
| |
| help: show this message, then exit |
| init: initialize the local nginx.conf |
| init_etcd: initialize the data of etcd |
| start: start the apisix server |
| stop: stop the apisix server |
| restart: restart the apisix server |
| reload: reload the apisix server |
| version: print the version of apisix |
| ]]) |
| end |
| |
| local function init() |
| -- read_yaml_conf |
| local yaml_conf, err = read_yaml_conf() |
| if not yaml_conf then |
| error("failed to read local yaml config of apisix: " .. err) |
| end |
| -- print("etcd: ", yaml_conf.etcd.host) |
| |
| local or_ver = excute_cmd("openresty -V 2>&1") |
| local with_module_status = true |
| if or_ver and not or_ver:find("http_stub_status_module", 1, true) then |
| io.stderr:write("'http_stub_status_module' module is missing in ", |
| "your openresty, please check it out. Without this ", |
| "module, there will be fewer monitoring indicators.\n") |
| with_module_status = false |
| end |
| |
| -- Using template.render |
| local sys_conf = { |
| lua_path = package.path, |
| lua_cpath = package.cpath, |
| os_name = exec("uname"), |
| apisix_lua_home = apisix_home, |
| with_module_status = with_module_status, |
| node_ssl_listen = 9443, -- default value |
| error_log = {level = "warn"}, |
| } |
| |
| if not yaml_conf.apisix then |
| error("failed to read `apisix` field from yaml file") |
| end |
| |
| if not yaml_conf.nginx_config then |
| error("failed to read `nginx_config` field from yaml file") |
| end |
| |
| for k,v in pairs(yaml_conf.apisix) do |
| sys_conf[k] = v |
| end |
| for k,v in pairs(yaml_conf.nginx_config) do |
| sys_conf[k] = v |
| end |
| |
| if(sys_conf["enable_dev_mode"] == true) then |
| sys_conf["worker_processes"] = 1 |
| else |
| sys_conf["worker_processes"] = "auto" |
| end |
| |
| local conf_render = template.compile(ngx_tpl) |
| local ngxconf = conf_render(sys_conf) |
| |
| local ok, err = write_file(apisix_home .. "/conf/nginx.conf", ngxconf) |
| if not ok then |
| error("failed to update nginx.conf: " .. err) |
| end |
| |
| local op_ver = get_openresty_version() |
| if op_ver == nil then |
| io.stderr:write("can not find openresty\n") |
| return |
| end |
| |
| local need_ver = "1.15.8" |
| if not check_or_version(op_ver, need_ver) then |
| io.stderr:write("openresty version must >=", need_ver, " current ", op_ver, "\n") |
| return |
| end |
| end |
| _M.init = init |
| |
| local function init_etcd(show_output) |
| -- read_yaml_conf |
| local yaml_conf, err = read_yaml_conf() |
| if not yaml_conf then |
| error("failed to read local yaml config of apisix: " .. err) |
| end |
| |
| if not yaml_conf.apisix then |
| error("failed to read `apisix` field from yaml file when init etcd") |
| end |
| |
| if yaml_conf.apisix.config_center ~= "etcd" then |
| return true |
| end |
| |
| if not yaml_conf.etcd then |
| error("failed to read `etcd` field from yaml file when init etcd") |
| end |
| |
| local etcd_conf = yaml_conf.etcd |
| local uri = etcd_conf.host .. "/v2/keys" .. (etcd_conf.prefix or "") |
| |
| local timeout = etcd_conf.timeout or 3 |
| |
| for _, dir_name in ipairs({"/routes", "/upstreams", "/services", |
| "/plugins", "/consumers", "/node_status", |
| "/ssl", "/global_rules", "/stream_routes", |
| "/proto"}) do |
| local cmd = "curl " .. uri .. dir_name |
| .. "?prev_exist=false -X PUT -d dir=true " |
| .. "--connect-timeout " .. timeout |
| .. " --max-time " .. timeout * 2 .. " --retry 1 2>&1" |
| |
| local res = exec(cmd) |
| if not res:find("index", 1, true) |
| and not res:find("createdIndex", 1, true) then |
| error(cmd .. "\n" .. res) |
| end |
| |
| if show_output then |
| print(cmd) |
| print(res) |
| end |
| end |
| end |
| _M.init_etcd = init_etcd |
| |
| local openresty_args = [[openresty -p ]] .. apisix_home .. [[ -c ]] |
| .. apisix_home .. [[/conf/nginx.conf]] |
| |
| function _M.start(...) |
| init(...) |
| init_etcd(...) |
| |
| local cmd = openresty_args |
| -- print(cmd) |
| os.execute(cmd) |
| end |
| |
| function _M.stop() |
| local cmd = openresty_args .. [[ -s stop]] |
| -- print(cmd) |
| os.execute(cmd) |
| end |
| |
| function _M.restart() |
| _M.stop() |
| _M.start() |
| end |
| |
| function _M.reload() |
| local test_cmd = openresty_args .. [[ -t -q ]] |
| if os.execute((test_cmd)) ~= 0 then |
| return |
| end |
| |
| local cmd = openresty_args .. [[ -s reload]] |
| -- print(cmd) |
| os.execute(cmd) |
| end |
| |
| function _M.version() |
| local ver = require("apisix.core.version") |
| print(ver['VERSION']) |
| end |
| |
| local cmd_action = arg[1] |
| if not cmd_action then |
| return _M.help() |
| end |
| |
| if not _M[cmd_action] then |
| print("invalid argument: ", cmd_action, "\n") |
| return |
| end |
| |
| _M[cmd_action](arg[2]) |