Skip to content

Commit

Permalink
feat(runloop): plugin:configure(configs) handler
Browse files Browse the repository at this point in the history
### Summary

Adds a new handler that plugins can implement:

```
 -- configs will be `nil` if there is no active configs for the plugin
function Plugin:configure(configs)
end
```

See the change in `acme` plugin.

Signed-off-by: Aapo Talvensaari <[email protected]>
  • Loading branch information
bungle committed Oct 18, 2023
1 parent c15493e commit 5ce96a4
Show file tree
Hide file tree
Showing 13 changed files with 310 additions and 231 deletions.
6 changes: 6 additions & 0 deletions changelog/unreleased/kong/plugin-configure-phase.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
message: >
Plugins can now implement `Plugin:configure(configs)` function that is called whenever
there is a change in plugin entities. An array of current plugin configurations is
passed to the function, or `nil` in case there is no active configurations for the plugin.
type: feature
scope: Core
8 changes: 5 additions & 3 deletions kong/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ local function execute_collected_plugins_iterator(plugins_iterator, phase, ctx)
span:finish()
end
end

if is_timing_enabled then
req_dyn_hook_run_hooks(ctx, "timing", "after:plugin_iterator")
end
Expand Down Expand Up @@ -946,8 +946,8 @@ function Kong.init_worker()
local errors = execute_init_worker_plugins_iterator(plugins_iterator, ctx)
if errors then
for _, e in ipairs(errors) do
local err = "failed to execute the \"init_worker\" " ..
"handler for plugin \"" .. e.plugin .."\": " .. e.err
local err = 'failed to execute the "init_worker" ' ..
'handler for plugin "' .. e.plugin ..'": ' .. e.err
stash_init_worker_error(err)
end
end
Expand All @@ -966,6 +966,8 @@ function Kong.init_worker()
stash_init_worker_error(err)
return
end

plugins_iterator:configure(ctx)
end


Expand Down
18 changes: 3 additions & 15 deletions kong/plugins/acme/client.lua
Original file line number Diff line number Diff line change
Expand Up @@ -508,21 +508,9 @@ local function renew_certificate_storage(conf)

end

local function renew_certificate(premature)
if premature then
return
end

for plugin, err in kong.db.plugins:each(1000) do
if err then
kong.log.warn("error fetching plugin: ", err)
end

if plugin.name == "acme" then
kong.log.info("renew storage configured in acme plugin: ", plugin.id)
renew_certificate_storage(plugin.config)
end
end
local function renew_certificate(config)
kong.log.info("renew storage configured in acme plugin: ", config.__plugin_id)
renew_certificate_storage(config)
end

local function load_renew_hosts(conf)
Expand Down
35 changes: 20 additions & 15 deletions kong/plugins/acme/handler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -70,28 +70,33 @@ local domains_matcher
-- expose it for use in api.lua
ACMEHandler.build_domain_matcher = build_domain_matcher

function ACMEHandler:init_worker()
local worker_id = ngx.worker.id()
kong.log.info("acme renew timer started on worker ", worker_id)
ngx.timer.every(86400, client.renew_certificate)

-- handle cache updating of domains_matcher
kong.worker_events.register(function(data)
if data.entity.name ~= "acme" then
return
end
local CONFIG

local operation = data.operation

if operation == "create" or operation == "update" then
local conf = data.entity.config
domains_matcher = build_domain_matcher(conf.domains)
end
local function renew(premature)
if premature or not CONFIG then
return
end
client.renew_certificate(CONFIG)
end


end, "crud", "plugins")
function ACMEHandler:init_worker()
local worker_id = ngx.worker.id()
kong.log.info("acme renew timer started on worker ", worker_id)
ngx.timer.every(86400, renew)
end


function ACMEHandler:configure(configs)
CONFIG = configs and configs[1] or nil
if CONFIG then
domains_matcher = build_domain_matcher(CONFIG.domains)
end
end


local function check_domains(conf, host)
if not conf.enable_ipv4_common_name and string_find(host, "^(%d+)%.(%d+)%.(%d+)%.(%d+)$") then
return false
Expand Down
69 changes: 12 additions & 57 deletions kong/plugins/prometheus/exporter.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ local role = kong.configuration.role

local KONG_LATENCY_BUCKETS = { 1, 2, 5, 7, 10, 15, 20, 30, 50, 75, 100, 200, 500, 750, 1000}
local UPSTREAM_LATENCY_BUCKETS = {25, 50, 80, 100, 250, 400, 700, 1000, 2000, 5000, 10000, 30000, 60000 }
local IS_PROMETHEUS_ENABLED


local metrics = {}
-- prometheus.lua instance
Expand All @@ -33,60 +35,6 @@ local kong_subsystem = ngx.config.subsystem
local http_subsystem = kong_subsystem == "http"


