Skip to content

Commit

Permalink
[configuration] allow passing full configuration by Data URL
Browse files Browse the repository at this point in the history
Data URLs can be passed as the configuration url.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
  • Loading branch information
mikz committed Feb 14, 2018
1 parent 95a0e45 commit ede3cad
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 4 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- 3scale configuration (staging/production) can be passed as `-3` or `--channel` on the CLI [PR #590](https://github.com/3scale/apicast/pull/590)
- APIcast CLI loads environments defined by `APICAST_ENVIRONMENT` variable [PR #590](https://github.com/3scale/apicast/pull/590)
- Endpoint in management API to retrieve all the JSON manifests of the policies [PR #592](https://github.com/3scale/apicast/pull/592)
- Development environment (`--dev`) starts with Echo policy unless some configuration is passed [PR #593](https://github.com/3scale/apicast/pull/593)
- Added support for passing whole configuration as Data URL [PR #593](https://github.com/3scale/apicast/pull/593)

## Fixed

Expand Down
22 changes: 21 additions & 1 deletion gateway/config/development.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,27 @@
local cjson = require('cjson')

local configuration = cjson.encode(cjson.decode([[{
"services": [
{
"proxy": {
"hosts": [
"localhost",
"127.0.0.1"
],
"policy_chain": [
{ "name": "apicast.policy.echo" }
]
}
}
]
}
]]))

return {
worker_processes = '1',
master_process = 'off',
lua_code_cache = 'off',
lua_code_cache = 'on',
configuration_loader = 'lazy',
configuration_cache = 0,
configuration = string.format([[data:application/json,%s]],ngx.escape_uri(configuration)),
}
2 changes: 1 addition & 1 deletion gateway/src/apicast/cli/command/start.lua
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ end

local function build_env(options, config, context)
return {
APICAST_CONFIGURATION = options.configuration,
APICAST_CONFIGURATION = options.configuration or context.configuration,
APICAST_CONFIGURATION_LOADER = tostring(options.configuration_loader or context.configuration_loader or 'lazy'),
APICAST_CONFIGURATION_CACHE = tostring(options.cache or context.configuration_cache or 0),
THREESCALE_DEPLOYMENT_ENV = context.configuration_channel or options.channel or config.name,
Expand Down
2 changes: 1 addition & 1 deletion gateway/src/apicast/cli/environment.lua
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ function _M:add(env)
local config = loadfile(path, 't', {
print = print, inspect = require('inspect'), context = self._context,
tonumber = tonumber, tostring = tostring, os = { getenv = resty_env.value },
pcall = pcall, require = require, assert = assert, error = error,
pcall = pcall, require = require, assert = assert, error = error, string = string, ngx = ngx,
})

if not config then
Expand Down
11 changes: 10 additions & 1 deletion gateway/src/apicast/configuration_loader.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ local configuration_store = require('apicast.configuration_store')
local configuration_parser = require 'apicast.configuration_parser'
local mock_loader = require 'apicast.configuration_loader.mock'
local file_loader = require 'apicast.configuration_loader.file'
local data_url_loader = require 'apicast.configuration_loader.data_url'
local remote_loader_v1 = require 'apicast.configuration_loader.remote_v1'
local remote_loader_v2 = require 'apicast.configuration_loader.remote_v2'
local util = require 'apicast.util'
Expand All @@ -24,7 +25,7 @@ local _M = {

function _M.load(host)
local configuration = env.get('APICAST_CONFIGURATION')
local uri = resty_url.parse(configuration)
local uri = resty_url.parse(configuration, [[\w+]])

if uri then
local scheme = uri.scheme
Expand All @@ -33,10 +34,18 @@ function _M.load(host)
env.set('THREESCALE_CONFIG_FILE', uri.path)
elseif scheme == 'http' or scheme == 'https' then
env.set('THREESCALE_PORTAL_ENDPOINT', uri)
elseif scheme == 'data' then -- TODO: this requires upgrading lua-resty-env
return data_url_loader.call(configuration)
else
ngx.log(ngx.WARN, 'unknown configuration URI: ', uri)
end
elseif configuration then
do -- TODO: this will be not necessary upgrading lua-resty-env
local config = data_url_loader.call(configuration)

if config then return config end
end

ngx.log(ngx.DEBUG, 'falling back to file system path for configuration')
env.set('THREESCALE_CONFIG_FILE', configuration)
end
Expand Down
58 changes: 58 additions & 0 deletions gateway/src/apicast/configuration_loader/data_url.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
local MimeType = require('resty.mime')
local cjson = require('cjson')

local _M = {}

local pattern = [[^data:(?<mediatype>[a-z]+\/[a-z0-9-+.]+(?<charset>;[a-z-]+=[a-z0-9-]+)?)?(?<base64>;base64)?,(?<data>[a-z0-9!$&',()*+;=\-._~:@\/?%\s]*?)$]]
local re_match = ngx.re.match

local function parse(url)
local match, err = re_match(url, pattern, 'oji')

if match then

return {
mime_type = MimeType.new(match.mediatype),
data = match.data,
base64 = not not match.base64,
}
else
return nil, err or 'not valid data-url'
end
end

local decoders = {
['application/json'] = function(data) return cjson.encode(cjson.decode(data)) end,
}

local function decode(data_url)

local data = data_url.data

if data_url.base64 then
data = ngx.decode_base64(data)
else
data = ngx.unescape_uri(data)
end

local media_type = data_url.mime_type.media_type

local decoder = decoders[media_type]

if decoder then
return decoder(data)
else
return nil, 'unsupported mediatype'
end
end

function _M.call(uri)
local data_url, err = parse(uri)

if not data_url then return nil, err end

return decode(data_url)
end


return _M
40 changes: 40 additions & 0 deletions spec/configuration_loader/data_url_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
local loader = require 'apicast.configuration_loader.data_url'
local cjson = require('cjson')

describe('Configuration Data URL loader', function()
describe('.call', function()
it('ignores empty url', function()
assert.same({nil, 'not valid data-url'}, { loader.call() })
assert.same({nil, 'not valid data-url'}, { loader.call('') })
end)

local config = cjson.encode{
services = {
{ id = 21 },
{ id = 42 },
}
}

it('decodes urlencoded data url', function()
local url = ([[data:application/json,%s]]):format(ngx.escape_uri(config))
assert.same(config, loader.call(url))
end)

it('accepts bug ignores charset in the data url', function()
local url = ([[data:application/json;charset=iso8601,%s]]):format(ngx.escape_uri(config))
assert.same(config, loader.call(url))
end)

it('decodes base64 encoded data url', function()
local url = ([[data:application/json;base64,%s]]):format(ngx.encode_base64(config))
assert.same(config, loader.call(url))
end)

it('requires application/json media type', function()
local url = ([[data:text/json,%s]]):format(ngx.escape_uri(config))

assert.same({nil, 'unsupported mediatype'}, { loader.call(url) })
end)

end)
end)

0 comments on commit ede3cad

Please sign in to comment.