foreman 0.23.1 → 0.24.0
Sign up to get free protection for your applications and to get access to all the features.
- data/data/example/Procfile +1 -1
- data/data/export/bluepill/master.pill.erb +2 -2
- data/lib/foreman/cli.rb +2 -3
- data/lib/foreman/engine.rb +29 -72
- data/lib/foreman/export/upstart.rb +1 -1
- data/lib/foreman/procfile.rb +37 -0
- data/lib/foreman/version.rb +1 -1
- data/spec/foreman/engine_spec.rb +8 -22
- metadata +8 -7
data/data/example/Procfile
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
ticker: ruby ./ticker $PORT
|
2
|
-
error
|
2
|
+
error: ruby ./error
|
@@ -3,7 +3,7 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
|
|
3
3
|
app.uid = "<%= user %>"
|
4
4
|
app.gid = "<%= user %>"
|
5
5
|
|
6
|
-
<% engine.processes.
|
6
|
+
<% engine.processes.each do |process| %>
|
7
7
|
<% 1.upto(concurrency[process.name]) do |num| %>
|
8
8
|
<% port = engine.port_for(process, num, options[:port]) %>
|
9
9
|
app.process("<%= process.name %>-<%=num%>") do |process|
|
@@ -24,4 +24,4 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
|
|
24
24
|
end
|
25
25
|
<% end %>
|
26
26
|
<% end %>
|
27
|
-
end
|
27
|
+
end
|
data/lib/foreman/cli.rb
CHANGED
@@ -53,9 +53,8 @@ class Foreman::CLI < Thor
|
|
53
53
|
desc "check", "Validate your application's Procfile"
|
54
54
|
|
55
55
|
def check
|
56
|
-
processes
|
57
|
-
|
58
|
-
display "valid procfile detected (#{processes.join(', ')})"
|
56
|
+
error "no processes defined" unless engine.processes.length > 0
|
57
|
+
display "valid procfile detected (#{engine.processes.map(&:name).join(', ')})"
|
59
58
|
end
|
60
59
|
|
61
60
|
private ######################################################################
|
data/lib/foreman/engine.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "foreman"
|
2
2
|
require "foreman/process"
|
3
|
+
require "foreman/procfile"
|
3
4
|
require "foreman/utils"
|
4
5
|
require "pty"
|
5
6
|
require "tempfile"
|
@@ -19,45 +20,17 @@ class Foreman::Engine
|
|
19
20
|
COLORS = [ cyan, yellow, green, magenta, red ]
|
20
21
|
|
21
22
|
def initialize(procfile, options={})
|
22
|
-
@procfile =
|
23
|
+
@procfile = Foreman::Procfile.new(procfile)
|
23
24
|
@directory = File.expand_path(File.dirname(procfile))
|
24
25
|
@options = options
|
25
26
|
@environment = read_environment_files(options[:env])
|
26
27
|
end
|
27
28
|
|
28
|
-
def processes
|
29
|
-
@processes ||= begin
|
30
|
-
@order = []
|
31
|
-
procfile.split("\n").inject({}) do |hash, line|
|
32
|
-
next hash if line.strip == ""
|
33
|
-
name, command = line.split(/\s*:\s+/, 2)
|
34
|
-
unless command
|
35
|
-
warn_deprecated_procfile!
|
36
|
-
name, command = line.split(/ +/, 2)
|
37
|
-
end
|
38
|
-
process = Foreman::Process.new(name, command)
|
39
|
-
process.color = next_color
|
40
|
-
@order << process.name
|
41
|
-
hash.update(process.name => process)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def process_order
|
47
|
-
processes
|
48
|
-
@order.uniq
|
49
|
-
end
|
50
|
-
|
51
|
-
def processes_in_order
|
52
|
-
process_order.map do |name|
|
53
|
-
[name, processes[name]]
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
29
|
def start
|
58
30
|
proctitle "ruby: foreman master"
|
59
31
|
|
60
|
-
|
32
|
+
processes.each do |process|
|
33
|
+
process.color = next_color
|
61
34
|
fork process
|
62
35
|
end
|
63
36
|
|
@@ -68,8 +41,7 @@ class Foreman::Engine
|
|
68
41
|
end
|
69
42
|
|
70
43
|
def execute(name)
|
71
|
-
|
72
|
-
fork processes[name]
|
44
|
+
fork procfile[name]
|
73
45
|
|
74
46
|
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
75
47
|
trap("INT") { puts "SIGINT received"; terminate_gracefully }
|
@@ -77,9 +49,13 @@ class Foreman::Engine
|
|
77
49
|
watch_for_termination
|
78
50
|
end
|
79
51
|
|
52
|
+
def processes
|
53
|
+
procfile.processes
|
54
|
+
end
|
55
|
+
|
80
56
|
def port_for(process, num, base_port=nil)
|
81
57
|
base_port ||= 5000
|
82
|
-
offset =
|
58
|
+
offset = procfile.process_names.index(process.name) * 100
|
83
59
|
base_port.to_i + offset + num - 1
|
84
60
|
end
|
85
61
|
|
@@ -134,6 +110,24 @@ private ######################################################################
|
|
134
110
|
end
|
135
111
|
end
|
136
112
|
|
113
|
+
def terminate_gracefully
|
114
|
+
info "sending SIGTERM to all processes"
|
115
|
+
kill_all "SIGTERM"
|
116
|
+
Timeout.timeout(3) { Process.waitall }
|
117
|
+
rescue Timeout::Error
|
118
|
+
info "sending SIGKILL to all processes"
|
119
|
+
kill_all "SIGKILL"
|
120
|
+
end
|
121
|
+
|
122
|
+
def watch_for_termination
|
123
|
+
pid, status = Process.wait2
|
124
|
+
process = running_processes.delete(pid)
|
125
|
+
info "process terminated", process
|
126
|
+
terminate_gracefully
|
127
|
+
kill_all
|
128
|
+
rescue Errno::ECHILD
|
129
|
+
end
|
130
|
+
|
137
131
|
def info(message, process=nil)
|
138
132
|
print process.color if process
|
139
133
|
print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(process)} | "
|
@@ -149,7 +143,7 @@ private ######################################################################
|
|
149
143
|
|
150
144
|
def longest_process_name
|
151
145
|
@longest_process_name ||= begin
|
152
|
-
longest =
|
146
|
+
longest = procfile.process_names.map { |name| name.length }.sort.last
|
153
147
|
longest = 6 if longest < 6 # system
|
154
148
|
longest
|
155
149
|
end
|
@@ -160,30 +154,10 @@ private ######################################################################
|
|
160
154
|
name.ljust(longest_process_name + 3) # add 3 for process number padding
|
161
155
|
end
|
162
156
|
|
163
|
-
def print_info
|
164
|
-
info "currently running processes:"
|
165
|
-
running_processes.each do |pid, process|
|
166
|
-
info "pid #{pid}", process
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
157
|
def proctitle(title)
|
171
158
|
$0 = title
|
172
159
|
end
|
173
160
|
|
174
|
-
def read_procfile(procfile)
|
175
|
-
File.read(procfile)
|
176
|
-
end
|
177
|
-
|
178
|
-
def watch_for_termination
|
179
|
-
pid, status = Process.wait2
|
180
|
-
process = running_processes.delete(pid)
|
181
|
-
info "process terminated", process
|
182
|
-
terminate_gracefully
|
183
|
-
kill_all
|
184
|
-
rescue Errno::ECHILD
|
185
|
-
end
|
186
|
-
|
187
161
|
def running_processes
|
188
162
|
@running_processes ||= {}
|
189
163
|
end
|
@@ -194,14 +168,6 @@ private ######################################################################
|
|
194
168
|
@current_color >= COLORS.length ? "" : COLORS[@current_color]
|
195
169
|
end
|
196
170
|
|
197
|
-
def warn_deprecated_procfile!
|
198
|
-
return if @already_warned_deprecated
|
199
|
-
@already_warned_deprecated = true
|
200
|
-
puts "!!! This format of Procfile is deprecated, and will not work starting in v0.12"
|
201
|
-
puts "!!! Use a colon to separate the process name from the command"
|
202
|
-
puts "!!! e.g. web: thin start"
|
203
|
-
end
|
204
|
-
|
205
171
|
def read_environment_files(filenames)
|
206
172
|
environment = {}
|
207
173
|
|
@@ -225,13 +191,4 @@ private ######################################################################
|
|
225
191
|
end
|
226
192
|
end
|
227
193
|
|
228
|
-
def terminate_gracefully
|
229
|
-
info "sending SIGTERM to all processes"
|
230
|
-
kill_all "SIGTERM"
|
231
|
-
Timeout.timeout(3) { Process.waitall }
|
232
|
-
rescue Timeout::Error
|
233
|
-
info "sending SIGKILL to all processes"
|
234
|
-
kill_all "SIGKILL"
|
235
|
-
end
|
236
|
-
|
237
194
|
end
|
@@ -26,7 +26,7 @@ class Foreman::Export::Upstart < Foreman::Export::Base
|
|
26
26
|
|
27
27
|
process_template = export_template("upstart", "process.conf.erb", template_root)
|
28
28
|
|
29
|
-
engine.processes.
|
29
|
+
engine.processes.each do |process|
|
30
30
|
process_master_template = export_template("upstart", "process_master.conf.erb", template_root)
|
31
31
|
process_master_config = ERB.new(process_master_template).result(binding)
|
32
32
|
write_file "#{location}/#{app}-#{process.name}.conf", process_master_config
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "foreman"
|
2
|
+
|
3
|
+
# A valid Procfile entry is captured by this regex.
|
4
|
+
# All other lines are ignored.
|
5
|
+
#
|
6
|
+
# /^([A-Za-z0-9_]+):\s*(.+)$/
|
7
|
+
#
|
8
|
+
# $1 = name
|
9
|
+
# $2 = command
|
10
|
+
#
|
11
|
+
class Foreman::Procfile
|
12
|
+
|
13
|
+
attr_reader :processes
|
14
|
+
|
15
|
+
def initialize(filename)
|
16
|
+
@processes = parse_procfile(filename)
|
17
|
+
end
|
18
|
+
|
19
|
+
def process_names
|
20
|
+
processes.map(&:name)
|
21
|
+
end
|
22
|
+
|
23
|
+
def [](name)
|
24
|
+
processes.detect { |process| process.name == name }
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def parse_procfile(filename)
|
30
|
+
File.read(filename).split("\n").map do |line|
|
31
|
+
if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
|
32
|
+
Foreman::Process.new($1, $2)
|
33
|
+
end
|
34
|
+
end.compact
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
data/lib/foreman/version.rb
CHANGED
data/spec/foreman/engine_spec.rb
CHANGED
@@ -15,21 +15,8 @@ describe "Foreman::Engine" do
|
|
15
15
|
before { write_procfile }
|
16
16
|
|
17
17
|
it "reads the processes" do
|
18
|
-
subject.
|
19
|
-
subject.
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
describe "with a deprecated Procfile" do
|
24
|
-
before do
|
25
|
-
File.open("Procfile", "w") do |file|
|
26
|
-
file.puts "name command"
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
it "should print a deprecation warning" do
|
31
|
-
mock(subject).warn_deprecated_procfile!
|
32
|
-
subject.processes.length.should == 1
|
18
|
+
subject.procfile["alpha"].command.should == "./alpha"
|
19
|
+
subject.procfile["bravo"].command.should == "./bravo"
|
33
20
|
end
|
34
21
|
end
|
35
22
|
end
|
@@ -37,8 +24,8 @@ describe "Foreman::Engine" do
|
|
37
24
|
describe "start" do
|
38
25
|
it "forks the processes" do
|
39
26
|
write_procfile
|
40
|
-
mock(subject).fork(subject.
|
41
|
-
mock(subject).fork(subject.
|
27
|
+
mock(subject).fork(subject.procfile["alpha"])
|
28
|
+
mock(subject).fork(subject.procfile["bravo"])
|
42
29
|
mock(subject).watch_for_termination
|
43
30
|
subject.start
|
44
31
|
end
|
@@ -46,9 +33,9 @@ describe "Foreman::Engine" do
|
|
46
33
|
it "handles concurrency" do
|
47
34
|
write_procfile
|
48
35
|
engine = Foreman::Engine.new("Procfile",:concurrency => "alpha=2")
|
49
|
-
mock(engine).fork_individual(engine.
|
50
|
-
mock(engine).fork_individual(engine.
|
51
|
-
mock(engine).fork_individual(engine.
|
36
|
+
mock(engine).fork_individual(engine.procfile["alpha"], 1, 5000)
|
37
|
+
mock(engine).fork_individual(engine.procfile["alpha"], 2, 5001)
|
38
|
+
mock(engine).fork_individual(engine.procfile["bravo"], 1, 5100)
|
52
39
|
mock(engine).watch_for_termination
|
53
40
|
engine.start
|
54
41
|
end
|
@@ -57,14 +44,13 @@ describe "Foreman::Engine" do
|
|
57
44
|
describe "execute" do
|
58
45
|
it "runs the processes" do
|
59
46
|
write_procfile
|
60
|
-
mock(subject).fork(subject.
|
47
|
+
mock(subject).fork(subject.procfile["alpha"])
|
61
48
|
mock(subject).watch_for_termination
|
62
49
|
subject.execute("alpha")
|
63
50
|
end
|
64
51
|
end
|
65
52
|
|
66
53
|
describe "environment" do
|
67
|
-
|
68
54
|
before(:each) do
|
69
55
|
write_procfile
|
70
56
|
stub(Process).fork
|
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.
|
4
|
+
version: 0.24.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2011-10-04 00:00:00.000000000Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: term-ansicolor
|
16
|
-
requirement: &
|
16
|
+
requirement: &70155363724260 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 1.0.5
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70155363724260
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: thor
|
27
|
-
requirement: &
|
27
|
+
requirement: &70155363722660 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: 0.13.6
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70155363722660
|
36
36
|
description: Process manager for applications with multiple components
|
37
37
|
email: ddollar@gmail.com
|
38
38
|
executables:
|
@@ -59,6 +59,7 @@ files:
|
|
59
59
|
- lib/foreman/export/upstart.rb
|
60
60
|
- lib/foreman/export.rb
|
61
61
|
- lib/foreman/process.rb
|
62
|
+
- lib/foreman/procfile.rb
|
62
63
|
- lib/foreman/utils.rb
|
63
64
|
- lib/foreman/version.rb
|
64
65
|
- lib/foreman.rb
|
@@ -93,7 +94,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
94
|
version: '0'
|
94
95
|
segments:
|
95
96
|
- 0
|
96
|
-
hash:
|
97
|
+
hash: 3975386254476166563
|
97
98
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
99
|
none: false
|
99
100
|
requirements:
|
@@ -102,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
103
|
version: '0'
|
103
104
|
segments:
|
104
105
|
- 0
|
105
|
-
hash:
|
106
|
+
hash: 3975386254476166563
|
106
107
|
requirements: []
|
107
108
|
rubyforge_project:
|
108
109
|
rubygems_version: 1.8.10
|