-- should we introduce a way to know if a plugin is configured or not?
local is_prometheus_enabled, register_events_handler do
local PLUGIN_NAME = "prometheus"
local CACHE_KEY = "prometheus:enabled"

local function is_prometheus_enabled_fetch()
for plugin, err in kong.db.plugins:each() do
if err then
kong.log.crit("could not obtain list of plugins: ", err)
return nil, err
end

if plugin.name == PLUGIN_NAME and plugin.enabled then
return true
end
end

return false
end


-- Returns `true` if Prometheus is enabled anywhere inside Kong.
-- The results are then cached and purged as necessary.
function is_prometheus_enabled()
local enabled, err = kong.cache:get(CACHE_KEY, nil, is_prometheus_enabled_fetch)

if err then
error("error when checking if prometheus enabled: " .. err)
end

return enabled
end


-- invalidate cache when a plugin is added/removed/updated
function register_events_handler()
local worker_events = kong.worker_events

if kong.configuration.database == "off" then
worker_events.register(function()
kong.cache:invalidate(CACHE_KEY)
end, "declarative", "reconfigure")

else
worker_events.register(function(data)
if data.entity.name == PLUGIN_NAME then
kong.cache:invalidate(CACHE_KEY)
end
end, "crud", "plugins")
end
end
end


local function init()
local shm = "prometheus_metrics"
if not ngx.shared[shm] then
Expand Down Expand Up @@ -230,11 +178,17 @@ local function init()
end
end


local function init_worker()
prometheus:init_worker()
register_events_handler()
end


local function configure(configs)
IS_PROMETHEUS_ENABLED = configs ~= nil
end


-- Convert the MD5 hex string to its numeric representation
-- Note the following will be represented as a float instead of int64 since luajit
-- don't like int64. Good news is prometheus uses float instead of int64 as well
Expand Down Expand Up @@ -412,7 +366,7 @@ local function metric_data(write_fn)
-- upstream targets accessible?
local upstreams_dict = get_all_upstreams()
for key, upstream_id in pairs(upstreams_dict) do
-- long loop maybe spike proxy request latency, so we
-- long loop maybe spike proxy request latency, so we
-- need yield to avoid blocking other requests
-- kong.tools.utils.yield(true)
yield(true, phase)
Expand Down Expand Up @@ -489,7 +443,7 @@ local function metric_data(write_fn)

-- notify the function if prometheus plugin is enabled,
-- so that it can avoid exporting unnecessary metrics if not
prometheus:metric_data(write_fn, not is_prometheus_enabled())
prometheus:metric_data(write_fn, not IS_PROMETHEUS_ENABLED)
end

local function collect()
Expand Down Expand Up @@ -525,6 +479,7 @@ end
return {
init = init,
init_worker = init_worker,
configure = configure,
log = log,
metric_data = metric_data,
collect = collect,
Expand Down
10 changes: 8 additions & 2 deletions kong/plugins/prometheus/handler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,20 @@ local PrometheusHandler = {
VERSION = kong_meta.version,
}

function PrometheusHandler.init_worker()
function PrometheusHandler:init_worker()
exporter.init_worker()
end


function PrometheusHandler:configure(configs)
exporter.configure(configs)
end


local http_subsystem = ngx.config.subsystem == "http"


function PrometheusHandler.log(self, conf)
function PrometheusHandler:log(conf)
local message = kong.log.serialize()

local serialized = {}
Expand Down
14 changes: 14 additions & 0 deletions kong/runloop/handler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ local exec = ngx.exec
local header = ngx.header
local set_header = ngx.req.set_header
local timer_at = ngx.timer.at
local get_phase = ngx.get_phase
local subsystem = ngx.config.subsystem
local clear_header = ngx.req.clear_header
local http_version = ngx.req.http_version
Expand Down Expand Up @@ -519,6 +520,14 @@ local function build_plugins_iterator(version)
if not plugins_iterator then
return nil, err
end

local phase = get_phase()
-- skip calling plugins_iterator:configure on init/init_worker
-- as it is explicitly called on init_worker
if phase ~= "init" and phase ~= "init_worker" then
plugins_iterator:configure()
end

PLUGINS_ITERATOR = plugins_iterator
return true
end
Expand Down Expand Up @@ -719,6 +728,11 @@ do
end

if plugins_iterator then
-- Before we replace plugin iterator we need to call configure handler
-- of each plugin. There is a slight chance that plugin configure handler
-- would yield, and that should be considered a bad practice.
plugins_iterator:configure()

PLUGINS_ITERATOR = plugins_iterator
CURRENT_PLUGINS_HASH = plugins_hash or 0
end
Expand Down
Loading

0 comments on commit 5ce96a4

Please sign in to comment.