Add a custom plugin configuration
Add configuration fields in the plugins’s schema.lua file, and define the features using the configuration fields in handler.lua.
Prerequisites
Series Prerequisites
This page is part of the Get started with custom plugin development series.
Complete the previous page, Add custom plugin testing before completing this page.
Add configuration fields to the schema
Let’s add some configuration fields to our schema.lua file.
- Include the Kong Gateway
typedefsmodule at the top of theschema.luafile:local typedefs = require "kong.db.schema.typedefs"Copied! - Add the following
header_nametype definition within thefieldsarray we defined earlier:{ response_header_name = typedefs.header_name { required = false, default = "X-MyPlugin" } },Copied!This type definition defines the field to be a string that cannot be null and conforms to the rules for header names. It also indicates that the configuration value is not required, which means it’s optional for the user when configuring the plugin. We also specify a default value that will be used when a user does not specify a value.
The full schema.lua now looks like this:
local typedefs = require "kong.db.schema.typedefs"
local PLUGIN_NAME = "my-plugin"
local schema = {
name = PLUGIN_NAME,
fields = {
{ config = {
type = "record",
fields = {
{ response_header_name = typedefs.header_name {
required = false,
default = "X-MyPlugin" } },
},
},
},
},
}
return schema
Read configuration values from plugin code
Modify the response function in the handler.lua file to read the configuration value from the incoming conf parameter instead of the current hardcoded value:
function MyPluginHandler:response(conf)
kong.response.set_header(conf.response_header_name, "response")
end
Manually validate the configuration
Let’s use Pongo to test the updated configuration.
- Launch Kong Gateway and open a shell:
pongo shellCopied! - Run the database migrations and start Kong Gateway:
kmsCopied! -
Add a test Gateway Service:
curl -X POST "http://localhost:8001/services" \ --no-progress-meter --fail-with-body \ --json '{ "name": "example_service", "url": "https://httpbin.konghq.com" }'Copied! -
Enable the plugin, this time with the configuration value:
curl -X POST "http://localhost:8001/services/example_service/plugins" \ --no-progress-meter --fail-with-body \ --json '{ "name": "my-plugin", "config": { "response_header_name": "X-CustomHeaderName" } }'Copied! -
curl -X POST "http://localhost:8001/services/example_service/routes" \ --no-progress-meter --fail-with-body \ --json '{ "name": "example_route", "paths": [ "/mock" ] }'Copied! -
Send a request to the Route:
curl -i "http://localhost:8000/mock/anything" \ --no-progress-meter --fail-with-bodyCopied!This time we should see the
X-CustomHeaderNamein the response. - Exit the Kong Gateway shell before proceeding to the next step:
exitCopied!
Add automated configuration testing
- Update the
setupfunction inside thespec/01-integration_spec.luamodule so that themy-pluginthat is added to the database is configured with a different value for theresponse_header_namefield:-- Add the custom plugin to the test Route blue_print.plugins:insert { name = PLUGIN_NAME, route = { id = test_route.id }, config = { response_header_name = "X-CustomHeaderName", }, }Copied! - Modify the test assertion to match the new header name:
-- now validate and retrieve the expected response header local header_value = assert.response(r).has.header("X-CustomHeaderName")Copied!The test file should now look like this:
-- Helper functions provided by Kong Gateway, see https://github.com/Kong/kong/blob/master/spec/helpers.lua local helpers = require "spec.helpers" -- matches our plugin name defined in the plugins's schema.lua local PLUGIN_NAME = "my-plugin" -- Run the tests for each strategy. Strategies include "postgres" and "off" -- which represent the deployment topologies for Kong Gateway for _, strategy in helpers.all_strategies() do describe(PLUGIN_NAME .. ": [#" .. strategy .. "]", function() -- Will be initialized before_each nested test local client setup(function() -- A BluePrint gives us a helpful database wrapper to -- manage Kong Gateway entities directly. -- This function also truncates any existing data in an existing db. -- The custom plugin name is provided to this function so it mark as loaded local blue_print = helpers.get_db_utils(strategy, nil, { PLUGIN_NAME }) -- Using the BluePrint to create a test Route, automatically attaches it -- to the default "echo" Service that will be created by the test framework local test_route = blue_print.routes:insert({ paths = { "/mock" }, }) -- Add the custom plugin to the test Route blue_print.plugins:insert { name = PLUGIN_NAME, route = { id = test_route.id }, config = { response_header_name = "X-CustomHeaderName", }, } -- start kong assert(helpers.start_kong({ -- use the custom test template to create a local mock server nginx_conf = "spec/fixtures/custom_nginx.template", -- make sure our plugin gets loaded plugins = "bundled," .. PLUGIN_NAME, })) end) -- teardown runs after its parent describe block teardown(function() helpers.stop_kong(nil, true) end) -- before_each runs before each child describe before_each(function() client = helpers.proxy_client() end) -- after_each runs after each child describe after_each(function() if client then client:close() end end) -- a nested describe defines an actual test on the plugin behavior describe("The response", function() it("gets the expected header", function() -- invoke a test request local r = client:get("/mock/anything", {}) -- validate that the request succeeded, response status 200 assert.response(r).has.status(200) -- now validate and retrieve the expected response header local header_value = assert.response(r).has.header("X-CustomHeaderName") -- validate the value of that header assert.equal("response", header_value) end) end) end) endCopied! - Run the tests:
pongo runCopied!Pongo should report a successful test run.