-
Notifications
You must be signed in to change notification settings - Fork 170
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
Liquid context debugging policy #849
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
822345d
policy/ngx_variable: return context as a LinkedList
davidor 278d735
policy: add schema for new liquid_context_debug policy
davidor 315168f
policy/liquid_context_debug: add ContextContent
davidor 43d6987
spec/policy/liquid_context_debug: add specs for ContextContent
davidor 5458c8a
policy: add LiquidContextDebug policy
davidor a508a85
spec/policy: add tests for LiquidContextDebug policy
davidor 07699ed
t: add tests for LiquidContextDebug policy
davidor b8965b9
CHANGELOG: add liquid context debugging policy
davidor File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
gateway/src/apicast/policy/liquid_context_debug/apicast-policy.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"$schema": "http://apicast.io/policy-v1/schema#manifest#", | ||
"name": "Liquid context debug", | ||
"summary": "Inspects the available liquid context.", | ||
"description": [ | ||
"This is a policy meant only for debugging purposes. This policy ", | ||
"returns the context available when evaluating liquid. Any policy can ", | ||
"modify the context that is shared between policies and that context is ", | ||
"available when evaluating liquid. However, documenting what's available ", | ||
"is not possible because policies can add any arbitrary field. Users who ", | ||
"want to develop a policy can use this one to know the context available ", | ||
"in their configuration." | ||
], | ||
"version": "builtin", | ||
"configuration": { | ||
"type": "object", | ||
"properties": { | ||
} | ||
} | ||
} |
97 changes: 97 additions & 0 deletions
97
gateway/src/apicast/policy/liquid_context_debug/context_content.lua
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
local type = type | ||
local pairs = pairs | ||
local tostring = tostring | ||
|
||
local _M = {} | ||
|
||
-- The context usually contains a lot of information. For example, it includes | ||
-- the whole service configuration. Also, some of the values are objects that | ||
-- we can't really use when evaluating liquid, like functions. | ||
-- That's why we define only a few types of keys and values to return. | ||
local accepted_types_for_keys = { | ||
string = true, | ||
number = true, | ||
} | ||
|
||
local accepted_types_for_values = { | ||
string = true, | ||
number = true, | ||
table = true | ||
} | ||
|
||
-- In the context, there might be large integers as keys. This can be a problem | ||
-- when converting to JSON. Imagine an entry of the table like this: | ||
-- { [10000] = 'something' }. To convert that to JSON, we need an array of 1000 | ||
-- positions with only one occupied. With arrays like these, 'cjson' raises an | ||
-- "Excessively sparse arrays" error: | ||
-- https://github.com/efelix/lua-cjson/blob/4f27182acabc435fcc220fdb710ddfaf4e648c86/README#L140 | ||
-- For example, there is a lrucache instance with a table that maps service ids | ||
-- (numbers) to hosts. So we can have something like { [123456] = "127.0.0.1" }. | ||
-- This can be limited using a global setting of the 'cjson' library. However, | ||
-- we're going to implement an add-hoc solution here so we don't affect the | ||
-- other modules. We're going to convert those large ints to strings as a | ||
-- workaround. | ||
local max_integer_key = 1000 | ||
|
||
local function key(k) | ||
if type(k) ~= 'number' then return k end | ||
|
||
if k <= max_integer_key then | ||
return k | ||
else | ||
return tostring(k) | ||
end | ||
end | ||
|
||
local value_from | ||
|
||
local ident = function(...) return ... end | ||
local value_from_fun = { | ||
string = ident, number = ident, | ||
table = function(table) | ||
local res = {} | ||
for k, v in pairs(table) do | ||
local wanted_types = accepted_types_for_keys[type(k)] and | ||
accepted_types_for_values[type(v)] | ||
|
||
if wanted_types then | ||
res[key(k)] = value_from(v) | ||
end | ||
end | ||
|
||
return res | ||
end | ||
} | ||
|
||
value_from = function(object) | ||
local fun = value_from_fun[type(object)] | ||
if fun then return fun(object) end | ||
end | ||
|
||
local function add_content(object, acc) | ||
if type(object) ~= 'table' then return nil end | ||
|
||
-- The context is a list where each element has a "current" and a "next". | ||
local current = object.current | ||
local next = object.next | ||
|
||
local values_of_current = value_from(current or object) | ||
|
||
for k, v in pairs(values_of_current) do | ||
if acc[key(k)] == nil then -- to return only the first occurrence | ||
acc[key(k)] = v | ||
end | ||
end | ||
|
||
if next then | ||
add_content(next, acc) | ||
end | ||
end | ||
|
||
function _M.from(context) | ||
local res = {} | ||
add_content(context, res) | ||
return res | ||
end | ||
|
||
return _M |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
return require('liquid_context_debug') |
21 changes: 21 additions & 0 deletions
21
gateway/src/apicast/policy/liquid_context_debug/liquid_context_debug.lua
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
local context_content = require('context_content') | ||
local cjson = require('cjson') | ||
local policy = require('apicast.policy') | ||
local ngx_variable = require('apicast.policy.ngx_variable') | ||
local _M = policy.new('Liquid context debug') | ||
|
||
local new = _M.new | ||
|
||
function _M.new(config) | ||
local self = new(config) | ||
return self | ||
end | ||
|
||
function _M.content(_, context) | ||
local liquid_context = ngx_variable.available_context(context) | ||
local content = context_content.from(liquid_context) | ||
|
||
ngx.say(cjson.encode(content)) | ||
end | ||
|
||
return _M |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
local LinkedList = require 'apicast.linked_list' | ||
local context_content = require 'apicast.policy.liquid_context_debug.context_content' | ||
|
||
describe('Context content', function() | ||
describe('.from', function() | ||
it('returns the content from a context with one node', function() | ||
local t = { a = 1, b = 2 } | ||
local context = LinkedList.readonly(t) | ||
|
||
local content = context_content.from(context) | ||
|
||
assert.contains(t, content) | ||
end) | ||
|
||
it('returns the content from a context with several nodes', function() | ||
local context = LinkedList.readonly( | ||
{ a = 1 }, | ||
LinkedList.readonly( | ||
{ b = 2, c = 3 }, | ||
LinkedList.readonly( | ||
{ d = 4 }, | ||
{ e = 5, f = 6 } | ||
) | ||
) | ||
) | ||
|
||
local content = context_content.from(context) | ||
|
||
local expected = { a = 1, b = 2, c = 3, d = 4, e = 5, f = 6 } | ||
assert.contains(expected, content) | ||
end) | ||
|
||
it('returns only the first value of a repeated element', function() | ||
local context = LinkedList.readonly( | ||
{ a = 1 }, | ||
LinkedList.readonly( | ||
{ a = 2 }, | ||
LinkedList.readonly( | ||
{ a = 3 }, | ||
{ a = 4 } | ||
) | ||
) | ||
) | ||
|
||
local content = context_content.from(context) | ||
|
||
assert.equals(1, content.a) | ||
end) | ||
|
||
it('ignores keys that are not strings or numbers', function() | ||
local context = LinkedList.readonly( | ||
{ a = 1, [function() end] = 2, [{}] = 3 } | ||
) | ||
|
||
local content = context_content.from(context) | ||
|
||
assert.contains({ a = 1 }, content) | ||
end) | ||
|
||
it('ignores values that are not strings, numbers or tables', function() | ||
local context = LinkedList.readonly( | ||
{ a = 1, b = {}, c = function() end } | ||
) | ||
|
||
local content = context_content.from(context) | ||
|
||
assert.contains({ a = 1, b = {} }, content) | ||
end) | ||
|
||
it('returns empty if the context is empty', function() | ||
assert.same({}, context_content.from({})) | ||
end) | ||
end) | ||
end) |
32 changes: 32 additions & 0 deletions
32
spec/policy/liquid_context_debug/liquid_context_debug_spec.lua
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
local ngx_variable = require('apicast.policy.ngx_variable') | ||
local LinkedList = require('apicast.linked_list') | ||
local context_content = require('apicast.policy.liquid_context_debug.context_content') | ||
local cjson = require('cjson') | ||
|
||
local LiquidContextDebug = require 'apicast.policy.liquid_context_debug' | ||
|
||
describe('Liquid context debug policy', function() | ||
describe('.content', function() | ||
before_each(function() | ||
stub(ngx, 'say') | ||
end) | ||
|
||
it('calls ngx.say with the "ngx_variable" available context in JSON', function() | ||
-- Return something simple with just headers instead of mocking all the | ||
-- ngx.var.* needed. | ||
stub(ngx_variable, 'available_context', function(policies_context) | ||
return LinkedList.readonly({ headers = { some_header = 'some_val' } }, | ||
policies_context) | ||
end) | ||
local policies_context = { a = 1 } | ||
|
||
LiquidContextDebug.new():content(policies_context) | ||
|
||
local expected_content = context_content.from( | ||
ngx_variable.available_context(policies_context) | ||
) | ||
local expected_json = cjson.encode(expected_content) | ||
assert.stub(ngx.say).was_called_with(expected_json) | ||
end) | ||
end) | ||
end) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
use lib 't'; | ||
use Test::APIcast::Blackbox 'no_plan'; | ||
|
||
run_tests(); | ||
|
||
__DATA__ | ||
|
||
=== TEST 1: liquid_context_debug policy does not crash | ||
If there's a problem while parsing the context or converting it to JSON, this | ||
will crash. | ||
--- configuration | ||
{ | ||
"services": [ | ||
{ | ||
"id": 42, | ||
"proxy": { | ||
"policy_chain": [ | ||
{ | ||
"name": "liquid_context_debug", | ||
"configuration": {} | ||
} | ||
], | ||
"proxy_rules": [ | ||
] | ||
} | ||
} | ||
] | ||
} | ||
--- request | ||
GET / | ||
--- error_code: 200 | ||
--- no_error_log | ||
[error] |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In theory we could add
__pairs
metamethod to the linked list that understands this.http://lua-users.org/wiki/GeneralizedPairsAndIpairs
Then you could just iterate through the context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good idea, but I'd rather leave it for a future PR as it involves adding the functionality in the
LinkedList
module, test it, and check whether we can use it in some of the existing callers.