Skip to content
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 @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Fixed Financial-grade API (FAPI) policy not showing up in the admin portal [PR #1528](https://github.com/3scale/APIcast/pull/1528) [THREESCALE-11620](https://issues.redhat.com/browse/THREESCALE-11620)
- Remove Conditional Policy from the UI [PR #1534](https://github.com/3scale/APIcast/pull/1534) [THREESCALE-6116](https://issues.redhat.com/browse/THREESCALE-6116)
- Remove redis connection error message from response body in edge limiting policy [PR #1537](https://github.com/3scale/APIcast/pull/1537) [THREESCALE-11701](https://issues.redhat.com/browse/THREESCALE-11701)
- Fix `on_failed` policy doesn't work with `conditional policy` [THREESCALE-11738](https://issues.redhat.com/browse/THREESCALE-11738) [PR #1541](https://github.com/3scale/APIcast/pull/1541)

### Added

Expand Down
40 changes: 40 additions & 0 deletions gateway/src/apicast/policy/conditional/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ When the request is not a POST, the order of execution for each phase is:
2) Caching
3) Upstream

NOTE: when one or more policies in conditional chain are invalid, APIcast will
skip the invalid policy and load the next policy in the chain, which may lead
to unexpected behavior. If you want to terminate the chain, add an `on_failed`
policy to the chain.

## Conditions

Expand Down Expand Up @@ -138,3 +142,39 @@ the `Backend` header of the request is `staging`:
}

```

With `on-failed` policy

```json
{
"name":"conditional",
"version":"builtin",
"configuration":{
"condition":{
"operations":[
{
"left":"{{ headers['Backend'] }}",
"left_type":"liquid",
"op":"==",
"right":"staging"
}
]
},
"policy_chain":[
{
"name": "example",
"version": "1.0",
"configuration": {}
},
{
"name": "on_failed",
"version": "builtin",
"configuration": {
"error_status_code": 419
}
}
]
}
}

```
68 changes: 40 additions & 28 deletions gateway/src/apicast/policy/conditional/conditional.lua
Original file line number Diff line number Diff line change
@@ -1,54 +1,66 @@
local ipairs = ipairs
local insert = table.insert

local policy = require('apicast.policy')
local Policy = require('apicast.policy')
local policy_phases = require('apicast.policy').phases
local PolicyChain = require('apicast.policy_chain')
local Condition = require('apicast.conditions.condition')
local Operation = require('apicast.conditions.operation')
local ngx_variable = require('apicast.policy.ngx_variable')

local _M = policy.new('Conditional policy', 'builtin')
local _M = Policy.new('Conditional policy', 'builtin')

local new = _M.new

local function build_policy_chain(chain)
if not chain then return {} end
do
local null = ngx.null

local policies = {}
local function policy_configuration(policy)
local config = policy.configuration

Check warning on line 19 in gateway/src/apicast/policy/conditional/conditional.lua

View check run for this annotation

Codecov / codecov/patch

gateway/src/apicast/policy/conditional/conditional.lua#L19

Added line #L19 was not covered by tests

for i=1, #chain do
policies[i] = PolicyChain.load_policy(
chain[i].name,
chain[i].version,
chain[i].configuration
)
if config and config ~= null then
return config

Check warning on line 22 in gateway/src/apicast/policy/conditional/conditional.lua

View check run for this annotation

Codecov / codecov/patch

gateway/src/apicast/policy/conditional/conditional.lua#L21-L22

Added lines #L21 - L22 were not covered by tests
end
end

return PolicyChain.new(policies)
end
local function build_policy_chain(policies)
if not policies then return {} end

local function build_operations(config_ops)
local res = {}
local chain = PolicyChain.new()

Check warning on line 29 in gateway/src/apicast/policy/conditional/conditional.lua

View check run for this annotation

Codecov / codecov/patch

gateway/src/apicast/policy/conditional/conditional.lua#L29

Added line #L29 was not covered by tests

for _, op in ipairs(config_ops) do
insert(res, Operation.new(op.left, op.left_type, op.op, op.right, op.right_type))
for i=1, #policies do
chain:add_policy(
policies[i].name,
policies[i].version,
policy_configuration(policies[i])

Check warning on line 35 in gateway/src/apicast/policy/conditional/conditional.lua

View check run for this annotation

Codecov / codecov/patch

gateway/src/apicast/policy/conditional/conditional.lua#L31-L35

Added lines #L31 - L35 were not covered by tests
)
end

return chain

Check warning on line 39 in gateway/src/apicast/policy/conditional/conditional.lua

View check run for this annotation

Codecov / codecov/patch

gateway/src/apicast/policy/conditional/conditional.lua#L39

Added line #L39 was not covered by tests
end

return res
end
local function build_operations(config_ops)
local res = {}

function _M.new(config)
local self = new(config)
for _, op in ipairs(config_ops) do
insert(res, Operation.new(op.left, op.left_type, op.op, op.right, op.right_type))
end

return res
end

config.condition = config.condition or {}
self.condition = Condition.new(
build_operations(config.condition.operations),
config.condition.combine_op
)
function _M.new(config)
local self = new(config)

self.policy_chain = build_policy_chain(config.policy_chain)
return self
config.condition = config.condition or {}
self.condition = Condition.new(
build_operations(config.condition.operations),
config.condition.combine_op
)

self.policy_chain = build_policy_chain(config.policy_chain)
return self
end
end

function _M:export()
Expand Down
117 changes: 117 additions & 0 deletions t/apicast-policy-conditional.t
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,120 @@ yay, api backend
--- error_code: 200
--- no_error_log
[error]



=== TEST 5: conditional policy combined with on-failed policy
This test shows that conditional policies can be used in conjunction with an
on-failed policy that will return a 419 when one or more policies in the chain
fail to load. In this test, we will attempt to load an example policy that
does not exist.
--- configuration
{
"services": [
{
"id": 42,
"proxy": {
"policy_chain": [
{
"name": "apicast.policy.conditional",
"configuration": {
"condition": {
"operations": [
{
"left": "{{ uri }}",
"left_type": "liquid",
"op": "==",
"right": "/",
"right_type": "plain"
}
]
},
"policy_chain": [
{
"name": "example",
"version": "1.0",
"configuration": {}
},
{
"name": "on_failed",
"version": "builtin",
"configuration": {
"error_status_code": 419
}
}
]
}
},
{
"name": "apicast.policy.echo"
}
]
}
}
]
}
--- request
GET /
--- error_code: 419
--- no_error_log
[error]



=== TEST 6: conditional policy combined with on-failed policy
With this test, the on-failed policy only triggers if the condition is met
(request path match "/get").
--- configuration
{
"services": [
{
"id": 42,
"proxy": {
"policy_chain": [
{
"name": "apicast.policy.conditional",
"configuration": {
"condition": {
"operations": [
{
"left": "{{ uri }}",
"left_type": "liquid",
"op": "==",
"right": "/get",
"right_type": "plain"
}
]
},
"policy_chain": [
{
"name": "example",
"version": "1.0",
"configuration": {}
},
{
"name": "on_failed",
"version": "builtin",
"configuration": {
"error_status_code": 419
}
}
]
}
},
{
"name": "apicast.policy.echo"
}
]
}
}
]
}
--- pipelined_requests eval
["GET /","GET /get"]
--- response_body eval
["GET / HTTP/1.1\n",""]
--- error_code eval
[200, 419]
--- no_error_log
[error]