Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ integration_test:
./integration_test/test_amoc_cluster.sh
./integration_test/test_distribute_scenario.sh
./integration_test/test_run_scenario.sh
./integration_test/test_update_settings.sh
./integration_test/test_add_new_node.sh

rerun_integration_test:
Expand All @@ -53,6 +54,7 @@ rerun_integration_test:
./integration_test/test_amoc_cluster.sh
./integration_test/test_distribute_scenario.sh
./integration_test/test_run_scenario.sh
./integration_test/test_update_settings.sh
./integration_test/test_add_new_node.sh

dialyzer:
Expand Down
2 changes: 1 addition & 1 deletion integration_test/build_docker_image.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ source "$(dirname "$0")/helper.sh"
enable_strict_mode
cd "$git_root"

otp_vsn="${OTP_RELEASE:-25.3}"
otp_vsn="${OTP_RELEASE:-28}"
echo "ERLANG/OTP '${otp_vsn}'"

docker build \
Expand Down
4 changes: 2 additions & 2 deletions integration_test/extra_code_paths/path1/dummy_helper.erl
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@ test_amoc_dist() ->
?assertEqual(N2, N1 + 20, ?comment(Users2)),
%% remove 10 users
remove_and_wait(Master, 10),
{N3, Max3, Nodes3, _Ids3, Users3} = get_users_info(Slaves),
{N3, _Max3, Nodes3, _Ids3, Users3} = get_users_info(Slaves),
?assertEqual(N2 - 10, N3, ?comment(Users3)),
?assertEqual(Max2, Max3, ?comment(Users3)),
?assertEqual(Nodes2, Nodes3, ?comment(Users3)),
%% try to remove N3 users
Ret = remove_and_wait(Master, N3),
Expand Down Expand Up @@ -67,6 +66,7 @@ get_users_info(SlaveNodes) ->
Nodes = lists:usort([Node || {Node, Users} <- Distrib, [] =/= Users]),
N = length(Ids),
MaxId = lists:max(Ids),
%% logger:error(#{n => N, max_id => MaxId, nodes => Nodes, ids => Ids, distrib => Distrib}),
{N, MaxId, Nodes, Ids, Distrib}.

add_and_wait(Master, Num) ->
Expand Down
15 changes: 12 additions & 3 deletions integration_test/extra_code_paths/path2/dummy_scenario.erl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
-module(dummy_scenario).
-behaviour(amoc_scenario).

Check warning on line 2 in integration_test/extra_code_paths/path2/dummy_scenario.erl

View workflow job for this annotation

GitHub Actions / integration test on OTP 28

behaviour amoc_scenario undefined

Check warning on line 2 in integration_test/extra_code_paths/path2/dummy_scenario.erl

View workflow job for this annotation

GitHub Actions / integration test on OTP 26

behaviour amoc_scenario undefined

Check warning on line 2 in integration_test/extra_code_paths/path2/dummy_scenario.erl

View workflow job for this annotation

GitHub Actions / integration test on OTP 27

behaviour amoc_scenario undefined

%% var1 must be equal to one of the values in the verification list
%% and the default value is def1
Expand Down Expand Up @@ -29,7 +29,15 @@
#{name => var6, description => "description6"},
#{name => nodes, description => "this variable is set for docker "
"container via AMOC_NODES env"},
#{name => test, description => "this one to be set via REST API"}]).
#{name => test1, description => "this variable is to be set via initial settings"},
#{name => test2, update => none, description => "this one to be updated"}]).


-required_variable([
#{name => global_test1, scope => global,
description => "this variable is to be set via initial settings"},
#{name => global_test2, scope => global, update => none,
description => "this one to be updated"}]).

%% parameter verification method
-export([test_verification_function/1]).
Expand Down Expand Up @@ -63,8 +71,9 @@
%% results in exception.
{invalid_setting, undeclared_variable} =
(catch amoc_config:get(undeclared_variable)),
%% this variable is set via REST API
<<"test_value">> = amoc_config:get(test),
%% this variable is set via settings
<<"test_value2">> = amoc_config:get(test1),

%% dummy_var variable is defined in the dummy_helper module.
%% if dummy_helper is not propagated, then this call crashes
default_value = amoc_config:get(dummy_var),
Expand Down
8 changes: 7 additions & 1 deletion integration_test/test_add_new_node.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ enable_strict_mode
docker_compose up --wait --wait-timeout 100 amoc-worker-3

