foreman 0.23.1 → 0.24.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/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
|