foreman 0.27.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 +36 -0
- data/data/export/bluepill/master.pill.erb +3 -3
- data/lib/foreman/cli.rb +34 -32
- data/lib/foreman/engine.rb +97 -78
- 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 +13 -1
- data/man/foreman.1 +5 -1
- data/spec/foreman/cli_spec.rb +77 -7
- data/spec/foreman/engine_spec.rb +13 -26
- 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 +8 -7
- data/spec/helper_spec.rb +18 -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 +31 -3
- metadata +19 -15
- data/README.markdown +0 -49
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
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/bin/sh
|
|
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
|
|
@@ -3,9 +3,9 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
|
|
|
3
3
|
app.uid = "<%= user %>"
|
|
4
4
|
app.gid = "<%= user %>"
|
|
5
5
|
|
|
6
|
-
<% engine.
|
|
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
|
|
|
@@ -19,7 +19,7 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
|
|
|
19
19
|
process.monitor_children do |children|
|
|
20
20
|
children.stop_command "kill -QUIT {{PID}}"
|
|
21
21
|
end
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
process.group = "<%= app %>-<%= process.name %>"
|
|
24
24
|
end
|
|
25
25
|
<% end %>
|
data/lib/foreman/cli.rb
CHANGED
|
@@ -1,29 +1,37 @@
|
|
|
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
|
|
8
10
|
|
|
9
11
|
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
|
|
10
12
|
|
|
11
|
-
desc "start
|
|
13
|
+
desc "start", "Start the application"
|
|
14
|
+
|
|
15
|
+
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
|
|
16
|
+
class_option :app_root, :type => :string, :aliases => "-d", :desc => "Default: Procfile directory"
|
|
12
17
|
|
|
13
18
|
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
|
|
14
19
|
method_option :port, :type => :numeric, :aliases => "-p"
|
|
15
20
|
method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
else
|
|
23
|
-
engine.start
|
|
22
|
+
class << self
|
|
23
|
+
# Hackery. Take the run method away from Thor so that we can redefine it.
|
|
24
|
+
def is_thor_reserved_word?(word, type)
|
|
25
|
+
return false if word == 'run'
|
|
26
|
+
super
|
|
24
27
|
end
|
|
25
28
|
end
|
|
26
29
|
|
|
30
|
+
def start
|
|
31
|
+
check_procfile!
|
|
32
|
+
engine.start
|
|
33
|
+
end
|
|
34
|
+
|
|
27
35
|
desc "export FORMAT LOCATION", "Export the application to another process management format"
|
|
28
36
|
|
|
29
37
|
method_option :app, :type => :string, :aliases => "-a"
|
|
@@ -32,22 +40,12 @@ class Foreman::CLI < Thor
|
|
|
32
40
|
method_option :port, :type => :numeric, :aliases => "-p"
|
|
33
41
|
method_option :user, :type => :string, :aliases => "-u"
|
|
34
42
|
method_option :template, :type => :string, :aliases => "-t"
|
|
35
|
-
method_option :concurrency, :type => :string, :aliases => "-c",
|
|
36
|
-
:banner => '"alpha=5,bar=3"'
|
|
43
|
+
method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
|
|
37
44
|
|
|
38
45
|
def export(format, location=nil)
|
|
39
46
|
check_procfile!
|
|
40
|
-
|
|
41
|
-
formatter
|
|
42
|
-
when "inittab" then Foreman::Export::Inittab
|
|
43
|
-
when "upstart" then Foreman::Export::Upstart
|
|
44
|
-
when "bluepill" then Foreman::Export::Bluepill
|
|
45
|
-
when "runit" then Foreman::Export::Runit
|
|
46
|
-
else error "Unknown export format: #{format}."
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
formatter.new(engine).export(location, options)
|
|
50
|
-
|
|
47
|
+
formatter = Foreman::Export.formatter(format)
|
|
48
|
+
formatter.new(location, engine, options).export
|
|
51
49
|
rescue Foreman::Export::Exception => ex
|
|
52
50
|
error ex.message
|
|
53
51
|
end
|
|
@@ -55,8 +53,21 @@ class Foreman::CLI < Thor
|
|
|
55
53
|
desc "check", "Validate your application's Procfile"
|
|
56
54
|
|
|
57
55
|
def check
|
|
58
|
-
error "no processes defined" unless engine.
|
|
59
|
-
|
|
56
|
+
error "no processes defined" unless engine.procfile.entries.length > 0
|
|
57
|
+
puts "valid procfile detected (#{engine.procfile.process_names.join(', ')})"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
desc "run COMMAND", "Run a command using your application's environment"
|
|
61
|
+
|
|
62
|
+
def run(*args)
|
|
63
|
+
engine.apply_environment!
|
|
64
|
+
begin
|
|
65
|
+
exec args.join(" ")
|
|
66
|
+
rescue Errno::EACCES
|
|
67
|
+
error "not executable: #{args.first}"
|
|
68
|
+
rescue Errno::ENOENT
|
|
69
|
+
error "command not found: #{args.first}"
|
|
70
|
+
end
|
|
60
71
|
end
|
|
61
72
|
|
|
62
73
|
private ######################################################################
|
|
@@ -73,24 +84,15 @@ private ######################################################################
|
|
|
73
84
|
options[:procfile] || "Procfile"
|
|
74
85
|
end
|
|
75
86
|
|
|
76
|
-
def display(message)
|
|
77
|
-
puts message
|
|
78
|
-
end
|
|
79
|
-
|
|
80
87
|
def error(message)
|
|
81
88
|
puts "ERROR: #{message}"
|
|
82
89
|
exit 1
|
|
83
90
|
end
|
|
84
91
|
|
|
85
|
-
def procfile_exists?(procfile)
|
|
86
|
-
File.exist?(procfile)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
92
|
def options
|
|
90
93
|
original_options = super
|
|
91
94
|
return original_options unless File.exists?(".foreman")
|
|
92
95
|
defaults = YAML::load_file(".foreman") || {}
|
|
93
96
|
Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options))
|
|
94
97
|
end
|
|
95
|
-
|
|
96
98
|
end
|
data/lib/foreman/engine.rb
CHANGED
|
@@ -2,11 +2,11 @@ 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"
|
|
9
8
|
require "fileutils"
|
|
9
|
+
require "thread"
|
|
10
10
|
|
|
11
11
|
class Foreman::Engine
|
|
12
12
|
|
|
@@ -16,13 +16,16 @@ class Foreman::Engine
|
|
|
16
16
|
|
|
17
17
|
extend Term::ANSIColor
|
|
18
18
|
|
|
19
|
-
COLORS = [ cyan, yellow, green, magenta, red
|
|
19
|
+
COLORS = [ cyan, yellow, green, magenta, red, blue,
|
|
20
|
+
intense_cyan, intense_yellow, intense_green, intense_magenta,
|
|
21
|
+
intense_red, intense_blue ]
|
|
20
22
|
|
|
21
23
|
def initialize(procfile, options={})
|
|
22
24
|
@procfile = Foreman::Procfile.new(procfile)
|
|
23
|
-
@directory = File.expand_path(File.dirname(procfile))
|
|
25
|
+
@directory = options[:app_root] || File.expand_path(File.dirname(procfile))
|
|
24
26
|
@options = options
|
|
25
27
|
@environment = read_environment_files(options[:env])
|
|
28
|
+
@output_mutex = Mutex.new
|
|
26
29
|
end
|
|
27
30
|
|
|
28
31
|
def self.load_env!(env_file)
|
|
@@ -32,34 +35,17 @@ class Foreman::Engine
|
|
|
32
35
|
|
|
33
36
|
def start
|
|
34
37
|
proctitle "ruby: foreman master"
|
|
35
|
-
termtitle "#{File.basename(@directory)} - foreman
|
|
36
|
-
|
|
37
|
-
processes.each do |process|
|
|
38
|
-
process.color = next_color
|
|
39
|
-
fork process
|
|
40
|
-
end
|
|
38
|
+
termtitle "#{File.basename(@directory)} - foreman"
|
|
41
39
|
|
|
42
40
|
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
|
43
41
|
trap("INT") { puts "SIGINT received"; terminate_gracefully }
|
|
44
42
|
|
|
43
|
+
assign_colors
|
|
44
|
+
spawn_processes
|
|
45
|
+
watch_for_output
|
|
45
46
|
watch_for_termination
|
|
46
47
|
end
|
|
47
48
|
|
|
48
|
-
def execute(name)
|
|
49
|
-
error "no such process: #{name}" unless process = procfile[name]
|
|
50
|
-
process.color = next_color
|
|
51
|
-
fork process
|
|
52
|
-
|
|
53
|
-
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
|
54
|
-
trap("INT") { puts "SIGINT received"; terminate_gracefully }
|
|
55
|
-
|
|
56
|
-
watch_for_termination
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def processes
|
|
60
|
-
procfile.processes
|
|
61
|
-
end
|
|
62
|
-
|
|
63
49
|
def port_for(process, num, base_port=nil)
|
|
64
50
|
base_port ||= 5000
|
|
65
51
|
offset = procfile.process_names.index(process.name) * 100
|
|
@@ -68,84 +54,94 @@ class Foreman::Engine
|
|
|
68
54
|
|
|
69
55
|
private ######################################################################
|
|
70
56
|
|
|
71
|
-
def
|
|
57
|
+
def spawn_processes
|
|
72
58
|
concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency])
|
|
73
59
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
apply_environment!
|
|
81
|
-
|
|
82
|
-
ENV["PORT"] = port.to_s
|
|
83
|
-
ENV["PS"] = "#{process.name}.#{num}"
|
|
84
|
-
|
|
85
|
-
pid = Process.fork do
|
|
86
|
-
run(process)
|
|
60
|
+
procfile.entries.each do |entry|
|
|
61
|
+
reader, writer = IO.pipe
|
|
62
|
+
entry.spawn(concurrency[entry.name], writer, @directory, @environment, port_for(entry, 1, base_port)).each do |process|
|
|
63
|
+
running_processes[process.pid] = process
|
|
64
|
+
readers[process] = reader
|
|
65
|
+
end
|
|
87
66
|
end
|
|
88
|
-
|
|
89
|
-
info "started with pid #{pid}", process
|
|
90
|
-
running_processes[pid] = process
|
|
91
67
|
end
|
|
92
68
|
|
|
93
|
-
def
|
|
94
|
-
|
|
95
|
-
trap("SIGINT", "IGNORE")
|
|
96
|
-
|
|
97
|
-
begin
|
|
98
|
-
Dir.chdir directory do
|
|
99
|
-
PTY.spawn(process.command) do |stdin, stdout, pid|
|
|
100
|
-
trap("SIGTERM") { Process.kill("SIGTERM", pid) }
|
|
101
|
-
until stdin.eof?
|
|
102
|
-
info stdin.gets, process
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
rescue PTY::ChildExited, Interrupt, Errno::EIO, Errno::ENOENT
|
|
107
|
-
begin
|
|
108
|
-
info "process exiting", process
|
|
109
|
-
rescue Interrupt
|
|
110
|
-
end
|
|
111
|
-
end
|
|
69
|
+
def base_port
|
|
70
|
+
options[:port] || 5000
|
|
112
71
|
end
|
|
113
72
|
|
|
114
73
|
def kill_all(signal="SIGTERM")
|
|
115
74
|
running_processes.each do |pid, process|
|
|
116
|
-
|
|
75
|
+
info "sending #{signal} to pid #{pid}"
|
|
76
|
+
process.kill signal
|
|
117
77
|
end
|
|
118
78
|
end
|
|
119
79
|
|
|
120
80
|
def terminate_gracefully
|
|
81
|
+
return if @terminating
|
|
82
|
+
@terminating = true
|
|
121
83
|
info "sending SIGTERM to all processes"
|
|
122
84
|
kill_all "SIGTERM"
|
|
123
|
-
Timeout.timeout(
|
|
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
|
|
124
92
|
rescue Timeout::Error
|
|
125
93
|
info "sending SIGKILL to all processes"
|
|
126
94
|
kill_all "SIGKILL"
|
|
127
95
|
end
|
|
128
96
|
|
|
97
|
+
def watch_for_output
|
|
98
|
+
Thread.new do
|
|
99
|
+
require "win32console" if Foreman.windows?
|
|
100
|
+
begin
|
|
101
|
+
loop do
|
|
102
|
+
rs, ws = IO.select(readers.values, [], [], 1)
|
|
103
|
+
(rs || []).each do |r|
|
|
104
|
+
data = r.gets
|
|
105
|
+
next unless data
|
|
106
|
+
ps, message = data.split(",", 2)
|
|
107
|
+
color = colors[ps.split(".").first]
|
|
108
|
+
info message, ps, color
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
rescue Exception => ex
|
|
112
|
+
puts ex.message
|
|
113
|
+
puts ex.backtrace
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
129
118
|
def watch_for_termination
|
|
130
119
|
pid, status = Process.wait2
|
|
131
120
|
process = running_processes.delete(pid)
|
|
132
|
-
info "process terminated", process
|
|
121
|
+
info "process terminated", process.name
|
|
133
122
|
terminate_gracefully
|
|
134
|
-
kill_all
|
|
135
123
|
rescue Errno::ECHILD
|
|
136
124
|
end
|
|
137
125
|
|
|
138
|
-
def info(message,
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
126
|
+
def info(message, name="system", color=Term::ANSIColor.white)
|
|
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
|
|
144
133
|
end
|
|
145
134
|
|
|
146
|
-
def
|
|
147
|
-
|
|
148
|
-
|
|
135
|
+
def print(message=nil)
|
|
136
|
+
@output_mutex.synchronize do
|
|
137
|
+
$stdout.print message
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def puts(message=nil)
|
|
142
|
+
@output_mutex.synchronize do
|
|
143
|
+
$stdout.puts message
|
|
144
|
+
end
|
|
149
145
|
end
|
|
150
146
|
|
|
151
147
|
def longest_process_name
|
|
@@ -156,9 +152,8 @@ private ######################################################################
|
|
|
156
152
|
end
|
|
157
153
|
end
|
|
158
154
|
|
|
159
|
-
def pad_process_name(
|
|
160
|
-
name
|
|
161
|
-
name.ljust(longest_process_name + 3) # add 3 for process number padding
|
|
155
|
+
def pad_process_name(name="system")
|
|
156
|
+
name.to_s.ljust(longest_process_name + 3) # add 3 for process number padding
|
|
162
157
|
end
|
|
163
158
|
|
|
164
159
|
def proctitle(title)
|
|
@@ -166,17 +161,36 @@ private ######################################################################
|
|
|
166
161
|
end
|
|
167
162
|
|
|
168
163
|
def termtitle(title)
|
|
169
|
-
printf("\033]0;#{title}\007")
|
|
164
|
+
printf("\033]0;#{title}\007") unless Foreman.windows?
|
|
170
165
|
end
|
|
171
166
|
|
|
172
167
|
def running_processes
|
|
173
168
|
@running_processes ||= {}
|
|
174
169
|
end
|
|
175
170
|
|
|
171
|
+
def readers
|
|
172
|
+
@readers ||= {}
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def colors
|
|
176
|
+
@colors ||= {}
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def assign_colors
|
|
180
|
+
procfile.entries.each do |entry|
|
|
181
|
+
colors[entry.name] = next_color
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def process_by_reader(reader)
|
|
186
|
+
readers.invert[reader]
|
|
187
|
+
end
|
|
188
|
+
|
|
176
189
|
def next_color
|
|
177
190
|
@current_color ||= -1
|
|
178
191
|
@current_color += 1
|
|
179
|
-
@current_color
|
|
192
|
+
@current_color = 0 if COLORS.length < @current_color
|
|
193
|
+
COLORS[@current_color]
|
|
180
194
|
end
|
|
181
195
|
|
|
182
196
|
module Env
|
|
@@ -208,6 +222,11 @@ private ######################################################################
|
|
|
208
222
|
def apply_environment!
|
|
209
223
|
@environment.each { |k,v| ENV[k] = v }
|
|
210
224
|
end
|
|
225
|
+
|
|
226
|
+
def error(message)
|
|
227
|
+
puts "ERROR: #{message}"
|
|
228
|
+
exit 1
|
|
229
|
+
end
|
|
211
230
|
end
|
|
212
231
|
|
|
213
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
|
|
@@ -26,7 +33,7 @@ private ######################################################################
|
|
|
26
33
|
def export_template(exporter, file, template_root)
|
|
27
34
|
if template_root && File.exist?(file_path = File.join(template_root, file))
|
|
28
35
|
File.read(file_path)
|
|
29
|
-
elsif File.exist?(file_path = File.join("~/.foreman/templates", file))
|
|
36
|
+
elsif File.exist?(file_path = File.expand_path(File.join("~/.foreman/templates", file)))
|
|
30
37
|
File.read(file_path)
|
|
31
38
|
else
|
|
32
39
|
File.read(File.expand_path("../../../../data/export/#{exporter}/#{file}", __FILE__))
|
|
@@ -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
|
-
engine.
|
|
16
|
-
1.upto(concurrency[process.name]) do |num|
|
|
13
|
+
engine.procfile.entries.inject(1) do |index, process|
|
|
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
|
@@ -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
|