304 lines
12 KiB
Erlang
304 lines
12 KiB
Erlang
%% -------------------------------------------------------------------
|
|
%%
|
|
%% Copyright (c) 2007-2014 Basho Technologies, Inc. All Rights Reserved.
|
|
%%
|
|
%% This file is provided to you under the Apache License,
|
|
%% Version 2.0 (the "License"); you may not use this file
|
|
%% except in compliance with the License. You may obtain
|
|
%% a copy of the License at
|
|
%%
|
|
%% http://www.apache.org/licenses/LICENSE-2.0
|
|
%%
|
|
%% Unless required by applicable law or agreed to in writing,
|
|
%% software distributed under the License is distributed on an
|
|
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
%% KIND, either express or implied. See the License for the
|
|
%% specific language governing permissions and limitations
|
|
%% under the License.
|
|
%%
|
|
%% -------------------------------------------------------------------
|
|
|
|
-module(machi_lifecycle_mgr_test).
|
|
-compile(export_all).
|
|
|
|
-ifdef(TEST).
|
|
-ifndef(PULSE).
|
|
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
|
|
-include("machi.hrl").
|
|
-include("machi_projection.hrl").
|
|
|
|
-define(MGR, machi_chain_manager1).
|
|
|
|
setup() ->
|
|
catch application:stop(machi),
|
|
{ok, SupPid} = machi_sup:start_link(),
|
|
error_logger:tty(false),
|
|
Dir = "./" ++ atom_to_list(?MODULE) ++ ".datadir",
|
|
machi_flu1_test:clean_up_data_dir(Dir ++ "/*/*"),
|
|
machi_flu1_test:clean_up_data_dir(Dir),
|
|
Envs = [{flu_data_dir, Dir ++ "/data/flu"},
|
|
{flu_config_dir, Dir ++ "/etc/flu-config"},
|
|
{chain_config_dir, Dir ++ "/etc/chain-config"},
|
|
{platform_data_dir, Dir ++ "/data"},
|
|
{platform_etc_dir, Dir ++ "/etc"},
|
|
{not_used_pending, Dir ++ "/etc/pending"}
|
|
],
|
|
EnvKeys = [K || {K,_V} <- Envs],
|
|
undefined = application:get_env(machi, yo),
|
|
Cleanup = machi_flu1_test:get_env_vars(machi, EnvKeys ++ [yo]),
|
|
[begin
|
|
filelib:ensure_dir(V ++ "/unused"),
|
|
application:set_env(machi, K, V)
|
|
end || {K, V} <- Envs],
|
|
{SupPid, Dir, Cleanup}.
|
|
|
|
cleanup({SupPid, Dir, Cleanup}) ->
|
|
exit(SupPid, normal),
|
|
machi_util:wait_for_death(SupPid, 100),
|
|
error_logger:tty(true),
|
|
catch application:stop(machi),
|
|
machi_flu1_test:clean_up_data_dir(Dir ++ "/*/*"),
|
|
machi_flu1_test:clean_up_data_dir(Dir),
|
|
machi_flu1_test:clean_up_env_vars(Cleanup),
|
|
undefined = application:get_env(machi, yo),
|
|
ok.
|
|
|
|
smoke_test_() ->
|
|
{timeout, 60, fun() -> smoke_test2() end}.
|
|
|
|
smoke_test2() ->
|
|
YoCleanup = setup(),
|
|
try
|
|
Prefix = <<"pre">>,
|
|
Chunk1 = <<"yochunk">>,
|
|
Host = "localhost",
|
|
PortBase = 60120,
|
|
|
|
Pa = #p_srvr{name=a,address="localhost",port=PortBase+0},
|
|
Pb = #p_srvr{name=b,address="localhost",port=PortBase+1},
|
|
Pc = #p_srvr{name=c,address="localhost",port=PortBase+2},
|
|
Pstores = [Pstore_a, Pstore_b, Pstore_c] =
|
|
[machi_flu_psup:make_proj_regname(a),
|
|
machi_flu_psup:make_proj_regname(b),
|
|
machi_flu_psup:make_proj_regname(c)],
|
|
ChMgrs = [ChMgr_a, ChMgr_b, ChMgr_c] =
|
|
[machi_chain_manager1:make_chmgr_regname(a),
|
|
machi_chain_manager1:make_chmgr_regname(b),
|
|
machi_chain_manager1:make_chmgr_regname(c)],
|
|
Fits = [Fit_a, Fit_b, Fit_c] =
|
|
[machi_flu_psup:make_fitness_regname(a),
|
|
machi_flu_psup:make_fitness_regname(b),
|
|
machi_flu_psup:make_fitness_regname(c)],
|
|
Advance = machi_chain_manager1_test:make_advance_fun(
|
|
Fits, [a,b,c], ChMgrs, 3),
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
io:format("\nSTEP: Start 3 FLUs, no chain.\n", []),
|
|
|
|
[machi_lifecycle_mgr:make_pending_config(P) || P <- [Pa,Pb,Pc] ],
|
|
{[_,_,_],[]} = machi_lifecycle_mgr:process_pending(),
|
|
[{ok, #projection_v1{epoch_number=0}} =
|
|
machi_projection_store:read_latest_projection(PSTORE, private)
|
|
|| PSTORE <- Pstores],
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
io:format("\nSTEP: Start chain = [a,b,c]\n", []),
|
|
|
|
C1 = #chain_def_v1{name=cx, mode=ap_mode, full=[Pa,Pb,Pc],
|
|
local_run=[a,b,c]},
|
|
machi_lifecycle_mgr:make_pending_config(C1),
|
|
{[],[_]} = machi_lifecycle_mgr:process_pending(),
|
|
Advance(),
|
|
[{ok, #projection_v1{all_members=[a,b,c]}} =
|
|
machi_projection_store:read_latest_projection(PSTORE, private)
|
|
|| PSTORE <- Pstores],
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
io:format("\nSTEP: Reset chain = [b,c]\n", []),
|
|
|
|
C2 = #chain_def_v1{name=cx, mode=ap_mode, full=[Pb,Pc],
|
|
old_full=[a,b,c], old_witnesses=[],
|
|
local_stop=[a], local_run=[b,c]},
|
|
machi_lifecycle_mgr:make_pending_config(C2),
|
|
{[],[_]} = machi_lifecycle_mgr:process_pending(),
|
|
Advance(),
|
|
%% a should be down
|
|
{'EXIT', _} = (catch machi_projection_store:read_latest_projection(
|
|
hd(Pstores), private)),
|
|
[{ok, #projection_v1{all_members=[b,c]}} =
|
|
machi_projection_store:read_latest_projection(PSTORE, private)
|
|
|| PSTORE <- tl(Pstores)],
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
io:format("\nSTEP: Reset chain = []\n", []),
|
|
|
|
C3 = #chain_def_v1{name=cx, mode=ap_mode, full=[],
|
|
old_full=[b,c], old_witnesses=[],
|
|
local_stop=[b,c], local_run=[]},
|
|
machi_lifecycle_mgr:make_pending_config(C3),
|
|
{[],[_]} = machi_lifecycle_mgr:process_pending(),
|
|
Advance(),
|
|
%% a,b,c should be down
|
|
[{'EXIT', _} = (catch machi_projection_store:read_latest_projection(
|
|
PSTORE, private))
|
|
|| PSTORE <- Pstores],
|
|
|
|
ok
|
|
after
|
|
cleanup(YoCleanup)
|
|
end.
|
|
|
|
ast_tuple_syntax_test() ->
|
|
T = fun(L) -> machi_lifecycle_mgr:check_ast_tuple_syntax(L) end,
|
|
Canon1 = [ {host, "localhost", []},
|
|
{host, "localhost", [{client_interface, "1.2.3.4"},
|
|
{admin_interface, "5.6.7.8"}]},
|
|
{flu, 'fx', "foohost", 4000, []},
|
|
switch_old_and_new,
|
|
{chain, 'cy', ['fx', 'fy'], [{foo,"yay"},{bar,baz}]} ],
|
|
|
|
{_Good,[]=_Bad} = T(Canon1),
|
|
Canon1_norm = machi_lifecycle_mgr:normalize_ast_tuple_syntax(Canon1),
|
|
true = (length(Canon1) == length(Canon1_norm)),
|
|
{Canon1_norm_b, []} = T(Canon1_norm),
|
|
true = (length(Canon1_norm) == length(Canon1_norm_b)),
|
|
|
|
{[],[_,_,_,_]} =
|
|
T([ {host, 'localhost', []},
|
|
{host, 'localhost', yo},
|
|
{host, "localhost", [{client_interface, 77.88293829832}]},
|
|
{host, "localhost", [{client_interface, "1.2.3.4"},
|
|
{bummer, "5.6.7.8"}]} ]),
|
|
{[],[_,_,_,_,_,_]} =
|
|
T([ {flu, 'fx', 'foohost', 4000, []},
|
|
{flu, 'fx', <<"foohost">>, 4000, []},
|
|
{flu, 'fx', "foohost", -4000, []},
|
|
{flu, 'fx', "foohost", 40009999, []},
|
|
{flu, 'fx', "foohost", 4000, gack},
|
|
{flu, 'fx', "foohost", 4000, [22]} ]),
|
|
{[],[_,_,_]} =
|
|
T([ {chain, 'cy', ["fx", "fy"], [foo,{bar,baz}]},
|
|
yoloyolo,
|
|
{chain, "cy", ["fx", 27], oops,arity,way,way,way,too,big,x}
|
|
]).
|
|
|
|
ast_run_test() ->
|
|
PortBase = 20300,
|
|
R1 = [
|
|
{host, "localhost", "localhost", "localhost", []},
|
|
{flu, 'f0', "localhost", PortBase+0, []},
|
|
{flu, 'f1', "localhost", PortBase+1, []},
|
|
{chain, 'ca', ['f0'], []},
|
|
{chain, 'cb', ['f1'], []},
|
|
switch_old_and_new,
|
|
{flu, 'f2', "localhost", PortBase+2, []},
|
|
{flu, 'f3', "localhost", PortBase+3, []},
|
|
{flu, 'f4', "localhost", PortBase+4, []},
|
|
{chain, 'ca', ['f0', 'f2'], []},
|
|
{chain, 'cc', ['f3', 'f4'], []}
|
|
],
|
|
|
|
{ok, Env1} = machi_lifecycle_mgr:run_ast(R1),
|
|
%% Uncomment to examine the Env trees.
|
|
%% Y1 = {lists:sort(gb_trees:to_list(element(1, Env1))),
|
|
%% lists:sort(gb_trees:to_list(element(2, Env1))),
|
|
%% element(3, Env1)},
|
|
%% io:format(user, "\nY1 ~p\n", [Y1]),
|
|
|
|
Negative_after_R1 =
|
|
[
|
|
{host, "localhost", "foo", "foo", []}, % dupe host
|
|
{flu, 'f1', "other", PortBase+9999999, []}, % bogus port # (syntax)
|
|
{flu, 'f1', "other", PortBase+888, []}, % dupe flu name
|
|
{flu, 'f7', "localhost", PortBase+1, []}, % dupe host+port
|
|
{chain, 'ca', ['f7'], []}, % unknown flu
|
|
{chain, 'cc', ['f0'], []}, % flu previously assigned
|
|
{chain, 'ca', cp_mode, ['f0', 'f1', 'f2'], [], []} % mode change
|
|
],
|
|
[begin
|
|
%% io:format(user, "dbg: Neg ~p\n", [Neg]),
|
|
{error, _} = machi_lifecycle_mgr:run_ast(R1 ++ [Neg])
|
|
end || Neg <- Negative_after_R1],
|
|
|
|
%% The 'run' phase doesn't blow smoke. What about 'diff'?
|
|
{X1a, X1b} = machi_lifecycle_mgr:diff_env(Env1, "localhost"),
|
|
%% There's only one host, "localhost", so 'all' should be exactly equal.
|
|
{X1a, X1b} = machi_lifecycle_mgr:diff_env(Env1, all),
|
|
%% io:format(user, "X1b: ~p\n", [X1b]),
|
|
|
|
%% Append to the R1 scenario: for chain cc: add f5, remove f4
|
|
%% Expect: see pattern matching below on X2b.
|
|
R2 = (R1 -- [switch_old_and_new]) ++
|
|
[switch_old_and_new,
|
|
{flu, 'f5', "localhost", PortBase+5, []},
|
|
{chain, 'cc', ['f3','f5'], []}],
|
|
{ok, Env2} = machi_lifecycle_mgr:run_ast(R2),
|
|
{_X2a, X2b} = machi_lifecycle_mgr:diff_env(Env2, "localhost"),
|
|
%% io:format(user, "X2b: ~p\n", [X2b]),
|
|
F5_port = PortBase+5,
|
|
[#p_srvr{name='f5',address="localhost",port=F5_port},
|
|
#chain_def_v1{name='cc',
|
|
full=[#p_srvr{name='f3'},#p_srvr{name='f5'}], witnesses=[],
|
|
old_full=[f3,f4], old_witnesses=[],
|
|
local_run=[f5], local_stop=[f4]}] = X2b,
|
|
|
|
ok.
|
|
|
|
ast_then_apply_test_() ->
|
|
{timeout, 60, fun() -> ast_then_apply_test2() end}.
|
|
|
|
ast_then_apply_test2() ->
|
|
YoCleanup = setup(),
|
|
try
|
|
PortBase = 20400,
|
|
NumChains = 4,
|
|
ChainLen = 3,
|
|
FLU_num = NumChains * ChainLen,
|
|
FLU_defs = [{flu, list_to_atom("f"++integer_to_list(X)),
|
|
"localhost", PortBase+X, []} || X <- lists:seq(1,FLU_num)],
|
|
FLU_names = [FLU || {flu,FLU,_,_,_} <- FLU_defs],
|
|
Ch_defs = [{chain, list_to_atom("c"++integer_to_list(X)),
|
|
lists:sublist(FLU_names, X, 3),
|
|
[]} || X <- lists:seq(1, FLU_num, 3)],
|
|
|
|
R1 = [switch_old_and_new,
|
|
{host, "localhost", "localhost", "localhost", []}]
|
|
++ FLU_defs ++ Ch_defs,
|
|
{ok, Env1} = machi_lifecycle_mgr:run_ast(R1),
|
|
{_X1a, X1b} = machi_lifecycle_mgr:diff_env(Env1, "localhost"),
|
|
%% io:format(user, "X1b ~p\n", [X1b]),
|
|
[machi_lifecycle_mgr:make_pending_config(X) || X <- X1b],
|
|
{PassFLUs, PassChains} = machi_lifecycle_mgr:process_pending(),
|
|
true = (length(PassFLUs) == length(FLU_defs)),
|
|
true = (length(PassChains) == length(Ch_defs)),
|
|
|
|
%% Kick the chain managers into doing something useful right now.
|
|
Pstores = [list_to_atom(atom_to_list(X) ++ "_pstore") || X <- FLU_names],
|
|
Fits = [list_to_atom(atom_to_list(X) ++ "_fitness") || X <- FLU_names],
|
|
ChMgrs = [list_to_atom(atom_to_list(X) ++ "_chmgr") || X <- FLU_names],
|
|
Advance = machi_chain_manager1_test:make_advance_fun(
|
|
Fits, FLU_names, ChMgrs, 3),
|
|
Advance(),
|
|
|
|
%% Sanity check: everyone is configured properly.
|
|
[begin
|
|
{ok, #projection_v1{epoch_number=Epoch, all_members=All,
|
|
chain_name=ChainName, upi=UPI}} =
|
|
machi_projection_store:read_latest_projection(PStore, private),
|
|
%% io:format(user, "~p: epoch ~p all ~p\n", [PStore, Epoch, All]),
|
|
true = Epoch > 0,
|
|
ChainLen = length(All),
|
|
true = (length(UPI) > 0),
|
|
{chain, _, Full, []} = lists:keyfind(ChainName, 2, Ch_defs),
|
|
true = lists:sort(Full) == lists:sort(All)
|
|
end || PStore <- Pstores],
|
|
|
|
ok
|
|
after
|
|
cleanup(YoCleanup)
|
|
end.
|
|
|
|
-endif. % !PULSE
|
|
-endif. % TEST
|