Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(declarative-config): set hash when config is loaded #9911

Merged
merged 3 commits into from
Dec 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@
- **Zipkin**: Fix an issue where the global plugin's sample ratio overrides route-specific.
[#9877](https://github.com/Kong/kong/pull/9877)

#### Core

- Fix an issue where after a valid declarative configuration is loaded,
the configuration hash is incorrectly set to the value: `00000000000000000000000000000000`.
[#9911](https://github.com/Kong/kong/pull/9911)

### Dependencies

- Bumped luarocks from 3.9.1 to 3.9.2
Expand Down
10 changes: 8 additions & 2 deletions kong/db/declarative/import.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ local cjson_encode = require("cjson.safe").encode
local deepcopy = require("pl.tablex").deepcopy
local marshall = require("kong.db.declarative.marshaller").marshall
local schema_topological_sort = require("kong.db.schema.topological_sort")

local nkeys = require("table.nkeys")

local assert = assert
local sort = table.sort
Expand Down Expand Up @@ -148,6 +148,12 @@ local function unique_field_key(schema_name, ws_id, field, value, unique_across_
end


local function config_is_empty(entities)
-- empty configuration has no entries other than workspaces
return entities.workspaces and nkeys(entities) == 1
end


-- entities format:
-- {
-- services: {
Expand All @@ -174,7 +180,7 @@ local function load_into_cache(entities, meta, hash)

assert(type(fallback_workspace) == "string")

if not hash or hash == "" then
if not hash or hash == "" or config_is_empty(entities) then
hash = DECLARATIVE_EMPTY_CONFIG_HASH
end

Expand Down
23 changes: 12 additions & 11 deletions kong/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ local CTX_NREC = 50 -- normally Kong has ~32 keys in ctx

local declarative_entities
local declarative_meta
local declarative_hash
local schema_state


Expand Down Expand Up @@ -448,15 +449,15 @@ local function parse_declarative_config(kong_config)
if not has_declarative_config(kong_config) then
-- return an empty configuration,
-- including only the default workspace
local entities, _, _, meta = dc:parse_table({ _format_version = "2.1" })
return entities, nil, meta
local entities, _, _, meta, hash = dc:parse_table({ _format_version = "2.1" })
return entities, nil, meta, hash
end

local entities, err, _, meta
local entities, err, _, meta, hash
if kong_config.declarative_config ~= nil then
entities, err, _, meta = dc:parse_file(kong_config.declarative_config)
entities, err, _, meta, hash = dc:parse_file(kong_config.declarative_config)
elseif kong_config.declarative_config_string ~= nil then
entities, err, _, meta = dc:parse_string(kong_config.declarative_config_string)
entities, err, _, meta, hash = dc:parse_string(kong_config.declarative_config_string)
end

if not entities then
Expand All @@ -469,7 +470,7 @@ local function parse_declarative_config(kong_config)
end
end

return entities, nil, meta
return entities, nil, meta, hash
end


Expand All @@ -491,7 +492,7 @@ local function declarative_init_build()
end


local function load_declarative_config(kong_config, entities, meta)
local function load_declarative_config(kong_config, entities, meta, hash)
local opts = {
name = "declarative_config",
}
Expand All @@ -502,8 +503,7 @@ local function load_declarative_config(kong_config, entities, meta)
if value then
return true
end

local ok, err = declarative.load_into_cache(entities, meta)
local ok, err = declarative.load_into_cache(entities, meta, hash)
if not ok then
return nil, err
end
Expand Down Expand Up @@ -622,7 +622,7 @@ function Kong.init()
#config.status_listeners == 0)
then
local err
declarative_entities, err, declarative_meta = parse_declarative_config(kong.configuration)
declarative_entities, err, declarative_meta, declarative_hash = parse_declarative_config(kong.configuration)
if not declarative_entities then
error(err)
end
Expand Down Expand Up @@ -752,7 +752,8 @@ function Kong.init_worker()
elseif declarative_entities then
ok, err = load_declarative_config(kong.configuration,
declarative_entities,
declarative_meta)
declarative_meta,
declarative_hash)
if not ok then
stash_init_worker_error("failed to load declarative config file: " .. err)
return
Expand Down
109 changes: 107 additions & 2 deletions spec/02-integration/02-cmd/02-start_stop_spec.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
local helpers = require "spec.helpers"

local helpers = require "spec.helpers"
local constants = require "kong.constants"
local cjson = require "cjson"

for _, strategy in helpers.each_strategy() do

Expand Down Expand Up @@ -472,6 +473,110 @@ describe("kong start/stop #" .. strategy, function()
return ok
end, 10)
end)

it("hash is set correctly for a non-empty configuration", function()
local yaml_file = helpers.make_yaml_file [[
_format_version: "1.1"
services:
- name: my-service
url: http://127.0.0.1:15555
routes:
- name: example-route
hosts:
- example.test
]]

local admin_client, json_body

finally(function()
os.remove(yaml_file)
helpers.stop_kong(helpers.test_conf.prefix)
if admin_client then
admin_client:close()
end
end)

assert(helpers.start_kong({
database = "off",
declarative_config = yaml_file,
nginx_conf = "spec/fixtures/custom_nginx.template",
}))

helpers.wait_until(function()
helpers.wait_until(function()
local pok
pok, admin_client = pcall(helpers.admin_client)
return pok
end, 10)

local res = assert(admin_client:send {
method = "GET",
path = "/status"
})
if res.status ~= 200 then
return false
end
local body = assert.res_status(200, res)
json_body = cjson.decode(body)

if admin_client then
admin_client:close()
admin_client = nil
end

return true
end, 10)

assert.is_string(json_body.configuration_hash)
assert.equals(32, #json_body.configuration_hash)
assert.not_equal(constants.DECLARATIVE_EMPTY_CONFIG_HASH, json_body.configuration_hash)
end)

it("hash is set correctly for an empty configuration", function()

local admin_client, json_body

finally(function()
helpers.stop_kong(helpers.test_conf.prefix)
if admin_client then
admin_client:close()
end
end)

-- not specifying declarative_config this time
assert(helpers.start_kong({
database = "off",
nginx_conf = "spec/fixtures/custom_nginx.template",
}))

helpers.wait_until(function()
helpers.wait_until(function()
local pok
pok, admin_client = pcall(helpers.admin_client)
return pok
end, 10)

local res = assert(admin_client:send {
method = "GET",
path = "/status"
})
if res.status ~= 200 then
return false
end
local body = assert.res_status(200, res)
json_body = cjson.decode(body)

if admin_client then
admin_client:close()
admin_client = nil
end

return true
end, 10)

assert.is_string(json_body.configuration_hash)
assert.equals(constants.DECLARATIVE_EMPTY_CONFIG_HASH, json_body.configuration_hash)
end)
end)
end

Expand Down
7 changes: 5 additions & 2 deletions spec/02-integration/04-admin_api/02-kong_routes_spec.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
local helpers = require "spec.helpers"
local cjson = require "cjson"
local constants = require "kong.constants"

local UUID_PATTERN = "%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x"

Expand Down Expand Up @@ -188,6 +189,8 @@ describe("Admin API - Kong routes with strategy #" .. strategy, function()
end)

describe("/status", function()
local empty_config_hash = constants.DECLARATIVE_EMPTY_CONFIG_HASH

it("returns status info", function()
local res = assert(client:send {
method = "GET",
Expand All @@ -208,7 +211,7 @@ describe("Admin API - Kong routes with strategy #" .. strategy, function()
assert.is_number(json.server.connections_waiting)
assert.is_number(json.server.total_requests)
if strategy == "off" then
assert.is_equal(string.rep("0", 32), json.configuration_hash) -- all 0 in DBLESS mode until configuration is applied
assert.is_equal(empty_config_hash, json.configuration_hash) -- all 0 in DBLESS mode until configuration is applied
else
assert.is_nil(json.configuration_hash) -- not present in DB mode
end
Expand Down Expand Up @@ -251,7 +254,7 @@ describe("Admin API - Kong routes with strategy #" .. strategy, function()
assert.is_number(json.server.total_requests)
assert.is_string(json.configuration_hash)
assert.equal(32, #json.configuration_hash)

assert.is_not_equal(empty_config_hash, json.configuration_hash)
end)

it("database.reachable is `true` when DB connection is healthy", function()
Expand Down