amoc_eval amoc-worker-3 "amoc_controller:get_status()." | contains_all dummy_scenario running
amoc_eval amoc-worker-3 "binary_to_list(amoc_config:get(test))." | contains_all "test_value"


amoc_eval amoc-worker-3 "binary_to_list(amoc_config:get(global_test1))." | contains_all "test_value1"
amoc_eval amoc-worker-3 "binary_to_list(amoc_config:get(global_test2))." | contains_all "test_value3"
amoc_eval amoc-worker-3 "binary_to_list(amoc_config:get(test1))." | contains_all "test_value2"
amoc_eval amoc-worker-3 "atom_to_list(amoc_config:get(test2))." | contains_all "undefined"

amoc_eval amoc-worker-3 "dummy_helper:test_amoc_dist()." | contains_all 'amoc_dist_works_as_expected'
echo "amoc_dist_works_as_expected"
9 changes: 5 additions & 4 deletions integration_test/test_run_scenario.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
source "$(dirname "$0")/helper.sh"
enable_strict_mode

#############################
## amoc REST API functions ##
#############################
####################
## amoc functions ##
####################
run_scenario() {
amoc_eval "$1" "amoc_dist:do(${2}, ${3}, [{test, <<\"test_value\">>}])."
amoc_eval "$1" "amoc_dist:do(${2}, ${3}, [{global_test1, <<\"test_value1\">>},
{test1, <<\"test_value2\">>}])."
}

result="$(run_scenario amoc-master dummy_scenario 10)"
Expand Down
49 changes: 49 additions & 0 deletions integration_test/test_update_settings.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/bin/bash

source "$(dirname "$0")/helper.sh"
enable_strict_mode

####################
## amoc functions ##
####################
update_settings() {
amoc_eval "$1" "amoc_dist:update_settings(${2}, ${3})."
}

amoc_eval amoc-worker-1 "binary_to_list(amoc_config:get(global_test1))." | contains_all "test_value1"
amoc_eval amoc-worker-1 "atom_to_list(amoc_config:get(global_test2))." | contains_all "undefined"
amoc_eval amoc-worker-1 "binary_to_list(amoc_config:get(test1))." | contains_all "test_value2"
amoc_eval amoc-worker-1 "atom_to_list(amoc_config:get(test2))." | contains_all "undefined"

amoc_eval amoc-worker-2 "binary_to_list(amoc_config:get(global_test1))." | contains_all "test_value1"
amoc_eval amoc-worker-2 "atom_to_list(amoc_config:get(global_test2))." | contains_all "undefined"
amoc_eval amoc-worker-2 "binary_to_list(amoc_config:get(test1))." | contains_all "test_value2"
amoc_eval amoc-worker-2 "atom_to_list(amoc_config:get(test2))." | contains_all "undefined"

amoc_eval amoc-master "binary_to_list(amoc_config:get(global_test1))." | contains_all "test_value1"
amoc_eval amoc-master "atom_to_list(amoc_config:get(global_test2))." | contains_all "undefined"
amoc_eval amoc-master "binary_to_list(amoc_config:get(test1))." | contains_all "test_value2"
amoc_eval amoc-master "atom_to_list(amoc_config:get(test2))." | contains_all "undefined"

update_settings amoc-master "[{global_test2, <<\"test_value3\">>}, {test2, <<\"test_value4\">>}]" "[node()]"

amoc_eval amoc-master "binary_to_list(amoc_config:get(global_test2))." | contains_all "test_value3"
amoc_eval amoc-master "binary_to_list(amoc_config:get(test2))." | contains_all "test_value4"

amoc_eval amoc-worker-1 "binary_to_list(amoc_config:get(global_test2))." | contains_all "test_value3"
amoc_eval amoc-worker-1 "atom_to_list(amoc_config:get(test2))." | contains_all "undefined"

amoc_eval amoc-worker-2 "binary_to_list(amoc_config:get(global_test2))." | contains_all "test_value3"
amoc_eval amoc-worker-2 "atom_to_list(amoc_config:get(test2))." | contains_all "undefined"

update_settings amoc-master "[{test2, <<\"test_value5\">>}]" "nodes()"

amoc_eval amoc-worker-1 "binary_to_list(amoc_config:get(test2))." | contains_all "test_value5"
amoc_eval amoc-worker-2 "binary_to_list(amoc_config:get(test2))." | contains_all "test_value5"

update_settings amoc-master "[{global_test2, <<\"test_value6\">>}]" "nodes()" | \
contains_all "changing_global_parameters_on_a_slave_node" "error"

amoc_eval amoc-master "binary_to_list(amoc_config:get(global_test2))." | contains_all "test_value3"
amoc_eval amoc-worker-1 "binary_to_list(amoc_config:get(global_test2))." | contains_all "test_value3"
amoc_eval amoc-worker-2 "binary_to_list(amoc_config:get(global_test2))." | contains_all "test_value3"
41 changes: 29 additions & 12 deletions src/amoc_controller.erl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
-define(DEFAULT_USER_RATE, 1200).

-required_variable(#{name => user_rate, default_value => ?DEFAULT_USER_RATE,
verification => {?MODULE, verify_user_rate, 1},
verification => {?MODULE, verify_user_rate, 1}, scope => global,
description => "Throttle rate for the Scenario:start/1,2 callback",
update => {?MODULE, update_user_rate, 2}}).

