From 62c692fad7ff448ccca3505e1dd40be245cbed53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Sun, 2 Feb 2020 23:09:48 +0100 Subject: Initial release upgrade test suite --- Makefile | 2 + ebin/ranch.appup | 43 ++++++ examples/tcp_echo/relx.config | 2 +- examples/tcp_echo/src/echo_protocol.erl | 2 +- examples/tcp_reverse/relx.config | 2 +- examples/tcp_reverse/src/reverse_protocol.erl | 2 +- test/upgrade_SUITE.erl | 187 ++++++++++++++++++++++++++ 7 files changed, 236 insertions(+), 4 deletions(-) create mode 100644 ebin/ranch.appup create mode 100644 test/upgrade_SUITE.erl diff --git a/Makefile b/Makefile index a7ec8c6..960b53e 100644 --- a/Makefile +++ b/Makefile @@ -69,6 +69,8 @@ prepare_tag: $(verbose) grep -m1 PROJECT_VERSION Makefile $(verbose) echo -n "APP: " $(verbose) grep -m1 vsn ebin/$(PROJECT).app | sed 's/ //g' + $(verbose) echo -n "APPUP: " + $(verbose) grep -m1 {\" ebin/$(PROJECT).appup $(verbose) echo $(verbose) echo "Links in the README:" $(verbose) grep http.*:// README.asciidoc diff --git a/ebin/ranch.appup b/ebin/ranch.appup new file mode 100644 index 0000000..6b25270 --- /dev/null +++ b/ebin/ranch.appup @@ -0,0 +1,43 @@ +%% @todo Remove the ending .* before release, we do not +%% want to support upgrades from/to release candidates. + +{"2.0.0-rc.2", + [{<<"2\\.0\\.[0-9]+.*">>, [ + {load_module, ranch}, + {load_module, ranch_acceptor}, + {load_module, ranch_acceptors_sup}, + {load_module, ranch_app}, + {load_module, ranch_conns_sup}, + {load_module, ranch_conns_sup_sup}, + {load_module, ranch_crc32c}, + {load_module, ranch_embedded_sup}, + {load_module, ranch_listener_sup}, + {load_module, ranch_protocol}, + {load_module, ranch_proxy_header}, + {load_module, ranch_server}, + {load_module, ranch_server_proxy}, + {load_module, ranch_ssl}, + {load_module, ranch_sup}, + {load_module, ranch_tcp}, + {load_module, ranch_transport} + ]}], + [{<<"2\\.0\\.[0-9]+.*">>, [ + {load_module, ranch}, + {load_module, ranch_acceptor}, + {load_module, ranch_acceptors_sup}, + {load_module, ranch_app}, + {load_module, ranch_conns_sup}, + {load_module, ranch_conns_sup_sup}, + {load_module, ranch_crc32c}, + {load_module, ranch_embedded_sup}, + {load_module, ranch_listener_sup}, + {load_module, ranch_protocol}, + {load_module, ranch_proxy_header}, + {load_module, ranch_server}, + {load_module, ranch_server_proxy}, + {load_module, ranch_ssl}, + {load_module, ranch_sup}, + {load_module, ranch_tcp}, + {load_module, ranch_transport} + ]}] +}. diff --git a/examples/tcp_echo/relx.config b/examples/tcp_echo/relx.config index a850b71..ee746d5 100644 --- a/examples/tcp_echo/relx.config +++ b/examples/tcp_echo/relx.config @@ -1,2 +1,2 @@ -{release, {tcp_echo_example, "1"}, [tcp_echo]}. +{release, {tcp_echo_example, "1"}, [tcp_echo, sasl]}. {extended_start_script, true}. diff --git a/examples/tcp_echo/src/echo_protocol.erl b/examples/tcp_echo/src/echo_protocol.erl index c07b424..7312e53 100644 --- a/examples/tcp_echo/src/echo_protocol.erl +++ b/examples/tcp_echo/src/echo_protocol.erl @@ -15,7 +15,7 @@ init(Ref, Transport, _Opts = []) -> loop(Socket, Transport). loop(Socket, Transport) -> - case Transport:recv(Socket, 0, 5000) of + case Transport:recv(Socket, 0, 30000) of {ok, Data} when Data =/= <<4>> -> Transport:send(Socket, Data), loop(Socket, Transport); diff --git a/examples/tcp_reverse/relx.config b/examples/tcp_reverse/relx.config index 2a83916..2b1651f 100644 --- a/examples/tcp_reverse/relx.config +++ b/examples/tcp_reverse/relx.config @@ -1,2 +1,2 @@ -{release, {tcp_reverse_example, "1"}, [tcp_reverse]}. +{release, {tcp_reverse_example, "1"}, [tcp_reverse, sasl]}. {extended_start_script, true}. diff --git a/examples/tcp_reverse/src/reverse_protocol.erl b/examples/tcp_reverse/src/reverse_protocol.erl index 2865a60..afd78b9 100644 --- a/examples/tcp_reverse/src/reverse_protocol.erl +++ b/examples/tcp_reverse/src/reverse_protocol.erl @@ -14,7 +14,7 @@ -export([terminate/3]). -export([code_change/4]). --define(TIMEOUT, 5000). +-define(TIMEOUT, 30000). -record(state, {socket, transport}). diff --git a/test/upgrade_SUITE.erl b/test/upgrade_SUITE.erl new file mode 100644 index 0000000..f3f849e --- /dev/null +++ b/test/upgrade_SUITE.erl @@ -0,0 +1,187 @@ +%% Copyright (c) 2020, Loïc Hoguin +%% +%% 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_get_paths(Example0) -> + Example = atom_to_list(Example0), + {ok, CWD} = file:get_cwd(), + Dir = CWD ++ "/../../examples/" ++ 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, Config) -> + Make = do_find_make_cmd(), + {Dir, Rel, _} = do_get_paths(Example), + ct:log("~s~n", [os:cmd(Make ++ " -C " ++ Dir ++ " distclean")]), + %% TERM=dumb disables relx coloring. + ct:log("~s~n", [os:cmd(Make ++ " -C " ++ Dir ++ " TERM=dumb")]), + %% @todo Should move the "1" tarball in the correct directory to avoid a warning on downgrade? + ct:log("~s~n", [os:cmd(Rel ++ " stop")]), + ct:log("~s~n", [os:cmd(Rel ++ " start")]), + timer:sleep(2000), + ct:log("~s~n", [os:cmd(Rel ++ " eval 'application:info()'")]). + +do_stop(Example) -> + {Dir, Rel, Log} = do_get_paths(Example), + ct:log("~s~n", [os:cmd("sed -i.bak s/\"2\"/\"1\"/ " ++ Dir ++ "/relx.config")]), + ct:log("~s~n", [os:cmd(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 = os:cmd("git tag | tr - \\~ | sort -V | tr \\~ -"), + ct:log("~s~n", [TagsOutput]), + Tags = string:lexemes(TagsOutput, "\n"), + DescribeOutput = os:cmd("git describe --exact-match"), + ct:log("~s~n", [DescribeOutput]), + {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), + ct:log("~s~n", [os:cmd( + "sed -i.bak s/\"dep_ranch_commit = .*\"/\"dep_ranch_commit = " + ++ Commit ++ "\"/ " ++ Dir ++ "/Makefile" + )]). + +%% Remove Ranch and rebuild, this time generating a relup. +do_build_relup(Example, CommitOrTag) -> + Make = do_find_make_cmd(), + {Dir, _, _} = do_get_paths(Example), + ct:log("~s~n", [os:cmd("rm -rf " ++ Dir ++ "/deps/ranch")]), + ct:log("~s~n", [os:cmd("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. + ct:log("~s~n", [os:cmd(Make ++ " -C " ++ Dir ++ " deps")]), + ct:log("~s~n", [os:cmd("cp " ++ Dir ++ "/../../ebin/ranch.appup " + ++ Dir ++ "/deps/ranch/ebin/")]), + case CommitOrTag of + tag -> ok; + commit -> + ProjectVersion = os:cmd("grep \"PROJECT_VERSION = \" " ++ Dir ++ "/deps/ranch/Makefile"), + ct:log(ProjectVersion), + ["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]), + ct:log("~s~n", [os:cmd( + "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. + ct:log("~s~n", [os:cmd( + "sed -i.bak s/\"" ++ Vsn0 ++ "\"/\"" ++ Vsn ++ "\"/ " + ++ Dir ++ "/deps/ranch/ebin/ranch.appup" + )]) + end, + ct:log("~s~n", [os:cmd(Make ++ " -C " ++ Dir ++ " relup")]). + +%% Copy the tarball in the correct location and upgrade. +do_upgrade(Example) -> + ExampleStr = atom_to_list(Example), + {Dir, Rel, _} = do_get_paths(Example), + ct:log("~s~n", [os:cmd("cp " + ++ Dir ++ "/_rel/" ++ ExampleStr + ++ "_example/" ++ ExampleStr ++ "_example-2.tar.gz " + ++ Dir ++ "/_rel/" ++ ExampleStr + ++ "_example/releases/2/" ++ ExampleStr ++ "_example.tar.gz")]), + ct:log("~s~n", [os:cmd(Rel ++ " upgrade \"2\"")]), + ct:log("~s~n", [os:cmd(Rel ++ " eval 'application:info()'")]). + +do_downgrade(Example) -> + {_, Rel, _} = do_get_paths(Example), + ct:log("~s~n", [os:cmd(Rel ++ " downgrade \"1\"")]), + ct:log("~s~n", [os:cmd(Rel ++ " eval 'application:info()'")]). + +%% Tests. + +upgrade_ranch_one_conn(Config) -> + Example = tcp_echo, + Port = 5555, + try + %% Build and start the example release using the previous Ranch version. + CommitOrTag = do_use_ranch_previous(Example), + do_compile_and_start(Example, Config), + %% 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_use_ranch_commit(Example, "master"), + do_build_relup(Example, CommitOrTag), + %% Perform the upgrade, then check that our connection is still up. + do_upgrade(Example), + 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]), + %% Perform the downgrade, then check that our connection is still up. + do_downgrade(Example), + 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) + end. + +%% @todo upgrade_ranch_max_conn -- cgit v1.2.3