diff --git a/CHANGELOG.md b/CHANGELOG.md index 49ed8e7e90a9..db6f5499c5ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/autodoc/admin-api/data/admin-api.lua b/autodoc/admin-api/data/admin-api.lua index b1fd155763ac..74e77512b32f 100644 --- a/autodoc/admin-api/data/admin-api.lua +++ b/autodoc/admin-api/data/admin-api.lua @@ -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. @@ -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-----...", @@ -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 = [[ @@ -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.]] }, @@ -2177,6 +2181,7 @@ return { fields = { id = { skip = true }, created_at = { skip = true }, + updated_at = { skip = true }, upstream = { skip = true }, target = { description = [[ diff --git a/kong-3.2.1-0.rockspec b/kong-3.2.1-0.rockspec index 218bd40b405b..2250fdbdedba 100644 --- a/kong-3.2.1-0.rockspec +++ b/kong-3.2.1-0.rockspec @@ -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", diff --git a/kong/clustering/compat/init.lua b/kong/clustering/compat/init.lua index dbb2a16c6bc4..736a6dee4ad0 100644 --- a/kong/clustering/compat/init.lua +++ b/kong/clustering/compat/init.lua @@ -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 diff --git a/kong/db/migrations/core/019_320_to_330.lua b/kong/db/migrations/core/019_320_to_330.lua new file mode 100644 index 000000000000..f512b146ac2a --- /dev/null +++ b/kong/db/migrations/core/019_320_to_330.lua @@ -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; + ]] + }, +} diff --git a/kong/db/migrations/core/init.lua b/kong/db/migrations/core/init.lua index d4834db2f27e..6c6787c54ae8 100644 --- a/kong/db/migrations/core/init.lua +++ b/kong/db/migrations/core/init.lua @@ -16,4 +16,5 @@ return { "016_280_to_300", "017_300_to_310", "018_310_to_320", + "019_320_to_330", } diff --git a/kong/db/schema/entities/ca_certificates.lua b/kong/db/schema/entities/ca_certificates.lua index ce58f55d739b..f87cd35722be 100644 --- a/kong/db/schema/entities/ca_certificates.lua +++ b/kong/db/schema/entities/ca_certificates.lua @@ -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 }, diff --git a/kong/db/schema/entities/certificates.lua b/kong/db/schema/entities/certificates.lua index dbe2d4a4024c..9e9127a20109 100644 --- a/kong/db/schema/entities/certificates.lua +++ b/kong/db/schema/entities/certificates.lua @@ -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 }, }, diff --git a/kong/db/schema/entities/clustering_data_planes.lua b/kong/db/schema/entities/clustering_data_planes.lua index a4fe01bf0aef..a1e00372f7eb 100644 --- a/kong/db/schema/entities/clustering_data_planes.lua +++ b/kong/db/schema/entities/clustering_data_planes.lua @@ -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, } }, diff --git a/kong/db/schema/entities/consumers.lua b/kong/db/schema/entities/consumers.lua index 30f23ae069e5..e53345b1fc70 100644 --- a/kong/db/schema/entities/consumers.lua +++ b/kong/db/schema/entities/consumers.lua @@ -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 }, diff --git a/kong/db/schema/entities/plugins.lua b/kong/db/schema/entities/plugins.lua index 2ac2777c92d6..04efd4a73338 100644 --- a/kong/db/schema/entities/plugins.lua +++ b/kong/db/schema/entities/plugins.lua @@ -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", }, }, diff --git a/kong/db/schema/entities/snis.lua b/kong/db/schema/entities/snis.lua index e1ca7c67b4c5..2f4b8ad39925 100644 --- a/kong/db/schema/entities/snis.lua +++ b/kong/db/schema/entities/snis.lua @@ -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 }, }, }, diff --git a/kong/db/schema/entities/targets.lua b/kong/db/schema/entities/targets.lua index 64b39e85fbca..fed599ba39a5 100644 --- a/kong/db/schema/entities/targets.lua +++ b/kong/db/schema/entities/targets.lua @@ -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 }, }, }, diff --git a/kong/db/schema/entities/upstreams.lua b/kong/db/schema/entities/upstreams.lua index c4d02fbad995..1a1840812870 100644 --- a/kong/db/schema/entities/upstreams.lua +++ b/kong/db/schema/entities/upstreams.lua @@ -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", diff --git a/kong/db/schema/entities/workspaces.lua b/kong/db/schema/entities/workspaces.lua index 647571c8e4fb..79f45b10d5bc 100644 --- a/kong/db/schema/entities/workspaces.lua +++ b/kong/db/schema/entities/workspaces.lua @@ -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 = {} } }, } diff --git a/kong/db/schema/init.lua b/kong/db/schema/init.lua index 97338b8965bb..6b53d22d9c0a 100644 --- a/kong/db/schema/init.lua +++ b/kong/db/schema/init.lua @@ -1110,7 +1110,6 @@ 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) @@ -1118,8 +1117,8 @@ validate_fields = function(self, input) 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 @@ -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 diff --git a/spec/01-unit/01-db/01-schema/05-services_spec.lua b/spec/01-unit/01-db/01-schema/05-services_spec.lua index 688901b835ec..85e823b527b7 100644 --- a/spec/01-unit/01-db/01-schema/05-services_spec.lua +++ b/spec/01-unit/01-db/01-schema/05-services_spec.lua @@ -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) .. "%-" diff --git a/spec/01-unit/01-db/01-schema/06-routes_spec.lua b/spec/01-unit/01-db/01-schema/06-routes_spec.lua index b104c3b78086..7f38d9b67a50 100644 --- a/spec/01-unit/01-db/01-schema/06-routes_spec.lua +++ b/spec/01-unit/01-db/01-schema/06-routes_spec.lua @@ -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 = { @@ -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 = { @@ -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 = { @@ -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 = { diff --git a/spec/01-unit/01-db/01-schema/08-targets_spec.lua b/spec/01-unit/01-db/01-schema/08-targets_spec.lua index 915ab26e335f..fb08ade2a428 100644 --- a/spec/01-unit/01-db/01-schema/08-targets_spec.lua +++ b/spec/01-unit/01-db/01-schema/08-targets_spec.lua @@ -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)) @@ -14,6 +29,7 @@ end describe("targets", function() + setup_global_env() describe("targets.target", function() it("validates", function() local upstream = { id = utils.uuid() } diff --git a/spec/01-unit/01-db/01-schema/09-upstreams_spec.lua b/spec/01-unit/01-db/01-schema/09-upstreams_spec.lua index 3bbea2ffb9bc..8b973f0ebdce 100644 --- a/spec/01-unit/01-db/01-schema/09-upstreams_spec.lua +++ b/spec/01-unit/01-db/01-schema/09-upstreams_spec.lua @@ -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) @@ -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, @@ -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", @@ -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", diff --git a/spec/01-unit/01-db/01-schema/11-declarative_config/03-flatten_spec.lua b/spec/01-unit/01-db/01-schema/11-declarative_config/03-flatten_spec.lua index 4dbbc3215961..097b7c3fd6aa 100644 --- a/spec/01-unit/01-db/01-schema/11-declarative_config/03-flatten_spec.lua +++ b/spec/01-unit/01-db/01-schema/11-declarative_config/03-flatten_spec.lua @@ -275,6 +275,7 @@ describe("declarative config: flatten", function() id = "UUID", tags = null, created_at = 1234567890, + updated_at = 1234567890, consumer = null, service = null, route = null, @@ -299,6 +300,7 @@ describe("declarative config: flatten", function() id = "UUID", tags = null, created_at = 1234567890, + updated_at = 1234567890, consumer = null, service = null, route = null, @@ -369,6 +371,7 @@ describe("declarative config: flatten", function() { tags = null, created_at = 1234567890, + updated_at = 1234567890, custom_id = null, id = "UUID", username = "my-consumer", @@ -393,6 +396,7 @@ describe("declarative config: flatten", function() id = "UUID" }, created_at = 1234567890, + updated_at = 1234567890, enabled = true, id = "UUID", name = "http-log", @@ -416,6 +420,7 @@ describe("declarative config: flatten", function() }, consumer = null, created_at = 1234567890, + updated_at = 1234567890, enabled = true, id = "UUID", name = "key-auth", @@ -554,6 +559,7 @@ describe("declarative config: flatten", function() }, consumer = null, created_at = 1234567890, + updated_at = 1234567890, enabled = true, id = "UUID", name = "basic-auth", @@ -579,6 +585,7 @@ describe("declarative config: flatten", function() }, consumer = null, created_at = 1234567890, + updated_at = 1234567890, enabled = true, id = "UUID", name = "http-log", @@ -601,6 +608,7 @@ describe("declarative config: flatten", function() }, consumer = null, created_at = 1234567890, + updated_at = 1234567890, enabled = true, id = "UUID", name = "key-auth", @@ -623,6 +631,7 @@ describe("declarative config: flatten", function() }, consumer = null, created_at = 1234567890, + updated_at = 1234567890, enabled = true, id = "UUID", name = "tcp-log", @@ -1056,6 +1065,7 @@ describe("declarative config: flatten", function() }, consumer = null, created_at = 1234567890, + updated_at = 1234567890, enabled = true, id = "UUID", name = "basic-auth", @@ -1081,6 +1091,7 @@ describe("declarative config: flatten", function() }, consumer = null, created_at = 1234567890, + updated_at = 1234567890, enabled = true, id = "UUID", name = "http-log", @@ -1103,6 +1114,7 @@ describe("declarative config: flatten", function() }, consumer = null, created_at = 1234567890, + updated_at = 1234567890, enabled = true, id = "UUID", name = "key-auth", @@ -1125,6 +1137,7 @@ describe("declarative config: flatten", function() }, consumer = null, created_at = 1234567890, + updated_at = 1234567890, enabled = true, id = "UUID", name = "tcp-log", @@ -1247,6 +1260,7 @@ describe("declarative config: flatten", function() targets = { { created_at = 1234567890, + updated_at = 1234567890, id = "UUID", tags = null, target = '127.0.0.1:6661', @@ -1255,6 +1269,7 @@ describe("declarative config: flatten", function() }, { created_at = 1234567890, + updated_at = 1234567890, id = "UUID", tags = null, target = '127.0.0.1:6661', @@ -1300,6 +1315,7 @@ describe("declarative config: flatten", function() username = 'consumer', custom_id = null, created_at = 1234567890, + updated_at = 1234567890, tags = null, }, }, @@ -1323,6 +1339,7 @@ describe("declarative config: flatten", function() username = 'consumer', custom_id = null, created_at = 1234567890, + updated_at = 1234567890, tags = null, }, }, @@ -1360,6 +1377,7 @@ describe("declarative config: flatten", function() username = 'consumer', custom_id = null, created_at = 1234567890, + updated_at = 1234567890, tags = null, }, }, @@ -1426,6 +1444,7 @@ describe("declarative config: flatten", function() username = 'consumer', custom_id = null, created_at = 1234567890, + updated_at = 1234567890, tags = null, }, }, @@ -1492,6 +1511,7 @@ describe("declarative config: flatten", function() username = 'consumer', custom_id = null, created_at = 1234567890, + updated_at = 1234567890, tags = null, }, }, @@ -1557,6 +1577,7 @@ describe("declarative config: flatten", function() username = 'consumer', custom_id = null, created_at = 1234567890, + updated_at = 1234567890, tags = null, }, }, @@ -1716,6 +1737,7 @@ describe("declarative config: flatten", function() assert.same({ consumers = { { created_at = 1234567890, + updated_at = 1234567890, custom_id = null, id = "UUID", tags = null, @@ -1756,6 +1778,7 @@ describe("declarative config: flatten", function() assert.same(helpers.deep_sort{ targets = { { created_at = 1234567890, + updated_at = 1234567890, id = "UUID", tags = null, target = "127.0.0.1:6661", @@ -1765,6 +1788,7 @@ describe("declarative config: flatten", function() weight = 1, }, { created_at = 1234567890, + updated_at = 1234567890, id = "UUID", tags = null, target = "127.0.0.1:6661", @@ -1777,6 +1801,7 @@ describe("declarative config: flatten", function() algorithm = "round-robin", client_certificate = null, created_at = 1234567890, + updated_at = 1234567890, hash_fallback = "none", hash_fallback_header = null, hash_on = "none", @@ -1834,6 +1859,7 @@ describe("declarative config: flatten", function() algorithm = "round-robin", client_certificate = null, created_at = 1234567890, + updated_at = 1234567890, hash_fallback = "none", hash_fallback_header = null, hash_on = "none", @@ -1907,6 +1933,7 @@ describe("declarative config: flatten", function() assert.same({ consumers = { { created_at = 1234567890, + updated_at = 1234567890, custom_id = null, id = "UUID", tags = null, @@ -1933,6 +1960,7 @@ describe("declarative config: flatten", function() assert.same({ consumers = { { created_at = 1234567890, + updated_at = 1234567890, custom_id = null, id = "UUID", tags = null, diff --git a/spec/01-unit/01-db/01-schema/11-snis_spec.lua b/spec/01-unit/01-db/01-schema/11-snis_spec.lua index 1ae8751ef36d..a5fdfa05ccaa 100644 --- a/spec/01-unit/01-db/01-schema/11-snis_spec.lua +++ b/spec/01-unit/01-db/01-schema/11-snis_spec.lua @@ -6,6 +6,21 @@ local utils = require "kong.tools.utils" Schema.new(certificates) local Snis = assert(Schema.new(snis)) +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 validate(b) return Snis:validate(Snis:process_auto_fields(b, "insert")) end @@ -14,6 +29,8 @@ end describe("snis", function() local certificate = { id = utils.uuid() } + setup_global_env() + describe("name", function() it("accepts a hostname", function() local names = { "valid.name", "foo.valid.name", "bar.foo.valid.name" } diff --git a/spec/02-integration/02-cmd/11-config_spec.lua b/spec/02-integration/02-cmd/11-config_spec.lua index 351448b26bef..acc8e37f0ad4 100644 --- a/spec/02-integration/02-cmd/11-config_spec.lua +++ b/spec/02-integration/02-cmd/11-config_spec.lua @@ -134,6 +134,7 @@ describe("kong config", function() local body = assert.res_status(200, res) local json = cjson.decode(body) json.created_at = nil + json.updated_at = nil json.protocols = nil assert.same({ name = "correlation-id", diff --git a/spec/02-integration/03-db/03-plugins_spec.lua b/spec/02-integration/03-db/03-plugins_spec.lua index 1f98f4e07ca2..474bfb15dfcd 100644 --- a/spec/02-integration/03-db/03-plugins_spec.lua +++ b/spec/02-integration/03-db/03-plugins_spec.lua @@ -51,6 +51,7 @@ for _, strategy in helpers.each_strategy() do assert.is_number(plugin.created_at) plugin.id = nil plugin.created_at = nil + plugin.updated_at = nil assert.same({ config = { @@ -125,6 +126,7 @@ for _, strategy in helpers.each_strategy() do assert.is_number(plugin.created_at) plugin.id = nil plugin.created_at = nil + plugin.updated_at = nil assert.same({ config = { diff --git a/spec/02-integration/04-admin_api/03-consumers_routes_spec.lua b/spec/02-integration/04-admin_api/03-consumers_routes_spec.lua index 6595d846d21e..1677bec346d4 100644 --- a/spec/02-integration/04-admin_api/03-consumers_routes_spec.lua +++ b/spec/02-integration/04-admin_api/03-consumers_routes_spec.lua @@ -350,6 +350,7 @@ describe("Admin API (#" .. strategy .. "): ", function() return function() local consumer = bp.consumers:insert() local new_username = gensym() + ngx.sleep(1) local res = assert(client:send { method = "PATCH", path = "/consumers/" .. consumer.id, @@ -362,6 +363,7 @@ describe("Admin API (#" .. strategy .. "): ", function() local json = cjson.decode(body) assert.equal(new_username, json.username) assert.equal(consumer.id, json.id) + assert.truthy(consumer.updated_at < json.updated_at) local in_db = assert(db.consumers:select({ id = consumer.id }, { nulls = true })) assert.same(json, in_db) diff --git a/spec/02-integration/04-admin_api/04-plugins_routes_spec.lua b/spec/02-integration/04-admin_api/04-plugins_routes_spec.lua index adb94d764545..31a781d12c8d 100644 --- a/spec/02-integration/04-admin_api/04-plugins_routes_spec.lua +++ b/spec/02-integration/04-admin_api/04-plugins_routes_spec.lua @@ -298,6 +298,7 @@ for _, strategy in helpers.each_strategy() do plugin.enabled = not plugin.enabled plugin.created_at = nil + plugin.updated_at = nil local res = assert(client:send { method = "PATCH", diff --git a/spec/02-integration/04-admin_api/06-certificates_routes_spec.lua b/spec/02-integration/04-admin_api/06-certificates_routes_spec.lua index 796a07a61800..2e6f6fda0a3a 100644 --- a/spec/02-integration/04-admin_api/06-certificates_routes_spec.lua +++ b/spec/02-integration/04-admin_api/06-certificates_routes_spec.lua @@ -424,6 +424,7 @@ describe("Admin API: #" .. strategy, function() it("upserts if found", function() local certificate = add_certificate() + ngx.sleep(1) local res = client:put("/certificates/" .. certificate.id, { body = { cert = ssl_fixtures.cert_alt, key = ssl_fixtures.key_alt }, headers = { ["Content-Type"] = "application/json" }, @@ -436,6 +437,7 @@ describe("Admin API: #" .. strategy, function() assert.equal(cjson.null, json.cert_alt) assert.equal(cjson.null, json.key_alt) assert.same({}, json.snis) + assert.truthy(certificate.updated_at < json.updated_at) json.snis = nil @@ -654,6 +656,9 @@ describe("Admin API: #" .. strategy, function() local body = assert.res_status(200, res) local json = cjson.decode(body) + certificate.updated_at = nil + json.updated_at = nil + assert.same(certificate, json) end end) @@ -669,7 +674,8 @@ describe("Admin API: #" .. strategy, function() local body = assert.res_status(200, res) local json = cjson.decode(body) - + json.updated_at = nil + certificate.updated_at = nil assert.same(certificate, json) end end) @@ -752,7 +758,8 @@ describe("Admin API: #" .. strategy, function() local body = assert.res_status(200, res) local json = cjson.decode(body) - + json.updated_at = nil + certificate.updated_at = nil assert.same(certificate, json) end end) @@ -768,7 +775,8 @@ describe("Admin API: #" .. strategy, function() local body = assert.res_status(200, res) local json = cjson.decode(body) - + json.updated_at = nil + certificate.updated_at = nil assert.same(certificate, json) end end) @@ -885,6 +893,8 @@ describe("Admin API: #" .. strategy, function() -- make sure we did not add any certificate or sni local json = get_certificates() + json.updated_at = nil + json_before.updated_at = nil assert.same(json_before, json) end) @@ -932,6 +942,8 @@ describe("Admin API: #" .. strategy, function() -- make sure we did not add any certificate or sni local json = get_certificates() + json_before.updated_at = nil + json.update_at = nil assert.same(json_before, json) end) @@ -1214,6 +1226,7 @@ describe("Admin API: #" .. strategy, function() }) local n2 = get_name() + ngx.sleep(1) local res = client:put("/snis/" .. sni.id, { body = { name = n2, @@ -1228,6 +1241,7 @@ describe("Admin API: #" .. strategy, function() local in_db = assert(db.snis:select({ id = sni.id }, { nulls = true })) assert.same(json, in_db) + assert.truthy(sni.updated_at < json.updated_at) end) it("handles invalid input", function() diff --git a/spec/02-integration/04-admin_api/08-targets_routes_spec.lua b/spec/02-integration/04-admin_api/08-targets_routes_spec.lua index b05caf8d9ff8..e3e0affd5901 100644 --- a/spec/02-integration/04-admin_api/08-targets_routes_spec.lua +++ b/spec/02-integration/04-admin_api/08-targets_routes_spec.lua @@ -412,8 +412,12 @@ describe("Admin API #" .. strategy, function() end) describe("with healthchecks on", function() + local checked before_each(function() - local status = client_send({ + if checked == nil then + ngx.sleep(1) + end + local status, body = client_send({ method = "PATCH", path = "/upstreams/" .. upstream.name, headers = { @@ -435,6 +439,11 @@ describe("Admin API #" .. strategy, function() } }) assert.same(200, status) + if checked == nil then + local json = assert(cjson.decode(body)) + assert.truthy(upstream.updated_at < json.updated_at) + checked = true + end end) it("returns DNS_ERROR if DNS cannot be resolved", function() @@ -821,6 +830,8 @@ describe("Admin API #" .. strategy, function() assert.response(res).has.status(200) local json = assert.response(res).has.jsonbody() json.tags = nil + json.updated_at = nil + target.updated_at = nil assert.same(target, json) end) end) @@ -847,6 +858,7 @@ describe("Admin API #" .. strategy, function() end) it("is allowed and works", function() + ngx.sleep(1) local res = client:patch("/upstreams/" .. upstream.name .. "/targets/" .. target.target, { body = { weight = 659, @@ -858,6 +870,7 @@ describe("Admin API #" .. strategy, function() assert.is_string(json.id) assert.are.equal(target.target, json.target) assert.are.equal(659, json.weight) + assert.truthy(target.updated_at < json.updated_at) local res = assert(client:send { method = "GET", @@ -906,6 +919,8 @@ describe("Admin API #" .. strategy, function() assert.response(res).has.status(200) local json = assert.response(res).has.jsonbody() json.tags = nil + json.updated_at = nil + target.updated_at = nil assert.same(target, json) end) diff --git a/spec/02-integration/04-admin_api/10-services_routes_spec.lua b/spec/02-integration/04-admin_api/10-services_routes_spec.lua index a6a6bc680e95..8b68245b4132 100644 --- a/spec/02-integration/04-admin_api/10-services_routes_spec.lua +++ b/spec/02-integration/04-admin_api/10-services_routes_spec.lua @@ -691,6 +691,7 @@ for _, strategy in helpers.each_strategy() do plugin.enabled = not plugin.enabled plugin.created_at = nil + plugin.updated_at = nil local res = assert(client:send { method = "PATCH", diff --git a/spec/02-integration/04-admin_api/12-plugins-conf_spec.lua b/spec/02-integration/04-admin_api/12-plugins-conf_spec.lua index 77c3797bd388..ea36a8ebdb08 100644 --- a/spec/02-integration/04-admin_api/12-plugins-conf_spec.lua +++ b/spec/02-integration/04-admin_api/12-plugins-conf_spec.lua @@ -112,6 +112,7 @@ describe("Plugins conf property" , function() describe("with a plugin list in conf, admin API" , function() local client + local basic_auth lazy_setup(function() assert(helpers.start_kong({ plugins = "key-auth, basic-auth" @@ -143,7 +144,8 @@ describe("Plugins conf property" , function() }, headers = { ["Content-Type"] = "application/json" } }) - assert.res_status(201 , res) + local body = assert.res_status(201 , res) + basic_auth = assert(cjson.decode(body)) end) it("returns 400 for plugins not included in the list" , function() local res = assert(client:send { @@ -156,6 +158,23 @@ describe("Plugins conf property" , function() }) assert.res_status(400 , res) end) + + it("update updated_at after config changed", function() + ngx.sleep(1) + local res = assert(client:send { + method = "PATCH", + path = "/plugins/" .. basic_auth.id, + body = { + config = { + hide_credentials = true + } + }, + headers = { ["Content-Type"] = "application/json" } + }) + local body = assert.res_status(200 , res) + local json = assert(cjson.decode(body)) + assert.truthy(basic_auth.updated_at < json.updated_at) + end) end) end) diff --git a/spec/02-integration/04-admin_api/15-off_spec.lua b/spec/02-integration/04-admin_api/15-off_spec.lua index 32929c653113..dc2309220410 100644 --- a/spec/02-integration/04-admin_api/15-off_spec.lua +++ b/spec/02-integration/04-admin_api/15-off_spec.lua @@ -721,6 +721,7 @@ describe("Admin API #off", function() consumers = { { id = "d885e256-1abe-5e24-80b6-8f68fe59ea8e", created_at = 1566863706, + updated_at = config.consumers[1].updated_at, username = "bobo", custom_id = lyaml.null, tags = lyaml.null, diff --git a/spec/02-integration/04-admin_api/16-ca_certificates_routes_spec.lua b/spec/02-integration/04-admin_api/16-ca_certificates_routes_spec.lua index fca69f4f89dd..10d81b88a3b3 100644 --- a/spec/02-integration/04-admin_api/16-ca_certificates_routes_spec.lua +++ b/spec/02-integration/04-admin_api/16-ca_certificates_routes_spec.lua @@ -196,6 +196,7 @@ for _, strategy in helpers.each_strategy() do end) it("works", function() + ngx.sleep(1) local res = client:patch("/ca_certificates/" .. ca.id, { body = { cert = ssl_fixtures.cert_ca, @@ -203,7 +204,10 @@ for _, strategy in helpers.each_strategy() do headers = { ["Content-Type"] = "application/json" }, }) - assert.res_status(200, res) + local body = assert.res_status(200, res) + local new_ca = assert(cjson.decode(body)) + + assert.truthy(ca.updated_at < new_ca.updated_at) end) end) diff --git a/spec/05-migration/db/migrations/core/019_320_to_330_spec.lua b/spec/05-migration/db/migrations/core/019_320_to_330_spec.lua new file mode 100644 index 000000000000..cf6e9e6f7ab6 --- /dev/null +++ b/spec/05-migration/db/migrations/core/019_320_to_330_spec.lua @@ -0,0 +1,15 @@ +local uh = require "spec/upgrade_helpers" + +describe("database migration", function() + uh.old_after_up("has created the expected new columns", function() + assert.table_has_column("ca_certificates", "updated_at", "timestamp with time zone", "timestamp") + assert.table_has_column("certificates", "updated_at", "timestamp with time zone", "timestamp") + assert.table_has_column("consumers", "updated_at", "timestamp with time zone", "timestamp") + assert.table_has_column("plugins", "updated_at", "timestamp with time zone", "timestamp") + assert.table_has_column("snis", "updated_at", "timestamp with time zone", "timestamp") + assert.table_has_column("targets", "updated_at", "timestamp with time zone", "timestamp") + assert.table_has_column("upstreams", "updated_at", "timestamp with time zone", "timestamp") + assert.table_has_column("workspaces", "updated_at", "timestamp with time zone", "timestamp") + assert.table_has_column("clustering_data_planes", "updated_at", "timestamp with time zone", "timestamp") + end) +end)