Codebase list lua-nginx-redis-connector / b39bd71
Import upstream version 0.11.0 Debian Janitor 2 years ago
15 changed file(s) with 781 addition(s) and 339 deletion(s). Raw diff Collapse all Expand all
0 github: pintsized
0 std = "ngx_lua"
1 redefined = false
0 sudo: required
1 dist: focal
2
3 os: linux
4
5 language: c
6
7 compiler: gcc
8
9 addons:
10 apt:
11 sources:
12 - sourceline: 'ppa:redislabs/redis'
13 packages:
14 - luarocks
15 - lsof
16
17 cache:
18 directories:
19 - download-cache
20
21 env:
22 global:
23 - JOBS=3
24 - NGX_BUILD_JOBS=$JOBS
25 - LUAJIT_PREFIX=/opt/luajit21
26 - LUAJIT_LIB=$LUAJIT_PREFIX/lib
27 - LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1
28 - LUA_INCLUDE_DIR=$LUAJIT_INC
29 - OPENSSL_PREFIX=/opt/ssl
30 - OPENSSL_LIB=$OPENSSL_PREFIX/lib
31 - OPENSSL_INC=$OPENSSL_PREFIX/include
32 - OPENSSL_VER=1.1.1f
33 - LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH
34 - TEST_NGINX_SLEEP=0.006
35 - LUACHECK_VER=0.21.1
36 jobs:
37 - NGINX_VERSION=1.19.9
38
39 before_install:
40 # we can't update redis in addons.apt.packages as updated package automatically tries to start and immediately fails
41 - echo exit 101 | sudo tee /usr/sbin/policy-rc.d
42 - sudo chmod +x /usr/sbin/policy-rc.d
43 - sudo apt-get install -y redis-server
44 - sudo luarocks install luacov
45 - sudo luarocks install lua-resty-redis
46 - sudo luarocks install luacheck $LUACHECK_VER
47 - luacheck -q .
48
49 install:
50 - if [ ! -d download-cache ]; then mkdir download-cache; fi
51 - if [ ! -f download-cache/openssl-$OPENSSL_VER.tar.gz ]; then wget -O download-cache/openssl-$OPENSSL_VER.tar.gz https://www.openssl.org/source/openssl-$OPENSSL_VER.tar.gz; fi
52 - sudo apt-get install -qq -y cpanminus axel
53 - sudo cpanm --notest Test::Nginx > build.log 2>&1 || (cat build.log && exit 1)
54 - git clone https://github.com/openresty/openresty.git ../openresty
55 - git clone https://github.com/openresty/nginx-devel-utils.git
56 - git clone https://github.com/openresty/lua-cjson.git
57 - git clone https://github.com/openresty/lua-nginx-module.git ../lua-nginx-module
58 - git clone https://github.com/openresty/stream-lua-nginx-module.git ../stream-lua-nginx-module
59 - git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core
60 - git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache
61 - git clone https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module
62 - git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx
63 - git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git
64
65 script:
66 - sudo iptables -A OUTPUT -p tcp --dst 127.0.0.2 --dport 12345 -j DROP
67 - cd luajit2/
68 - make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT' > build.log 2>&1 || (cat build.log && exit 1)
69 - sudo make install PREFIX=$LUAJIT_PREFIX > build.log 2>&1 || (cat build.log && exit 1)
70 - cd ../lua-cjson && make && sudo PATH=$PATH make install && cd ..
71 - tar zxf download-cache/openssl-$OPENSSL_VER.tar.gz
72 - cd openssl-$OPENSSL_VER/
73 - ./config shared --prefix=$OPENSSL_PREFIX -DPURIFY > build.log 2>&1 || (cat build.log && exit 1)
74 - make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1)
75 - sudo make PATH=$PATH install_sw > build.log 2>&1 || (cat build.log && exit 1)
76 - cd ..
77 - export PATH=$PWD/work/nginx/sbin:$PWD/nginx-devel-utils:$PATH
78 - export NGX_BUILD_CC=$CC
79 - ngx-build $NGINX_VERSION --with-ipv6 --with-http_realip_module --with-http_ssl_module --with-cc-opt="-I$OPENSSL_INC" --with-ld-opt="-L$OPENSSL_LIB -Wl,-rpath,$OPENSSL_LIB" --add-module=../echo-nginx-module --add-module=../lua-nginx-module --add-module=../stream-lua-nginx-module --with-stream --with-stream_ssl_module --with-debug > build.log 2>&1 || (cat build.log && exit 1)
80 - nginx -V
81 - ldd `which nginx`|grep -E 'luajit|ssl|pcre'
82 - mkdir -p tmp
83 - TMP_DIR=$PWD/tmp make test_all
22 OPENRESTY_PREFIX = /usr/local/openresty
33
44 TEST_FILE ?= t
5 SENTINEL_TEST_FILE ?= $(TEST_FILE)/sentinel
5 TMP_DIR ?= /tmp
66
77 REDIS_CMD = redis-server
88 SENTINEL_CMD = $(REDIS_CMD) --sentinel
1010 REDIS_SOCK = /redis.sock
1111 REDIS_PID = /redis.pid
1212 REDIS_LOG = /redis.log
13 REDIS_PREFIX = /tmp/redis-
13 REDIS_PREFIX = $(TMP_DIR)/redis-
1414
1515 # Overrideable redis test variables
16 TEST_REDIS_PORTS ?= 6379 6380 6378
17 TEST_REDIS_DATABASE ?= 1
16 TEST_REDIS_PORT ?= 6380
17 TEST_REDIS_PORT_SL1 ?= 6381
18 TEST_REDIS_PORT_SL2 ?= 6382
19 TEST_REDIS_PORT_AUTH ?= 6383
20 TEST_REDIS_PORTS ?= $(TEST_REDIS_PORT) $(TEST_REDIS_PORT_SL1) $(TEST_REDIS_PORT_SL2)
21 TEST_REDIS_PORTS_ALL ?= $(TEST_REDIS_PORTS) $(TEST_REDIS_PORT_AUTH)
22 TEST_REDIS_DATABASE ?= 1
23 TEST_REDIS_SOCKET ?= $(REDIS_PREFIX)$(TEST_REDIS_PORT)$(REDIS_SOCK)
1824
19 REDIS_FIRST_PORT := $(firstword $(TEST_REDIS_PORTS))
20 REDIS_SLAVE_ARG := --slaveof 127.0.0.1 $(REDIS_FIRST_PORT)
21 REDIS_CLI := redis-cli -p $(REDIS_FIRST_PORT) -n $(TEST_REDIS_DATABASE)
22
23 # Override socket for running make test on its own
24 # (make test TEST_REDIS_SOCKET=/path/to/sock.sock)
25 TEST_REDIS_SOCKET ?= $(REDIS_PREFIX)$(REDIS_FIRST_PORT)$(REDIS_SOCK)
25 REDIS_SLAVE_ARG := --slaveof 127.0.0.1 $(TEST_REDIS_PORT)
26 REDIS_CLI := redis-cli -p $(TEST_REDIS_PORT) -n $(TEST_REDIS_DATABASE)
2627
2728 # Overrideable redis + sentinel test variables
28 TEST_SENTINEL_PORTS ?= 6381 6382 6383
29 TEST_SENTINEL_PORT1 ?= 6390
30 TEST_SENTINEL_PORT2 ?= 6391
31 TEST_SENTINEL_PORT3 ?= 6392
32 TEST_SENTINEL_PORT_AUTH ?= 6393
33 TEST_SENTINEL_PORTS ?= $(TEST_SENTINEL_PORT1) $(TEST_SENTINEL_PORT2) $(TEST_SENTINEL_PORT3)
34 TEST_SENTINEL_PORTS_ALL ?= $(TEST_SENTINEL_PORTS) $(TEST_SENTINEL_PORT_AUTH)
2935 TEST_SENTINEL_MASTER_NAME ?= mymaster
3036 TEST_SENTINEL_PROMOTION_TIME ?= 20
3137
3238 # Command line arguments for redis tests
3339 TEST_REDIS_VARS = PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$(PATH) \
34 TEST_REDIS_SOCKET=unix://$(TEST_REDIS_SOCKET) \
35 TEST_REDIS_DATABASE=$(TEST_REDIS_DATABASE) \
40 TEST_NGINX_REDIS_PORT=$(TEST_REDIS_PORT) \
41 TEST_NGINX_REDIS_PORT_SL1=$(TEST_REDIS_PORT_SL1) \
42 TEST_NGINX_REDIS_PORT_SL2=$(TEST_REDIS_PORT_SL2) \
43 TEST_NGINX_REDIS_PORT_AUTH=$(TEST_REDIS_PORT_AUTH) \
44 TEST_NGINX_REDIS_SOCKET=unix:$(TEST_REDIS_SOCKET) \
45 TEST_NGINX_REDIS_DATABASE=$(TEST_REDIS_DATABASE) \
3646 TEST_NGINX_NO_SHUFFLE=1
3747
3848 # Command line arguments for sentinel tests
3949 TEST_SENTINEL_VARS = PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$(PATH) \
40 TEST_SENTINEL_PORT=$(firstword $(TEST_SENTINEL_PORTS)) \
41 TEST_SENTINEL_MASTER_NAME=$(TEST_SENTINEL_MASTER_NAME) \
42 TEST_REDIS_DATABASE=$(TEST_REDIS_DATABASE) \
50 TEST_NGINX_REDIS_PORT=$(TEST_NGINX_REDIS_PORT) \
51 TEST_NGINX_REDIS_PORT_SL1=$(TEST_NGINX_REDIS_PORT_SL1) \
52 TEST_NGINX_REDIS_PORT_SL2=$(TEST_NGINX_REDIS_PORT_SL2) \
53 TEST_NGINX_SENTINEL_PORT1=$(TEST_NGINX_SENTINEL_PORT1) \
54 TEST_NGINX_SENTINEL_PORT2=$(TEST_NGINX_SENTINEL_PORT2) \
55 TEST_NGINX_SENTINEL_PORT3=$(TEST_NGINX_SENTINEL_PORT3) \
56 TEST_NGINX_SENTINEL_PORT_AUTH=$(TEST_NGINX_SENTINEL_AUTH) \
57 TEST_NGINX_SENTINEL_MASTER_NAME=$(TEST_NGINX_SENTINEL_MASTER_NAME) \
58 TEST_NGINX_REDIS_DATABASE=$(TEST_NGINX_REDIS_DATABASE) \
4359 TEST_NGINX_NO_SHUFFLE=1
4460
4561 # Sentinel configuration can only be set by a config file
4662 define TEST_SENTINEL_CONFIG
47 sentinel monitor $(TEST_SENTINEL_MASTER_NAME) 127.0.0.1 $(REDIS_FIRST_PORT) 2
63 sentinel monitor $(TEST_SENTINEL_MASTER_NAME) 127.0.0.1 $(TEST_REDIS_PORT) 2
4864 sentinel down-after-milliseconds $(TEST_SENTINEL_MASTER_NAME) 2000
4965 sentinel failover-timeout $(TEST_SENTINEL_MASTER_NAME) 10000
5066 sentinel parallel-syncs $(TEST_SENTINEL_MASTER_NAME) 5
5167 endef
68 define TEST_SENTINEL_AUTH_CONFIG
69 sentinel monitor $(TEST_SENTINEL_MASTER_NAME) 127.0.0.1 $(TEST_REDIS_PORT_AUTH) 1
70 endef
5271
53 export TEST_SENTINEL_CONFIG
72 export TEST_SENTINEL_CONFIG TEST_SENTINEL_AUTH_CONFIG
5473
5574 SENTINEL_CONFIG_FILE = /tmp/sentinel-test-config
75 SENTINEL_AUTH_CONFIG_FILE = /tmp/sentinel-auth-test-config
5676
5777
5878 PREFIX ?= /usr/local
7595 test: test_redis
7696 test_all: start_redis_instances sleep test_redis stop_redis_instances
7797
98 check:
99 luacheck lib
100
78101 sleep:
79102 sleep 3
80103
81104 start_redis_instances: check_ports create_sentinel_config
105 $(REDIS_CMD) --version
106
82107 @$(foreach port,$(TEST_REDIS_PORTS), \
83 [[ "$(port)" != "$(REDIS_FIRST_PORT)" ]] && \
108 [[ "$(port)" != "$(TEST_REDIS_PORT)" ]] && \
84109 SLAVE="$(REDIS_SLAVE_ARG)" || \
85110 SLAVE="" && \
86111 $(MAKE) start_redis_instance args="$$SLAVE" port=$(port) \
87112 prefix=$(REDIS_PREFIX)$(port) && \
88113 ) true
114
115 $(MAKE) start_redis_instance \
116 args="--user redisuser on '>redisuserpass' '~*' '&*' '+@all'" \
117 port=$(TEST_REDIS_PORT_AUTH) \
118 prefix=$(REDIS_PREFIX)$(TEST_REDIS_PORT_AUTH)
89119
90120 @$(foreach port,$(TEST_SENTINEL_PORTS), \
91121 $(MAKE) start_redis_instance \
93123 prefix=$(REDIS_PREFIX)$(port) && \
94124 ) true
95125
126 $(MAKE) start_redis_instance \
127 args="$(SENTINEL_AUTH_CONFIG_FILE) --sentinel --user sentineluser on '>sentineluserpass' '~*' '&*' '+@all'" \
128 port=$(TEST_SENTINEL_PORT_AUTH) \
129 prefix=$(REDIS_PREFIX)$(TEST_SENTINEL_PORT_AUTH)
96130
97131 stop_redis_instances: delete_sentinel_config
98 -@$(foreach port,$(TEST_REDIS_PORTS) $(TEST_SENTINEL_PORTS), \
132 -@$(foreach port,$(TEST_REDIS_PORTS_ALL) $(TEST_SENTINEL_PORTS_ALL), \
99133 $(MAKE) stop_redis_instance cleanup_redis_instance port=$(port) \
100134 prefix=$(REDIS_PREFIX)$(port) && \
101135 ) true 2>&1 > /dev/null
104138 start_redis_instance:
105139 -@echo "Starting redis on port $(port) with args: \"$(args)\""
106140 -@mkdir -p $(prefix)
107 @$(REDIS_CMD) $(args) \
141 $(REDIS_CMD) $(args) \
108142 --pidfile $(prefix)$(REDIS_PID) \
109143 --bind 127.0.0.1 --port $(port) \
110144 --unixsocket $(prefix)$(REDIS_SOCK) \
130164 create_sentinel_config:
131165 -@echo "Creating $(SENTINEL_CONFIG_FILE)"
132166 @echo "$$TEST_SENTINEL_CONFIG" > $(SENTINEL_CONFIG_FILE)
167 -@echo "Creating $(SENTINEL_AUTH_CONFIG_FILE)"
168 @echo "$$TEST_SENTINEL_AUTH_CONFIG" > $(SENTINEL_AUTH_CONFIG_FILE)
133169
134170 delete_sentinel_config:
135171 -@echo "Removing $(SENTINEL_CONFIG_FILE)"
136172 @rm -f $(SENTINEL_CONFIG_FILE)
173 -@echo "Removing $(SENTINEL_AUTH_CONFIG_FILE)"
174 @rm -f $(SENTINEL_AUTH_CONFIG_FILE)
137175
138176 check_ports:
139 -@echo "Checking ports $(REDIS_PORTS)"
140 @$(foreach port,$(REDIS_PORTS),! lsof -i :$(port) &&) true 2>&1 > /dev/null
177 -@echo "Checking ports $(TEST_REDIS_PORTS_ALL) $(TEST_SENTINEL_PORTS_ALL)"
178 @$(foreach port,$(TEST_REDIS_PORTS_ALL) $(TEST_SENTINEL_PORTS_ALL),! lsof -i :$(port) &&) true 2>&1 > /dev/null
141179
142180 test_redis: flush_db
143181 util/lua-releng
00 # lua-resty-redis-connector
11
2 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).
2 [![Build
3 Status](https://travis-ci.org/ledgetech/lua-resty-redis-connector.svg?branch=master)](https://travis-ci.org/ledgetech/lua-resty-redis-connector)
4
5 Connection utilities for
6 [lua-resty-redis](https://github.com/openresty/lua-resty-redis), making it easy
7 and reliable to connect to Redis hosts, either directly or via [Redis
8 Sentinel](http://redis.io/topics/sentinel).
39
410
511 ## Synopsis
1723 ```lua
1824 local rc = require("resty.redis.connector").new({
1925 connect_timeout = 50,
26 send_timeout = 5000,
2027 read_timeout = 5000,
2128 keepalive_timeout = 30000,
2229 password = "mypass",
3643 ```lua
3744 local rc = require("resty.redis.connector").new({
3845 connect_timeout = 50,
46 send_timeout = 5000,
3947 read_timeout = 5000,
4048 keepalive_timeout = 30000,
4149
5260 local ok, err = rc:set_keepalive(redis)
5361 ```
5462
55 [connect](#connect) can be used to override some defaults given in [new](#new), which are pertinent to this connection only.
63 [connect](#connect) can be used to override some defaults given in [new](#new),
64 which are pertinent to this connection only.
5665
5766
5867 ```lua
7079
7180 ## DSN format
7281
73 If the `params.url` field is present then it will be parsed to set the other params. Any manually specified params will override values given in the DSN.
74
75 *Note: this is a behaviour change as of v0.06. Previously, the DSN values would take precedence.*
82 If the `params.url` field is present then it will be parsed to set the other
83 params. Any manually specified params will override values given in the DSN.
84
85 *Note: this is a behaviour change as of v0.06. Previously, the DSN values would
86 take precedence.*
7687
7788 ### Direct Redis connections
7889
7990 The format for connecting directly to Redis is:
8091
81 `redis://PASSWORD@HOST:PORT/DB`
82
83 The `PASSWORD` and `DB` fields are optional, all other components are required.
92 `redis://USERNAME:PASSWORD@HOST:PORT/DB`
93
94 The `USERNAME`, `PASSWORD` and `DB` fields are optional, all other components
95 are required.
96
97 Use of username requires Redis 6.0.0 or newer.
8498
8599 ### Connections via Redis Sentinel
86100
87101 When connecting via Redis Sentinel, the format is as follows:
88102
89 `sentinel://PASSWORD@MASTER_NAME:ROLE/DB`
90
91 Again, `PASSWORD` and `DB` are optional. `ROLE` must be either `m` or `s` for master / slave respectively.
103 `sentinel://USERNAME:PASSWORD@MASTER_NAME:ROLE/DB`
104
105 Again, `USERNAME`, `PASSWORD` and `DB` are optional. `ROLE` must be either `m`
106 or `s` for master / slave respectively.
107
108 On versions of Redis newer than 5.0.1, Sentinels can optionally require their
109 own password. If enabled, provide this password in the `sentinel_password`
110 parameter. On Redis 6.2.0 and newer you can pass username using
111 `sentinel_username` parameter.
92112
93113 A table of `sentinels` must also be supplied. e.g.
94114
96116 local redis, err = rc:connect{
97117 url = "sentinel://mymaster:a/2",
98118 sentinels = {
99 { host = "127.0.0.1", port = 26379" },
100 }
119 { host = "127.0.0.1", port = 26379 },
120 },
121 sentinel_username = "default",
122 sentinel_password = "password"
101123 }
102124 ```
103125
104126 ## Proxy Mode
105127
106 Enable the `connection_is_proxied` parameter if connecting to Redis through a proxy service (e.g. Twemproxy).
107 These proxies generally only support a limited sub-set of Redis commands, those which do not require state and do not affect multiple keys.
108 Databases and transactions are also not supported.
109
110 Proxy mode will disable switching to a DB on connect.
111 Unsupported commands (defaults to those not supported by Twemproxy) will return `nil, err` immediately rather than being sent to the proxy, which can result in dropped connections.
128 Enable the `connection_is_proxied` parameter if connecting to Redis through a
129 proxy service (e.g. Twemproxy). These proxies generally only support a limited
130 sub-set of Redis commands, those which do not require state and do not affect
131 multiple keys. Databases and transactions are also not supported.
132
133 Proxy mode will disable switching to a DB on connect. Unsupported commands
134 (defaults to those not supported by Twemproxy) will return `nil, err`
135 immediately rather than being sent to the proxy, which can result in dropped
136 connections.
112137
113138 `discard` will not be sent when adding connections to the keepalive pool
114139
115140
116141 ## Disabled commands
117142
118 If configured as a table of commands, the command methods will be replaced by a function which immediately returns `nil, err` without forwarding the command to the server
143 If configured as a table of commands, the command methods will be replaced by a
144 function which immediately returns `nil, err` without forwarding the command to
145 the server
119146
120147 ## Default Parameters
121148
123150 ```lua
124151 {
125152 connect_timeout = 100,
153 send_timeout = 1000,
126154 read_timeout = 1000,
127 connection_options = {}, -- pool, etc
128155 keepalive_timeout = 60000,
129156 keepalive_poolsize = 30,
157
158 -- ssl, ssl_verify, server_name, pool, pool_size, backlog
159 -- see: https://github.com/openresty/lua-resty-redis#connect
160 connection_options = {},
130161
131162 host = "127.0.0.1",
132163 port = "6379",
133164 path = "", -- unix socket path, e.g. /tmp/redis.sock
165 username = "",
134166 password = "",
167 sentinel_username = "",
168 sentinel_password = "",
135169 db = 0,
136170
137171 master_name = "mymaster",
162196
163197 `syntax: rc = redis_connector.new(params)`
164198
165 Creates the Redis Connector object, overring default params with the ones given. In case of failures, returns `nil` and a string describing the error.
199 Creates the Redis Connector object, overring default params with the ones given.
200 In case of failures, returns `nil` and a string describing the error.
166201
167202
168203 ### connect
169204
170205 `syntax: redis, err = rc:connect(params)`
171206
172 Attempts to create a connection, according to the [params](#parameters) supplied, falling back to defaults given in `new` or the predefined defaults. If a connection cannot be made, returns `nil` and a string describing the reason.
173
174 Note that `params` given here do not change the connector's own configuration, and are only used to alter this particular connection operation. As such, the following parameters have no meaning when given in `connect`.
207 Attempts to create a connection, according to the [params](#parameters)
208 supplied, falling back to defaults given in `new` or the predefined defaults. If
209 a connection cannot be made, returns `nil` and a string describing the reason.
210
211 Note that `params` given here do not change the connector's own configuration,
212 and are only used to alter this particular connection operation. As such, the
213 following parameters have no meaning when given in `connect`.
175214
176215 * `keepalive_poolsize`
177216 * `keepalive_timeout`
183222
184223 `syntax: ok, err = rc:set_keepalive(redis)`
185224
186 Attempts to place the given Redis connection on the keepalive pool, according to timeout and poolsize params given in `new` or the predefined defaults.
187
188 This allows an application to release resources without having to keep track of application wide keepalive settings.
225 Attempts to place the given Redis connection on the keepalive pool, according to
226 timeout and poolsize params given in `new` or the predefined defaults.
227
228 This allows an application to release resources without having to keep track of
229 application wide keepalive settings.
189230
190231 Returns `1` or in the case of error, `nil` and a string describing the error.
191232
192233
193234 ## Utilities
194235
195 The following methods are not typically needed, but may be useful if a custom interface is required.
236 The following methods are not typically needed, but may be useful if a custom
237 interface is required.
196238
197239
198240 ### connect_via_sentinel
199241
200242 `syntax: redis, err = rc:connect_via_sentinel(params)`
201243
202 Returns a Redis connection by first accessing a sentinel as supplied by the `params.sentinels` table,
203 and querying this with the `params.master_name` and `params.role`.
244 Returns a Redis connection by first accessing a sentinel as supplied by the
245 `params.sentinels` table, and querying this with the `params.master_name` and
246 `params.role`.
204247
205248
206249 ### try_hosts
221264
222265 `syntax: master, err = sentinel.get_master(sentinel, master_name)`
223266
224 Given a connected Sentinel instance and a master name, will return the current master Redis instance.
267 Given a connected Sentinel instance and a master name, will return the current
268 master Redis instance.
225269
226270
227271 ### sentinel.get_slaves
228272
229273 `syntax: slaves, err = sentinel.get_slaves(sentinel, master_name)`
230274
231 Given a connected Sentinel instance and a master name, will return a list of registered slave Redis instances.
275 Given a connected Sentinel instance and a master name, will return a list of
276 registered slave Redis instances.
232277
233278
234279 # Author
244289
245290 All rights reserved.
246291
247 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
248
249 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
250
251 * 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.
252
253 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.
292 Redistribution and use in source and binary forms, with or without modification,
293 are permitted provided that the following conditions are met:
294
295 * Redistributions of source code must retain the above copyright notice, this
296 list of conditions and the following disclaimer.
297
298 * Redistributions in binary form must reproduce the above copyright notice, this
299 list of conditions and the following disclaimer in the documentation and/or
300 other materials provided with the distribution.
301
302 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
303 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
304 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
305 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
306 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
307 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
308 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
309 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
310 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
311 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44 license=2bsd
55 lib_dir=lib
66 doc_dir=lib
7 repo_link=https://github.com/pintsized/lua-resty-redis-connector
7 repo_link=https://github.com/ledgetech/lua-resty-redis-connector
88 main_module=lib/resty/redis/connector.lua
44 local ngx_ERR = ngx.ERR
55 local ngx_re_match = ngx.re.match
66
7 local str_find = string.find
8 local str_sub = string.sub
79 local tbl_remove = table.remove
810 local tbl_sort = table.sort
911 local ok, tbl_new = pcall(require, "table.new")
1012 if not ok then
11 tbl_new = function (narr, nrec) return {} end
13 tbl_new = function (narr, nrec) return {} end -- luacheck: ignore 212
1214 end
1315
1416 local redis = require("resty.redis")
2123 -- A metatable which prevents undefined fields from being created / accessed
2224 local fixed_field_metatable = {
2325 __index =
24 function(t, k)
26 function(t, k) -- luacheck: ignore 212
2527 error("field " .. tostring(k) .. " does not exist", 3)
2628 end,
2729 __newindex =
28 function(t, k, v)
30 function(t, k, v) -- luacheck: ignore 212
2931 error("attempt to create new field " .. tostring(k), 3)
3032 end,
3133 }
6567 if t1 == nil then t1 = {} end
6668 if defaults == nil then defaults = {} end
6769 if type(t1) == "table" and type(defaults) == "table" then
68 local mt = getmetatable(defaults)
6970 local copy = {}
7071 for t1_key, t1_value in next, t1, nil do
7172 copy[tbl_copy(t1_key)] = tbl_copy_merge_defaults(
8788 local DEFAULTS = setmetatable({
8889 connect_timeout = 100,
8990 read_timeout = 1000,
91 send_timeout = 1000,
9092 connection_options = {}, -- pool, etc
9193 keepalive_timeout = 60000,
9294 keepalive_poolsize = 30,
9496 host = "127.0.0.1",
9597 port = 6379,
9698 path = "", -- /tmp/redis.sock
99 username = "",
97100 password = "",
101 sentinel_username = "",
102 sentinel_password = "",
98103 db = 0,
99104 url = "", -- DSN url
100105
123128
124129
125130 local _M = {
126 _VERSION = '0.06',
131 _VERSION = '0.11.0',
127132 }
128133
129134 local mt = { __index = _M }
147152 fields = { "password", "master_name", "role", "db" }
148153 end
149154
150 -- password may not be present
155 -- username/password may not be present
151156 if #m < 5 then tbl_remove(fields, 1) end
152157
153158 local roles = { m = "master", s = "slave" }
154159
155160 local parsed_params = {}
156161
157 for i,v in ipairs(fields) do
158 parsed_params[v] = m[i + 1]
162 for i, v in ipairs(fields) do
163 if v == "db" or v == "port" then
164 parsed_params[v] = tonumber(m[i + 1])
165 else
166 parsed_params[v] = m[i + 1]
167 end
168
159169 if v == "role" then
160170 parsed_params[v] = roles[parsed_params[v]]
161171 end
162172 end
163173
174 local colon_pos = str_find(parsed_params.password or "", ":", 1, true)
175 if colon_pos then
176 parsed_params.username = str_sub(parsed_params.password, 1, colon_pos - 1)
177 parsed_params.password = str_sub(parsed_params.password, colon_pos + 1)
178 end
179
164180 return tbl_copy_merge_defaults(params, parsed_params)
165181 end
182
183 return params
166184 end
167185 _M.parse_dsn = parse_dsn
168186
169187
170 function _M.new(config)
171 -- Fill out gaps in config with any dsn params
188 -- Fill out gaps in config with any dsn params
189 local function apply_dsn(config)
172190 if config and config.url then
173191 local err
174192 config, err = parse_dsn(config)
175 if not ok then ngx_log(ngx_ERR, err) end
176 end
193 if err then ngx_log(ngx_ERR, err) end
194 end
195 return config
196 end
197
198
199 -- For backwards compatability; previously send_timeout was implicitly the
200 -- same as read_timeout. So if only the latter is given, ensure the former
201 -- matches.
202 local function apply_fallback_send_timeout(config)
203 if config and not config.send_timeout and config.read_timeout then
204 config.send_timeout = config.read_timeout
205 end
206 end
207
208
209 function _M.new(config)
210 config = apply_dsn(config)
211 apply_fallback_send_timeout(config)
177212
178213 local ok, config = pcall(tbl_copy_merge_defaults, config, DEFAULTS)
179214 if not ok then
194229
195230
196231 function _M.connect(self, params)
197 if params and params.url then
198 local err
199 params, err = parse_dsn(params)
200 if not ok then ngx_log(ngx_ERR, err) end
201 end
232 params = apply_dsn(params)
233 apply_fallback_send_timeout(params)
202234
203235 params = tbl_copy_merge_defaults(params, self.config)
204236
224256 local master_name = params.master_name
225257 local role = params.role
226258 local db = params.db
259 local username = params.username
227260 local password = params.password
261 local sentinel_username = params.sentinel_username
262 local sentinel_password = params.sentinel_password
263 if sentinel_password then
264 for _, host in ipairs(sentinels) do
265 host.username = sentinel_username
266 host.password = sentinel_password
267 end
268 end
228269
229270 local sentnl, err, previous_errors = self:try_hosts(sentinels)
230271 if not sentnl then
233274
234275 if role == "master" then
235276 local master, err = get_master(sentnl, master_name)
236 if master then
237 master.db = db
238 master.password = password
239
240 local redis, err = self:connect_to_host(master)
241 if redis then
242 sentnl:set_keepalive()
243 return redis, err
244 else
245 if role == "master" then
246 return nil, err
247 end
248 end
249 end
250
277 if not master then
278 return nil, err
279 end
280
281 sentnl:set_keepalive()
282
283 master.db = db
284 master.username = username
285 master.password = password
286
287 local redis, err = self:connect_to_host(master)
288 if not redis then
289 return nil, err
290 end
291
292 return redis
251293 else
252294 -- We want a slave
253295 local slaves, err = get_slaves(sentnl, master_name)
254 sentnl:set_keepalive()
255
256296 if not slaves then
257297 return nil, err
258298 end
259299
300 sentnl:set_keepalive()
301
260302 -- Put any slaves on 127.0.0.1 at the front
261303 tbl_sort(slaves, sort_by_localhost)
262304
263305 if db or password then
264 for i,slave in ipairs(slaves) do
306 for _, slave in ipairs(slaves) do
265307 slave.db = db
308 slave.username = username
266309 slave.password = password
267310 end
268311 end
270313 local slave, err, previous_errors = self:try_hosts(slaves)
271314 if not slave then
272315 return nil, err, previous_errors
273 else
274 return slave
275 end
316 end
317
318 return slave
276319 end
277320 end
278321
297340
298341 function _M.connect_to_host(self, host)
299342 local r = redis.new()
300 local config = self.config
301 r:set_timeout(config.connect_timeout)
343
344 -- config options in 'host' should override the global defaults
345 -- host contains keys that aren't in config
346 -- this can break tbl_copy_merge_defaults, hence the mannual loop here
347 local config = tbl_copy(self.config)
348 for k, _ in pairs(config) do
349 if host[k] then
350 config[k] = host[k]
351 end
352 end
353
354 r:set_timeouts(
355 config.connect_timeout,
356 config.send_timeout,
357 config.read_timeout
358 )
302359
303360 -- Stub out methods for disabled commands
304361 if next(config.disabled_commands) then
305362 for _, cmd in ipairs(config.disabled_commands) do
306 r[cmd] = function(...)
363 r[cmd] = function()
307364 return nil, ("Command "..cmd.." is disabled")
308365 end
309366 end
329386 if not ok then
330387 return nil, err
331388 else
332 r:set_timeout(self, config.read_timeout)
333
389 local username = host.username
334390 local password = host.password
335391 if password and password ~= "" then
336 local res, err = r:auth(password)
392 local res
393 -- usernames are supported only on Redis 6+, so use new AUTH form only when absolutely necessary
394 if username and username ~= "" and username ~= "default" then
395 res, err = r:auth(username, password)
396 else
397 res, err = r:auth(password)
398 end
337399 if err then
338 ngx_log(ngx_ERR, err)
339400 return res, err
340401 end
341402 end
342403
343404 -- No support for DBs in proxied Redis.
344405 if config.connection_is_proxied ~= true and host.db ~= nil then
345 r:select(host.db)
406 local res, err = r:select(host.db)
407
408 -- SELECT will fail if we are connected to sentinel:
409 -- detect it and ignore error message it that's the case
410 if err and str_find(err, "ERR unknown command") then
411 local role = r:role()
412 if role and role[1] == "sentinel" then
413 err = nil
414 end
415 end
416 if err then
417 return res, err
418 end
346419 end
347420 return r, nil
348421 end
349422 end
350423
351424
352 local function set_keepalive(self, redis)
425 function _M.set_keepalive(self, redis)
353426 -- Restore connection to "NORMAL" before putting into keepalive pool,
354427 -- ignoring any errors.
355428 -- Proxied Redis does not support transactions.
362435 config.keepalive_timeout, config.keepalive_poolsize
363436 )
364437 end
365 _M.set_keepalive = set_keepalive
366
367438
368439
369440 -- Deprecated: use config table in new() or connect() instead.
44 local tbl_insert = table.insert
55 local ok, tbl_new = pcall(require, "table.new")
66 if not ok then
7 tbl_new = function (narr, nrec) return {} end
7 tbl_new = function (narr, nrec) return {} end -- luacheck: ignore 212
88 end
99
1010
1111 local _M = {
12 _VERSION = '0.06'
12 _VERSION = '0.11.0'
1313 }
1414
1515
2020 )
2121 if res and res ~= ngx_null and res[1] and res[2] then
2222 return { host = res[1], port = res[2] }
23 elseif res == ngx_null then
24 return nil, "invalid master name"
2325 else
2426 return nil, err
2527 end
3840 host[slave[i]] = slave[i + 1]
3941 end
4042
41 if host["master-link-status"] == "ok" then
43 local master_link_status_ok = host["master-link-status"] == "ok"
44 local is_down = host["flags"] and (string.find(host["flags"],"s_down")
45 or string.find(host["flags"],"disconnected"))
46 if master_link_status_ok and not is_down then
4247 host.host = host.ip -- for parity with other functions
4348 tbl_insert(hosts, host)
4449 end
+0
-27
lua-resty-redis-connector-0.06-0.rockspec less more
0 package = "lua-resty-redis-connector"
1 version = "0.06-0"
2 source = {
3 url = "git://github.com/pintsized/lua-resty-redis-connector",
4 tag = "v0.06"
5 }
6 description = {
7 summary = "Connection utilities for lua-resty-redis.",
8 detailed = [[
9 Connection utilities for lua-resty-redis, making it easy and
10 reliable to connect to Redis hosts, either directly or via Redis
11 Sentinel.
12 ]],
13 homepage = "https://github.com/pintsized/lua-resty-redis-connector",
14 license = "2-clause BSD",
15 maintainer = "James Hurst <james@pintsized.co.uk>"
16 }
17 dependencies = {
18 "lua >= 5.1",
19 }
20 build = {
21 type = "builtin",
22 modules = {
23 ["resty.redis.connector"] = "lib/resty/redis/connector.lua",
24 ["resty.redis.sentinel"] = "lib/resty/redis/sentinel.lua"
25 }
26 }
0 package = "lua-resty-redis-connector"
1 version = "0.11.0-0"
2 source = {
3 url = "git://github.com/ledgetech/lua-resty-redis-connector",
4 tag = "v0.11.0"
5 }
6 description = {
7 summary = "Connection utilities for lua-resty-redis.",
8 detailed = [[
9 Connection utilities for lua-resty-redis, making it easy and
10 reliable to connect to Redis hosts, either directly or via Redis
11 Sentinel.
12 ]],
13 homepage = "https://github.com/ledgetech/lua-resty-redis-connector",
14 license = "2-clause BSD",
15 maintainer = "James Hurst <james@pintsized.co.uk>"
16 }
17 dependencies = {
18 "lua >= 5.1",
19 }
20 build = {
21 type = "builtin",
22 modules = {
23 ["resty.redis.connector"] = "lib/resty/redis/connector.lua",
24 ["resty.redis.sentinel"] = "lib/resty/redis/sentinel.lua"
25 }
26 }
88
99 our $HttpConfig = qq{
1010 lua_package_path "$pwd/lib/?.lua;;";
11 lua_socket_log_errors Off;
1112
1213 init_by_lua_block {
1314 require("luacov.runner").init()
1415 }
1516 };
1617
17 $ENV{TEST_NGINX_REDIS_PORT} ||= 6379;
18 $ENV{TEST_NGINX_REDIS_PORT} ||= 6380;
1819
1920 no_long_string();
2021 run_tests();
2627 --- config
2728 location /t {
2829 content_by_lua_block {
29 local rc = require("resty.redis.connector").new()
30
31 local redis = assert(rc:connect(), "rc:connect should return postively")
32 assert(redis:set("foo", "bar"), "redis:set should return positvely")
33 assert(redis:get("foo") == "bar", "get(foo) should return bar")
34 redis:close()
30 local rc = assert(require("resty.redis.connector").new())
3531 }
3632 }
3733 --- request
4743 content_by_lua_block {
4844 local config = {
4945 connect_timeout = 500,
50 port = 6380,
46 port = $TEST_NGINX_REDIS_PORT,
5147 db = 6,
5248 }
5349 local rc = require("resty.redis.connector").new(config)
7167 content_by_lua_block {
7268 local rc = require("resty.redis.connector").new({
7369 connect_timeout = 500,
74 port = 6380,
70 port = $TEST_NGINX_REDIS_PORT,
7571 db = 6,
7672 keepalive_poolsize = 10,
7773 })
8379 assert(rc.config.keepalive_poolsize == 10,
8480 "keepalive_poolsize should be 10")
8581
86 local redis = assert(rc:connect({ port = 6379 }),
87 "rc:connect should return positively")
82 local redis, err = rc:connect({
83 port = $TEST_NGINX_REDIS_PORT,
84 disabled_commands = { "set" }
85 })
86
87 if not redis or err then
88 ngx.log(ngx.ERR, "connect failed: ", err)
89 return
90 end
91
92 local ok, err = redis:set("foo", "bar")
93 assert( ok == nil and (string.find(err, "disabled") ~= nil) , "Disabled commands not passed through" )
8894 }
8995 }
9096 --- request
100106 content_by_lua_block {
101107 local rc, err = require("resty.redis.connector").new({
102108 connect_timeout = 500,
103 port = 6380,
109 port = $TEST_NGINX_REDIS_PORT,
104110 db = 6,
105111 foo = "bar",
106112 })
113119
114120 assert(require("resty.redis.connector").new({
115121 connect_timeout = 100,
122 send_timeout = 500,
116123 read_timeout = 1000,
117124 connection_options = { pool = "<host>::<port>" },
118125 keepalive_timeout = 60000,
119126 keepalive_poolsize = 30,
120127
121128 host = "127.0.0.1",
122 port = 6379,
129 port = $TEST_NGINX_REDIS_PORT,
123130 path = "",
131 username = "",
124132 password = "",
125133 db = 0,
126134
137145
138146 assert(rc:connect({
139147 connect_timeout = 100,
148 send_timeout = 500,
140149 read_timeout = 1000,
141150 connection_options = { pool = "<host>::<port>" },
142151 keepalive_timeout = 60000,
143152 keepalive_poolsize = 30,
144153
145154 host = "127.0.0.1",
146 port = 6379,
155 port = $TEST_NGINX_REDIS_PORT,
147156 path = "",
157 username = "",
148158 password = "",
149159 db = 0,
150160
160170 GET /t
161171 --- no_error_log
162172 [error]
173
174
175 === TEST 5: timeout defaults
176 --- http_config eval: $::HttpConfig
177 --- config
178 location /t {
179 content_by_lua_block {
180 -- global defaults
181 local rc = require("resty.redis.connector").new({
182 port = $TEST_NGINX_REDIS_PORT,
183 db = 6,
184 keepalive_poolsize = 10,
185 })
186
187 assert(rc.config.connect_timeout == 100, "connect_timeout should be 100")
188 assert(rc.config.send_timeout == 1000, "send_timeout should be 1000")
189 assert(rc.config.read_timeout == 1000, "read_timeout should be 1000")
190
191 local redis = assert(rc:connect(), "rc:connect should return positively")
192 assert(redis:set("foo", "bar"))
193 rc:set_keepalive(redis)
194
195 -- send_timeout defaults to read_timeout
196 rc = require("resty.redis.connector").new({
197 read_timeout = 500,
198 port = $TEST_NGINX_REDIS_PORT,
199 db = 6,
200 keepalive_poolsize = 10,
201 })
202
203 assert(rc.config.connect_timeout == 100, "connect_timeout should be 100")
204 assert(rc.config.send_timeout == 500, "send_timeout should be 500")
205 assert(rc.config.read_timeout == 500, "read_timeout should be 500")
206
207 local redis = assert(rc:connect(), "rc:connect should return positively")
208 assert(redis:set("foo", "bar"))
209 rc:set_keepalive(redis)
210
211 -- send_timeout can be set separately from read_timeout
212 rc = require("resty.redis.connector").new({
213 send_timeout = 500,
214 read_timeout = 200,
215 port = $TEST_NGINX_REDIS_PORT,
216 db = 6,
217 keepalive_poolsize = 10,
218 })
219
220 assert(rc.config.connect_timeout == 100, "connect_timeout should be 100")
221 assert(rc.config.send_timeout == 500, "send_timeout should be 500")
222 assert(rc.config.read_timeout == 200, "read_timeout should be 200")
223 }
224 }
225 --- request
226 GET /t
227 --- no_error_log
228 [error]
44
55 our $HttpConfig = qq{
66 lua_package_path "$pwd/lib/?.lua;;";
7 lua_socket_log_errors Off;
78
89 init_by_lua_block {
910 require("luacov.runner").init()
1112 };
1213
1314 $ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
14 $ENV{TEST_NGINX_REDIS_PORT} ||= 6379;
15 $ENV{TEST_NGINX_REDIS_PORT} ||= 6380;
16 $ENV{TEST_NGINX_REDIS_PORT_AUTH} ||= 6393;
17 $ENV{TEST_NGINX_REDIS_SOCKET} ||= 'unix://tmp/redis/redis.sock';
1518
1619 no_long_string();
1720 run_tests();
6265 assert(redis and not err,
6366 "try_hosts should return a connection and no error")
6467
65 assert(previous_errors[1] == "connection refused",
66 "previous_errors[1] should be 'connection refused'")
67 assert(previous_errors[2] == "connection refused",
68 "previous_errors[2] should be 'connection refused'")
68 assert(string.len(previous_errors[1]) > 0,
69 "previous_errors[1] should contain an error")
70 assert(string.len(previous_errors[2]) > 0,
71 "previous_errors[2] should contain an error")
6972
7073 assert(redis:set("dog", "an animal"),
7174 "redis connection should be working")
8184 assert(not redis and err == "no hosts available",
8285 "no available hosts should return an error")
8386
84 assert(previous_errors[1] == "connection refused",
85 "previous_errors[1] should be 'connection refused'")
86 assert(previous_errors[2] == "connection refused",
87 "previous_errors[2] should be 'connection refused'")
87 assert(string.len(previous_errors[1]) > 0,
88 "previous_errors[1] should contain an error")
89 assert(string.len(previous_errors[2]) > 0,
90 "previous_errors[2] should contain an error")
8891 }
8992 }
9093 --- request
201204 })
202205
203206 local redis, err = rc:connect()
204 assert(not redis and string.find(err, "ERR Client sent AUTH, but no password is set"),
207 assert(not redis and string.find(err, "ERR") and string.find(err, "AUTH"),
205208 "connect should fail with password error")
206209
207210 }
208211 }
209212 --- request
210213 GET /t
211 --- error_log
212 ERR Client sent AUTH, but no password is set
213
214
215 === TEST 7: unix domain socket
214 --- no_error_log
215 [error]
216
217 === TEST 7: username and password
218 --- http_config eval: $::HttpConfig
219 --- config
220 location /t {
221 lua_socket_log_errors Off;
222 content_by_lua_block {
223 local rc = require("resty.redis.connector").new({
224 port = $TEST_NGINX_REDIS_PORT,
225 username = "x",
226 password = "foo",
227 })
228
229 local redis, err = rc:connect()
230 assert(not redis and string.find(err, "WRONGPASS"),
231 "connect should fail with invalid username-password error")
232 }
233 }
234 --- request
235 GET /t
236 --- no_error_log
237 [error]
238
239
240 === TEST 8: Bad unix domain socket path should fail
216241 --- http_config eval: $::HttpConfig
217242 --- config
218243 location /t {
219244 lua_socket_log_errors Off;
220245 content_by_lua_block {
221246 local redis, err = require("resty.redis.connector").new({
222 path = "unix://tmp/redis.sock",
247 path = "unix://GARBAGE_PATH_AKFDKAJSFKJSAFLKJSADFLKJSANCKAJSNCKJSANCLKAJS",
223248 }):connect()
224249
225250 assert(not redis and err == "no such file or directory",
232257 [error]
233258
234259
235 === TEST 8: parse_dsn
260 === TEST 8.1: Good unix domain socket path should succeed
261 --- http_config eval: $::HttpConfig
262 --- config
263 location /t {
264 lua_socket_log_errors Off;
265 content_by_lua_block {
266 local redis, err = require("resty.redis.connector").new({
267 path = "$TEST_NGINX_REDIS_SOCKET",
268 }):connect()
269
270 assert (redis and not err,
271 "connection should be valid")
272
273 redis:close()
274 }
275 }
276 --- request
277 GET /t
278 --- no_error_log
279 [error]
280
281
282 === TEST 9: parse_dsn
236283 --- http_config eval: $::HttpConfig
237284 --- config
238285 location /t {
248295 assert(params and not err,
249296 "url should parse without error: " .. tostring(err))
250297
251 assert(params.host == "127.0.0.1", "host should be localhost")
252 assert(tonumber(params.port) == $TEST_NGINX_REDIS_PORT,
253 "port should be $TEST_NGINX_REDIS_PORT")
254 assert(tonumber(params.db) == 4, "db should be 4")
255 assert(params.password == "foo", "password should be foo")
256
257
258 local user_params = {
259 url = "sentinel://foo@foomaster:s/2"
260 }
261
262 local params, err = rc.parse_dsn(user_params)
263 assert(params and not err,
264 "url should parse without error: " .. tostring(err))
265
266 assert(params.master_name == "foomaster", "master_name should be foomaster")
267 assert(params.role == "slave", "role should be slave")
268 assert(tonumber(params.db) == 2, "db should be 2")
269
270
271 local params = {
272 url = "sentinels:/wrongformat",
273 }
274
275 local ok, err = rc.parse_dsn(params)
276 assert(not ok and err == "could not parse DSN: nil",
277 "url should fail to parse")
278 }
279 }
280 --- request
281 GET /t
282 --- no_error_log
283 [error]
284
285
286 === TEST 9: params override dsn components
298 assert(params.host == "127.0.0.1", "host should be localhost")
299 assert(tonumber(params.port) == $TEST_NGINX_REDIS_PORT,
300 "port should be $TEST_NGINX_REDIS_PORT")
301 assert(tonumber(params.db) == 4, "db should be 4")
302 assert(params.password == "foo", "password should be foo")
303
304
305 local user_params = {
306 url = "sentinel://foo:bar@foomaster:s/2"
307 }
308
309 local params, err = rc.parse_dsn(user_params)
310 assert(params and not err,
311 "url should parse without error: " .. tostring(err))
312
313 assert(params.master_name == "foomaster", "master_name should be foomaster")
314 assert(params.role == "slave", "role should be slave")
315 assert(tonumber(params.db) == 2, "db should be 2")
316 assert(params.username == "foo", "username should be foo")
317 assert(params.password == "bar", "password should be bar")
318
319
320 local params = {
321 url = "sentinels:/wrongformat",
322 }
323
324 local ok, err = rc.parse_dsn(params)
325 assert(not ok and err == "could not parse DSN: nil",
326 "url should fail to parse")
327 }
328 }
329 --- request
330 GET /t
331 --- no_error_log
332 [error]
333
334
335 === TEST 10: params override dsn components
287336 --- http_config eval: $::HttpConfig
288337 --- config
289338 location /t {
292341 local rc = require("resty.redis.connector")
293342
294343 local user_params = {
295 url = "redis://foo@127.0.0.1:6381/4",
344 url = "redis://foo@127.0.0.1:$TEST_NGINX_REDIS_PORT/4",
296345 db = 2,
297346 password = "bar",
298347 host = "example.com",
306355 assert(params.password == "bar", "password should be bar")
307356 assert(params.host == "example.com", "host should be example.com")
308357
309 assert(tonumber(params.port) == 6381, "ort should still be 6381")
310
311 }
312 }
313 --- request
314 GET /t
315 --- no_error_log
316 [error]
317
318
319 === TEST 9: Integration test for parse_dsn
358 assert(tonumber(params.port) == $TEST_NGINX_REDIS_PORT, "port should still be $TEST_NGINX_REDIS_PORT")
359
360 }
361 }
362 --- request
363 GET /t
364 --- no_error_log
365 [error]
366
367
368 === TEST 11: Integration test for parse_dsn
320369 --- http_config eval: $::HttpConfig
321370 --- config
322371 location /t {
353402 assert(redis and not err, "connect should return positively")
354403 assert(redis:set("cat", "dog") and redis:get("cat") == "dog")
355404
356 }
357 }
358 --- request
359 GET /t
360 --- no_error_log
361 [error]
405 local redis, err = rc2:connect({
406 url = "redis://redisuser:redisuserpass@127.0.0.1:$TEST_NGINX_REDIS_PORT_AUTH/"
407 })
408 assert(redis and not err, "connect should return positively")
409 local username = assert(redis:acl("whoami"))
410 assert(username == "redisuser", "should connect as 'redisuser' but got " .. tostring(username))
411 }
412 }
413 --- request
414 GET /t
415 --- no_error_log
416 [error]
417
418
419 === TEST 12: DSN without DB
420 --- http_config eval: $::HttpConfig
421 --- config
422 location /t {
423 lua_socket_log_errors Off;
424 content_by_lua_block {
425 local user_params = {
426 url = "redis://foo.example:$TEST_NGINX_REDIS_PORT",
427 host = "127.0.0.1",
428 }
429
430 local rc, err = require("resty.redis.connector").new(user_params)
431 assert(rc and not err, "new should return positively")
432
433 local redis, err = rc:connect()
434 assert(redis and not err, "connect should return positively")
435 assert(redis:set("cat", "dog") and redis:get("cat") == "dog")
436 }
437 }
438 --- request
439 GET /t
440 --- no_error_log
441 [error]
44
55 our $HttpConfig = qq{
66 lua_package_path "$pwd/lib/?.lua;;";
7 lua_socket_log_errors Off;
78
89 init_by_lua_block {
910 require("luacov.runner").init()
1112 };
1213
1314 $ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
14 $ENV{TEST_NGINX_REDIS_PORT} ||= 6379;
15 $ENV{TEST_NGINX_REDIS_PORT} ||= 6380;
1516
1617 no_long_string();
1718 run_tests();
44
55 our $HttpConfig = qq{
66 lua_package_path "$pwd/lib/?.lua;;";
7 lua_socket_log_errors Off;
78
89 init_by_lua_block {
910 require("luacov.runner").init()
1112 };
1213
1314 $ENV{TEST_NGINX_RESOLVER} = '8.8.8.8';
14 $ENV{TEST_NGINX_REDIS_PORT} ||= 6379;
15 $ENV{TEST_NGINX_REDIS_PORT} ||= 6380;
16 $ENV{TEST_NGINX_REDIS_PORT_SL1} ||= 6381;
17 $ENV{TEST_NGINX_REDIS_PORT_SL2} ||= 6382;
18 $ENV{TEST_NGINX_SENTINEL_PORT1} ||= 6390;
19 $ENV{TEST_NGINX_SENTINEL_PORT2} ||= 6391;
20 $ENV{TEST_NGINX_SENTINEL_PORT3} ||= 6392;
21 $ENV{TEST_NGINX_SENTINEL_PORT_AUTH} ||= 6393;
1522
1623 no_long_string();
1724 run_tests();
2229 --- http_config eval: $::HttpConfig
2330 --- config
2431 location /t {
25 content_by_lua_block {
26 local rc = require("resty.redis.connector").new()
27
28 local sentinel, err = rc:connect{ url = "redis://127.0.0.1:6381" }
29 assert(sentinel and not err, "sentinel should connect without errors")
30
31 local master, err = require("resty.redis.sentinel").get_master(
32 sentinel,
33 "mymaster"
34 )
35
36 assert(master and not err, "get_master should return the master")
37
38 assert(master.host == "127.0.0.1" and tonumber(master.port) == 6379,
39 "host should be 127.0.0.1 and port should be 6379")
40
41 sentinel:close()
42 }
32 content_by_lua_block {
33 local rc = require("resty.redis.connector").new()
34 local rs = require("resty.redis.sentinel")
35
36 local sentinel, err = rc:connect{ url = "redis://127.0.0.1:$TEST_NGINX_SENTINEL_PORT1" }
37 assert(sentinel and not err, "sentinel should connect without errors but got " .. tostring(err))
38
39 local master, err = rs.get_master(sentinel, "mymaster")
40
41 assert(master and not err, "get_master should return the master")
42
43 assert(master.host == "127.0.0.1" and tonumber(master.port) == $TEST_NGINX_REDIS_PORT,
44 "host should be 127.0.0.1 and port should be $TEST_NGINX_REDIS_PORT")
45
46 master, err = rs.get_master(sentinel, "invalid-mymaster")
47
48 assert(not master and err, "invalid master name should result in error")
49
50 sentinel:close()
51 }
4352 }
4453 --- request
4554 GET /t
5160 --- http_config eval: $::HttpConfig
5261 --- config
5362 location /t {
54 content_by_lua_block {
55 local rc = require("resty.redis.connector").new()
56
57 local master, err = rc:connect({
63 content_by_lua_block {
64 local rc = require("resty.redis.connector").new()
65
66 local master, err = rc:connect({
5867 url = "sentinel://mymaster:m/3",
5968 sentinels = {
60 { host = "127.0.0.1", port = 6381 }
69 { host = "127.0.0.1", port = $TEST_NGINX_SENTINEL_PORT1 }
6170 }
6271 })
6372
64 assert(master and not err, "get_master should return the master")
73 assert(master and not err, "get_master should return the master")
6574 assert(master:set("foo", "bar"), "set should run without error")
6675 assert(master:get("foo") == "bar", "get(foo) should return bar")
6776
68 master:close()
69 }
77 master:close()
78 }
7079 }
7180 --- request
7281 GET /t
8089 location /t {
8190 content_by_lua_block {
8291 local rc = require("resty.redis.connector").new()
83
84 local sentinel, err = rc:connect{ url = "redis://127.0.0.1:6381" }
92 local rs = require("resty.redis.sentinel")
93
94 local sentinel, err = rc:connect{ url = "redis://127.0.0.1:$TEST_NGINX_SENTINEL_PORT1" }
95 assert(sentinel and not err, "sentinel should connect without error")
96
97 local slaves, err = rs.get_slaves(sentinel, "mymaster")
98
99 assert(slaves and not err, "slaves should be returned without error")
100
101 local slaveports = { ["$TEST_NGINX_REDIS_PORT_SL1"] = false, ["$TEST_NGINX_REDIS_PORT_SL2"] = false }
102
103 for _,slave in ipairs(slaves) do
104 slaveports[tostring(slave.port)] = true
105 end
106
107 assert(slaveports["$TEST_NGINX_REDIS_PORT_SL1"] == true and slaveports["$TEST_NGINX_REDIS_PORT_SL2"] == true,
108 "slaves should both be found")
109
110 slaves, err = rs.get_slaves(sentinel, "invalid-mymaster")
111
112 assert(not slaves and err, "invalid master name should result in error")
113
114 sentinel:close()
115 }
116 }
117 --- request
118 GET /t
119 --- no_error_log
120 [error]
121
122
123 === TEST 3: Get only healthy slaves
124 --- http_config eval: $::HttpConfig
125 --- config
126 location /t {
127 content_by_lua_block {
128 local rc = require("resty.redis.connector").new()
129
130 local sentinel, err = rc:connect({ url = "redis://127.0.0.1:$TEST_NGINX_SENTINEL_PORT1" })
85131 assert(sentinel and not err, "sentinel should connect without error")
86132
87133 local slaves, err = require("resty.redis.sentinel").get_slaves(
91137
92138 assert(slaves and not err, "slaves should be returned without error")
93139
94 local slaveports = { ["6378"] = false, ["6380"] = false }
95
96 for _,slave in ipairs(slaves) do
97 slaveports[tostring(slave.port)] = true
98 end
99
100 assert(slaveports["6378"] == true and slaveports["6380"] == true,
101 "slaves should both be found")
140 local slaveports = { ["$TEST_NGINX_REDIS_PORT_SL1"] = false, ["$TEST_NGINX_REDIS_PORT_SL2"] = false }
141
142 for _,slave in ipairs(slaves) do
143 slaveports[tostring(slave.port)] = true
144 end
145
146 assert(slaveports["$TEST_NGINX_REDIS_PORT_SL1"] == true and slaveports["$TEST_NGINX_REDIS_PORT_SL2"] == true,
147 "slaves should both be found")
148
149 -- connect to one and remove it
150 local r = require("resty.redis.connector").new():connect({
151 port = $TEST_NGINX_REDIS_PORT_SL1,
152 })
153 r:slaveof("127.0.0.1", 7000)
154
155 ngx.sleep(9)
156
157 local slaves, err = require("resty.redis.sentinel").get_slaves(
158 sentinel,
159 "mymaster"
160 )
161
162 assert(slaves and not err, "slaves should be returned without error")
163
164 local slaveports = { ["$TEST_NGINX_REDIS_PORT_SL1"] = false, ["$TEST_NGINX_REDIS_PORT_SL2"] = false }
165
166 for _,slave in ipairs(slaves) do
167 slaveports[tostring(slave.port)] = true
168 end
169
170 assert(slaveports["$TEST_NGINX_REDIS_PORT_SL1"] == false and slaveports["$TEST_NGINX_REDIS_PORT_SL2"] == true,
171 "only $TEST_NGINX_REDIS_PORT_SL2 should be found")
172
173 r:slaveof("127.0.0.1", $TEST_NGINX_REDIS_PORT)
102174
103175 sentinel:close()
104176 }
105177 }
106178 --- request
107 GET /t
108 --- no_error_log
109 [error]
110
111
112 === TEST 3: Get only healthy slaves
113 --- http_config eval: $::HttpConfig
114 --- config
115 location /t {
116 content_by_lua_block {
117 local rc = require("resty.redis.connector").new()
118
119 local sentinel, err = rc:connect({ url = "redis://127.0.0.1:6381" })
120 assert(sentinel and not err, "sentinel should connect without error")
121
122 local slaves, err = require("resty.redis.sentinel").get_slaves(
123 sentinel,
124 "mymaster"
125 )
126
127 assert(slaves and not err, "slaves should be returned without error")
128
129 local slaveports = { ["6378"] = false, ["6380"] = false }
130
131 for _,slave in ipairs(slaves) do
132 slaveports[tostring(slave.port)] = true
133 end
134
135 assert(slaveports["6378"] == true and slaveports["6380"] == true,
136 "slaves should both be found")
137
138 -- connect to one and remove it
139 local r = require("resty.redis.connector").new():connect({
140 port = 6378,
141 })
142 r:slaveof("127.0.0.1", 7000)
143
144 ngx.sleep(9)
145
146 local slaves, err = require("resty.redis.sentinel").get_slaves(
147 sentinel,
148 "mymaster"
149 )
150
151 assert(slaves and not err, "slaves should be returned without error")
152
153 local slaveports = { ["6378"] = false, ["6380"] = false }
154
155 for _,slave in ipairs(slaves) do
156 slaveports[tostring(slave.port)] = true
157 end
158
159 assert(slaveports["6378"] == false and slaveports["6380"] == true,
160 "only 6380 should be found")
161
162 r:slaveof("127.0.0.1", 6379)
163
164 sentinel:close()
165 }
166 }
167 --- request
168179 GET /t
169180 --- timeout: 10
170181 --- no_error_log
180191
181192 local params = {
182193 sentinels = {
183 { host = "127.0.0.1", port = 6381 },
184 { host = "127.0.0.1", port = 6382 },
185 { host = "127.0.0.1", port = 6383 },
194 { host = "127.0.0.1", port = $TEST_NGINX_SENTINEL_PORT1 },
195 { host = "127.0.0.1", port = $TEST_NGINX_SENTINEL_PORT2 },
196 { host = "127.0.0.1", port = $TEST_NGINX_SENTINEL_PORT3 },
186197 },
187198 master_name = "mymaster",
188199 role = "master",
213224
214225 local params = {
215226 sentinels = {
216 { host = "127.0.0.1", port = 6381 },
217 { host = "127.0.0.1", port = 6382 },
218 { host = "127.0.0.1", port = 6383 },
227 { host = "127.0.0.1", port = $TEST_NGINX_SENTINEL_PORT1 },
228 { host = "127.0.0.1", port = $TEST_NGINX_SENTINEL_PORT2 },
229 { host = "127.0.0.1", port = $TEST_NGINX_SENTINEL_PORT3 },
219230 },
220231 master_name = "mymaster",
221232 role = "slave",
225236 local sentinel = require("resty.redis.sentinel")
226237 sentinel.get_slaves = function()
227238 return {
228 { host = "127.0.0.1", port = 6380 },
229 { host = "127.0.0.1", port = 6378 },
230 { host = "127.0.0.1", port = 6377 },
231 { host = "134.123.51.2", port = 6380 },
239 { host = "127.0.0.1", port = $TEST_NGINX_REDIS_PORT_SL1 },
240 { host = "127.0.0.1", port = $TEST_NGINX_REDIS_PORT_SL2 },
241 { host = "134.123.51.2", port = $TEST_NGINX_REDIS_PORT_SL1 },
232242 }
233243 end
234244
240250 GET /t
241251 --- no_error_log
242252 [error]
253
254 === TEST 6: connect with acl
255 --- http_config eval: $::HttpConfig
256 --- config
257 location /t {
258 content_by_lua_block {
259 local rc = require("resty.redis.connector").new()
260 local redis, err = rc:connect({
261 username = "redisuser",
262 password = "redisuserpass",
263 sentinels = {
264 { host = "127.0.0.1", port = $TEST_NGINX_SENTINEL_PORT_AUTH }
265 },
266 master_name = "mymaster",
267 sentinel_username = "sentineluser",
268 sentinel_username = "sentineluserpass",
269 })
270 assert(redis and not err, "redis should connect without error")
271 local username = assert(redis:acl("whoami"))
272 assert(username == "redisuser", "should connect as 'redisuser' but got " .. tostring(username))
273 }
274 }
275 --- request
276 GET /t
277 --- no_error_log
278 [error]
3939 close $in;
4040
4141 #print "Checking use of Lua global variables in file $file ...\n";
42 system("luac5.1 -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)\$'");
42 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)\$'");
4343 #file_contains($file, "attempt to write to undeclared variable");
4444 system("grep -H -n -E --color '.{120}' $file");
4545 }