Skip to content

Commit

Permalink
fix(plugins): load wasm filter plugins before external plugins
Browse files Browse the repository at this point in the history
In 4059a31 (#13843) we added a
plugin-like interface for Wasm filters.

We now have 3 sources for plugins: Lua, External, and Wasm Filters. When a plugin is enabled or configured, the plugin system follows a
resolution order for looking up the plugin handler and schema:

1. Lua => `require kong.plugins.<name>.{handler,schema}`
2. External => `kong.runloop.plugin_servers.load_{handler,schema}(<name>)`
3. Wasm Filters => `kong.runloop.wasm.plugins.load_{handler,schema}(<name>)`

When a user configures Kong with a "bad" entry in `pluginserver_names`
(read: a plugin server that is not actually installed), step #2 of the
plugin resolution process throws an exception, because the external
plugin subsystem attempts to query a plugin server that does not exist.
Importantly, *this exception is not encountered when the user has only
configured/enabled Lua plugins,* because we never reach beyond step #1
of the plugin resolution process.

A side effect of adding the Wasm filter plugin interface is that
discovered Wasm filters are added to the global plugins table
(`kong.configuration.loaded_plugins`) when Wasm is enabled. This means
that, if Wasm is enabled, and any Wasm filters are installed, we
_always_ step through step #2 of the plugin resolution process,
triggering an exception if the user has any badly-configured plugin
server.

A future change will likely render this scenario unreachable by
performing deeper validation of `pluginserver_names` at startup. For
now, a simple fix is just to change the resolution order such that Wasm
filters are loaded _before_ we query the external plugin subsystem:

1. Lua
2. Wasm Filters
3. External
  • Loading branch information
flrgh authored and gszr committed Nov 27, 2024
1 parent 8ef295c commit b1cf758
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 6 deletions.
8 changes: 4 additions & 4 deletions kong/db/dao/plugins.lua
Original file line number Diff line number Diff line change
Expand Up @@ -162,16 +162,16 @@ local load_plugin_handler do
local plugin_handler = "kong.plugins." .. plugin .. ".handler"
local ok, handler = load_module_if_exists(plugin_handler)
if not ok then
ok, handler = plugin_servers.load_plugin(plugin)
ok, handler = wasm_plugins.load_plugin(plugin)
if type(handler) == "table" then
handler._go = true
handler._wasm = true
end
end

if not ok then
ok, handler = wasm_plugins.load_plugin(plugin)
ok, handler = plugin_servers.load_plugin(plugin)
if type(handler) == "table" then
handler._wasm = true
handler._go = true
end
end

Expand Down
4 changes: 2 additions & 2 deletions kong/db/schema/plugin_loader.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ function plugin_loader.load_subschema(parent_schema, plugin, errors)
local plugin_schema = "kong.plugins." .. plugin .. ".schema"
local ok, schema = load_module_if_exists(plugin_schema)
if not ok then
ok, schema = plugin_servers.load_schema(plugin)
ok, schema = wasm_plugins.load_schema(plugin)
end
if not ok then
ok, schema = wasm_plugins.load_schema(plugin)
ok, schema = plugin_servers.load_schema(plugin)
end

if not ok then
Expand Down
96 changes: 96 additions & 0 deletions spec/02-integration/10-external-plugins/03-wasm_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
local helpers = require "spec.helpers"

for _, strategy in helpers.each_strategy() do

describe("external plugins and wasm #" .. strategy, function()
describe("wasm enabled in conjunction with unused pluginservers", function()
it("does not prevent kong from starting", function()
require("kong.runloop.wasm").enable({
{ name = "tests",
path = helpers.test_conf.wasm_filters_path .. "/tests.wasm",
},
})

local bp = assert(helpers.get_db_utils(strategy, {
"services",
"routes",
"plugins",
"filter_chains",
}, { "response-transformer", "tests" }))

local route = assert(bp.routes:insert({
protocols = { "http" },
paths = { "/" },
service = assert(bp.services:insert({})),
}))

-- configure a wasm filter plugin
assert(bp.plugins:insert({
name = "tests",
route = route,
config = "",
}))

-- configure a lua plugin
assert(bp.plugins:insert({
name = "response-transformer",
route = route,
config = {
add = {
headers = {
"X-Lua-Plugin:hello from response-transformer",
},
},
},
}))

assert(helpers.start_kong({
nginx_conf = "spec/fixtures/custom_nginx.template",
database = strategy,

wasm = true,
wasm_filters = "tests",

plugins = "response-transformer",

-- this pluginserver does not exist, but we will validate that kong can
-- start so long as there are no configured/enabled plugins that will
-- require us to invoke it in any way
--
-- XXX: this configuration could be considered invalid, and future changes
-- to plugin resolution/pluginserver code MAY opt to change this behavior
pluginserver_names = "not-exists",
pluginserver_not_exists_start_cmd = "/i/do/not/exist",
pluginserver_not_exists_query_cmd = "/i/do/not/exist",
}))

local client

finally(function()
if client then
client:close()
end

helpers.stop_kong()
end)

client = helpers.proxy_client()
local res = client:get("/", {
headers = {
["X-PW-Test"] = "local_response",
["X-PW-Input"] = "hello from wasm",
},
})

-- verify that our wasm filter ran
local body = assert.res_status(200, res)
assert.equals("hello from wasm", body)

-- verify that our lua plugin (response-transformer) ran
local header = assert.response(res).has.header("X-Lua-Plugin")
assert.equals("hello from response-transformer", header)
end)
end)
end)

end -- each strategy

1 comment on commit b1cf758

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

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

Bazel Build

Docker image available kong/kong:b1cf758707a935fa6b1a85fa8209ca9900a0da5b
Artifacts available https://github.com/Kong/kong/actions/runs/12041246327

Please sign in to comment.