Skip to content

Commit

Permalink
Add policies document
Browse files Browse the repository at this point in the history
  • Loading branch information
davidor committed Nov 29, 2017
1 parent 2857766 commit d2f1e29
Showing 1 changed file with 180 additions and 0 deletions.
180 changes: 180 additions & 0 deletions doc/policies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# APIcast policies

The behaviour of APIcast is customizable via policies. A policy basically tells
APIcast what it should do in each of the nginx phases. This document details
how policies work and how you can write your own and integrate them into
APIcast.

- [**Policies**](#policies)
- [**Policy chains**](#policy-chains)
- [**APIcast default policies**](#apicast-default-policies)
- [**Write your own policy**](#write-your-own-policy)
- [**Integrate your policies**](#integrate-your-policies)


## Policies

A policy tells APIcast what it should do in each of the nginx phases: `init`,
`init_worker`, `rewrite`, `access`, `balancer`, `header_filter`, `body_filter`,
`post_action`, and `log`.

Policies can share data between them. They do that through what we call the
`context`. Policies can read from and modify that context in every phase.


## Policy chains

Policies can be combined using a policy chain. A policy chain is simply a
sorted list of policies.

The way policy chains work is as follows: suppose that we have a policy A that
describes what to do in the `rewrite` and `header_filter` phases and a policy B
that describes what to run in `access` and `header_filter`. Assume also that
when describing the chain, we indicate that policy A should be run before
policy B. When APIcast receives a request, it will check the policy chain
described to see what it should run on each phase:
- rewrite: execute what policy A specifies for this phase.
- access: execute what policy B specifies for this phase.
- balancer: do nothing. Neither policy A nor B describe what to do.
- header_filter: execute first what policy A specified for this phase and then
what B described. Remember that policy chains define an order. And we
specified that policy A comes before policy B.
- body_filter: do nothing. Neither policy A nor B describe what to do.
- post_action: do nothing. Neither policy A nor B describe what to do.
- log: do nothing. Neither policy A nor B describe what to do.

Notice that we did not indicate what APIcast does in the `init` and the
`init_worker` phases. The reason is that those two are not executed in every
request. `init` is executed when APIcast boots, and `init_worker` when each
of each of its workers start.

### Types

There are two types of policy chains in APIcast: per-service chains and a
global chain. Both of them can be configured.

As the name indicates, per-service chains allow us to define a specific chain
for a given service. This means that we can apply different policies to our
services, or the same policies but configured differently, or in a different
order. On the other hand, there's only one global chain, and the behavior it
defines applies to all the services.


## APIcast default policies

By default, APIcast applies the `apicast` policy to all the services. This
policy includes all the functionality offered by APIcast (mapping rules
matching, authorization and reporting against 3scale backend, etc.). In the
future, this policy will be split and each of the resulting policies will be
replaceable with custom ones.


## Write your own policy

To write your own policy you need to write a Lua module that instantiates a
[Policy](../gateway/src/apicast/policy.lua) and defines a method for each of
the phases where it needs to execute something.

Suppose that we wanted to run a policy that logged a message in the `rewrite`
and the `header_filter` phases. This is how our module would look
like:
```lua
local policy = require('apicast.policy')
local _M = policy.new('My custom policy')

function _M:rewrite()
ngx.log(ngx.INFO, 'Passing through the rewrite phase.')
end

function _M:header_filter()
ngx.log(ngx.INFO, 'Passing through the header_filter phase')
end

return _M
```

If we wanted to read or write in the `context`:
```lua
function _M:rewrite(context)
-- Read something from the context.
local smth = context.something

-- Write 'b' into the context so other policies or later phases can read it.
context.b = 'something_useful_to_be_shared'
end
```

Policies can also have a configuration:
```lua
local policy = require('apicast.policy')
local _M = policy.new('My custom policy')
local new = _M.new

function _M.new(config)
local self = new()

-- 'config' contains two params that define the behaviour of our policy and
-- we want to access them from other phases.
self.option_a = config.option_a
self.option_b = config.option_b

return self
end
```

In the [policy folder](../gateway/src/apicast/policy) you can find several
policies. These ones are quite simple and can be used as examples to write your
own:
- [Echo](../gateway/src/apicast/policy/echo.lua)
- [Phase logger](../gateway/src/apicast/policy/phase_logger.lua)
- [CORS](../gateway/src/apicast/policy/cors.lua)
- [Headers](../gateway/src/apicast/policy/headers.lua)


## Integrate your policies

At some point, it will be possible to configure policies and policy chains
through the 3scale UI, but you can also configure them in APIcast using a
configuration file. Remember that APIcast allows to specify a config file using
the `THREESCALE_CONFIG_FILE` env variable:
```shell
THREESCALE_CONFIG_FILE=my_config.json bin/apicast
```

Policies are specified in the `proxy` object of each service. A policy chain is
a JSON array where each object represents a policy with a name and an optional
configuration.

Here's an example that shows how to define a policy chain with the CORS policy
and the APIcast one. Please note that the configuration of services is more
complex than shown here. This is a very simplified version to illustrate how
policies can be configured:
```json
{
"services":[
{
"id":42,
"proxy":{
"policy_chain":[
{
"name":"apicast.policy.cors",
"configuration":{
"allow_headers":["X-Custom-Header-1","X-Custom-Header-2"],
"allow_methods":["POST","GET","OPTIONS"],
"allow_origin":"*",
"allow_credentials":false
}
},
{
"name":"apicast.policy.apicast"
}
]
}
}
]
}
```

If instead of configuring the policy chain of a specific service, you want to
customize the global policy chain, you can take a look at
[this example](../examples/policy_chain/README.md).

0 comments on commit d2f1e29

Please sign in to comment.