blob: b9de45b627cccb719c4b873889df661deaca764e [file] [log] [blame]
#!/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])