capricorn 0.2.25 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. data/LICENSE +19 -0
  2. data/README.md +1 -0
  3. data/bin/capricorn-app-scaffolder +27 -0
  4. data/bin/capricorn-gem-spec +33 -0
  5. data/bin/capricornctl +12 -0
  6. data/bin/capricornd +31 -0
  7. data/erlang/lib/bert/doc/overview.edoc +4 -0
  8. data/erlang/lib/bert/ebin/bert.app +9 -0
  9. data/erlang/lib/bert/rebar.config +3 -0
  10. data/erlang/lib/bert/src/bert.erl +69 -0
  11. data/erlang/lib/bertio/doc/overview.edoc +4 -0
  12. data/erlang/lib/bertio/ebin/bertio.app +9 -0
  13. data/erlang/lib/bertio/rebar.config +3 -0
  14. data/erlang/lib/bertio/src/bertio.erl +28 -0
  15. data/erlang/lib/bertrpc/doc/overview.edoc +4 -0
  16. data/erlang/lib/bertrpc/ebin/bertrpc.app +12 -0
  17. data/erlang/lib/bertrpc/include/bertrpc.hrl +1 -0
  18. data/erlang/lib/bertrpc/rebar.config +5 -0
  19. data/erlang/lib/bertrpc/src/bertrpc.erl +471 -0
  20. data/erlang/lib/bertrpc/src/bertrpc_hello_world.erl +148 -0
  21. data/erlang/lib/bertrpc/src/fd_tcp.erl +376 -0
  22. data/erlang/lib/capricorn/doc/overview.edoc +4 -0
  23. data/erlang/lib/capricorn/ebin/capricorn.app +56 -0
  24. data/erlang/lib/capricorn/include/capricorn.hrl +76 -0
  25. data/erlang/lib/capricorn/rebar.config +3 -0
  26. data/erlang/lib/capricorn/src/cap_application.erl +170 -0
  27. data/erlang/lib/capricorn/src/cap_cluster.erl +121 -0
  28. data/erlang/lib/capricorn/src/cap_cluster_gems.erl +422 -0
  29. data/erlang/lib/capricorn/src/cap_config.erl +25 -0
  30. data/erlang/lib/capricorn/src/cap_dets_updater.erl +26 -0
  31. data/erlang/lib/capricorn/src/cap_event_sup.erl +35 -0
  32. data/erlang/lib/capricorn/src/cap_events.erl +24 -0
  33. data/erlang/lib/capricorn/src/cap_external_api.erl +87 -0
  34. data/erlang/lib/capricorn/src/cap_external_apps_api.erl +125 -0
  35. data/erlang/lib/capricorn/src/cap_external_gems_api.erl +85 -0
  36. data/erlang/lib/capricorn/src/cap_external_machines_api.erl +18 -0
  37. data/erlang/lib/capricorn/src/cap_gem_utils.erl +111 -0
  38. data/erlang/lib/capricorn/src/cap_internal_api.erl +69 -0
  39. data/erlang/lib/capricorn/src/cap_internal_apps_api.erl +38 -0
  40. data/erlang/lib/capricorn/src/cap_log.erl +113 -0
  41. data/erlang/lib/capricorn/src/cap_machine.erl +338 -0
  42. data/erlang/lib/capricorn/src/cap_machine_apps.erl +410 -0
  43. data/erlang/lib/capricorn/src/cap_machine_apps_sup.erl +57 -0
  44. data/erlang/lib/capricorn/src/cap_sup.erl +200 -0
  45. data/erlang/lib/capricorn/src/cap_util.erl +278 -0
  46. data/erlang/lib/capricorn/src/capricorn.erl +45 -0
  47. data/erlang/lib/capricorn/src/capricorn_app.erl +36 -0
  48. data/erlang/lib/emq/ebin/emq.app +17 -0
  49. data/erlang/lib/emq/src/emq.erl +261 -0
  50. data/erlang/lib/emq/src/emq_app.erl +11 -0
  51. data/erlang/lib/emq/src/emq_pool.erl +178 -0
  52. data/erlang/lib/emq/src/emq_queue.erl +332 -0
  53. data/erlang/lib/emq/src/emq_status.erl +148 -0
  54. data/erlang/lib/emq/src/emq_sup.erl +132 -0
  55. data/erlang/lib/gcd/ebin/gcd.app +18 -0
  56. data/erlang/lib/gcd/include/gcd.hrl +5 -0
  57. data/erlang/lib/gcd/rebar.config +3 -0
  58. data/erlang/lib/gcd/src/gcd.erl +51 -0
  59. data/erlang/lib/gcd/src/gcd_app.erl +13 -0
  60. data/erlang/lib/gcd/src/gcd_event.erl +39 -0
  61. data/erlang/lib/gcd/src/gcd_server.erl +30 -0
  62. data/erlang/lib/gcd/src/gcd_srv.erl +186 -0
  63. data/erlang/lib/gcd/src/gcd_sup.erl +18 -0
  64. data/erlang/rebar +0 -0
  65. data/erlang/rebar.config +9 -0
  66. data/erlang/rel/overlay/bin/capricornd +146 -0
  67. data/erlang/rel/overlay/erts-vsn/bin/erl +34 -0
  68. data/erlang/rel/overlay/erts-vsn/bin/nodetool +80 -0
  69. data/erlang/rel/overlay/etc/capricorn/app.config +59 -0
  70. data/erlang/rel/overlay/etc/capricorn/cluster-vm.args +21 -0
  71. data/erlang/rel/overlay/etc/capricorn/machine-vm.args +21 -0
  72. data/erlang/rel/reltool.config +43 -0
  73. data/ext/Makefile +2 -0
  74. data/ext/extconf.rb +1 -0
  75. data/lib/capricorn-client.rb +86 -0
  76. data/lib/capricorn-client/cli/applications.rb +256 -0
  77. data/lib/capricorn-client/cli/gems.rb +57 -0
  78. data/lib/capricorn-client/cli/machines.rb +9 -0
  79. data/lib/capricorn-client/helpers.rb +62 -0
  80. data/lib/capricorn.rb +5 -99
  81. data/lib/capricorn/driver.rb +86 -0
  82. data/lib/capricorn/recipes/apache-debian.rb +112 -0
  83. data/lib/capricorn/recipes/centos-plesk.rb +162 -0
  84. data/lib/capricorn/recipes/macports.rb +54 -0
  85. data/lib/capricorn/system_context.rb +49 -0
  86. data/lib/capricorn/version.rb +5 -0
  87. metadata +233 -74
  88. data/app_generators/engine/engine_generator.rb +0 -40
  89. data/app_generators/engine/templates/Gmfile +0 -20
  90. data/app_generators/engine/templates/MIT-LICENSE.txt +0 -20
  91. data/app_generators/engine/templates/README.rdoc +0 -7
  92. data/app_generators/engine/templates/config/initializers/rails_init.rb +0 -1
  93. data/app_generators/engine/templates/config/routes.rb +0 -2
  94. data/app_generators/engine/templates/gitignore +0 -9
  95. data/app_generators/engine/templates/init.rb +0 -1
  96. data/app_generators/engine/templates/lib/engine.rb +0 -4
  97. data/app_generators/engine/templates/rails/init.rb +0 -1
  98. data/app_generators/engine/templates/tasks/engine_tasks.rake +0 -4
  99. data/bin/capricorn +0 -20
  100. data/lib/capricorn/actor.rb +0 -23
  101. data/lib/capricorn/actor/actions.rb +0 -76
  102. data/lib/capricorn/actors/apache_actor.rb +0 -56
  103. data/lib/capricorn/actors/base_actor.rb +0 -335
  104. data/lib/capricorn/actors/host_file_actor.rb +0 -77
  105. data/lib/capricorn/actors/mysql_actor.rb +0 -20
  106. data/lib/capricorn/actors/passenger_actor.rb +0 -28
  107. data/lib/capricorn/actors/plesk_actor.rb +0 -228
  108. data/lib/capricorn/actors/sqlite3_actor.rb +0 -44
  109. data/lib/capricorn/app_runner.rb +0 -108
  110. data/lib/capricorn/apps/dev.rb +0 -15
  111. data/lib/capricorn/apps/engines.rb +0 -33
  112. data/lib/capricorn/apps/jobs.rb +0 -35
  113. data/lib/capricorn/apps/satellite.rb +0 -68
  114. data/lib/capricorn/apps/server.rb +0 -73
  115. data/lib/capricorn/client.rb +0 -48
  116. data/lib/capricorn/client/auth_token.rb +0 -98
  117. data/lib/capricorn/daemon.rb +0 -81
  118. data/lib/capricorn/exception_handler.rb +0 -79
  119. data/lib/capricorn/extentions/rubygems_plugin.rb +0 -27
  120. data/lib/capricorn/extentions/thor_extentions.rb +0 -32
  121. data/lib/capricorn/job_queue.rb +0 -203
  122. data/lib/capricorn/satellite.rb +0 -52
  123. data/lib/capricorn/satellite/actions.rb +0 -55
  124. data/lib/capricorn/satellite/dependency_loader.rb +0 -82
  125. data/lib/capricorn/satellite/persistence.rb +0 -50
  126. data/lib/capricorn/server.rb +0 -144
  127. data/lib/capricorn/server/daemon.rb +0 -83
  128. data/lib/capricorn/server/proxy.rb +0 -25
  129. data/lib/capricorn/server/security.rb +0 -120
  130. data/lib/capricorn/system.rb +0 -218
  131. data/lib/capricorn/system/config.rb +0 -49
  132. data/lib/capricorn/system/helper.rb +0 -21
  133. data/lib/capricorn/system/options.rb +0 -79
  134. data/lib/capricorn/system/process_user.rb +0 -73
  135. data/lib/capricorn/system/satellites.rb +0 -44
  136. data/lib/capricorn/system/shell.rb +0 -80
  137. data/lib/rubygems_plugin.rb +0 -1
  138. data/spec/actor/actions_spec.rb +0 -13
  139. data/spec/spec_helper.rb +0 -1
