Skip to content

Commit

Permalink
wip: add validation
Browse files Browse the repository at this point in the history
  • Loading branch information
oleg-jukovec committed Jul 8, 2024
1 parent c940f9a commit 6546bb9
Show file tree
Hide file tree
Showing 4 changed files with 722 additions and 18 deletions.
205 changes: 198 additions & 7 deletions roles/metrics-export.lua
Original file line number Diff line number Diff line change
@@ -1,17 +1,208 @@
local function validate()
local urilib = require("uri")

local M = {}

local supported_formats = {"json", "prometheus"}

local function is_array(tbl)
assert(type(tbl) == "table", "a table expected")
for k, _ in pairs(tbl) do
local found = false
for idx, _ in ipairs(tbl) do
if type(k) == type(idx) and k == idx then
found = true
end
end
if not found then
return false
end
end
return true
end

local function parse_listen(listen)
if listen == nil then
return nil, nil, "must exist"
end
if type(listen) ~= "string" and type(listen) ~= "number" then
return nil, nil, "must be a string or a number, got " .. type(listen)
end

local host
local port
if type(listen) == "string" then
local uri, err = urilib.parse(listen)
if err ~= nil then
return nil, nil, "failed to parse URI: " .. err
end

if uri.scheme ~= nil then
if uri.scheme == "unix" then
uri.unix = uri.path
else
return nil, nil, "URI scheme is not supported"
end
end

if uri.login ~= nil or uri.password then
return nil, nil, "URI login and password are not supported"
end

if uri.query ~= nil then
return nil, nil, "URI query component is not supported"
end

if uri.unix ~= nil then
host = "unix/"
port = uri.unix
else
if uri.service == nil then
return nil, nil, "URI must contain a port"
end

local ok, ret = pcall(tonumber, uri.service)
if not ok then
return nil, nil, "URI port must be a number"
end
if uri.host ~= nil then
host = uri.host
elseif uri.ipv4 ~= nil then
host = uri.ipv4
elseif uri.ipv6 ~= nil then
host = uri.ipv6
else
host = "0.0.0.0"
end
port = ret
end
elseif type(listen) == "number" then
host = "0.0.0.0"
port = listen
end

if type(port) == "number" and (port < 1 or port > 65535) then
return nil, nil, "port must be in range [1, 65535]"
end
return host, port, nil
end

local function validate_http_endpoint(endpoint)
if type(endpoint) ~= "table" then
error("http endpoint must be a table, got " .. type(endpoint), 4)
end
if endpoint.path == nil then
error("http endpoint 'path' must exist", 4)
end
if type(endpoint.path) ~= "string" then
error("http endpoint 'path' must be a string, got " .. type(endpoint.path), 4)
end
if string.sub(endpoint.path, 1, 1) ~= '/' then
error("http endpoint 'path' must start with '/', got " .. endpoint.path, 4)
end

if endpoint.format == nil then
error("http endpoint 'format' must exist", 4)
end
if type(endpoint.format) ~= "string" then
error("http endpoint 'format' must be a string, got " .. type(endpoint.format), 4)
end
local found = false
for _, supported in pairs(supported_formats) do
if endpoint.format == supported then
found = true
end
end
if not found then
error("http endpoint 'format' must be one of: " ..
table.concat(supported_formats, ", ") .. ", got " .. endpoint.format, 4)
end
end

local function apply()
local function validate_http_node(node)
if type(node) ~= "table" then
error("http configuration node must be a table, got " .. type(node), 3)
end

local _, _, err = parse_listen(node.listen)
if err ~= nil then
error("failed to parse http 'listen' param: " .. err, 3)
end

node.endpoints = node.endpoints or {}
if type(node.endpoints) ~= "table" then
error("http 'endpoints' must be a table, got " .. type(node.endpoints), 3)
end
if not is_array(node.endpoints) then
error("http 'endpoints' must be an array, not a map", 3)
end
for _, endpoint in ipairs(node.endpoints) do
validate_http_endpoint(endpoint)
end

for i, ei in ipairs(node.endpoints) do
for j, ej in ipairs(node.endpoints) do
if i ~= j and ei.path == ej.path then
error("http 'endpoints' must have different paths", 3)
end
end
end
end

local function stop()
local function validate_http(opts)
if opts ~= nil and type(opts) ~= "table" then
error("http configuration must be a table, got " .. type(opts), 2)
end
opts = opts or {}

if not is_array(opts) then
error("http configuration must be an array, not a map", 2)
end

for _, http_node in ipairs(opts) do
validate_http_node(http_node)
end

for i, nodei in ipairs(opts) do
local hosti, porti, erri = parse_listen(nodei.listen)
assert(erri == nil) -- We should already successfully parse the URI.
for j, nodej in ipairs(opts) do
if i ~= j then
local hostj, portj, errj = parse_listen(nodej.listen)
assert(errj == nil) -- The same.
if hosti == hostj and porti == portj then
error("http configuration nodes must have different listen targets", 2)
end
end
end
end
end

return {
validate = validate,
apply = apply,
stop = stop,
local export_type_validators = {
["http"] = validate_http,
}

M.validate = function(conf)
if conf ~= nil and type(conf) ~= "table" then
error("configuration must be a table, got " .. type(conf))
end

for export_type, opts in pairs(conf or {}) do
if type(export_type) ~= "string" then
error("export type must be a string, got " .. type(export_type))
end
if export_type_validators[export_type] == nil then
error("unsupported export type '" .. tostring(export_type) .. "'")
end
export_type_validators[export_type](opts)
end
end

M.apply = function()

end

M.stop = function()

end

return M
5 changes: 5 additions & 0 deletions test/helper.lua
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,11 @@ function helpers.tarantool_role_is_supported()
return major >= 3
end

function helpers.skip_if_unsupported()
t.skip_if(not helpers.tarantool_role_is_supported(),
'Tarantool role is supported only for Tarantool starting from v3.0.0')
end

function helpers.error_function()
error("error function call")
end
Expand Down
19 changes: 8 additions & 11 deletions test/unit/metrics-export_test.lua
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
local t = require('luatest')
local helpers = require('test.helper')

local g = t.group('metrics_export_unit_test')
local g = t.group()

g.before_all(function()
t.skip_if(not helpers.tarantool_role_is_supported(),
'Tarantool role is supported only for Tarantool starting from v3.0.0')
g.default_cfg = { }
helpers.skip_if_unsupported()
end)

g.before_each(function()
g.role = require('roles.metrics-export')
g.role = require("roles.metrics-export")
end)

g.after_each(function()
g.role.stop()
end)

function g.test_dummy()
t.assert_equals(g.role.validate(), nil)
local expected_methods = {"apply", "validate", "stop"}
for _, method in ipairs(expected_methods) do
g["test_exist_method_" .. method] = function()
t.assert_type(g.role[method], "function")
end
end
Loading

0 comments on commit 6546bb9

Please sign in to comment.