From 5f02827ed2c591f9e729bbdf65db5d22a8be6f58 Mon Sep 17 00:00:00 2001 From: Michael Martin Date: Tue, 15 Oct 2024 15:53:56 -0700 Subject: [PATCH] fix(wasm): add a startup check for missing filters This adds a check during `init` that will prevent Kong from starting if any filter chain entities are found in the database using a filter that is not installed. Example: > Error: ./kong/cmd/start.lua:99: nginx: [error] init_by_lua error: /path/to/kong/init.lua:750: [wasm]: found one or more filter chain entities with filters that are not enabled/installed: > filter chain: 9e0b56d6-0e8c-469f-bf15-142debdd5d05, filter: #1 (response_transformer) > filter chain: 9e0b56d6-0e8c-469f-bf15-142debdd5d05, filter: #3 (response_transformer) Previously, this condition would not be caught until the Wasm state is built during `init_worker`. This change brings Wasm more in line with the behavior of the plugins iterator. --- .../kong/fix-wasm-check-missing-filters.yml | 5 ++ kong/init.lua | 5 ++ kong/runloop/wasm.lua | 32 +++++++ .../20-wasm/11-missing-filters_spec.lua | 87 +++++++++++++++++++ 4 files changed, 129 insertions(+) create mode 100644 changelog/unreleased/kong/fix-wasm-check-missing-filters.yml create mode 100644 spec/02-integration/20-wasm/11-missing-filters_spec.lua diff --git a/changelog/unreleased/kong/fix-wasm-check-missing-filters.yml b/changelog/unreleased/kong/fix-wasm-check-missing-filters.yml new file mode 100644 index 000000000000..ad1cabfa20c5 --- /dev/null +++ b/changelog/unreleased/kong/fix-wasm-check-missing-filters.yml @@ -0,0 +1,5 @@ +message: | + **proxy-wasm:** Added a check that prevents Kong from starting when the + database contains invalid Wasm filters. +type: bugfix +scope: Core diff --git a/kong/init.lua b/kong/init.lua index 70abad8b59c0..f47f7792c8b6 100644 --- a/kong/init.lua +++ b/kong/init.lua @@ -745,6 +745,11 @@ function Kong.init() if not is_control_plane(config) then assert(runloop.build_router("init")) + ok, err = wasm.check_enabled_filters() + if not ok then + error("[wasm]: " .. err) + end + ok, err = runloop.set_init_versions_in_cache() if not ok then error("error setting initial versions for router and plugins iterator in cache: " .. diff --git a/kong/runloop/wasm.lua b/kong/runloop/wasm.lua index 5833660c6297..351499ad4f7e 100644 --- a/kong/runloop/wasm.lua +++ b/kong/runloop/wasm.lua @@ -1000,4 +1000,36 @@ function _M.status() return true end +function _M.check_enabled_filters() + if not ENABLED then + return true + end + + local enabled_filters = _M.filters_by_name + + local errs + for chain, err in kong.db.filter_chains:each() do + if err then + return nil, err + end + + for i, filter in ipairs(chain.filters) do + if not enabled_filters[filter.name] then + errs = errs or {} + + insert(errs, fmt("filter chain: %s, filter: #%s (%s)", + chain.id, i, filter.name)) + end + end + end + + if errs then + return nil, "found one or more filter chain entities with filters that are " + .. "not enabled/installed:\n" .. table.concat(errs, "\n") + end + + + return true +end + return _M diff --git a/spec/02-integration/20-wasm/11-missing-filters_spec.lua b/spec/02-integration/20-wasm/11-missing-filters_spec.lua new file mode 100644 index 000000000000..8273d603079e --- /dev/null +++ b/spec/02-integration/20-wasm/11-missing-filters_spec.lua @@ -0,0 +1,87 @@ +local helpers = require "spec.helpers" + +local FILTER_PATH = assert(helpers.test_conf.wasm_filters_path) + +-- no cassandra support +for _, strategy in helpers.each_strategy({ "postgres" }) do + +describe("missing filters in the config [#" .. strategy .. "]", function() + local bp + local service, route + + lazy_setup(function() + require("kong.runloop.wasm").enable({ + { name = "tests", + path = FILTER_PATH .. "/tests.wasm", + }, + { name = "response_transformer", + path = FILTER_PATH .. "/response_transformer.wasm", + }, + }) + + bp = helpers.get_db_utils(strategy, { + "routes", + "services", + "filter_chains", + }) + + service = assert(bp.services:insert { + name = "wasm-test", + }) + + route = assert(bp.routes:insert { + service = service, + paths = { "/" }, + }) + + assert(bp.filter_chains:insert { + name = "test", + route = route, + filters = { + { + name = "response_transformer", + config = require("cjson").encode { + append = { + headers = { + "x-wasm-test:my-value", + }, + }, + } + }, + { + name = "tests", + config = nil, + }, + { + name = "response_transformer", + config = require("cjson").encode { + append = { + headers = { + "x-wasm-test:my-value", + }, + }, + } + } + } + }) + end) + + lazy_teardown(function() + helpers.clean_prefix() + end) + + it("causes Kong to fail to start", function() + local ok, err = helpers.start_kong({ + database = strategy, + nginx_conf = "spec/fixtures/custom_nginx.template", + wasm_filters = "tests", + wasm = true, + }) + + assert.falsy(ok, "expected `kong start` to fail") + assert.string(err) + assert.matches("response_transformer", err) + end) +end) + +end -- each strategy