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

Add document that explains the new policies system #512

Merged
merged 1 commit into from
Nov 30, 2017
Merged
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
181 changes: 181 additions & 0 deletions doc/policies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# 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

Choose a reason for hiding this comment

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

maybe say "share data between phases" to be even more explicit (or people could understand sharing between policies)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Both things are possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To clarify, whatever you write in the context will be available in later phases of the same policy and in other policies as well. Do you think that's clear in the document?

`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:

Choose a reason for hiding this comment

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

what would the term/name to use here? "policy chain definition" (sustantivo)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure what you mean here @andrewdavidmackenzie

- rewrite: execute the function policy A provides for this phase.
- access: execute the function policy B provides for this phase.
- balancer: do nothing. Neither policy A nor B describe what to do.
- header_filter: execute first the function policy A provides for this phase
and then the function policy B provides for this phase. 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

Choose a reason for hiding this comment

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

Instead of describing "in the negative", explain this as part of APIcast start-up before this, then move onto the description of handling requests?

`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.

Choose a reason for hiding this comment

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

so, policies defined by both chains will be run?
How is order defined between policies in the global chain and policies in the service chain....

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, per-service are included as part of the global one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The order of policies in the global chain is configurable. The order of policies in per-service chains is also configurable.



## 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)

Choose a reason for hiding this comment

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

Maybe explain when new() is called?
How to store values from the config it receives for use in the per-request methods later?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think that documenting when it's instantiated it's too much detail for this document.
Your second question is exactly what I wanted to show with this example. See self.option_a and self.option_b below.

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

Choose a reason for hiding this comment

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

"They are quite..."

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think that's correct, because I'm not listing all of them, just some examples.

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. Notice that the configuration is received in the `new` method of
the policy as shown above.

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).