Skip to content

Commit

Permalink
Add new domain_socket data type (#33)
Browse files Browse the repository at this point in the history
* parent 6a016f2
author Dairon Medina Caro <dmedinacaro@cars.com> 1674855971 -0500
committer Luke Bakken <luke@bakken.io> 1676314181 -0800
gpgsig -----BEGIN PGP SIGNATURE-----

 iQJDBAABCAAtFiEEsbgswM+EunAUfr0F2Z3jDkPq5EAFAmPqhkUPHGx1a2VAYmFr
 a2VuLmlvAAoJENmd4w5D6uRAdOIP/1f/JeDldc5ygq164ERRa6VbUGSQTluHEY/o
 0pXfs5nelmfws2e12MFyvxK8ar+qLg/XyN6Y/HUZBFLgwX/qwsgwDtZv8uT0X4bW
 1yb26UsG22Q/oIv3aBYOGl/Nrkqch+Pw2TGJBynSIznYVXqDOd+LbQG5aXrZbfCd
 2xMpESnGeCFTsEpjgsNZu7vPKeDjBHeCN24EMi2DtPaC9xzb4PHhriDvCgS2YdpX
 bhjmFfAUv0v27PS+6GN7SqygRkRgiplpvEM3ToNVePS6lPdMpz6dsOO1NFcnzmY/
 +AT+okaILgIGwof/nF2QFtbEMkzTSnml9P7FtsaDjlrTmn9yVEN5q5hoHSVBaTMS
 ISmOFGKsx3W5YJtgb4gp0UzSAktK9e1jjhcd7160hO/DCv7LvzC33lJVHlXK0Ep6
 vvRuOfbdhFOIFsTUBqz7cHoCIckz7smlohFi57znKop3DINEsq9cnbFBaqSKTAzE
 r+KRZOxQpc9Fv1j69DS6ASfM4gv9vPjgCJaTDRI4XZKUY6WWJCfxkY8pN4h6RLMz
 AhyDp/4IpDr8wafCg8dZcIgrDSnY+sWd3RkSaiWp4TfOjtFWb+D1gpyuBfgaZWHV
 1clNDvI2A7TJpJVOArhZpBfqFHijJwCuVzuGYkBTAOaowiJGNkk9kVOtdgq4b32R
 m7kRBCZ2
 =k54M
 -----END PGP SIGNATURE-----

Add support for Unix Domain Sockets type

Simplify domain sockets

Validate only 0 port can be provided

Use same format expected by inets

ci: Normalize GHA string quotes

* Port is always 0 for UDS

---------

Co-authored-by: Luke Bakken <luke@bakken.io>
  • Loading branch information
codeadict and lukebakken authored Feb 13, 2023
1 parent 6a016f2 commit be90c7d
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 2 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- otp_version: 23
os: ubuntu-20.04
env:
LATEST_OTP_RELEASE: '25'
LATEST_OTP_RELEASE: 25
steps:
- uses: actions/checkout@v3
- uses: erlef/setup-beam@v1
Expand All @@ -36,7 +36,7 @@ jobs:
if: ${{ matrix.otp_version == env.LATEST_OTP_RELEASE && startsWith(matrix.os, 'ubuntu') }}
with:
path: _build/*/rebar3_*_plt
key: "dialyzer-plt-cache-${{ steps.install-erlang.outputs.otp-version }}-${{ runner.os }}-${{ hashFiles('rebar.config*') }}-v0"
key: dialyzer-plt-cache-${{ steps.install-erlang.outputs.otp-version }}-${{ runner.os }}-${{ hashFiles('rebar.config*') }}-v0
- name: Compile
run: rebar3 compile
- name: Test
Expand Down
3 changes: 3 additions & 0 deletions src/cuttlefish_conf.erl
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,15 @@ pretty_datatype(integer) -> "an integer";
pretty_datatype({enum, L}) ->
"one of: " ++ string:join([ atom_to_list(A) || A <- L], ", ");
pretty_datatype(ip) -> "an IP/port pair, e.g. 127.0.0.1:10011";
pretty_datatype(domain_socket) -> "a Unix Domain Socket, e.g. local:/var/run/app.sock:0";
pretty_datatype({duration, _}) -> "a time duration with units, e.g. '10s' for 10 seconds";
pretty_datatype(bytesize) -> "a byte size with units, e.g. 10GB";
pretty_datatype({integer, I}) -> "the integer " ++ integer_to_list(I);
pretty_datatype({string, S}) -> "the text \"" ++ S ++ "\"";
pretty_datatype({atom, A}) -> "the text \"" ++ atom_to_list(A) ++ "\"";
pretty_datatype({ip, {IP, Port}}) -> ?FMT("the address ~ts:~tp", [IP, Port]);
pretty_datatype({domain_socket, {local, Path, Port}}) ->
?FMT("the Unix Domain Socket ~ts:~tp", [Path, Port]);
pretty_datatype({{duration,_}, D}) -> "the time duration " ++ D;
pretty_datatype({bytesize, B}) -> "the bytesize " ++ B;
pretty_datatype(file) -> "the path to a file";
Expand Down
80 changes: 80 additions & 0 deletions src/cuttlefish_datatypes.erl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
{enum, [atom()]} |
ip |
fqdn |
domain_socket |
{duration, cuttlefish_duration:time_unit() } |
bytesize |
{percent, integer} |
Expand All @@ -48,6 +49,7 @@
{ directory, file:filename() } |
{ atom, atom() } |
{ ip, { string(), integer() } } |
{ domain_socket, {string(), integer()} } |
{ {duration, cuttlefish_duration:time_unit() }, string() } |
{ bytesize, string() } |
{ {percent, integer}, integer() } |
Expand Down Expand Up @@ -78,6 +80,7 @@ is_supported(atom) -> true;
is_supported({enum, E}) when is_list(E) -> true;
is_supported(ip) -> true;
is_supported(fqdn) -> true;
is_supported(domain_socket) -> true;
is_supported({duration, f}) -> true;
is_supported({duration, w}) -> true;
is_supported({duration, d}) -> true;
Expand Down Expand Up @@ -106,6 +109,8 @@ is_extended({ip, {IP, Port}}) when is_list(IP) andalso is_integer(Port) -> true;
is_extended({ip, StringIP}) when is_list(StringIP) -> true;
is_extended({fqdn, {FQDN, Port}}) when is_list(FQDN) andalso is_integer(Port) -> true;
is_extended({fqdn, StringFQDN}) when is_list(StringFQDN) -> true;
is_extended({domain_socket, {local, UDS, Port}}) when is_list(UDS) andalso is_integer(Port) -> true;
is_extended({domain_socket, StringUDS}) when is_list(StringUDS) -> true;
is_extended({{duration, f}, D}) when is_list(D) -> true;
is_extended({{duration, w}, D}) when is_list(D) -> true;
is_extended({{duration, d}, D}) when is_list(D) -> true;
Expand All @@ -127,6 +132,7 @@ extended_from({file, _}) -> file;
extended_from({directory, _}) -> directory;
extended_from({ip, _}) -> ip;
extended_from({fqdn, _}) -> fqdn;
extended_from({domain_socket, _}) -> domain_socket;
extended_from({{duration, Unit}, _}) -> {duration, Unit};
extended_from({bytesize, _}) -> bytesize;
extended_from({{percent, integer}, _}) -> {percent, integer};
Expand Down Expand Up @@ -163,6 +169,10 @@ to_string(IPString, ip) when is_list(IPString) -> IPString;
to_string({FQDN, Port}, fqdn) when is_list(FQDN), is_integer(Port) -> FQDN ++ ":" ++ integer_to_list(Port);
to_string(FQDNString, fqdn) when is_list(FQDNString) -> FQDNString;

to_string({local, UDS, Port}, domain_socket) when is_list(UDS), is_integer(Port) ->
"local:" ++ UDS ++ ":" ++ integer_to_list(Port);
to_string(UDSString, domain_socket) when is_list(UDSString) -> UDSString;

to_string(Enum, {enum, _}) when is_list(Enum) -> Enum;
to_string(Enum, {enum, _}) when is_atom(Enum) -> atom_to_list(Enum);

Expand Down Expand Up @@ -229,6 +239,10 @@ from_string({FQDN, Port}, fqdn) when is_list(FQDN), is_integer(Port) -> {FQDN, P
from_string(String, fqdn) when is_list(String) ->
from_string_to_fqdn(String, lists:split(string:rchr(String, $:), String));

from_string({local, UDS, Port}, domain_socket) when is_list(UDS), is_integer(Port) -> {local, UDS, Port};
from_string(String, domain_socket) when is_list(String) ->
from_string_to_uds(String, lists:split(string:rchr(String, $:), String));

from_string(Duration, {duration, _}) when is_integer(Duration) -> Duration;
from_string(Duration, {duration, Unit}) when is_list(Duration) -> cuttlefish_duration:parse(Duration, Unit);

Expand Down Expand Up @@ -313,6 +327,20 @@ fqdn_conversions(String, _FQDNStr, _, undefined) ->
fqdn_conversions(_String, FQDNStr, {match, _}, Port) ->
{FQDNStr, Port}.

uds_conversions(String, _UDSStr, nomatch) ->
{error, {conversion, {String, 'UDS'}}};
uds_conversions(_String, _UDSStr, {match, Path}) ->
%% port is always 0 for unix domain sockets
{local, Path, 0}.

validate_uds(Str) ->
case string:tokens(Str, ":") of
[Pfx, Path] when Pfx =:= "local" orelse Pfx =:= "unix" ->
{match, Path};
_ ->
nomatch
end.

validate_fqdn(Str) ->
%% inspired by https://regexr.com/3g5j0, amended to disallow [:space:]
re:run(Str, "^(?!:\/\/)(?=[^[:space:]]{1,255}$)((.{1,63}\.){1,127}(?![0-9]*$)[a-z0-9-]+\.?)$", [unicode]).
Expand All @@ -335,6 +363,21 @@ from_string_to_fqdn(String, {FQDNPlusColon, PortString}) ->
FQDN = droplast(FQDNPlusColon),
fqdn_conversions(String, FQDN, validate_fqdn(FQDN), port_to_integer(PortString)).

from_string_to_uds(String, {[], String}) ->
{error, {conversion, {String, 'UDS'}}};
from_string_to_uds(String, {UDSPlusColon, PortString}) ->
%% Drop the trailing colon
UDS = droplast(UDSPlusColon),

%% In most API functions where you can use this address family
%% the port number must be 0.
%% See: https://www.erlang.org/doc/man/inet.html#type-local_address
case port_to_integer(PortString) of
0 ->
uds_conversions(String, UDS, validate_uds(UDS));
_OtherPort ->
{error, {conversion, {String, 'UDS'}}}
end.

-ifdef(TEST).

Expand Down Expand Up @@ -391,6 +434,14 @@ to_string_extended_type_test() ->
?assertEqual("127.0.0.1:8098", to_string({"127.0.0.1", 8098}, {ip, {"127.0.0.1", 8098}})),
?assertEqual("example.com:8098", to_string("example.com:8098", {fqdn, "example.com:8098"})),
?assertEqual("example.com:8098", to_string({"example.com", 8098}, {fqdn, {"example.com", 8098}})),
?assertEqual("local:/path/test.sock:0",
to_string("local:/path/test.sock:0", {domain_socket, "local:/path/test.sock:0"})),
?assertEqual("unix:/path/test.sock:0",
to_string("unix:/path/test.sock:0", {domain_socket, "unix:/path/test.sock:0"})),
?assertEqual("local:/path/test.sock:0",
to_string({local, "/path/test.sock", 0}, {domain_socket, {local, "/path/test.sock", 0}})),
?assertEqual("local:/path/test.sock:0",
to_string({local, "/path/test.sock", 0}, {domain_socket, {local, "/path/test.sock", 0}})),
?assertEqual("string", to_string("string", {string, "string"})),
?assertEqual("1w", to_string("1w", {{duration, s}, "1w"})),
?assertEqual("1w", to_string(604800000, {{duration, ms}, "1w"})),
Expand Down Expand Up @@ -480,6 +531,29 @@ from_string_fqdn_test() ->
BadFQDNs),
ok.

from_string_domain_socket_test() ->
?assertEqual({local, "/tmp/test.sock", 0}, from_string("local:/tmp/test.sock:0", domain_socket)),
?assertEqual({local, "/tmp/test.sock", 0}, from_string("unix:/tmp/test.sock:0", domain_socket)),
?assertEqual({local, "test.sock", 0}, from_string("local:test.sock:0", domain_socket)),
?assertEqual({local, "/run/不亦樂乎.sock", 0}, from_string("local:/run/不亦樂乎.sock:0", domain_socket)),

BadUDSs = [
"local:/tmp/test.sock", %% No port
"local:/tmp/test.sock:80", %% Non 0 port
"/tmp/test.sock", %% No local prefix
"",
"local:/tmp/test.sock:foo", %% invalid port
"local:/tmp/test.sock:0:0", %% double port
"prefix:foo.sock:0" %% Bad prefix
],

lists:foreach(fun(Bad) ->
?assertEqual({error, {conversion, {Bad, 'UDS'}}},
from_string(Bad, domain_socket))
end,
BadUDSs),
ok.

from_string_enum_test() ->
?assertEqual("\"a\" is not a valid enum value, acceptable values are: b, c", ?XLATE(from_string(a, {enum, [b, c]}))),
?assertEqual(true, from_string("true", {enum, [true, false]})),
Expand Down Expand Up @@ -567,6 +641,7 @@ is_supported_test() ->
?assert(is_supported({duration, s})),
?assert(is_supported({duration, ms})),
?assert(is_supported(bytesize)),
?assert(is_supported(domain_socket)),
?assert(is_supported({list, string})),
?assert(not(is_supported({list, {list, string}}))),
?assert(not(is_supported(some_unsupported_type))),
Expand Down Expand Up @@ -597,6 +672,11 @@ is_extended_test() ->
?assertEqual(false, is_extended({ip, {1234, 1234}})),
?assertEqual(false, is_extended({ip, {"1.2.3.4", "1234"}})),

?assertEqual(true, is_extended({domain_socket, {local, "test.sock", 1234}})),
?assertEqual(false, is_extended({domain_socket, {local, "test.sock", "1234"}})),
?assertEqual(false, is_extended({domain_socket, {local, 1234, 1234}})),
?assertEqual(false, is_extended({domain_socket, {local, "foo"}})),

?assertEqual(true, is_extended({{duration, f}, "10f"})),
?assertEqual(true, is_extended({{duration, w}, "10f"})),
?assertEqual(true, is_extended({{duration, d}, "10f"})),
Expand Down

0 comments on commit be90c7d

Please sign in to comment.