kong/spec/01-unit/01-db/01-schema/06-routes_spec.lua (1,060 lines of code) (raw):
local routes = require "kong.db.schema.entities.routes"
local routes_subschemas = require "kong.db.schema.entities.routes_subschemas"
local services = require "kong.db.schema.entities.services"
local Schema = require "kong.db.schema"
local certificates = require "kong.db.schema.entities.certificates"
local Entity = require "kong.db.schema.entity"
assert(Schema.new(certificates))
assert(Schema.new(services))
local Routes = assert(Entity.new(routes))
for name, subschema in pairs(routes_subschemas) do
Routes:new_subschema(name, subschema)
end
describe("routes schema", function()
local a_valid_uuid = "cbb297c0-a956-486d-ad1d-f9b42df9465a"
local another_uuid = "64a8670b-900f-44e7-a900-6ec7ef5aa4d3"
local uuid_pattern = "^" .. ("%x"):rep(8) .. "%-" .. ("%x"):rep(4) .. "%-"
.. ("%x"):rep(4) .. "%-" .. ("%x"):rep(4) .. "%-"
.. ("%x"):rep(12) .. "$"
it("validates a valid route", function()
local route = {
id = a_valid_uuid,
name = "my_route",
protocols = { "http" },
methods = { "GET", "POST" },
hosts = { "example.com" },
headers = { location = { "location-1" } },
paths = { "/ovo" },
regex_priority = 1,
strip_path = false,
preserve_host = true,
service = { id = another_uuid },
}
route = Routes:process_auto_fields(route, "insert")
assert.truthy(route.created_at)
assert.truthy(route.updated_at)
assert.same(route.created_at, route.updated_at)
assert.truthy(Routes:validate(route))
assert.falsy(route.strip_path)
end)
it("it does not fail when service is null", function()
local route = { service = ngx.null, paths = {"/"} }
route = Routes:process_auto_fields(route, "insert")
local ok, errs = Routes:validate_insert(route)
assert.truthy(ok)
assert.is_nil(errs)
end)
it("it does not fail when service is missing", function()
local route = { paths = {"/"} }
route = Routes:process_auto_fields(route, "insert")
local ok, errs = Routes:validate_insert(route)
assert.truthy(ok)
assert.is_nil(errs)
end)
it("fails when service.id is null", function()
local route = { service = { id = ngx.null }, paths = {"/"} }
route = Routes:process_auto_fields(route, "insert")
local ok, errs = Routes:validate_insert(route)
assert.falsy(ok)
assert.truthy(errs["service"])
end)
it("fails when protocol is missing", function()
local route = { protocols = ngx.null }
route = Routes:process_auto_fields(route, "insert")
local ok, errs = Routes:validate_insert(route)
assert.falsy(ok)
assert.truthy(errs["protocols"])
end)
it("fails given an invalid method", function()
local route = {
protocols = { "http" },
methods = { "get" },
}
route = Routes:process_auto_fields(route, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.truthy(errs["methods"])
end)
it("missing method, host, headers, path & service produces error", function()
local s = { id = "a4fbd24e-6a52-4937-bd78-2536713072d2" }
local tests = {
{ 1, { protocols = { "http" }, }, {} },
{ true, { protocols = { "http" }, service = s, methods = {"GET"} }, {"hosts", "paths"} },
{ true, { protocols = { "http" }, service = s, hosts = {"x.y"} }, {"methods", "paths"} },
{ true, { protocols = { "http" }, service = s, paths = {"/foo"} }, {"methods", "hosts"} },
{ true, { protocols = { "http" }, service = s, headers = { location = { "location-1" } } }, {"methods", "hosts", "paths"} },
}
for i, test in ipairs(tests) do
test[2] = Routes:process_auto_fields(test[2], "insert")
local ok, errs = Routes:validate(test[2])
if test[1] == true then
assert.truthy(ok, "case " .. tostring(i) .. " failed")
assert.falsy(errs)
else
assert.falsy(ok)
for _, name in ipairs(test[3]) do
assert.truthy(errs[name], "case " .. tostring(i) .. " failed: " .. name)
end
end
end
end)
it("invalid protocols produces error", function()
local route = Routes:process_auto_fields({ protocols = {} }, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.truthy(errs["protocols"])
local route2 = Routes:process_auto_fields({ protocols = { "ftp" } }, "insert")
local ok, errs = Routes:validate(route2)
assert.falsy(ok)
assert.truthy(errs["protocols"])
end)
it("conflicting protocols produces error", function()
local protocols_tests = {
{ {"http", "tcp"}, "('http', 'https'), ('tcp', 'tls', 'udp')" },
{ {"http", "tls"}, "('http', 'https'), ('tcp', 'tls', 'udp')" },
{ {"https", "tcp"}, "('http', 'https'), ('tcp', 'tls', 'udp')" },
{ {"https", "tls"}, "('http', 'https'), ('tcp', 'tls', 'udp')" },
}
for _, test in ipairs(protocols_tests) do
local route = Routes:process_auto_fields({ protocols = test[1] }, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.truthy(errs["protocols"])
assert.same(("these sets are mutually exclusive: %s"):format(test[2]), errs["protocols"])
end
end)
it("invalid https_redirect_status_code produces error", function()
local route = Routes:process_auto_fields({ protocols = { "http" },
https_redirect_status_code = 404,
}, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.truthy(errs["https_redirect_status_code"])
end)
it("produces defaults", function()
local route = {
protocols = { "http" },
service = { id = "5abfe322-9fc1-4e0e-bfa3-007f5b9ac4b4" },
paths = { "/foo" }
}
route = Routes:process_auto_fields(route, "insert")
local ok, err = Routes:validate(route)
assert.is_nil(err)
assert.truthy(ok)
assert.match(uuid_pattern, route.id)
assert.same(ngx.null, route.name)
assert.same({ "http" }, route.protocols)
assert.same(ngx.null, route.methods)
assert.same(ngx.null, route.hosts)
assert.same(ngx.null, route.headers)
assert.same({ "/foo" }, route.paths)
assert.same(0, route.regex_priority)
assert.same(true, route.strip_path)
assert.same(false, route.preserve_host)
assert.same(426, route.https_redirect_status_code)
end)
it("validates the foreign key in entities", function()
local route = {
protocols = { "http" },
paths = { "/foo" },
service = {
id = "blergh",
}
}
route = Routes:process_auto_fields(route, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.truthy(errs["service"])
assert.truthy(errs["service"]["id"])
end)
it("gives access to foreign schemas", function()
assert.truthy(Routes.fields.service)
assert.truthy(Routes.fields.service.schema)
assert.truthy(Routes.fields.service.schema.fields)
end)
describe("paths attribute", function()
-- refusals
it("must be a string", function()
local route = {
paths = { false },
protocols = {"http"}
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("expected a string", err.paths[1])
end)
it("must be a non-empty string", function()
local route = {
paths = { "" },
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("length must be at least 1", err.paths[1])
end)
it("must start with /", function()
local route = {
paths = { "foo" },
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("should start with: / (fixed path) or ~/ (regex path)", err.paths[1])
end)
it("must not have empty segments (/foo//bar)", function()
local route = {
paths = {
"/foo//bar",
"/foo/bar//",
"//foo/bar",
},
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("must not have empty segments", err.paths[1])
assert.equal("must not have empty segments", err.paths[2])
assert.equal("must not have empty segments", err.paths[3])
end)
it("must dry-run values that are considered regexes", function()
local u = require("spec.helpers").unindent
local invalid_paths = {
[[~/users/(foo/profile]],
}
for i = 1, #invalid_paths do
local route = {
paths = { invalid_paths[i] },
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal(u([[invalid regex: '/users/(foo/profile' (PCRE returned:
pcre_compile() failed: missing ) in
"/users/(foo/profile")]], true, true), err.paths[1])
end
end)
it("rejects badly percent-encoded values", function()
local invalid_paths = {
"/some%2words",
"/some%0Xwords",
"/some%2Gwords",
"/some%20words%",
"/some%20words%a",
"/some%20words%ax",
}
local errstr = { "%2w", "%0X", "%2G", "%", "%a", "%ax" }
for i = 1, #invalid_paths do
local route = {
paths = { invalid_paths[i] },
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.matches("invalid url-encoded value: '" .. errstr[i] .. "'",
err.paths[1], nil, true)
end
end)
-- acceptance
it("accepts an apex '/'", function()
local route = Routes:process_auto_fields({
protocols = { "http" },
service = { id = a_valid_uuid },
methods = {},
hosts = {},
paths = { "/" },
}, "insert")
local ok, err = Routes:validate(route)
assert.is_nil(err)
assert.is_true(ok)
end)
it("accepts unreserved characters from RFC 3986", function()
local route = Routes:process_auto_fields({
protocols = { "http" },
service = { id = a_valid_uuid },
methods = {},
hosts = {},
paths = { "/abcd~user~2" },
}, "insert")
local ok, err = Routes:validate(route)
assert.is_nil(err)
assert.is_true(ok)
end)
it("accepts properly percent-encoded values", function()
local valid_paths = { "/abcd%aa%10%ff%AA%FF" }
for i = 1, #valid_paths do
local route = Routes:process_auto_fields({
protocols = { "http" },
service = { id = a_valid_uuid },
methods = {},
hosts = {},
paths = { valid_paths[i] },
}, "insert")
local ok, err = Routes:validate(route)
assert.is_nil(err)
assert.is_true(ok)
end
end)
it("accepts trailing slash", function()
local route = Routes:process_auto_fields({
protocols = { "http" },
service = { id = a_valid_uuid },
methods = {},
hosts = {},
paths = { "/ovo/" },
}, "insert")
local ok, err = Routes:validate(route)
assert.is_nil(err)
assert.is_true(ok)
end)
end)
describe("hosts attribute", function()
-- refusals
it("must be a string", function()
local route = {
hosts = { false },
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("expected a string", err.hosts[1])
end)
it("must be a non-empty string", function()
local route = {
hosts = { "" },
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("length must be at least 1", err.hosts[1])
end)
it("rejects invalid hostnames", function()
local invalid_hosts = {
"/example",
".example",
"example:",
"mock;bin",
"example.com/org",
"example-.org",
"example.org-",
"hello..example.com",
"hello-.example.com",
}
for i = 1, #invalid_hosts do
local route = {
hosts = { invalid_hosts[i] },
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("invalid hostname: " .. invalid_hosts[i], err.hosts[1])
end
end)
it("rejects values with an invalid port", function()
local route = {
hosts = { "example.com:1000000" },
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("invalid port number", err.hosts[1])
end)
it("rejects invalid wildcard placement", function()
local invalid_hosts = {
"*example.com",
"www.example*",
"mock*bin.com",
}
for i = 1, #invalid_hosts do
local route = {
hosts = { invalid_hosts[i] },
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("invalid wildcard: must be placed at leftmost or " ..
"rightmost label", err.hosts[1])
end
end)
it("rejects host with too many wildcards", function()
local invalid_hosts = {
"*.example.*",
"**.example.com",
"*.example*.*",
}
for i = 1, #invalid_hosts do
local route = {
hosts = { invalid_hosts[i] },
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("invalid wildcard: must have at most one wildcard",
err.hosts[1])
end
end)
-- acceptance
it("accepts valid hosts", function()
local valid_hosts = {
"hello.com",
"hello.fr",
"test.hello.com",
"1991.io",
"hello.COM",
"HELLO.com",
"123helloWORLD.com",
"example.123",
"example-api.com",
"hello.abcd",
"example_api.com",
"localhost",
"example.com:80",
"example.com:8080",
-- below:
-- punycode examples from RFC3492;
-- https://tools.ietf.org/html/rfc3492#page-14
-- specifically the japanese ones as they mix
-- ascii with escaped characters
"3B-ww4c5e180e575a65lsy2b",
"-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n",
"Hello-Another-Way--fc4qua05auwb3674vfr0b",
"2-u9tlzr9756bt3uc0v",
"MajiKoi5-783gue6qz075azm5e",
"de-jg4avhby1noc0d",
"d9juau41awczczp",
}
for i = 1, #valid_hosts do
local route = Routes:process_auto_fields({
protocols = { "http" },
service = { id = a_valid_uuid },
methods = {},
paths = {},
hosts = { valid_hosts[i] },
}, "insert")
local ok, err = Routes:validate(route)
assert.is_nil(err)
assert.is_true(ok)
end
end)
it("accepts hosts with valid wildcard", function()
local valid_hosts = {
"example.*",
"*.example.org",
"*.example.org:321",
}
for i = 1, #valid_hosts do
local route = Routes:process_auto_fields({
protocols = { "http" },
service = { id = a_valid_uuid },
methods = {},
paths = {},
hosts = { valid_hosts[i] },
}, "insert")
local ok, err = Routes:validate(route)
assert.is_nil(err)
assert.is_true(ok)
end
end)
end)
describe("headers attribute", function()
-- refusals
it("key must be a string", function()
local route = {
headers = { false },
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("expected a string", err.headers)
end)
it("cannot contain 'host' key", function()
local values = { "host", "Host", "HoSt" }
for _, v in ipairs(values) do
local route = {
headers = { [v] = { "example.com" } },
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("cannot contain 'host' header, which must be specified " ..
"in the 'hosts' attribute", err.headers)
end
end)
it("value must be an array", function()
local route = {
headers = { location = true },
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("expected an array", err.headers)
end)
it("values must be a string", function()
local route = {
headers = { location = { true } },
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("expected a string", err.headers[1])
end)
it("values must be non-empty string", function()
local route = {
headers = { location = { "" } },
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("length must be at least 1", err.headers[1])
end)
end)
describe("methods attribute", function()
-- refusals
it("must be a string", function()
local route = {
methods = { false },
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("expected a string", err.methods[1])
end)
it("must be a non-empty string", function()
local route = {
methods = { "" },
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("length must be at least 1", err.methods[1])
end)
it("rejects invalid values", function()
local invalid_methods = {
"HELLO WORLD",
" GET",
}
for i = 1, #invalid_methods do
local route = {
methods = { invalid_methods[i] },
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("invalid value: " .. invalid_methods[i],
err.methods[1])
end
end)
it("rejects non-uppercased values", function()
local route = {
methods = { "get" },
protocols = { "http" },
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("invalid value: get", err.methods[1])
end)
-- acceptance
it("accepts valid HTTP methods", function()
local valid_methods = {
"GET",
"POST",
"CUSTOM",
}
for i = 1, #valid_methods do
local route = Routes:process_auto_fields({
protocols = { "http" },
service = { id = a_valid_uuid },
paths = {},
hosts = {},
methods = { valid_methods[i] },
}, "insert")
local ok, err = Routes:validate(route)
assert.is_nil(err)
assert.is_true(ok)
end
end)
end)
describe("name attribute", function()
-- refusals
it("must be a string", function()
local route = {
name = false,
protocols = {"http"}
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("expected a string", err.name)
end)
it("must be a non-empty string", function()
local route = {
name = "",
protocols = {"http"}
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.equal("length must be at least 1", err.name)
end)
it("rejects invalid names", function()
local invalid_names = {
"examp:le",
"examp;le",
"examp/le",
"examp le",
-- see tests for utils.validate_utf8 for more invalid values
string.char(105, 213, 205, 149),
}
for i = 1, #invalid_names do
local route = {
name = invalid_names[i],
protocols = {"http"}
}
local ok, err = Routes:validate(route)
assert.falsy(ok)
assert.matches("invalid", err.name)
end
end)
-- acceptance
it("accepts valid names", function()
local valid_names = {
"example",
"EXAMPLE",
"exa.mp.le",
"3x4mp13",
"3x4-mp-13",
"3x4_mp_13",
"~3x4~mp~13",
"~3..x4~.M-p~1__3_",
"孔",
"Конг",
"🦍",
}
for i = 1, #valid_names do
local route = Routes:process_auto_fields({
protocols = { "http" },
paths = { "/" },
name = valid_names[i],
service = { id = a_valid_uuid }
}, "insert")
local ok, err = Routes:validate(route)
assert.is_nil(err)
assert.is_true(ok)
end
end)
end)
describe("#stream context", function()
it("'protocol' accepts 'tcp'", function()
local s = { id = "a4fbd24e-6a52-4937-bd78-2536713072d2" }
local route = Routes:process_auto_fields({
protocols = { "tcp" },
sources = {{ ip = "127.0.0.1" }},
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.is_nil(errs)
assert.truthy(ok)
assert.same({ "tcp" }, route.protocols)
end)
it("'protocol' accepts 'tls'", function()
local s = { id = "a4fbd24e-6a52-4937-bd78-2536713072d2" }
local route = Routes:process_auto_fields({
protocols = { "tls" },
sources = {{ ip = "127.0.0.1" }},
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.is_nil(errs)
assert.truthy(ok)
assert.same({ "tls" }, route.protocols)
end)
it("'protocol' accepts 'udp'", function()
local s = { id = "a4fbd24e-6a52-4937-bd78-2536713072d2" }
local route = Routes:process_auto_fields({
protocols = { "udp" },
sources = {{ ip = "127.0.0.1" }},
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.is_nil(errs)
assert.truthy(ok)
assert.same({ "udp" }, route.protocols)
end)
it("if 'protocol = tcp/tls/udp', then 'paths' is empty", function()
local s = { id = "a4fbd24e-6a52-4937-bd78-2536713072d2" }
for _, v in ipairs({ "tcp", "tls", "udp" }) do
local route = Routes:process_auto_fields({
protocols = { v },
sources = {{ ip = "127.0.0.1" }},
paths = { "/" },
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.same({
paths = "cannot set 'paths' when 'protocols' is 'tcp', 'tls', 'tls_passthrough' or 'udp'",
}, errs)
end
end)
it("if 'protocol = tcp/tls/udp', then 'methods' is empty", function()
local s = { id = "a4fbd24e-6a52-4937-bd78-2536713072d2" }
for _, v in ipairs({ "tcp", "tls", "udp" }) do
local route = Routes:process_auto_fields({
protocols = { v },
sources = {{ ip = "127.0.0.1" }},
methods = { "GET" },
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.same({
methods = "cannot set 'methods' when 'protocols' is 'tcp', 'tls', 'tls_passthrough' or 'udp'",
}, errs)
end
end)
describe("'sources' and 'destinations' matching attributes", function()
local s = { id = "a4fbd24e-6a52-4937-bd78-2536713072d2" }
for _, v in ipairs({
"sources",
"destinations"
}) do
it("'" .. v .. "' accepts valid IPs and ports", function()
for _, protocol in ipairs({ "tcp", "tls", "udp" }) do
local route = Routes:process_auto_fields({
protocols = { protocol },
[v] = {
{ ip = "127.75.78.72", port = 8000 },
},
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.is_nil(errs)
assert.truthy(ok)
assert.same({ protocol }, route.protocols)
assert.same({ ip = "127.75.78.72", port = 8000 }, route[v][1])
end
end)
it("'" .. v .. "' accepts valid IPs (no port)", function()
for _, protocol in ipairs({ "tcp", "tls", "udp" }) do
local route = Routes:process_auto_fields({
protocols = { protocol },
[v] = {
{ ip = "127.0.0.1" },
{ ip = "127.75.78.72", port = 8000 },
},
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.is_nil(errs)
assert.truthy(ok)
assert.same({ protocol }, route.protocols)
assert.same({ ip = "127.0.0.1", port = ngx.null }, route[v][1])
end
end)
it("'" .. v .. "' accepts valid ports (no IP)", function()
for _, protocol in ipairs({ "tcp", "tls", "udp" }) do
local route = Routes:process_auto_fields({
protocols = { protocol },
[v] = {
{ port = 8000 },
{ ip = "127.75.78.72", port = 8000 },
},
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.is_nil(errs)
assert.truthy(ok)
assert.same({ protocol }, route.protocols)
assert.same({ ip = ngx.null, port = 8000 }, route[v][1])
end
end)
it("'" .. v .. "' rejects invalid 'port' values", function()
for _, protocol in ipairs({ "tcp", "tls", "udp" }) do
local route = Routes:process_auto_fields({
protocols = { protocol },
[v] = {
{ ip = "127.0.0.1" },
{ ip = "127.75.78.72", port = 65536 },
},
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.same({
[v] = { [2] = { port = "value should be between 0 and 65535" } },
}, errs)
end
end)
it("'" .. v .. "' rejects invalid 'ip' values", function()
-- invalid IPs
for _, ip_val in ipairs({ "127.", ":::1", "-1", "localhost", "foo" }) do
for _, protocol in ipairs({ "tcp", "tls", "udp" }) do
local route = Routes:process_auto_fields({
protocols = { protocol },
[v] = {
{ ip = ip_val },
{ ip = "127.75.78.72", port = 8000 },
},
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok, "ip test value was valid: " .. ip_val)
assert.equal("invalid ip or cidr range: '" .. ip_val .. "'", errs[v][1].ip)
end
end
-- hostnames
for _, ip_val in ipairs({ "f", "example.org" }) do
for _, protocol in ipairs({ "tcp", "tls", "udp" }) do
local route = Routes:process_auto_fields({
protocols = { protocol },
[v] = {
{ ip = ip_val },
{ ip = "127.75.78.72", port = 8000 },
},
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok, "ip test value was valid: " .. ip_val)
assert.equal("invalid ip or cidr range: '" .. ip_val .. "'", errs[v][1].ip)
end
end
end)
it("'" .. v .. "' accepts valid 'ip cidr' values", function()
-- valid CIDRs
for _, ip_val in ipairs({ "0.0.0.0/0", "::/0", "0.0.0.0/1", "::/1",
"0.0.0.0/32", "::/128" }) do
for _, protocol in ipairs({ "tcp", "tls", "udp" }) do
local route = Routes:process_auto_fields({
protocols = { protocol },
[v] = {
{ ip = ip_val },
{ ip = "127.75.78.72", port = 8000 },
},
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.truthy(ok, "ip test value was valid: " .. ip_val)
assert.is_nil(errs)
end
end
end)
it("'" .. v .. "' rejects invalid 'ip cidr' values", function()
-- invalid CIDRs
for _, ip_val in ipairs({ "1/0", "2130706433/2", "4294967295/3",
"-1/0", "4294967296/2", "0.0.0.0/a",
"::/a", "0.0.0.0/-1", "::/-1",
"0.0.0.0/33", "::/129" }) do
for _, protocol in ipairs({ "tcp", "tls", "udp" }) do
local route = Routes:process_auto_fields({
protocols = { protocol },
[v] = {
{ ip = ip_val },
{ ip = "127.75.78.72", port = 8000 },
},
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok, "ip test value was valid: " .. ip_val)
assert.equal("invalid ip or cidr range: '" .. ip_val .. "'", errs[v][1].ip)
end
end
end)
end
end)
describe("'snis' matching attribute", function()
local s = { id = "a4fbd24e-6a52-4937-bd78-2536713072d2" }
for _, protocol in ipairs { "tls", "https", "grpcs" } do
it("accepts valid SNIs for " .. protocol .. " Routes", function()
for _, sni in ipairs({ "example.org", "www.example.org" }) do
local route = Routes:process_auto_fields({
protocols = { protocol },
snis = { sni },
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.is_nil(errs)
assert.truthy(ok)
end
end)
end
it("rejects invalid SNIs", function()
for _, sni in ipairs({ "127.0.0.1", "example.org:80" }) do
local route = Routes:process_auto_fields({
protocols = { "tcp", "tls" },
snis = { sni },
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok, "sni test value was valid: " .. sni)
if not pcall(function()
assert.matches("must not be an IP", errs.snis[1], nil,
true)
end)
then
assert.matches("must not have a port", errs.snis[1], nil, true)
end
end
end)
it("rejects specifying 'snis' if 'protocols' does not have 'https', 'tls' or 'tls_passthrough'", function()
local route = Routes:process_auto_fields({
protocols = { "tcp", "udp" },
snis = { "example.org" },
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.same({
["@entity"] = {
"'snis' can only be set when 'protocols' is 'grpcs', 'https', 'tls' or 'tls_passthrough'",
},
snis = "length must be 0",
}, errs)
end)
end)
it("errors if no L4 matching attribute set", function()
local s = { id = "a4fbd24e-6a52-4937-bd78-2536713072d2" }
for _, v in ipairs({ "tcp", "tls", "udp" }) do
local route = Routes:process_auto_fields({
protocols = { v },
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.same({
["@entity"] = {
"must set one of 'sources', 'destinations', 'snis' when 'protocols' is 'tcp', 'tls' or 'udp'"
}
}, errs)
end
end)
end)
it("errors if no L7 matching attribute set", function()
local s = { id = "a4fbd24e-6a52-4937-bd78-2536713072d2" }
local route = Routes:process_auto_fields({
protocols = { "http" },
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.same({
["@entity"] = {
"must set one of 'methods', 'hosts', 'headers', 'paths' when 'protocols' is 'http'"
}
}, errs)
route = Routes:process_auto_fields({
protocols = { "https" },
service = s,
}, "insert")
ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.same({
["@entity"] = {
"must set one of 'methods', 'hosts', 'headers', 'paths', 'snis' when 'protocols' is 'https'"
}
}, errs)
end)
it("errors if no L7 matching attribute set", function()
local s = { id = "a4fbd24e-6a52-4937-bd78-2536713072d2" }
local route = Routes:process_auto_fields({
protocols = { "grpc" },
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.same({
["@entity"] = {
"must set one of 'hosts', 'headers', 'paths' when 'protocols' is 'grpc'"
}
}, errs)
route = Routes:process_auto_fields({
protocols = { "grpcs" },
service = s,
}, "insert")
ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.same({
["@entity"] = {
"must set one of 'hosts', 'headers', 'paths', 'snis' when 'protocols' is 'grpcs'"
}
}, errs)
end)
it("errors if methods attribute is set on grpc/grpcs", function()
local s = { id = "a4fbd24e-6a52-4937-bd78-2536713072d2" }
local route = Routes:process_auto_fields({
methods = "GET",
paths = { "/foo" },
protocols = { "grpc" },
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.same({
methods = "cannot set 'methods' when 'protocols' is 'grpc' or 'grpcs'"
}, errs)
route = Routes:process_auto_fields({
methods = "GET",
paths = { "/foo" },
protocols = { "grpcs" },
service = s,
}, "insert")
ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.same({
methods = "cannot set 'methods' when 'protocols' is 'grpc' or 'grpcs'"
}, errs)
end)
it("errors if methods attribute is set on grpc/grpcs", function()
local s = { id = "a4fbd24e-6a52-4937-bd78-2536713072d2" }
local route = Routes:process_auto_fields({
methods = "GET",
paths = { "/foo" },
protocols = { "grpc" },
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.same({
methods = "cannot set 'methods' when 'protocols' is 'grpc' or 'grpcs'"
}, errs)
route = Routes:process_auto_fields({
methods = "GET",
paths = { "/foo" },
protocols = { "grpcs" },
service = s,
}, "insert")
ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.same({
methods = "cannot set 'methods' when 'protocols' is 'grpc' or 'grpcs'"
}, errs)
end)
it("errors if strip_path is set on grpc/grpcs", function()
local s = { id = "a4fbd24e-6a52-4937-bd78-2536713072d2" }
local route = Routes:process_auto_fields({
hosts = { "foo.grpc.com" },
protocols = { "grpc" },
strip_path = true,
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.same({
strip_path = "cannot set 'strip_path' when 'protocols' is 'grpc' or 'grpcs'"
}, errs)
route = Routes:process_auto_fields({
hosts = { "foo.grpc.com" },
protocols = { "grpcs" },
strip_path = true,
service = s,
}, "insert")
ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.same({
strip_path = "cannot set 'strip_path' when 'protocols' is 'grpc' or 'grpcs'"
}, errs)
end)
it("errors if tls and tls_passthrough set on a same route", function()
local s = { id = "a4fbd24e-6a52-4937-bd78-2536713072d2" }
local route = Routes:process_auto_fields({
snis = { "foo.grpc.com" },
protocols = { "tls", "tls_passthrough" },
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.same({
protocols = "these sets are mutually exclusive: ('tcp', 'tls', 'udp'), ('tls_passthrough')",
}, errs)
end)
it("errors if snis is not set on tls_pasthrough", function()
local s = { id = "a4fbd24e-6a52-4937-bd78-2536713072d2" }
local route = Routes:process_auto_fields({
sources = {{ ip = "127.0.0.1" }},
protocols = { "tls_passthrough" },
service = s,
}, "insert")
local ok, errs = Routes:validate(route)
assert.falsy(ok)
assert.same({
["@entity"] = { "must set snis when 'protocols' is 'tls_passthrough'" },
}, errs)
end)
end)