Expand Down Expand Up @@ -63,10 +63,11 @@
start_scenario/2,
stop_scenario/0,
update_settings/1,
propagate_config/2,
add_users/2,
remove_users/2,
get_status/0,
disable/0]).
disable/2]).

%% ------------------------------------------------------------------
%% Parameters verification functions
Expand Down Expand Up @@ -107,6 +108,10 @@ stop_scenario() ->
update_settings(Settings) ->
gen_server:call(?SERVER, {update_settings, Settings}).

-spec propagate_config(node(), amoc_config:config()) -> ok.
propagate_config(Node, Config) ->
gen_server:cast({?SERVER, Node}, {propagate_config, Config}).

-spec add_users(amoc_scenario:user_id(), amoc_scenario:user_id()) ->
ok | {error, term()}.
add_users(StartId, EndId) ->
Expand All @@ -123,9 +128,9 @@ get_status() ->
{ok, Status} = gen_server:call(?SERVER, get_status),
Status.

-spec disable() -> ok | {error, term()}.
disable() ->
gen_server:call(?SERVER, disable).
-spec disable(amoc:scenario(), amoc_config:settings()) -> ok | {error, term()}.
disable(Scenario, Settings) ->
gen_server:call(?SERVER, {disable, Scenario, Settings}).

%% @private
-spec verify_user_rate(any()) -> boolean().
Expand Down Expand Up @@ -178,8 +183,8 @@ handle_call({remove, Count, ForceRemove}, _From, State) ->
handle_call(get_status, _From, State) ->
RetValue = handle_status(State),
{reply, {ok, RetValue}, State};
handle_call(disable, _From, State) ->
{RetValue, NewState} = handle_disable(State),
handle_call({disable, Scenario, Settings}, _From, State) ->
{RetValue, NewState} = handle_disable(Scenario, Settings, State),
{reply, RetValue, NewState};
handle_call(_Request, _From, State) ->
{reply, {error, not_implemented}, State}.
Expand All @@ -189,6 +194,9 @@ handle_call(_Request, _From, State) ->
handle_cast(zero_users_running, State) ->
NewSate = handle_zero_users_running(State),
{noreply, NewSate};
handle_cast({propagate_config, Config}, State) ->
handle_propagate_config(Config),
{noreply, State};
handle_cast(_Msg, State) ->
{noreply, State}.