@@ -0,0 +1,4 @@
1
+ @author Simon Menke <simon.menke@gmail.com>
2
+ @copyright 2010 Simon Menke
3
+ @version {@vsn}
4
+
@@ -0,0 +1,56 @@
1
+ %%
2
+ %% Resource application file
3
+ %%
4
+ %% File : capricorn.app
5
+ %% Created: 2010-01-04
6
+ %%
7
+ %% @author simonmenke <simon.menke@gmail.com>
8
+ %% @copyright 2010 simonmenke
9
+ %%
10
+ %% @doc TODO make nice description
11
+ %%
12
+
13
+ {application, capricorn, [
14
+ {description, "Capricorn application version 1.0"},
15
+ {vsn, "0.1"},
16
+ {modules, [
17
+ capricorn,
18
+ capricorn_app,
19
+ cap_application,
20
+ cap_cluster,
21
+ cap_cluster_gems,
22
+ cap_config,
23
+ cap_dets_updater,
24
+ cap_events,
25
+ cap_event_sup,
26
+ cap_external_api,
27
+ cap_external_apps_api,
28
+ cap_external_gems_api,
29
+ cap_external_machines_api,
30
+ cap_gem_utils,
31
+ cap_internal_api,
32
+ cap_internal_apps_api,
33
+ cap_log,
34
+ cap_machine,
35
+ cap_machine_apps,
36
+ cap_machine_apps_sup,
37
+ cap_sup,
38
+ cap_util
39
+ ]},
40
+ {registered, [
41
+ cap_cluster,
42
+ cap_cluster_gems,
43
+ cap_config,
44
+ cap_events,
45
+ cap_external_api,
46
+ cap_internal_api,
47
+ cap_log,
48
+ cap_machine,
49
+ cap_machine_apps_sup,
50
+ cap_primary_services,
51
+ cap_secondary_services,
52
+ cap_sup
53
+ ]},
54
+ {applications, [kernel, stdlib, sasl, inets, bertio, bertrpc, gcd, emq]},
55
+ {mod, {capricorn_app, []}}
56
+ ]}.
@@ -0,0 +1,76 @@
1
+ %%
2
+ %% Include file
3
+ %%
4
+ %% File : capricorn.hrl
5
+ %% Created: 2010-01-04
6
+ %%
7
+ %% @author simonmenke <simon.menke@gmail.com>
8
+ %% @copyright 2010 simonmenke
9
+ %%
10
+ %% @doc TODO make nice description
11
+ %%
12
+
13
+ -define(b2a(V), list_to_atom(binary_to_list(V))).
14
+ -define(b2l(V), binary_to_list(V)).
15
+ -define(l2b(V), list_to_binary(V)).
16
+
17
+ -define(LOG_DEBUG(Format, Args),
18
+ case cap_log:debug_on() of
19
+ true -> error_logger:info_report(capricorn_debug, {Format, Args});
20
+ false -> ok
21
+ end).
22
+
23
+ -define(LOG_INFO(Format, Args),
24
+ case cap_log:info_on() of
25
+ true -> error_logger:info_report(capricorn_info, {Format, Args});
26
+ false -> ok
27
+ end).
28
+
29
+ -define(LOG_ERROR(Format, Args),
30
+ error_logger:error_report(capricorn_error, {Format, Args})).
31
+
32
+
33
+ -type version_part() :: pos_integer() | string() .
34
+ -type version_parts() :: [version_part(),...] .
35
+ -type version() :: {version_parts()} .
36
+ -type requirement() :: {'='|'!='|'<'|'>'|'>='|'<='|'~>', version()} .
37
+ -type dependency() :: {binary(), [requirement()]} .
38
+
39
+
40
+ -record(gem_ref, {
41
+ name :: binary(),
42
+ version :: 'undefined' | version(),
43
+ lib :: 'undefined' | binary()
44
+ }).
45
+ -type gem_ref() :: #gem_ref{}.
46
+
47
+ -record(gem_id, {
48
+ name :: binary(),
49
+ version :: version()
50
+ }).
51
+ -type gem_id() :: #gem_id{}.
52
+
53
+ -record(application, {
54
+ id=undefined :: 'undefined' | binary(),
55
+ node=undefined :: 'undefined' | atom(),
56
+ name=undefined :: 'undefined' | binary(),
57
+ domains=[] :: [binary(),...],
58
+ environment=development :: atom(),
59
+ www_user=undefined :: 'undefined' | binary(),
60
+ www_group=undefined :: 'undefined' | binary(),
61
+ root_path=undefined :: 'undefined' | binary(),
62
+ installed_gems=[] :: [gem_id()],
63
+ required_gems=[] :: [binary()],
64
+ rvsn={rvsn, 0}
65
+ }).
66
+ -type application() :: #application{}.
67
+
68
+
69
+ -record(gem, {
70
+ id :: gem_id(),
71
+ deps=[] :: [dependency()],
72
+ missing=[] :: [dependency()],
73
+ rvsn={rvsn, 1}
74
+ }).
75
+ -type gem_spec() :: #gem{} .
76
+
@@ -0,0 +1,3 @@
1
+ {cover_enabled, true}.
2
+ {erl_opts, [debug_info, fail_on_warning, {i, ".."}]}.
3
+ {lib_dirs, [".."]}.
@@ -0,0 +1,170 @@
1
+ -module(cap_application).
2
+ -include("capricorn.hrl").
3
+ -behaviour(gen_server).
4
+
5
+ -export([start_link/1]).
6
+ -export([restart/1, relink/1, update/1, stop/1, start/1]).
7
+ -export([init/1, handle_call/3, handle_cast/2,
8
+ handle_info/2, terminate/2, code_change/3]).
9
+
10
+ %%% Start the server
11
+ start_link(#application{}=App) ->
12
+ {ok, Appname} = get_proc_def(App),
13
+ gen_server:start_link(Appname, ?MODULE, [App], []).
14
+
15
+ restart(App) ->
16
+ {ok, Appname} = get_proc_name(App),
17
+ gen_server:cast(Appname, {restart}).
18
+
19
+ update(App) ->
20
+ {ok, Appname} = get_proc_name(App),
21
+ gen_server:call(Appname, {update, App}, 15 * 60000).
22
+
23
+ relink(App) ->
24
+ {ok, Appname} = get_proc_name(App),
25
+ gen_server:cast(Appname, {relink}).
26
+
27
+ stop(App) ->
28
+ {ok, Appname} = get_proc_name(App),
29
+ gen_server:cast(Appname, {stop}).
30
+
31
+ start(App) ->
32
+ {ok, Appname} = get_proc_name(App),
33
+ gen_server:cast(Appname, {start}).
34
+
35
+
36
+ get_proc_def(App) ->
37
+ case get_proc_name(App) of
38
+ {ok, L} when is_atom(L) -> {ok, {local, L}};
39
+ Else -> Else
40
+ end.
41
+ get_proc_name(#application{id=undefined}) ->
42
+ {error, no_master_domain};
43
+ get_proc_name(#application{id=Id, node=Node}) ->
44
+ get_proc_name({Node, Id});
45
+ get_proc_name({Node, Id}) ->
46
+ if Node == node() ->
47
+ {ok, list_to_atom(lists:flatten([binary_to_list(Id), ".app"]))};
48
+ true ->
49
+ {ok, {list_to_atom(lists:flatten([binary_to_list(Id), ".app"])), Node}}
50
+ end.
51
+
52
+ -record(state, {app=undefined}).
53
+
54
+ %%% Initialize the server
55
+ init([#application{}=App]) ->
56
+
57
+ State = #state{app=App},
58
+
59
+ gen_server:cast(self(), {restart}),
60
+
61
+ {ok, State}.
62
+
63
+ %%% Handle call messages
64
+ handle_call({update, App}, _From, State) ->
65
+ ?LOG_DEBUG("reconfiguring app ~s", [App#application.id]),
66
+ case reconfigure_app(App) of
67
+ ok ->
68
+ ?LOG_DEBUG("reconfigured app ~s", [App#application.id]),
69
+ gen_server:cast(self(), {relink}),
70
+ {reply, ok, State#state{app=App}};
71
+ Error ->
72
+ ?LOG_ERROR("Error while relinking app with new gems: ~p", [Error]),
73
+ {reply, ok, State}
74
+ end;
75
+
76
+ handle_call(_Request, _From, State) ->
77
+ {reply, ok, State}.
78
+
79
+
80
+ %%% Handle cast messages
81
+ handle_cast(stop, State) ->
82
+ {stop, normal, State};
83
+
84
+ handle_cast({restart}, #state{app=App}=State) ->
85
+ os:cmd("touch "++get_app_root(App, 'host/tmp/restart.txt')),
86
+ app_chown(App, 'host/tmp/restart.txt'),
87
+ {noreply, State};
88
+
89
+ handle_cast({relink}, #state{app=App}=State) ->
90
+ os:cmd("touch "++get_app_root(App, 'host/tmp/relink.txt')),
91
+ os:cmd("touch "++get_app_root(App, 'host/tmp/restart.txt')),
92
+ app_chown(App, 'host/tmp/relink.txt'),
93
+ app_chown(App, 'host/tmp/restart.txt'),
94
+ {noreply, State};
95
+
96
+ handle_cast({stop}, #state{app=App}=State) ->
97
+ os:cmd("touch "++get_app_root(App, 'host/tmp/stop.txt')),
98
+ app_chown(App, 'host/tmp/stop.txt'),
99
+ {noreply, State};
100
+
101
+ handle_cast({start}, #state{app=App}=State) ->
102
+ os:cmd("rm -f "++get_app_root(App, 'host/tmp/stop.txt')),
103
+ {noreply, State};
104
+
105
+ handle_cast(Msg, State) ->
106
+ ?LOG_DEBUG("unhandled cast ~p", [Msg]),
107
+ {noreply, State}.
108
+
109
+
110
+ %%% Handle generic messages
111
+ handle_info(_Info, State) ->
112
+ {noreply, State}.
113
+
114
+ %%% Before stopping the server
115
+ terminate(_Reason, _State) ->
116
+ ok.
117
+
118
+ %%% Code Changes
119
+ code_change(_OldVsn, State, _Extra) ->
120
+ {ok, State}.
121
+
122
+
123
+ reconfigure_app(App) ->
124
+ write_milkshake_gem_config(App).
125
+
126
+ write_milkshake_gem_config(#application{installed_gems=[]}) -> {ok, "gems: {}\n"};
127
+ write_milkshake_gem_config(App) ->
128
+ Header = "gems:\n",
129
+ Gems = select_required_installed_gems(App),
130
+ {ok, Config} = write_milkshake_gem_config(Header, Gems),
131
+ ?LOG_DEBUG("config: ~s", [Config]),
132
+ ?LOG_DEBUG("writing milkshake.yml to ~s", [get_app_root(App, 'host/config/milkshake.yml')]),
133
+ file:write_file(get_app_root(App, 'host/config/milkshake.yml'), Config),
134
+ app_chown(App, 'host/config/milkshake.yml').
135
+
136
+ write_milkshake_gem_config(Config, []) -> {ok, Config};
137
+ write_milkshake_gem_config(Config, [#gem_id{}=Id|Other]) ->
138
+ #gem_id{name=Name,version=Version} = Id,
139
+ case Name of
140
+ undefined -> % skip
141
+ write_milkshake_gem_config(Config, Other);
142
+ _Else1 ->
143
+ Config1 = lists:concat([Config, " ", binary_to_list(Name), ":\n"]),
144
+ Config2 =
145
+ case Version of
146
+ undefined -> lists:concat([Config1, " version: \">= 0\"\n"]);
147
+ _Else2 -> lists:concat([Config1, " version: \"", cap_cluster_gems:version_to_string(Version), "\"\n"])
148
+ end,
149
+ write_milkshake_gem_config(Config2, Other)
150
+ end.
151
+
152
+ select_required_installed_gems(App) ->
153
+ #application{installed_gems=Installed, required_gems=Required} = App,
154
+ lists:foldl(fun
155
+ (#gem_id{name=Name}=Id, Acc) ->
156
+ case lists:member(Name, Required) of
157
+ true -> Acc ++ [Id];
158
+ false -> Acc
159
+ end
160
+ end, [], Installed).
161
+
162
+ get_app_root(App, Sub) when is_atom(Sub) ->
163
+ get_app_root(App, [atom_to_list(Sub)]);
164
+ get_app_root(#application{root_path=Path}, Sub) ->
165
+ filename:join([binary_to_list(Path)|Sub]).
166
+
167
+
168
+ app_chown(#application{www_user=U,www_group=G}=App, Path) ->
169
+ os:cmd("chown "++binary_to_list(U)++":"++binary_to_list(G)++" "++get_app_root(App, Path)),
170
+ ok.
@@ -0,0 +1,121 @@
1
+ %%
2
+ %% Supervised worker process module
3
+ %%
4
+ %% File : cap_cluster.erl
5
+ %% Created: 2010-01-04
6
+ %%
7
+ %% @author simonmenke <simon.menke@gmail.com>
8
+ %% @copyright 2010 simonmenke
9
+ %%
10
+ %% @doc TODO make nice description
11
+ %%
12
+
13
+ -module(cap_cluster).
14
+ -author('simonmenke <simon.menke@gmail.com>').
15
+ -include("capricorn.hrl").
16
+ -behaviour(gen_server).
17
+
18
+ %% operation & maintenance api
19
+ -export([start_link/0]).
20
+ -export([all_machines/0, get_machine/1, add_machine/2, rmv_machine/1, restart_machine/1]).
21
+
22
+ %% gen_server callbacks
23
+ -export([init/1, handle_call/3, handle_cast/2,
24
+ handle_info/2, terminate/2, code_change/3]).
25
+
26
+ -record(cluster_ctx, {
27
+
28
+ }).
29
+
30
+ -define(SERVER, {global, ?MODULE}).
31
+
32
+ %%
33
+ %% Operation & Maintenance API
34
+ %%
35
+
36
+ %% @spec start_link() -> {ok, Pid}
37
+ %% @doc Start the cap_cluster
38
+ start_link() ->
39
+ gen_server:start_link(?SERVER, ?MODULE, [], []).
40
+
41
+ all_machines() ->
42
+ gen_server:call(?SERVER, {all_machines}).
43
+
44
+ get_machine(Node) ->
45
+ gen_server:call(?SERVER, {get_machine, Node}).
46
+
47
+ add_machine(Node, Name) ->
48
+ gen_server:call(?SERVER, {add_machine, Name, Node}).
49
+
50
+ rmv_machine(Node) ->
51
+ gen_server:call(?SERVER, {rmv_machine, Node}).
52
+
53
+ restart_machine(Node) ->
54
+ gen_server:call(?SERVER, {restart_machine, Node}).
55
+
56
+ %%
57
+ %% Genserver callback functions
58
+ %%
59
+
60
+ %% @spec init(State) -> {ok, State}
61
+ %% @doc Callback for initialize the cap_cluster
62
+ init([]) ->
63
+ ets:new(cap_machines, [set, private, named_table]),
64
+ net_kernel:monitor_nodes(true),
65
+ {ok, #cluster_ctx{}}.
66
+
67
+ handle_call({all_machines}, _From, State) ->
68
+ MachineNodes = ets:foldl(fun({Node, _}, Acc) ->
69
+ [Node|Acc]
70
+ end, [], cap_machines),
71
+ {reply, MachineNodes, State};
72
+
73
+ handle_call({get_machine, Node}, _From, State) ->
74
+ case ets:lookup(cap_machines, Node) of
75
+ [] -> {reply, {error, not_found}, State};
76
+ [{_, Name}] -> {reply, {ok, {Node, Name}}, State}
77
+ end;
78
+
79
+ handle_call({rmv_machine, Node}, _From, State) ->
80
+ ets:delete(cap_machines, Node),
81
+ {reply, ok, State};
82
+
83
+ handle_call({restart_machine, _Node}, _From, State) ->
84
+ {reply, ok, State};
85
+
86
+ handle_call({add_machine, Name, Node}, _From, State) ->
87
+ ets:insert(cap_machines, {Node, Name}),
88
+ {reply, ok, State}.
89
+
90
+ %% @spec handle_cast(stop, State) -> {stop, normal, State}
91
+ %% @doc Callback for assynchronous messages
92
+ handle_cast(stop, State) ->
93
+ {stop, normal, State};
94
+
95
+ %% @spec handle_cast(_Msg, State) -> {noreply, State}
96
+ %% @doc Callback for assynchronous messages
97
+ handle_cast(Msg, State) ->
98
+ ?LOG_DEBUG("Unhandled cast: ~p", [Msg]),
99
+ {noreply, State}.
100
+
101
+ %% @spec handle_info(_Info, State) -> {noreply, State}
102
+ %% @doc Callback for timeout or other unreconized messages
103
+ handle_info({nodeup, Node}, State) ->
104
+ ?LOG_INFO("Machine came up: ~p", [Node]),
105
+ {noreply, State};
106
+ handle_info({nodedown, Node}, State) ->
107
+ ?LOG_INFO("Machine went down: ~p", [Node]),
108
+ {noreply, State};
109
+ handle_info(Info, State) ->
110
+ ?LOG_DEBUG("Unhandled info: ~p", [Info]),
111
+ {noreply, State}.
112
+
113
+ %% @spec terminate(_Reason, _State) -> ok
114
+ %% @doc Callback for free resources used by the server
115
+ terminate(_Reason, _State) ->
116
+ ok.
117
+
118
+ %% @spec code_change(_OldVsn, State, _Extra) -> {ok, State}
119
+ %% @doc Callback for upgrade source code
120
+ code_change(_OldVsn, State, _Extra) ->
121
+ {ok, State}.
@@ -0,0 +1,422 @@
1
+ -module(cap_cluster_gems).
2
+ -behaviour(gen_server).
3
+ -include("capricorn.hrl").
4
+
5
+ -export([start_link/0, push/1, missing/0, lookup/1, lookup/2, pull/1, pull/2, check/0, all/0]).
6
+ -export([init/1, handle_call/3, handle_cast/2,
7
+ handle_info/2, terminate/2, code_change/3]).
8
+
9
+ -export([version_to_string/1]).
10
+
11
+ -record(state, {table, gem_path, stage_path, spec_reader, tmp_id=0}).
12
+ -type state() :: #state{} .
13
+
14
+
15
+ -spec push(binary() | string()) -> {ok, [dependency()]} | {error, term()} .
16
+ push(Path) when is_list(Path) ->
17
+ case file:read_file(Path) of
18
+ {ok, Data} -> push(Data);
19
+ Else -> Else
20
+ end;
21
+ push(Data) ->
22
+ gen_server:call(cap_cluster_gems, {push, Data}).
23
+
24
+
25
+ -spec lookup(binary() | dependency()) -> {ok, gem_spec()} | {error, not_found} .
26
+ lookup(GemName) ->
27
+ gen_server:call(cap_cluster_gems, {lookup, GemName}).
28
+
29
+
30
+ -spec lookup(node(), binary() | dependency()) -> {ok, gem_spec()} | {error, not_found} .
31
+ lookup(Node, GemName) ->
32
+ gen_server:call({cap_cluster_gems, Node}, {lookup, GemName}).
33
+
34
+
35
+ -spec pull(gem_id() | gem_spec()) -> {ok, binary()} | {error, term()} .
36
+ pull(Spec) ->
37
+ gen_server:call(cap_cluster_gems, {pull, Spec}).
38
+
39
+
40
+ -spec pull(node(), gem_id() | gem_spec()) -> {ok, binary()} | {error, term()} .
41
+ pull(Node, Spec) ->
42
+ gen_server:call({cap_cluster_gems, Node}, {pull, Spec}).
43
+
44
+
45
+ -spec missing() -> {ok, [dependency()]} .
46
+ missing() ->
47
+ gen_server:call(cap_cluster_gems, {missing}).
48
+
49
+
50
+ -spec all() -> {ok, [gem_id()]} .
51
+ all() ->
52
+ gen_server:call(cap_cluster_gems, {all}).
53
+
54
+
55
+ -spec check() -> ok .
56
+ check() ->
57
+ gen_server:cast(cap_cluster_gems, check).
58
+
59
+
60
+ -spec start_link() -> {ok, pid()} .
61
+ start_link() ->
62
+ gen_server:start_link({local, cap_cluster_gems}, ?MODULE, [], []).
63
+
64
+
65
+ init([]) ->
66
+ Root = cap_config:get(cluster, database, "var/run/capricorn"),
67
+
68
+ TablePath = filename:join([Root, "gems.db"]),
69
+ {ok, Ref} = dets:open_file(cap_cluster_gems, [{file, TablePath}, {keypos, 2}]),
70
+ update_gems_table(Ref),
71
+
72
+ GemPath = filename:join([Root, "gems"]),
73
+ os:cmd("mkdir -p "++GemPath),
74
+
75
+ StagePath = filename:join([Root, "stage"]),
76
+ os:cmd("mkdir -p "++StagePath),
77
+
78
+ State = #state{table=Ref, gem_path=GemPath, stage_path=StagePath},
79
+ State1 = start_spec_reader(State),
80
+
81
+ {ok, State1}.
82
+
83
+
84
+ handle_call({push, Data}, _From, #state{stage_path=Stage,tmp_id=TmpId}=State) ->
85
+ StageGemPath = filename:join([Stage, integer_to_list(TmpId) ++ ".gem"]),
86
+ R = file:write_file(StageGemPath, Data),
87
+ ?LOG_INFO("G: ~p ~p", [StageGemPath, R]),
88
+ case do_push(StageGemPath, State) of
89
+ {ok, Missing} ->
90
+ {reply, {ok, Missing}, State#state{tmp_id=TmpId+1}};
91
+ {error, Reason} ->
92
+ file:delete(StageGemPath),
93
+ {reply, {error, Reason}, State#state{tmp_id=TmpId+1}}
94
+ end;
95
+
96
+ handle_call({pull, #gem{id=Id}}, _From, State) ->
97
+ handle_call({pull, Id}, _From, State);
98
+
99
+ handle_call({pull, #gem_id{}=Id}, _From, State) ->
100
+ Path = gem_path(Id, State),
101
+ {reply, file:read_file(Path), State};
102
+
103
+ handle_call({missing}, _From, #state{table=T}=State) ->
104
+ Missing = dets:foldl(fun(#gem{missing=Deps}, Acc)->
105
+ Acc++Deps
106
+ end, [], T),
107
+ {reply, {ok, lists:usort(Missing)}, State};
108
+
109
+ handle_call({all}, _From, #state{table=T}=State) ->
110
+ All = dets:foldl(fun(Gem, Acc)->
111
+ [Gem#gem.id |Acc]
112
+ end, [], T),
113
+ {reply, {ok, lists:usort(All)}, State};
114
+
115
+ handle_call({lookup, {_,_}=Dep}, _From, State) ->
116
+ case do_find_last_consistent_gem(Dep, State) of
117
+ not_found -> {reply, {error, not_found}, State};
118
+ GemSpec -> {reply, {ok, GemSpec}, State}
119
+ end;
120
+
121
+ handle_call({lookup, GemName}, _From, State) ->
122
+ case do_find_last_consistent_gem(GemName, State) of
123
+ not_found -> {reply, {error, not_found}, State};
124
+ GemSpec -> {reply, {ok, GemSpec}, State}
125
+ end;
126
+
127
+ handle_call(Msg, _From, State) ->
128
+ ?LOG_DEBUG("Unhandled call: ~p", [Msg]),
129
+ {reply, ok, State}.
130
+
131
+
132
+ handle_cast(check, State) ->
133
+ do_check(State),
134
+ {noreply, State};
135
+
136
+ handle_cast(stop, State) ->
137
+ {stop, normal, State};
138
+
139
+ handle_cast(Msg, State) ->
140
+ ?LOG_DEBUG("Unhandled cast: ~p", [Msg]),
141
+ {noreply, State}.
142
+
143
+
144
+ handle_info({Port, {exit_status, _Status}}, #state{spec_reader=Port}=State) ->
145
+ {noreply, start_spec_reader(State)};
146
+
147
+ handle_info(Info, State) ->
148
+ ?LOG_DEBUG("Unhandled info: ~p", [Info]),
149
+ {noreply, State}.
150
+
151
+
152
+ terminate(_Reason, #state{table=T,spec_reader=P}) ->
153
+ dets:close(T),
154
+ bertio:port_close(P),
155
+ ok.
156
+
157
+
158
+ code_change(_OldVsn, State, _Extra) ->
159
+ {ok, State}.
160
+
161
+
162
+ -spec start_spec_reader(state()) -> state() .
163
+ start_spec_reader(State) ->
164
+ RubyCmd = os:find_executable("capricorn-gem-spec"),
165
+ SpecReader = bertio:open_port({spawn_executable, RubyCmd}, [exit_status]),
166
+ State#state{spec_reader=SpecReader}.
167
+
168
+
169
+ -spec do_find_last_consistent_gem(dependency() | binary(), state()) -> not_found | gem_spec() .
170
+ do_find_last_consistent_gem({Name, Reqs}, #state{table=T}) ->
171
+ dets:foldl(fun
172
+ (#gem{missing=[]}=Spec, #gem{}=Acc) ->
173
+ if ((Spec#gem.id)#gem_id.name == Name)
174
+ andalso ((Spec#gem.id)#gem_id.version > (Acc#gem.id)#gem_id.version) ->
175
+ case cap_gem_utils:match_requirements((Spec#gem.id)#gem_id.version, Reqs) of
176
+ true -> Spec;
177
+ false -> Acc
178
+ end;
179
+ true ->
180
+ Acc
181
+ end;
182
+ (#gem{missing=[]}=Spec, not_found) ->
183
+ if ((Spec#gem.id)#gem_id.name == Name) ->
184
+ case cap_gem_utils:match_requirements((Spec#gem.id)#gem_id.version, Reqs) of
185
+ true -> Spec;
186
+ false -> not_found
187
+ end;
188
+ true ->
189
+ not_found
190
+ end;
191
+ (#gem{missing=_}, Acc) -> Acc
192
+ end, not_found, T);
193
+
194
+ do_find_last_consistent_gem(Gem, #state{table=T}) ->
195
+ dets:foldl(fun
196
+ (#gem{missing=[]}=Spec, #gem{}=Acc) ->
197
+ if ((Spec#gem.id)#gem_id.name == Gem)
198
+ andalso ((Spec#gem.id)#gem_id.version > (Acc#gem.id)#gem_id.version) ->
199
+ Spec;
200
+ true ->
201
+ Acc
202
+ end;
203
+ (#gem{missing=[]}=Spec, not_found) ->
204
+ if ((Spec#gem.id)#gem_id.name == Gem) ->
205
+ Spec;
206
+ true ->
207
+ not_found
208
+ end;
209
+ (#gem{missing=_}, Acc) -> Acc
210
+ end, not_found, T).
211
+
212
+
213
+ -spec do_push(string() | binary(), state()) -> {ok, [dependency()]} | {error, already_present} | {error, timeout | {not_found} | {gem_error, binary()}} .
214
+ do_push(StageGemPath, #state{table=T}=Ctx) ->
215
+ case do_spec(StageGemPath, Ctx) of
216
+ #gem{id=Id,deps=Deps}=Spec ->
217
+ case dets:member(T,Id) of
218
+ false ->
219
+ {_, Missing} = find_dependencies(Deps, Ctx),
220
+ Spec1 = Spec#gem{missing=Missing},
221
+ case dets:insert_new(T, Spec1) of
222
+ true ->
223
+ GemPath = gem_path(Spec1, Ctx),
224
+ file:rename(StageGemPath, GemPath),
225
+ mark_gem_as_found(Id, Missing, Ctx),
226
+ case Missing of
227
+ [] ->
228
+ % GemName = Id#gem_id.name,
229
+ % [cap_machine_apps:update_gem(Node, GemName) || Node <- nodes()],
230
+ {ok, Missing};
231
+ Missing ->
232
+ {ok, Missing}
233
+ end;
234
+ false -> {error, already_present}
235
+ end;
236
+ _ -> {error, already_present}
237
+ end;
238
+ E -> E
239
+ end.
240
+
241
+
242
+ -spec do_spec(string() | binary(), state()) -> gem_spec() | {error, timeout | {not_found} | {gem_error, binary()}} .
243
+ do_spec(GemPath, Ctx) when is_list(GemPath) ->
244
+ do_spec(list_to_binary(GemPath),Ctx);
245
+ do_spec(GemPath, #state{spec_reader=P}) ->
246
+ bertio:send(P, GemPath),
247
+ try bertio:recv(P) of
248
+ {bert, {_, _, _}=Gem} -> normalize_gem(Gem);
249
+ {bert, R} -> R
250
+ catch
251
+ error:timeout -> {error, timeout}
252
+ end.
253
+
254
+ -spec do_check(state()) -> ok .
255
+ do_check(#state{table=T}=Ctx) ->
256
+ dets:foldl(fun(Spec, Acc) ->
257
+ mark_gem_as_found(Spec, [], Ctx),
258
+ Acc
259
+ end, ok, T).
260
+
261
+ mark_gem_as_found(#gem{id=Id}, NewMissing, Ctx) ->
262
+ mark_gem_as_found(Id, NewMissing, Ctx);
263
+ mark_gem_as_found(#gem_id{}=Id, NewMissing, #state{table=T}) ->
264
+ UpdatedSpecs = dets:foldl(fun(#gem{missing=Missing}=Spec, Acc) ->
265
+ case fold_missing_dependants(Id, Spec, Missing, NewMissing, false) of
266
+ {true, Spec1} -> [Spec1|Acc];
267
+ {false, _} -> Acc
268
+ end
269
+ end, [], T),
270
+ dets:insert(T, UpdatedSpecs).
271
+
272
+ fold_missing_dependants(#gem_id{}, Spec, [], Acc, Update) ->
273
+ {Update, Spec#gem{missing=Acc}};
274
+ fold_missing_dependants(#gem_id{name=Name, version=Version}=Id, Spec,
275
+ [{Name, Reqs}=Dep|Rest], Acc, Update) ->
276
+ case cap_gem_utils:match_requirements(Version, Reqs) of
277
+ true -> fold_missing_dependants(Id, Spec, Rest, Acc, true);
278
+ false -> fold_missing_dependants(Id, Spec, Rest, [Dep|Acc], Update)
279
+ end;
280
+ fold_missing_dependants(Id, Spec, [Dep|Rest], Acc, Update) ->
281
+ fold_missing_dependants(Id, Spec, Rest, [Dep|Acc], Update).
282
+
283
+
284
+ -spec gem_path(gem_spec() | gem_id(), state()) -> string() .
285
+ gem_path(#gem{id=Id}, Ctx) ->
286
+ gem_path(Id, Ctx);
287
+ gem_path(#gem_id{name=Name, version=Version}, #state{gem_path=GemPath}) ->
288
+ filename:join([GemPath, binary_to_list(Name) ++ "-" ++version_to_string(Version) ++ ".gem"]).
289
+
290
+
291
+ -spec version_to_string(version()) -> string().
292
+ version_to_string({Parts}) ->
293
+ case version_parts_to_string(Parts, "") of
294
+ "." ++ V -> V;
295
+ R -> R
296
+ end.
297
+
298
+ -spec version_parts_to_string(version_parts(), string()) -> string().
299
+ version_parts_to_string([], Acc) -> Acc;
300
+ version_parts_to_string([Part|Rest], Acc) when is_integer(Part) ->
301
+ version_parts_to_string(Rest, Acc++"."++integer_to_list(Part));
302
+ version_parts_to_string([Part|Rest], Acc) ->
303
+ version_parts_to_string(Rest, Acc++"."++Part).
304
+
305
+
306
+ -spec find_dependencies([dependency()], state()) -> {[gem_spec()], [dependency()]} .
307
+ find_dependencies(Deps, Ctx) ->
308
+ find_dependencies(Deps, [], [], Ctx).
309
+
310
+
311
+ -spec find_dependencies([dependency()], [gem_spec()], [dependency()], state()) -> {[gem_spec()], [dependency()]} .
312
+ find_dependencies([], Found, Missing, _Ctx) -> {Found, lists:usort(Missing)};
313
+ find_dependencies([Dep|Rest], Found, Missing, Ctx) ->
314
+ case find_dependency(Dep, Ctx) of
315
+ undefined ->
316
+ find_dependencies(Rest, Found, [Dep|Missing], Ctx);
317
+ #gem{missing=M}=Spec ->
318
+ find_dependencies(Rest, [Spec|Found], M++Missing, Ctx)
319
+ end.
320
+
321
+
322
+ -spec find_dependency(dependency(), state()) -> undefined | gem_spec() .
323
+ find_dependency({Name, Reqs}, #state{table=T}) ->
324
+ dets:foldl(fun(#gem{id=#gem_id{name=Name1, version=Version}}=Spec, Acc) ->
325
+ if Name1 == Name ->
326
+ case cap_gem_utils:match_requirements(Version, Reqs) of
327
+ true ->
328
+ case Acc of
329
+ #gem{id=#gem_id{version=Version1}} ->
330
+ case cap_gem_utils:match_requirement(Version1, {'>', Version}) of
331
+ true -> Acc;
332
+ false -> Spec
333
+ end;
334
+ undefined -> Spec
335
+ end;
336
+ false -> Acc
337
+ end;
338
+ true ->
339
+ Acc
340
+ end
341
+ end, undefined, T).
342
+
343
+
344
+ -type port_requirement() :: {binary(), binary()} .
345
+ -type port_dependency() :: {binary(), [port_requirement()]} .
346
+ -type port_gem_spec() :: {binary(), binary(), [port_dependency()]} .
347
+
348
+ -spec normalize_gem(port_gem_spec()) -> gem_spec().
349
+ normalize_gem({Name, Version, Deps1}) ->
350
+ Deps2 = [begin
351
+ {DepName,Reqs1} = Dep1,
352
+ Reqs2 = [begin
353
+ {Op, Version1} = Req1,
354
+ Version2 = normalize_gem_version(Version1),
355
+ {?b2a(Op), Version2}
356
+ end || Req1 <- Reqs1],
357
+ {DepName, Reqs2}
358
+ end || Dep1 <- Deps1],
359
+
360
+ #gem{
361
+ id=#gem_id{
362
+ name=Name,
363
+ version=normalize_gem_version(Version)},
364
+ deps=Deps2}.
365
+
366
+
367
+ -spec normalize_gem_version(binary() | string() | version()) -> version() .
368
+ normalize_gem_version({Parts}) when is_list(Parts) ->
369
+ {Parts};
370
+ normalize_gem_version(Version) when is_binary(Version) ->
371
+ normalize_gem_version(binary_to_list(Version));
372
+ normalize_gem_version(Version) ->
373
+ {[case string:to_integer(Part) of
374
+ {I,[]} when is_integer(I) -> I;
375
+ _Else -> Part
376
+ end || Part <- string:tokens(Version, ".")]}.
377
+
378
+
379
+ update_gems_table(Table) ->
380
+ cap_dets_updater:update(Table, fun
381
+ ({gem, _Id, _Deps, _Missing, {rvsn, 2}}) ->
382
+ ok;
383
+
384
+ ({gem, Id1, Deps1, Missing1, {rvsn, 1}}) ->
385
+ UpVersion = fun
386
+ ({{Parts}}) -> {Parts};
387
+ ({Parts}) when is_list(Parts) -> {Parts}
388
+ end,
389
+ UpReq = fun({Op, V}) -> {Op, UpVersion(V)} end,
390
+ UpDep = fun({Name, Reqs1}) ->
391
+ Reqs2 = [UpReq(Req) || Req <- Reqs1],
392
+ {Name, Reqs2}
393
+ end,
394
+
395
+ Deps2 = [UpDep(Dep) || Dep <- Deps1],
396
+ Missing2 = [UpDep(Mis) || Mis <- Missing1],
397
+
398
+ {update, {gem, Id1, Deps2, Missing2, {rvsn, 2}}};
399
+
400
+ ({gem, Id1, Deps1, Missing1, {rvsn, 0}}) ->
401
+ UpVersion = fun
402
+ ({version, Parts}) -> {Parts};
403
+ ({Parts}) -> {Parts}
404
+ end,
405
+ UpId = fun({gem_id, Name, V}) -> {gem_id, Name, UpVersion(V)} end,
406
+ UpReq = fun({requirement, Op, V}) -> {?b2a(Op), UpVersion(V)} end,
407
+ UpDep = fun({dependency, Name, Reqs1}) ->
408
+ Reqs2 = [UpReq(Req) || Req <- Reqs1],
409
+ {Name, Reqs2}
410
+ end,
411
+
412
+ Id2 = UpId(Id1),
413
+ Deps2 = [UpDep(Dep) || Dep <- Deps1],
414
+ Missing2 = [UpDep(Mis) || Mis <- Missing1],
415
+
416
+ {update, {gem, Id2, Deps2, Missing2, {rvsn, 1}}};
417
+
418
+ ({gem, Id, Deps, Missing}) ->
419
+ {update, {gem, Id, Deps, Missing, {rvsn, 0}}}
420
+
421
+ end).
422
+