foreman 0.26.1 → 0.40.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 +40 -0
- data/bin/foreman-runner +36 -0
- data/data/example/Procfile +1 -0
- data/data/example/utf8 +11 -0
- data/data/export/bluepill/master.pill.erb +3 -3
- data/lib/foreman/cli.rb +35 -31
- data/lib/foreman/engine.rb +109 -82
- data/lib/foreman/export/base.rb +12 -5
- data/lib/foreman/export/bluepill.rb +5 -7
- data/lib/foreman/export/inittab.rb +11 -13
- data/lib/foreman/export/runit.rb +24 -25
- data/lib/foreman/export/upstart.rb +9 -11
- data/lib/foreman/export.rb +21 -0
- data/lib/foreman/helpers.rb +45 -0
- data/lib/foreman/process.rb +88 -6
- data/lib/foreman/procfile.rb +8 -7
- data/lib/foreman/procfile_entry.rb +22 -0
- data/lib/foreman/utils.rb +4 -1
- data/lib/foreman/version.rb +1 -1
- data/lib/foreman.rb +12 -1
- data/man/foreman.1 +17 -3
- data/spec/foreman/cli_spec.rb +96 -7
- data/spec/foreman/engine_spec.rb +52 -31
- data/spec/foreman/export/base_spec.rb +22 -0
- data/spec/foreman/export/bluepill_spec.rb +23 -7
- data/spec/foreman/export/inittab_spec.rb +40 -0
- data/spec/foreman/export/runit_spec.rb +18 -12
- data/spec/foreman/export/upstart_spec.rb +40 -8
- 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 +5 -0
- data/spec/helper_spec.rb +18 -0
- data/spec/resources/bin/utf8 +2 -0
- data/spec/resources/export/bluepill/app-concurrency.pill +47 -0
- data/spec/resources/export/bluepill/app.pill +1 -22
- data/spec/resources/export/inittab/inittab.concurrency +4 -0
- data/spec/resources/export/inittab/inittab.default +4 -0
- data/spec/spec_helper.rb +36 -4
- metadata +23 -11
- data/README.markdown +0 -49
data/lib/foreman/export/runit.rb
CHANGED
|
@@ -3,58 +3,57 @@ require "foreman/export"
|
|
|
3
3
|
|
|
4
4
|
class Foreman::Export::Runit < Foreman::Export::Base
|
|
5
5
|
ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
|
|
6
|
-
|
|
7
|
-
def export
|
|
6
|
+
|
|
7
|
+
def export
|
|
8
8
|
error("Must specify a location") unless location
|
|
9
|
-
|
|
10
|
-
app =
|
|
11
|
-
user =
|
|
12
|
-
log_root =
|
|
13
|
-
template_root =
|
|
14
|
-
|
|
15
|
-
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
|
16
|
-
|
|
9
|
+
|
|
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
|
|
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
|
-
engine.
|
|
21
|
-
1.upto(concurrency[process.name]) do |num|
|
|
18
|
+
engine.procfile.entries.each do |process|
|
|
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"
|
|
25
|
-
|
|
23
|
+
|
|
26
24
|
create_directory process_directory
|
|
27
25
|
create_directory process_env_directory
|
|
28
26
|
create_directory process_log_directory
|
|
29
|
-
|
|
27
|
+
|
|
30
28
|
run = ERB.new(run_template).result(binding)
|
|
31
29
|
write_file "#{process_directory}/run", run
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
FileUtils.chmod 0755, "#{process_directory}/run"
|
|
31
|
+
|
|
32
|
+
port = engine.port_for(process, num, self.port)
|
|
34
33
|
environment_variables = {'PORT' => port}.
|
|
35
34
|
merge(engine.environment).
|
|
36
35
|
merge(inline_variables(process.command))
|
|
37
|
-
|
|
36
|
+
|
|
38
37
|
environment_variables.each_pair do |var, env|
|
|
39
38
|
write_file "#{process_env_directory}/#{var.upcase}", env
|
|
40
39
|
end
|
|
41
|
-
|
|
40
|
+
|
|
42
41
|
log_run = ERB.new(log_run_template).result(binding)
|
|
43
42
|
write_file "#{process_log_directory}/run", log_run
|
|
44
|
-
|
|
43
|
+
FileUtils.chmod 0755, "#{process_log_directory}/run"
|
|
45
44
|
end
|
|
46
45
|
end
|
|
47
|
-
|
|
46
|
+
|
|
48
47
|
end
|
|
49
|
-
|
|
48
|
+
|
|
50
49
|
private
|
|
51
50
|
def create_directory(location)
|
|
52
51
|
say "creating: #{location}"
|
|
53
|
-
FileUtils.
|
|
52
|
+
FileUtils.mkdir_p(location)
|
|
54
53
|
end
|
|
55
|
-
|
|
54
|
+
|
|
56
55
|
def inline_variables(command)
|
|
57
|
-
variable_name_regex =
|
|
56
|
+
variable_name_regex =
|
|
58
57
|
Hash[*command.scan(ENV_VARIABLE_REGEX).flatten]
|
|
59
58
|
end
|
|
60
|
-
end
|
|
59
|
+
end
|
|
@@ -3,37 +3,35 @@ 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
|
|
26
24
|
|
|
27
25
|
process_template = export_template("upstart", "process.conf.erb", template_root)
|
|
28
26
|
|
|
29
|
-
engine.
|
|
30
|
-
next if (conc = concurrency[process.name]) < 1
|
|
27
|
+
engine.procfile.entries.each do |process|
|
|
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,14 +1,96 @@
|
|
|
1
1
|
require "foreman"
|
|
2
|
+
require "rubygems"
|
|
2
3
|
|
|
3
4
|
class Foreman::Process
|
|
4
5
|
|
|
5
|
-
attr_reader :
|
|
6
|
-
attr_reader :
|
|
7
|
-
|
|
6
|
+
attr_reader :entry
|
|
7
|
+
attr_reader :num
|
|
8
|
+
attr_reader :pid
|
|
9
|
+
attr_reader :port
|
|
8
10
|
|
|
9
|
-
def initialize(
|
|
10
|
-
@
|
|
11
|
-
@
|
|
11
|
+
def initialize(entry, num, port)
|
|
12
|
+
@entry = entry
|
|
13
|
+
@num = num
|
|
14
|
+
@port = port
|
|
12
15
|
end
|
|
13
16
|
|
|
17
|
+
def run(pipe, basedir, environment)
|
|
18
|
+
with_environment(environment.merge("PORT" => port.to_s)) do
|
|
19
|
+
run_process basedir, entry.command, pipe
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def name
|
|
24
|
+
"%s.%s" % [ entry.name, num ]
|
|
25
|
+
end
|
|
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
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def fork_with_io(command, basedir)
|
|
48
|
+
reader, writer = IO.pipe
|
|
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
|
|
67
|
+
end
|
|
68
|
+
[ reader, pid ]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def run_process(basedir, command, pipe)
|
|
72
|
+
io, @pid = fork_with_io(command, basedir)
|
|
73
|
+
output pipe, "started with pid %d" % @pid
|
|
74
|
+
Thread.new do
|
|
75
|
+
until io.eof?
|
|
76
|
+
output pipe, io.gets
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def output(pipe, message)
|
|
82
|
+
pipe.puts "%s,%s" % [ name, message ]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def replace_command_env(command)
|
|
86
|
+
command.gsub(/\$(\w+)/) { |e| ENV[e[1..-1]] }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def with_environment(environment)
|
|
90
|
+
original = ENV.to_hash
|
|
91
|
+
ENV.update environment
|
|
92
|
+
yield
|
|
93
|
+
ensure
|
|
94
|
+
ENV.replace original
|
|
95
|
+
end
|
|
14
96
|
end
|
data/lib/foreman/procfile.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "foreman"
|
|
2
|
+
require "foreman/procfile_entry"
|
|
2
3
|
|
|
3
4
|
# A valid Procfile entry is captured by this regex.
|
|
4
5
|
# All other lines are ignored.
|
|
@@ -10,18 +11,18 @@ require "foreman"
|
|
|
10
11
|
#
|
|
11
12
|
class Foreman::Procfile
|
|
12
13
|
|
|
13
|
-
attr_reader :
|
|
14
|
+
attr_reader :entries
|
|
14
15
|
|
|
15
16
|
def initialize(filename)
|
|
16
|
-
@
|
|
17
|
+
@entries = parse_procfile(filename)
|
|
17
18
|
end
|
|
18
19
|
|
|
19
|
-
def
|
|
20
|
-
|
|
20
|
+
def [](name)
|
|
21
|
+
entries.detect { |entry| entry.name == name }
|
|
21
22
|
end
|
|
22
23
|
|
|
23
|
-
def
|
|
24
|
-
|
|
24
|
+
def process_names
|
|
25
|
+
entries.map(&:name)
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
private
|
|
@@ -29,7 +30,7 @@ private
|
|
|
29
30
|
def parse_procfile(filename)
|
|
30
31
|
File.read(filename).split("\n").map do |line|
|
|
31
32
|
if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
|
|
32
|
-
Foreman::
|
|
33
|
+
Foreman::ProcfileEntry.new($1, $2)
|
|
33
34
|
end
|
|
34
35
|
end.compact
|
|
35
36
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require "foreman"
|
|
2
|
+
|
|
3
|
+
class Foreman::ProcfileEntry
|
|
4
|
+
|
|
5
|
+
attr_reader :name
|
|
6
|
+
attr_reader :command
|
|
7
|
+
attr_accessor :color
|
|
8
|
+
|
|
9
|
+
def initialize(name, command)
|
|
10
|
+
@name = name
|
|
11
|
+
@command = command
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def spawn(num, pipe, basedir, environment, base_port)
|
|
15
|
+
(1..num).to_a.map do |n|
|
|
16
|
+
process = Foreman::Process.new(self, n, base_port + (n-1))
|
|
17
|
+
process.run(pipe, basedir, environment)
|
|
18
|
+
process
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
data/lib/foreman/utils.rb
CHANGED
|
@@ -5,7 +5,10 @@ class Foreman::Utils
|
|
|
5
5
|
def self.parse_concurrency(concurrency)
|
|
6
6
|
begin
|
|
7
7
|
pairs = concurrency.to_s.gsub(/\s/, "").split(",")
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
default = concurrency.nil? ? 1 : 0
|
|
10
|
+
|
|
11
|
+
pairs.inject(Hash.new(default)) do |hash, pair|
|
|
9
12
|
process, amount = pair.split("=")
|
|
10
13
|
hash.update(process => amount.to_i)
|
|
11
14
|
end
|
data/lib/foreman/version.rb
CHANGED
data/lib/foreman.rb
CHANGED
|
@@ -4,5 +4,16 @@ module Foreman
|
|
|
4
4
|
|
|
5
5
|
class AppDoesNotExist < Exception; end
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
def self.runner
|
|
8
|
+
File.expand_path("../../bin/foreman-runner", __FILE__)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.jruby?
|
|
12
|
+
defined?(RUBY_PLATFORM) and RUBY_PLATFORM == "java"
|
|
13
|
+
end
|
|
8
14
|
|
|
15
|
+
def self.windows?
|
|
16
|
+
defined?(RUBY_PLATFORM) and RUBY_PLATFORM =~ /(win|w)32$/
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
data/man/foreman.1
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
.\" generated with Ronn/v0.7.3
|
|
2
2
|
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
|
3
3
|
.
|
|
4
|
-
.TH "FOREMAN" "1" "
|
|
4
|
+
.TH "FOREMAN" "1" "February 2012" "Foreman 0.39.0" "Foreman Manual"
|
|
5
5
|
.
|
|
6
6
|
.SH "NAME"
|
|
7
7
|
\fBforeman\fR \- manage Procfile\-based applications
|
|
@@ -10,10 +10,13 @@
|
|
|
10
10
|
\fBforeman start [process]\fR
|
|
11
11
|
.
|
|
12
12
|
.br
|
|
13
|
+
\fBforeman run <command>\fR
|
|
14
|
+
.
|
|
15
|
+
.br
|
|
13
16
|
\fBforeman export <format> [location]\fR
|
|
14
17
|
.
|
|
15
18
|
.SH "DESCRIPTION"
|
|
16
|
-
|
|
19
|
+
Foreman is a manager for Procfile\-based applications\. Its aim is to abstract away the details of the Procfile format, and allow you to either run your application directly or export it to some other process management format\.
|
|
17
20
|
.
|
|
18
21
|
.SH "RUNNING"
|
|
19
22
|
\fBforeman start\fR is used to run your application directly from the command line\.
|
|
@@ -35,6 +38,9 @@ Specify the number of each process type to run\. The value passed in should be i
|
|
|
35
38
|
\fB\-p\fR, \fB\-\-port\fR
|
|
36
39
|
Specify which port to use as the base for this application\. Should be a multiple of 1000\.
|
|
37
40
|
.
|
|
41
|
+
.P
|
|
42
|
+
\fBforeman run\fR is used to run one\-off commands using the same environment as your defined processes\.
|
|
43
|
+
.
|
|
38
44
|
.SH "EXPORTING"
|
|
39
45
|
\fBforeman export\fR is used to export your application to another process management format\.
|
|
40
46
|
.
|
|
@@ -61,13 +67,21 @@ Specify the directory to place process logs in\.
|
|
|
61
67
|
Specify which port to use as the base for this application\. Should be a multiple of 1000\.
|
|
62
68
|
.
|
|
63
69
|
.TP
|
|
70
|
+
\fB\-t\fR, \fB\-\-template\fR
|
|
71
|
+
Specify an alternate template to use for creating export files\. See \fIhttps://github\.com/ddollar/foreman/tree/master/data/export\fR for examples\.
|
|
72
|
+
.
|
|
73
|
+
.TP
|
|
64
74
|
\fB\-u\fR, \fB\-\-user\fR
|
|
65
75
|
Specify the user the application should be run as\. Defaults to the app name
|
|
66
76
|
.
|
|
67
|
-
.SH "OPTIONS"
|
|
77
|
+
.SH "GLOBAL OPTIONS"
|
|
68
78
|
These options control all modes of foreman\'s operation\.
|
|
69
79
|
.
|
|
70
80
|
.TP
|
|
81
|
+
\fB\-d\fR, \fB\-\-directory\fR
|
|
82
|
+
Specify an alternate application root\. This defaults to the directory containing the Procfile\.
|
|
83
|
+
.
|
|
84
|
+
.TP
|
|
71
85
|
\fB\-e\fR, \fB\-\-env\fR
|
|
72
86
|
Specify an alternate environment file\. You can specify more than one file by using: \fB\-\-env file1,file2\fR\.
|
|
73
87
|
.
|
data/spec/foreman/cli_spec.rb
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
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
|
+
let(:engine) { subject.send(:engine) }
|
|
7
|
+
let(:entries) { engine.procfile.entries.inject({}) { |h,e| h.update(e.name => e) } }
|
|
6
8
|
|
|
7
9
|
describe "start" do
|
|
8
10
|
describe "with a non-existent Procfile" do
|
|
@@ -22,15 +24,35 @@ describe "Foreman::CLI" do
|
|
|
22
24
|
mock.instance_of(Foreman::Engine).start
|
|
23
25
|
subject.start
|
|
24
26
|
end
|
|
27
|
+
|
|
28
|
+
it "can run a single process" do
|
|
29
|
+
dont_allow(subject).error
|
|
30
|
+
stub(engine).watch_for_output
|
|
31
|
+
stub(engine).watch_for_termination
|
|
32
|
+
mock(entries["alpha"]).spawn(1, is_a(IO), engine.directory, {}, 5000) { [] }
|
|
33
|
+
mock(entries["bravo"]).spawn(0, is_a(IO), engine.directory, {}, 5100) { [] }
|
|
34
|
+
subject.start("alpha")
|
|
35
|
+
end
|
|
25
36
|
end
|
|
26
37
|
end
|
|
27
38
|
|
|
28
39
|
describe "export" do
|
|
29
40
|
describe "options" do
|
|
41
|
+
it "uses .foreman" do
|
|
42
|
+
write_procfile
|
|
43
|
+
File.open(".foreman", "w") { |f| f.puts "concurrency: alpha=2" }
|
|
44
|
+
mock_export = mock(Foreman::Export::Upstart)
|
|
45
|
+
mock(Foreman::Export::Upstart).new("/upstart", is_a(Foreman::Engine), { "concurrency" => "alpha=2" }) { mock_export }
|
|
46
|
+
mock_export.export
|
|
47
|
+
foreman %{ export upstart /upstart }
|
|
48
|
+
end
|
|
49
|
+
|
|
30
50
|
it "respects --env" do
|
|
31
51
|
write_procfile
|
|
32
52
|
write_env("envfile")
|
|
33
|
-
mock
|
|
53
|
+
mock_export = mock(Foreman::Export::Upstart)
|
|
54
|
+
mock(Foreman::Export::Upstart).new("/upstart", is_a(Foreman::Engine), { "env" => "envfile" }) { mock_export }
|
|
55
|
+
mock_export.export
|
|
34
56
|
foreman %{ export upstart /upstart --env envfile }
|
|
35
57
|
end
|
|
36
58
|
end
|
|
@@ -47,10 +69,18 @@ describe "Foreman::CLI" do
|
|
|
47
69
|
describe "with a Procfile" do
|
|
48
70
|
before(:each) { write_procfile }
|
|
49
71
|
|
|
50
|
-
describe "with
|
|
72
|
+
describe "with a formatter with a generic error" do
|
|
73
|
+
before do
|
|
74
|
+
mock(Foreman::Export).formatter("errorful") { Class.new(Foreman::Export::Base) do
|
|
75
|
+
def export
|
|
76
|
+
raise Foreman::Export::Exception.new("foo")
|
|
77
|
+
end
|
|
78
|
+
end }
|
|
79
|
+
end
|
|
80
|
+
|
|
51
81
|
it "prints an error" do
|
|
52
|
-
mock_error(subject, "
|
|
53
|
-
subject.export("
|
|
82
|
+
mock_error(subject, "foo") do
|
|
83
|
+
subject.export("errorful")
|
|
54
84
|
end
|
|
55
85
|
end
|
|
56
86
|
end
|
|
@@ -60,7 +90,9 @@ describe "Foreman::CLI" do
|
|
|
60
90
|
|
|
61
91
|
it "runs successfully" do
|
|
62
92
|
dont_allow(subject).error
|
|
63
|
-
mock
|
|
93
|
+
mock_export = mock(Foreman::Export::Upstart)
|
|
94
|
+
mock(Foreman::Export::Upstart).new("/tmp/foo", is_a(Foreman::Engine), {}) { mock_export }
|
|
95
|
+
mock_export.export
|
|
64
96
|
subject.export("upstart", "/tmp/foo")
|
|
65
97
|
end
|
|
66
98
|
end
|
|
@@ -72,7 +104,7 @@ describe "Foreman::CLI" do
|
|
|
72
104
|
before { write_procfile }
|
|
73
105
|
|
|
74
106
|
it "displays the jobs" do
|
|
75
|
-
mock(subject).
|
|
107
|
+
mock(subject).puts("valid procfile detected (alpha, bravo)")
|
|
76
108
|
subject.check
|
|
77
109
|
end
|
|
78
110
|
end
|
|
@@ -88,6 +120,63 @@ describe "Foreman::CLI" do
|
|
|
88
120
|
end
|
|
89
121
|
end
|
|
90
122
|
end
|
|
123
|
+
|
|
124
|
+
describe "without a Procfile" do
|
|
125
|
+
it "displays an error" do
|
|
126
|
+
mock_error(subject, "Procfile does not exist.") do
|
|
127
|
+
subject.check
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
describe "run" do
|
|
134
|
+
describe "with a valid Procfile" do
|
|
135
|
+
before { write_procfile }
|
|
136
|
+
|
|
137
|
+
describe "and a command" do
|
|
138
|
+
let(:command) { ["ls", "-l"] }
|
|
139
|
+
|
|
140
|
+
before(:each) do
|
|
141
|
+
stub(subject).exec
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it "should load the environment file" do
|
|
145
|
+
write_env
|
|
146
|
+
preserving_env do
|
|
147
|
+
subject.run *command
|
|
148
|
+
ENV["FOO"].should == "bar"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
ENV["FOO"].should be_nil
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it "should runute the command as a string" do
|
|
155
|
+
mock(subject).exec(command.join(" "))
|
|
156
|
+
subject.run *command
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
describe "and a non-existent command" do
|
|
161
|
+
let(:command) { "iuhtngrglhulhdfg" }
|
|
162
|
+
|
|
163
|
+
it "should print an error" do
|
|
164
|
+
mock_error(subject, "command not found: #{command}") do
|
|
165
|
+
subject.run command
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
describe "and a non-executable command" do
|
|
171
|
+
let(:command) { __FILE__ }
|
|
172
|
+
|
|
173
|
+
it "should print an error" do
|
|
174
|
+
mock_error(subject, "not executable: #{command}") do
|
|
175
|
+
subject.run command
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
91
180
|
end
|
|
92
181
|
|
|
93
182
|
end
|