Skip to content

Commit

Permalink
refactor(clustering) dpcp config clean (#8893)
Browse files Browse the repository at this point in the history
### Summary
DP/CP's code is too complex and not human readable, it is hard to understand the callback workflows.

This PR separates the logic of update_config, moves the code into an isolated file,
and also de-coupled dp/cp modules.

This PR is same as #8880, but solved the merge conflict of #8888.

### Full changelog
* separates the logic of `update_config` into `config_helper.lua`
* do not use `self` to mimic oop
* remove duplicated functions `extract_major_minor `and `validate_shared_cert` in wrpc_cp
* add many functions in `utils.lua`
* remove `version_handshake`
* pick #8888's yield feature in `to_sorted_string`
* update config hash test case
  • Loading branch information
chronolaw authored Jun 6, 2022
1 parent 819d316 commit 7ef980d
Show file tree
Hide file tree
Showing 15 changed files with 660 additions and 1,519 deletions.
4 changes: 1 addition & 3 deletions kong-2.8.0-0.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,13 @@ build = {
["kong.conf_loader.listeners"] = "kong/conf_loader/listeners.lua",

["kong.clustering"] = "kong/clustering/init.lua",
["kong.clustering.version_negotiation"] = "kong/clustering/version_negotiation/init.lua",
["kong.clustering.version_negotiation.services_known"] = "kong/clustering/version_negotiation/services_known.lua",
["kong.clustering.version_negotiation.services_requested"] = "kong/clustering/version_negotiation/services_requested.lua",
["kong.clustering.data_plane"] = "kong/clustering/data_plane.lua",
["kong.clustering.control_plane"] = "kong/clustering/control_plane.lua",
["kong.clustering.wrpc_data_plane"] = "kong/clustering/wrpc_data_plane.lua",
["kong.clustering.wrpc_control_plane"] = "kong/clustering/wrpc_control_plane.lua",
["kong.clustering.utils"] = "kong/clustering/utils.lua",
["kong.clustering.compat.removed_fields"] = "kong/clustering/compat/removed_fields.lua",
["kong.clustering.config_helper"] = "kong/clustering/config_helper.lua",

["kong.cluster_events"] = "kong/cluster_events/init.lua",
["kong.cluster_events.strategies.cassandra"] = "kong/cluster_events/strategies/cassandra.lua",
Expand Down
234 changes: 234 additions & 0 deletions kong/clustering/config_helper.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
local constants = require("kong.constants")
local declarative = require("kong.db.declarative")

local tostring = tostring
local assert = assert
local type = type
local error = error
local pairs = pairs
local ipairs = ipairs
local concat = table.concat
local sort = table.sort

local isempty = require("table.isempty")
local isarray = require("table.isarray")
local nkeys = require("table.nkeys")
local new_tab = require("table.new")

local yield = require("kong.tools.utils").yield

local ngx_log = ngx.log
local ngx_null = ngx.null
local ngx_md5 = ngx.md5
local ngx_md5_bin = ngx.md5_bin

local ngx_DEBUG = ngx.DEBUG

local DECLARATIVE_EMPTY_CONFIG_HASH = constants.DECLARATIVE_EMPTY_CONFIG_HASH
local _log_prefix = "[clustering] "


local _M = {}


local function to_sorted_string(value)
yield(true)

if value == ngx_null then
return "/null/"
end

local t = type(value)
if t == "string" or t == "number" then
return value
end

if t == "boolean" then
return tostring(value)
end

if t == "table" then
if isempty(value) then
return "{}"
end

if isarray(value) then
local count = #value
if count == 1 then
return to_sorted_string(value[1])
end

if count == 2 then
return to_sorted_string(value[1]) .. ";" ..
to_sorted_string(value[2])
end

if count == 3 then
return to_sorted_string(value[1]) .. ";" ..
to_sorted_string(value[2]) .. ";" ..
to_sorted_string(value[3])
end

if count == 4 then
return to_sorted_string(value[1]) .. ";" ..
to_sorted_string(value[2]) .. ";" ..
to_sorted_string(value[3]) .. ";" ..
to_sorted_string(value[4])
end

if count == 5 then
return to_sorted_string(value[1]) .. ";" ..
to_sorted_string(value[2]) .. ";" ..
to_sorted_string(value[3]) .. ";" ..
to_sorted_string(value[4]) .. ";" ..
to_sorted_string(value[5])
end

local i = 0
local o = new_tab(count < 100 and count or 100, 0)
for j = 1, count do
i = i + 1
o[i] = to_sorted_string(value[j])

if j % 100 == 0 then
i = 1
o[i] = ngx_md5_bin(concat(o, ";", 1, 100))
end
end

return ngx_md5_bin(concat(o, ";", 1, i))

else
local count = nkeys(value)
local keys = new_tab(count, 0)
local i = 0
for k in pairs(value) do
i = i + 1
keys[i] = k
end

sort(keys)

local o = new_tab(count, 0)
for i = 1, count do
o[i] = keys[i] .. ":" .. to_sorted_string(value[keys[i]])
end

value = concat(o, ";", 1, count)

return #value > 512 and ngx_md5_bin(value) or value
end -- isarray

end -- table

error("invalid type to be sorted (JSON types are supported)")
end

local function calculate_config_hash(config_table)
if type(config_table) ~= "table" then
local config_hash = ngx_md5(to_sorted_string(config_table))
return config_hash, { config = config_hash, }
end

local routes = config_table.routes
local services = config_table.services
local plugins = config_table.plugins
local upstreams = config_table.upstreams
local targets = config_table.targets

local routes_hash = routes and ngx_md5(to_sorted_string(routes)) or DECLARATIVE_EMPTY_CONFIG_HASH
local services_hash = services and ngx_md5(to_sorted_string(services)) or DECLARATIVE_EMPTY_CONFIG_HASH
local plugins_hash = plugins and ngx_md5(to_sorted_string(plugins)) or DECLARATIVE_EMPTY_CONFIG_HASH
local upstreams_hash = upstreams and ngx_md5(to_sorted_string(upstreams)) or DECLARATIVE_EMPTY_CONFIG_HASH
local targets_hash = targets and ngx_md5(to_sorted_string(targets)) or DECLARATIVE_EMPTY_CONFIG_HASH

config_table.routes = nil
config_table.services = nil
config_table.plugins = nil
config_table.upstreams = nil
config_table.targets = nil

local config_hash = ngx_md5(to_sorted_string(config_table) .. routes_hash
.. services_hash
.. plugins_hash
.. upstreams_hash
.. targets_hash)

config_table.routes = routes
config_table.services = services
config_table.plugins = plugins
config_table.upstreams = upstreams
config_table.targets = targets

return config_hash, {
config = config_hash,
routes = routes_hash,
services = services_hash,
plugins = plugins_hash,
upstreams = upstreams_hash,
targets = targets_hash,
}
end

local hash_fields = {
"config",
"routes",
"services",
"plugins",
"upstreams",
"targets",
}

local function fill_empty_hashes(hashes)
for _, field_name in ipairs(hash_fields) do
hashes[field_name] = hashes[field_name] or DECLARATIVE_EMPTY_CONFIG_HASH
end
end

function _M.update(declarative_config, config_table, config_hash, hashes)
assert(type(config_table) == "table")

if not config_hash then
config_hash, hashes = calculate_config_hash(config_table)
end

if hashes then
fill_empty_hashes(hashes)
end

local current_hash = declarative.get_current_hash()
if current_hash == config_hash then
ngx_log(ngx_DEBUG, _log_prefix, "same config received from control plane, ",
"no need to reload")
return true
end

local entities, err, _, meta, new_hash =
declarative_config:parse_table(config_table, config_hash)
if not entities then
return nil, "bad config received from control plane " .. err
end

if current_hash == new_hash then
ngx_log(ngx_DEBUG, _log_prefix, "same config received from control plane, ",
"no need to reload")
return true
end

-- NOTE: no worker mutex needed as this code can only be
-- executed by worker 0

local res
res, err = declarative.load_into_cache_with_events(entities, meta, new_hash, hashes)
if not res then
return nil, err
end

return true
end


_M.calculate_config_hash = calculate_config_hash


return _M
Loading

0 comments on commit 7ef980d

Please sign in to comment.