diff --git a/src/box/lua/load_cfg.lua b/src/box/lua/load_cfg.lua index 5fc6c6729b0b2cc28b82bb79382f55a79f9e0682..b2be310c1541d9e21d3d38f2dc7fd7358c60a79d 100644 --- a/src/box/lua/load_cfg.lua +++ b/src/box/lua/load_cfg.lua @@ -1274,6 +1274,11 @@ box.internal.cfg = setmetatable({}, { end, }) +-- Expose list of all available box.cfg() options and their +-- default values for testing purposes. +box.internal.template_cfg = template_cfg +box.internal.default_cfg = default_cfg + -- gh-810: -- hack luajit default cpath -- commented out because we fixed luajit to build properly, see diff --git a/test/config-luatest/instance_config_schema_test.lua b/test/config-luatest/instance_config_schema_test.lua index e3fb42443dbe195b495d501aff5ac738f6621030..469b492ffb766444a46ea691ed61b9115b7209a9 100644 --- a/test/config-luatest/instance_config_schema_test.lua +++ b/test/config-luatest/instance_config_schema_test.lua @@ -1,3 +1,5 @@ +local fun = require('fun') +local log = require('log') local t = require('luatest') local instance_config = require('internal.config.instance_config') @@ -989,3 +991,181 @@ g.test_app = function() local res = instance_config:apply_default({}).app t.assert_equals(res, nil) end + +-- Whether all box.cfg() options can be set using the declarative +-- configuration. +-- +-- The test also verifies that all the box_cfg annotations are +-- point to existing box.cfg() options. +-- +-- And also compares default values in the schema against ones set +-- by the box.cfg() call. +g.test_box_cfg_coverage = function() + -- There are box.cfg() options that are set by the box_cfg + -- applier on its own and cannot be set directly in the + -- declarative config. + -- + -- Also, there are some options to be added into the declarative + -- config soon. + local ignore = { + -- Handled by box_cfg applier without the box_cfg schema + -- node annotation. + instance_name = true, + replicaset_name = true, + cluster_name = true, + log = true, + + -- Controlled by the leader and database.mode options, + -- handled by the box_cfg applier. + read_only = true, + + -- Deliberately moved out of the config, because the + -- box.cfg() options is deprecated. + replication_connect_quorum = true, + + -- Moved to the CLI options (see gh-8876). + force_recovery = true, + + -- TODO: Will be added in the scope of gh-8861. + audit_log = true, + audit_nonblock = true, + audit_format = true, + audit_filter = true, + + -- TODO: Will be added in the scope of gh-8861. + auth_type = true, + auth_delay = true, + disable_guest = true, + password_lifetime_days = true, + password_min_length = true, + password_enforce_uppercase = true, + password_enforce_lowercase = true, + password_enforce_digits = true, + password_enforce_specialchars = true, + password_history_length = true, + + -- TODO: Will be added in the scope of gh-8861. + feedback_enabled = true, + feedback_crashinfo = true, + feedback_host = true, + feedback_interval = true, + feedback_send_metrics = true, + feedback_metrics_collect_interval = true, + feedback_metrics_limit = true, + + -- TODO: Will be added in the scope of gh-8861. + flightrec_enabled = true, + flightrec_logs_size = true, + flightrec_logs_max_msg_size = true, + flightrec_logs_log_level = true, + flightrec_metrics_interval = true, + flightrec_metrics_period = true, + flightrec_requests_size = true, + flightrec_requests_max_req_size = true, + flightrec_requests_max_res_size = true, + + -- TODO: Will be added in the scope of gh-8861. + metrics = true, + + -- TODO: Will be added in the scope of gh-8861. + bootstrap_leader = true, + memtx_sort_threads = true, + } + + -- There are options, where defaults are changed deliberately. + local ignore_default = { + -- box.cfg.log_nonblock is set to nil by default, but + -- actually it means false. + log_nonblock = true, + + -- Adjusted to use {{ instance_name }}. + custom_proc_title = true, + memtx_dir = true, + pid_file = true, + wal_dir = true, + vinyl_dir = true, + + -- The effective default is determined depending of + -- the replication.failover option. + election_mode = true, + } + + local log_prefix = 'test_box_cfg_coverage' + local ljust = 33 + + -- Collect box_cfg annotations from the schema. + local box_cfg_options_in_schema = instance_config:pairs():filter(function(w) + -- Skip EE options on CE. + if w.schema.enterprise_edition and not is_enterprise then + return false + end + return w.schema.box_cfg ~= nil + end):map(function(w) + return w.schema.box_cfg, { + path = table.concat(w.path, '.'), + default = w.schema.default, + } + end):tomap() + + -- <schema>:pairs() iterates over scalar, array and map schema + -- nodes. It expands records on its own and don't add them + -- into the iterator. + -- + -- However, wal.ext schema node is a record. Let's add it + -- manually. + local records_to_traverse = { + ['wal.ext'] = instance_config.schema.fields.wal.fields.ext, + } + fun.iter(records_to_traverse):each(function(path, schema) + if schema.enterprise_edition and not is_enterprise then + return + end + if schema.box_cfg ~= nil then + box_cfg_options_in_schema[schema.box_cfg] = { + path = path, + default = schema.default, + } + end + end) + + -- Verify box_cfg annotations in the instance config schema: + -- they must correspond to existing box.cfg() options. + for option_name, in_schema in pairs(box_cfg_options_in_schema) do + log.info('%s: verify existence of box.cfg.%s that is pointed by %s...', + log_prefix, option_name:ljust(ljust), in_schema.path) + t.assert(box.internal.template_cfg[option_name] ~= nil, + ('%s points to non-existing box.cfg.%s'):format(in_schema.path, + option_name)) + end + + -- Verify that all box.cfg() option are present in the + -- instance config schema (with known exceptions). + for option_name, _ in pairs(box.internal.template_cfg) do + if ignore[option_name] then + log.info('%s: box.cfg.%s skip', log_prefix, + option_name:ljust(ljust)) + else + log.info('%s: box.cfg.%s find...', log_prefix, + option_name:ljust(ljust)) + t.assert(box_cfg_options_in_schema[option_name] ~= nil, + ('box.cfg.%s is not found in the instance config ' .. + 'schema'):format(option_name)) + end + end + + -- Compare defaults. + for option_name, in_schema in pairs(box_cfg_options_in_schema) do + if not ignore_default[option_name] then + t.assert_equals(in_schema.default, + box.internal.default_cfg[option_name], + ('defaults for box.cfg.%s are different'):format(option_name)) + end + end + for option_name, _ in pairs(box.internal.template_cfg) do + if not ignore[option_name] and not ignore_default[option_name] then + t.assert_equals(box_cfg_options_in_schema[option_name].default, + box.internal.default_cfg[option_name], + ('defaults for box.cfg.%s are different'):format(option_name)) + end + end +end