foreman 0.37.0.pre3-mingw32 → 0.37.0.pre4-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +25 -36
- data/data/export/bluepill/master.pill.erb +1 -1
- data/lib/foreman/cli.rb +13 -8
- data/lib/foreman/engine.rb +9 -7
- data/lib/foreman/export/base.rb +11 -4
- data/lib/foreman/export/bluepill.rb +5 -7
- data/lib/foreman/export/inittab.rb +6 -8
- data/lib/foreman/export/runit.rb +8 -10
- data/lib/foreman/export/upstart.rb +8 -10
- data/lib/foreman/helpers.rb +50 -0
- data/lib/foreman/process.rb +23 -7
- data/lib/foreman/version.rb +1 -1
- data/spec/foreman/cli_spec.rb +17 -13
- data/spec/foreman/engine_spec.rb +1 -1
- data/spec/foreman/export/bluepill_spec.rb +14 -9
- data/spec/foreman/export/runit_spec.rb +17 -12
- data/spec/foreman/export/upstart_spec.rb +19 -13
- data/spec/foreman/process_spec.rb +131 -2
- data/spec/foreman_spec.rb +2 -7
- data/spec/helper_spec.rb +3 -3
- data/spec/spec_helper.rb +3 -2
- metadata +10 -9
data/README.markdown
CHANGED
@@ -1,49 +1,38 @@
|
|
1
1
|
# Foreman
|
2
2
|
|
3
|
-
|
3
|
+
Manage Procfile-based applications
|
4
4
|
|
5
|
-
|
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>
|
6
19
|
|
7
|
-
|
20
|
+
## Getting Started
|
8
21
|
|
9
|
-
*
|
22
|
+
* http://blog.daviddollar.org/2011/05/06/introducing-foreman.html
|
10
23
|
|
11
|
-
|
12
|
-
|
13
|
-
* Standalone Tarball
|
14
|
-
|
15
|
-
http://assets.foreman.io/foreman/foreman.tgz
|
16
|
-
|
17
|
-
## Description
|
18
|
-
|
19
|
-
http://blog.daviddollar.org/2011/05/06/introducing-foreman.html
|
20
|
-
|
21
|
-
## Manual
|
24
|
+
## Documentation
|
22
25
|
|
23
26
|
* [man page](http://ddollar.github.com/foreman)
|
24
27
|
* [wiki](http://github.com/ddollar/foreman/wiki)
|
25
28
|
|
26
|
-
##
|
27
|
-
|
28
|
-
Created
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
* Dan Peterson
|
34
|
-
* Hunter Nield
|
35
|
-
* Jay Zeschin
|
36
|
-
* Keith Rarick
|
37
|
-
* Khaja Minhajuddin
|
38
|
-
* Matt Haynes
|
39
|
-
* Michael van Rooijen
|
40
|
-
* Mike Javorski
|
41
|
-
* Nathan L Smith
|
42
|
-
* Nick Zadrozny
|
43
|
-
* Ricardo Chimal, Jr
|
44
|
-
* Thom May
|
45
|
-
* clifff
|
46
|
-
* Greg Reinacker
|
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
|
47
36
|
|
48
37
|
## License
|
49
38
|
|
@@ -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
|
|
@@ -42,16 +46,17 @@ class Foreman::CLI < Thor
|
|
42
46
|
def export(format, location=nil)
|
43
47
|
check_procfile!
|
44
48
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
49
|
+
begin
|
50
|
+
require "foreman/export/#{ format.tr('-', '_') }"
|
51
|
+
classy_format = classify(format)
|
52
|
+
formatter = constantize("Foreman::Export::#{ classy_format }")
|
53
|
+
rescue NameError => ex
|
54
|
+
error "Unknown export format: #{format} (no class Foreman::Export::#{ classy_format })."
|
55
|
+
rescue LoadError => ex
|
56
|
+
error "Unknown export format: #{format} (unable to load file 'foreman/export/#{ format.tr('-', '_') }')."
|
51
57
|
end
|
52
58
|
|
53
|
-
formatter.new(
|
54
|
-
|
59
|
+
formatter.new(location, engine, options).export
|
55
60
|
rescue Foreman::Export::Exception => ex
|
56
61
|
error ex.message
|
57
62
|
end
|
data/lib/foreman/engine.rb
CHANGED
@@ -73,7 +73,7 @@ private ######################################################################
|
|
73
73
|
def kill_all(signal="SIGTERM")
|
74
74
|
running_processes.each do |pid, process|
|
75
75
|
info "sending #{signal} to pid #{pid}"
|
76
|
-
|
76
|
+
process.kill signal
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
@@ -93,7 +93,9 @@ private ######################################################################
|
|
93
93
|
loop do
|
94
94
|
rs, ws = IO.select(readers.values, [], [], 1)
|
95
95
|
(rs || []).each do |r|
|
96
|
-
|
96
|
+
data = r.gets
|
97
|
+
next unless data
|
98
|
+
ps, message = data.split(",", 2)
|
97
99
|
color = colors[ps.split(".").first]
|
98
100
|
info message, ps, color
|
99
101
|
end
|
@@ -134,11 +136,6 @@ private ######################################################################
|
|
134
136
|
end
|
135
137
|
end
|
136
138
|
|
137
|
-
def error(message)
|
138
|
-
puts "ERROR: #{message}"
|
139
|
-
exit 1
|
140
|
-
end
|
141
|
-
|
142
139
|
def longest_process_name
|
143
140
|
@longest_process_name ||= begin
|
144
141
|
longest = procfile.process_names.map { |name| name.length }.sort.last
|
@@ -217,6 +214,11 @@ private ######################################################################
|
|
217
214
|
def apply_environment!
|
218
215
|
@environment.each { |k,v| ENV[k] = v }
|
219
216
|
end
|
217
|
+
|
218
|
+
def error(message)
|
219
|
+
puts "ERROR: #{message}"
|
220
|
+
exit 1
|
221
|
+
end
|
220
222
|
end
|
221
223
|
|
222
224
|
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
|
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
|
@@ -0,0 +1,50 @@
|
|
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
|
10
|
+
|
11
|
+
# Tries to find a constant with the name specified in the argument string:
|
12
|
+
#
|
13
|
+
# constantize("Module") # => Module
|
14
|
+
# constantize("Test::Unit") # => Test::Unit
|
15
|
+
#
|
16
|
+
# The name is assumed to be the one of a top-level constant, no matter
|
17
|
+
# whether it starts with "::" or not. No lexical context is taken into
|
18
|
+
# account:
|
19
|
+
#
|
20
|
+
# C = 'outside'
|
21
|
+
# module M
|
22
|
+
# C = 'inside'
|
23
|
+
# C # => 'inside'
|
24
|
+
# constantize("C") # => 'outside', same as ::C
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# NameError is raised when the constant is unknown.
|
28
|
+
def constantize(camel_cased_word)
|
29
|
+
camel_cased_word = camel_cased_word.to_s
|
30
|
+
|
31
|
+
if camel_cased_word.include?('-')
|
32
|
+
camel_cased_word = classify(camel_cased_word)
|
33
|
+
end
|
34
|
+
|
35
|
+
names = camel_cased_word.split('::')
|
36
|
+
names.shift if names.empty? || names.first.empty?
|
37
|
+
|
38
|
+
constant = Object
|
39
|
+
names.each do |name|
|
40
|
+
args = Module.method(:const_get).arity != 1 ? [false] : []
|
41
|
+
|
42
|
+
if constant.const_defined?(name, *args)
|
43
|
+
constant = constant.const_get(name)
|
44
|
+
else
|
45
|
+
constant = constant.const_missing(name)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
constant
|
49
|
+
end
|
50
|
+
end
|
data/lib/foreman/process.rb
CHANGED
@@ -24,6 +24,24 @@ class Foreman::Process
|
|
24
24
|
"%s.%s" % [ entry.name, num ]
|
25
25
|
end
|
26
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
|
+
|
27
45
|
private
|
28
46
|
|
29
47
|
def fork_with_io(command, basedir)
|
@@ -69,12 +87,10 @@ private
|
|
69
87
|
end
|
70
88
|
|
71
89
|
def with_environment(environment)
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
ret
|
90
|
+
original = ENV.to_hash
|
91
|
+
ENV.update environment
|
92
|
+
yield
|
93
|
+
ensure
|
94
|
+
ENV.replace original
|
78
95
|
end
|
79
|
-
|
80
96
|
end
|
data/lib/foreman/version.rb
CHANGED
data/spec/foreman/cli_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
require "foreman/cli"
|
3
3
|
|
4
|
-
describe "Foreman::CLI" do
|
4
|
+
describe "Foreman::CLI", :fakefs do
|
5
5
|
subject { Foreman::CLI.new }
|
6
6
|
|
7
7
|
describe "start" do
|
@@ -30,7 +30,9 @@ describe "Foreman::CLI" do
|
|
30
30
|
it "respects --env" do
|
31
31
|
write_procfile
|
32
32
|
write_env("envfile")
|
33
|
-
mock
|
33
|
+
mock_export = mock(Foreman::Export::Upstart)
|
34
|
+
mock(Foreman::Export::Upstart).new("/upstart", is_a(Foreman::Engine), { "env" => "envfile" }) { mock_export }
|
35
|
+
mock_export.export
|
34
36
|
foreman %{ export upstart /upstart --env envfile }
|
35
37
|
end
|
36
38
|
end
|
@@ -49,7 +51,7 @@ describe "Foreman::CLI" do
|
|
49
51
|
|
50
52
|
describe "with an invalid formatter" do
|
51
53
|
it "prints an error" do
|
52
|
-
mock_error(subject, "Unknown export format: invalidformatter.") do
|
54
|
+
mock_error(subject, "Unknown export format: invalidformatter (unable to load file 'foreman/export/invalidformatter').") do
|
53
55
|
subject.export("invalidformatter")
|
54
56
|
end
|
55
57
|
end
|
@@ -60,7 +62,9 @@ describe "Foreman::CLI" do
|
|
60
62
|
|
61
63
|
it "runs successfully" do
|
62
64
|
dont_allow(subject).error
|
63
|
-
mock
|
65
|
+
mock_export = mock(Foreman::Export::Upstart)
|
66
|
+
mock(Foreman::Export::Upstart).new("/tmp/foo", is_a(Foreman::Engine), {}) { mock_export }
|
67
|
+
mock_export.export
|
64
68
|
subject.export("upstart", "/tmp/foo")
|
65
69
|
end
|
66
70
|
end
|
@@ -89,47 +93,47 @@ describe "Foreman::CLI" do
|
|
89
93
|
end
|
90
94
|
end
|
91
95
|
end
|
92
|
-
|
96
|
+
|
93
97
|
describe "run" do
|
94
98
|
describe "with a valid Procfile" do
|
95
99
|
before { write_procfile }
|
96
100
|
|
97
101
|
describe "and a command" do
|
98
102
|
let(:command) { ["ls", "-l"] }
|
99
|
-
|
103
|
+
|
100
104
|
before(:each) do
|
101
105
|
stub(subject).exec
|
102
106
|
end
|
103
|
-
|
107
|
+
|
104
108
|
it "should load the environment file" do
|
105
109
|
write_env
|
106
110
|
preserving_env do
|
107
111
|
subject.run *command
|
108
112
|
ENV["FOO"].should == "bar"
|
109
113
|
end
|
110
|
-
|
114
|
+
|
111
115
|
ENV["FOO"].should be_nil
|
112
116
|
end
|
113
|
-
|
117
|
+
|
114
118
|
it "should runute the command as a string" do
|
115
119
|
mock(subject).exec(command.join(" "))
|
116
120
|
subject.run *command
|
117
121
|
end
|
118
122
|
end
|
119
|
-
|
123
|
+
|
120
124
|
describe "and a non-existent command" do
|
121
125
|
let(:command) { "iuhtngrglhulhdfg" }
|
122
|
-
|
126
|
+
|
123
127
|
it "should print an error" do
|
124
128
|
mock_error(subject, "command not found: #{command}") do
|
125
129
|
subject.run command
|
126
130
|
end
|
127
131
|
end
|
128
132
|
end
|
129
|
-
|
133
|
+
|
130
134
|
describe "and a non-executable command" do
|
131
135
|
let(:command) { __FILE__ }
|
132
|
-
|
136
|
+
|
133
137
|
it "should print an error" do
|
134
138
|
mock_error(subject, "not executable: #{command}") do
|
135
139
|
subject.run command
|
data/spec/foreman/engine_spec.rb
CHANGED
@@ -3,22 +3,27 @@ require "foreman/engine"
|
|
3
3
|
require "foreman/export/bluepill"
|
4
4
|
require "tmpdir"
|
5
5
|
|
6
|
-
describe Foreman::Export::Bluepill do
|
6
|
+
describe Foreman::Export::Bluepill, :fakefs do
|
7
7
|
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
|
8
|
-
let(:engine)
|
9
|
-
let(:
|
8
|
+
let(:engine) { Foreman::Engine.new(procfile) }
|
9
|
+
let(:options) { Hash.new }
|
10
|
+
let(:bluepill) { Foreman::Export::Bluepill.new("/tmp/init", engine, options) }
|
10
11
|
|
11
12
|
before(:each) { load_export_templates_into_fakefs("bluepill") }
|
12
13
|
before(:each) { stub(bluepill).say }
|
13
14
|
|
14
15
|
it "exports to the filesystem" do
|
15
|
-
bluepill.export
|
16
|
+
bluepill.export
|
16
17
|
normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app.pill"))
|
17
18
|
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
context "with concurrency" do
|
21
|
+
let(:options) { Hash[:concurrency => "alpha=2"] }
|
22
|
+
|
23
|
+
it "exports to the filesystem with concurrency" do
|
24
|
+
bluepill.export
|
25
|
+
normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app-concurrency.pill"))
|
26
|
+
end
|
23
27
|
end
|
24
|
-
|
28
|
+
|
29
|
+
end
|
@@ -3,34 +3,39 @@ require "foreman/engine"
|
|
3
3
|
require "foreman/export/runit"
|
4
4
|
require "tmpdir"
|
5
5
|
|
6
|
-
describe Foreman::Export::Runit do
|
6
|
+
describe Foreman::Export::Runit, :fakefs do
|
7
7
|
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile", 'bar=baz') }
|
8
8
|
let(:engine) { Foreman::Engine.new(procfile) }
|
9
|
-
let(:runit) { Foreman::Export::Runit.new(engine) }
|
10
|
-
|
9
|
+
let(:runit) { Foreman::Export::Runit.new('/tmp/init', engine, :concurrency => 'alpha=2,bravo=1') }
|
10
|
+
|
11
11
|
before(:each) { load_export_templates_into_fakefs("runit") }
|
12
12
|
before(:each) { stub(runit).say }
|
13
13
|
before(:each) { stub(FakeFS::FileUtils).chmod }
|
14
|
-
|
14
|
+
|
15
15
|
it "exports to the filesystem" do
|
16
16
|
FileUtils.mkdir_p('/tmp/init')
|
17
|
-
|
18
|
-
|
17
|
+
|
18
|
+
runit.export
|
19
|
+
|
19
20
|
File.read("/tmp/init/app-alpha-1/run").should == example_export_file('runit/app-alpha-1-run')
|
20
|
-
File.read("/tmp/init/app-alpha-1/log/run").should ==
|
21
|
+
File.read("/tmp/init/app-alpha-1/log/run").should ==
|
21
22
|
example_export_file('runit/app-alpha-1-log-run')
|
22
23
|
File.read("/tmp/init/app-alpha-1/env/PORT").should == "5000\n"
|
23
24
|
File.read("/tmp/init/app-alpha-1/env/BAR").should == "baz\n"
|
24
|
-
|
25
|
+
|
25
26
|
File.read("/tmp/init/app-alpha-2/run").should == example_export_file('runit/app-alpha-2-run')
|
26
|
-
File.read("/tmp/init/app-alpha-2/log/run").should ==
|
27
|
+
File.read("/tmp/init/app-alpha-2/log/run").should ==
|
27
28
|
example_export_file('runit/app-alpha-2-log-run')
|
28
29
|
File.read("/tmp/init/app-alpha-2/env/PORT").should == "5001\n"
|
29
30
|
File.read("/tmp/init/app-alpha-2/env/BAR").should == "baz\n"
|
30
|
-
|
31
|
+
|
31
32
|
File.read("/tmp/init/app-bravo-1/run").should == example_export_file('runit/app-bravo-1-run')
|
32
|
-
File.read("/tmp/init/app-bravo-1/log/run").should ==
|
33
|
+
File.read("/tmp/init/app-bravo-1/log/run").should ==
|
33
34
|
example_export_file('runit/app-bravo-1-log-run')
|
34
35
|
File.read("/tmp/init/app-bravo-1/env/PORT").should == "5100\n"
|
35
36
|
end
|
36
|
-
|
37
|
+
|
38
|
+
it "creates a full path to the export directory" do
|
39
|
+
expect { runit.export }.to_not raise_error(Errno::ENOENT)
|
40
|
+
end
|
41
|
+
end
|
@@ -3,16 +3,17 @@ require "foreman/engine"
|
|
3
3
|
require "foreman/export/upstart"
|
4
4
|
require "tmpdir"
|
5
5
|
|
6
|
-
describe Foreman::Export::Upstart do
|
6
|
+
describe Foreman::Export::Upstart, :fakefs do
|
7
7
|
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
|
8
|
-
let(:engine)
|
9
|
-
let(:
|
8
|
+
let(:engine) { Foreman::Engine.new(procfile) }
|
9
|
+
let(:options) { Hash.new }
|
10
|
+
let(:upstart) { Foreman::Export::Upstart.new("/tmp/init", engine, options) }
|
10
11
|
|
11
12
|
before(:each) { load_export_templates_into_fakefs("upstart") }
|
12
13
|
before(:each) { stub(upstart).say }
|
13
14
|
|
14
15
|
it "exports to the filesystem" do
|
15
|
-
upstart.export
|
16
|
+
upstart.export
|
16
17
|
|
17
18
|
File.read("/tmp/init/app.conf").should == example_export_file("upstart/app.conf")
|
18
19
|
File.read("/tmp/init/app-alpha.conf").should == example_export_file("upstart/app-alpha.conf")
|
@@ -21,18 +22,23 @@ describe Foreman::Export::Upstart do
|
|
21
22
|
File.read("/tmp/init/app-bravo-1.conf").should == example_export_file("upstart/app-bravo-1.conf")
|
22
23
|
end
|
23
24
|
|
24
|
-
|
25
|
-
|
25
|
+
context "with concurrency" do
|
26
|
+
let(:options) { Hash[:concurrency => "alpha=2"] }
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
it "exports to the filesystem with concurrency" do
|
29
|
+
upstart.export
|
30
|
+
|
31
|
+
File.read("/tmp/init/app.conf").should == example_export_file("upstart/app.conf")
|
32
|
+
File.read("/tmp/init/app-alpha.conf").should == example_export_file("upstart/app-alpha.conf")
|
33
|
+
File.read("/tmp/init/app-alpha-1.conf").should == example_export_file("upstart/app-alpha-1.conf")
|
34
|
+
File.read("/tmp/init/app-alpha-2.conf").should == example_export_file("upstart/app-alpha-2.conf")
|
35
|
+
File.exists?("/tmp/init/app-bravo-1.conf").should == false
|
36
|
+
end
|
32
37
|
end
|
33
38
|
|
34
39
|
context "with alternate templates" do
|
35
40
|
let(:template_root) { "/tmp/alternate" }
|
41
|
+
let(:upstart) { Foreman::Export::Upstart.new("/tmp/init", engine, :template => template_root) }
|
36
42
|
|
37
43
|
before do
|
38
44
|
FileUtils.mkdir_p template_root
|
@@ -40,7 +46,7 @@ describe Foreman::Export::Upstart do
|
|
40
46
|
end
|
41
47
|
|
42
48
|
it "can export with alternate template files" do
|
43
|
-
upstart.export
|
49
|
+
upstart.export
|
44
50
|
|
45
51
|
File.read("/tmp/init/app.conf").should == "alternate_template\n"
|
46
52
|
end
|
@@ -61,7 +67,7 @@ describe Foreman::Export::Upstart do
|
|
61
67
|
end
|
62
68
|
|
63
69
|
it "can export with alternate template files" do
|
64
|
-
upstart.export
|
70
|
+
upstart.export
|
65
71
|
|
66
72
|
File.read("/tmp/init/app.conf").should == "default_alternate_template\n"
|
67
73
|
end
|
@@ -1,2 +1,131 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'foreman/process'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'timeout'
|
5
|
+
require 'tmpdir'
|
6
|
+
|
7
|
+
describe Foreman::Process do
|
8
|
+
subject { described_class.new entry, number, port }
|
9
|
+
|
10
|
+
let(:number) { 1 }
|
11
|
+
let(:port) { 777 }
|
12
|
+
let(:command) { "script" }
|
13
|
+
let(:name) { "foobar" }
|
14
|
+
let(:entry) { OpenStruct.new :name => name, :command => command }
|
15
|
+
|
16
|
+
its(:entry) { entry }
|
17
|
+
its(:num) { number }
|
18
|
+
its(:port) { port }
|
19
|
+
its(:name) { "#{name}.#{port}" }
|
20
|
+
its(:pid) { nil }
|
21
|
+
|
22
|
+
describe '#run' do
|
23
|
+
let(:pipe) { :pipe }
|
24
|
+
let(:basedir) { Dir.mktmpdir }
|
25
|
+
let(:env) {{ 'foo' => 'bar' }}
|
26
|
+
let(:init_delta) { 0.1 }
|
27
|
+
|
28
|
+
after { FileUtils.remove_entry_secure basedir }
|
29
|
+
|
30
|
+
def run(cmd=command)
|
31
|
+
entry.command = cmd
|
32
|
+
subject.run pipe, basedir, env
|
33
|
+
subject.detach && sleep(init_delta)
|
34
|
+
end
|
35
|
+
|
36
|
+
def run_file(executable, code)
|
37
|
+
file = File.open("#{basedir}/script", 'w') {|it| it << code }
|
38
|
+
run "#{executable} #{file.path}"
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'options' do
|
42
|
+
it 'should set PORT for environment' do
|
43
|
+
mock(subject).run_process(basedir, command, pipe) do
|
44
|
+
ENV['PORT'].should == port.to_s
|
45
|
+
end
|
46
|
+
run
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should set custom variables for environment' do
|
50
|
+
mock(subject).run_process(basedir, command, pipe) do
|
51
|
+
ENV['foo'].should == 'bar'
|
52
|
+
end
|
53
|
+
run
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should restore environment afterwards' do
|
57
|
+
mock(subject).run_process(basedir, command, pipe)
|
58
|
+
run
|
59
|
+
ENV.should_not include('PORT', 'foo')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'process' do
|
64
|
+
around do |spec|
|
65
|
+
IO.pipe do |reader, writer|
|
66
|
+
@reader, @writer = reader, writer
|
67
|
+
spec.run
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
let(:pipe) { @writer }
|
72
|
+
let(:output) { @reader.read_nonblock 1024 }
|
73
|
+
|
74
|
+
it 'should not block' do
|
75
|
+
expect {
|
76
|
+
Timeout.timeout(2*init_delta) { run 'sleep 2' }
|
77
|
+
}.should_not raise_exception
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should be alive' do
|
81
|
+
run 'sleep 1'
|
82
|
+
subject.should be_alive
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should be dead' do
|
86
|
+
run 'exit'
|
87
|
+
subject.should be_dead
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should be killable' do
|
91
|
+
run 'sleep 1'
|
92
|
+
subject.kill 'TERM'
|
93
|
+
subject.should be_dead
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should send different signals' do
|
97
|
+
run_file 'ruby', <<-CODE
|
98
|
+
trap "TERM", "IGNORE"
|
99
|
+
loop { sleep 1 }
|
100
|
+
CODE
|
101
|
+
sleep 1 # wait for ruby to start
|
102
|
+
subject.should be_alive
|
103
|
+
subject.kill 'TERM'
|
104
|
+
subject.should be_alive
|
105
|
+
subject.kill 'KILL'
|
106
|
+
subject.should be_dead
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should redirect stdout' do
|
110
|
+
run 'echo hey'
|
111
|
+
output.should include('hey')
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should redirect stderr' do
|
115
|
+
run 'echo hey >2'
|
116
|
+
output.should include('hey')
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should handle variables' do
|
120
|
+
run 'echo $PORT'
|
121
|
+
output.should include('777')
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'should handle arguments' do
|
125
|
+
pending
|
126
|
+
run %{ sh -c "trap '' TERM; sleep 10" }
|
127
|
+
subject.should be_alive
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
data/spec/foreman_spec.rb
CHANGED
@@ -8,13 +8,8 @@ describe Foreman do
|
|
8
8
|
it { should be_a String }
|
9
9
|
end
|
10
10
|
|
11
|
-
describe "::load_env!(env_file)" do
|
12
|
-
before do
|
13
|
-
FakeFS.activate!
|
14
|
-
end
|
15
|
-
|
11
|
+
describe "::load_env!(env_file)", :fakefs do
|
16
12
|
after do
|
17
|
-
FakeFS.deactivate!
|
18
13
|
ENV['FOO'] = nil
|
19
14
|
end
|
20
15
|
|
@@ -22,7 +17,7 @@ describe Foreman do
|
|
22
17
|
File.open("/tmp/env1", "w") { |f| f.puts("FOO=bar") }
|
23
18
|
Foreman.load_env!("/tmp/env1")
|
24
19
|
ENV['FOO'].should == 'bar'
|
25
|
-
end
|
20
|
+
end
|
26
21
|
|
27
22
|
it "should assume env_file in ./.env" do
|
28
23
|
File.open("./.env", "w") { |f| f.puts("FOO=bar") }
|
data/spec/helper_spec.rb
CHANGED
@@ -3,16 +3,16 @@ require "spec_helper"
|
|
3
3
|
describe "spec helpers" do
|
4
4
|
describe "#preserving_env" do
|
5
5
|
after { ENV.delete "FOO" }
|
6
|
-
|
6
|
+
|
7
7
|
it "should remove added environment vars" do
|
8
8
|
preserving_env { ENV["FOO"] = "baz" }
|
9
9
|
ENV["FOO"].should == nil
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
it "should reset modified environment vars" do
|
13
13
|
ENV["FOO"] = "bar"
|
14
14
|
preserving_env { ENV["FOO"] = "baz"}
|
15
15
|
ENV["FOO"].should == "bar"
|
16
16
|
end
|
17
17
|
end
|
18
|
-
end
|
18
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -76,10 +76,11 @@ end
|
|
76
76
|
def normalize_space(s)
|
77
77
|
s.gsub(/\n[\n\s]*/, "\n")
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
RSpec.configure do |config|
|
81
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
81
82
|
config.color_enabled = true
|
82
83
|
config.order = 'rand'
|
83
|
-
config.include FakeFS::SpecHelpers
|
84
|
+
config.include FakeFS::SpecHelpers, :fakefs
|
84
85
|
config.mock_with :rr
|
85
86
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreman
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.37.0.
|
4
|
+
version: 0.37.0.pre4
|
5
5
|
prerelease: 7
|
6
6
|
platform: mingw32
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-01-
|
12
|
+
date: 2012-01-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: term-ansicolor
|
16
|
-
requirement: &
|
16
|
+
requirement: &70246169986640 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 1.0.7
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70246169986640
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: thor
|
27
|
-
requirement: &
|
27
|
+
requirement: &70246169985900 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 0.13.6
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70246169985900
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: win32console
|
38
|
-
requirement: &
|
38
|
+
requirement: &70246169984620 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: 1.3.0
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70246169984620
|
47
47
|
description: Process manager for applications with multiple components
|
48
48
|
email: ddollar@gmail.com
|
49
49
|
executables:
|
@@ -73,6 +73,7 @@ files:
|
|
73
73
|
- lib/foreman/export/runit.rb
|
74
74
|
- lib/foreman/export/upstart.rb
|
75
75
|
- lib/foreman/export.rb
|
76
|
+
- lib/foreman/helpers.rb
|
76
77
|
- lib/foreman/process.rb
|
77
78
|
- lib/foreman/procfile.rb
|
78
79
|
- lib/foreman/procfile_entry.rb
|
@@ -125,7 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
125
126
|
version: 1.3.1
|
126
127
|
requirements: []
|
127
128
|
rubyforge_project:
|
128
|
-
rubygems_version: 1.8.
|
129
|
+
rubygems_version: 1.8.15
|
129
130
|
signing_key:
|
130
131
|
specification_version: 3
|
131
132
|
summary: Process manager for applications with multiple components
|