Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[THREESCALE-10130] APIcast using stale configuration for a deleted Product #1488

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Fixed confusing log display when APIcast listens on HTTPS and path routing is enabled [PR #1486](https://github.com/3scale/APIcast/pull/1486/files) [THREESCALE #8486](https://issues.redhat.com/browse/THREESCALE-8486)

- Fixed Conditional policy evaluating incorrectly: second policy in policy chain that implement export() always triggers [PR #1485](https://github.com/3scale/APIcast/pull/1485) [THREESCALE-9320](https://issues.redhat.com/browse/THREESCALE-9320)
- Fix APIcast using stale configuration for deleted products [PR #1488](https://github.com/3scale/APIcast/pull/1488) [THREESCALE-10130](https://issues.redhat.com/browse/THREESCALE-10130)

### Added

Expand Down
14 changes: 11 additions & 3 deletions gateway/src/apicast/configuration_loader.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
local configuration_store = require('apicast.configuration_store')
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'
Expand Down Expand Up @@ -75,7 +75,7 @@
return _M.configure(context.configuration, contents)
end

function _M.configure(configuration, contents)
function _M.configure(configuration, contents, reset_cache)
if not configuration then
return nil, 'not initialized'
end
Expand All @@ -90,6 +90,12 @@
end

if config then
-- We have the configuration available at this point so it's safe to purge the
-- cache and remove old items (deleted services)
if reset_cache then
ngx.log(ngx.DEBUG, "flushing caches as part of the configuration reload")
configuration:reset()
end
configuration:store(config, ttl())
collectgarbage()
return config
Expand Down Expand Up @@ -160,7 +166,7 @@

local function refresh_configuration(configuration)
local config = _M.load()
local init, err = _M.configure(configuration, config)
local init, err = _M.configure(configuration, config, true)

Check warning on line 169 in gateway/src/apicast/configuration_loader.lua

View check run for this annotation

Codecov / codecov/patch

gateway/src/apicast/configuration_loader.lua#L169

Added line #L169 was not covered by tests

if init then
ngx.log(ngx.DEBUG, 'updated configuration via timer: ', config)
Expand Down Expand Up @@ -229,6 +235,8 @@
if not config then
ngx.log(ngx.WARN, 'failed to get config for host: ', host)
end
-- Lazy load will never returned stale data, so no need to reset the
-- cache
_M.configure(configuration, config)
end

Expand Down
8 changes: 5 additions & 3 deletions gateway/src/apicast/configuration_store.lua
Original file line number Diff line number Diff line change
Expand Up @@ -165,16 +165,18 @@ function _M.store(self, config, ttl)
return config
end

function _M.reset(self, cache_size)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume the cache_size is never being used

--- Flush all LRU cache
function _M.reset(self)
if not self.services then
return nil, 'not initialized'
end

self.services = lrucache.new(cache_size or _M.cache_size)
self.cache = lrucache.new(cache_size or _M.cache_size)
self.services:flush_all()
self.cache:flush_all()
self.configured = false
end


function _M.add(self, service, ttl)
if not self.services then
return nil, 'not initialized'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ local _M = require('apicast.policy').new('Load Configuration')
local ssl = require('ngx.ssl')

local configuration_loader = require('apicast.configuration_loader').new()
local configuration_store = require('apicast.configuration_store', 'builtin')
local configuration_store = require('apicast.configuration_store')

local new = _M.new

Expand Down
1 change: 0 additions & 1 deletion spec/configuration_loader/remote_v2_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ local test_backend_client = require 'resty.http_ng.backend.test'
local cjson = require 'cjson'
local user_agent = require 'apicast.user_agent'
local env = require 'resty.env'
local format = string.format

local service_generator = function(n)
local services = {}
Expand Down
18 changes: 18 additions & 0 deletions spec/configuration_loader_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,24 @@ insulate('Configuration object', function()

assert.truthy(config:find_by_id('42'))
end)

it('should reset cache', function()
local config = configuration_store.new()

assert.truthy(_M.configure(config, cjson.encode({ services = {
{ id = 42, proxy = { hosts = { 'localhost' } } },
{ id = 43, proxy = { hosts = { 'localhost' } } }
}})))

assert.truthy(config:find_by_id('42'))
assert.truthy(config:find_by_id('43'))

assert.truthy(_M.configure(config, cjson.encode({ services = {
{ id = 42, proxy = { hosts = { 'localhost' } } },
}}), true))
assert.truthy(config:find_by_id('42'))
assert.falsy(config:find_by_id('43'))
end)
end)

describe('lazy loader', function()
Expand Down
77 changes: 54 additions & 23 deletions spec/configuration_store_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,35 @@ local configuration = require('apicast.configuration_store')
local tablex = require("pl.tablex")

describe('Configuration Store', function()
describe('.new', function()
it('should not store more than the size of the services cache', function()
local service1 = { id = '42', hosts = { 'example1.com' } }
local service2 = { id = '43', hosts = { 'example2.com' } }

local store = configuration.new(1)

-- Add 2, as size is 1 only the second will be kept.
store:add(service1)
store:add(service2)

assert.is_nil(store:find_by_id('42'))
assert.equal(service2, store:find_by_id('43'))
end)

it('should not store mote then the size of the hosts cache', function()
local service1 = { id = '42', hosts = { 'example1.com' } }
local service2 = { id = '43', hosts = { 'example2.com' } }

local store = configuration.new(1)

-- Add 2, as size is 1 only the second will be kept.
store:add(service1)
store:add(service2)

assert.same({}, store:find_by_host('example1.com'), false)
assert.same({ service2 }, store:find_by_host('example2.com'), false)
end)
end)

describe('.store', function()
it('stores configuration', function()
Expand Down Expand Up @@ -197,48 +226,50 @@ describe('Configuration Store', function()
assert.same({}, store.cache.hasht)
end)

it('deletes all services', function()
store.services['42'] = {}
it('should not serve old hosts', function()
local service1 = { id = '41', hosts = { 'example1.com' } }
local service2 = { id = '42', hosts = { 'example2.com' } }
local service3 = { id = '43', hosts = { 'example3.com' } }

store:add(service1)
store:add(service2)
store:reset()
store:add(service3)

assert.same({}, store.services.hasht)
assert.same({}, store:find_by_host('example1.com'), false)
assert.same({}, store:find_by_host('example2.com'), false)
assert.same({ service3 }, store:find_by_host('example3.com'), false)
end)

it('sets configured flag', function()
store.configured = true
it('deletes all services', function()
store.services['42'] = {}

store:reset()

assert.falsy(store.configured)
assert.same({}, store.services.hasht)
end)

it('resets de size of the services cache', function()
local service1 = { id = '42', hosts = { 'example1.com' } }
local service2 = { id = '43', hosts = { 'example2.com' } }

store:reset(1)
it('should not serve old services', function()
local service1 = { id = '41', hosts = { 'example1.com' } }
local service2 = { id = '42', hosts = { 'example2.com' } }
local service3 = { id = '43', hosts = { 'example3.com' } }

-- Add 2, as size is 1 only the second will be kept.
store:add(service1)
store:add(service2)
store:reset()
store:add(service3)

assert.is_nil(store:find_by_id('41'))
assert.is_nil(store:find_by_id('42'))
assert.equal(service2, store:find_by_id('43'))
assert.equal(service3, store:find_by_id('43'))
end)

it('resets de size of the hosts cache', function()
local service1 = { id = '42', hosts = { 'example1.com' } }
local service2 = { id = '43', hosts = { 'example2.com' } }

store:reset(1)
it('sets configured flag', function()
store.configured = true

-- Add 2, as size is 1 only the second will be kept.
store:add(service1)
store:add(service2)
store:reset()

assert.same({}, store:find_by_host('example1.com'), false)
assert.same({ service2 }, store:find_by_host('example2.com'), false)
assert.falsy(store.configured)
end)
end)
end)
Expand Down
Loading