foreman 0.47.0 → 0.48.0.pre1
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/bin/taskman +8 -0
- data/data/example/Procfile +4 -3
- data/data/example/spawnee +14 -0
- data/data/example/spawner +7 -0
- data/data/export/bluepill/master.pill.erb +10 -10
- data/data/export/launchd/launchd.plist.erb +3 -3
- data/data/export/runit/log/run.erb +7 -0
- data/data/export/runit/run.erb +2 -2
- data/data/export/supervisord/app.conf.erb +12 -12
- data/data/export/upstart/master.conf.erb +2 -2
- data/data/export/upstart/process.conf.erb +3 -3
- data/lib/foreman/cli.rb +49 -21
- data/lib/foreman/engine.rb +208 -148
- data/lib/foreman/engine/cli.rb +98 -0
- data/lib/foreman/env.rb +27 -0
- data/lib/foreman/export.rb +0 -1
- data/lib/foreman/export/base.rb +58 -35
- data/lib/foreman/export/bluepill.rb +3 -17
- data/lib/foreman/export/inittab.rb +8 -11
- data/lib/foreman/export/launchd.rb +4 -16
- data/lib/foreman/export/runit.rb +14 -39
- data/lib/foreman/export/supervisord.rb +3 -13
- data/lib/foreman/export/upstart.rb +9 -27
- data/lib/foreman/process.rb +56 -67
- data/lib/foreman/procfile.rb +59 -25
- data/lib/foreman/version.rb +1 -1
- data/man/foreman.1 +4 -0
- data/spec/foreman/cli_spec.rb +38 -152
- data/spec/foreman/engine_spec.rb +46 -80
- data/spec/foreman/export/base_spec.rb +4 -7
- data/spec/foreman/export/bluepill_spec.rb +7 -6
- data/spec/foreman/export/inittab_spec.rb +7 -7
- data/spec/foreman/export/launchd_spec.rb +4 -7
- data/spec/foreman/export/runit_spec.rb +12 -17
- data/spec/foreman/export/supervisord_spec.rb +7 -56
- data/spec/foreman/export/upstart_spec.rb +18 -23
- data/spec/foreman/process_spec.rb +27 -124
- data/spec/foreman/procfile_spec.rb +26 -16
- data/spec/resources/Procfile +4 -0
- data/spec/resources/bin/echo +2 -0
- data/spec/resources/bin/env +2 -0
- data/spec/resources/bin/test +2 -0
- data/spec/resources/export/bluepill/app-concurrency.pill +4 -4
- data/spec/resources/export/bluepill/app.pill +4 -4
- data/spec/resources/export/runit/{app-alpha-1-log-run → app-alpha-1/log/run} +0 -0
- data/spec/resources/export/runit/{app-alpha-1-run → app-alpha-1/run} +0 -0
- data/spec/resources/export/runit/{app-alpha-2-log-run → app-alpha-2/log/run} +0 -0
- data/spec/resources/export/runit/{app-alpha-2-run → app-alpha-2/run} +0 -0
- data/spec/resources/export/runit/{app-bravo-1-log-run → app-bravo-1/log/run} +0 -0
- data/spec/resources/export/runit/{app-bravo-1-run → app-bravo-1/run} +0 -0
- data/spec/resources/export/supervisord/app-alpha-1.conf +24 -0
- data/spec/resources/export/supervisord/app-alpha-2.conf +4 -4
- data/spec/spec_helper.rb +58 -6
- metadata +24 -22
- data/data/export/runit/log_run.erb +0 -7
- data/lib/foreman/color.rb +0 -40
- data/lib/foreman/procfile_entry.rb +0 -26
- data/lib/foreman/utils.rb +0 -18
- data/spec/foreman/color_spec.rb +0 -31
- data/spec/foreman/procfile_entry_spec.rb +0 -13
- data/spec/resources/export/supervisord/app-env-with-comma.conf +0 -24
- data/spec/resources/export/supervisord/app-env.conf +0 -21
- data/spec/resources/export/supervisord/app.conf +0 -24
@@ -0,0 +1,98 @@
|
|
1
|
+
require "foreman/engine"
|
2
|
+
|
3
|
+
class Foreman::Engine::CLI < Foreman::Engine
|
4
|
+
|
5
|
+
module Color
|
6
|
+
|
7
|
+
ANSI = {
|
8
|
+
:reset => 0,
|
9
|
+
:black => 30,
|
10
|
+
:red => 31,
|
11
|
+
:green => 32,
|
12
|
+
:yellow => 33,
|
13
|
+
:blue => 34,
|
14
|
+
:magenta => 35,
|
15
|
+
:cyan => 36,
|
16
|
+
:white => 37,
|
17
|
+
:bright_black => 30,
|
18
|
+
:bright_red => 31,
|
19
|
+
:bright_green => 32,
|
20
|
+
:bright_yellow => 33,
|
21
|
+
:bright_blue => 34,
|
22
|
+
:bright_magenta => 35,
|
23
|
+
:bright_cyan => 36,
|
24
|
+
:bright_white => 37,
|
25
|
+
}
|
26
|
+
|
27
|
+
def self.enable(io)
|
28
|
+
io.extend(self)
|
29
|
+
end
|
30
|
+
|
31
|
+
def color?
|
32
|
+
return false unless self.respond_to?(:isatty)
|
33
|
+
self.isatty && ENV["TERM"]
|
34
|
+
end
|
35
|
+
|
36
|
+
def color(name)
|
37
|
+
return "" unless color?
|
38
|
+
return "" unless ansi = ANSI[name.to_sym]
|
39
|
+
"\e[#{ansi}m"
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
FOREMAN_COLORS = %w( cyan yellow green magenta red blue intense_cyan intense_yellow
|
45
|
+
intense_green intense_magenta intense_red, intense_blue )
|
46
|
+
|
47
|
+
def startup
|
48
|
+
@colors = map_colors
|
49
|
+
proctitle "foreman: master"
|
50
|
+
end
|
51
|
+
|
52
|
+
def output(name, data)
|
53
|
+
data.to_s.chomp.split("\n").each do |message|
|
54
|
+
Color.enable($stdout) unless $stdout.respond_to?(:color?)
|
55
|
+
output = ""
|
56
|
+
output += $stdout.color(@colors[name.split(".").first].to_sym)
|
57
|
+
output += "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
|
58
|
+
output += $stdout.color(:reset)
|
59
|
+
output += message
|
60
|
+
$stdout.puts output
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def shutdown
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def name_padding
|
70
|
+
@name_padding ||= begin
|
71
|
+
index_padding = @names.values.map { |n| formation[n] }.max.to_s.length + 1
|
72
|
+
name_padding = @names.values.map { |n| n.length + index_padding }.sort.last
|
73
|
+
[ 6, name_padding ].max
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def pad_process_name(name)
|
78
|
+
name.ljust(name_padding, " ")
|
79
|
+
end
|
80
|
+
|
81
|
+
def map_colors
|
82
|
+
colors = Hash.new("white")
|
83
|
+
@names.values.each_with_index do |name, index|
|
84
|
+
colors[name] = FOREMAN_COLORS[index % FOREMAN_COLORS.length]
|
85
|
+
end
|
86
|
+
colors["system"] = "intense_white"
|
87
|
+
colors
|
88
|
+
end
|
89
|
+
|
90
|
+
def proctitle(title)
|
91
|
+
$0 = title
|
92
|
+
end
|
93
|
+
|
94
|
+
def termtitle(title)
|
95
|
+
printf("\033]0;#{title}\007") unless Foreman.windows?
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
data/lib/foreman/env.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require "foreman"
|
2
|
+
|
3
|
+
class Foreman::Env
|
4
|
+
|
5
|
+
attr_reader :entries
|
6
|
+
|
7
|
+
def initialize(filename)
|
8
|
+
@entries = File.read(filename).split("\n").inject({}) do |ax, line|
|
9
|
+
if line =~ /\A([A-Za-z_0-9]+)=(.*)\z/
|
10
|
+
key = $1
|
11
|
+
case val = $2
|
12
|
+
when /\A'(.*)'\z/ then ax[key] = $1
|
13
|
+
when /\A"(.*)"\z/ then ax[key] = $1.gsub(/\\(.)/, '\1')
|
14
|
+
else ax[key] = val
|
15
|
+
end
|
16
|
+
end
|
17
|
+
ax
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def entries
|
22
|
+
@entries.each do |key, value|
|
23
|
+
yield key, value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/lib/foreman/export.rb
CHANGED
data/lib/foreman/export/base.rb
CHANGED
@@ -1,23 +1,37 @@
|
|
1
1
|
require "foreman/export"
|
2
|
-
require "
|
2
|
+
require "shellwords"
|
3
3
|
|
4
4
|
class Foreman::Export::Base
|
5
5
|
|
6
|
-
attr_reader :location
|
6
|
+
attr_reader :location
|
7
|
+
attr_reader :engine
|
8
|
+
attr_reader :options
|
9
|
+
attr_reader :formation
|
7
10
|
|
8
11
|
def initialize(location, engine, options={})
|
9
|
-
@location
|
10
|
-
@engine
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@port = options[:port]
|
14
|
-
@user = options[:user]
|
15
|
-
@template = options[:template]
|
16
|
-
@concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
12
|
+
@location = location
|
13
|
+
@engine = engine
|
14
|
+
@options = options.dup
|
15
|
+
@formation = engine.formation
|
17
16
|
end
|
18
17
|
|
19
18
|
def export
|
20
|
-
|
19
|
+
error("Must specify a location") unless location
|
20
|
+
FileUtils.mkdir_p(location) rescue error("Could not create: #{location}")
|
21
|
+
FileUtils.mkdir_p(log) rescue error("Could not create: #{log}")
|
22
|
+
FileUtils.chown(user, nil, log) rescue error("Could not chown #{log} to #{user}")
|
23
|
+
end
|
24
|
+
|
25
|
+
def app
|
26
|
+
options[:app] || "app"
|
27
|
+
end
|
28
|
+
|
29
|
+
def log
|
30
|
+
options[:log] || "/var/log/#{app}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def user
|
34
|
+
options[:user] || app
|
21
35
|
end
|
22
36
|
|
23
37
|
private ######################################################################
|
@@ -29,38 +43,47 @@ private ######################################################################
|
|
29
43
|
def say(message)
|
30
44
|
puts "[foreman export] %s" % message
|
31
45
|
end
|
46
|
+
|
47
|
+
def clean(filename)
|
48
|
+
return unless File.exists?(filename)
|
49
|
+
say "cleaning up: #{filename}"
|
50
|
+
FileUtils.rm(filename)
|
51
|
+
end
|
32
52
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
53
|
+
def shell_quote(value)
|
54
|
+
'"' + Shellwords.escape(value) + '"'
|
55
|
+
end
|
56
|
+
|
57
|
+
def export_template(name)
|
58
|
+
name_without_first = name.split("/")[1..-1].join("/")
|
59
|
+
matchers = []
|
60
|
+
matchers << File.join(options[:template], name_without_first) if options[:template]
|
61
|
+
matchers << File.expand_path("~/.foreman/templates/#{name}")
|
62
|
+
matchers << File.expand_path("../../../../data/export/#{name}", __FILE__)
|
63
|
+
File.read(matchers.detect { |m| File.exists?(m) })
|
64
|
+
end
|
65
|
+
|
66
|
+
def write_template(name, target, binding)
|
67
|
+
compiled = ERB.new(export_template(name)).result(binding)
|
68
|
+
write_file target, compiled
|
69
|
+
end
|
70
|
+
|
71
|
+
def chmod(mode, file)
|
72
|
+
say "setting #{file} to mode #{mode}"
|
73
|
+
FileUtils.chmod mode, File.join(location, file)
|
74
|
+
end
|
75
|
+
|
76
|
+
def create_directory(dir)
|
77
|
+
say "creating: #{dir}"
|
78
|
+
FileUtils.mkdir_p(File.join(location, dir))
|
41
79
|
end
|
42
80
|
|
43
81
|
def write_file(filename, contents)
|
44
82
|
say "writing: #{filename}"
|
45
83
|
|
46
|
-
File.open(filename, "w") do |file|
|
84
|
+
File.open(File.join(location, filename), "w") do |file|
|
47
85
|
file.puts contents
|
48
86
|
end
|
49
87
|
end
|
50
88
|
|
51
|
-
# Quote a string to be used on the command line. Backslashes are escapde to \\ and quotes
|
52
|
-
# escaped to \"
|
53
|
-
#
|
54
|
-
# str - string to be quoted
|
55
|
-
#
|
56
|
-
# Examples
|
57
|
-
#
|
58
|
-
# shell_quote("FB|123\"\\1")
|
59
|
-
# # => "\"FB|123\"\\"\\\\1\""
|
60
|
-
#
|
61
|
-
# Returns the the escaped string surrounded by quotes
|
62
|
-
def shell_quote(str)
|
63
|
-
"\"#{str.gsub(/\\/){ '\\\\' }.gsub(/["]/){ "\\\"" }}\""
|
64
|
-
end
|
65
|
-
|
66
89
|
end
|
@@ -4,23 +4,9 @@ require "foreman/export"
|
|
4
4
|
class Foreman::Export::Bluepill < Foreman::Export::Base
|
5
5
|
|
6
6
|
def export
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
16
|
-
Dir["#{location}/#{app}.pill"].each do |file|
|
17
|
-
say "cleaning up: #{file}"
|
18
|
-
FileUtils.rm(file)
|
19
|
-
end
|
20
|
-
|
21
|
-
master_template = export_template("bluepill", "master.pill.erb", template_root)
|
22
|
-
master_config = ERB.new(master_template).result(binding)
|
23
|
-
write_file "#{location}/#{app}.pill", master_config
|
7
|
+
super
|
8
|
+
clean "#{location}/#{app}.pill"
|
9
|
+
write_template "bluepill/master.pill.erb", "#{app}.pill", binding
|
24
10
|
end
|
25
11
|
|
26
12
|
end
|
@@ -3,21 +3,19 @@ require "foreman/export"
|
|
3
3
|
class Foreman::Export::Inittab < Foreman::Export::Base
|
4
4
|
|
5
5
|
def export
|
6
|
-
|
7
|
-
user = self.user || app
|
8
|
-
log_root = self.log || "/var/log/#{app}"
|
6
|
+
error("Must specify a location") unless location
|
9
7
|
|
10
8
|
inittab = []
|
11
9
|
inittab << "# ----- foreman #{app} processes -----"
|
12
10
|
|
13
|
-
|
14
|
-
|
11
|
+
index = 1
|
12
|
+
engine.each_process do |name, process|
|
13
|
+
1.upto(engine.formation[name]) do |num|
|
15
14
|
id = app.slice(0, 2).upcase + sprintf("%02d", index)
|
16
|
-
port = engine.port_for(process, num
|
17
|
-
inittab << "#{id}:4:respawn:/bin/su - #{user} -c 'PORT=#{port} #{process.command} >> #{
|
15
|
+
port = engine.port_for(process, num)
|
16
|
+
inittab << "#{id}:4:respawn:/bin/su - #{user} -c 'PORT=#{port} #{process.command} >> #{log}/#{name}-#{num}.log 2>&1'"
|
18
17
|
index += 1
|
19
18
|
end
|
20
|
-
index
|
21
19
|
end
|
22
20
|
|
23
21
|
inittab << "# ----- end foreman #{app} processes -----"
|
@@ -27,9 +25,8 @@ class Foreman::Export::Inittab < Foreman::Export::Base
|
|
27
25
|
if location == "-"
|
28
26
|
puts inittab
|
29
27
|
else
|
30
|
-
|
31
|
-
|
32
|
-
write_file(location, inittab)
|
28
|
+
say "writing: #{location}"
|
29
|
+
File.open(location, "w") { |file| file.puts inittab }
|
33
30
|
end
|
34
31
|
end
|
35
32
|
|
@@ -4,24 +4,12 @@ require "foreman/export"
|
|
4
4
|
class Foreman::Export::Launchd < Foreman::Export::Base
|
5
5
|
|
6
6
|
def export
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
log_root = self.log || "/var/log/#{app}"
|
12
|
-
template_root = self.template
|
13
|
-
|
14
|
-
FileUtils.mkdir_p(location)
|
15
|
-
|
16
|
-
engine.procfile.entries.each do |process|
|
17
|
-
1.upto(self.concurrency[process.name]) do |num|
|
18
|
-
|
19
|
-
master_template = export_template("launchd", "launchd.plist.erb", template_root)
|
20
|
-
master_config = ERB.new(master_template).result(binding)
|
21
|
-
write_file "#{location}/#{app}-#{process.name}-#{num}.plist", master_config
|
7
|
+
super
|
8
|
+
engine.each_process do |name, process|
|
9
|
+
1.upto(engine.formation[name]) do |num|
|
10
|
+
write_template "launchd/launchd.plist.erb", "#{app}-#{name}-#{num}.plist", binding
|
22
11
|
end
|
23
12
|
end
|
24
|
-
|
25
13
|
end
|
26
14
|
|
27
15
|
end
|
data/lib/foreman/export/runit.rb
CHANGED
@@ -2,58 +2,33 @@ require "erb"
|
|
2
2
|
require "foreman/export"
|
3
3
|
|
4
4
|
class Foreman::Export::Runit < Foreman::Export::Base
|
5
|
+
|
5
6
|
ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
|
6
7
|
|
7
8
|
def export
|
8
|
-
|
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
|
9
|
+
super
|
14
10
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
engine.procfile.entries.each do |process|
|
19
|
-
1.upto(self.concurrency[process.name]) do |num|
|
20
|
-
process_directory = "#{location}/#{app}-#{process.name}-#{num}"
|
21
|
-
process_env_directory = "#{process_directory}/env"
|
22
|
-
process_log_directory = "#{process_directory}/log"
|
11
|
+
engine.each_process do |name, process|
|
12
|
+
1.upto(engine.formation[name]) do |num|
|
13
|
+
process_directory = "#{app}-#{name}-#{num}"
|
23
14
|
|
24
15
|
create_directory process_directory
|
25
|
-
create_directory
|
26
|
-
create_directory
|
27
|
-
|
28
|
-
run = ERB.new(run_template).result(binding)
|
29
|
-
write_file "#{process_directory}/run", run
|
30
|
-
FileUtils.chmod 0755, "#{process_directory}/run"
|
16
|
+
create_directory "#{process_directory}/env"
|
17
|
+
create_directory "#{process_directory}/log"
|
31
18
|
|
32
|
-
|
33
|
-
|
34
|
-
merge(engine.environment).
|
35
|
-
merge(inline_variables(process.command))
|
19
|
+
write_template "runit/run.erb", "#{process_directory}/run", binding
|
20
|
+
chmod 0755, "#{process_directory}/run"
|
36
21
|
|
37
|
-
|
38
|
-
|
22
|
+
port = engine.port_for(process, num)
|
23
|
+
engine.env.merge("PORT" => port.to_s).each do |key, value|
|
24
|
+
write_file "#{process_directory}/env/#{key}", value
|
39
25
|
end
|
40
26
|
|
41
|
-
|
42
|
-
|
43
|
-
FileUtils.chmod 0755, "#{process_log_directory}/run"
|
27
|
+
write_template "runit/log/run.erb", "#{process_directory}/log/run", binding
|
28
|
+
chmod 0755, "#{process_directory}/log/run"
|
44
29
|
end
|
45
30
|
end
|
46
31
|
|
47
32
|
end
|
48
33
|
|
49
|
-
private
|
50
|
-
def create_directory(location)
|
51
|
-
say "creating: #{location}"
|
52
|
-
FileUtils.mkdir_p(location)
|
53
|
-
end
|
54
|
-
|
55
|
-
def inline_variables(command)
|
56
|
-
variable_name_regex =
|
57
|
-
Hash[*command.scan(ENV_VARIABLE_REGEX).flatten]
|
58
|
-
end
|
59
34
|
end
|
@@ -4,23 +4,13 @@ require "foreman/export"
|
|
4
4
|
class Foreman::Export::Supervisord < Foreman::Export::Base
|
5
5
|
|
6
6
|
def export
|
7
|
-
|
8
|
-
|
9
|
-
FileUtils.mkdir_p location
|
10
|
-
|
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
|
7
|
+
super
|
15
8
|
|
16
9
|
Dir["#{location}/#{app}*.conf"].each do |file|
|
17
|
-
|
18
|
-
FileUtils.rm(file)
|
10
|
+
clean file
|
19
11
|
end
|
20
12
|
|
21
|
-
|
22
|
-
app_config = ERB.new(app_template, 0, '<').result(binding)
|
23
|
-
write_file "#{location}/#{app}.conf", app_config
|
13
|
+
write_template "supervisord/app.conf.erb", "#{app}.conf", binding
|
24
14
|
end
|
25
15
|
|
26
16
|
end
|