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

Policy chain - first version #450

Merged
merged 25 commits into from
Nov 15, 2017
Merged

Policy chain - first version #450

merged 25 commits into from
Nov 15, 2017

Conversation

mikz
Copy link
Contributor

@mikz mikz commented Oct 10, 2017

Early prototype of how we could chain policies.

Policies can export data that is stored in read only structure and resolved in order they are defined in.
So values can be overlaid, but not overridden and only the latest one is being used.
This would allow overriding some internal structures being passed between policies.

Context object is passed to each policy phase invocation method. This context object has last layer mutable so policies can pass data.

Policy chain is itself a policy. All phase methods are compatible. So policy chain can contain policy chains.
This is used to split the chain into two: global and local. Global chain is responsible for loading configuration and local can be created based on downloaded configuration.

TODO

  • ~ figure out how installed policies can change the nginx template ~ Allow policies to edit the nginx config #477
    • the list of policies enabled globally can be defined by some global config
    • the important piece is how would the policy be able to extend the nginx template
    • consider how policies would interact with each other and blocks that could extend
    • decide if it is template manipulation (like something like https://github.com/spree/deface ) or some dsl compiled to nginx config

@octobot
Copy link

octobot commented Oct 10, 2017

1 Error
🚫 Please include a CHANGELOG entry.
You can find it at CHANGELOG.md.
1 Warning
⚠️ PR is classed as Work in Progress
1 Message
📖 Note, we hard-wrap at 80 chars and use 2 spaces after the last line.

Generated by 🚫 Danger

local _M = {}

local setmetatable = setmetatable
local policy_chain = require('policy_chain')
Copy link
Contributor

Choose a reason for hiding this comment

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

How about requiring only what is needed? require('policy_chain').PHASES

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That would not really save anything. The whole file is executed and evaluated anyway.
And it would require unrolling it back when we need second property from that module.

Copy link
Contributor

Choose a reason for hiding this comment

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

👍
policy no longer depends on policy_chain, so this no longer applies.

policy[policy_chain.PHASES[i]] = noop
end

return policy, mt
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd document what this method returns as I think returning the mt might be a bit counter-intuitive. I think we should document that it's returned so policy.new can be overriden. Any other reason?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That is the only reason so far. I could also store it in _M like _M.class or _M.mt. Not sure what is better though.

But for sure has to be documented as this is public API for new policies.

Copy link
Contributor Author

@mikz mikz Nov 3, 2017

Choose a reason for hiding this comment

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

I'll try to set the mt as metatable of the returned module. Then you could get it by getmetatable. Maybe it won't cause an infinite loop :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the end went with not returning it at all and letting the user to decide what to do with it. They should upvalue the new function to a variable and call it later.

@@ -0,0 +1,33 @@
local setmetatable = setmetatable

local _M, mt = require('policy').new()
Copy link
Contributor

Choose a reason for hiding this comment

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

We should probably call new() with a name. Otherwise, it will use the default 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.

Good point!

Copy link
Contributor

Choose a reason for hiding this comment

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

All the policies are now initialized with names 👍

@mikz mikz force-pushed the policy-chain branch 9 times, most recently from 319b2e4 to 7624cd7 Compare November 7, 2017 15:35
@@ -55,9 +55,9 @@ local function ttl()
end

function _M.global(contents)
local module = require('module')
local context = require('executor'):context()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This will not work when APICAST_MODULE is set.

@mikz mikz force-pushed the policy-chain branch 2 times, most recently from 88ab50d to 7d4d450 Compare November 7, 2017 16:55
@@ -1,7 +1,7 @@
local _M = {}

local cjson = require('cjson')
local module = require('module')
local context = require('executor'):context()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This won't work when APICAST_MODULE env is set.

local resty_env = require('resty.env')

if resty_env.get('APICAST_MODULE') then
return require('module')
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe we should do this in the apicast module itself? To keep the compatibility with the old module system. Basically allow people to replace the APIcast module. But we need to figure out how to allow them to require it but to outside return their module.
Maybe this belongs to the policy_chain.build where it defaults to the apicast module?

Copy link
Contributor

Choose a reason for hiding this comment

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

How about doing this in the local_chain module?

When building the local chain, the default is require('policy_chain').build({ 'apicast' }). I think we could check the env 'APICAST_MODULE' and do require('policy_chain').build({ 'module' }) instead if it's set.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmmmm. That sounds good 👍

@@ -66,7 +66,7 @@ describe('Proxy', function()
assert.same('function', type(proxy.post_action))
end)

it('finds service by host', function()
pending('finds service by host', function()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This needs to be moved to the find service policy spec.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done.

@@ -75,7 +75,7 @@ describe('Proxy', function()
assert.falsy(proxy:find_service('unknown'))
end)

it('does not return old configuration when new one is available', function()
pending('does not return old configuration when new one is available', function()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This needs to be moved to the find service policy spec.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this one belongs to configuration_store. In fact, the same thing it's already tested there. I think we can just delete this test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah. 👍

@davidor
Copy link
Contributor

davidor commented Nov 8, 2017

There are some commits here already included in other PRs (curl in benchmarks, DEBUG=1, etc.)

@davidor davidor self-assigned this Nov 8, 2017
This also moves one of the tests marked as 'pending' in the proxy specs to the new file.
Simple policy to be used in the tests. It will allow us to verify that
the code define in the module for each phase is executed.
…y the apicast policy

The policy_chain module initializes a chain with the apicast policy if
none are specified, but I think that making the initialization explicit
here improves readability.
As the documentation of the method says.
The module is designed so its users call phases() instead.
freeze() does not need to be exposed. We can remove it and perform the
operation inline as it's just setting one attribute to true.
@davidor
Copy link
Contributor

davidor commented Nov 14, 2017

@mikz I addressed all the comments you and me wrote. I think this is ready.

@davidor davidor changed the title [WIP] policy chain prototype Policy chain - first version Nov 14, 2017
Copy link
Contributor Author

@mikz mikz left a comment

Choose a reason for hiding this comment

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

I think this looks great! 👍 We can verify the APICAST_MODULE support later. I suspect there will be issues because the old modules don't export context. But we will solve that when the time comes.

@davidor davidor merged commit 72fac3f into master Nov 15, 2017
@davidor davidor deleted the policy-chain branch November 15, 2017 09:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants