Skip to content
Snippets Groups Projects
Unverified Commit 7ccdacd2 authored by Matthias Schiffer's avatar Matthias Schiffer
Browse files

treewide: rework check_site_lib.lua

In addition to significant internal differences in check_site_lib.lua (in
particular unifying error handling to a single place for the upcoming
multi-domain support), this changes the way fields are addressed in site
check scripts: rather than providing a string like 'next_node.ip6', the
path is passed as an array {'next_node', 'ip6'}.

Other changes in site check scripts:
* need_array and need_table now pass the full path to the sub fields to the
subcheck instead of the key and value
* Any check referring to a field inside a table implies that all higher
levels must be tables if they exist: a check for {'next_node', 'ip6'} adds
an implicit (optional) check for {'next_node'}, which allows to remove many
explicit checks for such tables
parent 414dfa81
No related branches found
No related tags found
No related merge requests found
Showing
with 199 additions and 232 deletions
need_string_array(in_site('authorized_keys')) need_string_array(in_site({'authorized_keys'}))
need_string(in_site('autoupdater.branch')) need_string(in_site({'autoupdater', 'branch'}))
local function check_branch(k, _) need_table({'autoupdater', 'branches'}, function(branch)
assert_uci_name(k) need_alphanumeric_key(branch)
local prefix = string.format('autoupdater.branches[%q].', k) need_string(in_site(extend(branch, {'name'})))
need_string_array_match(extend(branch, {'mirrors'}), '^http://')
need_string(in_site(prefix .. 'name')) need_number(in_site(extend(branch, {'good_signatures'})))
need_string_array_match(prefix .. 'mirrors', '^http://') need_string_array_match(in_site(extend(branch, {'pubkeys'})), '^%x+$')
need_number(in_site(prefix .. 'good_signatures')) end)
need_string_array_match(in_site(prefix .. 'pubkeys'), '^%x+$')
end
need_table('autoupdater.branches', check_branch)
need_string_match(in_domain('next_node.mac'), '^%x[02468aAcCeE]:%x%x:%x%x:%x%x:%x%x:%x%x$', false) need_string_match(in_domain({'next_node', 'mac'}), '^%x[02468aAcCeE]:%x%x:%x%x:%x%x:%x%x:%x%x$', false)
if need_string_match(in_domain('next_node.ip4'), '^%d+.%d+.%d+.%d+$', false) then if need_string_match(in_domain({'next_node', 'ip4'}), '^%d+.%d+.%d+.%d+$', false) then
need_string_match(in_domain('prefix4'), '^%d+.%d+.%d+.%d+/%d+$') need_string_match(in_domain({'prefix4'}), '^%d+.%d+.%d+.%d+/%d+$')
end end
need_string_match(in_domain('next_node.ip6'), '^[%x:]+$', false) need_string_match(in_domain({'next_node', 'ip6'}), '^[%x:]+$', false)
for _, config in ipairs({'wifi24', 'wifi5'}) do for _, config in ipairs({'wifi24', 'wifi5'}) do
if need_table(config .. '.ap', nil, false) then if need_table({config}, nil, false) then
need_string(in_domain(config .. '.ap.ssid')) need_string(in_domain({config, 'ap', 'ssid'}))
need_boolean(config .. '.ap.disabled', false) need_boolean({config, 'ap', 'disabled'}, false)
end end
end end
if need_table(in_site('config_mode'), nil, false) and need_table(in_site('config_mode.owner'), nil, false) then need_boolean(in_site({'config_mode', 'owner', 'obligatory'}), false)
need_boolean(in_site('config_mode.owner.obligatory'), false)
end
if need_table(in_site('config_mode'), nil, false) and need_table(in_site('config_mode.geo_location'), nil, false) then need_boolean(in_site({'config_mode', 'geo_location', 'show_altitude'}), false)
need_boolean(in_site('config_mode.geo_location.show_altitude'), false)
end
need_string(in_site('site_code')) need_string(in_site({'site_code'}))
need_string(in_site('site_name')) need_string(in_site({'site_name'}))
need_string_match(in_domain('domain_seed'), '^' .. ('%x'):rep(64) .. '$') need_string_match(in_domain({'domain_seed'}), '^' .. ('%x'):rep(64) .. '$')
if need_table('opkg', nil, false) then need_string({'opkg', 'lede'}, false)
need_string('opkg.lede', false) need_table({'opkg', 'extra'}, function(extra_repo)
need_alphanumeric_key(extra_repo)
need_string(extra_repo)
end, false)
function check_repo(k, _) need_string(in_site({'hostname_prefix'}), false)
-- this is not actually a uci name, but using the same naming rules here is fine need_string(in_site({'timezone'}))
assert_uci_name(k)
local path = string.format('opkg.extra[%q]', k) need_string_array({'ntp_servers'}, false)
need_string(path)
end
need_table('opkg.extra', check_repo, false)
end
need_string(in_site('hostname_prefix'), false)
need_string(in_site('timezone'))
need_string_array('ntp_servers', false) need_string_match(in_domain({'prefix6'}), '^[%x:]+/64$')
need_string_match(in_domain('prefix6'), '^[%x:]+/64$')
for _, config in ipairs({'wifi24', 'wifi5'}) do for _, config in ipairs({'wifi24', 'wifi5'}) do
if need_table(config, nil, false) then if need_table({config}, nil, false) then
need_string(in_site('regdom')) -- regdom is only required when wifi24 or wifi5 is configured need_string(in_site({'regdom'})) -- regdom is only required when wifi24 or wifi5 is configured
need_number(config .. '.channel') need_number({config, 'channel'})
local rates = {1000, 2000, 5500, 6000, 9000, 11000, 12000, 18000, 24000, 36000, 48000, 54000} local rates = {1000, 2000, 5500, 6000, 9000, 11000, 12000, 18000, 24000, 36000, 48000, 54000}
local supported_rates = need_array_of(in_site(config .. '.supported_rates'), rates, false) local supported_rates = need_array_of(in_site({config, 'supported_rates'}), rates, false)
if supported_rates then need_array_of({config, 'basic_rate'}, supported_rates or rates, supported_rates ~= nil)
need_array_of(config .. '.basic_rate', supported_rates, true)
else
need_array_of(config .. '.basic_rate', rates, false)
end
end
end
need_boolean(in_site('poe_passthrough'), false) if need_table({config, 'ibss'}, nil, false) then
if need_table('dns', nil, false) then need_string(in_domain({config, 'ibss', 'ssid'}))
need_number('dns.cacheentries', false) need_string_match(in_domain({config, 'ibss', 'bssid'}), '^%x[02468aAcCeE]:%x%x:%x%x:%x%x:%x%x:%x%x$')
need_string_array_match('dns.servers', '^[%x:]+$', true) need_one_of({config, 'ibss', 'mcast_rate'}, supported_rates or rates, false)
need_number({config, 'ibss', 'vlan'}, false)
need_boolean({config, 'ibss', 'disabled'}, false)
end end
if need_table('next_node', nil, false) then if need_table({config, 'mesh'}, nil, false) then
need_string_match(in_domain('next_node.ip6'), '^[%x:]+$', false) need_string(in_domain({config, 'mesh', 'id'}))
need_string_match(in_domain('next_node.ip4'), '^%d+.%d+.%d+.%d+$', false) need_one_of({config, 'mesh', 'mcast_rate'}, supported_rates or rates, false)
need_boolean({config, 'mesh', 'disabled'}, false)
end
end
end end
for _, config in ipairs({'wifi24', 'wifi5'}) do need_boolean(in_site({'poe_passthrough'}), false)
local rates = {1000, 2000, 5500, 6000, 9000, 11000, 12000, 18000, 24000, 36000, 48000, 54000}
rates = need_array_of(in_site(config .. '.supported_rates'), rates, false) or rates
if need_table(config .. '.ibss', nil, false) then if need_table({'dns'}, nil, false) then
need_string(in_domain(config .. '.ibss.ssid')) need_string_array_match({'dns', 'servers'}, '^[%x:]+$')
need_string_match(in_domain(config .. '.ibss.bssid'), '^%x[02468aAcCeE]:%x%x:%x%x:%x%x:%x%x:%x%x$') need_number({'dns', 'cacheentries'}, false)
need_one_of(config .. '.ibss.mcast_rate', rates, false)
need_number(config .. '.ibss.vlan', false)
need_boolean(config .. '.ibss.disabled', false)
end end
if need_table(config .. '.mesh', nil, false) then need_string_match(in_domain({'next_node', 'ip6'}), '^[%x:]+$', false)
need_string(in_domain(config .. '.mesh.id')) need_string_match(in_domain({'next_node', 'ip4'}), '^%d+.%d+.%d+.%d+$', false)
need_one_of(config .. '.mesh.mcast_rate', rates, false)
need_boolean(config .. '.mesh.disabled', false)
end
end
need_boolean(in_site('mesh_on_wan'), false) need_boolean(in_site({'mesh_on_wan'}), false)
need_boolean(in_site('mesh_on_lan'), false) need_boolean(in_site({'mesh_on_lan'}), false)
need_boolean(in_site('single_as_lan'), false) need_boolean(in_site({'single_as_lan'}), false)
need_string_match(in_domain('prefix4'), '^%d+.%d+.%d+.%d+/%d+$', false) need_string_match(in_domain({'prefix4'}), '^%d+.%d+.%d+.%d+/%d+$', false)
need_string_array_match(in_domain('extra_prefixes6'), '^[%x:]+/%d+$', false) need_string_array_match(in_domain({'extra_prefixes6'}), '^[%x:]+/%d+$', false)
if need_table('mesh', nil, false) and need_table('mesh.batman_adv', nil, false) then need_number({'mesh', 'batman_adv', 'gw_sel_class'}, false)
need_number('mesh.batman_adv.gw_sel_class', false) need_one_of({'mesh', 'batman_adv', 'routing_algo'}, {'BATMAN_IV', 'BATMAN_V'}, false)
need_one_of('mesh.batman_adv.routing_algo', {'BATMAN_IV', 'BATMAN_V'}, false)
end
need_boolean(in_site('mesh_vpn.enabled'), false) need_boolean(in_site({'mesh_vpn', 'enabled'}), false)
need_number('mesh_vpn.mtu') need_number({'mesh_vpn', 'mtu'})
if need_table(in_site('mesh_vpn.bandwidth_limit'), nil, false) then need_boolean(in_site({'mesh_vpn', 'bandwidth_limit', 'enabled'}), false)
need_boolean(in_site('mesh_vpn.bandwidth_limit.enabled'), false) need_number(in_site({'mesh_vpn', 'bandwidth_limit', 'ingress'}), false)
need_number(in_site('mesh_vpn.bandwidth_limit.ingress'), false) need_number(in_site({'mesh_vpn', 'bandwidth_limit', 'egress'}), false)
need_number(in_site('mesh_vpn.bandwidth_limit.egress'), false)
end
local fastd_methods = {'salsa2012+gmac', 'salsa2012+umac', 'null+salsa2012+gmac', 'null+salsa2012+umac', 'null'} local fastd_methods = {'salsa2012+gmac', 'salsa2012+umac', 'null+salsa2012+gmac', 'null+salsa2012+umac', 'null'}
need_array_of('mesh_vpn.fastd.methods', fastd_methods) need_array_of({'mesh_vpn', 'fastd', 'methods'}, fastd_methods)
need_boolean(in_site('mesh_vpn.fastd.configurable'), false) need_boolean(in_site({'mesh_vpn', 'fastd', 'configurable'}), false)
need_one_of(in_site('mesh_vpn.fastd.syslog_level'), {'error', 'warn', 'info', 'verbose', 'debug', 'debug2'}, false) need_one_of(in_site({'mesh_vpn', 'fastd', 'syslog_level'}), {'error', 'warn', 'info', 'verbose', 'debug', 'debug2'}, false)
local function check_peer(prefix) local function check_peer(k)
return function(k, _) need_alphanumeric_key(k)
assert_uci_name(k)
local table = string.format('%s[%q].', prefix, k) need_string_match(in_domain(extend(k, {'key'})), '^%x+$')
need_string_array(in_domain(extend(k, {'remotes'})))
need_string_match(in_domain(table .. 'key'), '^%x+$')
need_string_array(in_domain(table .. 'remotes'))
end
end end
local function check_group(prefix) local function check_group(k)
return function(k, _) need_alphanumeric_key(k)
assert_uci_name(k)
local table = string.format('%s[%q].', prefix, k) need_number(extend(k, {'limit'}), false)
need_table(extend(k, {'peers'}), check_peer, false)
need_number(table .. 'limit', false) need_table(extend(k, {'groups'}), check_group, false)
need_table(table .. 'peers', check_peer(table .. 'peers'), false)
need_table(table .. 'groups', check_group(table .. 'groups'), false)
end
end end
need_table('mesh_vpn.fastd.groups', check_group('mesh_vpn.fastd.groups')) need_table({'mesh_vpn', 'fastd', 'groups'}, check_group)
need_string_array('mesh_vpn.tunneldigger.brokers') need_string_array({'mesh_vpn', 'tunneldigger', 'brokers'})
need_string(in_site('roles.default'), false) need_string(in_site({'roles', 'default'}), false)
need_boolean(in_site('setup_mode.skip'), false) need_boolean(in_site({'setup_mode', 'skip'}), false)
if need_table(in_site('config_mode'), nil, false) and need_table(in_site('config_mode.remote_login'), nil, false) then need_boolean(in_site({'config_mode', 'remote_login', 'show_password_form'}), false)
need_boolean(in_site('config_mode.remote_login.show_password_form'), false) need_number(in_site({'config_mode', 'remote_login', 'min_password_length'}), false)
need_number(in_site('config_mode.remote_login.min_password_length'), false)
end
assert(need_boolean(in_site('mesh_vpn.fastd.configurable')) == true, need_value(in_site({'mesh_vpn', 'fastd', 'configurable'}), true)
"site.conf error: expected `mesh_vpn.fastd.configurable' to be true")
need_string(in_site('roles.default')) need_string(in_site({'roles', 'default'}))
need_string_array(in_site('roles.list')) need_string_array(in_site({'roles', 'list'}))
...@@ -7,154 +7,162 @@ function in_domain(var) ...@@ -7,154 +7,162 @@ function in_domain(var)
end end
local function loadvar(varname) local function path_to_string(path)
local ok, val = pcall(assert(loadstring('return site.' .. varname))) return table.concat(path, '/')
if ok then
return val
else
return nil
end
end end
local function array_to_string(array) local function array_to_string(array)
local string = '' return '[' .. table.concat(array, ', ') .. ']'
for _, v in ipairs(array) do
if #string >= 1 then
string = string .. ', '
end end
string = string .. v
end local function var_error(path, val, msg)
return '[' .. string .. ']' print(string.format('*** site.conf error: expected %s to %s, but it is %s', path_to_string(path), msg, tostring(val)))
os.exit(1)
end end
local function assert_one_of(var, array, msg)
for _, v in ipairs(array) do function extend(path, c)
if v == var then local p = {unpack(path)}
return true
for _, e in ipairs(c) do
p[#p+1] = e
end end
return p
end end
error(msg) local function loadpath(path, base, c, ...)
if not c or base == nil then
return base
end end
local function assert_type(var, t, msg) if type(base) ~= 'table' then
assert(type(var) == t, msg) var_error(path, base, 'be a table')
end end
return loadpath(extend(path, {c}), base[c], ...)
end
function assert_uci_name(var) local function loadvar(path)
-- We don't use character classes like %w here to be independent of the locale return loadpath({}, site, unpack(path))
assert(var:match('^[0-9a-zA-Z_]+$'), "site.conf error: `" .. var .. "' is not a valid config section name (only alphanumeric characters and the underscore are allowed)")
end end
local function check_type(t)
return function(val)
return type(val) == t
end
end
function need_string(varname, required) local function check_one_of(array)
local var = loadvar(varname) return function(val)
for _, v in ipairs(array) do
if v == val then
return true
end
end
return false
end
end
if required == false and var == nil then function need(path, check, required, msg)
local val = loadvar(path)
if required == false and val == nil then
return nil return nil
end end
assert_type(var, 'string', "site.conf error: expected `" .. varname .. "' to be a string") if not check(val) then
return var var_error(path, val, msg)
end end
function need_string_match(varname, pat, required) return val
local var = need_string(varname, required)
if not var then
return nil
end end
assert(var:match(pat), "site.conf error: expected `" .. varname .. "' to match pattern `" .. pat .. "'") local function need_type(path, type, required, msg)
return need(path, check_type(type), required, msg)
return var
end end
function need_number(varname, required)
local var = loadvar(varname)
if required == false and var == nil then function need_alphanumeric_key(path)
return nil local val = path[#path]
-- We don't use character classes like %w here to be independent of the locale
if not val:match('^[0-9a-zA-Z_]+$') then
var_error(path, val, 'have a key using only alphanumeric characters and underscores')
end
end end
assert_type(var, 'number', "site.conf error: expected `" .. varname .. "' to be a number")
return var function need_string(path, required)
return need_type(path, 'string', required, 'be a string')
end end
function need_boolean(varname, required) function need_string_match(path, pat, required)
local var = loadvar(varname) local val = need_string(path, required)
if not val then
if required == false and var == nil then
return nil return nil
end end
assert_type(var, 'boolean', "site.conf error: expected `" .. varname .. "' to be a boolean") if not val:match(pat) then
var_error(path, val, "match pattern '" .. pat .. "'")
return var
end end
function need_array(varname, subcheck, required) return val
local var = loadvar(varname) end
if required == false and var == nil then function need_number(path, required)
return nil return need_type(path, 'number', required, 'be a number')
end end
assert_type(var, 'table', "site.conf error: expected `" .. varname .. "' to be an array") function need_boolean(path, required)
return need_type(path, 'boolean', required, 'be a boolean')
end
for _, e in ipairs(var) do function need_array(path, subcheck, required)
subcheck(e) local val = need_type(path, 'table', required, 'be an array')
if not val then
return nil
end end
return var if subcheck then
for i = 1, #val do
subcheck(extend(path, {i}))
end
end end
function need_table(varname, subcheck, required) return val
local var = loadvar(varname) end
if required == false and var == nil then function need_table(path, subcheck, required)
local val = need_type(path, 'table', required, 'be a table')
if not val then
return nil return nil
end end
assert_type(var, 'table', "site.conf error: expected `" .. varname .. "' to be a table")
if subcheck then if subcheck then
for k, v in pairs(var) do for k, _ in pairs(val) do
subcheck(k, v) subcheck(extend(path, {k}))
end end
end end
return var return val
end end
function need_one_of(varname, array, required) function need_value(path, value, required)
local var = loadvar(varname) return need(path, function(v)
return v == value
if required == false and var == nil then end, required, 'be ' .. tostring(value))
return nil
end end
assert_one_of(var, array, "site.conf error: expected `" .. varname .. "' to be one of given array: " .. array_to_string(array)) function need_one_of(path, array, required)
return need(path, check_one_of(array), required, 'be one of the given array ' .. array_to_string(array))
return var
end end
function need_string_array(varname, required) function need_string_array(path, required)
local ok, var = pcall(need_array, varname, function(e) assert_type(e, 'string') end, required) return need_array(path, need_string, required)
assert(ok, "site.conf error: expected `" .. varname .. "' to be a string array")
return var
end end
function need_string_array_match(varname, pat, required) function need_string_array_match(path, pat, required)
local ok, var = pcall(need_array, varname, function(e) assert(e:match(pat)) end, required) return need_array(path, function(e) need_string_match(e, pat) end, required)
assert(ok, "site.conf error: expected `" .. varname .. "' to be a string array matching pattern `" .. pat .. "'")
return var
end end
function need_array_of(varname, array, required) function need_array_of(path, array, required)
local ok, var = pcall(need_array, varname, function(e) assert_one_of(e, array) end,required) return need_array(path, function(e) need_one_of(e, array) end, required)
assert(ok, "site.conf error: expected `" .. varname .. "' to be a subset of given array: " .. array_to_string(array))
return var
end end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment