Skip to content

Commit

Permalink
Merge pull request #303 from 3scale/redis-url
Browse files Browse the repository at this point in the history
[redis] allow configuration via REDIS_URL
  • Loading branch information
mikz authored Mar 15, 2017
2 parents 829f20f + 85e9f19 commit a9e8baa
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions apicast/conf/nginx.conf
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
env REDIS_HOST;
env REDIS_PORT;
env REDIS_URL;
env RESOLVER;
env BACKEND_ENDPOINT_OVERRIDE;

Expand Down
8 changes: 6 additions & 2 deletions apicast/src/resty/url.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
62 changes: 54 additions & 8 deletions apicast/src/threescale_utils.lua
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
local sub = string.sub
local tonumber = tonumber

local redis = require 'resty.redis'
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
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions doc/parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions script/redis
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env sh

exec resty -I apicast/src script/redis.lua "$@"
35 changes: 35 additions & 0 deletions script/redis.lua
Original file line number Diff line number Diff line change
@@ -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))
4 changes: 4 additions & 0 deletions spec/resty/url_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand Down

0 comments on commit a9e8baa

Please sign in to comment.