Import upstream version 0.11.0
Debian Janitor
2 years ago
0 | github: pintsized |
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 |
2 | 2 | OPENRESTY_PREFIX = /usr/local/openresty |
3 | 3 | |
4 | 4 | TEST_FILE ?= t |
5 | SENTINEL_TEST_FILE ?= $(TEST_FILE)/sentinel | |
5 | TMP_DIR ?= /tmp | |
6 | 6 | |
7 | 7 | REDIS_CMD = redis-server |
8 | 8 | SENTINEL_CMD = $(REDIS_CMD) --sentinel |
10 | 10 | REDIS_SOCK = /redis.sock |
11 | 11 | REDIS_PID = /redis.pid |
12 | 12 | REDIS_LOG = /redis.log |
13 | REDIS_PREFIX = /tmp/redis- | |
13 | REDIS_PREFIX = $(TMP_DIR)/redis- | |
14 | 14 | |
15 | 15 | # 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) | |
18 | 24 | |
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) | |
26 | 27 | |
27 | 28 | # 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) | |
29 | 35 | TEST_SENTINEL_MASTER_NAME ?= mymaster |
30 | 36 | TEST_SENTINEL_PROMOTION_TIME ?= 20 |
31 | 37 | |
32 | 38 | # Command line arguments for redis tests |
33 | 39 | 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) \ | |
36 | 46 | TEST_NGINX_NO_SHUFFLE=1 |
37 | 47 | |
38 | 48 | # Command line arguments for sentinel tests |
39 | 49 | 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) \ | |
43 | 59 | TEST_NGINX_NO_SHUFFLE=1 |
44 | 60 | |
45 | 61 | # Sentinel configuration can only be set by a config file |
46 | 62 | 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 | |
48 | 64 | sentinel down-after-milliseconds $(TEST_SENTINEL_MASTER_NAME) 2000 |
49 | 65 | sentinel failover-timeout $(TEST_SENTINEL_MASTER_NAME) 10000 |
50 | 66 | sentinel parallel-syncs $(TEST_SENTINEL_MASTER_NAME) 5 |
51 | 67 | 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 | |
52 | 71 | |
53 | export TEST_SENTINEL_CONFIG | |
72 | export TEST_SENTINEL_CONFIG TEST_SENTINEL_AUTH_CONFIG | |
54 | 73 | |
55 | 74 | SENTINEL_CONFIG_FILE = /tmp/sentinel-test-config |
75 | SENTINEL_AUTH_CONFIG_FILE = /tmp/sentinel-auth-test-config | |
56 | 76 | |
57 | 77 | |
58 | 78 | PREFIX ?= /usr/local |
75 | 95 | test: test_redis |
76 | 96 | test_all: start_redis_instances sleep test_redis stop_redis_instances |
77 | 97 | |
98 | check: | |
99 | luacheck lib | |
100 | ||
78 | 101 | sleep: |
79 | 102 | sleep 3 |
80 | 103 | |
81 | 104 | start_redis_instances: check_ports create_sentinel_config |
105 | $(REDIS_CMD) --version | |
106 | ||
82 | 107 | @$(foreach port,$(TEST_REDIS_PORTS), \ |
83 | [[ "$(port)" != "$(REDIS_FIRST_PORT)" ]] && \ | |
108 | [[ "$(port)" != "$(TEST_REDIS_PORT)" ]] && \ | |
84 | 109 | SLAVE="$(REDIS_SLAVE_ARG)" || \ |
85 | 110 | SLAVE="" && \ |
86 | 111 | $(MAKE) start_redis_instance args="$$SLAVE" port=$(port) \ |
87 | 112 | prefix=$(REDIS_PREFIX)$(port) && \ |
88 | 113 | ) 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) | |
89 | 119 | |
90 | 120 | @$(foreach port,$(TEST_SENTINEL_PORTS), \ |
91 | 121 | $(MAKE) start_redis_instance \ |
93 | 123 | prefix=$(REDIS_PREFIX)$(port) && \ |
94 | 124 | ) true |
95 | 125 | |
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) | |
96 | 130 | |
97 | 131 | 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), \ | |
99 | 133 | $(MAKE) stop_redis_instance cleanup_redis_instance port=$(port) \ |
100 | 134 | prefix=$(REDIS_PREFIX)$(port) && \ |
101 | 135 | ) true 2>&1 > /dev/null |
104 | 138 | start_redis_instance: |
105 | 139 | -@echo "Starting redis on port $(port) with args: \"$(args)\"" |
106 | 140 | -@mkdir -p $(prefix) |
107 | @$(REDIS_CMD) $(args) \ | |
141 | $(REDIS_CMD) $(args) \ | |
108 | 142 | --pidfile $(prefix)$(REDIS_PID) \ |
109 | 143 | --bind 127.0.0.1 --port $(port) \ |
110 | 144 | --unixsocket $(prefix)$(REDIS_SOCK) \ |
130 | 164 | create_sentinel_config: |
131 | 165 | -@echo "Creating $(SENTINEL_CONFIG_FILE)" |
132 | 166 | @echo "$$TEST_SENTINEL_CONFIG" > $(SENTINEL_CONFIG_FILE) |
167 | -@echo "Creating $(SENTINEL_AUTH_CONFIG_FILE)" | |
168 | @echo "$$TEST_SENTINEL_AUTH_CONFIG" > $(SENTINEL_AUTH_CONFIG_FILE) | |
133 | 169 | |
134 | 170 | delete_sentinel_config: |
135 | 171 | -@echo "Removing $(SENTINEL_CONFIG_FILE)" |
136 | 172 | @rm -f $(SENTINEL_CONFIG_FILE) |
173 | -@echo "Removing $(SENTINEL_AUTH_CONFIG_FILE)" | |
174 | @rm -f $(SENTINEL_AUTH_CONFIG_FILE) | |
137 | 175 | |
138 | 176 | 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 | |
141 | 179 | |
142 | 180 | test_redis: flush_db |
143 | 181 | util/lua-releng |
0 | 0 | # lua-resty-redis-connector |
1 | 1 | |
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). | |
3 | 9 | |
4 | 10 | |
5 | 11 | ## Synopsis |
17 | 23 | ```lua |
18 | 24 | local rc = require("resty.redis.connector").new({ |
19 | 25 | connect_timeout = 50, |
26 | send_timeout = 5000, | |
20 | 27 | read_timeout = 5000, |
21 | 28 | keepalive_timeout = 30000, |
22 | 29 | password = "mypass", |
36 | 43 | ```lua |
37 | 44 | local rc = require("resty.redis.connector").new({ |
38 | 45 | connect_timeout = 50, |
46 | send_timeout = 5000, | |
39 | 47 | read_timeout = 5000, |
40 | 48 | keepalive_timeout = 30000, |
41 | 49 | |
52 | 60 | local ok, err = rc:set_keepalive(redis) |
53 | 61 | ``` |
54 | 62 | |
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. | |
56 | 65 | |
57 | 66 | |
58 | 67 | ```lua |
70 | 79 | |
71 | 80 | ## DSN format |
72 | 81 | |
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.* | |
76 | 87 | |
77 | 88 | ### Direct Redis connections |
78 | 89 | |
79 | 90 | The format for connecting directly to Redis is: |
80 | 91 | |
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. | |
84 | 98 | |
85 | 99 | ### Connections via Redis Sentinel |
86 | 100 | |
87 | 101 | When connecting via Redis Sentinel, the format is as follows: |
88 | 102 | |
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. | |
92 | 112 | |
93 | 113 | A table of `sentinels` must also be supplied. e.g. |
94 | 114 | |
96 | 116 | local redis, err = rc:connect{ |
97 | 117 | url = "sentinel://mymaster:a/2", |
98 | 118 | 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" | |
101 | 123 | } |
102 | 124 | ``` |
103 | 125 | |
104 | 126 | ## Proxy Mode |
105 | 127 | |
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. | |
112 | 137 | |
113 | 138 | `discard` will not be sent when adding connections to the keepalive pool |
114 | 139 | |
115 | 140 | |
116 | 141 | ## Disabled commands |
117 | 142 | |
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 | |
119 | 146 | |
120 | 147 | ## Default Parameters |
121 | 148 | |
123 | 150 | ```lua |
124 | 151 | { |
125 | 152 | connect_timeout = 100, |
153 | send_timeout = 1000, | |
126 | 154 | read_timeout = 1000, |
127 | connection_options = {}, -- pool, etc | |
128 | 155 | keepalive_timeout = 60000, |
129 | 156 | 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 = {}, | |
130 | 161 | |
131 | 162 | host = "127.0.0.1", |
132 | 163 | port = "6379", |
133 | 164 | path = "", -- unix socket path, e.g. /tmp/redis.sock |
165 | username = "", | |
134 | 166 | password = "", |
167 | sentinel_username = "", | |
168 | sentinel_password = "", | |
135 | 169 | db = 0, |
136 | 170 | |
137 | 171 | master_name = "mymaster", |
162 | 196 | |
163 | 197 | `syntax: rc = redis_connector.new(params)` |
164 | 198 | |
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. | |
166 | 201 | |
167 | 202 | |
168 | 203 | ### connect |
169 | 204 | |
170 | 205 | `syntax: redis, err = rc:connect(params)` |
171 | 206 | |
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`. | |
175 | 214 | |
176 | 215 | * `keepalive_poolsize` |
177 | 216 | * `keepalive_timeout` |
183 | 222 | |
184 | 223 | `syntax: ok, err = rc:set_keepalive(redis)` |
185 | 224 | |
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. | |
189 | 230 | |
190 | 231 | Returns `1` or in the case of error, `nil` and a string describing the error. |
191 | 232 | |
192 | 233 | |
193 | 234 | ## Utilities |
194 | 235 | |
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. | |
196 | 238 | |
197 | 239 | |
198 | 240 | ### connect_via_sentinel |
199 | 241 | |
200 | 242 | `syntax: redis, err = rc:connect_via_sentinel(params)` |
201 | 243 | |
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`. | |
204 | 247 | |
205 | 248 | |
206 | 249 | ### try_hosts |
221 | 264 | |
222 | 265 | `syntax: master, err = sentinel.get_master(sentinel, master_name)` |
223 | 266 | |
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. | |
225 | 269 | |
226 | 270 | |
227 | 271 | ### sentinel.get_slaves |
228 | 272 | |
229 | 273 | `syntax: slaves, err = sentinel.get_slaves(sentinel, master_name)` |
230 | 274 | |
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. | |
232 | 277 | |
233 | 278 | |
234 | 279 | # Author |
244 | 289 | |
245 | 290 | All rights reserved. |
246 | 291 | |
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. |
4 | 4 | license=2bsd |
5 | 5 | lib_dir=lib |
6 | 6 | 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 | |
8 | 8 | main_module=lib/resty/redis/connector.lua |
4 | 4 | local ngx_ERR = ngx.ERR |
5 | 5 | local ngx_re_match = ngx.re.match |
6 | 6 | |
7 | local str_find = string.find | |
8 | local str_sub = string.sub | |
7 | 9 | local tbl_remove = table.remove |
8 | 10 | local tbl_sort = table.sort |
9 | 11 | local ok, tbl_new = pcall(require, "table.new") |
10 | 12 | if not ok then |
11 | tbl_new = function (narr, nrec) return {} end | |
13 | tbl_new = function (narr, nrec) return {} end -- luacheck: ignore 212 | |
12 | 14 | end |
13 | 15 | |
14 | 16 | local redis = require("resty.redis") |
21 | 23 | -- A metatable which prevents undefined fields from being created / accessed |
22 | 24 | local fixed_field_metatable = { |
23 | 25 | __index = |
24 | function(t, k) | |
26 | function(t, k) -- luacheck: ignore 212 | |
25 | 27 | error("field " .. tostring(k) .. " does not exist", 3) |
26 | 28 | end, |
27 | 29 | __newindex = |
28 | function(t, k, v) | |
30 | function(t, k, v) -- luacheck: ignore 212 | |
29 | 31 | error("attempt to create new field " .. tostring(k), 3) |
30 | 32 | end, |
31 | 33 | } |
65 | 67 | if t1 == nil then t1 = {} end |
66 | 68 | if defaults == nil then defaults = {} end |
67 | 69 | if type(t1) == "table" and type(defaults) == "table" then |
68 | local mt = getmetatable(defaults) | |
69 | 70 | local copy = {} |
70 | 71 | for t1_key, t1_value in next, t1, nil do |
71 | 72 | copy[tbl_copy(t1_key)] = tbl_copy_merge_defaults( |
87 | 88 | local DEFAULTS = setmetatable({ |
88 | 89 | connect_timeout = 100, |
89 | 90 | read_timeout = 1000, |
91 | send_timeout = 1000, | |
90 | 92 | connection_options = {}, -- pool, etc |
91 | 93 | keepalive_timeout = 60000, |
92 | 94 | keepalive_poolsize = 30, |
94 | 96 | host = "127.0.0.1", |
95 | 97 | port = 6379, |
96 | 98 | path = "", -- /tmp/redis.sock |
99 | username = "", | |
97 | 100 | password = "", |
101 | sentinel_username = "", | |
102 | sentinel_password = "", | |
98 | 103 | db = 0, |
99 | 104 | url = "", -- DSN url |
100 | 105 | |
123 | 128 | |
124 | 129 | |
125 | 130 | local _M = { |
126 | _VERSION = '0.06', | |
131 | _VERSION = '0.11.0', | |
127 | 132 | } |
128 | 133 | |
129 | 134 | local mt = { __index = _M } |
147 | 152 | fields = { "password", "master_name", "role", "db" } |
148 | 153 | end |
149 | 154 | |
150 | -- password may not be present | |
155 | -- username/password may not be present | |
151 | 156 | if #m < 5 then tbl_remove(fields, 1) end |
152 | 157 | |
153 | 158 | local roles = { m = "master", s = "slave" } |
154 | 159 | |
155 | 160 | local parsed_params = {} |
156 | 161 | |
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 | ||
159 | 169 | if v == "role" then |
160 | 170 | parsed_params[v] = roles[parsed_params[v]] |
161 | 171 | end |
162 | 172 | end |
163 | 173 | |
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 | ||
164 | 180 | return tbl_copy_merge_defaults(params, parsed_params) |
165 | 181 | end |
182 | ||
183 | return params | |
166 | 184 | end |
167 | 185 | _M.parse_dsn = parse_dsn |
168 | 186 | |
169 | 187 | |
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) | |
172 | 190 | if config and config.url then |
173 | 191 | local err |
174 | 192 | 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) | |
177 | 212 | |
178 | 213 | local ok, config = pcall(tbl_copy_merge_defaults, config, DEFAULTS) |
179 | 214 | if not ok then |
194 | 229 | |
195 | 230 | |
196 | 231 | 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) | |
202 | 234 | |
203 | 235 | params = tbl_copy_merge_defaults(params, self.config) |
204 | 236 | |
224 | 256 | local master_name = params.master_name |
225 | 257 | local role = params.role |
226 | 258 | local db = params.db |
259 | local username = params.username | |
227 | 260 | 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 | |
228 | 269 | |
229 | 270 | local sentnl, err, previous_errors = self:try_hosts(sentinels) |
230 | 271 | if not sentnl then |
233 | 274 | |
234 | 275 | if role == "master" then |
235 | 276 | 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 | |
251 | 293 | else |
252 | 294 | -- We want a slave |
253 | 295 | local slaves, err = get_slaves(sentnl, master_name) |
254 | sentnl:set_keepalive() | |
255 | ||
256 | 296 | if not slaves then |
257 | 297 | return nil, err |
258 | 298 | end |
259 | 299 | |
300 | sentnl:set_keepalive() | |
301 | ||
260 | 302 | -- Put any slaves on 127.0.0.1 at the front |
261 | 303 | tbl_sort(slaves, sort_by_localhost) |
262 | 304 | |
263 | 305 | if db or password then |
264 | for i,slave in ipairs(slaves) do | |
306 | for _, slave in ipairs(slaves) do | |
265 | 307 | slave.db = db |
308 | slave.username = username | |
266 | 309 | slave.password = password |
267 | 310 | end |
268 | 311 | end |
270 | 313 | local slave, err, previous_errors = self:try_hosts(slaves) |
271 | 314 | if not slave then |
272 | 315 | return nil, err, previous_errors |
273 | else | |
274 | return slave | |
275 | end | |
316 | end | |
317 | ||
318 | return slave | |
276 | 319 | end |
277 | 320 | end |
278 | 321 | |
297 | 340 | |
298 | 341 | function _M.connect_to_host(self, host) |
299 | 342 | 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 | ) | |
302 | 359 | |
303 | 360 | -- Stub out methods for disabled commands |
304 | 361 | if next(config.disabled_commands) then |
305 | 362 | for _, cmd in ipairs(config.disabled_commands) do |
306 | r[cmd] = function(...) | |
363 | r[cmd] = function() | |
307 | 364 | return nil, ("Command "..cmd.." is disabled") |
308 | 365 | end |
309 | 366 | end |
329 | 386 | if not ok then |
330 | 387 | return nil, err |
331 | 388 | else |
332 | r:set_timeout(self, config.read_timeout) | |
333 | ||
389 | local username = host.username | |
334 | 390 | local password = host.password |
335 | 391 | 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 | |
337 | 399 | if err then |
338 | ngx_log(ngx_ERR, err) | |
339 | 400 | return res, err |
340 | 401 | end |
341 | 402 | end |
342 | 403 | |
343 | 404 | -- No support for DBs in proxied Redis. |
344 | 405 | 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 | |
346 | 419 | end |
347 | 420 | return r, nil |
348 | 421 | end |
349 | 422 | end |
350 | 423 | |
351 | 424 | |
352 | local function set_keepalive(self, redis) | |
425 | function _M.set_keepalive(self, redis) | |
353 | 426 | -- Restore connection to "NORMAL" before putting into keepalive pool, |
354 | 427 | -- ignoring any errors. |
355 | 428 | -- Proxied Redis does not support transactions. |
362 | 435 | config.keepalive_timeout, config.keepalive_poolsize |
363 | 436 | ) |
364 | 437 | end |
365 | _M.set_keepalive = set_keepalive | |
366 | ||
367 | 438 | |
368 | 439 | |
369 | 440 | -- Deprecated: use config table in new() or connect() instead. |
4 | 4 | local tbl_insert = table.insert |
5 | 5 | local ok, tbl_new = pcall(require, "table.new") |
6 | 6 | if not ok then |
7 | tbl_new = function (narr, nrec) return {} end | |
7 | tbl_new = function (narr, nrec) return {} end -- luacheck: ignore 212 | |
8 | 8 | end |
9 | 9 | |
10 | 10 | |
11 | 11 | local _M = { |
12 | _VERSION = '0.06' | |
12 | _VERSION = '0.11.0' | |
13 | 13 | } |
14 | 14 | |
15 | 15 | |
20 | 20 | ) |
21 | 21 | if res and res ~= ngx_null and res[1] and res[2] then |
22 | 22 | return { host = res[1], port = res[2] } |
23 | elseif res == ngx_null then | |
24 | return nil, "invalid master name" | |
23 | 25 | else |
24 | 26 | return nil, err |
25 | 27 | end |
38 | 40 | host[slave[i]] = slave[i + 1] |
39 | 41 | end |
40 | 42 | |
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 | |
42 | 47 | host.host = host.ip -- for parity with other functions |
43 | 48 | tbl_insert(hosts, host) |
44 | 49 | end |
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 | } |
8 | 8 | |
9 | 9 | our $HttpConfig = qq{ |
10 | 10 | lua_package_path "$pwd/lib/?.lua;;"; |
11 | lua_socket_log_errors Off; | |
11 | 12 | |
12 | 13 | init_by_lua_block { |
13 | 14 | require("luacov.runner").init() |
14 | 15 | } |
15 | 16 | }; |
16 | 17 | |
17 | $ENV{TEST_NGINX_REDIS_PORT} ||= 6379; | |
18 | $ENV{TEST_NGINX_REDIS_PORT} ||= 6380; | |
18 | 19 | |
19 | 20 | no_long_string(); |
20 | 21 | run_tests(); |
26 | 27 | --- config |
27 | 28 | location /t { |
28 | 29 | 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()) | |
35 | 31 | } |
36 | 32 | } |
37 | 33 | --- request |
47 | 43 | content_by_lua_block { |
48 | 44 | local config = { |
49 | 45 | connect_timeout = 500, |
50 | port = 6380, | |
46 | port = $TEST_NGINX_REDIS_PORT, | |
51 | 47 | db = 6, |
52 | 48 | } |
53 | 49 | local rc = require("resty.redis.connector").new(config) |
71 | 67 | content_by_lua_block { |
72 | 68 | local rc = require("resty.redis.connector").new({ |
73 | 69 | connect_timeout = 500, |
74 | port = 6380, | |
70 | port = $TEST_NGINX_REDIS_PORT, | |
75 | 71 | db = 6, |
76 | 72 | keepalive_poolsize = 10, |
77 | 73 | }) |
83 | 79 | assert(rc.config.keepalive_poolsize == 10, |
84 | 80 | "keepalive_poolsize should be 10") |
85 | 81 | |
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" ) | |
88 | 94 | } |
89 | 95 | } |
90 | 96 | --- request |
100 | 106 | content_by_lua_block { |
101 | 107 | local rc, err = require("resty.redis.connector").new({ |
102 | 108 | connect_timeout = 500, |
103 | port = 6380, | |
109 | port = $TEST_NGINX_REDIS_PORT, | |
104 | 110 | db = 6, |
105 | 111 | foo = "bar", |
106 | 112 | }) |
113 | 119 | |
114 | 120 | assert(require("resty.redis.connector").new({ |
115 | 121 | connect_timeout = 100, |
122 | send_timeout = 500, | |
116 | 123 | read_timeout = 1000, |
117 | 124 | connection_options = { pool = "<host>::<port>" }, |
118 | 125 | keepalive_timeout = 60000, |
119 | 126 | keepalive_poolsize = 30, |
120 | 127 | |
121 | 128 | host = "127.0.0.1", |
122 | port = 6379, | |
129 | port = $TEST_NGINX_REDIS_PORT, | |
123 | 130 | path = "", |
131 | username = "", | |
124 | 132 | password = "", |
125 | 133 | db = 0, |
126 | 134 | |
137 | 145 | |
138 | 146 | assert(rc:connect({ |
139 | 147 | connect_timeout = 100, |
148 | send_timeout = 500, | |
140 | 149 | read_timeout = 1000, |
141 | 150 | connection_options = { pool = "<host>::<port>" }, |
142 | 151 | keepalive_timeout = 60000, |
143 | 152 | keepalive_poolsize = 30, |
144 | 153 | |
145 | 154 | host = "127.0.0.1", |
146 | port = 6379, | |
155 | port = $TEST_NGINX_REDIS_PORT, | |
147 | 156 | path = "", |
157 | username = "", | |
148 | 158 | password = "", |
149 | 159 | db = 0, |
150 | 160 | |
160 | 170 | GET /t |
161 | 171 | --- no_error_log |
162 | 172 | [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] |
4 | 4 | |
5 | 5 | our $HttpConfig = qq{ |
6 | 6 | lua_package_path "$pwd/lib/?.lua;;"; |
7 | lua_socket_log_errors Off; | |
7 | 8 | |
8 | 9 | init_by_lua_block { |
9 | 10 | require("luacov.runner").init() |
11 | 12 | }; |
12 | 13 | |
13 | 14 | $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'; | |
15 | 18 | |
16 | 19 | no_long_string(); |
17 | 20 | run_tests(); |
62 | 65 | assert(redis and not err, |
63 | 66 | "try_hosts should return a connection and no error") |
64 | 67 | |
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") | |
69 | 72 | |
70 | 73 | assert(redis:set("dog", "an animal"), |
71 | 74 | "redis connection should be working") |
81 | 84 | assert(not redis and err == "no hosts available", |
82 | 85 | "no available hosts should return an error") |
83 | 86 | |
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") | |
88 | 91 | } |
89 | 92 | } |
90 | 93 | --- request |
201 | 204 | }) |
202 | 205 | |
203 | 206 | 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"), | |
205 | 208 | "connect should fail with password error") |
206 | 209 | |
207 | 210 | } |
208 | 211 | } |
209 | 212 | --- request |
210 | 213 | 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 | |
216 | 241 | --- http_config eval: $::HttpConfig |
217 | 242 | --- config |
218 | 243 | location /t { |
219 | 244 | lua_socket_log_errors Off; |
220 | 245 | content_by_lua_block { |
221 | 246 | local redis, err = require("resty.redis.connector").new({ |
222 | path = "unix://tmp/redis.sock", | |
247 | path = "unix://GARBAGE_PATH_AKFDKAJSFKJSAFLKJSADFLKJSANCKAJSNCKJSANCLKAJS", | |
223 | 248 | }):connect() |
224 | 249 | |
225 | 250 | assert(not redis and err == "no such file or directory", |
232 | 257 | [error] |
233 | 258 | |
234 | 259 | |
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 | |
236 | 283 | --- http_config eval: $::HttpConfig |
237 | 284 | --- config |
238 | 285 | location /t { |
248 | 295 | assert(params and not err, |
249 | 296 | "url should parse without error: " .. tostring(err)) |
250 | 297 | |
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 | |
287 | 336 | --- http_config eval: $::HttpConfig |
288 | 337 | --- config |
289 | 338 | location /t { |
292 | 341 | local rc = require("resty.redis.connector") |
293 | 342 | |
294 | 343 | 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", | |
296 | 345 | db = 2, |
297 | 346 | password = "bar", |
298 | 347 | host = "example.com", |
306 | 355 | assert(params.password == "bar", "password should be bar") |
307 | 356 | assert(params.host == "example.com", "host should be example.com") |
308 | 357 | |
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 | |
320 | 369 | --- http_config eval: $::HttpConfig |
321 | 370 | --- config |
322 | 371 | location /t { |
353 | 402 | assert(redis and not err, "connect should return positively") |
354 | 403 | assert(redis:set("cat", "dog") and redis:get("cat") == "dog") |
355 | 404 | |
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] |
4 | 4 | |
5 | 5 | our $HttpConfig = qq{ |
6 | 6 | lua_package_path "$pwd/lib/?.lua;;"; |
7 | lua_socket_log_errors Off; | |
7 | 8 | |
8 | 9 | init_by_lua_block { |
9 | 10 | require("luacov.runner").init() |
11 | 12 | }; |
12 | 13 | |
13 | 14 | $ENV{TEST_NGINX_RESOLVER} = '8.8.8.8'; |
14 | $ENV{TEST_NGINX_REDIS_PORT} ||= 6379; | |
15 | $ENV{TEST_NGINX_REDIS_PORT} ||= 6380; | |
15 | 16 | |
16 | 17 | no_long_string(); |
17 | 18 | run_tests(); |
4 | 4 | |
5 | 5 | our $HttpConfig = qq{ |
6 | 6 | lua_package_path "$pwd/lib/?.lua;;"; |
7 | lua_socket_log_errors Off; | |
7 | 8 | |
8 | 9 | init_by_lua_block { |
9 | 10 | require("luacov.runner").init() |
11 | 12 | }; |
12 | 13 | |
13 | 14 | $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; | |
15 | 22 | |
16 | 23 | no_long_string(); |
17 | 24 | run_tests(); |
22 | 29 | --- http_config eval: $::HttpConfig |
23 | 30 | --- config |
24 | 31 | 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 | } | |
43 | 52 | } |
44 | 53 | --- request |
45 | 54 | GET /t |
51 | 60 | --- http_config eval: $::HttpConfig |
52 | 61 | --- config |
53 | 62 | 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({ | |
58 | 67 | url = "sentinel://mymaster:m/3", |
59 | 68 | sentinels = { |
60 | { host = "127.0.0.1", port = 6381 } | |
69 | { host = "127.0.0.1", port = $TEST_NGINX_SENTINEL_PORT1 } | |
61 | 70 | } |
62 | 71 | }) |
63 | 72 | |
64 | assert(master and not err, "get_master should return the master") | |
73 | assert(master and not err, "get_master should return the master") | |
65 | 74 | assert(master:set("foo", "bar"), "set should run without error") |
66 | 75 | assert(master:get("foo") == "bar", "get(foo) should return bar") |
67 | 76 | |
68 | master:close() | |
69 | } | |
77 | master:close() | |
78 | } | |
70 | 79 | } |
71 | 80 | --- request |
72 | 81 | GET /t |
80 | 89 | location /t { |
81 | 90 | content_by_lua_block { |
82 | 91 | 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" }) | |
85 | 131 | assert(sentinel and not err, "sentinel should connect without error") |
86 | 132 | |
87 | 133 | local slaves, err = require("resty.redis.sentinel").get_slaves( |
91 | 137 | |
92 | 138 | assert(slaves and not err, "slaves should be returned without error") |
93 | 139 | |
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) | |
102 | 174 | |
103 | 175 | sentinel:close() |
104 | 176 | } |
105 | 177 | } |
106 | 178 | --- 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 | |
168 | 179 | GET /t |
169 | 180 | --- timeout: 10 |
170 | 181 | --- no_error_log |
180 | 191 | |
181 | 192 | local params = { |
182 | 193 | 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 }, | |
186 | 197 | }, |
187 | 198 | master_name = "mymaster", |
188 | 199 | role = "master", |
213 | 224 | |
214 | 225 | local params = { |
215 | 226 | 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 }, | |
219 | 230 | }, |
220 | 231 | master_name = "mymaster", |
221 | 232 | role = "slave", |
225 | 236 | local sentinel = require("resty.redis.sentinel") |
226 | 237 | sentinel.get_slaves = function() |
227 | 238 | 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 }, | |
232 | 242 | } |
233 | 243 | end |
234 | 244 | |
240 | 250 | GET /t |
241 | 251 | --- no_error_log |
242 | 252 | [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] |
39 | 39 | close $in; |
40 | 40 | |
41 | 41 | #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)\$'"); | |
43 | 43 | #file_contains($file, "attempt to write to undeclared variable"); |
44 | 44 | system("grep -H -n -E --color '.{120}' $file"); |
45 | 45 | } |