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,69 @@
1
+ -module(cap_internal_api).
2
+ -behaviour(bertrpc).
3
+
4
+
5
+
6
+ -export([start_link/1]).
7
+ -export([init/1, handle_call/3, handle_cast/2,
8
+ handle_info/2, terminate/2, code_change/3]).
9
+
10
+
11
+
12
+ -record(state, {
13
+ user :: binary() | undefined,
14
+ services :: [atom()]
15
+ }).
16
+ -type state() :: #state{} .
17
+
18
+
19
+
20
+ -spec start_link([{atom(), atom()}]) -> {ok, pid()} .
21
+
22
+ start_link(Services) ->
23
+ bertrpc:listen_link({local, ?MODULE}, ?MODULE, Services, 6789).
24
+
25
+
26
+
27
+ -spec init([{atom(), atom()}]) -> {ok, state()} .
28
+
29
+ init(Services) ->
30
+ {ok, #state{ services = Services }}.
31
+
32
+
33
+
34
+ handle_call({M, F, A, I}, From, State) ->
35
+ #state { services = Services } = State,
36
+ case proplists:get_value(M, Services) of
37
+ undefined ->
38
+ {reply, {error, service_not_found}, State};
39
+ Module ->
40
+ Module:handle_call({F, A, I}, From, State)
41
+ end.
42
+
43
+
44
+
45
+ handle_cast({M, F, A, I}, State) ->
46
+ #state { services = Services } = State,
47
+ case proplists:get_value(M, Services) of
48
+ undefined ->
49
+ {noreply, State};
50
+ Module ->
51
+ Module:handle_cast({F, A, I}, State)
52
+ end.
53
+
54
+
55
+
56
+ handle_info(_Info, State) ->
57
+ {noreply, State}.
58
+
59
+
60
+
61
+ terminate(_Reason, _State) ->
62
+ ok.
63
+
64
+
65
+
66
+ code_change(_OldVsn, State, _Extra) ->
67
+ {ok, State}.
68
+
69
+
@@ -0,0 +1,38 @@
1
+ -module(cap_internal_apps_api).
2
+
3
+
4
+ % start_link() ->
5
+ % bertrpc_module:start_link(?MODULE, [], []).
6
+ %
7
+ % init([]) ->
8
+ % {ok, state}.
9
+ %
10
+ %
11
+ % bert_call(call, [Node, Id, Module, Function, Args], _, State) ->
12
+ % try
13
+ % cap_machine_apps:call(Node, Id, Module, Function, Args)
14
+ % of
15
+ % {ok, Result} -> {reply, Result, State};
16
+ % {error, Error} -> {error, Error, State}
17
+ % catch
18
+ % throw:T -> {error, T, State}
19
+ % end;
20
+ % bert_call(cast, [Node, Id, Module, Function, Args], _, State) ->
21
+ % try
22
+ % cap_machine_apps:cast(Node, Id, Module, Function, Args)
23
+ % of
24
+ % {ok, Result} -> {reply, Result, State};
25
+ % {error, Error} -> {error, Error, State}
26
+ % catch
27
+ % throw:T -> {error, T, State}
28
+ % end.
29
+ %
30
+ % bert_cast(_, _, _Extra, State) ->
31
+ % {noreply, State}.
32
+ %
33
+ %
34
+ % terminate(_Reason, _State) ->
35
+ % ok.
36
+ %
37
+ % code_change(_OldVsn, State, _Extra) ->
38
+ % {ok, State}.
@@ -0,0 +1,113 @@
1
+ %%
2
+ %% Application module
3
+ %%
4
+ %% File : cap_log.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_log).
14
+ -behaviour(gen_event).
15
+
16
+ -export([start_link/0,stop/0]).
17
+ -export([debug_on/0,info_on/0,get_level/0,get_level_integer/0, set_level/1]).
18
+ -export([init/1, handle_event/2, terminate/2, code_change/3, handle_info/2, handle_call/2]).
19
+
20
+ -define(LEVEL_ERROR, 3).
21
+ -define(LEVEL_INFO, 2).
22
+ -define(LEVEL_DEBUG, 1).
23
+ -define(LEVEL_TMI, 0).
24
+
25
+ level_integer(error) -> ?LEVEL_ERROR;
26
+ level_integer(info) -> ?LEVEL_INFO;
27
+ level_integer(debug) -> ?LEVEL_DEBUG;
28
+ level_integer(tmi) -> ?LEVEL_TMI;
29
+ level_integer(_Else) -> ?LEVEL_ERROR. % anything else default to ERROR level
30
+
31
+ level_atom(?LEVEL_ERROR) -> error;
32
+ level_atom(?LEVEL_INFO) -> info;
33
+ level_atom(?LEVEL_DEBUG) -> debug;
34
+ level_atom(?LEVEL_TMI) -> tmi.
35
+
36
+
37
+ start_link() ->
38
+ cap_event_sup:start_link({local, cap_log}, error_logger, cap_log, []).
39
+
40
+ stop() ->
41
+ cap_event_sup:stop(cap_log).
42
+
43
+ init([]) ->
44
+ % read config and register for configuration changes
45
+
46
+ % just stop if one of the config settings change. capricorn_server_sup
47
+ % will restart us and then we will pick up the new settings.
48
+
49
+ Filename = cap_config:get(log, file, "var/log/capricorn.log"),
50
+ Level = cap_config:get(log, level, info),
51
+
52
+ {ok, Fd} = file:open(Filename, [append]),
53
+ {ok, {Fd, level_integer(Level)}}.
54
+
55
+ debug_on() ->
56
+ get_level_integer() =< ?LEVEL_DEBUG.
57
+
58
+ info_on() ->
59
+ get_level_integer() =< ?LEVEL_INFO.
60
+
61
+ set_level(LevelAtom) ->
62
+ set_level_integer(level_integer(LevelAtom)).
63
+
64
+ get_level() ->
65
+ level_atom(get_level_integer()).
66
+
67
+ get_level_integer() ->
68
+ catch gen_event:call(error_logger, cap_log, get_level_integer).
69
+
70
+ set_level_integer(Int) ->
71
+ gen_event:call(error_logger, cap_log, {set_level_integer, Int}).
72
+
73
+ handle_event({error_report, _, {Pid, capricorn_error, {Format, Args}}}, {Fd, _LogLevel}=State) ->
74
+ log(Fd, Pid, error, Format, Args),
75
+ {ok, State};
76
+ handle_event({error_report, _, {Pid, _, _}}=Event, {Fd, _LogLevel}=State) ->
77
+ log(Fd, Pid, error, "~p", [Event]),
78
+ {ok, State};
79
+ handle_event({error, _, {Pid, Format, Args}}, {Fd, _LogLevel}=State) ->
80
+ log(Fd, Pid, error, Format, Args),
81
+ {ok, State};
82
+ handle_event({info_report, _, {Pid, capricorn_info, {Format, Args}}}, {Fd, LogLevel}=State) when LogLevel =< ?LEVEL_INFO ->
83
+ log(Fd, Pid, info, Format, Args),
84
+ {ok, State};
85
+ handle_event({info_report, _, {Pid, capricorn_debug, {Format, Args}}}, {Fd, LogLevel}=State) when LogLevel =< ?LEVEL_DEBUG ->
86
+ log(Fd, Pid, debug, Format, Args),
87
+ {ok, State};
88
+ handle_event({_, _, {Pid, _, _}}=Event, {Fd, LogLevel}=State) when LogLevel =< ?LEVEL_TMI ->
89
+ % log every remaining event if tmi!
90
+ log(Fd, Pid, tmi, "~p", [Event]),
91
+ {ok, State};
92
+ handle_event(_Event, State) ->
93
+ {ok, State}.
94
+
95
+ handle_call(get_level_integer, {_Fd, LogLevel}=State) ->
96
+ {ok, LogLevel, State};
97
+ handle_call({set_level_integer, NewLevel}, {Fd, _LogLevel}) ->
98
+ {ok, ok, {Fd, NewLevel}}.
99
+
100
+ handle_info(_Info, State) ->
101
+ {ok, State}.
102
+
103
+ code_change(_OldVsn, State, _Extra) ->
104
+ {ok, State}.
105
+
106
+ terminate(_Arg, {Fd, _LoggingLevel}) ->
107
+ file:close(Fd).
108
+
109
+ log(Fd, Pid, Level, Format, Args) ->
110
+ Msg = io_lib:format(Format, Args),
111
+ ok = io:format("[~s] [~p] ~s~n", [Level, Pid, Msg]), % dump to console too
112
+ Msg2 = re:replace(lists:flatten(Msg),"\\r\\n|\\r|\\n", "\n", [global, {return, list}]),
113
+ ok = io:format(Fd, "[~s] [~s] [~p] ~s~n", [httpd_util:rfc1123_date(), Level, Pid, Msg2]).
@@ -0,0 +1,338 @@
1
+ %%
2
+ %% Supervised worker process module
3
+ %%
4
+ %% File : cap_machine.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_machine).
14
+ -author('simonmenke <simon.menke@gmail.com>').
15
+ -include("capricorn.hrl").
16
+ -behaviour(gen_server).
17
+
18
+ %% external api
19
+ -export([config/1]).
20
+
21
+ %% operation & maintenance api
22
+ -export([start_link/0, stop/0]).
23
+
24
+ %% gen_server callbacks
25
+ -export([init/1, handle_call/3, handle_cast/2,
26
+ handle_info/2, terminate/2, code_change/3]).
27
+ -export([wait_for_cluster/2, monitor_cluster/2]).
28
+ -export([ensure_gems_are_present_for_app/1]).
29
+
30
+ -record(ctx, {
31
+ cluster,
32
+ knows_cluster=false,
33
+ gems_path,
34
+ installed_gems
35
+ }).
36
+
37
+ config(Node) ->
38
+ lists:sort(gen_server:call({cap_config, Node}, all)).
39
+
40
+ %%
41
+ %% Operation & Maintenance API
42
+ %%
43
+
44
+ %% @spec start_link() -> {ok, Pid}
45
+ %% @doc Start the cap_machine
46
+ start_link() ->
47
+ gen_server:start_link({local, cap_machine}, ?MODULE, [], []).
48
+
49
+ stop() ->
50
+ gen_server:cast(cap_machine, stop).
51
+
52
+ ensure_gems_are_present_for_app(App) ->
53
+ gen_server:call(cap_machine, {ensure_gems_are_present_for_app, App}, 900000).
54
+
55
+ %%
56
+ %% Genserver callback functions
57
+ %%
58
+
59
+ %% @spec init(State) -> {ok, State}
60
+ %% @doc Callback for initialize the cap_machine
61
+ init([]) ->
62
+ Node = cap_config:get(machine, cluster, cluster),
63
+ KnowsCluster =
64
+ case net_adm:ping(Node) of
65
+ pong ->
66
+ monitor_cluster(Node),
67
+ ?LOG_INFO("Connected to ~s", [Node]),
68
+ true;
69
+ pang ->
70
+ ?LOG_INFO("Cannot connect to ~s", [Node]),
71
+ wait_for_cluster(Node),
72
+ false
73
+ end,
74
+
75
+ GemsPath = cap_config:get(machine, gems_path, "/usr/lib/ruby/gems/1.8"),
76
+
77
+ InstalledGems = ets:new(gems, [set,private,{keypos,2}]),
78
+
79
+ emq:new(machine_queue, [{size, 1}]),
80
+
81
+ {ok, #ctx{
82
+ knows_cluster = KnowsCluster,
83
+ cluster = Node,
84
+ gems_path = GemsPath,
85
+ installed_gems = InstalledGems
86
+ }}.
87
+
88
+ %% @spec handle_call(_Request, _From, State) -> {reply, Reply, State}
89
+ %% @doc Callback for synchronous requests
90
+ handle_call({ensure_gems_are_present_for_app, App}, _From, State) ->
91
+ R = do_ensure_gem_for_app(App, State),
92
+ {reply, R, State};
93
+ handle_call(_Request, _From, State) ->
94
+ Reply = ok,
95
+ {reply, Reply, State}.
96
+
97
+ %% @spec handle_cast(stop, State) -> {stop, normal, State}
98
+ %% @doc Callback for assynchronous messages
99
+ handle_cast(stop, State) ->
100
+ {stop, normal, State};
101
+ handle_cast({cluster_up, Node}, State) ->
102
+ ?LOG_INFO("Cluster came up: ~s", [Node]),
103
+ monitor_cluster(Node),
104
+ {noreply, State#ctx{knows_cluster=true}};
105
+ handle_cast({cluster_down, Node}, State) ->
106
+ ?LOG_INFO("Cluster went down: ~s", [Node]),
107
+ wait_for_cluster(Node),
108
+ {noreply, State#ctx{knows_cluster=false}};
109
+ handle_cast(_Msg, State) ->
110
+ {noreply, State}.
111
+
112
+ %% @spec handle_info(_Info, State) -> {noreply, State}
113
+ %% @doc Callback for timeout or other unreconized messages
114
+ handle_info(_Info, State) ->
115
+ {noreply, State}.
116
+
117
+ %% @spec terminate(_Reason, _State) -> ok
118
+ %% @doc Callback for free resources used by the server
119
+ terminate(_Reason, _State) ->
120
+ ok.
121
+
122
+ %% @spec code_change(_OldVsn, State, _Extra) -> {ok, State}
123
+ %% @doc Callback for upgrade source code
124
+ code_change(_OldVsn, State, _Extra) ->
125
+ {ok, State}.
126
+
127
+ wait_for_cluster(Node) ->
128
+ spawn_link(?MODULE, wait_for_cluster, [Node, self()]).
129
+ wait_for_cluster(Node, Owner) ->
130
+ case net_adm:ping(Node) of
131
+ pong ->
132
+ gen_server:cast(Owner, {cluster_up, Node});
133
+ pang ->
134
+ receive after 5000 -> wait_for_cluster(Node, Owner) end
135
+ end.
136
+
137
+ monitor_cluster(Node) ->
138
+ spawn_link(?MODULE, monitor_cluster, [Node, self()]).
139
+ monitor_cluster(Node, Owner) ->
140
+ erlang:monitor_node(Node, true),
141
+ receive
142
+ {nodedown, Node} ->
143
+ gen_server:cast(Owner, {cluster_down, Node})
144
+ end.
145
+
146
+
147
+
148
+ do_ensure_gem_for_app(App, Ctx) ->
149
+ RequiredGems = App#application.required_gems,
150
+ case do_install_gems(RequiredGems, Ctx) of
151
+ {ok, Installed} ->
152
+ {ok, App#application{
153
+ installed_gems = uniq_list(Installed)
154
+ }};
155
+ Error -> Error
156
+ end.
157
+
158
+
159
+
160
+ do_install_gems(Gems, Ctx) ->
161
+ lists:foldl(fun
162
+ (Gem, {ok, Acc}) ->
163
+ case do_install_gem(Gem, Ctx) of
164
+ {ok, Installed} ->
165
+ {ok, Acc ++ Installed};
166
+ Error -> Error
167
+ end;
168
+ (_, Error) ->
169
+ Error
170
+ end, {ok, []}, Gems).
171
+
172
+ do_install_gem(#gem{}=Spec, Ctx) ->
173
+ do_install_gem(deps, Spec, Ctx);
174
+
175
+ do_install_gem(NameOrDep, Ctx) ->
176
+ do_install_gem(lookup, NameOrDep, Ctx).
177
+
178
+ do_install_gem(lookup, {DepName,_}=Dep, #ctx{cluster=Cluster}=Ctx) ->
179
+ case cap_cluster_gems:lookup(Cluster, Dep) of
180
+ {ok, Spec} ->
181
+ ?LOG_DEBUG("found gem ~s", [DepName]),
182
+ do_install_gem(deps, Spec, Ctx);
183
+
184
+ {error, not_found} ->
185
+ ?LOG_DEBUG("error ~p", [{missing_gem, DepName}]),
186
+ {error, {missing_gem, Dep}}
187
+ end;
188
+
189
+ do_install_gem(lookup, GemName, #ctx{cluster=Cluster}=Ctx) ->
190
+ case cap_cluster_gems:lookup(Cluster, GemName) of
191
+ {ok, Spec} ->
192
+ ?LOG_DEBUG("found gem ~s", [GemName]),
193
+ do_install_gem(deps, Spec, Ctx);
194
+
195
+ {error, not_found} ->
196
+ ?LOG_DEBUG("error ~p", [{missing_gem, GemName}]),
197
+ {error, {missing_gem, GemName}}
198
+ end;
199
+
200
+ do_install_gem(deps, #gem{deps=Deps}=Spec, Ctx) ->
201
+ Installed = lists:foldl(fun
202
+ (Dep, Acc) when is_list(Acc) ->
203
+ case do_install_gem(lookup, Dep, Ctx) of
204
+ {ok, CurrentInstalled} ->
205
+ Acc ++ CurrentInstalled;
206
+ Error -> Error
207
+ end;
208
+ (_, Error) ->
209
+ Error
210
+ end, [], Deps),
211
+
212
+ case Installed of
213
+ Installed when is_list(Installed) ->
214
+ case do_install_gem(check, Spec, Ctx) of
215
+ {ok, Installed2} ->
216
+ {ok, Installed ++ Installed2};
217
+ Error -> Error
218
+ end;
219
+
220
+ Error ->
221
+ ?LOG_DEBUG("error ~p", [Error]),
222
+ Error
223
+ end;
224
+
225
+ do_install_gem(check, #gem{}=Spec, Ctx) ->
226
+ GemName = (Spec#gem.id)#gem_id.name,
227
+ case do_is_gem_installed(Spec, Ctx) of
228
+ true ->
229
+ ?LOG_DEBUG("allready installed gem ~s", [GemName]),
230
+ {ok, [Spec#gem.id]};
231
+ false ->
232
+ do_install_gem(pull, Spec, Ctx);
233
+ {error, E} ->
234
+ ?LOG_DEBUG("error ~p", [E]),
235
+ {error, E}
236
+ end;
237
+
238
+ do_install_gem(pull, #gem{}=Spec, #ctx{cluster=Cluster}=Ctx) ->
239
+ GemName = (Spec#gem.id)#gem_id.name,
240
+ ?LOG_DEBUG("pull gem ~s", [GemName]),
241
+ case cap_cluster_gems:pull(Cluster, Spec) of
242
+ {ok, Data} ->
243
+
244
+ ?LOG_DEBUG("staging gem ~s", [GemName]),
245
+ case file:write_file("/tmp/capricorn-gem.gem", Data) of
246
+ ok ->
247
+
248
+ do_install_gem(install, Spec, Ctx);
249
+
250
+ Error ->
251
+ ?LOG_DEBUG("error ~p", [Error]),
252
+ Error
253
+ end;
254
+
255
+ Error ->
256
+ ?LOG_DEBUG("error ~p", [Error]),
257
+ Error
258
+ end;
259
+
260
+ do_install_gem(install, #gem{}=Spec, _Ctx) ->
261
+ GemName = (Spec#gem.id)#gem_id.name,
262
+ Cmd = "install --no-rdoc --no-ri --local --no-update-sources "++
263
+ "/tmp/capricorn-gem.gem",
264
+
265
+ ?LOG_INFO("installing gem ~s", [GemName]),
266
+
267
+ case gem_exec(Cmd) of
268
+ "Successfully"++_ ->
269
+ {ok, [Spec#gem.id]};
270
+
271
+ Error ->
272
+ ?LOG_DEBUG("error ~p", [Error]),
273
+ {error, {install_failed, GemName}}
274
+ end.
275
+
276
+
277
+
278
+ -spec do_is_gem_installed(gem_spec(), #ctx{}) -> true | false | {error, badarg} .
279
+ do_is_gem_installed(#gem{}=Gem, #ctx{installed_gems=T, gems_path=GemsPath}) ->
280
+ case ets:lookup(T, Gem#gem.id) of
281
+ [] ->
282
+ Name1 = (Gem#gem.id)#gem_id.name,
283
+ Name2 = binary_to_list(Name1),
284
+
285
+ Version1 = (Gem#gem.id)#gem_id.version,
286
+ case Version1 of
287
+ undefined ->
288
+ Version2 = "*",
289
+ Fullname = lists:flatten([Name2, "-", Version2]),
290
+ Path = filename:join([GemsPath, "gems", Fullname]),
291
+
292
+ case filelib:wildcard(Path) of
293
+ [] -> false;
294
+ _Else ->
295
+ ets:insert(T, Gem),
296
+ true
297
+ end;
298
+ _Else ->
299
+ Version2 = cap_cluster_gems:version_to_string(Version1),
300
+ Fullname = lists:flatten([Name2, "-", Version2]),
301
+ Path = filename:join([GemsPath, "gems", Fullname]),
302
+
303
+ case filelib:is_file(Path) of
304
+ false -> false;
305
+ true ->
306
+ ets:insert(T, Gem),
307
+ true
308
+ end
309
+ end;
310
+ _Else -> true
311
+ end.
312
+
313
+ gem_exec(Args) ->
314
+ os:cmd(lists:flatten([gem_exe(), " " |Args])).
315
+
316
+ gem_exe() ->
317
+ case get(gem_exe) of
318
+ Cmd when is_list(Cmd) -> Cmd;
319
+ _ ->
320
+ case os:find_executable("gem") of
321
+ false -> throw({error, missing_cmd, "gem"});
322
+ GemPath ->
323
+ put(gem_exe, GemPath),
324
+ GemPath
325
+ end
326
+ end.
327
+
328
+
329
+
330
+ uniq_list(List) ->
331
+ lists:foldl(fun(Elem, Acc) ->
332
+ case lists:member(Elem, Acc) of
333
+ true -> Acc;
334
+ false -> Acc ++ [Elem]
335
+ end
336
+ end, [], List).
337
+
338
+