release_handler
Serge Aleynikov
serge@REDACTED
Fri Jul 28 14:08:06 CEST 2006
Gunilla,
Thank you for looking into this. I guess it was a misuse of this
functionality on my part. Initially I tried this code in order to
reload configuration in a running node:
reload_config() ->
case init:get_argument(config) of
{ok, [ Files ]} ->
ConfFiles = [begin
S = filename:basename(F,".config"),
filename:join(filename:dirname(F),
S ++ ".config")
end || F <- Files],
% Move sys.config to the head of the list
Config = lists:sort(fun("sys.config", _) -> true;
(_, _) -> false
end, ConfFiles),
OldEnv = application_controller:prep_config_change(),
Apps = [{application, A, element(2,application:get_all_key(A))}
|| {A,_,_} <- application:which_applications()],
application_controller:change_application_data(Apps, Config),
application_controller:config_change(OldEnv);
_ ->
{ok, []}
end.
This worked except it didn't remove old environment variables.
Initially I thought that application_controller:do_change_appl was at
fault, but later I realized that application:get_all_key/1 among other
things returns not only the environment read from the *.app file, but a
merged environment (*.app file + whatever was read from a config file).
Upon realizing that I had to use the following solution to properly
reload configuration without restarting a node. This indeed removes the
*old* environment not found in the new config files. Perhaps there are
other ways?
reload_config() ->
case init:get_argument(config) of
{ok, [ Files ]} ->
ConfFiles = [begin
S = filename:basename(F,".config"),
filename:join(filename:dirname(F),
S ++ ".config")
end || F <- Files],
% Move sys.config to the head of the list
Config = lists:sort(fun("sys.config", _) -> true;
(_, _) -> false end, ConfFiles),
OldEnv = application_controller:prep_config_change(),
Apps = [{application, A, make_appl(A)}
|| {A,_,_} <- application:which_applications()],
application_controller:change_application_data(Apps, Config),
application_controller:config_change(OldEnv);
_ ->
{ok, []}
end.
make_appl(App) when is_atom(App) ->
AppList = element(2,application:get_all_key(App)),
FullName = code:where_is_file(atom_to_list(App) ++ ".app"),
case file:consult(FullName) of
{ok, [{application, _, Opts}]} ->
Env = proplists:get_value(env, Opts, []),
lists:keyreplace(env, 1, AppList, {env, Env});
{error, _Reason} ->
lists:keyreplace(env, 1, AppList, {env, []})
end.
Regards,
Serge
Gunilla Arendt wrote:
> Hi Serge,
>
> After having been on vacation for a few weeks, I finally got around to
> take a look at this. What happens in do_change_appl/3 is that a new
> "env" for the application is created by merging the configuration
> parameters in the .app file with configuration parameters set for
> the application in the sys.config file and on the command line.
>
> The call to Mod:config_change/3 isn't made here, but in
> do_config_change/3 where the previous "env" is compared with the new
> "env" as created above:
>
> ...
> case AppEnvNow of
> AppEnvBefore ->
> ok;
> _ ->
> case do_config_diff(AppEnvNow, AppEnvBefore) of
> {[], [], []} ->
> ok;
> {Changed, New, Removed} ->
> case application:get_key(App, mod) of
> {ok, {Mod, _Para}} ->
> case catch Mod:config_change(Changed, New,
> Removed) of
> ok ->
> ok;
> ...
>
> do_config_diff/1 does look for removed parameters, so as far as I can
> see it looks correct.
>
> Regards,
> Gunilla
>
> (FYI, I'm going on vacation again and will be back August 14)
>
> Serge Aleynikov wrote:
>> Thanks, Gunilla!
>>
>> Also, when I was experimenting with the application:config_change/3 it
>> seemed that the third parameter (Deleted) did not ever get set with
>> the list of deleted application's environment entries.
>>
>> A closer look into the application_controller:do_change_appl/3 showed
>> that the environment (NewEnv1, NewEnv2, NewEnv3) would always get
>> appended/replaced but never deleted:
>>
>> do_change_appl({ok, {ApplData, Env, IncApps, Descr, Id, Vsn, Apps}},
>> OldAppl, Config) ->
>> AppName = OldAppl#appl.name,
>>
>> %% Merge application env with env from sys.config, if any
>> ConfEnv = get_opt(AppName, Config, []),
>> NewEnv1 = merge_app_env(Env, ConfEnv),
>>
>> %% Merge application env with command line arguments, if any
>> CmdLineEnv = get_cmd_env(AppName),
>> NewEnv2 = merge_app_env(NewEnv1, CmdLineEnv),
>>
>> %% included_apps is made into an env parameter as well
>> NewEnv3 = keyreplaceadd(included_applications, 1, NewEnv2,
>> {included_applications, IncApps}),
>>
>> %% Update ets table with new application env
>> del_env(AppName),
>> add_env(AppName, NewEnv3),
>>
>> While you are looking at the previous bug report, please also verify
>> this one.
>>
>> Regards,
>>
>> Serge
>>
>>
>> Gunilla Arendt wrote:
>>> Hi Serge,
>>>
>>> Yes, the config_change/1 and change_application_data/2 calls should
>>> be done in the opposite order.
>>>
>>> Note however that release_handler:eval_appup_script/4 is actually not
>>> used by release_handler (the process), but is primarily intended to be
>>> called by the release_handler (the module) functions upgrade_app/2
>>> and downgrade_app/2,3. These two functions were added rather recently
>>> to simplify testing of .appup files. (the release_handler process
>>> calls do_install_release/3 to install a new release.)
>>>
>>> I will correct the bug and try to clarify the documentation.
>>>
>>> Thanks,
>>> Gunilla
>>>
>>>
>>> Serge Aleynikov wrote:
>>>
>>>> OTP team:
>>>>
>>>> I've been studying the code of the sasl's release_handler, and came
>>>> up with this question that perhaps someone qualified would be able
>>>> to answer.
>>>>
>>>> As far as I understand, the following functions do the following tasks:
>>>>
>>>> application_controller:prep_config_change()
>>>> - Fetch OLD applications' environment from
>>>> application controller's internal ETS table.
>>>>
>>>> application_controller:config_change(EnvBefore)
>>>> - Notify running applications (via config_change/3 callback) about
>>>> configuration changes derived from comparing EnvBefore with the
>>>> content of the application controller's ETS table storing
>>>> applications' environment.
>>>>
>>>> application_controller:change_application_data(AppSpecs, Config)
>>>> - Rereads applications' environment from config files stored on disk
>>>> and stores it to the application controller's ETS table.
>>>>
>>>> Now with this background let's look at how the release handler does
>>>> the application upgrade:
>>>>
>>>> ==[begin]== sasl/src/release_handler.erl =====
>>>> ...
>>>> eval_appup_script(App, ToVsn, ToDir, Script) ->
>>>> EnvBefore = application_controller:prep_config_change(),
>>>> AppSpecL = read_appspec(App, ToDir),
>>>> Res = release_handler_1:eval_script(Script,
>>>> [], % [AppSpec]
>>>> [{App, ToVsn, ToDir}],
>>>> []), % [Opt]
>>>> case Res of
>>>> {ok, _Unpurged} ->
>>>> application_controller:config_change(EnvBefore),
>>>> application_controller:change_application_data(AppSpecL,[]);
>>>> _Res ->
>>>> ignore
>>>> end,
>>>> Res.
>>>> ...
>>>> ==[end]=======================================
>>>>
>>>> Unless I am wrong, from this code it does *not* look like
>>>> application's config_change/3 callback will ever be invoked by
>>>> application_controller:config_change(EnvBefore) because EnvBefore
>>>> has the same applications' environment information as the current
>>>> content of the applications' controller's ETS table. It seems to me
>>>> that at least the config_change/1 and change_application_data/2
>>>> calls should be done in the opposite order, or better, do something
>>>> like this:
>>>>
>>>> ...
>>>> {ok, _Unpurged} ->
>>>> Files =
>>>> case init:get_argument(config) of
>>>> {ok, [ ConfFileNames ]} ->
>>>> [begin
>>>> S = filename:basename(F,".config"),
>>>> filename:join(filename:dirname(F), S ++ ".config")
>>>> end || F <- ConfFileNames],
>>>> {ok, _} ->
>>>> []
>>>> end,
>>>> application_controller:change_application_data(AppSpecL, Files);
>>>> application_controller:config_change(EnvBefore);
>>>> _Res ->
>>>> ignore
>>>> ...
>>>>
>>>> I'd appreciate some clarification on the subject.
>>>>
>>>> Regards,
>>>>
>>>> Serge
>>>>
>>>>
>>>
>>>
>>
>
>
More information about the erlang-questions
mailing list