WIP: 'Run' AST thingie ha, take that, wheel!
This commit is contained in:
parent
9cec53eea6
commit
c37f23d97a
2 changed files with 180 additions and 16 deletions
|
@ -620,31 +620,173 @@ delete_chain_config(Name, File, S) ->
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
check_ast_tuple_syntax(Ts) ->
|
check_ast_tuple_syntax(Ts) ->
|
||||||
lists:partition(fun check_a_ast_tuple/1, Ts).
|
lists:partition(fun check_an_ast_tuple/1, Ts).
|
||||||
|
|
||||||
check_a_ast_tuple({host, Name, Props}) ->
|
check_an_ast_tuple({host, Name, Props}) ->
|
||||||
is_stringy(Name) andalso is_proplisty(Props) andalso
|
is_stringy(Name) andalso is_proplisty(Props) andalso
|
||||||
lists:all(fun({admin_interface, X}) -> is_stringy(X);
|
lists:all(fun({admin_interface, X}) -> is_stringy(X);
|
||||||
({client_interface, X}) -> is_stringy(X);
|
({client_interface, X}) -> is_stringy(X);
|
||||||
(_) -> false
|
(_) -> false
|
||||||
end, Props);
|
end, Props);
|
||||||
check_a_ast_tuple({flu, Name, HostName, Port, Props}) ->
|
check_an_ast_tuple({host, Name, AdminI, ClientI, Props}) ->
|
||||||
|
is_stringy(Name) andalso
|
||||||
|
is_stringy(AdminI) andalso is_stringy(ClientI) andalso
|
||||||
|
is_proplisty(Props) andalso
|
||||||
|
lists:all(fun({admin_interface, X}) -> is_stringy(X);
|
||||||
|
({client_interface, X}) -> is_stringy(X);
|
||||||
|
(_) -> false
|
||||||
|
end, Props);
|
||||||
|
check_an_ast_tuple({flu, Name, HostName, Port, Props}) ->
|
||||||
is_stringy(Name) andalso is_stringy(HostName) andalso
|
is_stringy(Name) andalso is_stringy(HostName) andalso
|
||||||
is_porty(Port) andalso is_proplisty(Props);
|
is_porty(Port) andalso is_proplisty(Props);
|
||||||
check_a_ast_tuple({chain, Name, AddList, RemoveList, Props}) ->
|
check_an_ast_tuple({chain, Name, AddList, RemoveList, Props}) ->
|
||||||
is_stringy(Name) andalso
|
is_stringy(Name) andalso
|
||||||
lists:all(fun is_stringy/1, AddList) andalso
|
lists:all(fun is_stringy/1, AddList) andalso
|
||||||
lists:all(fun is_stringy/1, RemoveList) andalso
|
lists:all(fun is_stringy/1, RemoveList) andalso
|
||||||
is_proplisty(Props);
|
is_proplisty(Props);
|
||||||
check_a_ast_tuple(_) ->
|
check_an_ast_tuple({chain, Name, CMode, AddList, Add_Witnesses,
|
||||||
|
RemoveList, Remove_Witnesses, Props}) ->
|
||||||
|
is_stringy(Name) andalso
|
||||||
|
(CMode == ap_mode orelse CMode == cp_mode) andalso
|
||||||
|
lists:all(fun is_stringy/1, AddList) andalso
|
||||||
|
lists:all(fun is_stringy/1, Add_Witnesses) andalso
|
||||||
|
lists:all(fun is_stringy/1, RemoveList) andalso
|
||||||
|
lists:all(fun is_stringy/1, Remove_Witnesses) andalso
|
||||||
|
is_proplisty(Props);
|
||||||
|
check_an_ast_tuple(switch_old_and_new) ->
|
||||||
|
true;
|
||||||
|
check_an_ast_tuple(_) ->
|
||||||
false.
|
false.
|
||||||
|
|
||||||
|
%% Prerequisite: all tuples are approved by check_ast_tuple_syntax().
|
||||||
|
|
||||||
|
normalize_ast_tuple_syntax(Ts) ->
|
||||||
|
lists:map(fun normalize_an_ast_tuple/1, Ts).
|
||||||
|
|
||||||
|
normalize_an_ast_tuple({host, Name, Props}) ->
|
||||||
|
AdminI = proplists:get_value(admin_interface, Props, Name),
|
||||||
|
ClientI = proplists:get_value(client_interface, Props, Name),
|
||||||
|
Props2 = lists:keydelete(admin_interface, 1,
|
||||||
|
lists:keydelete(client_interface, 1, Props)),
|
||||||
|
{host, Name, AdminI, ClientI, n(Props2)};
|
||||||
|
normalize_an_ast_tuple({host, Name, AdminI, ClientI, Props}) ->
|
||||||
|
Props2 = lists:keydelete(admin_interface, 1,
|
||||||
|
lists:keydelete(client_interface, 1, Props)),
|
||||||
|
{host, Name, AdminI, ClientI, n(Props2)};
|
||||||
|
normalize_an_ast_tuple({flu, Name, HostName, Port, Props}) ->
|
||||||
|
{flu, Name, HostName, Port, n(Props)};
|
||||||
|
normalize_an_ast_tuple({chain, Name, AddList, RemoveList, Props}) ->
|
||||||
|
{chain, Name, ap_mode, n(AddList), [], n(RemoveList), [], n(Props)};
|
||||||
|
normalize_an_ast_tuple({chain, Name, CMode, AddList, Add_Witnesses,
|
||||||
|
RemoveList, Remove_Witnesses, Props}) ->
|
||||||
|
{chain, Name, CMode, n(AddList), n(Add_Witnesses),
|
||||||
|
n(RemoveList), n(Remove_Witnesses), n(Props)};
|
||||||
|
normalize_an_ast_tuple(A=switch_old_and_new) ->
|
||||||
|
A.
|
||||||
|
|
||||||
|
run_ast(Ts) ->
|
||||||
|
{_, []} = check_ast_tuple_syntax(Ts),
|
||||||
|
Ts2 = normalize_ast_tuple_syntax(Ts),
|
||||||
|
Env1 = make_ast_run_env(),
|
||||||
|
try
|
||||||
|
Env2 = lists:foldl(fun run_ast_cmd/2, Env1, Ts2),
|
||||||
|
{ok, Env2}
|
||||||
|
catch throw:DbgStuff ->
|
||||||
|
{error, DbgStuff}
|
||||||
|
end.
|
||||||
|
|
||||||
|
run_ast_cmd({host, Name, _AdminI, _ClientI, _Props}=T, E) ->
|
||||||
|
Key = {kv, {host, Name}},
|
||||||
|
case d_find(Key, E) of
|
||||||
|
error ->
|
||||||
|
d_store(Key, T, E);
|
||||||
|
{ok, _} ->
|
||||||
|
err("Duplicate host definition ~p: ~p", [Name], T)
|
||||||
|
end;
|
||||||
|
run_ast_cmd({flu, Name, HostName, Port, _Props}=T, E) ->
|
||||||
|
Key = {kv, {flu, Name}},
|
||||||
|
HostExists_p = env_host_exists(HostName, E),
|
||||||
|
case d_find(Key, E) of
|
||||||
|
error when HostExists_p ->
|
||||||
|
case host_port_is_assigned(HostName, Port, E) of
|
||||||
|
false ->
|
||||||
|
d_store(Key, T, E);
|
||||||
|
{true, UsedBy} ->
|
||||||
|
err("Host ~p port ~p already in use by FLU ~p",
|
||||||
|
[HostName, Port, UsedBy], T)
|
||||||
|
end;
|
||||||
|
error ->
|
||||||
|
err("Unknown host ~p", [HostName], T);
|
||||||
|
{ok, _} ->
|
||||||
|
err("Duplicate flu ~p", [Name], T)
|
||||||
|
end;
|
||||||
|
run_ast_cmd(switch_old_and_new, E) ->
|
||||||
|
switch_env_dict(E);
|
||||||
|
run_ast_cmd(Unknown, _E) ->
|
||||||
|
err("Unknown AST thingie", [], Unknown).
|
||||||
|
|
||||||
|
make_ast_run_env() ->
|
||||||
|
{_KV_old=dict:new(), _KV_new=dict:new(), _IsNew=false}.
|
||||||
|
|
||||||
|
env_host_exists(HostName, E) ->
|
||||||
|
Key = {kv, {host, HostName}},
|
||||||
|
case d_find(Key, E) of
|
||||||
|
error ->
|
||||||
|
false;
|
||||||
|
{ok, _} ->
|
||||||
|
true
|
||||||
|
end.
|
||||||
|
|
||||||
|
host_port_is_assigned(HostName, Port, {KV_old, KV_new, _}) ->
|
||||||
|
L = dict:to_list(KV_old) ++ dict:to_list(KV_new),
|
||||||
|
FLU_Ts = [V || {{kv, {flu, _}}, V} <- L],
|
||||||
|
case [V || {flu, _Nm, Host_, Port_, _Ps}=V <- FLU_Ts,
|
||||||
|
Host_ == HostName, Port_ == Port] of
|
||||||
|
[{flu, Name, _Host, _Port, _Ps}] ->
|
||||||
|
{true, Name};
|
||||||
|
[] ->
|
||||||
|
false
|
||||||
|
end.
|
||||||
|
|
||||||
|
d_find(Key, {KV_old, KV_new, IsNew}) ->
|
||||||
|
case dict:find(Key, KV_new) of
|
||||||
|
{ok, Val} when IsNew ->
|
||||||
|
Val;
|
||||||
|
_ ->
|
||||||
|
dict:find(Key, KV_old)
|
||||||
|
end.
|
||||||
|
|
||||||
|
d_store(Key, Val, {KV_old, KV_new, false}) ->
|
||||||
|
{dict:store(Key, Val, KV_old), KV_new, false};
|
||||||
|
d_store(Key, Val, {KV_old, KV_new, true}) ->
|
||||||
|
{KV_old, dict:store(Key, Val, KV_new), true}.
|
||||||
|
|
||||||
|
switch_env_dict({KV_old, KV_new, false}) ->
|
||||||
|
{KV_old, KV_new, true};
|
||||||
|
switch_env_dict({_, _, true}) ->
|
||||||
|
A = switch_old_and_new,
|
||||||
|
err("Duplicate ~p", [A], A).
|
||||||
|
|
||||||
|
n(L) ->
|
||||||
|
lists:sort(L).
|
||||||
|
|
||||||
|
err(Fmt, Args, AST) ->
|
||||||
|
throw({lists:flatten(io_lib:format(Fmt, Args)), AST}).
|
||||||
|
|
||||||
|
%% We won't allow 'atom' style proplist members: too difficult to normalize.
|
||||||
|
%% Also, no duplicates, again because normalizing useful for checksums but
|
||||||
|
%% bad for order-based traditional proplists (first key wins).
|
||||||
|
|
||||||
is_proplisty(Props) ->
|
is_proplisty(Props) ->
|
||||||
is_list(Props) andalso
|
is_list(Props) andalso
|
||||||
lists:all(fun({_,_}) -> true;
|
lists:all(fun({_,_}) -> true;
|
||||||
(X) when is_atom(X) -> true;
|
%% nope: (X) when is_atom(X) -> true;
|
||||||
(_) -> false
|
(_) -> false
|
||||||
end, Props).
|
end, Props) andalso
|
||||||
|
begin
|
||||||
|
Ks = [K || {K,_V} <- Props],
|
||||||
|
lists:sort(Ks) == lists:usort(Ks)
|
||||||
|
end.
|
||||||
|
|
||||||
is_stringy(L) ->
|
is_stringy(L) ->
|
||||||
is_list(L) andalso
|
is_list(L) andalso
|
||||||
|
|
|
@ -48,7 +48,7 @@ smoke_test2() ->
|
||||||
{platform_etc_dir, Dir ++ "/etc"},
|
{platform_etc_dir, Dir ++ "/etc"},
|
||||||
{not_used_pending, Dir ++ "/etc/pending"}
|
{not_used_pending, Dir ++ "/etc/pending"}
|
||||||
],
|
],
|
||||||
EnvKeys = [K || {K, V} <- Envs],
|
EnvKeys = [K || {K,_V} <- Envs],
|
||||||
undefined = application:get_env(machi, yo),
|
undefined = application:get_env(machi, yo),
|
||||||
Cleanup = machi_flu1_test:get_env_vars(machi, EnvKeys ++ [yo]),
|
Cleanup = machi_flu1_test:get_env_vars(machi, EnvKeys ++ [yo]),
|
||||||
[begin
|
[begin
|
||||||
|
@ -153,12 +153,19 @@ make_pending_config(Term) ->
|
||||||
|
|
||||||
ast_tuple_syntax_test() ->
|
ast_tuple_syntax_test() ->
|
||||||
T = fun(L) -> machi_lifecycle_mgr:check_ast_tuple_syntax(L) end,
|
T = fun(L) -> machi_lifecycle_mgr:check_ast_tuple_syntax(L) end,
|
||||||
{_Good,[]=_Bad} =
|
Canon1 = [ {host, "localhost", []},
|
||||||
T([ {host, "localhost", []},
|
{host, "localhost", [{client_interface, "1.2.3.4"},
|
||||||
{host, "localhost", [{client_interface, "1.2.3.4"},
|
{admin_interface, "5.6.7.8"}]},
|
||||||
{admin_interface, "5.6.7.8"}]},
|
{flu, "fx", "foohost", 4000, []},
|
||||||
{flu, "fx", "foohost", 4000, []},
|
switch_old_and_new,
|
||||||
{chain, "cy", ["fx", "fy"], ["fz"], [foo,{bar,baz}]} ]),
|
{chain, "cy", ["fx", "fy"], ["fz"], [{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', []},
|
T([ {host, 'localhost', []},
|
||||||
{host, 'localhost', yo},
|
{host, 'localhost', yo},
|
||||||
|
@ -172,10 +179,25 @@ ast_tuple_syntax_test() ->
|
||||||
{flu, "fx", "foohost", 40009999, []},
|
{flu, "fx", "foohost", 40009999, []},
|
||||||
{flu, "fx", "foohost", 4000, gack},
|
{flu, "fx", "foohost", 4000, gack},
|
||||||
{flu, "fx", "foohost", 4000, [22]} ]),
|
{flu, "fx", "foohost", 4000, [22]} ]),
|
||||||
{[],[_,_]} =
|
{[],[_,_,_]} =
|
||||||
T([ {chain, 'cy', ["fx", "fy"], ["fz"], [foo,{bar,baz}]},
|
T([ {chain, 'cy', ["fx", "fy"], ["fz"], [foo,{bar,baz}]},
|
||||||
|
yoloyolo,
|
||||||
{chain, "cy", ["fx", 27], ["fz"], oops,arity,way,way,way,too,big,x}
|
{chain, "cy", ["fx", 27], ["fz"], oops,arity,way,way,way,too,big,x}
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
ast_run_test() ->
|
||||||
|
PortBase = 20300,
|
||||||
|
R1 = [
|
||||||
|
{host, "localhost", "localhost", "localhost", []},
|
||||||
|
switch_old_and_new,
|
||||||
|
{flu, "f1", "localhost", PortBase+0, []},
|
||||||
|
{flu, "f2", "localhost", PortBase+1, []}
|
||||||
|
],
|
||||||
|
{ok, X1} = machi_lifecycle_mgr:run_ast(R1),
|
||||||
|
Y1 = {lists:sort(dict:to_list(element(1, X1))),
|
||||||
|
lists:sort(dict:to_list(element(2, X1))),
|
||||||
|
element(3, X1)},
|
||||||
|
io:format(user, "\nY1 ~p\n", [Y1]).
|
||||||
|
|
||||||
-endif. % !PULSE
|
-endif. % !PULSE
|
||||||
-endif. % TEST
|
-endif. % TEST
|
||||||
|
|
Loading…
Reference in a new issue