Skip to content

Commit

Permalink
[THREESCALE-1524][config] add support to filter services by public URL.
Browse files Browse the repository at this point in the history
This commit adds the option to filter services based on the endpoint.
This commit expose a new config flag using the env variable
`APICAST_SERVICES_FILTER_BY_URL`, services filter happens on
`configuration.filter_services`.

Signed-off-by: Eloy Coto <eloy.coto@gmail.com>
  • Loading branch information
eloycoto committed Apr 26, 2019
1 parent d976a98 commit 7a9320e
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Added

- Ability to configure client certificate chain depth [PR #1006](https://github.com/3scale/APIcast/pull/1006)
- Ability to filter services by endpoint name using Regexp [PR #1022](https://github.com/3scale/APIcast/pull/1022) [THREESCALE-1524](https://issues.jboss.org/browse/THREESCALE-1524)

### Fixed

Expand Down
23 changes: 23 additions & 0 deletions doc/parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,29 @@ before the client is throttled by adding latency.
When set to _true_, APIcast will log the response code of the response returned by the API backend in 3scale. In some plans this information can later be consulted from the 3scale admin portal.
Find more information about the Response Codes feature on the [3scale support site](https://access.redhat.com/documentation/en-us/red_hat_3scale/2.saas/html/analytics/response-codes-tracking).

### `APICAST_SERVICES_FILTER_BY_URL`
**Value:** a regular expression
**Default:**: .*
**Example:** *.example.com

Used to filter the service configured in the 3scale API Manager, the filter
matches with the public base URL. The services that don't match the filter will
be discarded.

Note: If a service does not match, but is included in the
`APICAST_SERVICES_LIST` the service will not be discarded

Example:

Regexp Filter: http:\/\/^test.*.com
Service 1: backend endpoint http://test.foo.com
Service 2: backend endpoint http://prod.foo.com
Service 3: backend endpoint http://test.bar.com
Service 4: backend endpoint http://prod.bar.com

The services that will be configured in Apicast will be 1 and 3. Services 2 and
4 will be discarded.

### `APICAST_SERVICES_LIST`
**Value:** a comma-separated list of service IDs

Expand Down
44 changes: 37 additions & 7 deletions gateway/src/apicast/configuration.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ local insert = table.insert
local setmetatable = setmetatable
local null = ngx.null

local re = require 'ngx.re'
local env = require 'resty.env'
local resty_url = require 'resty.url'
local util = require 'apicast.util'
local policy_chain = require 'apicast.policy_chain'
local mapping_rule = require 'apicast.mapping_rule'
local tab_new = require('resty.core.base').new_tab

local re = require 'ngx.re'
local match = ngx.re.match

local mt = { __index = _M, __tostring = function() return 'Configuration' end }

local function map(func, tbl)
Expand Down Expand Up @@ -140,21 +142,49 @@ function _M.services_limit()
end

function _M.filter_services(services, subset)
subset = subset and util.to_hash(subset) or _M.services_limit()
if not subset or not next(subset) then return services end
local selected_services = {}
local service_regexp_filter = env.value("APICAST_SERVICES_FILTER_BY_URL")

if service_regexp_filter then
-- Checking that the regexp sent is correct, if not an empty service list
-- will be returned.
local _, err = match("", service_regexp_filter)
if err then
-- @todo this return and empty list, Apicast will continue running maybe
-- process need to be stopped here.
ngx.log(ngx.ERR, "APICAST_SERVICES_FILTER_BY_URL cannot compile and all services are filtering out")
return selected_services
end
end

local s = {}
subset = subset and util.to_hash(subset) or _M.services_limit()
if (not subset or not next(subset)) and not service_regexp_filter then return services end
subset = subset or {}

for i = 1, #services do
local service = services[i]
local success = false

if service_regexp_filter then
for j = 1,#service.hosts do
local val, _ = match(service.hosts[j], service_regexp_filter)
if val then
success = true
end
end
end

if subset[service.id] then
insert(s, service)
success = true
end

if success then
insert(selected_services, service)
else
ngx.log(ngx.WARN, 'filtering out service ', service.id)
end
end

return s
return selected_services
end

function _M.new(configuration)
Expand Down
47 changes: 47 additions & 0 deletions spec/configuration_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,53 @@ describe('Configuration object', function()
assert.same(services, filter_services(services, { '42' }))
assert.same({}, filter_services(services, { '21' }))
end)

describe("with service filter", function()
local mockservices = {
{id="42", hosts={"test.foo.com", "test.bar.com"}},
{id="12", hosts={"staging.foo.com"}},
{id="21", hosts={"prod.foo.com"}},
}

it("validates default case", function()
assert.same(filter_services(mockservices, nil), mockservices)
end)

it("reads from environment variable", function()
env.set('APICAST_SERVICES_FILTER_BY_URL', '.*.foo.com')
assert.same(filter_services(mockservices, nil), mockservices)

env.set('APICAST_SERVICES_FILTER_BY_URL', '^test.*')
assert.same(filter_services(mockservices, nil), {mockservices[1]})

env.set('APICAST_SERVICES_FILTER_BY_URL', '^(test|prod).*')
assert.same(filter_services(mockservices, nil), {mockservices[1], mockservices[3]})
end)

it("matches the second host", function()
env.set('APICAST_SERVICES_FILTER_BY_URL', '^test.bar.com')
assert.same(filter_services(mockservices, nil), {mockservices[1]})
end)

it("validates invalid regexp", function()
env.set('APICAST_SERVICES_FILTER_BY_URL', '^]')
assert.same(filter_services(mockservices, nil), {})
end)

it("combination with service list", function()
env.set('APICAST_SERVICES_FILTER_BY_URL', '^test.*')
assert.same(filter_services(mockservices, {"21"}), {
mockservices[1],
mockservices[3]})


env.set('APICAST_SERVICES_LIST', '42,21')
assert.same(filter_services(mockservices, nil), {
mockservices[1],
mockservices[3]})
end)
end)

end)

insulate('.services_limit', function()
Expand Down

0 comments on commit 7a9320e

Please sign in to comment.