├── .gitignore ├── .settings └── org.eclipse.ldt.prefs ├── .travis.yml ├── scripts ├── start.sh ├── stop.sh ├── restart.sh └── debug.sh ├── .buildpath ├── conf ├── fake.conf ├── dyn.conf ├── nginx.conf └── api.conf ├── src ├── ngx_dynamic_upstream_lua.h ├── ngx_dynamic_upstream_lua_module.c ├── ngx_dynamic_upstream_stream_lua_module.c ├── ngx_dynamic_upstream_stream_lua.c └── ngx_dynamic_upstream_lua.c ├── .project ├── ChangeLog ├── tests.sh ├── config ├── LICENSE ├── t ├── up-down.t ├── list.t ├── add.t ├── update.t └── remove.t └── README.markdown /.gitignore: -------------------------------------------------------------------------------- 1 | servroot 2 | install 3 | download 4 | build 5 | tmp 6 | -------------------------------------------------------------------------------- /.settings/org.eclipse.ldt.prefs: -------------------------------------------------------------------------------- 1 | Grammar__default_id=lua-5.2 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | compiler: 4 | - gcc 5 | 6 | script: 7 | - ./build.sh 8 | -------------------------------------------------------------------------------- /scripts/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR=$(pwd) 4 | export LD_LIBRARY_PATH=$DIR/lib 5 | ./sbin/nginx -p $DIR -------------------------------------------------------------------------------- /scripts/stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR=$(pwd) 4 | export LD_LIBRARY_PATH=$DIR/lib 5 | ./sbin/nginx -s stop -p $DIR -------------------------------------------------------------------------------- /scripts/restart.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR=$(pwd) 4 | export LD_LIBRARY_PATH=$DIR/lib 5 | ./sbin/nginx -s reload -p $DIR -------------------------------------------------------------------------------- /.buildpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /scripts/debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ulimit -c unlimited 4 | DIR=$(pwd) 5 | export LD_LIBRARY_PATH=$DIR/lib 6 | ./sbin/nginx.debug -p $DIR 7 | -------------------------------------------------------------------------------- /conf/fake.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 9090; 3 | listen 9091; 4 | 5 | default_type text/plain; 6 | 7 | location / { 8 | return 200 'Hello world!'; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /conf/dyn.conf: -------------------------------------------------------------------------------- 1 | http { 2 | lua_package_path "/path/to/lua-ngx-dynamic-upstream-lua/lib/?.lua;;"; 3 | 4 | lua_socket_log_errors off; 5 | 6 | include fake.conf; 7 | include api.conf; 8 | } 9 | -------------------------------------------------------------------------------- /src/ngx_dynamic_upstream_lua.h: -------------------------------------------------------------------------------- 1 | #ifndef _ngx_dynamic_upstream_lua_h_ 2 | #define _ngx_dynamic_upstream_lua_h_ 3 | 4 | 5 | #include 6 | 7 | 8 | ngx_int_t 9 | ngx_http_dynamic_upstream_lua_init(ngx_conf_t *cf); 10 | 11 | 12 | #endif -------------------------------------------------------------------------------- /conf/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | 3 | pid logs/nginx.pid; 4 | 5 | error_log logs/error.log info; 6 | error_log logs/debug.log debug; 7 | 8 | events { 9 | use epoll; 10 | worker_connections 1024; 11 | multi_accept on; 12 | } 13 | 14 | include "dyn.conf"; -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | ngx_dynamic_upstream_lua 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.dltk.core.scriptbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.ldt.nature 16 | 17 | 18 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2.0.0 2 | 3 | 1) Remove: Healthcheck directives and lua healthcheck api removed. Use ngx_dynamic_healthcheck module. 4 | 2) Update: Upstreams without 'zone' directive are not supported. 5 | 6 | 1.1.0 7 | 8 | 03.01.2017 9 | 10 | 1) Fix: crash on try to get information from the upstream without backups peers. 11 | 2) New: check_request_body, check_response_body to stream upstreams. 12 | 3) New: disconnect_if_market_down stream upstream directive. 13 | 14 | 1.0.0 15 | 16 | Initial release -------------------------------------------------------------------------------- /tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR=$(pwd) 4 | nginx_fname=$(ls -1 $DIR/install/*.tar.gz) 5 | 6 | [ -d install/tmp ] || mkdir install/tmp 7 | tar zxf $nginx_fname -C install/tmp 8 | 9 | folder="$(ls -1 $DIR/install/tmp | grep nginx)" 10 | 11 | export PATH=$DIR/install/tmp/$folder/sbin:$PATH 12 | export LD_LIBRARY_PATH=$DIR/install/tmp/$folder/lib 13 | export LUA_PATH="$DIR/install/tmp/$folder/lib/?.lua" 14 | export LUA_CPATH="$DIR/install/tmp/$folder/lib/lua/5.1/?.so" 15 | 16 | ret=0 17 | 18 | for t in $(ls t/*.t) 19 | do 20 | echo "Tests : "$t 21 | prove $t 22 | if [ $? -ne 0 ]; then 23 | ret=$? 24 | fi 25 | done 26 | 27 | rm -rf t/servroot 28 | rm -rf install/tmp 29 | 30 | exit $ret -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name="ngx_http_dynamic_upstream_lua_module ngx_stream_dynamic_upstream_lua_module" 2 | 3 | HTTP_LUA_UPSTREAM_SRCS="$ngx_addon_dir/src/ngx_dynamic_upstream_lua.c \ 4 | $ngx_addon_dir/src/ngx_dynamic_upstream_lua_module.c \ 5 | $ngx_addon_dir/src/ngx_dynamic_upstream_stream_lua.c \ 6 | $ngx_addon_dir/src/ngx_dynamic_upstream_stream_lua_module.c" 7 | 8 | stream_lua_nginx_module_incs="$(echo $CORE_INCS | awk '{ 9 | match($0, /([^ ]*\/stream-lua-nginx-module\/src)/, arr) 10 | print arr[1] 11 | }')" 12 | CORE_INCS="$CORE_INCS $stream_lua_nginx_module_incs" 13 | 14 | if test -n "$ngx_module_link"; then 15 | ngx_module_type=HTTP 16 | ngx_module_name=$ngx_addon_name 17 | ngx_module_srcs="$HTTP_LUA_UPSTREAM_SRCS" 18 | 19 | . auto/module 20 | else 21 | HTTP_MODULES="$HTTP_MODULES $ngx_addon_name" 22 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $HTTP_LUA_UPSTREAM_SRCS" 23 | fi 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2016, Aleksey Konovkin 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /src/ngx_dynamic_upstream_lua_module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "ngx_http_lua_api.h" 5 | 6 | 7 | #include "ngx_dynamic_upstream_lua.h" 8 | 9 | 10 | ngx_module_t ngx_http_dynamic_upstream_lua_module; 11 | 12 | 13 | static ngx_int_t 14 | ngx_http_dynamic_upstream_lua_post_conf(ngx_conf_t *cf); 15 | 16 | 17 | static ngx_http_module_t ngx_http_dynamic_upstream_lua_ctx = { 18 | NULL, /* preconfiguration */ 19 | ngx_http_dynamic_upstream_lua_post_conf, /* postconfiguration */ 20 | NULL, /* create main */ 21 | NULL, /* init main */ 22 | NULL, /* create server */ 23 | NULL, /* merge server */ 24 | NULL, /* create location */ 25 | NULL /* merge location */ 26 | }; 27 | 28 | 29 | ngx_module_t ngx_http_dynamic_upstream_lua_module = { 30 | NGX_MODULE_V1, 31 | &ngx_http_dynamic_upstream_lua_ctx, /* module context */ 32 | NULL, /* module directives */ 33 | NGX_HTTP_MODULE, /* module type */ 34 | NULL, /* init master */ 35 | NULL, /* init module */ 36 | NULL, /* init process */ 37 | NULL, /* init thread */ 38 | NULL, /* exit thread */ 39 | NULL, /* exit process */ 40 | NULL, /* exit master */ 41 | NGX_MODULE_V1_PADDING 42 | }; 43 | 44 | extern int 45 | ngx_stream_dynamic_upstream_lua_create_module(lua_State *L); 46 | 47 | ngx_int_t 48 | ngx_http_dynamic_upstream_lua_post_conf(ngx_conf_t *cf) 49 | { 50 | if (ngx_http_dynamic_upstream_lua_init(cf) != NGX_OK) { 51 | return NGX_ERROR; 52 | } 53 | 54 | if (ngx_http_lua_add_package_preload(cf, "ngx.dynamic_upstream.stream", 55 | ngx_stream_dynamic_upstream_lua_create_module) != NGX_OK) { 56 | return NGX_ERROR; 57 | } 58 | 59 | return NGX_OK; 60 | } 61 | -------------------------------------------------------------------------------- /conf/api.conf: -------------------------------------------------------------------------------- 1 | upstream backend { 2 | zone backend 1m; 3 | server 127.0.0.1:9090; 4 | } 5 | 6 | server { 7 | listen 8888; 8 | 9 | default_type text/plain; 10 | 11 | # curl "http://localhost:8888/backend/add?peer=127.0.0.1:9091&primary=1&weight=1&max_fails=2&fail_timeout=30&max_conns=100" 12 | # curl "http://localhost:8888/backend/add?peer=127.0.0.1:9091&backup=1&weight=1&max_fails=2&fail_timeout=30&max_conns=100" 13 | location ~* ^/(.+)/add { 14 | set $upstream $1; 15 | content_by_lua_block { 16 | local upstream = require "ngx.dynamic_upstream" 17 | 18 | local peer = ngx.var.arg_peer 19 | local u = ngx.var.upstream 20 | 21 | local ok, err 22 | if ngx.var.arg_backup and ngx.var.arg_backup == 1 then 23 | ok, _, err = upstream.add_backup_peer(u, peer) 24 | else 25 | ok, _, err = upstream.add_primary_peer(u, peer) 26 | end 27 | 28 | if not ok then 29 | ngx.say("Failed to add peer " .. peer .. ": ", err) 30 | return 31 | end 32 | 33 | local peer_args = { 34 | weight = ngx.var.arg_weight or 1, 35 | max_fails = ngx.var.arg_max_fails or 2, 36 | fail_timeout = ngx.var.arg_fail_timeout or 5, 37 | max_conns = ngx.var.arg_max_conns or 1, 38 | down = 1 39 | } 40 | 41 | ok, _, err = upstream.update_peer(u, peer, peer_args) 42 | if not ok then 43 | ngx.say("Failed to update peer " .. peer .. " params, error: ", err) 44 | return 45 | end 46 | 47 | ngx.say("Added " .. peer .. " to " .. u .. " upstream") 48 | } 49 | } 50 | 51 | # remove peer 52 | # curl "http://localhost:8888/backend/remove?peer=127.0.0.1:9091" 53 | location ~* ^/(.+)/remove { 54 | set $upstream $1; 55 | content_by_lua_block { 56 | local upstream = require "ngx.dynamic_upstream" 57 | 58 | local peer = ngx.var.arg_peer 59 | local u = ngx.var.upstream 60 | 61 | local ok, _, err = upstream.remove_peer(u, peer) 62 | if not ok then 63 | ngx.say("Failed to remove peer " .. peer .. ": ", err) 64 | return 65 | end 66 | 67 | ngx.say("Removed " .. peer .. " from " .. u .. " upstream") 68 | } 69 | } 70 | 71 | # status page for all the peers: 72 | location = /status { 73 | content_by_lua_block { 74 | local upstream = require "ngx.dynamic_upstream" 75 | 76 | local ok, upstreams, err = upstream.get_upstreams() 77 | if not ok then 78 | ngx.say(err) 79 | ngx.exit(200) 80 | end 81 | 82 | local get_peers = function(u) 83 | local ok, peers, err = upstream.get_primary_peers(u) 84 | if not ok then 85 | ngx.say(err) 86 | ngx.exit(200) 87 | end 88 | 89 | local t = {} 90 | 91 | for _, peer in pairs(peers) 92 | do 93 | table.insert(t, peer) 94 | end 95 | 96 | ok, peers, err = upstream.get_backup_peers(u) 97 | if not ok then 98 | ngx.say(err) 99 | ngx.exit(200) 100 | end 101 | 102 | for _, peer in pairs(peers) 103 | do 104 | table.insert(t, peer) 105 | end 106 | 107 | return t 108 | end 109 | 110 | local tointeger = function (b) if b then return 1 else return 0 end end 111 | 112 | for _, u in pairs(upstreams) 113 | do 114 | ngx.say(u) 115 | 116 | for _, peer in pairs(get_peers(u)) 117 | do 118 | local status = "up" 119 | if peer.down ~= nil then 120 | status = "down" 121 | end 122 | 123 | ngx.say(" server " .. peer.name .. " backup=" .. tointeger(peer.backup) .. " weight=" .. peer.weight .. " max_conns=" .. peer.max_conns .. " max_fails=" .. peer.max_fails .. " fail_timeout=" .. peer.fail_timeout .. " status=" .. status) 124 | end 125 | end 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /t/up-down.t: -------------------------------------------------------------------------------- 1 | use Test::Nginx::Socket; 2 | use Test::Nginx::Socket::Lua::Stream; 3 | 4 | repeat_each(1); 5 | 6 | plan tests => repeat_each() * 2 * blocks(); 7 | 8 | run_tests(); 9 | 10 | __DATA__ 11 | 12 | === TEST 1: down primary peer 13 | --- http_config 14 | upstream backends { 15 | zone shm-backends 128k; 16 | server 127.0.0.1:6001; 17 | server 127.0.0.1:6002; 18 | } 19 | --- config 20 | location /test { 21 | content_by_lua_block { 22 | local upstream = require "ngx.dynamic_upstream" 23 | local ok, err = upstream.update_peer(ngx.var.arg_upstream, ngx.var.arg_peer, { down = 1 }) 24 | if not ok then 25 | ngx.say(err) 26 | ngx.exit(200) 27 | end 28 | local tointeger = function(b) if b then return 1 else return 0 end end 29 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 30 | if not ok then 31 | ngx.say(err) 32 | ngx.exit(200) 33 | end 34 | for _, peer in pairs(peers) 35 | do 36 | ngx.say(peer.name .. " down=" .. tointeger(peer.down)) 37 | end 38 | } 39 | } 40 | --- request 41 | GET /test?upstream=backends&peer=127.0.0.1:6002 42 | --- response_body 43 | 127.0.0.1:6001 down=0 44 | 127.0.0.1:6002 down=1 45 | 46 | 47 | === TEST 2: up primary peer 48 | --- http_config 49 | upstream backends { 50 | zone shm-backends 128k; 51 | server 127.0.0.1:6001 down; 52 | server 127.0.0.1:6002 down; 53 | } 54 | --- config 55 | location /test { 56 | content_by_lua_block { 57 | local upstream = require "ngx.dynamic_upstream" 58 | local ok, err = upstream.update_peer(ngx.var.arg_upstream, ngx.var.arg_peer, { down = 0 }) 59 | if not ok then 60 | ngx.say(err) 61 | ngx.exit(200) 62 | end 63 | local tointeger = function(b) if b then return 1 else return 0 end end 64 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 65 | if not ok then 66 | ngx.say(err) 67 | ngx.exit(200) 68 | end 69 | for _, peer in pairs(peers) 70 | do 71 | ngx.say(peer.name .. " down=" .. tointeger(peer.down)) 72 | end 73 | } 74 | } 75 | --- request 76 | GET /test?upstream=backends&peer=127.0.0.1:6001 77 | --- response_body 78 | 127.0.0.1:6001 down=0 79 | 127.0.0.1:6002 down=1 80 | 81 | 82 | === TEST 3: down primary peer 83 | --- stream_config 84 | upstream backends { 85 | zone shm-backends 128k; 86 | server 127.0.0.1:6001; 87 | server 127.0.0.1:6002; 88 | } 89 | --- stream_server_config 90 | proxy_pass backends; 91 | --- config 92 | location /test { 93 | content_by_lua_block { 94 | local upstream = require "ngx.dynamic_upstream.stream" 95 | local ok, err = upstream.update_peer(ngx.var.arg_upstream, ngx.var.arg_peer, { down = 1 }) 96 | if not ok then 97 | ngx.say(err) 98 | ngx.exit(200) 99 | end 100 | local tointeger = function(b) if b then return 1 else return 0 end end 101 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 102 | if not ok then 103 | ngx.say(err) 104 | ngx.exit(200) 105 | end 106 | for _, peer in pairs(peers) 107 | do 108 | ngx.say(peer.name .. " down=" .. tointeger(peer.down)) 109 | end 110 | } 111 | } 112 | --- request 113 | GET /test?upstream=backends&peer=127.0.0.1:6002 114 | --- response_body 115 | 127.0.0.1:6001 down=0 116 | 127.0.0.1:6002 down=1 117 | 118 | 119 | === TEST 3: up stream primary peer 120 | --- stream_config 121 | upstream backends { 122 | zone shm-backends 128k; 123 | server 127.0.0.1:6001 down; 124 | server 127.0.0.1:6002 down; 125 | } 126 | --- stream_server_config 127 | proxy_pass backends; 128 | --- config 129 | location /test { 130 | content_by_lua_block { 131 | local upstream = require "ngx.dynamic_upstream.stream" 132 | local ok, err = upstream.update_peer(ngx.var.arg_upstream, ngx.var.arg_peer, { down = 0 }) 133 | if not ok then 134 | ngx.say(err) 135 | ngx.exit(200) 136 | end 137 | local tointeger = function(b) if b then return 1 else return 0 end end 138 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 139 | if not ok then 140 | ngx.say(err) 141 | ngx.exit(200) 142 | end 143 | for _, peer in pairs(peers) 144 | do 145 | ngx.say(peer.name .. " down=" .. tointeger(peer.down)) 146 | end 147 | } 148 | } 149 | --- request 150 | GET /test?upstream=backends&peer=127.0.0.1:6001 151 | --- response_body 152 | 127.0.0.1:6001 down=0 153 | 127.0.0.1:6002 down=1 154 | -------------------------------------------------------------------------------- /t/list.t: -------------------------------------------------------------------------------- 1 | use Test::Nginx::Socket; 2 | use Test::Nginx::Socket::Lua::Stream; 3 | 4 | repeat_each(1); 5 | 6 | plan tests => repeat_each() * 2 * blocks(); 7 | 8 | run_tests(); 9 | 10 | __DATA__ 11 | 12 | === TEST 1: list upstreams 13 | --- http_config 14 | upstream backends1 { 15 | zone shm-backends1 128k; 16 | server 127.0.0.1:6001; 17 | } 18 | upstream backends2 { 19 | zone shm-backends2 128k; 20 | server 127.0.0.1:6002; 21 | } 22 | upstream backends3 { 23 | server 127.0.0.1:6003; 24 | } 25 | --- config 26 | location /test { 27 | content_by_lua_block { 28 | local upstream = require "ngx.dynamic_upstream" 29 | local ok, ups, err = upstream.get_upstreams() 30 | if not ok then 31 | ngx.say(err) 32 | ngx.exit(500) 33 | end 34 | for _, u in pairs(ups) 35 | do 36 | ngx.say(u) 37 | end 38 | } 39 | } 40 | --- request 41 | GET /test 42 | --- response_body 43 | backends1 44 | backends2 45 | backends3 46 | 47 | 48 | === TEST 2: list peers 49 | --- http_config 50 | upstream backends { 51 | zone shm-backends 128k; 52 | server 127.0.0.1:6001 weight=1 max_fails=2 max_conns=100 fail_timeout=10 down; 53 | server 127.0.0.1:6002 weight=1 max_fails=2 max_conns=100 fail_timeout=10; 54 | server 127.0.0.1:6003 weight=1 max_fails=1 max_conns=200 fail_timeout=10 backup; 55 | } 56 | --- config 57 | location /test { 58 | content_by_lua_block { 59 | local upstream = require "ngx.dynamic_upstream" 60 | local ok, pp, err = upstream.get_primary_peers(ngx.var.arg_upstream) 61 | if not ok then 62 | ngx.say(err) 63 | ngx.exit(500) 64 | end 65 | local ok, bp, err = upstream.get_backup_peers(ngx.var.arg_upstream) 66 | if not ok then 67 | ngx.say(err) 68 | ngx.exit(500) 69 | end 70 | local tointeger = function(b) if b then return 1 else return 0 end end 71 | for _, peer in pairs(pp) 72 | do 73 | ngx.say(peer.name .. ";" .. peer.weight .. ";" .. peer.max_fails .. ";" .. peer.max_conns .. ";" .. peer.fail_timeout .. ";" .. tointeger(peer.down)) 74 | end 75 | for _, peer in pairs(bp) 76 | do 77 | ngx.say(peer.name .. ";" .. peer.weight .. ";" .. peer.max_fails .. ";" .. peer.max_conns .. ";" .. peer.fail_timeout .. ";" .. tointeger (peer.down) .. ";backup") 78 | end 79 | } 80 | } 81 | --- request 82 | GET /test?upstream=backends 83 | --- response_body 84 | 127.0.0.1:6001;1;2;100;10;1 85 | 127.0.0.1:6002;1;2;100;10;0 86 | 127.0.0.1:6003;1;1;200;10;0;backup 87 | 88 | 89 | === TEST 3: list stream upstreams 90 | --- stream_config 91 | upstream backends1 { 92 | zone shm-backends1 128k; 93 | server 127.0.0.1:6001; 94 | } 95 | upstream backends2 { 96 | zone shm-backends2 128k; 97 | server 127.0.0.1:6002; 98 | } 99 | upstream backends3 { 100 | server 127.0.0.1:6003; 101 | } 102 | --- stream_server_config 103 | proxy_pass backends1; 104 | --- config 105 | location /test { 106 | content_by_lua_block { 107 | local upstream = require "ngx.dynamic_upstream.stream" 108 | local ok, ups, err = upstream.get_upstreams() 109 | if not ok then 110 | ngx.say(err) 111 | ngx.exit(500) 112 | end 113 | for _, u in pairs(ups) 114 | do 115 | ngx.say(u) 116 | end 117 | } 118 | } 119 | --- request 120 | GET /test 121 | --- response_body 122 | backends1 123 | backends2 124 | backends3 125 | 126 | === TEST 4: list stream peers 127 | --- stream_config 128 | upstream backends { 129 | zone shm-backends 128k; 130 | server 127.0.0.1:6001 weight=1 max_fails=2 max_conns=100 fail_timeout=10 down; 131 | server 127.0.0.1:6002 weight=1 max_fails=2 max_conns=100 fail_timeout=10; 132 | server 127.0.0.1:6003 weight=1 max_fails=1 max_conns=200 fail_timeout=10 backup; 133 | } 134 | --- stream_server_config 135 | proxy_pass backends; 136 | --- config 137 | location /test { 138 | content_by_lua_block { 139 | local upstream = require "ngx.dynamic_upstream.stream" 140 | local ok, pp, err = upstream.get_primary_peers(ngx.var.arg_upstream) 141 | if not ok then 142 | ngx.say(err) 143 | ngx.exit(500) 144 | end 145 | local ok, bp, err = upstream.get_backup_peers(ngx.var.arg_upstream) 146 | if not ok then 147 | ngx.say(err) 148 | ngx.exit(500) 149 | end 150 | local tointeger = function(b) if b then return 1 else return 0 end end 151 | for _, peer in pairs(pp) 152 | do 153 | ngx.say(peer.name .. ";" .. peer.weight .. ";" .. peer.max_fails .. ";" .. peer.max_conns .. ";" .. peer.fail_timeout .. ";" .. tointeger(peer.down)) 154 | end 155 | for _, peer in pairs(bp) 156 | do 157 | ngx.say(peer.name .. ";" .. peer.weight .. ";" .. peer.max_fails .. ";" .. peer.max_conns .. ";" .. peer.fail_timeout .. ";" .. tointeger (peer.down) .. ";backup") 158 | end 159 | } 160 | } 161 | --- request 162 | GET /test?upstream=backends 163 | --- response_body 164 | 127.0.0.1:6001;1;2;100;10;1 165 | 127.0.0.1:6002;1;2;100;10;0 166 | 127.0.0.1:6003;1;1;200;10;0;backup 167 | -------------------------------------------------------------------------------- /src/ngx_dynamic_upstream_stream_lua_module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "ngx_stream_lua_request.h" 5 | #include "ngx_stream_lua_api.h" 6 | 7 | 8 | ngx_module_t ngx_stream_dynamic_upstream_lua_module; 9 | 10 | 11 | static ngx_int_t 12 | ngx_stream_dynamic_upstream_lua_post_conf(ngx_conf_t *cf); 13 | 14 | static void * 15 | ngx_stream_dynamic_upstream_lua_create_srv_conf(ngx_conf_t *cf); 16 | 17 | 18 | static ngx_int_t ngx_stream_dynamic_upstream_write_filter 19 | (ngx_stream_session_t *s, ngx_chain_t *in, ngx_uint_t from_upstream); 20 | 21 | 22 | typedef struct { 23 | ngx_flag_t disconnect_backup; 24 | ngx_flag_t disconnect_down; 25 | ngx_flag_t disconnect_on_exiting; 26 | } ngx_stream_dynamic_upstream_lua_srv_conf_t; 27 | 28 | 29 | static char * 30 | ngx_stream_dynamic_upstream_lua_disconnect_backup_if_primary_up(ngx_conf_t *cf, 31 | ngx_command_t *cmd, void *conf); 32 | 33 | static char * 34 | ngx_stream_dynamic_upstream_lua_disconnect_if_market_down(ngx_conf_t *cf, 35 | ngx_command_t *cmd, void *conf); 36 | 37 | static char * 38 | ngx_stream_dynamic_upstream_lua_disconnect_on_exiting(ngx_conf_t *cf, 39 | ngx_command_t *cmd, void *conf); 40 | 41 | 42 | static ngx_command_t ngx_stream_dynamic_upstream_lua_commands[] = { 43 | 44 | { ngx_string("disconnect_backup_if_primary_up"), 45 | NGX_STREAM_UPS_CONF|NGX_CONF_NOARGS, 46 | ngx_stream_dynamic_upstream_lua_disconnect_backup_if_primary_up, 47 | NGX_STREAM_SRV_CONF_OFFSET, 48 | 0, 49 | NULL }, 50 | 51 | { ngx_string("disconnect_if_market_down"), 52 | NGX_STREAM_UPS_CONF|NGX_CONF_NOARGS, 53 | ngx_stream_dynamic_upstream_lua_disconnect_if_market_down, 54 | NGX_STREAM_SRV_CONF_OFFSET, 55 | 0, 56 | NULL }, 57 | 58 | { ngx_string("disconnect_on_exiting"), 59 | NGX_STREAM_UPS_CONF|NGX_CONF_NOARGS, 60 | ngx_stream_dynamic_upstream_lua_disconnect_on_exiting, 61 | NGX_STREAM_SRV_CONF_OFFSET, 62 | 0, 63 | NULL }, 64 | 65 | ngx_null_command 66 | 67 | }; 68 | 69 | 70 | static ngx_stream_module_t ngx_stream_dynamic_upstream_lua_ctx = { 71 | NULL, /* preconfiguration */ 72 | ngx_stream_dynamic_upstream_lua_post_conf, /* postconfiguration */ 73 | NULL, /* create main */ 74 | NULL, /* init main */ 75 | ngx_stream_dynamic_upstream_lua_create_srv_conf, /* create server */ 76 | NULL /* merge server */ 77 | }; 78 | 79 | 80 | ngx_module_t ngx_stream_dynamic_upstream_lua_module = { 81 | NGX_MODULE_V1, 82 | &ngx_stream_dynamic_upstream_lua_ctx, /* module context */ 83 | ngx_stream_dynamic_upstream_lua_commands, /* module directives */ 84 | NGX_STREAM_MODULE, /* module type */ 85 | NULL, /* init master */ 86 | NULL, /* init module */ 87 | NULL, /* init process */ 88 | NULL, /* init thread */ 89 | NULL, /* exit thread */ 90 | NULL, /* exit process */ 91 | NULL, /* exit master */ 92 | NGX_MODULE_V1_PADDING 93 | }; 94 | 95 | 96 | static ngx_stream_filter_pt 97 | ngx_stream_next_filter; 98 | 99 | 100 | static ngx_uint_t 101 | ngx_stream_dynamic_upstream_alive_primary(ngx_stream_upstream_rr_peers_t *peers, 102 | ngx_stream_upstream_rr_peer_t *current) 103 | { 104 | ngx_stream_upstream_rr_peer_t *peer; 105 | int alive = 0; 106 | 107 | for (peer = peers->peer; peer; peer = peer->next) { 108 | 109 | if (current == peer) 110 | return 0; 111 | 112 | alive = alive + (peer->down == 0 ? 1 : 0); 113 | } 114 | 115 | return alive; 116 | } 117 | 118 | 119 | static ngx_stream_upstream_rr_peer_t * 120 | ngx_stream_dynamic_upstream_get_peer(ngx_stream_upstream_rr_peers_t *primary, 121 | ngx_str_t *name) 122 | { 123 | ngx_stream_upstream_rr_peer_t *peer; 124 | ngx_stream_upstream_rr_peers_t *peers; 125 | 126 | for (peers = primary; peers; peers = peers->next) { 127 | 128 | for (peer = peers->peer; peer; peer = peer->next) { 129 | 130 | if (peer->name.len == name->len && 131 | ngx_strncmp(peer->name.data, name->data, name->len) == 0) { 132 | return peer; 133 | } 134 | } 135 | } 136 | 137 | return NULL; 138 | } 139 | 140 | 141 | static ngx_int_t 142 | ngx_have_upstream(ngx_stream_session_t *s) 143 | { 144 | return s->upstream && 145 | s->upstream->upstream && 146 | s->upstream->upstream->srv_conf && 147 | s->upstream->state && 148 | s->upstream->state->peer; 149 | } 150 | 151 | 152 | typedef struct { 153 | ngx_stream_upstream_rr_peer_t *peer; 154 | ngx_msec_t check_ms; 155 | } context_t; 156 | 157 | 158 | static ngx_int_t 159 | ngx_stream_dynamic_upstream_write_filter(ngx_stream_session_t *s, 160 | ngx_chain_t *in, ngx_uint_t from_upstream) 161 | { 162 | ngx_stream_dynamic_upstream_lua_srv_conf_t *ucscf; 163 | ngx_stream_upstream_srv_conf_t *uscf; 164 | ngx_stream_upstream_rr_peers_t *peers = NULL; 165 | context_t *ctx; 166 | 167 | if (!ngx_have_upstream(s)) 168 | goto skip; 169 | 170 | uscf = s->upstream->upstream; 171 | peers = uscf->peer.data; 172 | 173 | ngx_stream_upstream_rr_peers_rlock(peers); 174 | 175 | ctx = ngx_stream_get_module_ctx(s, ngx_stream_dynamic_upstream_lua_module); 176 | 177 | if (ctx == NULL) { 178 | ctx = ngx_pcalloc(s->connection->pool, sizeof(context_t)); 179 | if (ctx == NULL) 180 | goto skip; 181 | 182 | ngx_stream_set_ctx(s, ctx, ngx_stream_dynamic_upstream_lua_module); 183 | 184 | ctx->peer = ngx_stream_dynamic_upstream_get_peer(peers, 185 | s->upstream->state->peer); 186 | 187 | if (ctx->peer == NULL) 188 | goto skip; 189 | } 190 | 191 | if (ctx->peer == NULL) 192 | goto skip; 193 | 194 | if (ngx_current_msec - ctx->check_ms < 1000) 195 | goto skip; 196 | 197 | ucscf = ngx_stream_conf_upstream_srv_conf(uscf, 198 | ngx_stream_dynamic_upstream_lua_module); 199 | 200 | if (ucscf->disconnect_down && ctx->peer->down) { 201 | 202 | ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, 203 | "[disconnect_if_market_down] peer=%V upstream=%V", 204 | &ctx->peer->name, &uscf->host); 205 | 206 | ngx_stream_upstream_rr_peers_unlock(peers); 207 | return NGX_ERROR; 208 | } 209 | 210 | if (ucscf->disconnect_on_exiting 211 | && (ngx_exiting || ngx_quit || ngx_terminate)) { 212 | 213 | ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, 214 | "[disconnect_on_exiting] peer=%V upstream=%V", 215 | &ctx->peer->name, &uscf->host); 216 | 217 | ngx_stream_upstream_rr_peers_unlock(peers); 218 | return NGX_ERROR; 219 | } 220 | 221 | if (ucscf->disconnect_backup && 222 | ngx_stream_dynamic_upstream_alive_primary(peers, ctx->peer)) { 223 | 224 | ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, 225 | "[disconnect_backup_if_primary_up] peer=%V " 226 | "upstream=%V", &ctx->peer->name, &uscf->host); 227 | 228 | ngx_stream_upstream_rr_peers_unlock(peers); 229 | return NGX_ERROR; 230 | } 231 | 232 | ctx->check_ms = ngx_current_msec; 233 | 234 | skip: 235 | 236 | if (peers) 237 | ngx_stream_upstream_rr_peers_unlock(peers); 238 | 239 | if (ngx_stream_next_filter) 240 | return ngx_stream_next_filter(s, in, from_upstream); 241 | 242 | return NGX_OK; 243 | } 244 | 245 | 246 | extern int 247 | ngx_stream_dynamic_upstream_lua_create_module(lua_State *L); 248 | 249 | 250 | ngx_int_t 251 | ngx_stream_dynamic_upstream_lua_post_conf(ngx_conf_t *cf) 252 | { 253 | #ifndef NO_NGX_STREAM_LUA_MODULE 254 | if (ngx_stream_lua_add_package_preload(cf, "ngx.dynamic_upstream.stream", 255 | ngx_stream_dynamic_upstream_lua_create_module) != NGX_OK) 256 | return NGX_ERROR; 257 | #endif 258 | 259 | ngx_stream_next_filter = ngx_stream_top_filter; 260 | ngx_stream_top_filter = ngx_stream_dynamic_upstream_write_filter; 261 | 262 | return NGX_OK; 263 | } 264 | 265 | 266 | static void * 267 | ngx_stream_dynamic_upstream_lua_create_srv_conf(ngx_conf_t *cf) 268 | { 269 | ngx_stream_dynamic_upstream_lua_srv_conf_t *ucscf; 270 | 271 | ucscf = ngx_palloc(cf->pool, 272 | sizeof(ngx_stream_dynamic_upstream_lua_srv_conf_t)); 273 | if (ucscf == NULL) { 274 | return NULL; 275 | } 276 | 277 | ucscf->disconnect_backup = 0; 278 | ucscf->disconnect_down = 0; 279 | ucscf->disconnect_on_exiting = 0; 280 | 281 | return ucscf; 282 | } 283 | 284 | 285 | static char * 286 | ngx_stream_dynamic_upstream_lua_disconnect_backup_if_primary_up(ngx_conf_t *cf, 287 | ngx_command_t *cmd, void *conf) 288 | { 289 | ngx_stream_dynamic_upstream_lua_srv_conf_t *ucscf = conf; 290 | 291 | ucscf->disconnect_backup = 1; 292 | 293 | return NGX_CONF_OK; 294 | } 295 | 296 | 297 | static char * 298 | ngx_stream_dynamic_upstream_lua_disconnect_if_market_down(ngx_conf_t *cf, 299 | ngx_command_t *cmd, void *conf) 300 | { 301 | ngx_stream_dynamic_upstream_lua_srv_conf_t *ucscf = conf; 302 | 303 | ucscf->disconnect_down = 1; 304 | 305 | return NGX_CONF_OK; 306 | } 307 | 308 | 309 | static char * 310 | ngx_stream_dynamic_upstream_lua_disconnect_on_exiting(ngx_conf_t *cf, 311 | ngx_command_t *cmd, void *conf) 312 | { 313 | ngx_stream_dynamic_upstream_lua_srv_conf_t *ucscf = conf; 314 | 315 | ucscf->disconnect_on_exiting = 1; 316 | 317 | return NGX_CONF_OK; 318 | } 319 | -------------------------------------------------------------------------------- /t/add.t: -------------------------------------------------------------------------------- 1 | use Test::Nginx::Socket; 2 | use Test::Nginx::Socket::Lua::Stream; 3 | 4 | repeat_each(1); 5 | 6 | plan tests => repeat_each() * 2 * blocks(); 7 | 8 | run_tests(); 9 | 10 | __DATA__ 11 | 12 | === TEST 1: add primary peer 13 | --- http_config 14 | upstream backends { 15 | zone shm-backends 128k; 16 | server 127.0.0.1:6001; 17 | } 18 | --- config 19 | location /test { 20 | content_by_lua_block { 21 | local upstream = require "ngx.dynamic_upstream" 22 | local ok, _, err = upstream.add_primary_peer(ngx.var.arg_upstream, ngx.var.arg_peer) 23 | if not ok then 24 | ngx.say(err) 25 | ngx.exit(200) 26 | end 27 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 28 | if not ok then 29 | ngx.say(err) 30 | ngx.exit(200) 31 | end 32 | for _, peer in pairs(peers) 33 | do 34 | ngx.say(peer.name) 35 | end 36 | } 37 | } 38 | --- request 39 | GET /test?upstream=backends&peer=127.0.0.1:6666 40 | --- response_body 41 | 127.0.0.1:6666 42 | 127.0.0.1:6001 43 | 44 | 45 | === TEST 2: add primary server 46 | --- http_config 47 | upstream backends { 48 | zone shm-backends 128k; 49 | dns_update 1s; 50 | server 127.0.0.1:6001; 51 | } 52 | --- config 53 | location /test { 54 | content_by_lua_block { 55 | local upstream = require "ngx.dynamic_upstream" 56 | local ok, _, err = upstream.add_primary_peer(ngx.var.arg_upstream, ngx.var.arg_peer) 57 | if not ok then 58 | ngx.say(err) 59 | ngx.exit(200) 60 | end 61 | ngx.sleep(2) 62 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 63 | if not ok then 64 | ngx.say(err) 65 | ngx.exit(200) 66 | end 67 | for _, peer in pairs(peers) 68 | do 69 | ngx.say(peer.server) 70 | ngx.say(peer.name) 71 | end 72 | } 73 | } 74 | --- request 75 | GET /test?upstream=backends&peer=localhost4:6666 76 | --- response_body_like 77 | localhost4:6666 78 | 127.0.0.1:6666 79 | 127.0.0.1:6001 80 | 127.0.0.1:6001 81 | --- timeout: 3 82 | 83 | 84 | === TEST 3: add backup peer 85 | --- http_config 86 | upstream backends { 87 | zone shm-backends 128k; 88 | server 127.0.0.1:6001; 89 | } 90 | --- config 91 | location /test { 92 | content_by_lua_block { 93 | local upstream = require "ngx.dynamic_upstream" 94 | local ok, _, err = upstream.add_backup_peer(ngx.var.arg_upstream, ngx.var.arg_peer) 95 | if not ok then 96 | ngx.say(err) 97 | ngx.exit(200) 98 | end 99 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 100 | if not ok then 101 | ngx.say(err) 102 | ngx.exit(200) 103 | end 104 | for _, peer in pairs(peers) 105 | do 106 | ngx.say(peer.name) 107 | end 108 | local ok, peers, err = upstream.get_backup_peers(ngx.var.arg_upstream) 109 | if not ok then 110 | ngx.say(err) 111 | ngx.exit(200) 112 | end 113 | for _, peer in pairs(peers) 114 | do 115 | ngx.say(peer.name .. " backup") 116 | end 117 | } 118 | } 119 | --- request 120 | GET /test?upstream=backends&peer=127.0.0.1:6666 121 | --- response_body 122 | 127.0.0.1:6001 123 | 127.0.0.1:6666 backup 124 | 125 | 126 | === TEST 4: add backup server 127 | --- http_config 128 | upstream backends { 129 | zone shm-backends 128k; 130 | dns_update 1s; 131 | server 127.0.0.1:6001; 132 | } 133 | --- config 134 | location /test { 135 | content_by_lua_block { 136 | local upstream = require "ngx.dynamic_upstream" 137 | local ok, _, err = upstream.add_backup_peer(ngx.var.arg_upstream, ngx.var.arg_peer) 138 | if not ok then 139 | ngx.say(err) 140 | ngx.exit(200) 141 | end 142 | ngx.sleep(2) 143 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 144 | if not ok then 145 | ngx.say(err) 146 | ngx.exit(200) 147 | end 148 | for _, peer in pairs(peers) 149 | do 150 | ngx.say(peer.server) 151 | ngx.say(peer.name) 152 | end 153 | local ok, peers, err = upstream.get_backup_peers(ngx.var.arg_upstream) 154 | if not ok then 155 | ngx.say(err) 156 | ngx.exit(200) 157 | end 158 | for _, peer in pairs(peers) 159 | do 160 | ngx.say(peer.server .. " backup") 161 | ngx.say(peer.name .. " backup") 162 | end 163 | } 164 | } 165 | --- request 166 | GET /test?upstream=backends&peer=localhost4:6666 167 | --- response_body 168 | 127.0.0.1:6001 169 | 127.0.0.1:6001 170 | localhost4:6666 backup 171 | 127.0.0.1:6666 backup 172 | --- timeout: 3 173 | 174 | 175 | === TEST 5: add stream primary peer 176 | --- stream_config 177 | upstream backends { 178 | zone shm-backends 128k; 179 | server 127.0.0.1:6001; 180 | } 181 | --- stream_server_config 182 | proxy_pass backends; 183 | --- config 184 | location /test { 185 | content_by_lua_block { 186 | local upstream = require "ngx.dynamic_upstream.stream" 187 | local ok, _, err = upstream.add_primary_peer(ngx.var.arg_upstream, ngx.var.arg_peer) 188 | if not ok then 189 | ngx.say(err) 190 | ngx.exit(200) 191 | end 192 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 193 | if not ok then 194 | ngx.say(err) 195 | ngx.exit(200) 196 | end 197 | for _, peer in pairs(peers) 198 | do 199 | ngx.say(peer.name) 200 | end 201 | } 202 | } 203 | --- request 204 | GET /test?upstream=backends&peer=127.0.0.1:6666 205 | --- response_body 206 | 127.0.0.1:6666 207 | 127.0.0.1:6001 208 | 209 | 210 | === TEST 6: add stream primary server 211 | --- stream_config 212 | upstream backends { 213 | zone shm-backends 128k; 214 | dns_update 1s; 215 | server 127.0.0.1:6001; 216 | } 217 | --- stream_server_config 218 | proxy_pass backends; 219 | --- config 220 | location /test { 221 | content_by_lua_block { 222 | local upstream = require "ngx.dynamic_upstream.stream" 223 | local ok, _, err = upstream.add_primary_peer(ngx.var.arg_upstream, ngx.var.arg_peer) 224 | if not ok then 225 | ngx.say(err) 226 | ngx.exit(200) 227 | end 228 | ngx.sleep(2) 229 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 230 | if not ok then 231 | ngx.say(err) 232 | ngx.exit(200) 233 | end 234 | for _, peer in pairs(peers) 235 | do 236 | ngx.say(peer.server) 237 | ngx.say(peer.name) 238 | end 239 | } 240 | } 241 | --- request 242 | GET /test?upstream=backends&peer=localhost4:6666 243 | --- response_body 244 | localhost4:6666 245 | 127.0.0.1:6666 246 | 127.0.0.1:6001 247 | 127.0.0.1:6001 248 | --- timeout: 3 249 | 250 | 251 | === TEST 7: add stream backup peer 252 | --- stream_config 253 | upstream backends { 254 | zone shm-backends 128k; 255 | server 127.0.0.1:6001; 256 | } 257 | --- stream_server_config 258 | proxy_pass backends; 259 | --- config 260 | location /test { 261 | content_by_lua_block { 262 | local upstream = require "ngx.dynamic_upstream.stream" 263 | local ok, _, err = upstream.add_backup_peer(ngx.var.arg_upstream, ngx.var.arg_peer) 264 | if not ok then 265 | ngx.say(err) 266 | ngx.exit(200) 267 | end 268 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 269 | if not ok then 270 | ngx.say(err) 271 | ngx.exit(200) 272 | end 273 | for _, peer in pairs(peers) 274 | do 275 | ngx.say(peer.name) 276 | end 277 | local ok, peers, err = upstream.get_backup_peers(ngx.var.arg_upstream) 278 | if not ok then 279 | ngx.say(err) 280 | ngx.exit(200) 281 | end 282 | for _, peer in pairs(peers) 283 | do 284 | ngx.say(peer.name .. " backup") 285 | end 286 | } 287 | } 288 | --- request 289 | GET /test?upstream=backends&peer=127.0.0.1:6666 290 | --- response_body 291 | 127.0.0.1:6001 292 | 127.0.0.1:6666 backup 293 | 294 | 295 | === TEST 8: add stream backup server 296 | --- stream_config 297 | upstream backends { 298 | zone shm-backends 128k; 299 | dns_update 1s; 300 | server 127.0.0.1:6001; 301 | } 302 | --- stream_server_config 303 | proxy_pass backends; 304 | --- config 305 | location /test { 306 | content_by_lua_block { 307 | local upstream = require "ngx.dynamic_upstream.stream" 308 | local ok, _, err = upstream.add_backup_peer(ngx.var.arg_upstream, ngx.var.arg_peer) 309 | if not ok then 310 | ngx.say(err) 311 | ngx.exit(200) 312 | end 313 | ngx.sleep(2) 314 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 315 | if not ok then 316 | ngx.say(err) 317 | ngx.exit(200) 318 | end 319 | for _, peer in pairs(peers) 320 | do 321 | ngx.say(peer.server) 322 | ngx.say(peer.name) 323 | end 324 | local ok, peers, err = upstream.get_backup_peers(ngx.var.arg_upstream) 325 | if not ok then 326 | ngx.say(err) 327 | ngx.exit(200) 328 | end 329 | for _, peer in pairs(peers) 330 | do 331 | ngx.say(peer.server .. " backup") 332 | ngx.say(peer.name .. " backup") 333 | end 334 | } 335 | } 336 | --- request 337 | GET /test?upstream=backends&peer=localhost4:6666 338 | --- response_body 339 | 127.0.0.1:6001 340 | 127.0.0.1:6001 341 | localhost4:6666 backup 342 | 127.0.0.1:6666 backup 343 | --- timeout: 3 344 | 345 | -------------------------------------------------------------------------------- /t/update.t: -------------------------------------------------------------------------------- 1 | use Test::Nginx::Socket; 2 | use Test::Nginx::Socket::Lua::Stream; 3 | 4 | repeat_each(1); 5 | 6 | plan tests => repeat_each() * 2 * blocks(); 7 | 8 | run_tests(); 9 | 10 | __DATA__ 11 | 12 | === TEST 1: update primary peer 13 | --- http_config 14 | upstream backends { 15 | zone shm-backends 128k; 16 | server 127.0.0.1:6001 weight=1 max_fails=2 max_conns=100 fail_timeout=10 down; 17 | server 127.0.0.1:6002 weight=1 max_fails=2 max_conns=100 fail_timeout=10; 18 | server 127.0.0.1:6003 weight=1 max_fails=1 max_conns=200 fail_timeout=10 backup; 19 | } 20 | --- config 21 | location /test { 22 | content_by_lua_block { 23 | local upstream = require "ngx.dynamic_upstream" 24 | local ok, pp, err = upstream.get_primary_peers(ngx.var.arg_upstream) 25 | if not ok then 26 | ngx.say(err) 27 | ngx.exit(500) 28 | end 29 | local ok, bp, err = upstream.get_backup_peers(ngx.var.arg_upstream) 30 | if not ok then 31 | ngx.say(err) 32 | ngx.exit(500) 33 | end 34 | local tointeger = function(b) if b then return 1 else return 0 end end 35 | for _, peer in pairs(pp) 36 | do 37 | ngx.say(peer.name .. ";" .. peer.weight .. ";" .. peer.max_fails .. ";" .. peer.max_conns .. ";" .. peer.fail_timeout .. ";" .. tointeger(peer.down)) 38 | end 39 | for _, peer in pairs(bp) 40 | do 41 | ngx.say(peer.name .. ";" .. peer.weight .. ";" .. peer.max_fails .. ";" .. peer.max_conns .. ";" .. peer.fail_timeout .. ";" .. tointeger (peer.down) .. ";backup") 42 | end 43 | local ok, err = upstream.update_peer(ngx.var.arg_upstream, ngx.var.arg_peer, { 44 | max_fails=ngx.var.arg_max_fails, 45 | max_conns=ngx.var.arg_max_conns, 46 | fail_timeout=ngx.var.arg_fail_timeout, 47 | weight=ngx.var.arg_weight, 48 | down=ngx.var.arg_down 49 | }) 50 | local ok, pp, err = upstream.get_primary_peers(ngx.var.arg_upstream) 51 | if not ok then 52 | ngx.say(err) 53 | ngx.exit(500) 54 | end 55 | for _, peer in pairs(pp) 56 | do 57 | ngx.say(peer.name .. ";" .. peer.weight .. ";" .. peer.max_fails .. ";" .. peer.max_conns .. ";" .. peer.fail_timeout .. ";" .. tointeger(peer.down)) 58 | end 59 | } 60 | } 61 | --- request 62 | GET /test?upstream=backends&peer=127.0.0.1:6002&max_fails=99&max_conns=88&fail_timeout=77&weight=66&down=1 63 | --- response_body 64 | 127.0.0.1:6001;1;2;100;10;1 65 | 127.0.0.1:6002;1;2;100;10;0 66 | 127.0.0.1:6003;1;1;200;10;0;backup 67 | 127.0.0.1:6001;1;2;100;10;1 68 | 127.0.0.1:6002;66;99;88;77;1 69 | 70 | 71 | === TEST 2: update backup peer 72 | --- http_config 73 | upstream backends { 74 | zone shm-backends 128k; 75 | server 127.0.0.1:6001 weight=1 max_fails=2 max_conns=100 fail_timeout=10 down; 76 | server 127.0.0.1:6002 weight=1 max_fails=2 max_conns=100 fail_timeout=10; 77 | server 127.0.0.1:6003 weight=1 max_fails=1 max_conns=200 fail_timeout=10 backup; 78 | } 79 | --- config 80 | location /test { 81 | content_by_lua_block { 82 | local upstream = require "ngx.dynamic_upstream" 83 | local ok, pp, err = upstream.get_primary_peers(ngx.var.arg_upstream) 84 | if not ok then 85 | ngx.say(err) 86 | ngx.exit(500) 87 | end 88 | local ok, bp, err = upstream.get_backup_peers(ngx.var.arg_upstream) 89 | if not ok then 90 | ngx.say(err) 91 | ngx.exit(500) 92 | end 93 | local tointeger = function(b) if b then return 1 else return 0 end end 94 | for _, peer in pairs(pp) 95 | do 96 | ngx.say(peer.name .. ";" .. peer.weight .. ";" .. peer.max_fails .. ";" .. peer.max_conns .. ";" .. peer.fail_timeout .. ";" .. tointeger(peer.down)) 97 | end 98 | for _, peer in pairs(bp) 99 | do 100 | ngx.say(peer.name .. ";" .. peer.weight .. ";" .. peer.max_fails .. ";" .. peer.max_conns .. ";" .. peer.fail_timeout .. ";" .. tointeger (peer.down) .. ";backup") 101 | end 102 | local ok, err = upstream.update_peer(ngx.var.arg_upstream, ngx.var.arg_peer, { 103 | max_fails=ngx.var.arg_max_fails, 104 | max_conns=ngx.var.arg_max_conns, 105 | fail_timeout=ngx.var.arg_fail_timeout, 106 | weight=ngx.var.arg_weight, 107 | down=ngx.var.arg_down 108 | }) 109 | local ok, pp, err = upstream.get_backup_peers(ngx.var.arg_upstream) 110 | if not ok then 111 | ngx.say(err) 112 | ngx.exit(500) 113 | end 114 | for _, peer in pairs(pp) 115 | do 116 | ngx.say(peer.name .. ";" .. peer.weight .. ";" .. peer.max_fails .. ";" .. peer.max_conns .. ";" .. peer.fail_timeout .. ";" .. tointeger(peer.down) .. ";backup") 117 | end 118 | } 119 | } 120 | --- request 121 | GET /test?upstream=backends&peer=127.0.0.1:6003&max_fails=99&max_conns=88&fail_timeout=77&weight=66&down=1 122 | --- response_body 123 | 127.0.0.1:6001;1;2;100;10;1 124 | 127.0.0.1:6002;1;2;100;10;0 125 | 127.0.0.1:6003;1;1;200;10;0;backup 126 | 127.0.0.1:6003;66;99;88;77;1;backup 127 | 128 | 129 | === TEST 3: update stream primary peer 130 | --- stream_config 131 | upstream backends { 132 | zone shm-backends 128k; 133 | server 127.0.0.1:6001 weight=1 max_fails=2 max_conns=100 fail_timeout=10 down; 134 | server 127.0.0.1:6002 weight=1 max_fails=2 max_conns=100 fail_timeout=10; 135 | server 127.0.0.1:6003 weight=1 max_fails=1 max_conns=200 fail_timeout=10 backup; 136 | } 137 | --- stream_server_config 138 | proxy_pass backends; 139 | --- config 140 | location /test { 141 | content_by_lua_block { 142 | local upstream = require "ngx.dynamic_upstream.stream" 143 | local ok, pp, err = upstream.get_primary_peers(ngx.var.arg_upstream) 144 | if not ok then 145 | ngx.say(err) 146 | ngx.exit(500) 147 | end 148 | local ok, bp, err = upstream.get_backup_peers(ngx.var.arg_upstream) 149 | if not ok then 150 | ngx.say(err) 151 | ngx.exit(500) 152 | end 153 | local tointeger = function(b) if b then return 1 else return 0 end end 154 | for _, peer in pairs(pp) 155 | do 156 | ngx.say(peer.name .. ";" .. peer.weight .. ";" .. peer.max_fails .. ";" .. peer.max_conns .. ";" .. peer.fail_timeout .. ";" .. tointeger(peer.down)) 157 | end 158 | for _, peer in pairs(bp) 159 | do 160 | ngx.say(peer.name .. ";" .. peer.weight .. ";" .. peer.max_fails .. ";" .. peer.max_conns .. ";" .. peer.fail_timeout .. ";" .. tointeger (peer.down) .. ";backup") 161 | end 162 | local ok, err = upstream.update_peer(ngx.var.arg_upstream, ngx.var.arg_peer, { 163 | max_fails=ngx.var.arg_max_fails, 164 | max_conns=ngx.var.arg_max_conns, 165 | fail_timeout=ngx.var.arg_fail_timeout, 166 | weight=ngx.var.arg_weight, 167 | down=ngx.var.arg_down 168 | }) 169 | local ok, pp, err = upstream.get_primary_peers(ngx.var.arg_upstream) 170 | if not ok then 171 | ngx.say(err) 172 | ngx.exit(500) 173 | end 174 | for _, peer in pairs(pp) 175 | do 176 | ngx.say(peer.name .. ";" .. peer.weight .. ";" .. peer.max_fails .. ";" .. peer.max_conns .. ";" .. peer.fail_timeout .. ";" .. tointeger(peer.down)) 177 | end 178 | } 179 | } 180 | --- request 181 | GET /test?upstream=backends&peer=127.0.0.1:6002&max_fails=99&max_conns=88&fail_timeout=77&weight=66&down=1 182 | --- response_body 183 | 127.0.0.1:6001;1;2;100;10;1 184 | 127.0.0.1:6002;1;2;100;10;0 185 | 127.0.0.1:6003;1;1;200;10;0;backup 186 | 127.0.0.1:6001;1;2;100;10;1 187 | 127.0.0.1:6002;66;99;88;77;1 188 | 189 | 190 | === TEST 4: update backup peer 191 | --- stream_config 192 | upstream backends { 193 | zone shm-backends 128k; 194 | server 127.0.0.1:6001 weight=1 max_fails=2 max_conns=100 fail_timeout=10 down; 195 | server 127.0.0.1:6002 weight=1 max_fails=2 max_conns=100 fail_timeout=10; 196 | server 127.0.0.1:6003 weight=1 max_fails=1 max_conns=200 fail_timeout=10 backup; 197 | } 198 | --- stream_server_config 199 | proxy_pass backends; 200 | --- config 201 | location /test { 202 | content_by_lua_block { 203 | local upstream = require "ngx.dynamic_upstream.stream" 204 | local ok, pp, err = upstream.get_primary_peers(ngx.var.arg_upstream) 205 | if not ok then 206 | ngx.say(err) 207 | ngx.exit(500) 208 | end 209 | local ok, bp, err = upstream.get_backup_peers(ngx.var.arg_upstream) 210 | if not ok then 211 | ngx.say(err) 212 | ngx.exit(500) 213 | end 214 | local tointeger = function(b) if b then return 1 else return 0 end end 215 | for _, peer in pairs(pp) 216 | do 217 | ngx.say(peer.name .. ";" .. peer.weight .. ";" .. peer.max_fails .. ";" .. peer.max_conns .. ";" .. peer.fail_timeout .. ";" .. tointeger(peer.down)) 218 | end 219 | for _, peer in pairs(bp) 220 | do 221 | ngx.say(peer.name .. ";" .. peer.weight .. ";" .. peer.max_fails .. ";" .. peer.max_conns .. ";" .. peer.fail_timeout .. ";" .. tointeger (peer.down) .. ";backup") 222 | end 223 | local ok, err = upstream.update_peer(ngx.var.arg_upstream, ngx.var.arg_peer, { 224 | max_fails=ngx.var.arg_max_fails, 225 | max_conns=ngx.var.arg_max_conns, 226 | fail_timeout=ngx.var.arg_fail_timeout, 227 | weight=ngx.var.arg_weight, 228 | down=ngx.var.arg_down 229 | }) 230 | local ok, pp, err = upstream.get_backup_peers(ngx.var.arg_upstream) 231 | if not ok then 232 | ngx.say(err) 233 | ngx.exit(500) 234 | end 235 | for _, peer in pairs(pp) 236 | do 237 | ngx.say(peer.name .. ";" .. peer.weight .. ";" .. peer.max_fails .. ";" .. peer.max_conns .. ";" .. peer.fail_timeout .. ";" .. tointeger(peer.down) .. ";backup") 238 | end 239 | } 240 | } 241 | --- request 242 | GET /test?upstream=backends&peer=127.0.0.1:6003&max_fails=99&max_conns=88&fail_timeout=77&weight=66&down=1 243 | --- response_body 244 | 127.0.0.1:6001;1;2;100;10;1 245 | 127.0.0.1:6002;1;2;100;10;0 246 | 127.0.0.1:6003;1;1;200;10;0;backup 247 | 127.0.0.1:6003;66;99;88;77;1;backup 248 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Name 2 | ==== 3 | 4 | dynamic-upstream-module-lua - Lua bindings for dynamic-upstream-module 5 | 6 | This module supports http and stream upstream types. 7 | 8 | Healthcheck directives and lua api removed. Use [ngx_dynamic_healthcheck](https://github.com/ZigzagAK/ngx_dynamic_healthcheck) module. 9 | Upstreams without [zone](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#zone) directive are unsupported. 10 | 11 | Build status 12 | ====== 13 | [![Build Status](https://travis-ci.org/ZigzagAK/ngx_dynamic_upstream_lua.svg)](https://travis-ci.org/ZigzagAK/ngx_dynamic_upstream_lua) 14 | 15 | Table of Contents 16 | ================= 17 | 18 | * [Name](#name) 19 | * [Status](#status) 20 | * [Synopsis](#synopsis) 21 | * [Description](#description) 22 | * [Install](#install) 23 | * [Configuration directives](#configuration-directives) 24 | * [disconnect_backup_if_primary_up](#disconnect_backup_if_primary_up) 25 | * [disconnect_if_market_down](#disconnect_if_market_down) 26 | * [disconnect_on_exiting](#disconnect_on_exiting) 27 | * [Packages](#packages) 28 | * [Methods](#methods) 29 | * [get_upstreams](#get_upstreams) 30 | * [get_peers](#get_peers) 31 | * [get_primary_peers](#get_primary_peers) 32 | * [get_backup_peers](#get_backup_peers) 33 | * [set_peer_down](#set_peer_down) 34 | * [set_peer_up](#set_peer_up) 35 | * [add_primary_peer](#add_primary_peer) 36 | * [add_backup_peer](#add_backup_peer) 37 | * [remove_peer](#remove_peer) 38 | * [update_peer](#update_peer) 39 | * [current_upstream](#current_upstream) 40 | 41 | Dependencies 42 | ============ 43 | 44 | This module has several dependencies: 45 | * [dynamic-upstream-module](https://github.com/ZigzagAK/ngx_dynamic_upstream) 46 | 47 | Status 48 | ====== 49 | 50 | This library is production ready. 51 | 52 | Description 53 | =========== 54 | 55 | This module provides Lua bindings to manage content of nginx upstreams. 56 | 57 | [Back to TOC](#table-of-contents) 58 | 59 | Install 60 | ======= 61 | 62 | Build nginx. 63 | All dependencies are downloaded automaticaly. 64 | 65 | ``` 66 | git clone git@github.com:ZigzagAK/ngx_dynamic_upstream_lua.git 67 | cd ngx_dynamic_upstream_lua 68 | ./build.sh 69 | ``` 70 | 71 | Archive will be placed in the `install` folder after successful build. 72 | 73 | [Back to TOC](#table-of-contents) 74 | 75 | Configuration directives 76 | ======================== 77 | 78 | disconnect_backup_if_primary_up 79 | ------------------------------- 80 | * **syntax**: `disconnect_backup_if_primary_up` 81 | * **default**: `none` 82 | * **context**: `stream/upstream` 83 | 84 | Disconnect from backup peers when primary peers becomes available. 85 | 86 | disconnect_if_market_down 87 | ------------------------- 88 | * **syntax**: `disconnect_if_market_down` 89 | * **default**: `none` 90 | * **context**: `stream/upstream` 91 | 92 | Disconnect peers when server is market down by healthcheck. 93 | 94 | disconnect_on_exiting 95 | --------------------- 96 | * **syntax**: `disconnect_on_exiting` 97 | * **default**: `none` 98 | * **context**: `stream/upstream` 99 | 100 | Disconnect from upstream when nginx reloaded. 101 | 102 | [Back to TOC](#table-of-contents) 103 | 104 | Synopsis 105 | ======== 106 | 107 | ```nginx 108 | http { 109 | lua_package_path "/path/to/lua-ngx-dynamic-upstream-lua/lib/?.lua;;"; 110 | 111 | upstream backend { 112 | zone backend 1m; 113 | server 127.0.0.1:9090; 114 | } 115 | 116 | server { 117 | listen 8888; 118 | 119 | default_type text/plain; 120 | 121 | # curl "http://localhost:8888/backend/add?peer=127.0.0.1:9091&weight=1&max_fails=2&fail_timeout=30&max_conns=100" 122 | # curl "http://localhost:8888/backend/add?peer=127.0.0.1:9092&backup=1&weight=1&max_fails=2&fail_timeout=30&max_conns=100" 123 | location ~* ^/(.+)/add { 124 | set $upstream $1; 125 | content_by_lua_block { 126 | local upstream = require "ngx.dynamic_upstream" 127 | 128 | local peer = ngx.var.arg_peer 129 | local u = ngx.var.upstream 130 | 131 | local ok, err 132 | if ngx.var.arg_backup and ngx.var.arg_backup == 1 then 133 | ok, _, err = upstream.add_backup_peer(u, peer) 134 | else 135 | ok, _, err = upstream.add_primary_peer(u, peer) 136 | end 137 | 138 | if not ok then 139 | say("Failed to add peer " .. peer .. ": ", err) 140 | return 141 | end 142 | 143 | local peer_args = { 144 | weight = ngx.var.arg_weight or 1, 145 | max_fails = ngx.var.arg_max_fails or 2, 146 | fail_timeout = ngx.var.arg_fail_timeout or 5, 147 | max_conns = ngx.var.arg_max_conns or 1, 148 | down = 1 149 | } 150 | 151 | ok, _, err = upstream.update_peer(u, peer, peer_args) 152 | if not ok then 153 | ngx.say("Failed to update peer " .. peer .. " params, error: ", err) 154 | return 155 | end 156 | 157 | ngx.say("Added " .. peer .. " to " .. u .. " upstream") 158 | } 159 | } 160 | 161 | # remove peer 162 | # curl "http://localhost:8888/backend/remove?peer=127.0.0.1:9091" 163 | location ~* ^/(.+)/remove { 164 | set $upstream $1; 165 | content_by_lua_block { 166 | local upstream = require "ngx.dynamic_upstream" 167 | 168 | local peer = ngx.var.arg_peer 169 | local u = ngx.var.upstream 170 | 171 | local ok, _, err = upstream.remove_peer(u, peer) 172 | if not ok then 173 | ngx.say("Failed to remove peer " .. peer .. ": ", err) 174 | return 175 | end 176 | 177 | ngx.say("Removed " .. peer .. " from " .. u .. " upstream") 178 | } 179 | } 180 | 181 | # status page for all the peers: 182 | location = /status { 183 | content_by_lua_block { 184 | local upstream = require "ngx.dynamic_upstream" 185 | 186 | local ok, upstreams, err = upstream.get_upstreams() 187 | if not ok then 188 | ngx.say(err) 189 | ngx.exit(200) 190 | end 191 | 192 | local get_peers = function(u) 193 | local ok, peers, err = upstream.get_primary_peers(u) 194 | if not ok then 195 | ngx.say(err) 196 | ngx.exit(200) 197 | end 198 | 199 | local t = {} 200 | 201 | for _, peer in pairs(peers) 202 | do 203 | table.insert(t, peer) 204 | end 205 | 206 | ok, peers, err = upstream.get_backup_peers(u) 207 | if not ok then 208 | ngx.say(err) 209 | ngx.exit(200) 210 | end 211 | 212 | for _, peer in pairs(peers) 213 | do 214 | table.insert(t, peer) 215 | end 216 | 217 | return t 218 | end 219 | 220 | local tointeger = function (b) if b then return 1 else return 0 end end 221 | 222 | for _, u in pairs(upstreams) 223 | do 224 | ngx.say(u) 225 | 226 | for _, peer in pairs(get_peers(u)) 227 | do 228 | local status = "up" 229 | if peer.down ~= nil then 230 | status = "down" 231 | end 232 | 233 | ngx.say(" server " .. peer.name .. " backup=" .. tointeger(peer.backup) .. " weight=" .. peer.weight .. " max_conns=" .. peer.max_conns .. " max_fails=" .. peer.max_fails .. " fail_timeout=" .. peer.fail_timeout .. " status=" .. status) 234 | end 235 | end 236 | } 237 | } 238 | } 239 | } 240 | ``` 241 | [Back to TOC](#table-of-contents) 242 | 243 | Packages 244 | ======= 245 | 246 | Package `ngx.dynamic_upstream` is used for manipulation http upstreams. 247 | Package `ngx.dynamic_upstream.stream` is used for manipulation stream upstreams. 248 | 249 | Functionality of both packages are same. 250 | 251 | 252 | Methods 253 | ======= 254 | 255 | get_upstreams 256 | ------------- 257 | **syntax:** `ok, upstreams, error = dynamic_upstream.get_upstreams()` 258 | 259 | **context:** **_by_lua** 260 | 261 | Get table of upstreams. 262 | 263 | Returns true and lua table on success, or false and a string describing an error otherwise. 264 | 265 | 266 | get_peers 267 | ------------- 268 | **syntax:** `ok, servers, error = dynamic_upstream.get_peers(upstream)` 269 | 270 | **context:** **_by_lua** 271 | 272 | Get table of servers in the `upstream`. 273 | 274 | Returns true and lua table on success, or false and a string describing an error otherwise. 275 | 276 | 277 | get_primary_peers 278 | ------------- 279 | **syntax:** `ok, peers, error = dynamic_upstream.get_primary_peers(upstream)` 280 | 281 | **context:** **_by_lua** 282 | 283 | Get table of primary peers in the `upstream`. 284 | 285 | Returns true and lua table on success, or false and a string describing an error otherwise. 286 | 287 | 288 | get_backup_peers 289 | ------------- 290 | **syntax:** `ok, peers, error = dynamic_upstream.get_backup_peers(upstream)` 291 | 292 | **context:** **_by_lua** 293 | 294 | Get table of backup peers in the `upstream`. 295 | 296 | Returns true and lua table on success, or false and a string describing an error otherwise. 297 | 298 | 299 | set_peer_down 300 | ------------- 301 | **syntax:** `ok, _, error = dynamic_upstream.set_peer_down(upstream, peer)` 302 | 303 | **context:** **_by_lua** 304 | 305 | Go `peer` of the `upstream` to DOWN state. 306 | 307 | Returns true on success, or false and a string describing an error otherwise. 308 | 309 | 310 | set_peer_up 311 | ------------- 312 | **syntax:** `ok, _, error = dynamic_upstream.set_peer_up(upstream, peer)` 313 | 314 | **context:** **_by_lua** 315 | 316 | Go `peer` of the `upstream` to UP state. 317 | 318 | Returns true on success, or false and a string describing an error otherwise. 319 | 320 | 321 | add_primary_peer 322 | ------------- 323 | **syntax:** `ok, _, error = dynamic_upstream.add_primary_peer(upstream, peer)` 324 | 325 | **context:** **_by_lua** 326 | 327 | Add `peer` to the `upstream` as primary. 328 | 329 | Returns true on success, or false and a string describing an error otherwise. 330 | 331 | 332 | add_backup_peer 333 | ------------- 334 | **syntax:** `ok, _, error = dynamic_upstream.add_backup_peer(upstream, peer)` 335 | 336 | **context:** **_by_lua** 337 | 338 | Add `peer` to the `upstream` as backup. 339 | 340 | Returns true on success, or false and a string describing an error otherwise. 341 | 342 | 343 | remove_peer 344 | ------------- 345 | **syntax:** `ok, _, error = dynamic_upstream.remove_peer(upstream, peer)` 346 | 347 | **context:** **_by_lua** 348 | 349 | Remove `peer` from the `upstream`. 350 | 351 | Returns true on success, or false and a string describing an error otherwise. 352 | 353 | 354 | update_peer 355 | ----------- 356 | **syntax:** `ok, _, error = dynamic_upstream.update_peer(upstream, peer, { 357 | weight = N, 358 | max_fails = N, 359 | fail_timeout = N (seconds), 360 | max_conns = N, 361 | down = 0/1 362 | })` 363 | 364 | **context:** **_by_lua** 365 | 366 | Update `peer` attributes. 367 | 368 | Returns true on success, or false and a string describing an error otherwise. 369 | 370 | 371 | current_upstream 372 | ---------------- 373 | **syntax:** `ok, _, error = dynamic_upstream.current_upstream()` 374 | 375 | **context:** **_by_lua** 376 | 377 | Returns true and current upstream name on success, or false and a string describing an error otherwise. 378 | 379 | [Back to TOC](#table-of-contents) -------------------------------------------------------------------------------- /t/remove.t: -------------------------------------------------------------------------------- 1 | use Test::Nginx::Socket; 2 | use Test::Nginx::Socket::Lua::Stream; 3 | 4 | repeat_each(1); 5 | 6 | plan tests => repeat_each() * 2 * blocks(); 7 | 8 | run_tests(); 9 | 10 | __DATA__ 11 | 12 | === TEST 1: remove primary peer 13 | --- http_config 14 | upstream backends { 15 | zone shm-backends 128k; 16 | server 127.0.0.1:6001; 17 | server 127.0.0.1:6002; 18 | } 19 | --- config 20 | location /test { 21 | content_by_lua_block { 22 | local upstream = require "ngx.dynamic_upstream" 23 | local ok, _, err = upstream.remove_peer(ngx.var.arg_upstream, ngx.var.arg_peer) 24 | if not ok then 25 | ngx.say(err) 26 | ngx.exit(200) 27 | end 28 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 29 | if not ok then 30 | ngx.say(err) 31 | ngx.exit(200) 32 | end 33 | for _, peer in pairs(peers) 34 | do 35 | ngx.say(peer.name) 36 | end 37 | } 38 | } 39 | --- request 40 | GET /test?upstream=backends&peer=127.0.0.1:6002 41 | --- response_body_like 42 | 127.0.0.1:6001 43 | 44 | 45 | === TEST 2: remove primary server 46 | --- http_config 47 | upstream backends { 48 | zone shm-backends 128k; 49 | server 127.0.0.1:6001; 50 | server localhost4:6002; 51 | } 52 | --- config 53 | location /test { 54 | content_by_lua_block { 55 | local upstream = require "ngx.dynamic_upstream" 56 | local ok, _, err = upstream.remove_peer(ngx.var.arg_upstream, ngx.var.arg_peer) 57 | if not ok then 58 | ngx.say(err) 59 | ngx.exit(200) 60 | end 61 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 62 | if not ok then 63 | ngx.say(err) 64 | ngx.exit(200) 65 | end 66 | for _, peer in pairs(peers) 67 | do 68 | ngx.say(peer.name) 69 | end 70 | } 71 | } 72 | --- request 73 | GET /test?upstream=backends&peer=localhost4:6002 74 | --- response_body_like 75 | 127.0.0.1:6001 76 | 77 | 78 | === TEST 3: remove backup peer 79 | --- http_config 80 | upstream backends { 81 | zone shm-backends 128k; 82 | server 127.0.0.1:6001; 83 | server 127.0.0.1:6002 backup; 84 | server 127.0.0.1:6003 backup; 85 | } 86 | --- config 87 | location /test { 88 | content_by_lua_block { 89 | local upstream = require "ngx.dynamic_upstream" 90 | local ok, _, err = upstream.remove_peer(ngx.var.arg_upstream, ngx.var.arg_peer) 91 | if not ok then 92 | ngx.say(err) 93 | ngx.exit(200) 94 | end 95 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 96 | if not ok then 97 | ngx.say(err) 98 | ngx.exit(200) 99 | end 100 | for _, peer in pairs(peers) 101 | do 102 | ngx.say(peer.name) 103 | end 104 | local ok, peers, err = upstream.get_backup_peers(ngx.var.arg_upstream) 105 | if not ok then 106 | ngx.say(err) 107 | ngx.exit(200) 108 | end 109 | for _, peer in pairs(peers) 110 | do 111 | ngx.say(peer.name .. " backup") 112 | end 113 | } 114 | } 115 | --- request 116 | GET /test?upstream=backends&peer=127.0.0.1:6002 117 | --- response_body_like 118 | 127.0.0.1:6001 119 | 127.0.0.1:6003 backup 120 | 121 | 122 | === TEST 4: remove backup server 123 | --- http_config 124 | upstream backends { 125 | zone shm-backends 128k; 126 | server 127.0.0.1:6001; 127 | server localhost4:6002 backup; 128 | server 127.0.0.1:6003 backup; 129 | } 130 | --- config 131 | location /test { 132 | content_by_lua_block { 133 | local upstream = require "ngx.dynamic_upstream" 134 | local ok, _, err = upstream.remove_peer(ngx.var.arg_upstream, ngx.var.arg_peer) 135 | if not ok then 136 | ngx.say(err) 137 | ngx.exit(200) 138 | end 139 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 140 | if not ok then 141 | ngx.say(err) 142 | ngx.exit(200) 143 | end 144 | for _, peer in pairs(peers) 145 | do 146 | ngx.say(peer.name) 147 | end 148 | local ok, peers, err = upstream.get_backup_peers(ngx.var.arg_upstream) 149 | if not ok then 150 | ngx.say(err) 151 | ngx.exit(200) 152 | end 153 | for _, peer in pairs(peers) 154 | do 155 | ngx.say(peer.name .. " backup") 156 | end 157 | } 158 | } 159 | --- request 160 | GET /test?upstream=backends&peer=localhost4:6002 161 | --- response_body_like 162 | 127.0.0.1:6001 163 | 127.0.0.1:6003 backup 164 | 165 | 166 | === TEST 5: remove all backup peers 167 | --- http_config 168 | upstream backends { 169 | zone shm-backends 128k; 170 | server 127.0.0.1:6001; 171 | server 127.0.0.1:6002 backup; 172 | server 127.0.0.1:6003 backup; 173 | server 127.0.0.1:6004 backup; 174 | } 175 | --- config 176 | location /test { 177 | content_by_lua_block { 178 | local upstream = require "ngx.dynamic_upstream" 179 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 180 | if not ok then 181 | ngx.say(err) 182 | ngx.exit(200) 183 | end 184 | for _, peer in pairs(peers) 185 | do 186 | ngx.say(peer.name) 187 | end 188 | local ok, peers, err = upstream.get_backup_peers(ngx.var.arg_upstream) 189 | if not ok then 190 | ngx.say(err) 191 | ngx.exit(200) 192 | end 193 | for _, peer in pairs(peers) 194 | do 195 | ngx.say(peer.name .. " backup") 196 | local ok, _, err = upstream.remove_peer(ngx.var.arg_upstream, peer.name) 197 | if not ok then 198 | ngx.say(err) 199 | ngx.exit(200) 200 | end 201 | ngx.say("removed " .. peer.name) 202 | end 203 | local ok, peers, err = upstream.get_backup_peers(ngx.var.arg_upstream) 204 | if not ok then 205 | ngx.say(err) 206 | ngx.exit(200) 207 | end 208 | for _, peer in pairs(peers) 209 | do 210 | ngx.say(peer.name .. " backup") 211 | end 212 | } 213 | } 214 | --- request 215 | GET /test?upstream=backends 216 | --- response_body_like 217 | 127.0.0.1:6001 218 | 127.0.0.1:6002 backup 219 | removed 127.0.0.1:6002 220 | 127.0.0.1:6003 backup 221 | removed 127.0.0.1:6003 222 | 127.0.0.1:6004 backup 223 | removed 127.0.0.1:6004 224 | 225 | 226 | === TEST 6: remove stream primary peer 227 | --- stream_config 228 | upstream backends { 229 | zone shm-backends 128k; 230 | server 127.0.0.1:6001; 231 | server 127.0.0.1:6002; 232 | } 233 | --- stream_server_config 234 | proxy_pass backends; 235 | --- config 236 | location /test { 237 | content_by_lua_block { 238 | local upstream = require "ngx.dynamic_upstream.stream" 239 | local ok, _, err = upstream.remove_peer(ngx.var.arg_upstream, ngx.var.arg_peer) 240 | if not ok then 241 | ngx.say(err) 242 | ngx.exit(200) 243 | end 244 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 245 | if not ok then 246 | ngx.say(err) 247 | ngx.exit(200) 248 | end 249 | for _, peer in pairs(peers) 250 | do 251 | ngx.say(peer.name) 252 | end 253 | } 254 | } 255 | --- request 256 | GET /test?upstream=backends&peer=127.0.0.1:6002 257 | --- response_body_like 258 | 127.0.0.1:6001 259 | 260 | 261 | === TEST 7: remove stream primary server 262 | --- stream_config 263 | upstream backends { 264 | zone shm-backends 128k; 265 | server 127.0.0.1:6001; 266 | server localhost4:6002; 267 | } 268 | --- stream_server_config 269 | proxy_pass backends; 270 | --- config 271 | location /test { 272 | content_by_lua_block { 273 | local upstream = require "ngx.dynamic_upstream.stream" 274 | local ok, _, err = upstream.remove_peer(ngx.var.arg_upstream, ngx.var.arg_peer) 275 | if not ok then 276 | ngx.say(err) 277 | ngx.exit(200) 278 | end 279 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 280 | if not ok then 281 | ngx.say(err) 282 | ngx.exit(200) 283 | end 284 | for _, peer in pairs(peers) 285 | do 286 | ngx.say(peer.name) 287 | end 288 | } 289 | } 290 | --- request 291 | GET /test?upstream=backends&peer=localhost4:6002 292 | --- response_body_like 293 | 127.0.0.1:6001 294 | 295 | 296 | === TEST 8: remove backup peer 297 | --- stream_config 298 | upstream backends { 299 | zone shm-backends 128k; 300 | server 127.0.0.1:6001; 301 | server 127.0.0.1:6002 backup; 302 | server 127.0.0.1:6003 backup; 303 | } 304 | --- stream_server_config 305 | proxy_pass backends; 306 | --- config 307 | location /test { 308 | content_by_lua_block { 309 | local upstream = require "ngx.dynamic_upstream.stream" 310 | local ok, _, err = upstream.remove_peer(ngx.var.arg_upstream, ngx.var.arg_peer) 311 | if not ok then 312 | ngx.say(err) 313 | ngx.exit(200) 314 | end 315 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 316 | if not ok then 317 | ngx.say(err) 318 | ngx.exit(200) 319 | end 320 | for _, peer in pairs(peers) 321 | do 322 | ngx.say(peer.name) 323 | end 324 | local ok, peers, err = upstream.get_backup_peers(ngx.var.arg_upstream) 325 | if not ok then 326 | ngx.say(err) 327 | ngx.exit(200) 328 | end 329 | for _, peer in pairs(peers) 330 | do 331 | ngx.say(peer.name .. " backup") 332 | end 333 | } 334 | } 335 | --- request 336 | GET /test?upstream=backends&peer=127.0.0.1:6002 337 | --- response_body_like 338 | 127.0.0.1:6001 339 | 127.0.0.1:6003 backup 340 | 341 | 342 | === TEST 9: remove backup server 343 | --- stream_config 344 | upstream backends { 345 | zone shm-backends 128k; 346 | server 127.0.0.1:6001; 347 | server localhost4:6002 backup; 348 | server 127.0.0.1:6003 backup; 349 | } 350 | --- stream_server_config 351 | proxy_pass backends; 352 | --- config 353 | location /test { 354 | content_by_lua_block { 355 | local upstream = require "ngx.dynamic_upstream.stream" 356 | local ok, _, err = upstream.remove_peer(ngx.var.arg_upstream, ngx.var.arg_peer) 357 | if not ok then 358 | ngx.say(err) 359 | ngx.exit(200) 360 | end 361 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 362 | if not ok then 363 | ngx.say(err) 364 | ngx.exit(200) 365 | end 366 | for _, peer in pairs(peers) 367 | do 368 | ngx.say(peer.name) 369 | end 370 | local ok, peers, err = upstream.get_backup_peers(ngx.var.arg_upstream) 371 | if not ok then 372 | ngx.say(err) 373 | ngx.exit(200) 374 | end 375 | for _, peer in pairs(peers) 376 | do 377 | ngx.say(peer.name .. " backup") 378 | end 379 | } 380 | } 381 | --- request 382 | GET /test?upstream=backends&peer=localhost4:6002 383 | --- response_body_like 384 | 127.0.0.1:6001 385 | 127.0.0.1:6003 backup 386 | 387 | 388 | === TEST 10: remove all backup peers 389 | --- stream_config 390 | upstream backends { 391 | zone shm-backends 128k; 392 | server 127.0.0.1:6001; 393 | server 127.0.0.1:6002 backup; 394 | server 127.0.0.1:6003 backup; 395 | server 127.0.0.1:6004 backup; 396 | } 397 | --- stream_server_config 398 | proxy_pass backends; 399 | --- config 400 | location /test { 401 | content_by_lua_block { 402 | local upstream = require "ngx.dynamic_upstream.stream" 403 | local ok, peers, err = upstream.get_primary_peers(ngx.var.arg_upstream) 404 | if not ok then 405 | ngx.say(err) 406 | ngx.exit(200) 407 | end 408 | for _, peer in pairs(peers) 409 | do 410 | ngx.say(peer.name) 411 | end 412 | local ok, peers, err = upstream.get_backup_peers(ngx.var.arg_upstream) 413 | if not ok then 414 | ngx.say(err) 415 | ngx.exit(200) 416 | end 417 | for _, peer in pairs(peers) 418 | do 419 | ngx.say(peer.name .. " backup") 420 | local ok, _, err = upstream.remove_peer(ngx.var.arg_upstream, peer.name) 421 | if not ok then 422 | ngx.say(err) 423 | ngx.exit(200) 424 | end 425 | ngx.say("removed " .. peer.name) 426 | end 427 | local ok, peers, err = upstream.get_backup_peers(ngx.var.arg_upstream) 428 | if not ok then 429 | ngx.say(err) 430 | ngx.exit(200) 431 | end 432 | for _, peer in pairs(peers) 433 | do 434 | ngx.say(peer.name .. " backup") 435 | end 436 | } 437 | } 438 | --- request 439 | GET /test?upstream=backends 440 | --- response_body_like 441 | 127.0.0.1:6001 442 | 127.0.0.1:6002 backup 443 | removed 127.0.0.1:6002 444 | 127.0.0.1:6003 backup 445 | removed 127.0.0.1:6003 446 | 127.0.0.1:6004 backup 447 | removed 127.0.0.1:6004 448 | -------------------------------------------------------------------------------- /src/ngx_dynamic_upstream_stream_lua.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "ngx_stream_lua_request.h" 8 | #include "ngx_http_lua_api.h" 9 | 10 | 11 | #include "ngx_dynamic_upstream_module.h" 12 | 13 | 14 | extern ngx_module_t ngx_stream_dynamic_upstream_lua_module; 15 | 16 | 17 | static int 18 | ngx_stream_dynamic_upstream_lua_get_upstreams(lua_State *L); 19 | static int 20 | ngx_stream_dynamic_upstream_lua_get_peers(lua_State *L); 21 | static int 22 | ngx_stream_dynamic_upstream_lua_get_peers_locked(lua_State *L); 23 | static int 24 | ngx_stream_dynamic_upstream_lua_get_primary_peers(lua_State *L); 25 | static int 26 | ngx_stream_dynamic_upstream_lua_get_backup_peers(lua_State *L); 27 | static int 28 | ngx_stream_dynamic_upstream_lua_set_peer_down(lua_State *L); 29 | static int 30 | ngx_stream_dynamic_upstream_lua_set_peer_up(lua_State *L); 31 | static int 32 | ngx_stream_dynamic_upstream_lua_add_primary_peer(lua_State *L); 33 | static int 34 | ngx_stream_dynamic_upstream_lua_add_backup_peer(lua_State *L); 35 | static int 36 | ngx_stream_dynamic_upstream_lua_remove_peer(lua_State *L); 37 | static int 38 | ngx_stream_dynamic_upstream_lua_update_peer(lua_State *L); 39 | 40 | 41 | static ngx_stream_upstream_main_conf_t * 42 | ngx_stream_lua_upstream_get_upstream_main_conf(); 43 | 44 | 45 | int 46 | ngx_stream_dynamic_upstream_lua_create_module(lua_State *L) 47 | { 48 | lua_newtable(L); 49 | 50 | lua_pushcfunction(L, ngx_stream_dynamic_upstream_lua_get_upstreams); 51 | lua_setfield(L, -2, "get_upstreams"); 52 | 53 | lua_pushcfunction(L, ngx_stream_dynamic_upstream_lua_get_peers); 54 | lua_setfield(L, -2, "get_peers"); 55 | 56 | lua_pushcfunction(L, ngx_stream_dynamic_upstream_lua_get_peers_locked); 57 | lua_setfield(L, -2, "get_peers_locked"); 58 | 59 | lua_pushcfunction(L, ngx_stream_dynamic_upstream_lua_get_primary_peers); 60 | lua_setfield(L, -2, "get_primary_peers"); 61 | 62 | lua_pushcfunction(L, ngx_stream_dynamic_upstream_lua_get_backup_peers); 63 | lua_setfield(L, -2, "get_backup_peers"); 64 | 65 | lua_pushcfunction(L, ngx_stream_dynamic_upstream_lua_set_peer_down); 66 | lua_setfield(L, -2, "set_peer_down"); 67 | 68 | lua_pushcfunction(L, ngx_stream_dynamic_upstream_lua_set_peer_up); 69 | lua_setfield(L, -2, "set_peer_up"); 70 | 71 | lua_pushcfunction(L, ngx_stream_dynamic_upstream_lua_add_primary_peer); 72 | lua_setfield(L, -2, "add_primary_peer"); 73 | 74 | lua_pushcfunction(L, ngx_stream_dynamic_upstream_lua_add_backup_peer); 75 | lua_setfield(L, -2, "add_backup_peer"); 76 | 77 | lua_pushcfunction(L, ngx_stream_dynamic_upstream_lua_remove_peer); 78 | lua_setfield(L, -2, "remove_peer"); 79 | 80 | lua_pushcfunction(L, ngx_stream_dynamic_upstream_lua_update_peer); 81 | lua_setfield(L, -2, "update_peer"); 82 | 83 | return 1; 84 | } 85 | 86 | 87 | static ngx_stream_upstream_main_conf_t * 88 | ngx_stream_lua_upstream_get_upstream_main_conf() 89 | { 90 | return ngx_stream_cycle_get_module_main_conf(ngx_cycle, 91 | ngx_stream_upstream_module); 92 | } 93 | 94 | 95 | static ngx_stream_upstream_srv_conf_t * 96 | ngx_dynamic_upstream_get(lua_State *L, ngx_dynamic_upstream_op_t *op) 97 | { 98 | ngx_uint_t i; 99 | ngx_stream_upstream_srv_conf_t *uscf, **uscfp; 100 | ngx_stream_upstream_main_conf_t *umcf; 101 | 102 | umcf = ngx_stream_lua_upstream_get_upstream_main_conf(); 103 | if (umcf == NULL) { 104 | return NULL; 105 | } 106 | uscfp = umcf->upstreams.elts; 107 | 108 | for (i = 0; i < umcf->upstreams.nelts; i++) { 109 | uscf = uscfp[i]; 110 | if (op->upstream.len == uscf->host.len && 111 | ngx_strncmp(uscf->host.data, op->upstream.data, 112 | op->upstream.len) == 0) { 113 | return uscf; 114 | } 115 | } 116 | 117 | return NULL; 118 | } 119 | 120 | 121 | static const int PRIMARY = 1; 122 | static const int BACKUP = 2; 123 | static const int LOCK = 4; 124 | 125 | 126 | static void 127 | ngx_dynamic_upstream_lua_create_response(ngx_stream_upstream_rr_peers_t *primary, 128 | lua_State *L, int flags) 129 | { 130 | ngx_stream_upstream_rr_peer_t *peer; 131 | ngx_stream_upstream_rr_peers_t *peers, *backup; 132 | int i = 1; 133 | 134 | backup = primary->next; 135 | 136 | if (flags & LOCK) { 137 | ngx_stream_upstream_rr_peers_rlock(primary); 138 | } 139 | 140 | lua_newtable(L); 141 | 142 | for (peers = primary; peers; peers = peers->next) { 143 | 144 | if ( (flags & PRIMARY && peers == primary) 145 | || (flags & BACKUP && peers == backup) ) { 146 | 147 | for (peer = peers->peer; peer; peer = peer->next, ++i) { 148 | lua_newtable(L); 149 | 150 | lua_pushlstring(L, (char *) peer->server.data, 151 | peer->server.len); 152 | lua_setfield(L, -2, "server"); 153 | 154 | lua_pushlstring(L, (char *) peer->name.data, 155 | peer->name.len); 156 | lua_setfield(L, -2, "name"); 157 | 158 | lua_pushinteger(L, (lua_Integer) peer->weight); 159 | lua_setfield(L, -2, "weight"); 160 | 161 | lua_pushinteger(L, (lua_Integer) peer->max_conns); 162 | lua_setfield(L, -2, "max_conns"); 163 | 164 | lua_pushinteger(L, (lua_Integer) peer->conns); 165 | lua_setfield(L, -2, "conns"); 166 | 167 | lua_pushinteger(L, (lua_Integer) peer->max_fails); 168 | lua_setfield(L, -2, "max_fails"); 169 | 170 | lua_pushinteger(L, (lua_Integer) peer->fail_timeout); 171 | lua_setfield(L, -2, "fail_timeout"); 172 | 173 | lua_pushboolean(L, peers != primary); 174 | lua_setfield(L, -2, "backup"); 175 | 176 | if (peer->down) { 177 | lua_pushboolean(L, 1); 178 | lua_setfield(L, -2, "down"); 179 | } 180 | 181 | lua_rawseti(L, -2, i); 182 | } 183 | } 184 | } 185 | 186 | if (flags & LOCK) { 187 | ngx_stream_upstream_rr_peers_unlock(primary); 188 | } 189 | } 190 | 191 | 192 | static void 193 | ngx_stream_dynamic_upstream_lua_op_defaults(lua_State *L, 194 | ngx_dynamic_upstream_op_t *op, int operation) 195 | { 196 | ngx_memzero(op, sizeof(ngx_dynamic_upstream_op_t)); 197 | 198 | op->op = operation; 199 | 200 | op->status = NGX_HTTP_OK; 201 | ngx_str_null(&op->upstream); 202 | op->weight = 1; 203 | op->max_fails = 1; 204 | op->fail_timeout = 10; 205 | op->verbose = 0; 206 | op->backup = 0; 207 | op->op_param = NGX_DYNAMIC_UPSTEAM_OP_PARAM_STREAM; 208 | op->op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_RESOLVE; 209 | 210 | op->upstream.data = (u_char *) luaL_checklstring(L, 1, &op->upstream.len); 211 | } 212 | 213 | 214 | static int 215 | ngx_stream_dynamic_upstream_lua_error(lua_State *L, const char *error) 216 | { 217 | lua_pushboolean(L, 0); 218 | lua_pushnil(L); 219 | lua_pushlstring(L, error, strlen(error)); 220 | return 3; 221 | } 222 | 223 | 224 | static int 225 | ngx_stream_dynamic_upstream_lua_op(lua_State *L, ngx_dynamic_upstream_op_t *op, 226 | int flags) 227 | { 228 | ngx_int_t rc; 229 | ngx_stream_upstream_srv_conf_t *uscf; 230 | ngx_stream_upstream_rr_peers_t *primary; 231 | 232 | uscf = ngx_dynamic_upstream_get(L, op); 233 | if (uscf == NULL) { 234 | return ngx_stream_dynamic_upstream_lua_error(L, "upstream not found"); 235 | } 236 | 237 | if (op->op != NGX_DYNAMIC_UPSTEAM_OP_LIST) { 238 | if (flags & LOCK) { 239 | rc = ngx_dynamic_upstream_stream_op(ngx_http_lua_get_request(L)-> 240 | connection->log, op, uscf); 241 | if (rc != NGX_OK && rc != NGX_AGAIN) { 242 | return ngx_stream_dynamic_upstream_lua_error(L, 243 | op->err); 244 | } 245 | } else { 246 | return ngx_stream_dynamic_upstream_lua_error(L, 247 | "locked operations can be used only with readonly functions"); 248 | } 249 | } 250 | 251 | lua_pushboolean(L, 1); 252 | 253 | if (op->verbose) { 254 | primary = uscf->peer.data; 255 | ngx_dynamic_upstream_lua_create_response(primary, L, flags); 256 | } else { 257 | lua_pushnil(L); 258 | } 259 | 260 | lua_pushnil(L); 261 | 262 | return 3; 263 | } 264 | 265 | 266 | static int 267 | ngx_stream_dynamic_upstream_lua_get_upstreams(lua_State *L) 268 | { 269 | ngx_uint_t i, j, count = 0; 270 | ngx_stream_upstream_srv_conf_t **uscfp, *uscf; 271 | ngx_stream_upstream_main_conf_t *umcf; 272 | 273 | if (lua_gettop(L) != 0) { 274 | return ngx_stream_dynamic_upstream_lua_error(L, "no argument expected"); 275 | } 276 | 277 | umcf = ngx_stream_lua_upstream_get_upstream_main_conf(); 278 | if (umcf == NULL) { 279 | lua_pushboolean(L, 1); 280 | lua_newtable(L); 281 | return 2; 282 | } 283 | 284 | uscfp = umcf->upstreams.elts; 285 | 286 | lua_pushboolean(L, 1); 287 | 288 | for (i = 0; i < umcf->upstreams.nelts; i++) { 289 | uscf = uscfp[i]; 290 | if (uscf->srv_conf != NULL) { 291 | ++count; 292 | } 293 | } 294 | 295 | lua_newtable(L); 296 | 297 | umcf = ngx_stream_lua_upstream_get_upstream_main_conf(); 298 | uscfp = umcf->upstreams.elts; 299 | 300 | for (i = 0, j = 1; i < umcf->upstreams.nelts; i++) { 301 | uscf = uscfp[i]; 302 | if (uscf->srv_conf != NULL) { 303 | lua_pushlstring(L, (char *) uscf->host.data, uscf->host.len); 304 | lua_rawseti(L, -2, j++); 305 | } 306 | } 307 | 308 | return 2; 309 | } 310 | 311 | 312 | static int 313 | ngx_stream_dynamic_upstream_lua_get_peers(lua_State *L) 314 | { 315 | ngx_dynamic_upstream_op_t op; 316 | if (lua_gettop(L) != 1) { 317 | return ngx_stream_dynamic_upstream_lua_error(L, 318 | "exactly one argument expected"); 319 | } 320 | ngx_stream_dynamic_upstream_lua_op_defaults(L, &op, 321 | NGX_DYNAMIC_UPSTEAM_OP_LIST); 322 | op.verbose = 1; 323 | return ngx_stream_dynamic_upstream_lua_op(L, &op, PRIMARY|BACKUP|LOCK); 324 | } 325 | 326 | 327 | static int 328 | ngx_stream_dynamic_upstream_lua_get_peers_locked(lua_State *L) 329 | { 330 | ngx_dynamic_upstream_op_t op; 331 | if (lua_gettop(L) != 1) { 332 | return ngx_stream_dynamic_upstream_lua_error(L, 333 | "exactly one argument expected"); 334 | } 335 | ngx_stream_dynamic_upstream_lua_op_defaults(L, &op, 336 | NGX_DYNAMIC_UPSTEAM_OP_LIST); 337 | op.verbose = 1; 338 | return ngx_stream_dynamic_upstream_lua_op(L, &op, PRIMARY|BACKUP); 339 | } 340 | 341 | 342 | static int 343 | ngx_stream_dynamic_upstream_lua_get_primary_peers(lua_State *L) 344 | { 345 | ngx_dynamic_upstream_op_t op; 346 | if (lua_gettop(L) != 1) { 347 | return ngx_stream_dynamic_upstream_lua_error(L, 348 | "exactly one argument expected"); 349 | } 350 | ngx_stream_dynamic_upstream_lua_op_defaults(L, &op, 351 | NGX_DYNAMIC_UPSTEAM_OP_LIST); 352 | op.verbose = 1; 353 | return ngx_stream_dynamic_upstream_lua_op(L, &op, PRIMARY|LOCK); 354 | } 355 | 356 | 357 | static int 358 | ngx_stream_dynamic_upstream_lua_get_backup_peers(lua_State *L) 359 | { 360 | ngx_dynamic_upstream_op_t op; 361 | if (lua_gettop(L) != 1) { 362 | return ngx_stream_dynamic_upstream_lua_error(L, 363 | "exactly one argument expected"); 364 | } 365 | ngx_stream_dynamic_upstream_lua_op_defaults(L, &op, 366 | NGX_DYNAMIC_UPSTEAM_OP_LIST); 367 | op.verbose = 1; 368 | return ngx_stream_dynamic_upstream_lua_op(L, &op, BACKUP|LOCK); 369 | } 370 | 371 | 372 | static int 373 | ngx_stream_dynamic_upstream_lua_set_peer_down(lua_State *L) 374 | { 375 | ngx_dynamic_upstream_op_t op; 376 | 377 | if (lua_gettop(L) != 2) { 378 | return ngx_stream_dynamic_upstream_lua_error(L, 379 | "exactly 2 arguments expected"); 380 | } 381 | 382 | ngx_stream_dynamic_upstream_lua_op_defaults(L, &op, 383 | NGX_DYNAMIC_UPSTEAM_OP_PARAM); 384 | 385 | op.down = 1; 386 | op.op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_DOWN; 387 | 388 | op.server.data = (u_char *) luaL_checklstring(L, 2, &op.server.len); 389 | 390 | return ngx_stream_dynamic_upstream_lua_op(L, &op, LOCK); 391 | } 392 | 393 | 394 | static int 395 | ngx_stream_dynamic_upstream_lua_set_peer_up(lua_State *L) 396 | { 397 | ngx_dynamic_upstream_op_t op; 398 | 399 | if (lua_gettop(L) != 2) { 400 | return ngx_stream_dynamic_upstream_lua_error(L, 401 | "exactly 2 arguments expected"); 402 | } 403 | 404 | ngx_stream_dynamic_upstream_lua_op_defaults(L, &op, 405 | NGX_DYNAMIC_UPSTEAM_OP_PARAM); 406 | 407 | op.up = 1; 408 | op.op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_UP; 409 | 410 | op.server.data = (u_char *) luaL_checklstring(L, 2, &op.server.len); 411 | 412 | return ngx_stream_dynamic_upstream_lua_op(L, &op, LOCK); 413 | } 414 | 415 | 416 | static int 417 | ngx_stream_dynamic_upstream_lua_add_peer_impl(lua_State *L, int backup) 418 | { 419 | ngx_dynamic_upstream_op_t op; 420 | 421 | if (lua_gettop(L) != 2) { 422 | return ngx_stream_dynamic_upstream_lua_error(L, 423 | "exactly 2 arguments expected"); 424 | } 425 | 426 | ngx_stream_dynamic_upstream_lua_op_defaults(L, &op, 427 | NGX_DYNAMIC_UPSTEAM_OP_ADD); 428 | 429 | op.server.data = (u_char *) luaL_checklstring(L, 2, &op.server.len); 430 | 431 | op.backup = backup; 432 | 433 | return ngx_stream_dynamic_upstream_lua_op(L, &op, LOCK); 434 | } 435 | 436 | 437 | static int 438 | ngx_stream_dynamic_upstream_lua_add_primary_peer(lua_State *L) 439 | { 440 | return ngx_stream_dynamic_upstream_lua_add_peer_impl(L, 0); 441 | } 442 | 443 | 444 | static int 445 | ngx_stream_dynamic_upstream_lua_add_backup_peer(lua_State *L) 446 | { 447 | return ngx_stream_dynamic_upstream_lua_add_peer_impl(L, 1); 448 | } 449 | 450 | 451 | static int 452 | ngx_stream_dynamic_upstream_lua_remove_peer(lua_State *L) 453 | { 454 | ngx_dynamic_upstream_op_t op; 455 | 456 | if (lua_gettop(L) != 2) { 457 | return ngx_stream_dynamic_upstream_lua_error(L, 458 | "exactly 2 arguments expected"); 459 | } 460 | 461 | ngx_stream_dynamic_upstream_lua_op_defaults(L, &op, 462 | NGX_DYNAMIC_UPSTEAM_OP_REMOVE); 463 | 464 | op.server.data = (u_char *) luaL_checklstring(L, 2, &op.server.len); 465 | 466 | return ngx_stream_dynamic_upstream_lua_op(L, &op, LOCK); 467 | } 468 | 469 | 470 | static int 471 | ngx_stream_dynamic_upstream_lua_update_peer_parse_params(lua_State *L, 472 | ngx_dynamic_upstream_op_t *op) 473 | { 474 | const char *key; 475 | 476 | lua_pushvalue(L, -1); 477 | lua_pushnil(L); 478 | 479 | while (lua_next(L, -2)) 480 | { 481 | lua_pushvalue(L, -2); 482 | key = lua_tostring(L, -1); 483 | if (strcmp(key, "weight") == 0) { 484 | op->weight = lua_tonumber(L, -2); 485 | op->op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_WEIGHT; 486 | } else if (strcmp(key, "max_fails") == 0) { 487 | op->max_fails = lua_tonumber(L, -2); 488 | op->op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_MAX_FAILS; 489 | } else if (strcmp(key, "max_conns") == 0) { 490 | op->max_conns = lua_tonumber(L, -2); 491 | op->op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_MAX_CONNS; 492 | } else if (strcmp(key, "fail_timeout") == 0) { 493 | op->fail_timeout = lua_tonumber(L, -2); 494 | op->op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_FAIL_TIMEOUT; 495 | } else if (strcmp(key, "down") == 0) { 496 | if (lua_tonumber(L, -2) == 1) { 497 | op->down = 1; 498 | op->op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_DOWN; 499 | } else if (lua_tonumber(L, -2) == 0) { 500 | op->up = 1; 501 | op->op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_UP; 502 | } 503 | } 504 | lua_pop(L, 2); 505 | } 506 | 507 | lua_pop(L, 1); 508 | 509 | return NGX_OK; 510 | } 511 | 512 | 513 | static int 514 | ngx_stream_dynamic_upstream_lua_update_peer(lua_State *L) 515 | { 516 | ngx_dynamic_upstream_op_t op; 517 | 518 | if (lua_gettop(L) != 3 || !lua_istable(L, 3)) { 519 | return ngx_stream_dynamic_upstream_lua_error(L, 520 | "exactly 3 arguments expected"); 521 | } 522 | 523 | ngx_stream_dynamic_upstream_lua_op_defaults(L, &op, 524 | NGX_DYNAMIC_UPSTEAM_OP_PARAM); 525 | 526 | op.server.data = (u_char *) luaL_checklstring(L, 2, &op.server.len); 527 | 528 | ngx_stream_dynamic_upstream_lua_update_peer_parse_params(L, &op); 529 | 530 | return ngx_stream_dynamic_upstream_lua_op(L, &op, LOCK); 531 | } 532 | -------------------------------------------------------------------------------- /src/ngx_dynamic_upstream_lua.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "ngx_http_lua_api.h" 5 | 6 | 7 | #include "ngx_dynamic_upstream_lua.h" 8 | #include "ngx_dynamic_upstream_module.h" 9 | 10 | 11 | extern ngx_module_t ngx_http_dynamic_upstream_lua_module; 12 | 13 | 14 | static int 15 | ngx_http_dynamic_upstream_lua_create_module(lua_State *L); 16 | 17 | 18 | static int 19 | ngx_http_dynamic_upstream_lua_get_upstreams(lua_State *L); 20 | static int 21 | ngx_http_dynamic_upstream_lua_get_peers(lua_State *L); 22 | static int 23 | ngx_http_dynamic_upstream_lua_get_peers_locked(lua_State *L); 24 | static int 25 | ngx_http_dynamic_upstream_lua_get_primary_peers(lua_State *L); 26 | static int 27 | ngx_http_dynamic_upstream_lua_get_backup_peers(lua_State *L); 28 | static int 29 | ngx_http_dynamic_upstream_lua_set_peer_down(lua_State *L); 30 | static int 31 | ngx_http_dynamic_upstream_lua_set_peer_up(lua_State *L); 32 | static int 33 | ngx_http_dynamic_upstream_lua_add_primary_peer(lua_State *L); 34 | static int 35 | ngx_http_dynamic_upstream_lua_add_backup_peer(lua_State *L); 36 | static int 37 | ngx_http_dynamic_upstream_lua_remove_peer(lua_State *L); 38 | static int 39 | ngx_http_dynamic_upstream_lua_update_peer(lua_State *L); 40 | static int 41 | ngx_http_dynamic_upstream_lua_current_upstream(lua_State *L); 42 | 43 | 44 | ngx_int_t 45 | ngx_http_dynamic_upstream_lua_init(ngx_conf_t *cf) 46 | { 47 | if (ngx_http_lua_add_package_preload(cf, "ngx.dynamic_upstream", 48 | ngx_http_dynamic_upstream_lua_create_module) != NGX_OK) { 49 | return NGX_ERROR; 50 | } 51 | 52 | return NGX_OK; 53 | } 54 | 55 | 56 | static int 57 | ngx_http_dynamic_upstream_lua_create_module(lua_State *L) 58 | { 59 | lua_newtable(L); 60 | 61 | lua_pushcfunction(L, ngx_http_dynamic_upstream_lua_get_upstreams); 62 | lua_setfield(L, -2, "get_upstreams"); 63 | 64 | lua_pushcfunction(L, ngx_http_dynamic_upstream_lua_get_peers); 65 | lua_setfield(L, -2, "get_peers"); 66 | 67 | lua_pushcfunction(L, ngx_http_dynamic_upstream_lua_get_peers_locked); 68 | lua_setfield(L, -2, "get_peers_locked"); 69 | 70 | lua_pushcfunction(L, ngx_http_dynamic_upstream_lua_get_primary_peers); 71 | lua_setfield(L, -2, "get_primary_peers"); 72 | 73 | lua_pushcfunction(L, ngx_http_dynamic_upstream_lua_get_backup_peers); 74 | lua_setfield(L, -2, "get_backup_peers"); 75 | 76 | lua_pushcfunction(L, ngx_http_dynamic_upstream_lua_set_peer_down); 77 | lua_setfield(L, -2, "set_peer_down"); 78 | 79 | lua_pushcfunction(L, ngx_http_dynamic_upstream_lua_set_peer_up); 80 | lua_setfield(L, -2, "set_peer_up"); 81 | 82 | lua_pushcfunction(L, ngx_http_dynamic_upstream_lua_add_primary_peer); 83 | lua_setfield(L, -2, "add_primary_peer"); 84 | 85 | lua_pushcfunction(L, ngx_http_dynamic_upstream_lua_add_backup_peer); 86 | lua_setfield(L, -2, "add_backup_peer"); 87 | 88 | lua_pushcfunction(L, ngx_http_dynamic_upstream_lua_remove_peer); 89 | lua_setfield(L, -2, "remove_peer"); 90 | 91 | lua_pushcfunction(L, ngx_http_dynamic_upstream_lua_update_peer); 92 | lua_setfield(L, -2, "update_peer"); 93 | 94 | lua_pushcfunction(L, ngx_http_dynamic_upstream_lua_current_upstream); 95 | lua_setfield(L, -2, "current_upstream"); 96 | 97 | return 1; 98 | } 99 | 100 | 101 | static ngx_http_upstream_main_conf_t * 102 | ngx_http_lua_upstream_get_upstream_main_conf(lua_State *L) 103 | { 104 | ngx_http_request_t *r; 105 | 106 | r = ngx_http_lua_get_request(L); 107 | 108 | if (r == NULL) { 109 | return ngx_http_cycle_get_module_main_conf(ngx_cycle, 110 | ngx_http_upstream_module); 111 | } 112 | 113 | return ngx_http_get_module_main_conf(r, ngx_http_upstream_module); 114 | } 115 | 116 | 117 | static ngx_http_upstream_srv_conf_t * 118 | ngx_dynamic_upstream_get(lua_State *L, ngx_dynamic_upstream_op_t *op) 119 | { 120 | ngx_uint_t i; 121 | ngx_http_upstream_srv_conf_t *uscf, **uscfp; 122 | ngx_http_upstream_main_conf_t *umcf; 123 | 124 | umcf = ngx_http_lua_upstream_get_upstream_main_conf(L); 125 | uscfp = umcf->upstreams.elts; 126 | 127 | for (i = 0; i < umcf->upstreams.nelts; i++) { 128 | uscf = uscfp[i]; 129 | if (op->upstream.len == uscf->host.len && 130 | ngx_strncmp(uscf->host.data, op->upstream.data, 131 | op->upstream.len) == 0) { 132 | return uscf; 133 | } 134 | } 135 | 136 | return NULL; 137 | } 138 | 139 | 140 | static const int PRIMARY = 1; 141 | static const int BACKUP = 2; 142 | static const int LOCK = 4; 143 | 144 | 145 | static void 146 | ngx_dynamic_upstream_lua_create_response(ngx_http_upstream_rr_peers_t *primary, 147 | lua_State *L, int flags) 148 | { 149 | ngx_http_upstream_rr_peer_t *peer; 150 | ngx_http_upstream_rr_peers_t *peers, *backup; 151 | int i = 1; 152 | 153 | backup = primary->next; 154 | 155 | if (flags & LOCK) { 156 | ngx_http_upstream_rr_peers_rlock(primary); 157 | } 158 | 159 | lua_newtable(L); 160 | 161 | for (peers = primary; peers; peers = peers->next) { 162 | if ( (flags & PRIMARY && peers == primary) 163 | || (flags & BACKUP && peers == backup) ) { 164 | for (peer = peers->peer; peer; peer = peer->next, ++i) { 165 | lua_newtable(L); 166 | 167 | lua_pushlstring(L, (char *) peer->server.data, 168 | peer->server.len); 169 | lua_setfield(L, -2, "server"); 170 | 171 | lua_pushlstring(L, (char *) peer->name.data, 172 | peer->name.len); 173 | lua_setfield(L, -2, "name"); 174 | 175 | lua_pushinteger(L, (lua_Integer) peer->weight); 176 | lua_setfield(L, -2, "weight"); 177 | 178 | lua_pushinteger(L, (lua_Integer) peer->max_conns); 179 | lua_setfield(L, -2, "max_conns"); 180 | 181 | lua_pushinteger(L, (lua_Integer) peer->conns); 182 | lua_setfield(L, -2, "conns"); 183 | 184 | lua_pushinteger(L, (lua_Integer) peer->max_fails); 185 | lua_setfield(L, -2, "max_fails"); 186 | 187 | lua_pushinteger(L, (lua_Integer) peer->fail_timeout); 188 | lua_setfield(L, -2, "fail_timeout"); 189 | 190 | lua_pushboolean(L, peers != primary); 191 | lua_setfield(L, -2, "backup"); 192 | 193 | if (peer->down) { 194 | lua_pushboolean(L, 1); 195 | lua_setfield(L, -2, "down"); 196 | } 197 | 198 | lua_rawseti(L, -2, i); 199 | } 200 | } 201 | } 202 | 203 | if (flags & LOCK) { 204 | ngx_http_upstream_rr_peers_unlock(primary); 205 | } 206 | } 207 | 208 | 209 | static void 210 | ngx_http_dynamic_upstream_lua_op_defaults(lua_State *L, 211 | ngx_dynamic_upstream_op_t *op, int operation) 212 | { 213 | ngx_memzero(op, sizeof(ngx_dynamic_upstream_op_t)); 214 | 215 | op->op = operation; 216 | 217 | op->status = NGX_HTTP_OK; 218 | ngx_str_null(&op->upstream); 219 | op->weight = 1; 220 | op->max_fails = 1; 221 | op->fail_timeout = 10; 222 | op->verbose = 0; 223 | op->backup = 0; 224 | op->op_param = NGX_DYNAMIC_UPSTEAM_OP_PARAM_RESOLVE; 225 | 226 | op->upstream.data = (u_char *) luaL_checklstring(L, 1, &op->upstream.len); 227 | } 228 | 229 | 230 | static int 231 | ngx_http_dynamic_upstream_lua_error(lua_State *L, const char *error) 232 | { 233 | lua_pushboolean(L, 0); 234 | lua_pushnil(L); 235 | lua_pushlstring(L, error, strlen(error)); 236 | return 3; 237 | } 238 | 239 | 240 | static int 241 | ngx_http_dynamic_upstream_lua_op(lua_State *L, ngx_dynamic_upstream_op_t *op, 242 | int flags) 243 | { 244 | ngx_int_t rc; 245 | ngx_http_upstream_srv_conf_t *uscf; 246 | ngx_http_upstream_rr_peers_t *primary; 247 | 248 | uscf = ngx_dynamic_upstream_get(L, op); 249 | if (uscf == NULL) { 250 | return ngx_http_dynamic_upstream_lua_error(L, "upstream not found"); 251 | } 252 | 253 | if (op->op != NGX_DYNAMIC_UPSTEAM_OP_LIST) { 254 | if (flags & LOCK) { 255 | rc = ngx_dynamic_upstream_op(ngx_http_lua_get_request(L)-> 256 | connection->log, op, uscf); 257 | if (rc != NGX_OK && rc != NGX_AGAIN) { 258 | return ngx_http_dynamic_upstream_lua_error(L, 259 | op->err); 260 | } 261 | } else { 262 | return ngx_http_dynamic_upstream_lua_error(L, 263 | "locked operations can be used only with readonly functions"); 264 | } 265 | } 266 | 267 | lua_pushboolean(L, 1); 268 | 269 | if (op->verbose) { 270 | primary = uscf->peer.data; 271 | ngx_dynamic_upstream_lua_create_response(primary, L, flags); 272 | } else { 273 | lua_pushnil(L); 274 | } 275 | 276 | lua_pushnil(L); 277 | 278 | return 3; 279 | } 280 | 281 | 282 | static int 283 | ngx_http_dynamic_upstream_lua_get_upstreams(lua_State *L) 284 | { 285 | ngx_uint_t i, j, count = 0; 286 | ngx_http_upstream_srv_conf_t **uscfp, *uscf; 287 | ngx_http_upstream_main_conf_t *umcf; 288 | 289 | if (lua_gettop(L) != 0) { 290 | return ngx_http_dynamic_upstream_lua_error(L, "no argument expected"); 291 | } 292 | 293 | umcf = ngx_http_lua_upstream_get_upstream_main_conf(L); 294 | if (umcf == NULL) { 295 | lua_pushboolean(L, 1); 296 | lua_newtable(L); 297 | return 2; 298 | } 299 | 300 | uscfp = umcf->upstreams.elts; 301 | 302 | lua_pushboolean(L, 1); 303 | 304 | for (i = 0; i < umcf->upstreams.nelts; i++) { 305 | uscf = uscfp[i]; 306 | if (uscf->srv_conf != NULL) { 307 | ++count; 308 | } 309 | } 310 | 311 | lua_newtable(L); 312 | 313 | umcf = ngx_http_lua_upstream_get_upstream_main_conf(L); 314 | uscfp = umcf->upstreams.elts; 315 | 316 | for (i = 0, j = 1; i < umcf->upstreams.nelts; i++) { 317 | uscf = uscfp[i]; 318 | if (uscf->srv_conf != NULL) { 319 | lua_pushlstring(L, (char *) uscf->host.data, uscf->host.len); 320 | lua_rawseti(L, -2, j++); 321 | } 322 | } 323 | 324 | return 2; 325 | } 326 | 327 | 328 | static int 329 | ngx_http_dynamic_upstream_lua_get_peers(lua_State *L) 330 | { 331 | ngx_dynamic_upstream_op_t op; 332 | if (lua_gettop(L) != 1) { 333 | return ngx_http_dynamic_upstream_lua_error(L, 334 | "exactly one argument expected"); 335 | } 336 | ngx_http_dynamic_upstream_lua_op_defaults(L, &op, 337 | NGX_DYNAMIC_UPSTEAM_OP_LIST); 338 | op.verbose = 1; 339 | return ngx_http_dynamic_upstream_lua_op(L, &op, PRIMARY|BACKUP|LOCK); 340 | } 341 | 342 | 343 | static int 344 | ngx_http_dynamic_upstream_lua_get_peers_locked(lua_State *L) 345 | { 346 | ngx_dynamic_upstream_op_t op; 347 | if (lua_gettop(L) != 1) { 348 | return ngx_http_dynamic_upstream_lua_error(L, 349 | "exactly one argument expected"); 350 | } 351 | ngx_http_dynamic_upstream_lua_op_defaults(L, &op, 352 | NGX_DYNAMIC_UPSTEAM_OP_LIST); 353 | op.verbose = 1; 354 | return ngx_http_dynamic_upstream_lua_op(L, &op, PRIMARY|BACKUP); 355 | } 356 | 357 | 358 | static int 359 | ngx_http_dynamic_upstream_lua_get_primary_peers(lua_State *L) 360 | { 361 | ngx_dynamic_upstream_op_t op; 362 | if (lua_gettop(L) != 1) { 363 | return ngx_http_dynamic_upstream_lua_error(L, 364 | "exactly one argument expected"); 365 | } 366 | ngx_http_dynamic_upstream_lua_op_defaults(L, &op, 367 | NGX_DYNAMIC_UPSTEAM_OP_LIST); 368 | op.verbose = 1; 369 | return ngx_http_dynamic_upstream_lua_op(L, &op, PRIMARY|LOCK); 370 | } 371 | 372 | 373 | static int 374 | ngx_http_dynamic_upstream_lua_get_backup_peers(lua_State *L) 375 | { 376 | ngx_dynamic_upstream_op_t op; 377 | if (lua_gettop(L) != 1) { 378 | return ngx_http_dynamic_upstream_lua_error(L, 379 | "exactly one argument expected"); 380 | } 381 | ngx_http_dynamic_upstream_lua_op_defaults(L, &op, 382 | NGX_DYNAMIC_UPSTEAM_OP_LIST); 383 | op.verbose = 1; 384 | return ngx_http_dynamic_upstream_lua_op(L, &op, BACKUP|LOCK); 385 | } 386 | 387 | 388 | static int 389 | ngx_http_dynamic_upstream_lua_set_peer_down(lua_State *L) 390 | { 391 | ngx_dynamic_upstream_op_t op; 392 | 393 | if (lua_gettop(L) != 2) { 394 | return ngx_http_dynamic_upstream_lua_error(L, 395 | "exactly 2 arguments expected"); 396 | } 397 | 398 | ngx_http_dynamic_upstream_lua_op_defaults(L, &op, 399 | NGX_DYNAMIC_UPSTEAM_OP_PARAM); 400 | 401 | op.down = 1; 402 | op.op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_DOWN; 403 | 404 | op.server.data = (u_char *) luaL_checklstring(L, 2, &op.server.len); 405 | 406 | return ngx_http_dynamic_upstream_lua_op(L, &op, LOCK); 407 | } 408 | 409 | 410 | static int 411 | ngx_http_dynamic_upstream_lua_set_peer_up(lua_State *L) 412 | { 413 | ngx_dynamic_upstream_op_t op; 414 | 415 | if (lua_gettop(L) != 2) { 416 | return ngx_http_dynamic_upstream_lua_error(L, 417 | "exactly 2 arguments expected"); 418 | } 419 | 420 | ngx_http_dynamic_upstream_lua_op_defaults(L, &op, 421 | NGX_DYNAMIC_UPSTEAM_OP_PARAM); 422 | 423 | op.up = 1; 424 | op.op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_UP; 425 | 426 | op.server.data = (u_char *) luaL_checklstring(L, 2, &op.server.len); 427 | 428 | return ngx_http_dynamic_upstream_lua_op(L, &op, LOCK); 429 | } 430 | 431 | 432 | static int 433 | ngx_http_dynamic_upstream_lua_add_peer_impl(lua_State *L, int backup) 434 | { 435 | ngx_dynamic_upstream_op_t op; 436 | 437 | if (lua_gettop(L) != 2) { 438 | return ngx_http_dynamic_upstream_lua_error(L, 439 | "exactly 2 arguments expected"); 440 | } 441 | 442 | ngx_http_dynamic_upstream_lua_op_defaults(L, &op, 443 | NGX_DYNAMIC_UPSTEAM_OP_ADD); 444 | 445 | op.server.data = (u_char *) luaL_checklstring(L, 2, &op.server.len); 446 | 447 | op.backup = backup; 448 | 449 | return ngx_http_dynamic_upstream_lua_op(L, &op, LOCK); 450 | } 451 | 452 | 453 | static int 454 | ngx_http_dynamic_upstream_lua_add_primary_peer(lua_State *L) 455 | { 456 | return ngx_http_dynamic_upstream_lua_add_peer_impl(L, 0); 457 | } 458 | 459 | 460 | static int 461 | ngx_http_dynamic_upstream_lua_add_backup_peer(lua_State *L) 462 | { 463 | return ngx_http_dynamic_upstream_lua_add_peer_impl(L, 1); 464 | } 465 | 466 | 467 | static int 468 | ngx_http_dynamic_upstream_lua_remove_peer(lua_State *L) 469 | { 470 | ngx_dynamic_upstream_op_t op; 471 | 472 | if (lua_gettop(L) != 2) { 473 | return ngx_http_dynamic_upstream_lua_error(L, 474 | "exactly 2 arguments expected"); 475 | } 476 | 477 | ngx_http_dynamic_upstream_lua_op_defaults(L, &op, 478 | NGX_DYNAMIC_UPSTEAM_OP_REMOVE); 479 | 480 | op.server.data = (u_char *) luaL_checklstring(L, 2, &op.server.len); 481 | 482 | return ngx_http_dynamic_upstream_lua_op(L, &op, LOCK); 483 | } 484 | 485 | 486 | static int 487 | ngx_http_dynamic_upstream_lua_update_peer_parse_params(lua_State *L, 488 | ngx_dynamic_upstream_op_t *op) 489 | { 490 | const char *key; 491 | 492 | lua_pushvalue(L, -1); 493 | lua_pushnil(L); 494 | 495 | while (lua_next(L, -2)) 496 | { 497 | lua_pushvalue(L, -2); 498 | key = lua_tostring(L, -1); 499 | if (strcmp(key, "weight") == 0) { 500 | op->weight = lua_tonumber(L, -2); 501 | op->op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_WEIGHT; 502 | } else if (strcmp(key, "max_fails") == 0) { 503 | op->max_fails = lua_tonumber(L, -2); 504 | op->op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_MAX_FAILS; 505 | } else if (strcmp(key, "max_conns") == 0) { 506 | op->max_conns = lua_tonumber(L, -2); 507 | op->op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_MAX_CONNS; 508 | } else if (strcmp(key, "fail_timeout") == 0) { 509 | op->fail_timeout = lua_tonumber(L, -2); 510 | op->op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_FAIL_TIMEOUT; 511 | } else if (strcmp(key, "down") == 0) { 512 | if (lua_tonumber(L, -2) == 1) { 513 | op->down = 1; 514 | op->op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_DOWN; 515 | } else if (lua_tonumber(L, -2) == 0) { 516 | op->up = 1; 517 | op->op_param |= NGX_DYNAMIC_UPSTEAM_OP_PARAM_UP; 518 | } 519 | } 520 | lua_pop(L, 2); 521 | } 522 | 523 | lua_pop(L, 1); 524 | 525 | return NGX_OK; 526 | } 527 | 528 | 529 | static int 530 | ngx_http_dynamic_upstream_lua_update_peer(lua_State *L) 531 | { 532 | ngx_dynamic_upstream_op_t op; 533 | 534 | if (lua_gettop(L) != 3 || !lua_istable(L, 3)) { 535 | return ngx_http_dynamic_upstream_lua_error(L, 536 | "exactly 3 arguments expected"); 537 | } 538 | 539 | ngx_http_dynamic_upstream_lua_op_defaults(L, &op, 540 | NGX_DYNAMIC_UPSTEAM_OP_PARAM); 541 | 542 | op.server.data = (u_char *) luaL_checklstring(L, 2, &op.server.len); 543 | 544 | ngx_http_dynamic_upstream_lua_update_peer_parse_params(L, &op); 545 | 546 | return ngx_http_dynamic_upstream_lua_op(L, &op, LOCK); 547 | } 548 | 549 | 550 | static int 551 | ngx_http_dynamic_upstream_lua_current_upstream(lua_State *L) 552 | { 553 | ngx_http_request_t *r; 554 | ngx_http_upstream_t *us; 555 | ngx_http_upstream_conf_t *ucf; 556 | ngx_http_upstream_srv_conf_t *uscf; 557 | 558 | r = ngx_http_lua_get_request(L); 559 | if (r == NULL) { 560 | return ngx_http_dynamic_upstream_lua_error(L, "no request object"); 561 | } 562 | 563 | us = r->upstream; 564 | if (us == NULL) { 565 | return ngx_http_dynamic_upstream_lua_error(L, "no proxying"); 566 | } 567 | 568 | uscf = us->upstream; 569 | 570 | if (uscf == NULL) { 571 | ucf = us->conf; 572 | if (ucf == NULL) { 573 | return ngx_http_dynamic_upstream_lua_error(L,"no upstream"); 574 | } 575 | uscf = ucf->upstream; 576 | if (uscf == NULL) { 577 | return ngx_http_dynamic_upstream_lua_error(L, "no srv upstream"); 578 | } 579 | } 580 | 581 | lua_pushboolean(L, 1); 582 | lua_pushlstring(L, (char *) uscf->host.data, uscf->host.len); 583 | lua_pushnil(L); 584 | 585 | return 3; 586 | } 587 | --------------------------------------------------------------------------------