diff --git a/apicast/src/apicast.lua b/apicast/src/apicast.lua index 053f42ee5..ec58a5f23 100644 --- a/apicast/src/apicast.lua +++ b/apicast/src/apicast.lua @@ -7,6 +7,7 @@ local math = math local setmetatable = setmetatable local env = require('resty.env') local reload_config = env.enabled('APICAST_RELOAD_CONFIG') +local configuration_store = require('configuration_store') local user_agent = require('user_agent') local noop = function() end @@ -34,11 +35,16 @@ end local mt = { __index = _M } +--- This is called when APIcast boots the master process. function _M.new() - return setmetatable({ proxy = proxy.new() }, mt) + -- FIXME: this is really bad idea, this file is shared across all requests, + -- so that means sharing something in this module would be sharing it acros all requests + -- and in multi-tenant environment that would mean leaking information + local configuration = configuration_store.new() + return setmetatable({ proxy = proxy.new(configuration) }, mt) end -function _M.init() +function _M:init() user_agent.cache() math.randomseed(ngx.now()) @@ -46,24 +52,24 @@ function _M.init() for _=1,3 do math.random() end local config, err = configuration_loader.init() - local init = config and proxy.init(config) + local init = config and self.proxy:configure(config) if not init then handle_missing_configuration(err) end end -local function refresh_config() +local function refresh_config(proxy) local config, err = configuration_loader.boot() if config then - proxy.init(config) + proxy:configure(config) else ngx.log(ngx.ERR, 'failed to refresh configuration: ', err) end end -function _M.init_worker() +function _M:init_worker() local interval = tonumber(env.get('AUTO_UPDATE_INTERVAL'), 10) or 0 local function schedule(...) @@ -77,12 +83,12 @@ function _M.init_worker() local handler - handler = function (premature) + handler = function (premature, ...) if premature then return end ngx.log(ngx.INFO, 'auto updating configuration') - local updated, err = pcall(refresh_config) + local updated, err = pcall(refresh_config, ...) if updated then ngx.log(ngx.INFO, 'auto updating configuration finished successfuly') @@ -90,11 +96,11 @@ function _M.init_worker() ngx.log(ngx.ERR, 'auto updating configuration failed with: ', err) end - schedule(interval, handler) + schedule(interval, handler, ...) end if interval > 0 then - schedule(interval, handler) + schedule(interval, handler, self.proxy) end end @@ -116,11 +122,11 @@ function _M:rewrite() p:configure(config) end - p.set_upstream(p.set_service(host)) + p.set_upstream(p:set_service(host)) end -function _M.post_action() - proxy:post_action() +function _M:post_action() + self.proxy:post_action() end function _M:access() diff --git a/apicast/src/configuration_loader.lua b/apicast/src/configuration_loader.lua index 40ff938cf..0a0552935 100644 --- a/apicast/src/configuration_loader.lua +++ b/apicast/src/configuration_loader.lua @@ -16,7 +16,7 @@ function _M.boot(host) return mock_loader.call() or file_loader.call() or remote_loader_v2.call() or remote_loader_v1.call(host) or error('missing configuration') end -_M.save = mock_loader.save +_M.mock = mock_loader.save -- Cosocket API is not available in the init_by_lua* context (see more here: https://github.com/openresty/lua-nginx-module#cosockets-not-available-everywhere) -- For this reason a new process needs to be started to download the configuration through 3scale API diff --git a/apicast/src/management.lua b/apicast/src/management.lua index fb7c19f03..b6cdce309 100644 --- a/apicast/src/management.lua +++ b/apicast/src/management.lua @@ -1,7 +1,7 @@ local _M = {} local cjson = require('cjson') -local provider = require('proxy') +local proxy = require('module').proxy local router = require('router') local configuration_parser = require('configuration_parser') local configuration_loader = require('configuration_loader') @@ -31,8 +31,8 @@ end function _M.status() -- TODO: this should be fixed for multi-tenant deployment - local has_configuration = provider.configuration.configured - local has_services = #(provider.configuration:all()) > 0 + local has_configuration = proxy.configuration.configured + local has_services = #(proxy.configuration:all()) > 0 if not has_configuration then return { status = 'error', error = 'not configured', success = false } @@ -44,7 +44,7 @@ function _M.status() end function _M.config() - local config = provider.configuration + local config = proxy.configuration local contents = cjson.encode(config.configured and { services = config:all() } or nil) ngx.header.content_type = 'application/json; charset=utf-8' @@ -64,7 +64,7 @@ function _M.update_config() end local config = configuration_parser.decode(data) - provider.configure(config) + proxy:configure(config) -- TODO: respond with proper 304 Not Modified when config is the same local response = cjson.encode({ status = 'ok', config = config or cjson.null }) ngx.header.content_type = 'application/json; charset=utf-8' @@ -74,7 +74,7 @@ end function _M.delete_config() ngx.log(ngx.DEBUG, 'management config delete') - provider.configuration:reset() + proxy.configuration:reset() -- TODO: respond with proper 304 Not Modified when config is the same local response = cjson.encode({ status = 'ok', config = cjson.null }) ngx.header.content_type = 'application/json; charset=utf-8' @@ -90,7 +90,7 @@ function _M.boot() ngx.log(ngx.DEBUG, 'management boot config:' .. inspect(data)) - provider.init(config) + proxy:configure(config) ngx.say(response) end diff --git a/apicast/src/proxy.lua b/apicast/src/proxy.lua index 85a3eda15..2c150b87b 100644 --- a/apicast/src/proxy.lua +++ b/apicast/src/proxy.lua @@ -24,12 +24,7 @@ local empty = {} local response_codes = env.enabled('APICAST_RESPONSE_CODES') local request_logs = env.enabled('APICAST_REQUEST_LOGS') -local _M = { - -- FIXME: this is really bad idea, this file is shared across all requests, - -- so that means sharing something in this module would be sharing it acros all requests - -- and in multi-tenant environment that would mean leaking information - configuration = configuration_store.new() -} +local _M = { } local mt = { __index = _M @@ -37,12 +32,16 @@ local mt = { function _M.new(configuration) return setmetatable({ - configuration = configuration + configuration = assert(configuration, 'missing proxy configuration') }, mt) end function _M:configure(contents) - if self and not contents then contents = self end + local configuration = self.configuration + + if not configuration then + return nil, 'not initialized' + end local config, err = configuration_parser.parse(contents) @@ -53,9 +52,6 @@ function _M:configure(contents) return nil, err end - -- FIXME: using global configuration interface - local configuration = self.configuration or _M.configuration - if config then return configuration:store(config) end @@ -71,8 +67,6 @@ function _M:configured(host) return next(hosts) and true end -_M.init = function(config) return _M.configure(_M, config) end - -- Error Codes local function error_no_credentials(service) ngx.log(ngx.INFO, 'no credentials provided for service ', service.id) @@ -254,9 +248,9 @@ function _M.authorize(backend_version, service) end end -function _M.set_service(host) +function _M:set_service(host) host = host or ngx.var.host - local service = _M:find_service(host) + local service = self:find_service(host) if not service then error_service_not_found(host) @@ -314,7 +308,7 @@ end function _M:call(host) host = host or ngx.var.host - local service = ngx.ctx.service or self.set_service(host) + local service = ngx.ctx.service or self:set_service(host) self:set_backend_upstream(service) diff --git a/spec/proxy_spec.lua b/spec/proxy_spec.lua index fd7a25c27..5a4e6cc48 100644 --- a/spec/proxy_spec.lua +++ b/spec/proxy_spec.lua @@ -1,9 +1,11 @@ -local proxy = require 'proxy' local configuration_store = require 'configuration_store' describe('Proxy', function() + local configuration, proxy + before_each(function() - proxy.configuration:reset() + configuration = configuration_store.new() + proxy = require('proxy').new(configuration) end) it('has access function', function() @@ -23,12 +25,10 @@ describe('Proxy', function() it('finds service by host', function() local example = { id = 42, hosts = { 'example.com'} } - local configuration = configuration_store.new() - local p = assert(proxy.new(configuration)) configuration:add(example) - assert.same(example, p:find_service('example.com')) + assert.same(example, proxy:find_service('example.com')) assert.falsy(proxy:find_service('unknown')) end) @@ -49,9 +49,9 @@ describe('Proxy', function() end) it('returns true when configured', function() - local configuration = configuration_store.new() - configuration:add({ id = 42, hosts = { 'example.com' } }) - local p = assert(proxy.new(configuration)) + local config = configuration_store.new() + config:add({ id = 42, hosts = { 'example.com' } }) + local p = assert(proxy.new(config)) assert.truthy(p:configured('example.com')) end) diff --git a/t/001-management.t b/t/001-management.t index 3ea649644..07597f2f0 100644 --- a/t/001-management.t +++ b/t/001-management.t @@ -22,7 +22,7 @@ When configuration is saved, readiness probe returns success. lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('proxy').configure({ services = { { id = 42 } } }) + require('module').proxy:configure({ services = { { id = 42 } } }) } --- config include $TEST_NGINX_MANAGEMENT_CONFIG; @@ -53,7 +53,7 @@ Should respond with error status and a reason. --- http_config lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('proxy').configure({services = { }}) + require('module').proxy:configure({services = { }}) } --- config include $TEST_NGINX_MANAGEMENT_CONFIG; @@ -86,7 +86,7 @@ Endpoint that dumps the original configuration. lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('proxy').configure({ services = { { id = 42 } } }) + require('module').proxy:configure({ services = { { id = 42 } } }) } --- config include $TEST_NGINX_MANAGEMENT_CONFIG; @@ -148,7 +148,7 @@ env RESOLVER=127.0.0.1:1953; --- http_config lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ services = { { id = 42 } } }) + require('module').proxy:configure({ services = { { id = 42 } } }) } --- config include $TEST_NGINX_MANAGEMENT_CONFIG; @@ -171,7 +171,7 @@ env RESOLVER=127.0.0.1:1953; --- http_config lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ services = { { id = 42 } } }) + require('module').proxy:configure({ services = { { id = 42 } } }) } --- config include $TEST_NGINX_MANAGEMENT_CONFIG; diff --git a/t/003-apicast.t b/t/003-apicast.t index 7768839c4..4619a01c2 100644 --- a/t/003-apicast.t +++ b/t/003-apicast.t @@ -23,7 +23,7 @@ The message is configurable as well as the status. --- http_config lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { backend_version = 1, @@ -49,7 +49,7 @@ The message is configurable and status also. --- http_config lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 42, @@ -77,7 +77,7 @@ The message is configurable and default status is 403. --- http_config lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { backend_version = 1, @@ -118,7 +118,7 @@ It asks backend and then forwards the request to the api. lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 42, @@ -172,7 +172,7 @@ When mapping rule has a parameter with fixed value it has to be matched. include $TEST_NGINX_UPSTREAM_CONFIG; lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 42, @@ -210,7 +210,7 @@ When mapping rule has a parameter with fixed value it has to be matched. include $TEST_NGINX_UPSTREAM_CONFIG; lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 42, @@ -255,7 +255,7 @@ When mapping rule has a parameter with variable value it has to exist. include $TEST_NGINX_UPSTREAM_CONFIG; lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 42, @@ -300,7 +300,7 @@ X-3scale-usage: usage%5Bbar%5D=3 include $TEST_NGINX_UPSTREAM_CONFIG; lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 42, @@ -370,7 +370,7 @@ So when booting it can be immediately known that some of them won't work. --- config location /t { content_by_lua_block { - require('proxy').configure({ + require('module').proxy:configure({ services = { { id = 1, proxy = { hosts = { 'foo', 'bar' } } }, { id = 2, proxy = { hosts = { 'foo', 'daz' } } }, @@ -397,7 +397,7 @@ Including it's host so it is easy to see that configuration was loaded. --- config location /t { content_by_lua_block { - require('proxy').configure({ + require('module').proxy:configure({ services = { { id = 1, proxy = { hosts = { 'foo', 'bar' } } }, { id = 2, proxy = { hosts = { 'baz', 'daz' } } }, @@ -422,7 +422,7 @@ When X-3scale-Debug header has value of the backend authentication. include $TEST_NGINX_UPSTREAM_CONFIG; lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('proxy').configure({ + require('configuration_loader').mock({ services = { { id = 42, @@ -470,7 +470,7 @@ env RESOLVER=127.0.0.1:1953; include $TEST_NGINX_UPSTREAM_CONFIG; lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('proxy').configure({ + require('configuration_loader').mock({ services = { { id = 42, diff --git a/t/004-apicast-path-routing.t b/t/004-apicast-path-routing.t index aa9606b4f..79832224e 100644 --- a/t/004-apicast-path-routing.t +++ b/t/004-apicast-path-routing.t @@ -24,7 +24,7 @@ env APICAST_PATH_ROUTING_ENABLED=1; include $TEST_NGINX_UPSTREAM_CONFIG; lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 42, @@ -100,7 +100,7 @@ env APICAST_PATH_ROUTING_ENABLED=1; include $TEST_NGINX_UPSTREAM_CONFIG; lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 42, diff --git a/t/005-apicast-oauth.t b/t/005-apicast-oauth.t index b42d0492c..1ac4a0fde 100644 --- a/t/005-apicast-oauth.t +++ b/t/005-apicast-oauth.t @@ -24,7 +24,7 @@ __DATA__ lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { backend_version = 'oauth', proxy = { oauth_login_url = "http://example.com/redirect" } } @@ -49,7 +49,7 @@ Location: http://example.com/redirect?error=invalid_client lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { backend_version = 'oauth', proxy = { oauth_login_url = "http://example.com/redirect" } } @@ -89,7 +89,7 @@ Location: http://example.com/redirect\?scope=whatever&response_type=code&state=[ lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 42, backend_version = 'oauth', @@ -127,7 +127,7 @@ Location: http://example.com/redirect\?scope=whatever&response_type=token&error= --- http_config lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { backend_version = 'oauth' } } @@ -145,7 +145,7 @@ POST /oauth/token --- http_config lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { backend_version = 'oauth' } } @@ -163,7 +163,7 @@ POST /oauth/token?grant_type=authorization_code&client_id=client_id&redirect_uri --- http_config lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { backend_version = 'oauth' } } @@ -181,7 +181,7 @@ GET /callback --- http_config lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { backend_version = 'oauth' } } @@ -204,7 +204,7 @@ include $TEST_NGINX_APICAST_CONFIG; --- http_config lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { backend_version = 'oauth' } } @@ -226,7 +226,7 @@ Not part of the RFC. This is the Gateway API to create access tokens and redirec --- http_config lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 42, backend_version = 'oauth', oauth_login_url = "" } } @@ -264,7 +264,7 @@ Location: http://example.com/redirect\?code=\w+&state=clientstate --- http_config lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 42, backend_version = 'oauth' } } @@ -331,7 +331,7 @@ GET /t include $TEST_NGINX_UPSTREAM_CONFIG; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { backend_version = 'oauth', @@ -376,7 +376,7 @@ yay, upstream lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 42, backend_version = 'oauth', @@ -409,7 +409,7 @@ Location: http://example.com/redirect\?code=\w+&state=12345 include $TEST_NGINX_UPSTREAM_CONFIG; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { backend_version = 'oauth', @@ -452,7 +452,7 @@ yay, upstream lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { backend_version = 'oauth', @@ -483,7 +483,7 @@ credentials missing! lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { backend_version = 'oauth', diff --git a/t/006-apicast-subset-of-services.t b/t/006-apicast-subset-of-services.t index 4ff98318f..9d738bbec 100644 --- a/t/006-apicast-subset-of-services.t +++ b/t/006-apicast-subset-of-services.t @@ -23,7 +23,7 @@ env APICAST_SERVICES=42,21; include $TEST_NGINX_UPSTREAM_CONFIG; lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 42, diff --git a/t/008-apicast-request-logs.t b/t/008-apicast-request-logs.t index 674542a07..d94fb153d 100644 --- a/t/008-apicast-request-logs.t +++ b/t/008-apicast-request-logs.t @@ -25,7 +25,7 @@ __DATA__ lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 42, @@ -77,7 +77,7 @@ env APICAST_REQUEST_LOGS=1; lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 42, @@ -134,7 +134,7 @@ env APICAST_RESPONSE_CODES=1; lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 42, @@ -192,7 +192,7 @@ env APICAST_RESPONSE_CODES=1; lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 42, diff --git a/t/009-apicast-caching.t b/t/009-apicast-caching.t index 89253fe7e..7e3dfd663 100644 --- a/t/009-apicast-caching.t +++ b/t/009-apicast-caching.t @@ -22,7 +22,7 @@ First call is done synchronously and the second out of band. include $TEST_NGINX_UPSTREAM_CONFIG; lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 42, @@ -80,7 +80,7 @@ Two services can exist together and are split by their hostname. include $TEST_NGINX_UPSTREAM_CONFIG; lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 1, diff --git a/t/010-apicast-mapping-rules.t b/t/010-apicast-mapping-rules.t index fe479e86c..8ba07f8d2 100644 --- a/t/010-apicast-mapping-rules.t +++ b/t/010-apicast-mapping-rules.t @@ -22,7 +22,7 @@ First call is done synchronously and the second out of band. include $TEST_NGINX_UPSTREAM_CONFIG; lua_package_path "$TEST_NGINX_LUA_PATH"; init_by_lua_block { - require('configuration_loader').save({ + require('configuration_loader').mock({ services = { { id = 42,