jameskilton (owner)

Revisions

gist: 87804 Download_button fork
public
Public Clone URL: git://gist.github.com/87804.git
Embed All Files: show embed
migrations.erl #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
%%
%% 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]).
 
upgrade_manager.erl #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
%%
%% 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).