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

feat(wasm): combine config and json_config fields #11697

Merged
merged 1 commit into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
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
3 changes: 3 additions & 0 deletions changelog/unreleased/kong/wasm-filter-json-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
message: Support JSON in Wasm filter configuration
type: feature
scope: Core
18 changes: 7 additions & 11 deletions kong/db/schema/entities/filter_chains.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,33 @@ local constants = require "kong.constants"
---
---@field name string
---@field enabled boolean
---@field config string|nil
---@field json_config any|nil
---@field config any|nil


local filter = {
type = "record",
fields = {
{ name = { type = "string", required = true, one_of = wasm.filter_names,
err = "no such filter", }, },
{ config = { type = "string", required = false, }, },
{ enabled = { type = "boolean", default = true, required = true, }, },

{ json_config = {
{ config = {
type = "json",
required = false,
json_schema = {
parent_subschema_key = "name",
namespace = constants.SCHEMA_NAMESPACES.PROXY_WASM_FILTERS,
optional = true,
default = {
-- filters with no user-defined JSON schema may accept an optional
-- config, but only as a string
type = { "string", "null" },
},
},
},
},

},
entity_checks = {
{ mutually_exclusive = {
"config",
"json_config",
},
},
},
}

return {
Expand Down
2 changes: 1 addition & 1 deletion kong/db/schema/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1135,7 +1135,7 @@ validate_fields = function(self, input)

if json_subschema_key then
local schema_name = json_schema.namespace .. "/" .. json_subschema_key
inline_schema = json.get_schema(schema_name)
inline_schema = json.get_schema(schema_name) or json_schema.default

if inline_schema then
_, errors[k] = json_validate(v, inline_schema)
Expand Down
25 changes: 25 additions & 0 deletions kong/db/schema/metaschema.lua
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,38 @@ local validators = {
-- means that input validation will fail. When `optional` is `true`, the input
-- is always accepted.
--
-- Schemas which use this dynamic reference format can also optionally supply
-- a default inline schema, which will be evaluated when the dynamic schema
-- does not exist:
--
-- ```lua
-- local record = {
-- type = "record",
-- fields = {
-- { name = { type = "string" } },
-- { config = {
-- type = "json",
-- json_schema = {
-- namespace = "my-record-type",
-- parent_subschema_key = "name",
-- default = {
-- { type = { "string", "null" } },
-- },
-- },
-- },
-- },
-- },
-- }
-- ```
--
local json_metaschema = {
type = "record",
fields = {
{ namespace = { type = "string", one_of = values(constants.SCHEMA_NAMESPACES), }, },
{ parent_subschema_key = { type = "string" }, },
{ optional = { type = "boolean", }, },
{ inline = { type = "any", custom_validator = json_lib.validate_schema, }, },
{ default = { type = "any", custom_validator = json_lib.validate_schema, }, },
},
entity_checks = {
{ at_least_one_of = { "inline", "namespace", "parent_subschema_key" }, },
Expand Down
16 changes: 12 additions & 4 deletions kong/runloop/wasm.lua
Original file line number Diff line number Diff line change
Expand Up @@ -398,10 +398,18 @@ local function rebuild_state(db, version, old_state)

for _, filter in ipairs(chain.filters) do
if filter.enabled then
-- serialize all JSON configurations up front
if not filter.config and filter.json_config ~= nil then
filter.config = cjson_encode(filter.json_config)
filter.json_config = nil
-- Serialize all JSON configurations up front
--
-- NOTE: there is a subtle difference between a raw, non-JSON filter
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Reviewers: we've judged filters that require a JSON-encoded string to be an uncommon use case, compared to, say, an object or an array, which will still work perfectly fine.

-- configuration which requires no encoding (e.g. `my config bytes`)
-- and a JSON filter configuration of type=string, which should be
-- JSON-encoded (e.g. `"my config string"`).
--
-- Properly disambiguating between the two cases requires an
-- inspection of the filter metadata, which is not guaranteed to be
-- present on data-plane/proxy nodes.
if filter.config ~= nil and type(filter.config) ~= "string" then
filter.config = cjson_encode(filter.config)
end
end
end
Expand Down
11 changes: 5 additions & 6 deletions spec/02-integration/20-wasm/01-admin-api_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ local utils = require "kong.tools.utils"
local fmt = string.format

local FILTER_PATH = assert(helpers.test_conf.wasm_filters_path)
local NULL = ngx.null

local function json(body)
return {
Expand Down Expand Up @@ -207,9 +206,9 @@ describe("wasm admin API [#" .. strategy .. "]", function()
assert.same({ "foo", "bar" }, patched.tags)
assert.is_false(patched.enabled)
assert.equals(2, #patched.filters)
assert.same({ name = "tests", config = "123", enabled = true, json_config = NULL },
assert.same({ name = "tests", config = "123", enabled = true },
patched.filters[1])
assert.same({ name = "tests", config = "456", enabled = false, json_config = NULL },
assert.same({ name = "tests", config = "456", enabled = false },
patched.filters[2])
end)
end)
Expand Down Expand Up @@ -366,9 +365,9 @@ describe("wasm admin API [#" .. strategy .. "]", function()
assert.same({ "foo", "bar" }, patched.tags)
assert.is_false(patched.enabled)
assert.equals(2, #patched.filters)
assert.same({ name = "tests", config = "123", enabled = true, json_config = NULL },
assert.same({ name = "tests", config = "123", enabled = true },
patched.filters[1])
assert.same({ name = "tests", config = "456", enabled = false, json_config = NULL },
assert.same({ name = "tests", config = "456", enabled = false },
patched.filters[2])
end)
end)
Expand Down Expand Up @@ -428,7 +427,7 @@ describe("wasm admin API [#" .. strategy .. "]", function()
fcs = {
assert(bp.filter_chains:insert({
filters = {
{ name = "tests", config = ngx.null, enabled = true },
flrgh marked this conversation as resolved.
Show resolved Hide resolved
{ name = "tests", config = nil, enabled = true },
{ name = "response_transformer", config = "{}", enabled = false },
},
service = { id = service.id },
Expand Down
56 changes: 42 additions & 14 deletions spec/02-integration/20-wasm/02-db_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,13 @@ describe("wasm DB entities [#" .. strategy .. "]", function()
end)

describe(".config", function()
it("is an optional string", function()
local schema_name = "proxy-wasm-filters/test"

lazy_teardown(function()
schema_lib.remove_schema(schema_name)
end)

it("is an optional string when no json schema exists", function()
local service = assert(db.services:insert({
url = "http://example.test",
}))
Expand All @@ -381,7 +387,7 @@ describe("wasm DB entities [#" .. strategy .. "]", function()
filters = {
{
name = "test",
config = "foo",
config = nil,
}
}
}))
Expand All @@ -395,26 +401,48 @@ describe("wasm DB entities [#" .. strategy .. "]", function()
filters = {
{
name = "test",
config = nil,
config = "my config",
}
}
}))
end)
end)

describe(".json_config", function()
local schema_name = "proxy-wasm-filters/test"
assert.falsy(dao:insert({
service = { id = service.id },
filters = {
{
name = "test",
config = 123,
}
}
}))

lazy_teardown(function()
schema_lib.remove_schema(schema_name)
assert.falsy(dao:insert({
service = { id = service.id },
filters = {
{
name = "test",
config = true,
}
}
}))

assert.falsy(dao:insert({
service = { id = service.id },
filters = {
{
name = "test",
config = { a = 1, b = 2 },
}
}
}))
end)

it("is validated against user schema", function()
local service = assert(db.services:insert({
url = "http://example.test",
}))

schema_lib.add_schema("proxy-wasm-filters/test", {
schema_lib.add_schema(schema_name, {
type = "object",
properties = {
foo = { type = "string" },
Expand All @@ -429,7 +457,7 @@ describe("wasm DB entities [#" .. strategy .. "]", function()
filters = {
{
name = "test",
json_config = {
config = {
foo = "foo string",
bar = { a = 1, b = 2 },
},
Expand All @@ -446,7 +474,7 @@ describe("wasm DB entities [#" .. strategy .. "]", function()
filters = {
{
name = "test",
json_config = {
config = {
foo = 123,
bar = { a = 1, b = 2 },
},
Expand All @@ -465,7 +493,7 @@ describe("wasm DB entities [#" .. strategy .. "]", function()
filters = {
{
name = "test",
json_config = ngx.null,
config = ngx.null,
}
}
})
Expand All @@ -481,7 +509,7 @@ describe("wasm DB entities [#" .. strategy .. "]", function()
filters = {
{
name = "test",
json_config = nil,
config = nil,
}
}
})
Expand Down
38 changes: 31 additions & 7 deletions spec/02-integration/20-wasm/09-filter-meta_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ describe("filter metadata [#" .. strategy .. "]", function()
filters = {
{
name = "rt_with_validation",
json_config = {}, -- empty
config = {}, -- empty
},
},
})
Expand All @@ -185,7 +185,7 @@ describe("filter metadata [#" .. strategy .. "]", function()
assert.same({
filters = {
{
json_config = "property add is required"
config = "property add is required"
}
}
}, body.fields)
Expand All @@ -197,7 +197,7 @@ describe("filter metadata [#" .. strategy .. "]", function()
filters = {
{
name = "rt_with_validation",
json_config = {
config = {
add = {
headers = {
"x-foo:123",
Expand All @@ -217,19 +217,44 @@ describe("filter metadata [#" .. strategy .. "]", function()
end).has_no_error()
end)

it("filters without config schemas are not validated", function()
it("filters without config schemas can only accept a string", function()
local host = random_name() .. ".test"

local res = create_filter_chain(host, {
name = random_name(),
filters = {
{
name = "rt_no_validation",
json_config = {
config = {
add = {
headers = 1234,
},
},
}
},
},
})

assert.response(res).has.status(400)
local body = assert.response(res).has.jsonbody()
assert.same({
filters = {
{ config = "wrong type: expected one of string, null, got object" }
}
}, body.fields)
end)

it("filters without config schemas are not validated", function()
local host = random_name() .. ".test"
local res = create_filter_chain(host, {
name = random_name(),
filters = {
{
name = "rt_no_validation",
config = cjson.encode({
add = {
headers = 123,
},
}),
},
},
})
Expand All @@ -242,7 +267,6 @@ describe("filter metadata [#" .. strategy .. "]", function()
assert.logfile().has.line("failed parsing filter config", true, 0)
end).has_no_error()
end)

end)

end)
Expand Down