foreman 0.36.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +39 -0
- data/bin/runner +35 -1
- data/data/export/bluepill/master.pill.erb +1 -1
- data/lib/foreman/cli.rb +8 -22
- data/lib/foreman/engine.rb +26 -16
- data/lib/foreman/export/base.rb +11 -4
- data/lib/foreman/export/bluepill.rb +5 -7
- data/lib/foreman/export/inittab.rb +10 -12
- data/lib/foreman/export/runit.rb +8 -10
- data/lib/foreman/export/upstart.rb +8 -10
- data/lib/foreman/export.rb +21 -0
- data/lib/foreman/helpers.rb +45 -0
- data/lib/foreman/process.rb +47 -21
- data/lib/foreman/version.rb +1 -1
- data/lib/foreman.rb +8 -1
- data/spec/foreman/cli_spec.rb +37 -16
- data/spec/foreman/engine_spec.rb +5 -5
- data/spec/foreman/export/base_spec.rb +22 -0
- data/spec/foreman/export/bluepill_spec.rb +21 -9
- data/spec/foreman/export/inittab_spec.rb +40 -0
- data/spec/foreman/export/runit_spec.rb +17 -12
- data/spec/foreman/export/upstart_spec.rb +30 -13
- data/spec/foreman/export_spec.rb +22 -0
- data/spec/foreman/helpers_spec.rb +26 -0
- data/spec/foreman/process_spec.rb +131 -2
- data/spec/foreman_spec.rb +8 -7
- data/spec/helper_spec.rb +3 -3
- data/spec/resources/export/inittab/inittab.concurrency +4 -0
- data/spec/resources/export/inittab/inittab.default +4 -0
- data/spec/spec_helper.rb +17 -4
- metadata +15 -15
- data/README.markdown +0 -50
data/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Foreman
|
|
2
|
+
|
|
3
|
+
Manage Procfile-based applications
|
|
4
|
+
|
|
5
|
+
<table>
|
|
6
|
+
<tr>
|
|
7
|
+
<th>If you have...</th>
|
|
8
|
+
<th>Install with...</th>
|
|
9
|
+
</tr>
|
|
10
|
+
<tr>
|
|
11
|
+
<td>Ruby (MRI, JRuby, Windows)</td>
|
|
12
|
+
<td><pre>$ gem install foreman</pre></td>
|
|
13
|
+
</tr>
|
|
14
|
+
<tr>
|
|
15
|
+
<td>Mac OS X</td>
|
|
16
|
+
<td><a href="http://assets.foreman.io/foreman/foreman.pkg">foreman.pkg</a></td>
|
|
17
|
+
</tr>
|
|
18
|
+
</table>
|
|
19
|
+
|
|
20
|
+
## Getting Started
|
|
21
|
+
|
|
22
|
+
* http://blog.daviddollar.org/2011/05/06/introducing-foreman.html
|
|
23
|
+
|
|
24
|
+
## Documentation
|
|
25
|
+
|
|
26
|
+
* [man page](http://ddollar.github.com/foreman)
|
|
27
|
+
* [wiki](http://github.com/ddollar/foreman/wiki)
|
|
28
|
+
|
|
29
|
+
## Authors
|
|
30
|
+
|
|
31
|
+
#### Created and maintained by
|
|
32
|
+
David Dollar
|
|
33
|
+
|
|
34
|
+
#### Patches contributed by
|
|
35
|
+
Adam Wiggins, Chris Continanza, Chris Lowder, Craig R Webster, Dan Farina, Dan Peterson, David Dollar, Fletcher Nichol, Gabriel Burt, Gamaliel Toro, Greg Reinacker, Hugues Le Gendre, Hunter Nield, Iain Hecker, Jay Zeschin, Keith Rarick, Khaja Minhajuddin, Lincoln Stoll, Marcos Muino Garcia, Mark McGranaghan, Matt Griffin, Matt Haynes, Matthijs Langenberg, Michael Dwan, Michael van Rooijen, Mike Javorski, Nathan Broadbent, Nathan L Smith, Nick Zadrozny, Phil Hagelberg, Ricardo Chimal, Jr, Thom May, Tom Ward, brainopia, clifff, jc00ke
|
|
36
|
+
|
|
37
|
+
## License
|
|
38
|
+
|
|
39
|
+
MIT
|
data/bin/runner
CHANGED
|
@@ -1,2 +1,36 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
|
-
|
|
2
|
+
#
|
|
3
|
+
#/ Usage: runner [-d <dir>] <command>
|
|
4
|
+
#/
|
|
5
|
+
#/ Run a command with exec, optionally changing directory first
|
|
6
|
+
|
|
7
|
+
set -e
|
|
8
|
+
|
|
9
|
+
error() {
|
|
10
|
+
echo $@ >&2
|
|
11
|
+
exit 1
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
usage() {
|
|
15
|
+
cat $0 | grep '^#/' | cut -c4-
|
|
16
|
+
exit
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
while getopts ":hd:" OPT; do
|
|
20
|
+
case $OPT in
|
|
21
|
+
d) cd $OPTARG ;;
|
|
22
|
+
h) usage ;;
|
|
23
|
+
\?) error "invalid option: -$OPTARG" ;;
|
|
24
|
+
:) error "option -$OPTARG requires an argument" ;;
|
|
25
|
+
esac
|
|
26
|
+
done
|
|
27
|
+
|
|
28
|
+
shift $((OPTIND-1))
|
|
29
|
+
|
|
30
|
+
command=$1
|
|
31
|
+
|
|
32
|
+
if [ -z "$1" ]; then
|
|
33
|
+
usage
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
exec $1
|
|
@@ -5,7 +5,7 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
|
|
|
5
5
|
|
|
6
6
|
<% engine.procfile.entries.each do |process| %>
|
|
7
7
|
<% 1.upto(concurrency[process.name]) do |num| %>
|
|
8
|
-
<% port = engine.port_for(process, num,
|
|
8
|
+
<% port = engine.port_for(process, num, self.port) %>
|
|
9
9
|
app.process("<%= process.name %>-<%=num%>") do |process|
|
|
10
10
|
process.start_command = "<%= process.command.gsub("$PORT", port.to_s) %>"
|
|
11
11
|
|
data/lib/foreman/cli.rb
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
require "foreman"
|
|
2
|
+
require "foreman/helpers"
|
|
2
3
|
require "foreman/engine"
|
|
3
4
|
require "foreman/export"
|
|
4
5
|
require "thor"
|
|
5
6
|
require "yaml"
|
|
6
7
|
|
|
7
8
|
class Foreman::CLI < Thor
|
|
9
|
+
include Foreman::Helpers
|
|
10
|
+
|
|
11
|
+
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
|
|
8
12
|
|
|
9
13
|
desc "start", "Start the application"
|
|
10
14
|
|
|
@@ -36,22 +40,12 @@ class Foreman::CLI < Thor
|
|
|
36
40
|
method_option :port, :type => :numeric, :aliases => "-p"
|
|
37
41
|
method_option :user, :type => :string, :aliases => "-u"
|
|
38
42
|
method_option :template, :type => :string, :aliases => "-t"
|
|
39
|
-
method_option :concurrency, :type => :string, :aliases => "-c",
|
|
40
|
-
:banner => '"alpha=5,bar=3"'
|
|
43
|
+
method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
|
|
41
44
|
|
|
42
45
|
def export(format, location=nil)
|
|
43
46
|
check_procfile!
|
|
44
|
-
|
|
45
|
-
formatter
|
|
46
|
-
when "inittab" then Foreman::Export::Inittab
|
|
47
|
-
when "upstart" then Foreman::Export::Upstart
|
|
48
|
-
when "bluepill" then Foreman::Export::Bluepill
|
|
49
|
-
when "runit" then Foreman::Export::Runit
|
|
50
|
-
else error "Unknown export format: #{format}."
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
formatter.new(engine).export(location, options)
|
|
54
|
-
|
|
47
|
+
formatter = Foreman::Export.formatter(format)
|
|
48
|
+
formatter.new(location, engine, options).export
|
|
55
49
|
rescue Foreman::Export::Exception => ex
|
|
56
50
|
error ex.message
|
|
57
51
|
end
|
|
@@ -60,7 +54,7 @@ class Foreman::CLI < Thor
|
|
|
60
54
|
|
|
61
55
|
def check
|
|
62
56
|
error "no processes defined" unless engine.procfile.entries.length > 0
|
|
63
|
-
|
|
57
|
+
puts "valid procfile detected (#{engine.procfile.process_names.join(', ')})"
|
|
64
58
|
end
|
|
65
59
|
|
|
66
60
|
desc "run COMMAND", "Run a command using your application's environment"
|
|
@@ -90,19 +84,11 @@ private ######################################################################
|
|
|
90
84
|
options[:procfile] || "Procfile"
|
|
91
85
|
end
|
|
92
86
|
|
|
93
|
-
def display(message)
|
|
94
|
-
puts message
|
|
95
|
-
end
|
|
96
|
-
|
|
97
87
|
def error(message)
|
|
98
88
|
puts "ERROR: #{message}"
|
|
99
89
|
exit 1
|
|
100
90
|
end
|
|
101
91
|
|
|
102
|
-
def procfile_exists?(procfile)
|
|
103
|
-
File.exist?(procfile)
|
|
104
|
-
end
|
|
105
|
-
|
|
106
92
|
def options
|
|
107
93
|
original_options = super
|
|
108
94
|
return original_options unless File.exists?(".foreman")
|
data/lib/foreman/engine.rb
CHANGED
|
@@ -2,7 +2,6 @@ require "foreman"
|
|
|
2
2
|
require "foreman/process"
|
|
3
3
|
require "foreman/procfile"
|
|
4
4
|
require "foreman/utils"
|
|
5
|
-
require "pty"
|
|
6
5
|
require "tempfile"
|
|
7
6
|
require "timeout"
|
|
8
7
|
require "term/ansicolor"
|
|
@@ -74,14 +73,22 @@ private ######################################################################
|
|
|
74
73
|
def kill_all(signal="SIGTERM")
|
|
75
74
|
running_processes.each do |pid, process|
|
|
76
75
|
info "sending #{signal} to pid #{pid}"
|
|
77
|
-
|
|
76
|
+
process.kill signal
|
|
78
77
|
end
|
|
79
78
|
end
|
|
80
79
|
|
|
81
80
|
def terminate_gracefully
|
|
81
|
+
return if @terminating
|
|
82
|
+
@terminating = true
|
|
82
83
|
info "sending SIGTERM to all processes"
|
|
83
84
|
kill_all "SIGTERM"
|
|
84
|
-
Timeout.timeout(5)
|
|
85
|
+
Timeout.timeout(5) do
|
|
86
|
+
while running_processes.length > 0
|
|
87
|
+
pid, status = Process.wait2
|
|
88
|
+
process = running_processes.delete(pid)
|
|
89
|
+
info "process terminated", process.name
|
|
90
|
+
end
|
|
91
|
+
end
|
|
85
92
|
rescue Timeout::Error
|
|
86
93
|
info "sending SIGKILL to all processes"
|
|
87
94
|
kill_all "SIGKILL"
|
|
@@ -89,11 +96,14 @@ private ######################################################################
|
|
|
89
96
|
|
|
90
97
|
def watch_for_output
|
|
91
98
|
Thread.new do
|
|
99
|
+
require "win32console" if Foreman.windows?
|
|
92
100
|
begin
|
|
93
101
|
loop do
|
|
94
102
|
rs, ws = IO.select(readers.values, [], [], 1)
|
|
95
103
|
(rs || []).each do |r|
|
|
96
|
-
|
|
104
|
+
data = r.gets
|
|
105
|
+
next unless data
|
|
106
|
+
ps, message = data.split(",", 2)
|
|
97
107
|
color = colors[ps.split(".").first]
|
|
98
108
|
info message, ps, color
|
|
99
109
|
end
|
|
@@ -110,16 +120,16 @@ private ######################################################################
|
|
|
110
120
|
process = running_processes.delete(pid)
|
|
111
121
|
info "process terminated", process.name
|
|
112
122
|
terminate_gracefully
|
|
113
|
-
kill_all
|
|
114
123
|
rescue Errno::ECHILD
|
|
115
124
|
end
|
|
116
125
|
|
|
117
126
|
def info(message, name="system", color=Term::ANSIColor.white)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
127
|
+
output = ""
|
|
128
|
+
output += color
|
|
129
|
+
output += "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
|
|
130
|
+
output += Term::ANSIColor.reset
|
|
131
|
+
output += message.chomp
|
|
132
|
+
puts output
|
|
123
133
|
end
|
|
124
134
|
|
|
125
135
|
def print(message=nil)
|
|
@@ -134,11 +144,6 @@ private ######################################################################
|
|
|
134
144
|
end
|
|
135
145
|
end
|
|
136
146
|
|
|
137
|
-
def error(message)
|
|
138
|
-
puts "ERROR: #{message}"
|
|
139
|
-
exit 1
|
|
140
|
-
end
|
|
141
|
-
|
|
142
147
|
def longest_process_name
|
|
143
148
|
@longest_process_name ||= begin
|
|
144
149
|
longest = procfile.process_names.map { |name| name.length }.sort.last
|
|
@@ -156,7 +161,7 @@ private ######################################################################
|
|
|
156
161
|
end
|
|
157
162
|
|
|
158
163
|
def termtitle(title)
|
|
159
|
-
printf("\033]0;#{title}\007")
|
|
164
|
+
printf("\033]0;#{title}\007") unless Foreman.windows?
|
|
160
165
|
end
|
|
161
166
|
|
|
162
167
|
def running_processes
|
|
@@ -217,6 +222,11 @@ private ######################################################################
|
|
|
217
222
|
def apply_environment!
|
|
218
223
|
@environment.each { |k,v| ENV[k] = v }
|
|
219
224
|
end
|
|
225
|
+
|
|
226
|
+
def error(message)
|
|
227
|
+
puts "ERROR: #{message}"
|
|
228
|
+
exit 1
|
|
229
|
+
end
|
|
220
230
|
end
|
|
221
231
|
|
|
222
232
|
include Env
|
data/lib/foreman/export/base.rb
CHANGED
|
@@ -3,10 +3,17 @@ require "foreman/utils"
|
|
|
3
3
|
|
|
4
4
|
class Foreman::Export::Base
|
|
5
5
|
|
|
6
|
-
attr_reader :engine
|
|
7
|
-
|
|
8
|
-
def initialize(engine)
|
|
9
|
-
@
|
|
6
|
+
attr_reader :location, :engine, :app, :log, :port, :user, :template, :concurrency
|
|
7
|
+
|
|
8
|
+
def initialize(location, engine, options={})
|
|
9
|
+
@location = location
|
|
10
|
+
@engine = engine
|
|
11
|
+
@app = options[:app]
|
|
12
|
+
@log = options[:log]
|
|
13
|
+
@port = options[:port]
|
|
14
|
+
@user = options[:user]
|
|
15
|
+
@template = options[:template]
|
|
16
|
+
@concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
|
10
17
|
end
|
|
11
18
|
|
|
12
19
|
def export
|
|
@@ -3,23 +3,21 @@ require "foreman/export"
|
|
|
3
3
|
|
|
4
4
|
class Foreman::Export::Bluepill < Foreman::Export::Base
|
|
5
5
|
|
|
6
|
-
def export
|
|
6
|
+
def export
|
|
7
7
|
error("Must specify a location") unless location
|
|
8
8
|
|
|
9
9
|
FileUtils.mkdir_p location
|
|
10
10
|
|
|
11
|
-
app =
|
|
12
|
-
user =
|
|
13
|
-
log_root =
|
|
14
|
-
template_root =
|
|
11
|
+
app = self.app || File.basename(engine.directory)
|
|
12
|
+
user = self.user || app
|
|
13
|
+
log_root = self.log || "/var/log/#{app}"
|
|
14
|
+
template_root = self.template
|
|
15
15
|
|
|
16
16
|
Dir["#{location}/#{app}.pill"].each do |file|
|
|
17
17
|
say "cleaning up: #{file}"
|
|
18
18
|
FileUtils.rm(file)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
|
22
|
-
|
|
23
21
|
master_template = export_template("bluepill", "master.pill.erb", template_root)
|
|
24
22
|
master_config = ERB.new(master_template).result(binding)
|
|
25
23
|
write_file "#{location}/#{app}.pill", master_config
|
|
@@ -2,20 +2,18 @@ require "foreman/export"
|
|
|
2
2
|
|
|
3
3
|
class Foreman::Export::Inittab < Foreman::Export::Base
|
|
4
4
|
|
|
5
|
-
def export
|
|
6
|
-
app =
|
|
7
|
-
user =
|
|
8
|
-
log_root =
|
|
9
|
-
|
|
10
|
-
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
|
5
|
+
def export
|
|
6
|
+
app = self.app || File.basename(engine.directory)
|
|
7
|
+
user = self.user || app
|
|
8
|
+
log_root = self.log || "/var/log/#{app}"
|
|
11
9
|
|
|
12
10
|
inittab = []
|
|
13
11
|
inittab << "# ----- foreman #{app} processes -----"
|
|
14
12
|
|
|
15
13
|
engine.procfile.entries.inject(1) do |index, process|
|
|
16
|
-
1.upto(concurrency[process.name]) do |num|
|
|
14
|
+
1.upto(self.concurrency[process.name]) do |num|
|
|
17
15
|
id = app.slice(0, 2).upcase + sprintf("%02d", index)
|
|
18
|
-
port = engine.port_for(process, num,
|
|
16
|
+
port = engine.port_for(process, num, self.port)
|
|
19
17
|
inittab << "#{id}:4:respawn:/bin/su - #{user} -c 'PORT=#{port} #{process.command} >> #{log_root}/#{process.name}-#{num}.log 2>&1'"
|
|
20
18
|
index += 1
|
|
21
19
|
end
|
|
@@ -26,12 +24,12 @@ class Foreman::Export::Inittab < Foreman::Export::Base
|
|
|
26
24
|
|
|
27
25
|
inittab = inittab.join("\n") + "\n"
|
|
28
26
|
|
|
29
|
-
if
|
|
27
|
+
if location == "-"
|
|
28
|
+
puts inittab
|
|
29
|
+
else
|
|
30
30
|
FileUtils.mkdir_p(log_root) rescue error "could not create #{log_root}"
|
|
31
31
|
FileUtils.chown(user, nil, log_root) rescue error "could not chown #{log_root} to #{user}"
|
|
32
|
-
write_file(
|
|
33
|
-
else
|
|
34
|
-
puts inittab
|
|
32
|
+
write_file(location, inittab)
|
|
35
33
|
end
|
|
36
34
|
end
|
|
37
35
|
|
data/lib/foreman/export/runit.rb
CHANGED
|
@@ -4,21 +4,19 @@ require "foreman/export"
|
|
|
4
4
|
class Foreman::Export::Runit < Foreman::Export::Base
|
|
5
5
|
ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
|
|
6
6
|
|
|
7
|
-
def export
|
|
7
|
+
def export
|
|
8
8
|
error("Must specify a location") unless location
|
|
9
9
|
|
|
10
|
-
app =
|
|
11
|
-
user =
|
|
12
|
-
log_root =
|
|
13
|
-
template_root =
|
|
14
|
-
|
|
15
|
-
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
|
10
|
+
app = self.app || File.basename(engine.directory)
|
|
11
|
+
user = self.user || app
|
|
12
|
+
log_root = self.log || "/var/log/#{app}"
|
|
13
|
+
template_root = self.template
|
|
16
14
|
|
|
17
15
|
run_template = export_template('runit', 'run.erb', template_root)
|
|
18
16
|
log_run_template = export_template('runit', 'log_run.erb', template_root)
|
|
19
17
|
|
|
20
18
|
engine.procfile.entries.each do |process|
|
|
21
|
-
1.upto(concurrency[process.name]) do |num|
|
|
19
|
+
1.upto(self.concurrency[process.name]) do |num|
|
|
22
20
|
process_directory = "#{location}/#{app}-#{process.name}-#{num}"
|
|
23
21
|
process_env_directory = "#{process_directory}/env"
|
|
24
22
|
process_log_directory = "#{process_directory}/log"
|
|
@@ -31,7 +29,7 @@ class Foreman::Export::Runit < Foreman::Export::Base
|
|
|
31
29
|
write_file "#{process_directory}/run", run
|
|
32
30
|
FileUtils.chmod 0755, "#{process_directory}/run"
|
|
33
31
|
|
|
34
|
-
port = engine.port_for(process, num,
|
|
32
|
+
port = engine.port_for(process, num, self.port)
|
|
35
33
|
environment_variables = {'PORT' => port}.
|
|
36
34
|
merge(engine.environment).
|
|
37
35
|
merge(inline_variables(process.command))
|
|
@@ -51,7 +49,7 @@ class Foreman::Export::Runit < Foreman::Export::Base
|
|
|
51
49
|
private
|
|
52
50
|
def create_directory(location)
|
|
53
51
|
say "creating: #{location}"
|
|
54
|
-
FileUtils.
|
|
52
|
+
FileUtils.mkdir_p(location)
|
|
55
53
|
end
|
|
56
54
|
|
|
57
55
|
def inline_variables(command)
|
|
@@ -3,23 +3,21 @@ require "foreman/export"
|
|
|
3
3
|
|
|
4
4
|
class Foreman::Export::Upstart < Foreman::Export::Base
|
|
5
5
|
|
|
6
|
-
def export
|
|
6
|
+
def export
|
|
7
7
|
error("Must specify a location") unless location
|
|
8
8
|
|
|
9
9
|
FileUtils.mkdir_p location
|
|
10
10
|
|
|
11
|
-
app =
|
|
12
|
-
user =
|
|
13
|
-
log_root =
|
|
14
|
-
template_root =
|
|
11
|
+
app = self.app || File.basename(engine.directory)
|
|
12
|
+
user = self.user || app
|
|
13
|
+
log_root = self.log || "/var/log/#{app}"
|
|
14
|
+
template_root = self.template
|
|
15
15
|
|
|
16
16
|
Dir["#{location}/#{app}*.conf"].each do |file|
|
|
17
17
|
say "cleaning up: #{file}"
|
|
18
18
|
FileUtils.rm(file)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
|
22
|
-
|
|
23
21
|
master_template = export_template("upstart", "master.conf.erb", template_root)
|
|
24
22
|
master_config = ERB.new(master_template).result(binding)
|
|
25
23
|
write_file "#{location}/#{app}.conf", master_config
|
|
@@ -27,13 +25,13 @@ class Foreman::Export::Upstart < Foreman::Export::Base
|
|
|
27
25
|
process_template = export_template("upstart", "process.conf.erb", template_root)
|
|
28
26
|
|
|
29
27
|
engine.procfile.entries.each do |process|
|
|
30
|
-
next if (conc = concurrency[process.name]) < 1
|
|
28
|
+
next if (conc = self.concurrency[process.name]) < 1
|
|
31
29
|
process_master_template = export_template("upstart", "process_master.conf.erb", template_root)
|
|
32
30
|
process_master_config = ERB.new(process_master_template).result(binding)
|
|
33
31
|
write_file "#{location}/#{app}-#{process.name}.conf", process_master_config
|
|
34
32
|
|
|
35
|
-
1.upto(concurrency[process.name]) do |num|
|
|
36
|
-
port = engine.port_for(process, num,
|
|
33
|
+
1.upto(self.concurrency[process.name]) do |num|
|
|
34
|
+
port = engine.port_for(process, num, self.port)
|
|
37
35
|
process_config = ERB.new(process_template).result(binding)
|
|
38
36
|
write_file "#{location}/#{app}-#{process.name}-#{num}.conf", process_config
|
|
39
37
|
end
|
data/lib/foreman/export.rb
CHANGED
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
require "foreman"
|
|
2
|
+
require "foreman/helpers"
|
|
2
3
|
|
|
3
4
|
module Foreman::Export
|
|
5
|
+
extend Foreman::Helpers
|
|
6
|
+
|
|
4
7
|
class Exception < ::Exception; end
|
|
8
|
+
|
|
9
|
+
def self.formatter(format)
|
|
10
|
+
begin
|
|
11
|
+
require "foreman/export/#{ format.tr('-', '_') }"
|
|
12
|
+
classy_format = classify(format)
|
|
13
|
+
formatter = constantize("Foreman::Export::#{ classy_format }")
|
|
14
|
+
rescue NameError => ex
|
|
15
|
+
error "Unknown export format: #{format} (no class Foreman::Export::#{ classy_format })."
|
|
16
|
+
rescue LoadError => ex
|
|
17
|
+
error "Unknown export format: #{format} (unable to load file 'foreman/export/#{ format.tr('-', '_') }')."
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.error(message)
|
|
22
|
+
raise Foreman::Export::Exception.new(message)
|
|
23
|
+
end
|
|
24
|
+
|
|
5
25
|
end
|
|
6
26
|
|
|
27
|
+
|
|
7
28
|
require "foreman/export/base"
|
|
8
29
|
require "foreman/export/inittab"
|
|
9
30
|
require "foreman/export/upstart"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Foreman::Helpers
|
|
2
|
+
# Copied whole sale from, https://github.com/defunkt/resque/
|
|
3
|
+
|
|
4
|
+
# Given a word with dashes, returns a camel cased version of it.
|
|
5
|
+
#
|
|
6
|
+
# classify('job-name') # => 'JobName'
|
|
7
|
+
def classify(dashed_word)
|
|
8
|
+
dashed_word.split('-').each { |part| part[0] = part[0].chr.upcase }.join
|
|
9
|
+
end # Tries to find a constant with the name specified in the argument string:
|
|
10
|
+
|
|
11
|
+
#
|
|
12
|
+
# constantize("Module") # => Module
|
|
13
|
+
# constantize("Test::Unit") # => Test::Unit
|
|
14
|
+
#
|
|
15
|
+
# The name is assumed to be the one of a top-level constant, no matter
|
|
16
|
+
# whether it starts with "::" or not. No lexical context is taken into
|
|
17
|
+
# account:
|
|
18
|
+
#
|
|
19
|
+
# C = 'outside'
|
|
20
|
+
# module M
|
|
21
|
+
# C = 'inside'
|
|
22
|
+
# C # => 'inside'
|
|
23
|
+
# constantize("C") # => 'outside', same as ::C
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# NameError is raised when the constant is unknown.
|
|
27
|
+
def constantize(camel_cased_word)
|
|
28
|
+
camel_cased_word = camel_cased_word.to_s
|
|
29
|
+
|
|
30
|
+
names = camel_cased_word.split('::')
|
|
31
|
+
names.shift if names.empty? || names.first.empty?
|
|
32
|
+
|
|
33
|
+
constant = Object
|
|
34
|
+
names.each do |name|
|
|
35
|
+
args = Module.method(:const_get).arity != 1 ? [false] : []
|
|
36
|
+
|
|
37
|
+
if constant.const_defined?(name, *args)
|
|
38
|
+
constant = constant.const_get(name)
|
|
39
|
+
else
|
|
40
|
+
constant = constant.const_missing(name)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
constant
|
|
44
|
+
end
|
|
45
|
+
end
|
data/lib/foreman/process.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "foreman"
|
|
2
|
+
require "rubygems"
|
|
2
3
|
|
|
3
4
|
class Foreman::Process
|
|
4
5
|
|
|
@@ -14,10 +15,8 @@ class Foreman::Process
|
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def run(pipe, basedir, environment)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
run_process entry.command, pipe
|
|
20
|
-
end
|
|
18
|
+
with_environment(environment.merge("PORT" => port.to_s)) do
|
|
19
|
+
run_process basedir, entry.command, pipe
|
|
21
20
|
end
|
|
22
21
|
end
|
|
23
22
|
|
|
@@ -25,23 +24,52 @@ class Foreman::Process
|
|
|
25
24
|
"%s.%s" % [ entry.name, num ]
|
|
26
25
|
end
|
|
27
26
|
|
|
27
|
+
def kill(signal)
|
|
28
|
+
pid && Process.kill(signal, pid)
|
|
29
|
+
rescue Errno::ESRCH
|
|
30
|
+
false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def detach
|
|
34
|
+
pid && Process.detach(pid)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def alive?
|
|
38
|
+
kill(0)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def dead?
|
|
42
|
+
!alive?
|
|
43
|
+
end
|
|
44
|
+
|
|
28
45
|
private
|
|
29
46
|
|
|
30
|
-
def fork_with_io(command)
|
|
47
|
+
def fork_with_io(command, basedir)
|
|
31
48
|
reader, writer = IO.pipe
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
command = replace_command_env(command)
|
|
50
|
+
pid = if Foreman.windows?
|
|
51
|
+
Dir.chdir(basedir) do
|
|
52
|
+
Process.spawn command, :out => writer, :err => writer
|
|
53
|
+
end
|
|
54
|
+
elsif Foreman.jruby?
|
|
55
|
+
require "posix/spawn"
|
|
56
|
+
POSIX::Spawn.spawn(Foreman.runner, "-d", basedir, command, {
|
|
57
|
+
:out => writer, :err => writer
|
|
58
|
+
})
|
|
59
|
+
else
|
|
60
|
+
fork do
|
|
61
|
+
writer.sync = true
|
|
62
|
+
$stdout.reopen writer
|
|
63
|
+
$stderr.reopen writer
|
|
64
|
+
reader.close
|
|
65
|
+
exec Foreman.runner, "-d", basedir, command
|
|
66
|
+
end
|
|
39
67
|
end
|
|
40
68
|
[ reader, pid ]
|
|
41
69
|
end
|
|
42
70
|
|
|
43
|
-
def run_process(command, pipe)
|
|
44
|
-
io, @pid = fork_with_io(command)
|
|
71
|
+
def run_process(basedir, command, pipe)
|
|
72
|
+
io, @pid = fork_with_io(command, basedir)
|
|
45
73
|
output pipe, "started with pid %d" % @pid
|
|
46
74
|
Thread.new do
|
|
47
75
|
until io.eof?
|
|
@@ -59,12 +87,10 @@ private
|
|
|
59
87
|
end
|
|
60
88
|
|
|
61
89
|
def with_environment(environment)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
ret
|
|
90
|
+
original = ENV.to_hash
|
|
91
|
+
ENV.update environment
|
|
92
|
+
yield
|
|
93
|
+
ensure
|
|
94
|
+
ENV.replace original
|
|
68
95
|
end
|
|
69
|
-
|
|
70
96
|
end
|
data/lib/foreman/version.rb
CHANGED
data/lib/foreman.rb
CHANGED
|
@@ -14,5 +14,12 @@ module Foreman
|
|
|
14
14
|
File.expand_path("../../bin/runner", __FILE__)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
def self.jruby?
|
|
18
|
+
defined?(RUBY_PLATFORM) and RUBY_PLATFORM == "java"
|
|
19
|
+
end
|
|
18
20
|
|
|
21
|
+
def self.windows?
|
|
22
|
+
defined?(RUBY_PLATFORM) and RUBY_PLATFORM =~ /(win|w)32$/
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|