upgrade_SUITE.erl 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. %% Copyright (c) 2020-2021, Loïc Hoguin <essen@ninenines.eu>
  2. %% Copyright (c) 2021, Maria Scott <maria-12648430@hnc-agency.org>
  3. %%
  4. %% Permission to use, copy, modify, and/or distribute this software for any
  5. %% purpose with or without fee is hereby granted, provided that the above
  6. %% copyright notice and this permission notice appear in all copies.
  7. %%
  8. %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. -module(upgrade_SUITE).
  16. -compile(export_all).
  17. -compile(nowarn_export_all).
  18. -import(ct_helper, [doc/1]).
  19. %% ct.
  20. all() ->
  21. ct_helper:all(?MODULE).
  22. init_per_suite(Config) ->
  23. %% Remove environment variables inherited from Erlang.mk.
  24. os:unsetenv("ERLANG_MK_TMP"),
  25. os:unsetenv("APPS_DIR"),
  26. os:unsetenv("DEPS_DIR"),
  27. os:unsetenv("ERL_LIBS"),
  28. os:unsetenv("CI_ERLANG_MK"),
  29. %% Ensure we are using the C locale for all os:cmd calls.
  30. os:putenv("LC_ALL", "C"),
  31. Config.
  32. end_per_suite(_Config) ->
  33. ok.
  34. %% Find GNU Make.
  35. do_find_make_cmd() ->
  36. case os:getenv("MAKE") of
  37. false ->
  38. case os:find_executable("gmake") of
  39. false -> "make";
  40. Cmd -> Cmd
  41. end;
  42. Cmd ->
  43. Cmd
  44. end.
  45. %% Manipulate the release.
  46. do_copy(Example0) ->
  47. Example = atom_to_list(Example0),
  48. {ok, CWD} = file:get_cwd(),
  49. _ = do_exec_log("cp -R " ++ CWD ++ "/../../examples/" ++ Example ++ " " ++ CWD),
  50. Dir = CWD ++ "/" ++ Example,
  51. _ = do_exec_log("sed -i.bak s/\"include \\.\\.\\/\\.\\.\\/erlang.mk\"/\"include ..\\/..\\/..\\/erlang.mk\"/ " ++ Dir ++ "/Makefile"),
  52. ok.
  53. do_remove(Example0) ->
  54. Example = atom_to_list(Example0),
  55. {ok, CWD} = file:get_cwd(),
  56. _ = do_exec_log("rm -rf " ++ CWD ++ "/" ++ Example),
  57. ok.
  58. do_get_paths(Example0) ->
  59. Example = atom_to_list(Example0),
  60. {ok, CWD} = file:get_cwd(),
  61. Dir = CWD ++ "/" ++ Example,
  62. Rel = Dir ++ "/_rel/" ++ Example ++ "_example/bin/" ++ Example ++ "_example",
  63. Log = Dir ++ "/_rel/" ++ Example ++ "_example/log/erlang.log.1",
  64. {Dir, Rel, Log}.
  65. do_compile_and_start(Example) ->
  66. Make = do_find_make_cmd(),
  67. {Dir, Rel, _} = do_get_paths(Example),
  68. _ = do_exec_log(Make ++ " -C " ++ Dir ++ " distclean"),
  69. %% TERM=dumb disables relx coloring.
  70. _ = do_exec_log(Make ++ " -C " ++ Dir ++ " TERM=dumb"),
  71. %% For some reason the release has ExampleStr.boot
  72. %% while the downgrade expects start.boot?
  73. ExampleStr = atom_to_list(Example),
  74. _ = do_exec_log("cp "
  75. ++ Dir ++ "/_rel/" ++ ExampleStr
  76. ++ "_example/releases/1/" ++ ExampleStr ++ "_example.boot "
  77. ++ Dir ++ "/_rel/" ++ ExampleStr
  78. ++ "_example/releases/1/start.boot"),
  79. _ = do_exec_log(Rel ++ " stop"),
  80. _ = do_exec_log(Rel ++ " daemon"),
  81. timer:sleep(2000),
  82. _ = do_exec_log(Rel ++ " eval 'application:info()'"),
  83. ok.
  84. do_stop(Example) ->
  85. {Dir, Rel, Log} = do_get_paths(Example),
  86. _ = do_exec_log("sed -i.bak s/\"2\"/\"1\"/ " ++ Dir ++ "/relx.config"),
  87. _ = do_exec_log(Rel ++ " stop"),
  88. ct:log("~s~n", [element(2, file:read_file(Log))]).
  89. %% When we are on a tag (git describe --exact-match succeeds),
  90. %% we use the tag before that as a starting point. Otherwise
  91. %% we use the most recent tag.
  92. do_use_ranch_previous(Example) ->
  93. _ = do_exec_log("git fetch --tags"), %% Ensure we have all tags.
  94. TagsOutput = do_exec_log("git tag | tr - \\~ | sort -V | tr \\~ -"),
  95. Tags = string:lexemes(TagsOutput, "\n"),
  96. DescribeOutput = do_exec_log("git describe --exact-match"),
  97. {CommitOrTag, Prev} = case DescribeOutput of
  98. "fatal: no tag exactly matches " ++ _ -> {commit, hd(lists:reverse(Tags))};
  99. _ -> {tag, hd(tl(lists:reverse(Tags)))}
  100. end,
  101. do_use_ranch_commit(Example, Prev),
  102. CommitOrTag.
  103. %% Replace the current Ranch commit with the one given as argument.
  104. do_use_ranch_commit(Example, Commit) ->
  105. {Dir, _, _} = do_get_paths(Example),
  106. _ = do_exec_log(
  107. "sed -i.bak s/\"dep_ranch_commit = .*\"/\"dep_ranch_commit = "
  108. ++ Commit ++ "\"/ " ++ Dir ++ "/Makefile"
  109. ),
  110. ok.
  111. %% Remove Ranch and rebuild, this time generating a relup.
  112. do_build_relup(Example, CommitOrTag) ->
  113. Make = do_find_make_cmd(),
  114. {Dir, _, _} = do_get_paths(Example),
  115. _ = do_exec_log("rm -rf " ++ Dir ++ "/deps/ranch/*"),
  116. _ = do_exec_log("sed -i.bak s/\"1\"/\"2\"/ " ++ Dir ++ "/relx.config"),
  117. %% We need Ranch to be fetched first in order to copy the current appup
  118. %% and optionally update its version when we are not on a tag.
  119. _ = do_exec_log("cp -R "
  120. ++ Dir ++ "/../../../Makefile "
  121. ++ Dir ++ "/../../../erlang.mk "
  122. ++ Dir ++ "/../../../src "
  123. ++ Dir ++ "/deps/ranch/"),
  124. _ = do_exec_log(Make ++ " -C " ++ Dir ++ " deps"),
  125. _ = case CommitOrTag of
  126. tag -> ok;
  127. commit ->
  128. %% Force the rebuild of Ranch.
  129. _ = do_exec_log(Make ++ " -C " ++ Dir ++ "/deps/ranch clean"),
  130. %% Update the Ranch version so that the upgrade can be applied.
  131. ProjectVersion = do_exec_log("grep \"PROJECT_VERSION = \" " ++ Dir ++ "/deps/ranch/Makefile"),
  132. ["PROJECT_VERSION = " ++ Vsn0|_] = string:lexemes(ProjectVersion, "\n"),
  133. [A, B|Tail] = string:lexemes(Vsn0, "."),
  134. Vsn = binary_to_list(iolist_to_binary([A, $., B, ".9", lists:join($., Tail)])),
  135. ct:log("Changing Ranch version from ~s to ~s~n", [Vsn0, Vsn]),
  136. _ = do_exec_log(
  137. "sed -i.bak s/\"PROJECT_VERSION = .*\"/\"PROJECT_VERSION = " ++ Vsn ++ "\"/ "
  138. ++ Dir ++ "/deps/ranch/Makefile"
  139. ),
  140. %% The version in the appup must be the same as PROJECT_VERSION.
  141. _ = do_exec_log(
  142. "sed -i.bak s/\"" ++ Vsn0 ++ "\"/\"" ++ Vsn ++ "\"/ "
  143. ++ Dir ++ "/deps/ranch/src/ranch.appup"
  144. )
  145. end,
  146. _ = do_exec_log(Make ++ " -C " ++ Dir ++ " relup"),
  147. ok.
  148. %% Copy the tarball in the correct location and upgrade.
  149. do_upgrade(Example) ->
  150. ExampleStr = atom_to_list(Example),
  151. {Dir, Rel, _} = do_get_paths(Example),
  152. _ = do_exec_log("cp "
  153. ++ Dir ++ "/_rel/" ++ ExampleStr
  154. ++ "_example/" ++ ExampleStr ++ "_example-2.tar.gz "
  155. ++ Dir ++ "/_rel/" ++ ExampleStr
  156. ++ "_example/releases/2/" ++ ExampleStr ++ "_example.tar.gz"),
  157. _ = do_exec_log(Rel ++ " upgrade \"2\""),
  158. _ = do_exec_log(Rel ++ " eval 'application:info()'"),
  159. ok.
  160. do_downgrade(Example) ->
  161. {_, Rel, _} = do_get_paths(Example),
  162. _ = do_exec_log(Rel ++ " downgrade \"1\""),
  163. _ = do_exec_log(Rel ++ " eval 'application:info()'"),
  164. ok.
  165. %% Tests.
  166. upgrade_ranch_one_conn(_) ->
  167. case os:type() of
  168. {win32, nt} ->
  169. {skip, "This test suite is not currently supported on Windows."};
  170. _ ->
  171. do_upgrade_ranch_one_conn()
  172. end.
  173. do_upgrade_ranch_one_conn() ->
  174. Example = tcp_echo,
  175. % ExampleStr = atom_to_list(Example),
  176. Port = 5555,
  177. % {_, Rel, _} = do_get_paths(Example),
  178. try
  179. %% Copy the example.
  180. do_copy(Example),
  181. %% Build and start the example release using the previous Ranch version.
  182. CommitOrTag = do_use_ranch_previous(Example),
  183. do_compile_and_start(Example),
  184. %% Establish a connection and check that it works.
  185. {ok, S1} = gen_tcp:connect("localhost", Port, [{active, false}, binary]),
  186. ok = gen_tcp:send(S1, "Hello!"),
  187. {ok, <<"Hello!">>} = gen_tcp:recv(S1, 0, 1000),
  188. %% Update Ranch to master then build a release upgrade.
  189. do_build_relup(Example, CommitOrTag),
  190. %% Perform the upgrade, then check that our connection is still up.
  191. do_upgrade(Example),
  192. ok = gen_tcp:send(S1, "Hello!"),
  193. {ok, <<"Hello!">>} = gen_tcp:recv(S1, 0, 1000),
  194. %% Check that new connections are still accepted.
  195. {ok, S2} = gen_tcp:connect("localhost", Port, [{active, false}, binary]),
  196. %% Perform the downgrade, then check that our connections are still up.
  197. do_downgrade(Example),
  198. ok = gen_tcp:send(S1, "Hello!"),
  199. {ok, <<"Hello!">>} = gen_tcp:recv(S1, 0, 1000),
  200. ok = gen_tcp:send(S2, "Hello!"),
  201. {ok, <<"Hello!">>} = gen_tcp:recv(S2, 0, 1000),
  202. %% Check that new connections are still accepted.
  203. {ok, _} = gen_tcp:connect("localhost", Port, [{active, false}, binary]),
  204. ok
  205. after
  206. do_stop(tcp_echo),
  207. do_remove(Example)
  208. end.
  209. %% @todo upgrade_ranch_max_conn
  210. do_exec_log(Cmd) ->
  211. ct:log("Command: ~s~n", [Cmd]),
  212. Out=os:cmd(Cmd),
  213. ct:log("Output:~n~n~s~n", [Out]),
  214. Out.