pax_global_header00006660000000000000000000000064126320616770014524gustar00rootroot0000000000000052 comment=4b8743fe35574145868fe26b762d805747740be6 lua-resty-redis-connector-0.03/000077500000000000000000000000001263206167700164675ustar00rootroot00000000000000lua-resty-redis-connector-0.03/.gitignore000066400000000000000000000000301263206167700204500ustar00rootroot00000000000000t/servroot/ t/error.log lua-resty-redis-connector-0.03/Makefile000066400000000000000000000112131263206167700201250ustar00rootroot00000000000000SHELL := /bin/bash # Cheat by using bash :) OPENRESTY_PREFIX = /usr/local/openresty-debug TEST_FILE ?= t SENTINEL_TEST_FILE ?= $(TEST_FILE)/sentinel REDIS_CMD = redis-server SENTINEL_CMD = $(REDIS_CMD) --sentinel REDIS_SOCK = /redis.sock REDIS_PID = /redis.pid REDIS_LOG = /redis.log REDIS_PREFIX = /tmp/redis- # Overrideable redis test variables TEST_REDIS_PORTS ?= 6379 6380 6378 TEST_REDIS_DATABASE ?= 1 REDIS_FIRST_PORT := $(firstword $(TEST_REDIS_PORTS)) REDIS_SLAVE_ARG := --slaveof 127.0.0.1 $(REDIS_FIRST_PORT) REDIS_CLI := redis-cli -p $(REDIS_FIRST_PORT) -n $(TEST_REDIS_DATABASE) # Override socket for running make test on its own # (make test TEST_REDIS_SOCKET=/path/to/sock.sock) TEST_REDIS_SOCKET ?= $(REDIS_PREFIX)$(REDIS_FIRST_PORT)$(REDIS_SOCK) # Overrideable redis + sentinel test variables TEST_SENTINEL_PORTS ?= 6381 6382 6383 TEST_SENTINEL_MASTER_NAME ?= mymaster TEST_SENTINEL_PROMOTION_TIME ?= 20 # Command line arguments for redis tests TEST_REDIS_VARS = PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$(PATH) \ TEST_REDIS_SOCKET=unix://$(TEST_REDIS_SOCKET) \ TEST_REDIS_DATABASE=$(TEST_REDIS_DATABASE) \ TEST_NGINX_NO_SHUFFLE=1 # Command line arguments for sentinel tests TEST_SENTINEL_VARS = PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$(PATH) \ TEST_SENTINEL_PORT=$(firstword $(TEST_SENTINEL_PORTS)) \ TEST_SENTINEL_MASTER_NAME=$(TEST_SENTINEL_MASTER_NAME) \ TEST_REDIS_DATABASE=$(TEST_REDIS_DATABASE) \ TEST_NGINX_NO_SHUFFLE=1 # Sentinel configuration can only be set by a config file define TEST_SENTINEL_CONFIG sentinel monitor $(TEST_SENTINEL_MASTER_NAME) 127.0.0.1 $(REDIS_FIRST_PORT) 2 sentinel down-after-milliseconds $(TEST_SENTINEL_MASTER_NAME) 2000 sentinel failover-timeout $(TEST_SENTINEL_MASTER_NAME) 10000 sentinel parallel-syncs $(TEST_SENTINEL_MASTER_NAME) 5 endef export TEST_SENTINEL_CONFIG SENTINEL_CONFIG_FILE = /tmp/sentinel-test-config PREFIX ?= /usr/local LUA_INCLUDE_DIR ?= $(PREFIX)/include LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION) PROVE ?= prove -I ../test-nginx/lib INSTALL ?= install .PHONY: all install test test_all start_redis_instances stop_redis_instances \ start_redis_instance stop_redis_instance cleanup_redis_instance flush_db \ create_sentinel_config delete_sentinel_config check_ports test_redis \ test_sentinel sleep all: ; install: all $(INSTALL) -d $(DESTDIR)/$(LUA_LIB_DIR)/resty/redis $(INSTALL) lib/resty/redis/*.lua $(DESTDIR)/$(LUA_LIB_DIR)/resty/redis test: test_redis test_all: start_redis_instances sleep test_redis stop_redis_instances sleep: sleep 3 start_redis_instances: check_ports create_sentinel_config @$(foreach port,$(TEST_REDIS_PORTS), \ [[ "$(port)" != "$(REDIS_FIRST_PORT)" ]] && \ SLAVE="$(REDIS_SLAVE_ARG)" || \ SLAVE="" && \ $(MAKE) start_redis_instance args="$$SLAVE" port=$(port) \ prefix=$(REDIS_PREFIX)$(port) && \ ) true @$(foreach port,$(TEST_SENTINEL_PORTS), \ $(MAKE) start_redis_instance \ port=$(port) args="$(SENTINEL_CONFIG_FILE) --sentinel" \ prefix=$(REDIS_PREFIX)$(port) && \ ) true stop_redis_instances: delete_sentinel_config -@$(foreach port,$(TEST_REDIS_PORTS) $(TEST_SENTINEL_PORTS), \ $(MAKE) stop_redis_instance cleanup_redis_instance port=$(port) \ prefix=$(REDIS_PREFIX)$(port) && \ ) true 2>&1 > /dev/null start_redis_instance: -@echo "Starting redis on port $(port) with args: \"$(args)\"" -@mkdir -p $(prefix) @$(REDIS_CMD) $(args) \ --pidfile $(prefix)$(REDIS_PID) \ --bind 127.0.0.1 --port $(port) \ --unixsocket $(prefix)$(REDIS_SOCK) \ --unixsocketperm 777 \ --dir $(prefix) \ --logfile $(prefix)$(REDIS_LOG) \ --loglevel debug \ --daemonize yes stop_redis_instance: -@echo "Stopping redis on port $(port)" -@[[ -f "$(prefix)$(REDIS_PID)" ]] && kill -QUIT \ `cat $(prefix)$(REDIS_PID)` 2>&1 > /dev/null || true cleanup_redis_instance: stop_redis_instance -@echo "Cleaning up redis files in $(prefix)" -@rm -rf $(prefix) flush_db: -@echo "Flushing Redis DB" @$(REDIS_CLI) flushdb create_sentinel_config: -@echo "Creating $(SENTINEL_CONFIG_FILE)" @echo "$$TEST_SENTINEL_CONFIG" > $(SENTINEL_CONFIG_FILE) delete_sentinel_config: -@echo "Removing $(SENTINEL_CONFIG_FILE)" @rm -f $(SENTINEL_CONFIG_FILE) check_ports: -@echo "Checking ports $(REDIS_PORTS)" @$(foreach port,$(REDIS_PORTS),! lsof -i :$(port) &&) true 2>&1 > /dev/null test_redis: flush_db $(TEST_REDIS_VARS) $(PROVE) $(TEST_FILE) util/lua-releng test_leak: flush_db $(TEST_REDIS_VARS) TEST_NGINX_CHECK_LEAK=1 $(PROVE) $(TEST_FILE) lua-resty-redis-connector-0.03/README.md000066400000000000000000000130561263206167700177530ustar00rootroot00000000000000# lua-resty-redis-connector Connection utilities for [lua-resty-redis](https://github.com/openresty/lua-resty-redis), making it easy and reliable to connect to Redis hosts, either directly or via [Redis Sentinel](http://redis.io/topics/sentinel). ## Synopsis ```lua local redis_connector = require "resty.redis.connector" local rc = redis_connector.new() local redis, err = rc:connect{ url = "redis://PASSWORD@127.0.0.1:6379/2" } -- or... local redis, err = rc:connect{ host = "127.0.0.1", port = 6379, db = 2, password = "PASSWORD", } if not redis then ngx.log(ngx.ERR, err) end ``` ## DSN format The [connect](#connect) method accepts a single table of named arguments. If the `url` field is present then it will be parsed, overriding values supplied in the parameters table. The format for connecting directly to Redis is: `redis://PASSWORD@HOST:PORT/DB` The `PASSWORD` and `DB` fields are optional, all other components are required. When connecting via Redis Sentinel, the format is as follows: `sentinel://PASSWORD@MASTER_NAME:ROLE/DB` Again, `PASSWORD` and `DB` are optional. `ROLE` must be any of `m`, `s` or `a`, meaning: * `m`: master * `s`: slave * `a`: any (first tries the master, but will failover to a slave if required) ## Parameters The [connect](#connect) method expects the following field values, either by falling back to defaults, populating the fields by parsing the DSN, or being specified directly. The defaults are as follows: ```lua { host = "127.0.0.1", port = "6379", path = nil, -- unix socket path, e.g. /tmp/redis.sock password = "", db = 0, master_name = "mymaster", role = "master", -- master | slave | any sentinels = nil, } ``` Note that if `sentinel://` is supplied as the `url` parameter, a table of `sentinels` must also be supplied. e.g. ```lua local redis, err = rc:connect{ url = "sentinel://mymaster:a/2", sentinels = { { host = "127.0.0.1", port = 26379" }, } } ``` ## API * [new](#new) * [set_connect_timeout](#set_connect_timeout) * [set_read_timeout](#set_read_timeout) * [set_connection_options](#set_connection_options) * [connect](#connect) * [Utilities](#utilities) * [connect_via_sentinel](#connect_via_sentinel) * [try_hosts](#try_hosts) * [connect_to_host](#connect_to_host) * [Sentinel Utilities](#sentinel-utilities) * [sentinel.get_master](#sentinelget_master) * [sentinel.get_slaves](#sentinelget_slaves) ### new `syntax: rc = redis_connector.new()` Creates the Redis Connector object. In case of failures, returns `nil` and a string describing the error. ### set_connect_timeout `syntax: rc:set_connect_timeout(100)` Sets the cosocket connection timeout, in ms. ### set_read_timeout `syntax: rc:set_read_timeout(500)` Sets the cosocket read timeout, in ms. ### set_connection_options `syntax: rc:set_connection_options({ pool = params.host .. ":" .. params.port .. "/" .. params.db })` Sets the connection options table, as supplied to [tcpsock:connect](https://github.com/openresty/lua-nginx-module#tcpsockconnect) method. ### connect `syntax: redis, err = rc:connect(params)` Attempts to create a connection, according to the [params](#parameters) supplied. ## Utilities ### connect_via_sentinel `syntax: redis, err = rc:connect_via_sentinel(params)` Returns a Redis connection by first accessing a sentinel as supplied by the `params.sentinels` table, and querying this with the `params.master_name` and `params.role`. ### try_hosts `syntax: redis, err = rc:try_hosts(hosts)` Tries the hosts supplied in order and returns the first successful connection. ### connect_to_host `syntax: redis, err = rc:connect_to_host(host)` Attempts to connect to the supplied `host`. ## Sentinel Utilities ### sentinel.get_master `syntax: master, err = sentinel.get_master(sentinel, master_name)` Given a connected Sentinel instance and a master name, will return the current master Redis instance. ### sentinel.get_slaves `syntax: slaves, err = sentinel.get_slaves(sentinel, master_name)` Given a connected Sentinel instance and a master name, will return a list of registered slave Redis instances. ## TODO * Redis Cluster support. # Author James Hurst # Licence This module is licensed under the 2-clause BSD license. Copyright (c) 2015, James Hurst All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. lua-resty-redis-connector-0.03/lib/000077500000000000000000000000001263206167700172355ustar00rootroot00000000000000lua-resty-redis-connector-0.03/lib/resty/000077500000000000000000000000001263206167700204035ustar00rootroot00000000000000lua-resty-redis-connector-0.03/lib/resty/redis/000077500000000000000000000000001263206167700215115ustar00rootroot00000000000000lua-resty-redis-connector-0.03/lib/resty/redis/connector.lua000066400000000000000000000140211263206167700242040ustar00rootroot00000000000000local redis = require "resty.redis" redis.add_commands("sentinel") local sentinel = require "resty.redis.sentinel" local ipairs, setmetatable, pcall = ipairs, setmetatable, pcall local ngx_null = ngx.null local ngx_log = ngx.log local ngx_DEBUG = ngx.DEBUG local ngx_ERR = ngx.ERR local ngx_re_match = ngx.re.match local tbl_insert = table.insert local tbl_remove = table.remove local tbl_sort = table.sort local ok, tbl_new = pcall(require, "table.new") if not ok then tbl_new = function (narr, nrec) return {} end end local _M = { _VERSION = '0.03', } local mt = { __index = _M } local DEFAULTS = { host = "127.0.0.1", port = 6379, path = nil, -- /tmp/redis.sock password = nil, db = 0, master_name = "mymaster", role = "master", -- master | slave | any (tries master first, failover to a slave) sentinels = nil, cluster_startup_nodes = {}, } function _M.new() return setmetatable({ connect_timeout = 100, read_timeout = 1000, connection_options = nil, -- pool, etc }, mt) end function _M.set_connect_timeout(self, timeout) self.connect_timeout = timeout end function _M.set_read_timeout(self, timeout) self.read_timeout = timeout end function _M.set_connection_options(self, options) self.connection_options = options end local function parse_dsn(params) local url = params.url if url then local url_pattern = [[^(?:(redis|sentinel)://)(?:([^@]*)@)?([^:/]+)(?::(\d+|[msa]+))/?(.*)$]] local m, err = ngx_re_match(url, url_pattern, "") if not m then ngx_log(ngx_ERR, "could not parse DSN: ", err) else local fields if m[1] == "redis" then fields = { "password", "host", "port", "db" } elseif m[1] == "sentinel" then fields = { "password", "master_name", "role", "db" } end -- password may not be present if #m < 5 then tbl_remove(fields, 1) end local roles = { m = "master", s = "slave", a = "any" } for i,v in ipairs(fields) do params[v] = m[i + 1] if v == "role" then params[v] = roles[params[v]] end end end end end function _M.connect(self, params) -- If we have nothing, assume default host connection options apply if not params or type(params) ~= "table" then params = {} end if params.url then parse_dsn(params) end if params.sentinels then setmetatable(params, { __index = DEFAULTS } ) return self:connect_via_sentinel(params) elseif params.startup_cluster_nodes then setmetatable(params, { __index = DEFAULTS } ) -- TODO: Implement cluster return nil, "Redis Cluster not yet implemented" else setmetatable(params, { __index = DEFAULTS } ) return self:connect_to_host(params) end end local function sort_by_localhost(a, b) if a.host == "127.0.0.1" then return true else return false end end function _M.connect_via_sentinel(self, params) local sentinels = params.sentinels local master_name = params.master_name local role = params.role local db = params.db local password = params.password local sentnl, err, previous_errors = self:try_hosts(sentinels) if not sentnl then return nil, err, previous_errors end if role == "master" or role == "any" then local master, err = sentinel.get_master(sentnl, master_name) if master then master.db = db master.password = password local redis, err = self:connect_to_host(master) if redis then sentnl:set_keepalive() return redis, err else if role == "master" then return nil, err end end end end -- We either wanted a slave, or are failing over to a slave "any" local slaves, err = sentinel.get_slaves(sentnl, master_name) sentnl:set_keepalive() if not slaves then return nil, err end -- Put any slaves on 127.0.0.1 at the front tbl_sort(slaves, sort_by_localhost) if db or password then for i,slave in ipairs(slaves) do slave.db = db slave.password = password end end local slave, err, previous_errors = self:try_hosts(slaves) if not slave then return nil, err, previous_errors else return slave end end -- In case of errors, returns "nil, err, previous_errors" where err is -- the last error received, and previous_errors is a table of the previous errors. function _M.try_hosts(self, hosts) local errors = tbl_new(#hosts, 0) for i, host in ipairs(hosts) do local r r, errors[i] = self:connect_to_host(host) if r then return r, tbl_remove(errors), errors end end return nil, tbl_remove(errors), errors end function _M.connect_to_host(self, host) local r = redis.new() r:set_timeout(self.connect_timeout) local ok, err local socket = host.socket if socket then if self.connection_options then ok, err = r:connect(socket, self.connection_options) else ok, err = r:connect(socket) end else if self.connection_options then ok, err = r:connect(host.host, host.port, self.connection_options) else ok, err = r:connect(host.host, host.port) end end if not ok then ngx_log(ngx_ERR, err, " for ", host.host, ":", host.port) return nil, err else r:set_timeout(self, self.read_timeout) local password = host.password if password then local res, err = r:auth(password) if err then ngx_log(ngx_ERR, err) return res, err end end r:select(host.db) return r, nil end end return _M lua-resty-redis-connector-0.03/lib/resty/redis/sentinel.lua000066400000000000000000000024741263206167700240440ustar00rootroot00000000000000local ipairs, type = ipairs, type local ngx_null = ngx.null local tbl_insert = table.insert local ok, tbl_new = pcall(require, "table.new") if not ok then tbl_new = function (narr, nrec) return {} end end local _M = {} _M._VERSION = 0.03 function _M.get_master(sentinel, master_name) local res, err = sentinel:sentinel( "get-master-addr-by-name", master_name ) if res and res ~= ngx_null and res[1] and res[2] then return { host = res[1], port = res[2] } else return nil, err end end function _M.get_slaves(sentinel, master_name) local res, err = sentinel:sentinel("slaves", master_name) if res and type(res) == "table" then local hosts = tbl_new(#res, 0) for _,slave in ipairs(res) do local num_recs = #slave local host = tbl_new(0, num_recs + 1) for i = 1, num_recs, 2 do host[slave[i]] = slave[i + 1] end if host["master-link-status"] == "ok" then host.host = host.ip -- for parity with other functions tbl_insert(hosts, host) end end if hosts[1] ~= nil then return hosts else return nil, "no slaves available" end else return nil, err end end return _M lua-resty-redis-connector-0.03/lua-resty-redis-connector-0.02-0.rockspec000066400000000000000000000013671263206167700257640ustar00rootroot00000000000000package = "lua-resty-redis-connector" version = "0.02-0" source = { url = "git://github.com/pintsized/lua-resty-redis-connector", tag = "v0.02" } description = { summary = "Connection utilities for lua-resty-redis.", detailed = [[ Connection utilities for lua-resty-redis, making it easy and reliable to connect to Redis hosts, either directly or via Redis Sentinel. ]], homepage = "https://github.com/pintsized/lua-resty-redis-connector", license = "2-clause BSD", maintainer = "James Hurst " } dependencies = { "lua >= 5.1", } build = { type = "builtin", modules = { ["resty.redis.connector"] = "lib/resty/redis/connector.lua", ["resty.redis.sentinel"] = "lib/resty/redis/sentinel.lua" } } lua-resty-redis-connector-0.03/t/000077500000000000000000000000001263206167700167325ustar00rootroot00000000000000lua-resty-redis-connector-0.03/t/connector.t000066400000000000000000000105241263206167700211130ustar00rootroot00000000000000# vim:set ft= ts=4 sw=4 et: use Test::Nginx::Socket::Lua; use Cwd qw(cwd); repeat_each(2); plan tests => repeat_each() * (3 * blocks()); my $pwd = cwd(); our $HttpConfig = qq{ lua_package_path "$pwd/lib/?.lua;;"; }; $ENV{TEST_NGINX_RESOLVER} = '8.8.8.8'; $ENV{TEST_NGINX_REDIS_PORT} ||= 6379; no_long_string(); #no_diff(); run_tests(); __DATA__ === TEST 1: basic --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local redis_connector = require "resty.redis.connector" local rc = redis_connector.new() local params = { host = "127.0.0.1", port = $TEST_NGINX_REDIS_PORT } local redis, err = rc:connect(params) if not redis then ngx.say("failed to connect: ", err) return end local res, err = redis:set("dog", "an animal") if not res then ngx.say("failed to set dog: ", err) return end ngx.say("set dog: ", res) redis:close() '; } --- request GET /t --- response_body set dog: OK --- no_error_log [error] === TEST 2: test we can try a list of hosts, and connect to the first working one --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local redis_connector = require "resty.redis.connector" local rc = redis_connector.new() rc:set_connect_timeout(100) local hosts = { { host = "127.0.0.1", port = 1 }, { host = "127.0.0.1", port = 2 }, { host = "127.0.0.1", port = $TEST_NGINX_REDIS_PORT }, } local redis, err, previous_errors = rc:try_hosts(hosts) if not redis then ngx.say("failed to connect: ", err) return end -- Print the failed connection errors ngx.say("connection 1 error: ", err) ngx.say("connection 2 error: ", previous_errors[1]) local res, err = redis:set("dog", "an animal") if not res then ngx.say("failed to set dog: ", err) return end ngx.say("set dog: ", res) redis:close() '; } --- request GET /t --- response_body connection 1 error: connection refused connection 2 error: connection refused set dog: OK --- error_log 111: Connection refused === TEST 3: Test connect_to_host directly --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local redis_connector = require "resty.redis.connector" local rc = redis_connector.new() local host = { host = "127.0.0.1", port = $TEST_NGINX_REDIS_PORT } local redis, err = rc:connect_to_host(host) if not redis then ngx.say("failed to connect: ", err) return end local res, err = redis:set("dog", "an animal") if not res then ngx.say("failed to set dog: ", err) return end ngx.say("set dog: ", res) redis:close() '; } --- request GET /t --- response_body set dog: OK --- no_error_log [error] === TEST 4: Test connect options override --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local redis_connector = require "resty.redis.connector" local rc = redis_connector.new() local host = { host = "127.0.0.1", port = $TEST_NGINX_REDIS_PORT, db = 1, } local redis, err = rc:connect_to_host(host) if not redis then ngx.say("failed to connect: ", err) return end local res, err = redis:set("dog", "an animal") if not res then ngx.say("failed to set dog: ", err) return end ngx.say("set dog: ", res) redis:select(2) ngx.say(redis:get("dog")) redis:select(1) ngx.say(redis:get("dog")) redis:close() '; } --- request GET /t --- response_body set dog: OK null an animal --- no_error_log [error] lua-resty-redis-connector-0.03/t/sentinel.t000066400000000000000000000067741263206167700207560ustar00rootroot00000000000000# vim:set ft= ts=4 sw=4 et: use Test::Nginx::Socket::Lua; use Cwd qw(cwd); #repeat_each(2); plan tests => repeat_each() * (3 * blocks()); my $pwd = cwd(); our $HttpConfig = qq{ lua_package_path "$pwd/lib/?.lua;;"; }; $ENV{TEST_NGINX_RESOLVER} = '8.8.8.8'; $ENV{TEST_NGINX_REDIS_PORT} ||= 6379; no_long_string(); #no_diff(); run_tests(); __DATA__ === TEST 1: Get the master --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local redis_connector = require "resty.redis.connector" local rc = redis_connector.new() local sentinel, err = rc:connect{ url = "redis://127.0.0.1:6381" } if not sentinel then ngx.say("failed to connect: ", err) return end local redis_sentinel = require "resty.redis.sentinel" local master, err = redis_sentinel.get_master(sentinel, "mymaster") if not master then ngx.say(err) else ngx.say("host: ", master.host) ngx.say("port: ", master.port) end sentinel:close() '; } --- request GET /t --- response_body host: 127.0.0.1 port: 6379 --- no_error_log [error] === TEST 2: Get slaves --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local redis_connector = require "resty.redis.connector" local rc = redis_connector.new() local sentinel, err = rc:connect{ url = "redis://127.0.0.1:6381" } if not sentinel then ngx.say("failed to connect: ", err) return end local redis_sentinel = require "resty.redis.sentinel" local slaves, err = redis_sentinel.get_slaves(sentinel, "mymaster") if not slaves then ngx.say(err) else -- order is undefined local all = {} for i,slave in ipairs(slaves) do all[i] = tonumber(slave.port) end table.sort(all) for _,p in ipairs(all) do ngx.say(p) end end sentinel:close() '; } --- request GET /t --- response_body 6378 6380 --- no_error_log [error] === TEST 3: Get only healthy slaves --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local redis = require "resty.redis" local r = redis.new() r:connect("127.0.0.1", 6378) r:slaveof("127.0.0.1", 7000) ngx.sleep(9) local redis_connector = require "resty.redis.connector" local rc = redis_connector.new() local sentinel, err = rc:connect{ url = "redis://127.0.0.1:6381" } if not sentinel then ngx.say("failed to connect: ", err) return end local redis_sentinel = require "resty.redis.sentinel" local slaves, err = redis_sentinel.get_slaves(sentinel, "mymaster") if not slaves then ngx.say(err) else for _,slave in ipairs(slaves) do ngx.say("host: ", slave.host) ngx.say("port: ", slave.port) end end sentinel:close() '; } --- request GET /t --- timeout: 10 --- response_body host: 127.0.0.1 port: 6380 --- no_error_log [error] lua-resty-redis-connector-0.03/util/000077500000000000000000000000001263206167700174445ustar00rootroot00000000000000lua-resty-redis-connector-0.03/util/lua-releng000077500000000000000000000040511263206167700214250ustar00rootroot00000000000000#!/usr/bin/env perl use strict; use warnings; sub file_contains ($$); my $version; for my $file (map glob, qw{ *.lua lib/*.lua lib/*/*.lua lib/*/*/*.lua lib/*/*/*/*.lua lib/*/*/*/*/*.lua }) { # Check the sanity of each .lua file open my $in, $file or die "ERROR: Can't open $file for reading: $!\n"; my $found_ver; while (<$in>) { my ($ver, $skipping); if (/(?x) (?:_VERSION) \s* = .*? ([\d\.]*\d+) (.*? SKIP)?/) { my $orig_ver = $ver = $1; $found_ver = 1; # $skipping = $2; $ver =~ s{^(\d+)\.(\d{3})(\d{3})$}{join '.', int($1), int($2), int($3)}e; warn "$file: $orig_ver ($ver)\n"; } elsif (/(?x) (?:_VERSION) \s* = \s* ([a-zA-Z_]\S*)/) { warn "$file: $1\n"; $found_ver = 1; last; } if ($ver and $version and !$skipping) { if ($version ne $ver) { # die "$file: $ver != $version\n"; } } elsif ($ver and !$version) { $version = $ver; } } if (!$found_ver) { warn "WARNING: No \"_VERSION\" or \"version\" field found in `$file`.\n"; } close $in; print "Checking use of Lua global variables in file $file ...\n"; system("luac -p -l $file | grep ETGLOBAL | grep -vE 'require|type|tostring|error|ngx|ndk|jit|setmetatable|getmetatable|string|table|io|os|print|tonumber|math|pcall|xpcall|unpack|pairs|ipairs|assert|module|package|coroutine|[gs]etfenv|next|select|rawset|rawget|debug'"); #file_contains($file, "attempt to write to undeclared variable"); system("grep -H -n -E --color '.{120}' $file"); } sub file_contains ($$) { my ($file, $regex) = @_; open my $in, $file or die "Cannot open $file fo reading: $!\n"; my $content = do { local $/; <$in> }; close $in; #print "$content"; return scalar ($content =~ /$regex/); } if (-d 't') { for my $file (map glob, qw{ t/*.t t/*/*.t t/*/*/*.t }) { system(qq{grep -H -n --color -E '\\--- ?(ONLY|LAST)' $file}); } }