diff --git a/README.md b/README.md index c559021..af40201 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ Table of Contents * [Functions](#functions) * [get_upstreams](#get_upstreams) * [get_servers](#get_servers) + * [add_upstream_peer](#add_upstream_peer) + * [remove_server_from_upstream](#remove_server_from_upstream) * [get_primary_peers](#get_primary_peers) * [get_backup_peers](#get_backup_peers) * [set_peer_down](#set_peer_down) @@ -126,6 +128,24 @@ The return value is an array-like Lua table. Each table entry is a hash-like Lua [Back to TOC](#table-of-contents) +add_upstream_peer +----------- +`syntax: ok, err = upstream.add_upstream_peer(upstream_name, ip:port, optional weight = 1, optional max_fails = 1, optional fail_timeout = 10, optional backup = false)` + +The return values are a boolean denoting the success of the operation and an associated error if one occurs. + +[Back to TOC](#table-of-contents) + +remove_server_from_upstream +----------- +`syntax: err = upstream.remove_server_from_upstream(upstream_name, server_name)` + +Removes a server from an upstream given the upstream and server names. The server name is expected to match the `name` field on the server in the upstream. + +The return value is an error string or nil. + +[Back to TOC](#table-of-contents) + get_primary_peers --------- `syntax: peers = upstream.get_primary_peers(upstream_name)` @@ -329,4 +349,3 @@ See Also * the [lua-resty-upstream-healthcheck](https://github.com/openresty/lua-resty-upstream-healthcheck) library which makes use of the Lua API provided by this module. [Back to TOC](#table-of-contents) - diff --git a/src/ngx_http_lua_upstream_module.c b/src/ngx_http_lua_upstream_module.c index 361ee92..048d6d0 100644 --- a/src/ngx_http_lua_upstream_module.c +++ b/src/ngx_http_lua_upstream_module.c @@ -23,6 +23,7 @@ static ngx_int_t ngx_http_lua_upstream_init(ngx_conf_t *cf); static int ngx_http_lua_upstream_create_module(lua_State * L); static int ngx_http_lua_upstream_get_upstreams(lua_State * L); static int ngx_http_lua_upstream_get_servers(lua_State * L); +static int ngx_http_lua_upstream_add_upstream_peer(lua_State * L); static ngx_http_upstream_main_conf_t * ngx_http_lua_upstream_get_upstream_main_conf(lua_State *L); static int ngx_http_lua_upstream_get_primary_peers(lua_State * L); @@ -37,6 +38,7 @@ static int ngx_http_lua_upstream_set_peer_down(lua_State * L); static int ngx_http_lua_upstream_current_upstream_name(lua_State *L); + static ngx_http_module_t ngx_http_lua_upstream_ctx = { NULL, /* preconfiguration */ ngx_http_lua_upstream_init, /* postconfiguration */ @@ -90,6 +92,9 @@ ngx_http_lua_upstream_create_module(lua_State * L) lua_pushcfunction(L, ngx_http_lua_upstream_get_servers); lua_setfield(L, -2, "get_servers"); + lua_pushcfunction(L, ngx_http_lua_upstream_add_upstream_peer); + lua_setfield(L, -2, "add_upstream_peer"); + lua_pushcfunction(L, ngx_http_lua_upstream_get_primary_peers); lua_setfield(L, -2, "get_primary_peers"); @@ -141,6 +146,127 @@ ngx_http_lua_upstream_get_upstreams(lua_State * L) return 1; } +static int +ngx_http_lua_upstream_add_upstream_peer(lua_State * L) +{ + ngx_http_request_t *r; + + ngx_http_upstream_srv_conf_t *us; + + ngx_http_upstream_rr_peers_t *peers; + ngx_http_upstream_rr_peer_t *peer, *last; + + u_char *url; + + ngx_url_t upstream; + ngx_str_t host; + ngx_int_t weight = 1; + ngx_int_t max_fails = 1; + time_t fail_timeout = 10; + ngx_uint_t backup = 0; + + if ((lua_gettop(L) < 2) || (lua_gettop(L) > 6)) { + /* + * "upstream name", "host:port", "weight", "max_fails", "fail_timeout", "backup" + */ + return luaL_error(L, "at least 2 arguments are required and as many as 6 are allowed"); + } + + r = ngx_http_lua_get_request(L); + if (r == NULL) { + lua_pushnil(L); + lua_pushliteral(L, "could not retrieve request\n"); + return 2; + } + + host.data = (u_char *) luaL_checklstring(L, 1, &host.len); + + ngx_memzero(&upstream, sizeof(ngx_url_t)); + + url = (u_char *) luaL_checklstring(L, 2, &upstream.url.len); + upstream.default_port = 80; + + if (lua_gettop(L) >= 3) { + weight = (ngx_int_t) luaL_checkint(L, 3); + } + + if (lua_gettop(L) >= 4) { + max_fails = (ngx_int_t) luaL_checkint(L, 4); + } + + if (lua_gettop(L) >= 5) { + fail_timeout = (time_t) luaL_checklong(L, 5); + } + + if (lua_gettop(L) >= 6) { + backup = lua_toboolean(L, 6); + } + +#if (NGX_DEBUG) + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "%s %s params: %s,%s,%d,%d,%d,%d\n", __FILE__, __FUNCTION__, host.data, url, weight, max_fails, fail_timeout, backup); +#endif + + us = ngx_http_lua_upstream_find_upstream(L, &host); + if (us == NULL) { + lua_pushnil(L); + lua_pushliteral(L, "upstream not found\n"); + return 2; + } + + peers = us->peer.data; + + for (peer = peers->peer, last = peer; peer; peer = peer->next){ + if (len(url) == peer->name.len && ngx_strncmp(url, peer->name.data, peer->name.len) == 0) { + lua_pushnil(L); + lua_pushliteral(L, "server already exists\n"); + return 2; + } + last = peer; + } + + upstream.url.data = ngx_pcalloc(r->pool, upstream.url.len + 1); + ngx_memcpy(upstream.url.data, url, upstream.url.len); + + // validate URL + if (ngx_parse_url(r->pool, &upstream) != NGX_OK) { + if (upstream.err) { + lua_pushnil(L); + lua_pushliteral(L, "url parser error\n"); + return 2; + } + } + + last->next = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_rr_peer_t)); + + if (last->next == NULL) { + lua_pushnil(L); + lua_pushliteral(L, "failed to allocate memory\n"); + return 2; + } + + last->next->name = upstream.url; + last->next->server = upstream.url; + last->next->sockaddr = upstream.addrs[0].sockaddr; + last->next->socklen = upstream.addrs[0].socklen; + + last->next->weight = weight; + last->next->effective_weight = weight; + last->next->current_weight = weight; + + last->next->max_fails = max_fails; + + last->next->fail_timeout = fail_timeout; + + last->next->backup = backup; + + peers->number++; + peers->total_weight += last->next->weight; + peers->single = (peers->number == 1); + peers->weighted = (peers->total_weight != peers->number); + + lua_pushboolean(L, 1); + return 1; +} static int ngx_http_lua_upstream_get_servers(lua_State * L) @@ -551,7 +677,6 @@ ngx_http_lua_upstream_find_upstream(lua_State *L, ngx_str_t *host) return NULL; } - static int ngx_http_lua_upstream_current_upstream_name(lua_State *L) { diff --git a/t/sanity.t b/t/sanity.t index c713c78..4d9ebb7 100644 --- a/t/sanity.t +++ b/t/sanity.t @@ -648,3 +648,35 @@ nil --- no_error_log [error] + + +=== TEST 19: add server to pre-defined upstream +--- http_config + upstream foo { + server 127.0.0.2; + } + +--- config + location /t { + content_by_lua ' + local ljson = require "ljson" + local upstream = require "ngx.upstream" + local ok, err = upstream.add_upstream_peer("foo", "127.0.0.10") + if err then + ngx.say(err) + else + local peers, err = upstream.get_primary_peers("foo") + if not peers then + ngx.say("failed to get primary peers from upstream foo: " .. err) + else + ngx.say(json.encode(peers)) + end + end + '; + } +--- request + GET /t +--- response_body +[{"addr": "127.0.0.2:80", "weight": 1, "fail_timeout": 10, "name": "127.0.0.2", "max_fails": 1}, {"addr": "127.0.0.10:80", "weight": 1, "fail_timeout": 1, "name": "127.0.0.10", "max_fails": 1}] +--- no_error_log +[error]