From 9bada76b8de2b9fcee4c6c22a75a143b2ea1d0b5 Mon Sep 17 00:00:00 2001 From: Michal Cichra Date: Wed, 15 Mar 2017 08:41:05 +0100 Subject: [PATCH 1/3] [resty.url] optionally require custom schema --- apicast/src/resty/url.lua | 8 ++++++-- spec/resty/url_spec.lua | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/apicast/src/resty/url.lua b/apicast/src/resty/url.lua index aafce71c6..e7d02b032 100644 --- a/apicast/src/resty/url.lua +++ b/apicast/src/resty/url.lua @@ -14,12 +14,16 @@ function _M.default_port(scheme) end end -function _M.split(url) +function _M.split(url, protocol) if not url then return nil, 'missing endpoint' end - local m = re_match(url, "^(https?):\\/\\/(?:(.+)@)?([^\\/\\s]+?)(?::(\\d+))?(\\/.*)?$", 'oj') + if not protocol then + protocol = 'https?' + end + + local m = re_match(url, "^(" .. protocol .. "):\\/\\/(?:(.+)@)?([^\\/\\s]+?)(?::(\\d+))?(\\/.*)?$", 'oj') if not m then return nil, 'invalid endpoint' -- TODO: maybe improve the error message? diff --git a/spec/resty/url_spec.lua b/spec/resty/url_spec.lua index 2d6f4e2bc..950467da8 100644 --- a/spec/resty/url_spec.lua +++ b/spec/resty/url_spec.lua @@ -40,6 +40,10 @@ describe('resty.url', function() it('removes the trailing slash', function() assert.same({'http', false, false, 'api.twitter.com', false }, split('http://api.twitter.com/')) end) + + it('works with redis DSN', function() + assert.same({'redis', 'user', 'pass', 'localhost', '6379', '/42' }, split('redis://user:pass@localhost:6379/42', 'redis')) + end) end) From 4bb526dedeb38976822f71e2689111f00cbc76d5 Mon Sep 17 00:00:00 2001 From: Michal Cichra Date: Wed, 15 Mar 2017 08:41:28 +0100 Subject: [PATCH 2/3] [redis] allow configuration via REDIS_URL [doc] REDIS_URL parameter --- CHANGELOG.md | 1 + apicast/conf/nginx.conf | 1 + apicast/src/threescale_utils.lua | 62 +++++++++++++++++++++++++++----- doc/parameters.md | 6 ++++ 4 files changed, 62 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a920fb91c..5f84f3425 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Backend HTTP client that uses cosockets [PR #295](https://github.com/3scale/apicast/pull/295) - Ability to customize main section of nginx configuration (and expose more env variables) [PR #292](https://github.com/3scale/apicast/pull/292) - Ability to lock service to specific configuration version [PR #293](https://github.com/3scale/apicast/pull/292) +- Ability to use Redis DB and password via `REDIS_URL` [PR #303](https://github.com/3scale/apicast/pull/303) ### Removed diff --git a/apicast/conf/nginx.conf b/apicast/conf/nginx.conf index d8523030c..a2c64d644 100644 --- a/apicast/conf/nginx.conf +++ b/apicast/conf/nginx.conf @@ -1,5 +1,6 @@ env REDIS_HOST; env REDIS_PORT; +env REDIS_URL; env RESOLVER; env BACKEND_ENDPOINT_OVERRIDE; diff --git a/apicast/src/threescale_utils.lua b/apicast/src/threescale_utils.lua index 73f1488ee..693afaac7 100644 --- a/apicast/src/threescale_utils.lua +++ b/apicast/src/threescale_utils.lua @@ -1,3 +1,5 @@ +local sub = string.sub +local tonumber = tonumber local redis = require 'resty.redis' local env = require 'resty.env' @@ -5,6 +7,8 @@ local env = require 'resty.env' local resty_resolver = require 'resty.resolver' local resty_balancer = require 'resty.balancer' +local resty_url = require 'resty.url' + local _M = {} -- public interface -- private @@ -110,23 +114,65 @@ function _M.resolve(host, port) return ip, port end -function _M.connect_redis(host, port) - local h = host or env.get('REDIS_HOST') or "127.0.0.1" - local p = port or env.get('REDIS_PORT') or 6379 +function _M.connect_redis(options) + local opts = {} + + local url = options and options.url or env.get('REDIS_URL') + + + if url then + url = resty_url.split(url, 'redis') + if url then + opts.host = url[4] + opts.port = url[5] + opts.db = url[6] and tonumber(sub(url[6], 2)) + opts.password = url[3] or url[2] + end + elseif options then + opts.host = options.host + opts.port = options.port + opts.db = options.db + opts.password = options.password + end + + local host = opts.host or env.get('REDIS_HOST') or "127.0.0.1" + local port = opts.prot or env.get('REDIS_PORT') or 6379 + local red = redis:new() - local ok, err = red:connect(_M.resolve(h, p)) + local ok, err = red:connect(_M.resolve(host, port)) if not ok then - return nil, _M.error("failed to connect to redis on " .. h .. ":" .. p .. ":", err) + return nil, _M.error("failed to connect to redis on " .. host .. ":" .. port .. ":", err) end + + if opts.password then + ok = red:auth(opts.password) + + if not ok then + return nil, _M.error("failed to auth on redis " .. host .. ":" .. port) + end + end + + if opts.db then + ok = red:select(opts.db) + + if not ok then + return nil, _M.error("failed to select db " .. opts.db .. " on redis " .. host .. ":" .. port) + end + end + return red end -- error and exist function _M.error(...) - ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR - ngx.say(...) - ngx.exit(ngx.status) + if ngx.get_phase() == 'timer' then + return ... + else + ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR + ngx.say(...) + ngx.exit(ngx.status) + end end function _M.missing_args(text) diff --git a/doc/parameters.md b/doc/parameters.md index 0c306d961..0314ec2e8 100644 --- a/doc/parameters.md +++ b/doc/parameters.md @@ -82,6 +82,12 @@ APIcast requires a running Redis instance for OAuth 2.0 flow. `REDIS_HOST` param APIcast requires a running Redis instance for OAuth 2.0 flow. `REDIS_PORT` parameter can be used to set the port of the Redis instance. +### `REDIS_URL` + +**Default:** no value + +APIcast requires a running Redis instance for OAuth 2.0 flow. `REDIS_URL` parameter can be used to set the full URI as DSN format like: `redis://PASSWORD@HOST:PORT/DB`. Takes precedence over `REDIS_PORT` and `REDIS_URL`. + ### `RESOLVER` Allows to specify a custom DNS resolver that will be used by OpenResty. If the `RESOLVER` parameter is empty, the DNS resolver will be autodiscovered. From 85e9f19212dca971bc50a4064d8bceece8b0222d Mon Sep 17 00:00:00 2001 From: Michal Cichra Date: Wed, 15 Mar 2017 08:41:58 +0100 Subject: [PATCH 3/3] [script] try redis commands via our connector --- script/redis | 3 +++ script/redis.lua | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100755 script/redis create mode 100644 script/redis.lua diff --git a/script/redis b/script/redis new file mode 100755 index 000000000..53c18f3e9 --- /dev/null +++ b/script/redis @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +exec resty -I apicast/src script/redis.lua "$@" diff --git a/script/redis.lua b/script/redis.lua new file mode 100644 index 000000000..0869bf1d6 --- /dev/null +++ b/script/redis.lua @@ -0,0 +1,35 @@ +#!/usr/bin/env resty -I apicast/src + +local cmd = arg[1] +local args = {} + +for i=2, #arg do + table.insert(args, arg[i]) +end + +if not cmd then + print('missing command') + print('usage: ' .. arg[0] .. ' cmd [arg [arg ...]]') + os.exit(1) +end + +local inspect = require 'inspect' +local ts = require 'threescale_utils' + +local red, err = ts.connect_redis() + +if not red and err then + print('could not connect to redis: ', err) + os.exit(1) +end + +local fn = red[cmd] +local res, err = fn(red, unpack(args)) + +if err then + print('error: ', err) + print(inspect(r)) + os.exit(1) +end + +print(inspect(res))