From af4e8a02d11d3685baf681cb48370d6aa8ea8e11 Mon Sep 17 00:00:00 2001 From: Michal Cichra Date: Tue, 6 Feb 2018 16:50:41 +0100 Subject: [PATCH 1/3] [policy] imlement safe way to load policies --- gateway/src/apicast/policy_loader.lua | 11 +++++++++++ spec/policy_loader_spec.lua | 15 +++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/gateway/src/apicast/policy_loader.lua b/gateway/src/apicast/policy_loader.lua index 24dd04415..a5247ecfa 100644 --- a/gateway/src/apicast/policy_loader.lua +++ b/gateway/src/apicast/policy_loader.lua @@ -13,6 +13,7 @@ local getenv = os.getenv local insert = table.insert local setmetatable = setmetatable local concat = table.concat +local pcall = pcall local _G = _G local _M = {} @@ -277,4 +278,14 @@ function _M:call(name, version, dir) return loader('policy', true) end +function _M:pcall(name, version, dir) + local ok, ret = pcall(self.call, self, name, version, dir) + + if ok then + return ret + else + return nil, ret + end +end + return setmetatable(_M, { __call = _M.call }) diff --git a/spec/policy_loader_spec.lua b/spec/policy_loader_spec.lua index 08e6dcb4b..46ee49211 100644 --- a/spec/policy_loader_spec.lua +++ b/spec/policy_loader_spec.lua @@ -41,4 +41,19 @@ describe('APIcast Policy Loader', function() assert.are.same({ '2.0 dependency' }, test2.dependency) end) end) + + describe(':pcall', function() + it('returns the existing module', function() + assert(_M:call('apicast')) + end) + + it('returns nil and error for invalid module', function() + local ok, err = _M:pcall('invalid', '0.1') + + assert.is_nil(ok) + assert.is_string(err) + assert.match([[module 'init' not found: +%s+no file '%g+/gateway/policies/invalid/0.1/init.lua']], err) + end) + end) end) From 342d634245d29f5d4694a765c0bf1874079b1105 Mon Sep 17 00:00:00 2001 From: Michal Cichra Date: Tue, 6 Feb 2018 15:05:36 +0100 Subject: [PATCH 2/3] [policy] rename policy.lua to init.lua * and let policy to decide own file name for the implementation --- CHANGELOG.md | 1 + examples/custom-module/README.md | 2 +- gateway/src/apicast/loader.lua | 25 ++++++++----------- .../apicast/{policy.lua => apicast.lua} | 0 gateway/src/apicast/policy/apicast/init.lua | 1 + .../caching/{policy.lua => caching.lua} | 0 gateway/src/apicast/policy/caching/init.lua | 1 + .../policy/cors/{policy.lua => cors.lua} | 0 gateway/src/apicast/policy/cors/init.lua | 1 + .../policy/echo/{policy.lua => echo.lua} | 0 gateway/src/apicast/policy/echo/init.lua | 1 + .../{policy.lua => find_service.lua} | 0 .../src/apicast/policy/find_service/init.lua | 1 + .../headers/{policy.lua => headers.lua} | 0 gateway/src/apicast/policy/headers/init.lua | 1 + .../policy/load_configuration/init.lua | 1 + .../{policy.lua => load_configuration.lua} | 0 .../src/apicast/policy/local_chain/init.lua | 1 + .../{policy.lua => local_chain.lua} | 0 .../src/apicast/policy/phase_logger/init.lua | 1 + .../{policy.lua => phase_logger.lua} | 0 gateway/src/apicast/policy/upstream/init.lua | 1 + .../upstream/{policy.lua => upstream.lua} | 0 .../src/apicast/policy/url_rewriting/init.lua | 1 + .../{policy.lua => url_rewriting.lua} | 0 gateway/src/apicast/policy_loader.lua | 2 +- spec/fixtures/policies/test/1.0.0-0/init.lua | 1 + .../test/1.0.0-0/{policy.lua => test.lua} | 0 .../test/2.0.0-0/{policy.lua => init.lua} | 0 spec/policy_loader_spec.lua | 4 +-- 30 files changed, 27 insertions(+), 19 deletions(-) rename gateway/src/apicast/policy/apicast/{policy.lua => apicast.lua} (100%) create mode 100644 gateway/src/apicast/policy/apicast/init.lua rename gateway/src/apicast/policy/caching/{policy.lua => caching.lua} (100%) create mode 100644 gateway/src/apicast/policy/caching/init.lua rename gateway/src/apicast/policy/cors/{policy.lua => cors.lua} (100%) create mode 100644 gateway/src/apicast/policy/cors/init.lua rename gateway/src/apicast/policy/echo/{policy.lua => echo.lua} (100%) create mode 100644 gateway/src/apicast/policy/echo/init.lua rename gateway/src/apicast/policy/find_service/{policy.lua => find_service.lua} (100%) create mode 100644 gateway/src/apicast/policy/find_service/init.lua rename gateway/src/apicast/policy/headers/{policy.lua => headers.lua} (100%) create mode 100644 gateway/src/apicast/policy/headers/init.lua create mode 100644 gateway/src/apicast/policy/load_configuration/init.lua rename gateway/src/apicast/policy/load_configuration/{policy.lua => load_configuration.lua} (100%) create mode 100644 gateway/src/apicast/policy/local_chain/init.lua rename gateway/src/apicast/policy/local_chain/{policy.lua => local_chain.lua} (100%) create mode 100644 gateway/src/apicast/policy/phase_logger/init.lua rename gateway/src/apicast/policy/phase_logger/{policy.lua => phase_logger.lua} (100%) create mode 100644 gateway/src/apicast/policy/upstream/init.lua rename gateway/src/apicast/policy/upstream/{policy.lua => upstream.lua} (100%) create mode 100644 gateway/src/apicast/policy/url_rewriting/init.lua rename gateway/src/apicast/policy/url_rewriting/{policy.lua => url_rewriting.lua} (100%) create mode 100644 spec/fixtures/policies/test/1.0.0-0/init.lua rename spec/fixtures/policies/test/1.0.0-0/{policy.lua => test.lua} (100%) rename spec/fixtures/policies/test/2.0.0-0/{policy.lua => init.lua} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index df92bc931..1ef3114df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Renamed `apicast/policy/policy.lua` to `apicast/policy.lua` [PR #569](https://github.com/3scale/apicast/pull/569) - Sandbox loading policies [PR #566](https://github.com/3scale/apicast/pull/566) - Extracted `usage` and `mapping_rules_matcher` modules so they can be used from policies [PR #580](https://github.com/3scale/apicast/pull/580) +- Renamed all `apicast/policy/*/policy.lua` to `apicast/policy/*/init.lua` to match Lua naming [PR #579](https://github.com/3scale/apicast/pull/579) ## [3.2.0-alpha2] - 2017-11-30 diff --git a/examples/custom-module/README.md b/examples/custom-module/README.md index 8d867c878..863bfa9f3 100644 --- a/examples/custom-module/README.md +++ b/examples/custom-module/README.md @@ -15,7 +15,7 @@ There is another example of custom module implementation for IP blacklisting in APICAST_MODULE=$(pwd)/verbose.lua ../../bin/apicast -c $(pwd)/../configuration/local.json ``` -This starts APIcast with module `verbose.lua` instead of the default [`apicast.lua`](../../gateway/src/apicast/policy/apicast/policy.lua). Local configuration file is used, so no 3scale account is needed. +This starts APIcast with module `verbose.lua` instead of the default [`apicast.lua`](../../gateway/src/apicast/policy/apicast/apicast.lua). Local configuration file is used, so no 3scale account is needed. ## Testing diff --git a/gateway/src/apicast/loader.lua b/gateway/src/apicast/loader.lua index 6a7317a90..51ef645e0 100644 --- a/gateway/src/apicast/loader.lua +++ b/gateway/src/apicast/loader.lua @@ -8,7 +8,9 @@ -- Policies can be packaged as `some_name/policy.lua` so the directory also contains the JSON spec. local loadfile = loadfile -local find = string.find +local sub = string.sub + +local policy_loader = require 'apicast.policy_loader' local map = { ['apicast'] = 'apicast.policy.apicast' @@ -24,20 +26,13 @@ local function loader(name, path) return file, err end ---- Try to load a policy. Policies can have a `.policy` suffix. -local function policy_loader(name, path) - if not find(name, '.policy.', 1, true) then return end - - local policy = name .. '.policy' - - return loader(policy, path or package.path) -end - --- Searcher has to return the loader or an error message. -local function policy_searcher(name, path) - local found, err = policy_loader(name, path) +local function policy_searcher(name) + if sub(name, 1, 15) == 'apicast.policy.' then + local mod = policy_loader:pcall(sub(name, 16), 'builtin') - return found or err + if mod then return function () return mod end end + end end local function prefix_loader(name, path) @@ -45,7 +40,7 @@ local function prefix_loader(name, path) local found, err = loader(prefixed, path) if not found then - found = policy_loader(prefixed, path) + found = policy_searcher(prefixed) end if found then @@ -60,7 +55,7 @@ local function rename_loader(name, path) local found, err = loader(new, path) if not found then - found = policy_loader(new, path) + found = policy_searcher(new) end if found then diff --git a/gateway/src/apicast/policy/apicast/policy.lua b/gateway/src/apicast/policy/apicast/apicast.lua similarity index 100% rename from gateway/src/apicast/policy/apicast/policy.lua rename to gateway/src/apicast/policy/apicast/apicast.lua diff --git a/gateway/src/apicast/policy/apicast/init.lua b/gateway/src/apicast/policy/apicast/init.lua new file mode 100644 index 000000000..621fbbe9f --- /dev/null +++ b/gateway/src/apicast/policy/apicast/init.lua @@ -0,0 +1 @@ +return require('apicast') diff --git a/gateway/src/apicast/policy/caching/policy.lua b/gateway/src/apicast/policy/caching/caching.lua similarity index 100% rename from gateway/src/apicast/policy/caching/policy.lua rename to gateway/src/apicast/policy/caching/caching.lua diff --git a/gateway/src/apicast/policy/caching/init.lua b/gateway/src/apicast/policy/caching/init.lua new file mode 100644 index 000000000..a566c3cb2 --- /dev/null +++ b/gateway/src/apicast/policy/caching/init.lua @@ -0,0 +1 @@ +return require('caching') diff --git a/gateway/src/apicast/policy/cors/policy.lua b/gateway/src/apicast/policy/cors/cors.lua similarity index 100% rename from gateway/src/apicast/policy/cors/policy.lua rename to gateway/src/apicast/policy/cors/cors.lua diff --git a/gateway/src/apicast/policy/cors/init.lua b/gateway/src/apicast/policy/cors/init.lua new file mode 100644 index 000000000..f6004a766 --- /dev/null +++ b/gateway/src/apicast/policy/cors/init.lua @@ -0,0 +1 @@ +return require('cors') diff --git a/gateway/src/apicast/policy/echo/policy.lua b/gateway/src/apicast/policy/echo/echo.lua similarity index 100% rename from gateway/src/apicast/policy/echo/policy.lua rename to gateway/src/apicast/policy/echo/echo.lua diff --git a/gateway/src/apicast/policy/echo/init.lua b/gateway/src/apicast/policy/echo/init.lua new file mode 100644 index 000000000..752b91411 --- /dev/null +++ b/gateway/src/apicast/policy/echo/init.lua @@ -0,0 +1 @@ +return require('echo') diff --git a/gateway/src/apicast/policy/find_service/policy.lua b/gateway/src/apicast/policy/find_service/find_service.lua similarity index 100% rename from gateway/src/apicast/policy/find_service/policy.lua rename to gateway/src/apicast/policy/find_service/find_service.lua diff --git a/gateway/src/apicast/policy/find_service/init.lua b/gateway/src/apicast/policy/find_service/init.lua new file mode 100644 index 000000000..2be243c14 --- /dev/null +++ b/gateway/src/apicast/policy/find_service/init.lua @@ -0,0 +1 @@ +return require('find_service') diff --git a/gateway/src/apicast/policy/headers/policy.lua b/gateway/src/apicast/policy/headers/headers.lua similarity index 100% rename from gateway/src/apicast/policy/headers/policy.lua rename to gateway/src/apicast/policy/headers/headers.lua diff --git a/gateway/src/apicast/policy/headers/init.lua b/gateway/src/apicast/policy/headers/init.lua new file mode 100644 index 000000000..28cdeb51e --- /dev/null +++ b/gateway/src/apicast/policy/headers/init.lua @@ -0,0 +1 @@ +return require('headers') diff --git a/gateway/src/apicast/policy/load_configuration/init.lua b/gateway/src/apicast/policy/load_configuration/init.lua new file mode 100644 index 000000000..9a09d0f7f --- /dev/null +++ b/gateway/src/apicast/policy/load_configuration/init.lua @@ -0,0 +1 @@ +return require('load_configuration') diff --git a/gateway/src/apicast/policy/load_configuration/policy.lua b/gateway/src/apicast/policy/load_configuration/load_configuration.lua similarity index 100% rename from gateway/src/apicast/policy/load_configuration/policy.lua rename to gateway/src/apicast/policy/load_configuration/load_configuration.lua diff --git a/gateway/src/apicast/policy/local_chain/init.lua b/gateway/src/apicast/policy/local_chain/init.lua new file mode 100644 index 000000000..ccbf8b7e8 --- /dev/null +++ b/gateway/src/apicast/policy/local_chain/init.lua @@ -0,0 +1 @@ +return require('local_chain') diff --git a/gateway/src/apicast/policy/local_chain/policy.lua b/gateway/src/apicast/policy/local_chain/local_chain.lua similarity index 100% rename from gateway/src/apicast/policy/local_chain/policy.lua rename to gateway/src/apicast/policy/local_chain/local_chain.lua diff --git a/gateway/src/apicast/policy/phase_logger/init.lua b/gateway/src/apicast/policy/phase_logger/init.lua new file mode 100644 index 000000000..cd5c53e87 --- /dev/null +++ b/gateway/src/apicast/policy/phase_logger/init.lua @@ -0,0 +1 @@ +return require('phase_logger') diff --git a/gateway/src/apicast/policy/phase_logger/policy.lua b/gateway/src/apicast/policy/phase_logger/phase_logger.lua similarity index 100% rename from gateway/src/apicast/policy/phase_logger/policy.lua rename to gateway/src/apicast/policy/phase_logger/phase_logger.lua diff --git a/gateway/src/apicast/policy/upstream/init.lua b/gateway/src/apicast/policy/upstream/init.lua new file mode 100644 index 000000000..49c8a7c16 --- /dev/null +++ b/gateway/src/apicast/policy/upstream/init.lua @@ -0,0 +1 @@ +return require('upstream') diff --git a/gateway/src/apicast/policy/upstream/policy.lua b/gateway/src/apicast/policy/upstream/upstream.lua similarity index 100% rename from gateway/src/apicast/policy/upstream/policy.lua rename to gateway/src/apicast/policy/upstream/upstream.lua diff --git a/gateway/src/apicast/policy/url_rewriting/init.lua b/gateway/src/apicast/policy/url_rewriting/init.lua new file mode 100644 index 000000000..62a066f3a --- /dev/null +++ b/gateway/src/apicast/policy/url_rewriting/init.lua @@ -0,0 +1 @@ +return require('url_rewriting') diff --git a/gateway/src/apicast/policy/url_rewriting/policy.lua b/gateway/src/apicast/policy/url_rewriting/url_rewriting.lua similarity index 100% rename from gateway/src/apicast/policy/url_rewriting/policy.lua rename to gateway/src/apicast/policy/url_rewriting/url_rewriting.lua diff --git a/gateway/src/apicast/policy_loader.lua b/gateway/src/apicast/policy_loader.lua index a5247ecfa..b67e117f6 100644 --- a/gateway/src/apicast/policy_loader.lua +++ b/gateway/src/apicast/policy_loader.lua @@ -275,7 +275,7 @@ function _M:call(name, version, dir) -- passing the "exclusive" flag for the require so it does not fallback to native require -- it should load only policies and not other code and fail if there is no such policy - return loader('policy', true) + return loader('init', true) end function _M:pcall(name, version, dir) diff --git a/spec/fixtures/policies/test/1.0.0-0/init.lua b/spec/fixtures/policies/test/1.0.0-0/init.lua new file mode 100644 index 000000000..afb67f4c0 --- /dev/null +++ b/spec/fixtures/policies/test/1.0.0-0/init.lua @@ -0,0 +1 @@ +return require('test') diff --git a/spec/fixtures/policies/test/1.0.0-0/policy.lua b/spec/fixtures/policies/test/1.0.0-0/test.lua similarity index 100% rename from spec/fixtures/policies/test/1.0.0-0/policy.lua rename to spec/fixtures/policies/test/1.0.0-0/test.lua diff --git a/spec/fixtures/policies/test/2.0.0-0/policy.lua b/spec/fixtures/policies/test/2.0.0-0/init.lua similarity index 100% rename from spec/fixtures/policies/test/2.0.0-0/policy.lua rename to spec/fixtures/policies/test/2.0.0-0/init.lua diff --git a/spec/policy_loader_spec.lua b/spec/policy_loader_spec.lua index 46ee49211..14da172cb 100644 --- a/spec/policy_loader_spec.lua +++ b/spec/policy_loader_spec.lua @@ -17,8 +17,8 @@ describe('APIcast Policy Loader', function() local ok, ret = pcall(_M.call, _M, 'unknown', '0.1') assert.falsy(ok) - assert.match([[module 'policy' not found: -%s+no file '%g+/gateway/policies/unknown/0.1/policy.lua']], ret) + assert.match([[module 'init' not found: +%s+no file '%g+/gateway/policies/unknown/0.1/init.lua']], ret) end) it('loads two instances of the same policy', function() From 075d54afc4ac50741c3b51e7446b07ae7a57a524 Mon Sep 17 00:00:00 2001 From: Michal Cichra Date: Wed, 7 Feb 2018 14:45:35 +0100 Subject: [PATCH 3/3] [doc] describe policy directory structure --- doc/policies.md | 45 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/doc/policies.md b/doc/policies.md index c06271981..f6429f946 100644 --- a/doc/policies.md +++ b/doc/policies.md @@ -15,7 +15,7 @@ APIcast. ## 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`, +`init_worker`, `rewrite`, `access`,`content`, `balancer`, `header_filter`, `body_filter`, `post_action`, and `log`. Policies can share data between them. They do that through what we call the @@ -35,6 +35,7 @@ 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 the function policy A provides for this phase. - access: execute the function policy B provides for this phase. +- content: do nothing. Neither policy A nor B describe what to do. - 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 @@ -68,9 +69,45 @@ 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 +### Policy structure + +Policy is expected to have some structure, so APIcast can find it. Minimal policy structure consists of two files: `init.lua` and `apicast-manifest.json`. + +Custom policies are expected to be on following paths: + +* `APICAST_DIR/policies/${name}/${version}/` + +And builtin ones also on: + +* `APICAST_DIR/src/apicast/policy/${name}/` + +All files in the policy directory are namespaced, so you can vendor dependencies. Consider following structure: + +``` +APICAST_DIR/policies/my_stuff/1.0/ +APICAST_DIR/policies/my_stuff/1.0/init.lua +APICAST_DIR/policies/my_stuff/1.0/my_stuff.lua +APICAST_DIR/policies/my_stuff/1.0/vendor/dependency.lua +APICAST_DIR/policies/my_stuff/1.0/apicast-manifest.json +``` + +First file to be loaded will be `init.lua`. That is the only Lua file APIcast cares about. +For better code organization we recommend that file to have just very simple implementation like: + +```lua +return require('my_stuff') +``` + +And actually implement everything in `my_stuff.lua`. This makes the policy file easier to find by humans. + +Lets say the policy needs some 3rd party dependency. Those can be put anywhere in the policy structure and will be available. Try to imagine it as UNIX `chroot`. So for example `require('vendor/dependency')` will load `APICAST_DIR/policies/my_stuff/1.0/vendor/dependency.lua` when loading your policy. + +The policy has access to only code it provides and shared code in `APICAST_DIR/src/`. + +### Policy code + 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. @@ -158,7 +195,7 @@ policies can be configured: "proxy":{ "policy_chain":[ { - "name":"apicast.policy.cors", + "name":"cors", "version": "builtin", "configuration":{ "allow_headers":["X-Custom-Header-1","X-Custom-Header-2"], "allow_methods":["POST","GET","OPTIONS"], @@ -167,7 +204,7 @@ policies can be configured: } }, { - "name":"apicast.policy.apicast" + "name":"apicast", "version": "builtin", } ] }