Expand Down Expand Up @@ -236,14 +244,19 @@ handle_stop_scenario(#state{status = Status} = State) ->
{{error, {invalid_status, Status}}, State}.

-spec handle_update_settings(amoc_config:settings(), state()) -> handle_call_res().
handle_update_settings(Settings, #state{status = running}) ->
handle_update_settings(Settings, #state{status = Status}) when Status =:= disabled;
Status =:= running ->
case amoc_config_scenario:update_settings(Settings) of
ok -> ok;
{error, Type, Reason} -> {error, {Type, Reason}}
end;
handle_update_settings(_Settings, #state{status = Status}) ->
{error, {invalid_status, Status}}.

-spec handle_propagate_config(amoc_config:config()) -> ok.
handle_propagate_config(Config) ->
amoc_config_utils:store_scenario_config(Config).

-spec handle_add(amoc_scenario:user_id(), amoc_scenario:user_id(), state()) ->
{handle_call_res(), state()}.
handle_add(StartId, EndId, #state{status = running,
Expand Down Expand Up @@ -281,10 +294,14 @@ handle_status(#state{status = finished, scenario = Scenario}) ->
handle_status(#state{status = Status}) ->
Status. %% idle, disabled or {error, Reason}.

-spec handle_disable(state()) -> {handle_call_res(), state()}.
handle_disable(#state{status = idle} = State) ->
{ok, State#state{status = disabled}};
handle_disable(#state{status = Status} = State) ->
-spec handle_disable(amoc:scenario(), amoc_config:settings(), state()) ->
{handle_call_res(), state()}.
handle_disable(Scenario, Settings, #state{status = idle} = State) ->
case amoc_config_scenario:parse_scenario_settings(Scenario, Settings) of
ok -> {ok, State#state{status = disabled}};
{error, Type, Reason} -> {error, {Type, Reason}}
end;
handle_disable(_Scenario, _Settings, #state{status = Status} = State) ->
{{error, {invalid_status, Status}}, State}.

%% ------------------------------------------------------------------
Expand Down
3 changes: 2 additions & 1 deletion src/config/amoc_config.erl
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@

-include("amoc_config.hrl").

-type config() :: module_configuration().
-export([get/1, get/2]).
-export_type([name/0, value/0, settings/0, maybe_module_config/0]).
-export_type([name/0, value/0, settings/0, config/0, maybe_module_config/0]).

%% ------------------------------------------------------------------
%% API
Expand Down
3 changes: 3 additions & 0 deletions src/config/amoc_config.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@
-type maybe_verification_fun() :: verification_fun() | fun((_)-> any()).
-type maybe_update_fun() :: update_fun() | fun((_,_)-> any()).

-type scope() :: local | global.

-record(module_parameter, {name :: name(),
mod :: module(),
value :: value(),
description :: unicode:chardata(),
scope = local :: scope(),
verification_fn :: maybe_verification_fun(),
update_fn = read_only :: maybe_update_fun() | read_only}).

Expand Down
24 changes: 21 additions & 3 deletions src/config/amoc_config_attributes.erl
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ process_var_attr(Module, Attr) ->
{fun check_default_value/1, []},
{fun check_verification_method/1, []},
{fun check_update_method/1, []},
{fun check_scope/1, []},
{fun make_module_parameter/2, [Module]}],
case amoc_config_utils:pipeline(PipelineActions, {ok, Attr}) of
{error, Reason} -> {error, add_original_attribute(Reason, Attr)};
Expand Down Expand Up @@ -109,18 +110,35 @@ check_update_method(Attr) ->
{ok, Attr#{update => UpdateFn}}
end.

-spec check_scope(#{any() => any()}) ->
{ok, #{scope := scope(), any() => any()}} | {error, reason()}.
check_scope(Attr) ->
Scope = maps:get(scope, Attr, local),
case lists:member(Scope, [local, global]) of
true ->
{ok, Attr#{scope => Scope}};
false ->
{error, invalid_scope}
end.

-spec make_module_parameter(#{name := name(),
description := unicode:chardata(),
default_value := value(),
verification := maybe_verification_fun(),
update := maybe_update_fun(),
scope := scope(),
any() => any()},
module()) ->
{ok, module_parameter()}.
make_module_parameter(#{name := Name, description := Description, default_value := Value,
verification := VerificationFn, update := UpdateFn}, Module) ->
make_module_parameter(#{name := Name,
description := Description,
default_value := Value,
verification := VerificationFn,
update := UpdateFn,
scope := Scope},
Module) ->
{ok, #module_parameter{name = Name, mod = Module, description = Description, value = Value,
verification_fn = VerificationFn, update_fn = UpdateFn}}.
scope = Scope, verification_fn = VerificationFn, update_fn = UpdateFn}}.

-spec verification_fn(maybe_verification_method()) ->
maybe_verification_fun() | not_exported | invalid_method.
Expand Down
Loading
Loading