Skip to content

Instantly share code, notes, and snippets.

@jasonroelofs
Created March 30, 2009 14:11
Show Gist options
  • Save jasonroelofs/87804 to your computer and use it in GitHub Desktop.
Save jasonroelofs/87804 to your computer and use it in GitHub Desktop.
%%
%% Ad-hoc hot-code swap upgrade definitions file. Unlike .appup and .relup,
%% this is a pure Erlang file. Use this module to define how an upgrade is supposed
%% to take place for this system.
%%
%% Usage of this module is simple. You need to define up() and down() methods for
%% each version of the application. On an upgrade, the upgrade_manager will call
%% up(NewVsn) for upgrades. If you need to roll back, it will call down(NewVsn)
%% (with NewVsn being the version defined in your .app file).
%%
%% For example, to upgrade from version "0.1" to "0.2", you need to define:
%%
%% up("0.2") ->
%% [update_manager commands].
%% down("0.2") ->
%% [update_manager commands for downgrading to 0.1].
%%
%% See upgrade_manager for the avilable commands
-module(migrations).
-export([up/1, down/1]).
%%
%% Define how an application upgrades to a given version from the previous
%% version. It's expected that the application is currently at the version
%% previous (if versions are 0.1, 0.2, 0.3 and you're deploying 0.4, it's assumed
%% that the app is at 0.3).
%%
up("0.2") ->
upgrade_manager:upgrade([{reload_process, video_dispatcher}]);
up(Vsn) ->
io:format("Don't know how to upgrade to version ~p~n", [Vsn]).
%%
%% Downgrade the application from the passed in version
%%
down("0.2") ->
upgrade_manager:upgrade([{reload_process, video_dispatcher}]);
down(Vsn) ->
io:format("Don't know how to downgrade from version ~p~n", [Vsn]).
%%
%% This module is for running real-time code upgrades on the system.
%% Actual upgrade code paths are defined in the migrations module.
%%
%% Upgrade your system by calling:
%%
%% upgrade_manager:migrate(up / down, Version).
%%
-module(upgrade_manager).
-export([migrate/2, upgrade/1,print_message/1]).
%%
%% Top-level migration management function. This takes care of replacing code paths,
%% reloading this module, and then running the appropriate migration version as defined
%% below in up() and down().
%%
%% Calling this is simple:
%%
%% migrations:migrate(up / down, Vsn)
%%
migrate(Path, Version) ->
% Replace code dir to new version
CurrentPath = code:lib_dir(video_dispatcher),
Tokens = string:tokens(CurrentPath, "-"),
BasePath = string:join(lists:delete(lists:last(Tokens), Tokens), "-"),
NewPath = string:join([BasePath, Version], "-"),
% Sanity check - ensure the new version directory actually exists
case file:read_file_info(NewPath) of
{error, Error} ->
io:format("Was unable to open the directory ~p, given the error ~p~n", [NewPath, Error]);
{ok, _} ->
io:format("Now migrating to version ~p~n", [Version]),
code:replace_path(video_dispatcher, NewPath),
% Reload this class
c:l(migrations),
% Call the migration
apply(migrations, Path, [Version])
end.
%%
%% Upgrade the system, following the rules passed into this method.
%% Arguments should be formatted much in the same way an .appup file
%% is formatted. This module supports the following commands:
%%
%% {reload_process, ProcName} - Reload a module that's also a process
%% {reload_module, Module} - Reload a module
%%
%% Args = [Arg]
%% Arg = term()
%%
upgrade(Args) ->
process_args(Args).
print_message(Message) ->
io:format("Message: ~p~n", [Message]).
%%
%% Private methods
%%
%%
%% Process each argument
%%
process_args([]) ->
ok;
process_args([Arg | Rest]) ->
case Arg of
{reload_process, ProcName} ->
reload_process(ProcName);
{reload_module, Module} ->
reload_module(Module);
Any ->
% Eh, ignore commands we don't expect
io:format("Unknown command ~p, ignored~n", [Any])
end,
process_args(Rest).
% Reload a given module
% This simply suspends the module's process, loads the code,
% and resumes the process
reload_process(Name) ->
% Get rid of any stale code from last update
code:purge(Name),
% Suspend processes
sys:suspend(whereis(Name)),
% Load new code into the VM
reload_module(Name),
% Fire off code change message
sys:change_code(Name, Name, vsn, vsn),
% Start our process back up
sys:resume(whereis(Name)),
% Clean up all code we can, leaving that which might
% be still in use by processes
code:soft_purge(Name).
% Reload pure module code. To be used when reload a module that
% is not a process, such as this one
reload_module(Module) ->
c:l(Module).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment