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.
Files changed (73) hide show
  1. data/README.md +46 -0
  2. data/bin/foreman-runner +32 -0
  3. data/bin/taskman +8 -0
  4. data/data/example/Procfile +4 -2
  5. data/data/example/spawnee +14 -0
  6. data/data/example/spawner +7 -0
  7. data/data/example/utf8 +11 -0
  8. data/data/export/bluepill/master.pill.erb +11 -10
  9. data/data/export/launchd/launchd.plist.erb +22 -0
  10. data/data/export/runit/log/run.erb +7 -0
  11. data/data/export/runit/run.erb +2 -2
  12. data/data/export/supervisord/app.conf.erb +27 -0
  13. data/data/export/upstart/master.conf.erb +2 -2
  14. data/data/export/upstart/process.conf.erb +3 -3
  15. data/lib/foreman/cli.rb +83 -34
  16. data/lib/foreman/engine/cli.rb +105 -0
  17. data/lib/foreman/engine.rb +232 -140
  18. data/lib/foreman/env.rb +27 -0
  19. data/lib/foreman/export/base.rb +108 -6
  20. data/lib/foreman/export/bluepill.rb +4 -20
  21. data/lib/foreman/export/inittab.rb +11 -16
  22. data/lib/foreman/export/launchd.rb +15 -0
  23. data/lib/foreman/export/runit.rb +15 -41
  24. data/lib/foreman/export/supervisord.rb +16 -0
  25. data/lib/foreman/export/upstart.rb +10 -28
  26. data/lib/foreman/export.rb +23 -0
  27. data/lib/foreman/helpers.rb +45 -0
  28. data/lib/foreman/process.rb +80 -46
  29. data/lib/foreman/procfile.rb +68 -14
  30. data/lib/foreman/version.rb +1 -1
  31. data/lib/foreman.rb +12 -7
  32. data/man/foreman.1 +29 -3
  33. data/spec/foreman/cli_spec.rb +54 -60
  34. data/spec/foreman/engine_spec.rb +64 -46
  35. data/spec/foreman/export/base_spec.rb +19 -0
  36. data/spec/foreman/export/bluepill_spec.rb +24 -6
  37. data/spec/foreman/export/inittab_spec.rb +40 -0
  38. data/spec/foreman/export/launchd_spec.rb +21 -0
  39. data/spec/foreman/export/runit_spec.rb +23 -22
  40. data/spec/foreman/export/supervisord_spec.rb +36 -0
  41. data/spec/foreman/export/upstart_spec.rb +49 -16
  42. data/spec/foreman/export_spec.rb +22 -0
  43. data/spec/foreman/helpers_spec.rb +26 -0
  44. data/spec/foreman/process_spec.rb +48 -2
  45. data/spec/foreman/procfile_spec.rb +41 -0
  46. data/spec/foreman_spec.rb +3 -20
  47. data/spec/helper_spec.rb +18 -0
  48. data/spec/resources/Procfile +4 -0
  49. data/spec/resources/bin/echo +2 -0
  50. data/spec/resources/bin/env +2 -0
  51. data/spec/resources/bin/test +2 -0
  52. data/spec/resources/bin/utf8 +2 -0
  53. data/spec/resources/export/bluepill/app-concurrency.pill +49 -0
  54. data/spec/resources/export/bluepill/app.pill +6 -25
  55. data/spec/resources/export/inittab/inittab.concurrency +4 -0
  56. data/spec/resources/export/inittab/inittab.default +4 -0
  57. data/spec/resources/export/launchd/launchd-a.default +22 -0
  58. data/spec/resources/export/launchd/launchd-b.default +22 -0
  59. data/spec/resources/export/supervisord/app-alpha-1.conf +24 -0
  60. data/spec/resources/export/supervisord/app-alpha-2.conf +24 -0
  61. data/spec/spec_helper.rb +93 -10
  62. metadata +72 -76
  63. data/README.markdown +0 -49
  64. data/bin/runner +0 -2
  65. data/data/export/runit/log_run.erb +0 -7
  66. data/lib/foreman/procfile_entry.rb +0 -22
  67. data/lib/foreman/utils.rb +0 -15
  68. /data/spec/resources/export/runit/{app-alpha-1-log-run → app-alpha-1/log/run} +0 -0
  69. /data/spec/resources/export/runit/{app-alpha-1-run → app-alpha-1/run} +0 -0
  70. /data/spec/resources/export/runit/{app-alpha-2-log-run → app-alpha-2/log/run} +0 -0
  71. /data/spec/resources/export/runit/{app-alpha-2-run → app-alpha-2/run} +0 -0
  72. /data/spec/resources/export/runit/{app-bravo-1-log-run → app-bravo-1/log/run} +0 -0
  73. /data/spec/resources/export/runit/{app-bravo-1-run → app-bravo-1/run} +0 -0
@@ -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 :procfile
15
- attr_reader :directory
12
+ attr_reader :env
16
13
  attr_reader :options
