dudeswave2/ranch/test/upgrade_SUITE.erl

262 lines
9.3 KiB
Erlang

%% Copyright (c) 2020-2021, Loïc Hoguin <essen@ninenines.eu>
%% Copyright (c) 2021, Maria Scott <maria-12648430@hnc-agency.org>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-module(upgrade_SUITE).
-compile(export_all).
-compile(nowarn_export_all).
-import(ct_helper, [doc/1]).
%% ct.
all() ->
ct_helper:all(?MODULE).
init_per_suite(Config) ->
%% Remove environment variables inherited from Erlang.mk.
os:unsetenv("ERLANG_MK_TMP"),
os:unsetenv("APPS_DIR"),
os:unsetenv("DEPS_DIR"),
os:unsetenv("ERL_LIBS"),
os:unsetenv("CI_ERLANG_MK"),
%% Ensure we are using the C locale for all os:cmd calls.
os:putenv("LC_ALL", "C"),
Config.
end_per_suite(_Config) ->
ok.
%% Find GNU Make.
do_find_make_cmd() ->
case os:getenv("MAKE") of
false ->
case os:find_executable("gmake") of
false -> "make";
Cmd -> Cmd
end;
Cmd ->
Cmd
end.
%% Manipulate the release.
do_copy(Example0) ->
Example = atom_to_list(Example0),
{ok, CWD} = file:get_cwd(),
_ = do_exec_log("cp -R " ++ CWD ++ "/../../examples/" ++ Example ++ " " ++ CWD),
Dir = CWD ++ "/" ++ Example,
_ = do_exec_log("sed -i.bak s/\"include \\.\\.\\/\\.\\.\\/erlang.mk\"/\"include ..\\/..\\/..\\/erlang.mk\"/ " ++ Dir ++ "/Makefile"),
ok.
do_remove(Example0) ->
Example = atom_to_list(Example0),
{ok, CWD} = file:get_cwd(),
_ = do_exec_log("rm -rf " ++ CWD ++ "/" ++ Example),
ok.
do_get_paths(Example0) ->
Example = atom_to_list(Example0),
{ok, CWD} = file:get_cwd(),
Dir = CWD ++ "/" ++ Example,
Rel = Dir ++ "/_rel/" ++ Example ++ "_example/bin/" ++ Example ++ "_example",
Log = Dir ++ "/_rel/" ++ Example ++ "_example/log/erlang.log.1",
{Dir, Rel, Log}.
do_compile_and_start(Example) ->
Make = do_find_make_cmd(),
{Dir, Rel, _} = do_get_paths(Example),
_ = do_exec_log(Make ++ " -C " ++ Dir ++ " distclean"),
%% TERM=dumb disables relx coloring.
_ = do_exec_log(Make ++ " -C " ++ Dir ++ " TERM=dumb"),
%% For some reason the release has ExampleStr.boot
%% while the downgrade expects start.boot?
ExampleStr = atom_to_list(Example),
_ = do_exec_log("cp "
++ Dir ++ "/_rel/" ++ ExampleStr
++ "_example/releases/1/" ++ ExampleStr ++ "_example.boot "
++ Dir ++ "/_rel/" ++ ExampleStr
++ "_example/releases/1/start.boot"),
_ = do_exec_log(Rel ++ " stop"),
_ = do_exec_log(Rel ++ " start"),
timer:sleep(2000),
_ = do_exec_log(Rel ++ " eval 'application:info()'"),
ok.
do_stop(Example) ->
{Dir, Rel, Log} = do_get_paths(Example),
_ = do_exec_log("sed -i.bak s/\"2\"/\"1\"/ " ++ Dir ++ "/relx.config"),
_ = do_exec_log(Rel ++ " stop"),
ct:log("~s~n", [element(2, file:read_file(Log))]).
%% When we are on a tag (git describe --exact-match succeeds),
%% we use the tag before that as a starting point. Otherwise
%% we use the most recent tag.
do_use_ranch_previous(Example) ->
TagsOutput = do_exec_log("git tag | tr - \\~ | sort -V | tr \\~ -"),
Tags = string:lexemes(TagsOutput, "\n"),
DescribeOutput = do_exec_log("git describe --exact-match"),
{CommitOrTag, Prev} = case DescribeOutput of
"fatal: no tag exactly matches " ++ _ -> {commit, hd(lists:reverse(Tags))};
_ -> {tag, hd(tl(lists:reverse(Tags)))}
end,
do_use_ranch_commit(Example, Prev),
CommitOrTag.
%% Replace the current Ranch commit with the one given as argument.
do_use_ranch_commit(Example, Commit) ->
{Dir, _, _} = do_get_paths(Example),
_ = do_exec_log(
"sed -i.bak s/\"dep_ranch_commit = .*\"/\"dep_ranch_commit = "
++ Commit ++ "\"/ " ++ Dir ++ "/Makefile"
),
ok.
%% Remove Ranch and rebuild, this time generating a relup.
do_build_relup(Example, CommitOrTag) ->
Make = do_find_make_cmd(),
{Dir, _, _} = do_get_paths(Example),
_ = do_exec_log("rm -rf " ++ Dir ++ "/deps/ranch/*"),
_ = do_exec_log("sed -i.bak s/\"1\"/\"2\"/ " ++ Dir ++ "/relx.config"),
%% We need Ranch to be fetched first in order to copy the current appup
%% and optionally update its version when we are not on a tag.
_ = do_exec_log("cp -R "
++ Dir ++ "/../../../Makefile "
++ Dir ++ "/../../../erlang.mk "
++ Dir ++ "/../../../src "
++ Dir ++ "/deps/ranch/"),
_ = do_exec_log(Make ++ " -C " ++ Dir ++ " deps"),
_ = case CommitOrTag of
tag -> ok;
commit ->
%% Force the rebuild of Ranch.
_ = do_exec_log(Make ++ " -C " ++ Dir ++ "/deps/ranch clean"),
%% Update the Ranch version so that the upgrade can be applied.
ProjectVersion = do_exec_log("grep \"PROJECT_VERSION = \" " ++ Dir ++ "/deps/ranch/Makefile"),
["PROJECT_VERSION = " ++ Vsn0|_] = string:lexemes(ProjectVersion, "\n"),
[A, B|Tail] = string:lexemes(Vsn0, "."),
Vsn = binary_to_list(iolist_to_binary([A, $., B, ".9", lists:join($., Tail)])),
ct:log("Changing Ranch version from ~s to ~s~n", [Vsn0, Vsn]),
_ = do_exec_log(
"sed -i.bak s/\"PROJECT_VERSION = .*\"/\"PROJECT_VERSION = " ++ Vsn ++ "\"/ "
++ Dir ++ "/deps/ranch/Makefile"
),
%% The version in the appup must be the same as PROJECT_VERSION.
_ = do_exec_log(
"sed -i.bak s/\"" ++ Vsn0 ++ "\"/\"" ++ Vsn ++ "\"/ "
++ Dir ++ "/deps/ranch/src/ranch.appup"
)
end,
_ = do_exec_log(Make ++ " -C " ++ Dir ++ " relup"),
ok.
%% Copy the tarball in the correct location and upgrade.
do_upgrade(Example) ->
ExampleStr = atom_to_list(Example),
{Dir, Rel, _} = do_get_paths(Example),
_ = do_exec_log("cp "
++ Dir ++ "/_rel/" ++ ExampleStr
++ "_example/" ++ ExampleStr ++ "_example-2.tar.gz "
++ Dir ++ "/_rel/" ++ ExampleStr
++ "_example/releases/2/" ++ ExampleStr ++ "_example.tar.gz"),
_ = do_exec_log(Rel ++ " upgrade \"2\""),
_ = do_exec_log(Rel ++ " eval 'application:info()'"),
ok.
do_downgrade(Example) ->
{_, Rel, _} = do_get_paths(Example),
_ = do_exec_log(Rel ++ " downgrade \"1\""),
_ = do_exec_log(Rel ++ " eval 'application:info()'"),
ok.
%% Tests.
upgrade_ranch_one_conn(_) ->
case os:type() of
{win32, nt} ->
{skip, "This test suite is not currently supported on Windows."};
_ ->
do_upgrade_ranch_one_conn()
end.
do_upgrade_ranch_one_conn() ->
Example = tcp_echo,
ExampleStr = atom_to_list(Example),
Port = 5555,
{_, Rel, _} = do_get_paths(Example),
try
%% Copy the example.
do_copy(Example),
%% Build and start the example release using the previous Ranch version.
CommitOrTag = do_use_ranch_previous(Example),
do_compile_and_start(Example),
%% Ensure that the metrics key is not present in the ranch:info output.
"false\n" = do_exec_log(Rel ++ " eval "
"'maps:is_key(metrics, ranch:info(" ++ ExampleStr ++ "))'"),
%% Establish a connection and check that it works.
{ok, S} = gen_tcp:connect("localhost", Port, [{active, false}, binary]),
ok = gen_tcp:send(S, "Hello!"),
{ok, <<"Hello!">>} = gen_tcp:recv(S, 0, 1000),
%% Update Ranch to master then build a release upgrade.
do_build_relup(Example, CommitOrTag),
%% Perform the upgrade, then check that our connection is still up.
do_upgrade(Example),
%% Ensure that the mextrics key is present in the ranch:info output.
"true\n" = do_exec_log(Rel ++ " eval "
"'maps:is_key(metrics, ranch:info(" ++ ExampleStr ++ "))'"),
ok = gen_tcp:send(S, "Hello!"),
{ok, <<"Hello!">>} = gen_tcp:recv(S, 0, 1000),
%% Ensure that no accepts have been counted yet.
"0\n" = do_exec_log(Rel ++ " eval "
"'lists:sum([N || {{conns_sup, _, accept}, N} <- "
"maps:to_list(maps:get(metrics, ranch:info(" ++ ExampleStr ++ ")))])'"),
%% Check that new connections are still accepted.
{ok, S2} = gen_tcp:connect("localhost", Port, [{active, false}, binary]),
%% Ensure that the accept has been counted.
"1\n" = do_exec_log(Rel ++ " eval "
"'lists:sum([N || {{conns_sup, _, accept}, N} <- "
"maps:to_list(maps:get(metrics, ranch:info(" ++ ExampleStr ++ ")))])'"),
%% Ensure that no terminates have been counted yet.
"0\n" = do_exec_log(Rel ++ " eval "
"'lists:sum([N || {{conns_sup, _, terminate}, N} <- "
"maps:to_list(maps:get(metrics, ranch:info(" ++ ExampleStr ++ ")))])'"),
%% Close the socket, ensure that the termination has been counted.
ok = gen_tcp:close(S2),
"1\n" = do_exec_log(Rel ++ " eval "
"'lists:sum([N || {{conns_sup, _, terminate}, N} <- "
"maps:to_list(maps:get(metrics, ranch:info(" ++ ExampleStr ++ ")))])'"),
%% Perform the downgrade, then check that our connection is still up.
do_downgrade(Example),
%% Ensure that the mextrics key is not present any more.
"false\n" = do_exec_log(Rel ++ " eval "
"'maps:is_key(metrics, ranch:info(" ++ ExampleStr ++ "))'"),
ok = gen_tcp:send(S, "Hello!"),
{ok, <<"Hello!">>} = gen_tcp:recv(S, 0, 1000),
%% Check that new connections are still accepted.
{ok, _} = gen_tcp:connect("localhost", Port, [{active, false}, binary]),
ok
after
do_stop(tcp_echo),
do_remove(Example)
end.
%% @todo upgrade_ranch_max_conn
do_exec_log(Cmd) ->
ct:log("Command: ~s~n", [Cmd]),
Out=os:cmd(Cmd),
ct:log("Output:~n~n~s~n", [Out]),
Out.