foreman 0.31.0 → 0.50.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 +46 -0
- data/bin/foreman-runner +32 -0
- data/bin/taskman +8 -0
- data/data/example/Procfile +4 -2
- data/data/example/spawnee +14 -0
- data/data/example/spawner +7 -0
- data/data/example/utf8 +11 -0
- data/data/export/bluepill/master.pill.erb +11 -10
- data/data/export/launchd/launchd.plist.erb +22 -0
- data/data/export/runit/log/run.erb +7 -0
- data/data/export/runit/run.erb +2 -2
- data/data/export/supervisord/app.conf.erb +27 -0
- data/data/export/upstart/master.conf.erb +2 -2
- data/data/export/upstart/process.conf.erb +3 -3
- data/lib/foreman/cli.rb +83 -34
- data/lib/foreman/engine/cli.rb +105 -0
- data/lib/foreman/engine.rb +232 -140
- data/lib/foreman/env.rb +27 -0
- data/lib/foreman/export/base.rb +108 -6
- data/lib/foreman/export/bluepill.rb +4 -20
- data/lib/foreman/export/inittab.rb +11 -16
- data/lib/foreman/export/launchd.rb +15 -0
- data/lib/foreman/export/runit.rb +15 -41
- data/lib/foreman/export/supervisord.rb +16 -0
- data/lib/foreman/export/upstart.rb +10 -28
- data/lib/foreman/export.rb +23 -0
- data/lib/foreman/helpers.rb +45 -0
- data/lib/foreman/process.rb +80 -46
- data/lib/foreman/procfile.rb +68 -14
- data/lib/foreman/version.rb +1 -1
- data/lib/foreman.rb +12 -7
- data/man/foreman.1 +29 -3
- data/spec/foreman/cli_spec.rb +54 -60
- data/spec/foreman/engine_spec.rb +64 -46
- data/spec/foreman/export/base_spec.rb +19 -0
- data/spec/foreman/export/bluepill_spec.rb +24 -6
- data/spec/foreman/export/inittab_spec.rb +40 -0
- data/spec/foreman/export/launchd_spec.rb +21 -0
- data/spec/foreman/export/runit_spec.rb +23 -22
- data/spec/foreman/export/supervisord_spec.rb +36 -0
- data/spec/foreman/export/upstart_spec.rb +49 -16
- data/spec/foreman/export_spec.rb +22 -0
- data/spec/foreman/helpers_spec.rb +26 -0
- data/spec/foreman/process_spec.rb +48 -2
- data/spec/foreman/procfile_spec.rb +41 -0
- data/spec/foreman_spec.rb +3 -20
- data/spec/helper_spec.rb +18 -0
- 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/bin/utf8 +2 -0
- data/spec/resources/export/bluepill/app-concurrency.pill +49 -0
- data/spec/resources/export/bluepill/app.pill +6 -25
- data/spec/resources/export/inittab/inittab.concurrency +4 -0
- data/spec/resources/export/inittab/inittab.default +4 -0
- data/spec/resources/export/launchd/launchd-a.default +22 -0
- data/spec/resources/export/launchd/launchd-b.default +22 -0
- data/spec/resources/export/supervisord/app-alpha-1.conf +24 -0
- data/spec/resources/export/supervisord/app-alpha-2.conf +24 -0
- data/spec/spec_helper.rb +93 -10
- metadata +72 -76
- data/README.markdown +0 -49
- data/bin/runner +0 -2
- data/data/export/runit/log_run.erb +0 -7
- data/lib/foreman/procfile_entry.rb +0 -22
- data/lib/foreman/utils.rb +0 -15
- /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/lib/foreman/engine.rb
CHANGED
|
@@ -1,221 +1,313 @@
|
|
|
1
1
|
require "foreman"
|
|
2
|
+
require "foreman/env"
|
|
2
3
|
require "foreman/process"
|
|
3
4
|
require "foreman/procfile"
|
|
4
|
-
require "foreman/utils"
|
|
5
|
-
require "pty"
|
|
6
5
|
require "tempfile"
|
|
7
6
|
require "timeout"
|
|
8
|
-
require "term/ansicolor"
|
|
9
7
|
require "fileutils"
|
|
10
8
|
require "thread"
|
|
11
9
|
|
|
12
10
|
class Foreman::Engine
|
|
13
11
|
|
|
14
|
-
attr_reader :
|
|
15
|
-
attr_reader :directory
|
|
12
|
+
attr_reader :env
|
|
16
13
|
attr_reader :options
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
14
|
+
attr_reader :processes
|
|
15
|
+
|
|
16
|
+
# Create an +Engine+ for running processes
|
|
17
|
+
#
|
|
18
|
+
# @param [Hash] options
|
|
19
|
+
#
|
|
20
|
+
# @option options [String] :formation (all=1) The process formation to use
|
|
21
|
+
# @option options [Fixnum] :port (5000) The base port to assign to processes
|
|
22
|
+
# @option options [String] :root (Dir.pwd) The root directory from which to run processes
|
|
23
|
+
#
|
|
24
|
+
def initialize(options={})
|
|
25
|
+
@options = options.dup
|
|
26
|
+
|
|
27
|
+
@options[:formation] ||= (options[:concurrency] || "all=1")
|
|
28
|
+
|
|
29
|
+
@env = {}
|
|
30
|
+
@mutex = Mutex.new
|
|
31
|
+
@names = {}
|
|
32
|
+
@processes = []
|
|
33
|
+
@running = {}
|
|
34
|
+
@readers = {}
|
|
33
35
|
end
|
|
34
36
|
|
|
37
|
+
# Start the processes registered to this +Engine+
|
|
38
|
+
#
|
|
35
39
|
def start
|
|
36
|
-
proctitle "ruby: foreman master"
|
|
37
|
-
termtitle "#{File.basename(@directory)} - foreman"
|
|
38
|
-
|
|
39
40
|
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
|
40
41
|
trap("INT") { puts "SIGINT received"; terminate_gracefully }
|
|
42
|
+
trap("HUP") { puts "SIGHUP received"; terminate_gracefully } if ::Signal.list.keys.include? 'HUP'
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
startup
|
|
43
45
|
spawn_processes
|
|
44
46
|
watch_for_output
|
|
45
|
-
|
|
47
|
+
sleep 0.1
|
|
48
|
+
watch_for_termination { terminate_gracefully }
|
|
49
|
+
shutdown
|
|
46
50
|
end
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
# Register a process to be run by this +Engine+
|
|
53
|
+
#
|
|
54
|
+
# @param [String] name A name for this process
|
|
55
|
+
# @param [String] command The command to run
|
|
56
|
+
# @param [Hash] options
|
|
57
|
+
#
|
|
58
|
+
# @option options [Hash] :env A custom environment for this process
|
|
59
|
+
#
|
|
60
|
+
def register(name, command, options={})
|
|
61
|
+
options[:env] ||= env
|
|
62
|
+
options[:cwd] ||= File.dirname(command.split(" ").first)
|
|
63
|
+
process = Foreman::Process.new(command, options)
|
|
64
|
+
@names[process] = name
|
|
65
|
+
@processes << process
|
|
52
66
|
end
|
|
53
67
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
procfile.entries.each do |entry|
|
|
60
|
-
reader, writer = IO.pipe
|
|
61
|
-
entry.spawn(concurrency[entry.name], writer, @directory, @environment, port_for(entry, 1, base_port)).each do |process|
|
|
62
|
-
running_processes[process.pid] = process
|
|
63
|
-
readers[process] = reader
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def base_port
|
|
69
|
-
options[:port] || 5000
|
|
68
|
+
# Clear the processes registered to this +Engine+
|
|
69
|
+
#
|
|
70
|
+
def clear
|
|
71
|
+
@names = {}
|
|
72
|
+
@processes = []
|
|
70
73
|
end
|
|
71
74
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
# Register processes by reading a Procfile
|
|
76
|
+
#
|
|
77
|
+
# @param [String] filename A Procfile from which to read processes to register
|
|
78
|
+
#
|
|
79
|
+
def load_procfile(filename)
|
|
80
|
+
options[:root] ||= File.dirname(filename)
|
|
81
|
+
Foreman::Procfile.new(filename).entries do |name, command|
|
|
82
|
+
register name, command, :cwd => options[:root]
|
|
76
83
|
end
|
|
84
|
+
self
|
|
77
85
|
end
|
|
78
86
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
# Load a .env file into the +env+ for this +Engine+
|
|
88
|
+
#
|
|
89
|
+
# @param [String] filename A .env file to load into the environment
|
|
90
|
+
#
|
|
91
|
+
def load_env(filename)
|
|
92
|
+
Foreman::Env.new(filename).entries do |name, value|
|
|
93
|
+
@env[name] = value
|
|
94
|
+
end
|
|
86
95
|
end
|
|
87
96
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
# Send a signal to all processesstarted by this +Engine+
|
|
98
|
+
#
|
|
99
|
+
# @param [String] signal The signal to send to each process
|
|
100
|
+
#
|
|
101
|
+
def killall(signal="SIGTERM")
|
|
102
|
+
if Foreman.windows?
|
|
103
|
+
@running.each do |pid, (process, index)|
|
|
104
|
+
system "sending #{signal} to #{name_for(pid)} at pid #{pid}"
|
|
105
|
+
begin
|
|
106
|
+
Process.kill(signal, pid)
|
|
107
|
+
rescue Errno::ESRCH, Errno::EPERM
|
|
98
108
|
end
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
109
|
+
end
|
|
110
|
+
else
|
|
111
|
+
begin
|
|
112
|
+
Process.kill "-#{signal}", Process.pid
|
|
113
|
+
rescue Errno::ESRCH, Errno::EPERM
|
|
102
114
|
end
|
|
103
115
|
end
|
|
104
116
|
end
|
|
105
117
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
rescue Errno::ECHILD
|
|
118
|
+
# Get the process formation
|
|
119
|
+
#
|
|
120
|
+
# @returns [Fixnum] The formation count for the specified process
|
|
121
|
+
#
|
|
122
|
+
def formation
|
|
123
|
+
@formation ||= parse_formation(options[:formation])
|
|
113
124
|
end
|
|
114
125
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
126
|
+
# List the available process names
|
|
127
|
+
#
|
|
128
|
+
# @returns [Array] A list of process names
|
|
129
|
+
#
|
|
130
|
+
def process_names
|
|
131
|
+
@processes.map { |p| @names[p] }
|
|
121
132
|
end
|
|
122
133
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
134
|
+
# Get the +Process+ for a specifid name
|
|
135
|
+
#
|
|
136
|
+
# @param [String] name The process name
|
|
137
|
+
#
|
|
138
|
+
# @returns [Foreman::Process] The +Process+ for the specified name
|
|
139
|
+
#
|
|
140
|
+
def process(name)
|
|
141
|
+
@names.invert[name]
|
|
127
142
|
end
|
|
128
143
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
144
|
+
# Yield each +Process+ in order
|
|
145
|
+
#
|
|
146
|
+
def each_process
|
|
147
|
+
process_names.each do |name|
|
|
148
|
+
yield name, process(name)
|
|
132
149
|
end
|
|
133
150
|
end
|
|
134
151
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
152
|
+
# Get the root directory for this +Engine+
|
|
153
|
+
#
|
|
154
|
+
# @returns [String] The root directory
|
|
155
|
+
#
|
|
156
|
+
def root
|
|
157
|
+
File.expand_path(options[:root] || Dir.pwd)
|
|
138
158
|
end
|
|
139
159
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
160
|
+
# Get the port for a given process and offset
|
|
161
|
+
#
|
|
162
|
+
# @param [Foreman::Process] process A +Process+ associated with this engine
|
|
163
|
+
# @param [Fixnum] instance The instance of the process
|
|
164
|
+
#
|
|
165
|
+
# @returns [Fixnum] port The port to use for this instance of this process
|
|
166
|
+
#
|
|
167
|
+
def port_for(process, instance, base=nil)
|
|
168
|
+
if base
|
|
169
|
+
base + (@processes.index(process.process) * 100) + (instance - 1)
|
|
170
|
+
else
|
|
171
|
+
base_port + (@processes.index(process) * 100) + (instance - 1)
|
|
145
172
|
end
|
|
146
173
|
end
|
|
147
174
|
|
|
148
|
-
|
|
149
|
-
|
|
175
|
+
# Get the base port for this foreman instance
|
|
176
|
+
#
|
|
177
|
+
# @returns [Fixnum] port The base port
|
|
178
|
+
#
|
|
179
|
+
def base_port
|
|
180
|
+
(options[:port] || env["PORT"] || ENV["PORT"] || 5000).to_i
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# deprecated
|
|
184
|
+
def environment
|
|
185
|
+
env
|
|
150
186
|
end
|
|
151
187
|
|
|
152
|
-
|
|
153
|
-
|
|
188
|
+
private
|
|
189
|
+
|
|
190
|
+
### Engine API ######################################################
|
|
191
|
+
|
|
192
|
+
def startup
|
|
193
|
+
raise TypeError, "must use a subclass of Foreman::Engine"
|
|
154
194
|
end
|
|
155
195
|
|
|
156
|
-
def
|
|
157
|
-
|
|
196
|
+
def output(name, data)
|
|
197
|
+
raise TypeError, "must use a subclass of Foreman::Engine"
|
|
158
198
|
end
|
|
159
199
|
|
|
160
|
-
def
|
|
161
|
-
|
|
200
|
+
def shutdown
|
|
201
|
+
raise TypeError, "must use a subclass of Foreman::Engine"
|
|
162
202
|
end
|
|
163
203
|
|
|
164
|
-
|
|
165
|
-
|
|
204
|
+
## Helpers ##########################################################
|
|
205
|
+
|
|
206
|
+
def create_pipe
|
|
207
|
+
IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
|
|
166
208
|
end
|
|
167
209
|
|
|
168
|
-
def
|
|
169
|
-
|
|
210
|
+
def name_for(pid)
|
|
211
|
+
process, index = @running[pid]
|
|
212
|
+
[ @names[process], index.to_s ].compact.join(".")
|
|
170
213
|
end
|
|
171
214
|
|
|
172
|
-
def
|
|
173
|
-
|
|
174
|
-
|
|
215
|
+
def parse_formation(formation)
|
|
216
|
+
pairs = formation.to_s.gsub(/\s/, "").split(",")
|
|
217
|
+
|
|
218
|
+
pairs.inject(Hash.new(0)) do |ax, pair|
|
|
219
|
+
process, amount = pair.split("=")
|
|
220
|
+
process == "all" ? ax.default = amount.to_i : ax[process] = amount.to_i
|
|
221
|
+
ax
|
|
175
222
|
end
|
|
176
223
|
end
|
|
177
224
|
|
|
178
|
-
def
|
|
179
|
-
|
|
225
|
+
def output_with_mutex(name, message)
|
|
226
|
+
@mutex.synchronize do
|
|
227
|
+
output name, message
|
|
228
|
+
end
|
|
180
229
|
end
|
|
181
230
|
|
|
182
|
-
def
|
|
183
|
-
|
|
184
|
-
@current_color += 1
|
|
185
|
-
@current_color >= COLORS.length ? "" : COLORS[@current_color]
|
|
231
|
+
def system(message)
|
|
232
|
+
output_with_mutex "system", message
|
|
186
233
|
end
|
|
187
234
|
|
|
188
|
-
|
|
189
|
-
|
|
235
|
+
def termination_message_for(status)
|
|
236
|
+
if status.exited?
|
|
237
|
+
"exited with code #{status.exitstatus}"
|
|
238
|
+
elsif status.signaled?
|
|
239
|
+
"terminated by SIG#{Signal.list.invert[status.termsig]}"
|
|
240
|
+
else
|
|
241
|
+
"died a mysterious death"
|
|
242
|
+
end
|
|
243
|
+
end
|
|
190
244
|
|
|
191
|
-
|
|
192
|
-
|
|
245
|
+
def flush_reader(reader)
|
|
246
|
+
until reader.eof?
|
|
247
|
+
data = reader.gets
|
|
248
|
+
output_with_mutex name_for(@readers.key(reader)), data
|
|
249
|
+
end
|
|
250
|
+
end
|
|
193
251
|
|
|
194
|
-
|
|
195
|
-
error "No such file: #{filename}" unless File.exists?(filename)
|
|
196
|
-
environment.merge!(read_environment(filename))
|
|
197
|
-
end
|
|
252
|
+
## Engine ###########################################################
|
|
198
253
|
|
|
199
|
-
|
|
200
|
-
|
|
254
|
+
def spawn_processes
|
|
255
|
+
@processes.each do |process|
|
|
256
|
+
1.upto(formation[@names[process]]) do |n|
|
|
257
|
+
reader, writer = create_pipe
|
|
258
|
+
begin
|
|
259
|
+
pid = process.run(:output => writer, :env => { "PORT" => port_for(process, n).to_s })
|
|
260
|
+
writer.puts "started with pid #{pid}"
|
|
261
|
+
rescue Errno::ENOENT
|
|
262
|
+
writer.puts "unknown command: #{process.command}"
|
|
263
|
+
end
|
|
264
|
+
@running[pid] = [process, n]
|
|
265
|
+
@readers[pid] = reader
|
|
266
|
+
end
|
|
201
267
|
end
|
|
268
|
+
end
|
|
202
269
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
270
|
+
def watch_for_output
|
|
271
|
+
Thread.new do
|
|
272
|
+
begin
|
|
273
|
+
loop do
|
|
274
|
+
(IO.select(@readers.values).first || []).each do |reader|
|
|
275
|
+
data = reader.gets
|
|
276
|
+
output_with_mutex name_for(@readers.invert[reader]), data
|
|
277
|
+
end
|
|
209
278
|
end
|
|
210
|
-
|
|
279
|
+
rescue Exception => ex
|
|
280
|
+
puts ex.message
|
|
281
|
+
puts ex.backtrace
|
|
211
282
|
end
|
|
212
283
|
end
|
|
284
|
+
end
|
|
213
285
|
|
|
214
|
-
|
|
215
|
-
|
|
286
|
+
def watch_for_termination
|
|
287
|
+
pid, status = Process.wait2
|
|
288
|
+
output_with_mutex name_for(pid), termination_message_for(status)
|
|
289
|
+
@running.delete(pid)
|
|
290
|
+
yield if block_given?
|
|
291
|
+
pid
|
|
292
|
+
rescue Errno::ECHILD
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def terminate_gracefully
|
|
296
|
+
return if @terminating
|
|
297
|
+
@terminating = true
|
|
298
|
+
if Foreman.windows?
|
|
299
|
+
system "sending SIGKILL to all processes"
|
|
300
|
+
killall "SIGKILL"
|
|
301
|
+
else
|
|
302
|
+
system "sending SIGTERM to all processes"
|
|
303
|
+
killall "SIGTERM"
|
|
216
304
|
end
|
|
305
|
+
Timeout.timeout(5) do
|
|
306
|
+
watch_for_termination while @running.length > 0
|
|
307
|
+
end
|
|
308
|
+
rescue Timeout::Error
|
|
309
|
+
system "sending SIGKILL to all processes"
|
|
310
|
+
killall "SIGKILL"
|
|
217
311
|
end
|
|
218
312
|
|
|
219
|
-
include Env
|
|
220
|
-
extend Env
|
|
221
313
|
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/base.rb
CHANGED
|
@@ -1,20 +1,81 @@
|
|
|
1
1
|
require "foreman/export"
|
|
2
|
-
require "
|
|
2
|
+
require "ostruct"
|
|
3
|
+
require "pathname"
|
|
4
|
+
require "shellwords"
|
|
3
5
|
|
|
4
6
|
class Foreman::Export::Base
|
|
5
7
|
|
|
8
|
+
attr_reader :location
|
|
6
9
|
attr_reader :engine
|
|
10
|
+
attr_reader :options
|
|
11
|
+
attr_reader :formation
|
|
7
12
|
|
|
8
|
-
|
|
9
|
-
|
|
13
|
+
# deprecated
|
|
14
|
+
attr_reader :port
|
|
15
|
+
|
|
16
|
+
def initialize(location, engine, options={})
|
|
17
|
+
@location = location
|
|
18
|
+
@engine = engine
|
|
19
|
+
@options = options.dup
|
|
20
|
+
@formation = engine.formation
|
|
21
|
+
|
|
22
|
+
# deprecated
|
|
23
|
+
def port
|
|
24
|
+
Foreman::Export::Base.warn_deprecation!
|
|
25
|
+
engine.base_port
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# deprecated
|
|
29
|
+
def template
|
|
30
|
+
Foreman::Export::Base.warn_deprecation!
|
|
31
|
+
options[:template]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# deprecated
|
|
35
|
+
def @engine.procfile
|
|
36
|
+
Foreman::Export::Base.warn_deprecation!
|
|
37
|
+
@processes.map do |process|
|
|
38
|
+
OpenStruct.new(
|
|
39
|
+
:name => @names[process],
|
|
40
|
+
:process => process
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
10
44
|
end
|
|
11
45
|
|
|
12
46
|
def export
|
|
13
|
-
|
|
47
|
+
error("Must specify a location") unless location
|
|
48
|
+
FileUtils.mkdir_p(location) rescue error("Could not create: #{location}")
|
|
49
|
+
FileUtils.mkdir_p(log) rescue error("Could not create: #{log}")
|
|
50
|
+
FileUtils.chown(user, nil, log) rescue error("Could not chown #{log} to #{user}")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def app
|
|
54
|
+
options[:app] || "app"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def log
|
|
58
|
+
options[:log] || "/var/log/#{app}"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def user
|
|
62
|
+
options[:user] || app
|
|
14
63
|
end
|
|
15
64
|
|
|
16
65
|
private ######################################################################
|
|
17
66
|
|
|
67
|
+
def self.warn_deprecation!
|
|
68
|
+
@@deprecation_warned ||= false
|
|
69
|
+
return if @@deprecation_warned
|
|
70
|
+
puts "WARNING: Using deprecated exporter interface. Please update your exporter"
|
|
71
|
+
puts "the interface shown in the upstart exporter:"
|
|
72
|
+
puts
|
|
73
|
+
puts "https://github.com/ddollar/foreman/blob/master/lib/foreman/export/upstart.rb"
|
|
74
|
+
puts "https://github.com/ddollar/foreman/blob/master/data/export/upstart/process.conf.erb"
|
|
75
|
+
puts
|
|
76
|
+
@@deprecation_warned = true
|
|
77
|
+
end
|
|
78
|
+
|
|
18
79
|
def error(message)
|
|
19
80
|
raise Foreman::Export::Exception.new(message)
|
|
20
81
|
end
|
|
@@ -22,20 +83,61 @@ private ######################################################################
|
|
|
22
83
|
def say(message)
|
|
23
84
|
puts "[foreman export] %s" % message
|
|
24
85
|
end
|
|
86
|
+
|
|
87
|
+
def clean(filename)
|
|
88
|
+
return unless File.exists?(filename)
|
|
89
|
+
say "cleaning up: #{filename}"
|
|
90
|
+
FileUtils.rm(filename)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def shell_quote(value)
|
|
94
|
+
'"' + Shellwords.escape(value) + '"'
|
|
95
|
+
end
|
|
25
96
|
|
|
26
|
-
|
|
97
|
+
# deprecated
|
|
98
|
+
def old_export_template(exporter, file, template_root)
|
|
27
99
|
if template_root && File.exist?(file_path = File.join(template_root, file))
|
|
28
100
|
File.read(file_path)
|
|
29
|
-
elsif File.exist?(file_path = File.join("~/.foreman/templates", file))
|
|
101
|
+
elsif File.exist?(file_path = File.expand_path(File.join("~/.foreman/templates", file)))
|
|
30
102
|
File.read(file_path)
|
|
31
103
|
else
|
|
32
104
|
File.read(File.expand_path("../../../../data/export/#{exporter}/#{file}", __FILE__))
|
|
33
105
|
end
|
|
34
106
|
end
|
|
35
107
|
|
|
108
|
+
def export_template(name, file=nil, template_root=nil)
|
|
109
|
+
if file && template_root
|
|
110
|
+
old_export_template name, file, template_root
|
|
111
|
+
else
|
|
112
|
+
name_without_first = name.split("/")[1..-1].join("/")
|
|
113
|
+
matchers = []
|
|
114
|
+
matchers << File.join(options[:template], name_without_first) if options[:template]
|
|
115
|
+
matchers << File.expand_path("~/.foreman/templates/#{name}")
|
|
116
|
+
matchers << File.expand_path("../../../../data/export/#{name}", __FILE__)
|
|
117
|
+
File.read(matchers.detect { |m| File.exists?(m) })
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def write_template(name, target, binding)
|
|
122
|
+
compiled = ERB.new(export_template(name)).result(binding)
|
|
123
|
+
write_file target, compiled
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def chmod(mode, file)
|
|
127
|
+
say "setting #{file} to mode #{mode}"
|
|
128
|
+
FileUtils.chmod mode, File.join(location, file)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def create_directory(dir)
|
|
132
|
+
say "creating: #{dir}"
|
|
133
|
+
FileUtils.mkdir_p(File.join(location, dir))
|
|
134
|
+
end
|
|
135
|
+
|
|
36
136
|
def write_file(filename, contents)
|
|
37
137
|
say "writing: #{filename}"
|
|
38
138
|
|
|
139
|
+
filename = File.join(location, filename) unless Pathname.new(filename).absolute?
|
|
140
|
+
|
|
39
141
|
File.open(filename, "w") do |file|
|
|
40
142
|
file.puts contents
|
|
41
143
|
end
|
|
@@ -3,26 +3,10 @@ require "foreman/export"
|
|
|
3
3
|
|
|
4
4
|
class Foreman::Export::Bluepill < Foreman::Export::Base
|
|
5
5
|
|
|
6
|
-
def export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
app = options[:app] || File.basename(engine.directory)
|
|
12
|
-
user = options[:user] || app
|
|
13
|
-
log_root = options[:log] || "/var/log/#{app}"
|
|
14
|
-
template_root = options[:template]
|
|
15
|
-
|
|
16
|
-
Dir["#{location}/#{app}.pill"].each do |file|
|
|
17
|
-
say "cleaning up: #{file}"
|
|
18
|
-
FileUtils.rm(file)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
|
22
|
-
|
|
23
|
-
master_template = export_template("bluepill", "master.pill.erb", template_root)
|
|
24
|
-
master_config = ERB.new(master_template).result(binding)
|
|
25
|
-
write_file "#{location}/#{app}.pill", master_config
|
|
6
|
+
def export
|
|
7
|
+
super
|
|
8
|
+
clean "#{location}/#{app}.pill"
|
|
9
|
+
write_template "bluepill/master.pill.erb", "#{app}.pill", binding
|
|
26
10
|
end
|
|
27
11
|
|
|
28
12
|
end
|