17
-
18
- extend Term::ANSIColor
19
-
20
- COLORS = [ cyan, yellow, green, magenta, red ]
21
-
22
- def initialize(procfile, options={})
23
- @procfile = Foreman::Procfile.new(procfile)
24
- @directory = File.expand_path(File.dirname(procfile))
25
- @options = options
26
- @environment = read_environment_files(options[:env])
27
- @output_mutex = Mutex.new
28
- end
29
-
30
- def self.load_env!(env_file)
31
- @environment = read_environment_files(env_file)
32
- apply_environment!
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
- assign_colors
44
+ startup
43
45
  spawn_processes
44
46
  watch_for_output
45
- watch_for_termination
47
+ sleep 0.1
48
+ watch_for_termination { terminate_gracefully }
49
+ shutdown
46
50
  end
47
51
 
48
- def port_for(process, num, base_port=nil)
49
- base_port ||= 5000
50
- offset = procfile.process_names.index(process.name) * 100
51
- base_port.to_i + offset + num - 1
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
- private ######################################################################
55
-
56
- def spawn_processes
57
- concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency])
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
- def kill_all(signal="SIGTERM")
73
- running_processes.each do |pid, process|
74
- info "sending #{signal} to pid #{pid}"
75
- Process.kill(signal, pid) rescue Errno::ESRCH
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
- def terminate_gracefully
80
- info "sending SIGTERM to all processes"
81
- kill_all "SIGTERM"
82
- Timeout.timeout(5) { Process.waitall }
83
- rescue Timeout::Error
84
- info "sending SIGKILL to all processes"
85
- kill_all "SIGKILL"
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
- def watch_for_output
89
- Thread.new do
90
- begin
91
- loop do
92
- rs, ws = IO.select(readers.values, [], [], 1)
93
- (rs || []).each do |r|
94
- ps, message = r.gets.split(",", 2)
95
- color = colors[ps.split(".").first]
96
- info message, ps, color
97
- end
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
- rescue Exception => ex
100
- puts ex.message
101
- puts ex.backtrace
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
- def watch_for_termination
107
- pid, status = Process.wait2
108
- process = running_processes.delete(pid)
109
- info "process terminated", process.name
110
- terminate_gracefully
111
- kill_all
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
- def info(message, name="system", color=Term::ANSIColor.white)
116
- print color
117
- print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
118
- print Term::ANSIColor.reset
119
- print message.chomp
120
- puts ""
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
- def print(message=nil)
124
- @output_mutex.synchronize do
125
- $stdout.print message
126
- end
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
- def puts(message=nil)
130
- @output_mutex.synchronize do
131
- $stdout.puts message
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
- def error(message)
136
- puts "ERROR: #{message}"
137
- exit 1
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
- def longest_process_name
141
- @longest_process_name ||= begin
142
- longest = procfile.process_names.map { |name| name.length }.sort.last
143
- longest = 6 if longest < 6 # system
144
- longest
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
- def pad_process_name(name="system")
149
- name.to_s.ljust(longest_process_name + 3) # add 3 for process number padding
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
- def proctitle(title)
153
- $0 = title
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 termtitle(title)
157
- printf("\033]0;#{title}\007")
196
+ def output(name, data)
197
+ raise TypeError, "must use a subclass of Foreman::Engine"
158
198
  end
159
199
 
160
- def running_processes
161
- @running_processes ||= {}
200
+ def shutdown
201
+ raise TypeError, "must use a subclass of Foreman::Engine"
162
202
  end
163
203
 
164
- def readers
165
- @readers ||= {}
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 colors
169
- @colors ||= {}
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 assign_colors
173
- procfile.entries.each do |entry|
174
- colors[entry.name] = next_color
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 process_by_reader(reader)
179
- readers.invert[reader]
225
+ def output_with_mutex(name, message)
226
+ @mutex.synchronize do
227
+ output name, message
228
+ end
180
229
  end
181
230
 
182
- def next_color
183
- @current_color ||= -1
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
- module Env
189
- attr_reader :environment
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
- def read_environment_files(filenames)
192
- environment = {}
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
- (filenames || "").split(",").map(&:strip).each do |filename|
195
- error "No such file: #{filename}" unless File.exists?(filename)
196
- environment.merge!(read_environment(filename))
197
- end
252
+ ## Engine ###########################################################
198
253
 
199
- environment.merge!(read_environment(".env")) unless filenames
200
- environment
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
- def read_environment(filename)
204
- return {} unless File.exists?(filename)
205
-
206
- File.read(filename).split("\n").inject({}) do |hash, line|
207
- if line =~ /\A([A-Za-z_0-9]+)=(.*)\z/
208
- hash[$1] = $2
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
- hash
279
+ rescue Exception => ex
280
+ puts ex.message
281
+ puts ex.backtrace
211
282
  end
212
283
  end
284
+ end
213
285
 
214
- def apply_environment!
215
- @environment.each { |k,v| ENV[k] = v }
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
@@ -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
@@ -1,20 +1,81 @@
1
1
  require "foreman/export"
2
- require "foreman/utils"
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
- def initialize(engine)
9
- @engine = engine
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
- raise "export method must be overridden"
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
- def export_template(exporter, file, template_root)
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(location, options={})
7
- error("Must specify a location") unless location
8
-
9
- FileUtils.mkdir_p location
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