Skip to content

Commit

Permalink
feat(entities): add updated_at column for entities (#10400)
Browse files Browse the repository at this point in the history
* feat(entities): add updated_at column for entities

FTI-1294 FTI-1292 FTI-2103

* Modify changelog
* autodoc
* add new migration to rockspec
* add `updated_at` for workspaces and clustering_data_planes
* changelog and spec
* ignores time-irrelevant field for compatability between CP and DP when validation
* compatiblility
* simplifies the default value for timestamp in migrations, fixes the compat and fixes spec
* on targets table use ms precision in current_timestamp

---------

Co-authored-by: Aapo Talvensaari <aapo.talvensaari@gmail.com>
  • Loading branch information
liverpool8056 and bungle authored Mar 15, 2023
1 parent 8be4110 commit ecbf062
Show file tree
Hide file tree
Showing 33 changed files with 287 additions and 12 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@

- Make runloop and init error response content types compliant with Accept header value
[#10366](https://github.com/Kong/kong/pull/10366)
- Add a new field `updated_at` for core entities ca_certificates, certificates, consumers,
targets, upstreams, plugins, workspaces, clustering_data_planes and snis.
[#10400](https://github.com/Kong/kong/pull/10400)
- Allow configuring custom error templates
[#10374](https://github.com/Kong/kong/pull/10374)

Expand Down
5 changes: 5 additions & 0 deletions autodoc/admin-api/data/admin-api.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1611,6 +1611,7 @@ return {
fields = {
id = { skip = true },
created_at = { skip = true },
updated_at = { skip = true },
cert = {
description = [[
PEM-encoded public certificate chain of the SSL key pair.
Expand Down Expand Up @@ -1691,6 +1692,7 @@ return {
fields = {
id = { skip = true },
created_at = { skip = true },
updated_at = { skip = true },
cert = {
description = [[PEM-encoded public certificate of the CA.]],
example = "-----BEGIN CERTIFICATE-----...",
Expand Down Expand Up @@ -1726,6 +1728,7 @@ return {
fields = {
id = { skip = true },
created_at = { skip = true },
updated_at = { skip = true },
name = { description = [[The SNI name to associate with the given certificate.]] },
certificate = {
description = [[
Expand Down Expand Up @@ -1859,6 +1862,7 @@ return {
fields = {
id = { skip = true },
created_at = { skip = true },
updated_at = { skip = true },
["name"] = { description = [[This is a hostname, which must be equal to the `host` of a Service.]] },
["slots"] = { description = [[The number of slots in the load balancer algorithm. If `algorithm` is set to `round-robin`, this setting determines the maximum number of slots. If `algorithm` is set to `consistent-hashing`, this setting determines the actual number of slots in the algorithm. Accepts an integer in the range `10`-`65536`.]] },
["algorithm"] = { description = [[Which load balancing algorithm to use.]] },
Expand Down Expand Up @@ -2177,6 +2181,7 @@ return {
fields = {
id = { skip = true },
created_at = { skip = true },
updated_at = { skip = true },
upstream = { skip = true },
target = {
description = [[
Expand Down
1 change: 1 addition & 0 deletions kong-3.2.1-0.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ build = {
["kong.db.migrations.core.016_280_to_300"] = "kong/db/migrations/core/016_280_to_300.lua",
["kong.db.migrations.core.017_300_to_310"] = "kong/db/migrations/core/017_300_to_310.lua",
["kong.db.migrations.core.018_310_to_320"] = "kong/db/migrations/core/018_310_to_320.lua",
["kong.db.migrations.core.019_320_to_330"] = "kong/db/migrations/core/019_320_to_330.lua",
["kong.db.migrations.operations.200_to_210"] = "kong/db/migrations/operations/200_to_210.lua",
["kong.db.migrations.operations.210_to_211"] = "kong/db/migrations/operations/210_to_211.lua",
["kong.db.migrations.operations.212_to_213"] = "kong/db/migrations/operations/212_to_213.lua",
Expand Down
17 changes: 17 additions & 0 deletions kong/clustering/compat/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,23 @@ function _M.update_compatible_payload(payload, dp_version, log_suffix)
end
end

if dp_version_num < 3003000000 --[[ 3.3.0.0 ]] then
-- remove updated_at field for core entities ca_certificates, certificates, consumers,
-- targets, upstreams, plugins, workspaces, clustering_data_planes and snis
local entity_names = {'ca_certificates', 'certificates', 'consumers', 'targets', 'upstreams',
'plugins', 'workspaces', 'clustering_data_planes', 'snis'}

for _, name in ipairs(entity_names) do
for _, config_entity in ipairs(config_table[name] or {}) do
ngx_log(ngx_WARN, _log_prefix, "Kong Gateway v" .. KONG_VERSION ..
" contains configuration '" .. name .. ".updated_at'",
" which is incompatible with dataplane version " .. dp_version .. " and will",
" be removed.", log_suffix)
config_entity.updated_at = nil
end
end
end

if dp_version_num < 3002000000 --[[ 3.2.0.0 ]] then
local config_plugins = config_table["plugins"]
if config_plugins then
Expand Down
35 changes: 35 additions & 0 deletions kong/db/migrations/core/019_320_to_330.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
return {
postgres = {
up = [[
DO $$
BEGIN
ALTER TABLE IF EXISTS ONLY "plugins" ADD "updated_at" TIMESTAMP WITH TIME ZONE DEFAULT (CURRENT_TIMESTAMP(0) AT TIME ZONE 'UTC');
ALTER TABLE IF EXISTS ONLY "ca_certificates" ADD "updated_at" TIMESTAMP WITH TIME ZONE DEFAULT (CURRENT_TIMESTAMP(0) AT TIME ZONE 'UTC');
ALTER TABLE IF EXISTS ONLY "certificates" ADD "updated_at" TIMESTAMP WITH TIME ZONE DEFAULT (CURRENT_TIMESTAMP(0) AT TIME ZONE 'UTC');
ALTER TABLE IF EXISTS ONLY "consumers" ADD "updated_at" TIMESTAMP WITH TIME ZONE DEFAULT (CURRENT_TIMESTAMP(0) AT TIME ZONE 'UTC');
ALTER TABLE IF EXISTS ONLY "snis" ADD "updated_at" TIMESTAMP WITH TIME ZONE DEFAULT (CURRENT_TIMESTAMP(0) AT TIME ZONE 'UTC');
ALTER TABLE IF EXISTS ONLY "targets" ADD "updated_at" TIMESTAMP WITH TIME ZONE DEFAULT (CURRENT_TIMESTAMP(3) AT TIME ZONE 'UTC');
ALTER TABLE IF EXISTS ONLY "upstreams" ADD "updated_at" TIMESTAMP WITH TIME ZONE DEFAULT (CURRENT_TIMESTAMP(0) AT TIME ZONE 'UTC');
ALTER TABLE IF EXISTS ONLY "workspaces" ADD "updated_at" TIMESTAMP WITH TIME ZONE DEFAULT (CURRENT_TIMESTAMP(0) AT TIME ZONE 'UTC');
ALTER TABLE IF EXISTS ONLY "clustering_data_planes" ADD "updated_at" TIMESTAMP WITH TIME ZONE DEFAULT (CURRENT_TIMESTAMP(0) AT TIME ZONE 'UTC');
EXCEPTION WHEN DUPLICATE_COLUMN THEN
-- Do nothing, accept existing state
END;
$$;
]]
},

cassandra = {
up = [[
ALTER TABLE plugins ADD updated_at timestamp;
ALTER TABLE ca_certificates ADD updated_at timestamp;
ALTER TABLE certificates ADD updated_at timestamp;
ALTER TABLE consumers ADD updated_at timestamp;
ALTER TABLE snis ADD updated_at timestamp;
ALTER TABLE targets ADD updated_at timestamp;
ALTER TABLE upstreams ADD updated_at timestamp;
ALTER TABLE workspaces ADD updated_at timestamp;
ALTER TABLE clustering_data_planes ADD updated_at timestamp;
]]
},
}
1 change: 1 addition & 0 deletions kong/db/migrations/core/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ return {
"016_280_to_300",
"017_300_to_310",
"018_310_to_320",
"019_320_to_330",
}
1 change: 1 addition & 0 deletions kong/db/schema/entities/ca_certificates.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ return {
fields = {
{ id = typedefs.uuid, },
{ created_at = typedefs.auto_timestamp_s },
{ updated_at = typedefs.auto_timestamp_s },
{ cert = typedefs.certificate { required = true }, },
{ cert_digest = { type = "string", unique = true }, },
{ tags = typedefs.tags },
Expand Down
1 change: 1 addition & 0 deletions kong/db/schema/entities/certificates.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ return {
fields = {
{ id = typedefs.uuid, },
{ created_at = typedefs.auto_timestamp_s },
{ updated_at = typedefs.auto_timestamp_s },
{ cert = typedefs.certificate { required = true, referenceable = true }, },
{ key = typedefs.key { required = true, referenceable = true, encrypted = true }, },
{ cert_alt = typedefs.certificate { required = false, referenceable = true }, },
Expand Down
1 change: 1 addition & 0 deletions kong/db/schema/entities/clustering_data_planes.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ return {
fields = {
{ id = typedefs.uuid { required = true, }, },
{ last_seen = typedefs.auto_timestamp_s },
{ updated_at = typedefs.auto_timestamp_s },
{ ip = typedefs.ip { required = true, } },
{ config_hash = { type = "string", len_eq = 32, } },
{ hostname = typedefs.host { required = true, } },
Expand Down
1 change: 1 addition & 0 deletions kong/db/schema/entities/consumers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ return {
fields = {
{ id = typedefs.uuid, },
{ created_at = typedefs.auto_timestamp_s },
{ updated_at = typedefs.auto_timestamp_s },
{ username = { type = "string", unique = true }, },
{ custom_id = { type = "string", unique = true }, },
{ tags = typedefs.tags },
Expand Down
1 change: 1 addition & 0 deletions kong/db/schema/entities/plugins.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ return {
{ name = { type = "string", required = true, }, },
{ instance_name = typedefs.utf8_name },
{ created_at = typedefs.auto_timestamp_s },
{ updated_at = typedefs.auto_timestamp_s },
{ route = { type = "foreign", reference = "routes", default = null, on_delete = "cascade", }, },
{ service = { type = "foreign", reference = "services", default = null, on_delete = "cascade", }, },
{ consumer = { type = "foreign", reference = "consumers", default = null, on_delete = "cascade", }, },
Expand Down
1 change: 1 addition & 0 deletions kong/db/schema/entities/snis.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ return {
{ id = typedefs.uuid, },
{ name = typedefs.wildcard_host { required = true, unique = true, unique_across_ws = true }},
{ created_at = typedefs.auto_timestamp_s },
{ updated_at = typedefs.auto_timestamp_s },
{ tags = typedefs.tags },
{ certificate = { type = "foreign", reference = "certificates", required = true }, },
},
Expand Down
1 change: 1 addition & 0 deletions kong/db/schema/entities/targets.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ return {
fields = {
{ id = typedefs.uuid },
{ created_at = typedefs.auto_timestamp_ms },
{ updated_at = typedefs.auto_timestamp_ms },
{ upstream = { type = "foreign", reference = "upstreams", required = true, on_delete = "cascade" }, },
{ target = { type = "string", required = true, custom_validator = validate_target, }, },
{ weight = { type = "integer", default = 100, between = { 0, 65535 }, }, },
Expand Down
1 change: 1 addition & 0 deletions kong/db/schema/entities/upstreams.lua
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ local r = {
fields = {
{ id = typedefs.uuid, },
{ created_at = typedefs.auto_timestamp_s },
{ updated_at = typedefs.auto_timestamp_s },
{ name = { type = "string", required = true, unique = true, custom_validator = validate_name }, },
{ algorithm = { type = "string",
default = "round-robin",
Expand Down
1 change: 1 addition & 0 deletions kong/db/schema/entities/workspaces.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ return {
{ name = typedefs.utf8_name { required = true, not_one_of = { table.unpack(constants.CORE_ENTITIES) }, } },
{ comment = { type = "string" } },
{ created_at = typedefs.auto_timestamp_s },
{ updated_at = typedefs.auto_timestamp_s },
{ meta = { type = "record", fields = {} } },
{ config = { type = "record", fields = {} } },
}
Expand Down
29 changes: 26 additions & 3 deletions kong/db/schema/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1110,16 +1110,15 @@ validate_fields = function(self, input)
for k, v in pairs(input) do
local err
local field = self.fields[tostring(k)]
local is_ttl = k == "ttl" and self.ttl
if field and field.type == "self" then
local pok
pok, err, errors[k] = pcall(self.validate_field, self, input, v)
if not pok then
errors[k] = validation_errors.SCHEMA_CANNOT_VALIDATE
kong.log.debug(errors[k], ": ", err)
end
elseif is_ttl then
kong.log.debug("ignoring validation on ttl field")
elseif self.unvalidated_fields[k]() then
kong.log.debug("ignoring validation on ", k, " field")
else
field, err = resolve_field(self, k, field, subschema)
if field then
Expand Down Expand Up @@ -2425,6 +2424,30 @@ function Schema.new(definition, is_subschema)
_cache[self.name].schema = self
end

-- timestamp-irrelevant fields should not be a critial factor on entities to
-- be loaded or refreshed correctly. These fields, such as `ttl` and `updated_at`
-- might be ignored during validation.
-- unvalidated_fields is added for ignoring some fields, key in the table is the
-- name of the field to be ignored, the value must be a function, when the field
-- should be ignored, it returns true otherwise returns false.
self.unvalidated_fields = {
["ttl"] = function ()
return self.ttl
end,
["updated_at"] = function()
return true
end
}

setmetatable(self.unvalidated_fields, {
__index = function()
return function() -- default option
return false
end
end
})


return self
end

Expand Down
15 changes: 15 additions & 0 deletions spec/01-unit/01-db/01-schema/05-services_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,23 @@ local certificates = require "kong.db.schema.entities.certificates"
assert(Schema.new(certificates))
local Services = assert(Schema.new(services))

local function setup_global_env()
_G.kong = _G.kong or {}
_G.kong.log = _G.kong.log or {
debug = function(msg)
ngx.log(ngx.DEBUG, msg)
end,
error = function(msg)
ngx.log(ngx.ERR, msg)
end,
warn = function (msg)
ngx.log(ngx.WARN, msg)
end
}
end

describe("services", function()
setup_global_env()
local a_valid_uuid = "cbb297c0-a956-486d-ad1d-f9b42df9465a"
local uuid_pattern = "^" .. ("%x"):rep(8) .. "%-" .. ("%x"):rep(4) .. "%-"
.. ("%x"):rep(4) .. "%-" .. ("%x"):rep(4) .. "%-"
Expand Down
18 changes: 18 additions & 0 deletions spec/01-unit/01-db/01-schema/06-routes_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ local Entity = require "kong.db.schema.entity"

local Routes

local function setup_global_env()
_G.kong = _G.kong or {}
_G.kong.log = _G.kong.log or {
debug = function(msg)
ngx.log(ngx.DEBUG, msg)
end,
error = function(msg)
ngx.log(ngx.ERR, msg)
end,
warn = function (msg)
ngx.log(ngx.WARN, msg)
end
}
end

local function reload_flavor(flavor)
_G.kong = {
configuration = {
Expand Down Expand Up @@ -36,6 +51,7 @@ describe("routes schema (flavor = traditional/traditional_compatible)", function
.. ("%x"):rep(12) .. "$"

reload_flavor("traditional")
setup_global_env()

it("validates a valid route", function()
local route = {
Expand Down Expand Up @@ -1255,6 +1271,7 @@ describe("routes schema (flavor = expressions)", function()
local another_uuid = "64a8670b-900f-44e7-a900-6ec7ef5aa4d3"

reload_flavor("expressions")
setup_global_env()

it("validates a valid route", function()
local route = {
Expand Down Expand Up @@ -1310,6 +1327,7 @@ describe("routes schema (flavor = traditional_compatible)", function()
local another_uuid = "64a8670b-900f-44e7-a900-6ec7ef5aa4d3"

reload_flavor("traditional_compatible")
setup_global_env()

it("validates a valid route", function()
local route = {
Expand Down
16 changes: 16 additions & 0 deletions spec/01-unit/01-db/01-schema/08-targets_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@ local certificates = require "kong.db.schema.entities.certificates"
local upstreams = require "kong.db.schema.entities.upstreams"
local utils = require "kong.tools.utils"

local function setup_global_env()
_G.kong = _G.kong or {}
_G.kong.log = _G.kong.log or {
debug = function(msg)
ngx.log(ngx.DEBUG, msg)
end,
error = function(msg)
ngx.log(ngx.ERR, msg)
end,
warn = function (msg)
ngx.log(ngx.WARN, msg)
end
}
end

assert(Schema.new(certificates))
assert(Schema.new(upstreams))
local Targets = assert(Schema.new(targets))
Expand All @@ -14,6 +29,7 @@ end


describe("targets", function()
setup_global_env()
describe("targets.target", function()
it("validates", function()
local upstream = { id = utils.uuid() }
Expand Down
18 changes: 15 additions & 3 deletions spec/01-unit/01-db/01-schema/09-upstreams_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@ local Schema = require "kong.db.schema"
local certificates = require "kong.db.schema.entities.certificates"
local upstreams = require "kong.db.schema.entities.upstreams"

local function setup_global_env()
_G.kong = _G.kong or {}
_G.kong.log = _G.kong.log or {
debug = function(msg)
ngx.log(ngx.DEBUG, msg)
end,
error = function(msg)
ngx.log(ngx.ERR, msg)
end,
warn = function (msg)
ngx.log(ngx.WARN, msg)
end
}
end

assert(Schema.new(certificates))
local Upstreams = Schema.new(upstreams)
Expand All @@ -18,7 +32,7 @@ describe("load upstreams", function()
.. ("%x"):rep(4) .. "%-" .. ("%x"):rep(4) .. "%-"
.. ("%x"):rep(12) .. "$"


setup_global_env()
it("validates a valid load upstream", function()
local u = {
id = a_valid_uuid,
Expand Down Expand Up @@ -225,7 +239,6 @@ describe("load upstreams", function()
assert.not_nil(errs.hash_fallback_uri_capture)
end)


it("produces set use_srv_name flag", function()
local u = {
name = "www.example.com",
Expand All @@ -239,7 +252,6 @@ describe("load upstreams", function()
assert.same(u.use_srv_name, true)
end)


it("produces defaults", function()
local u = {
name = "www.example.com",
Expand Down
Loading

1 comment on commit ecbf062

@khcp-gha-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bazel Build

Docker image available kong/kong:ecbf062bad7971062db930cb2cd2d50b11b68e9a
Artifacts available https://github.com/Kong/kong/actions/runs/4427926813

Please sign in to comment.