foreman-capistrano 0.51.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 (82) hide show
  1. data/README.md +46 -0
  2. data/bin/foreman +7 -0
  3. data/bin/foreman-runner +32 -0
  4. data/bin/taskman +8 -0
  5. data/data/example/Procfile +4 -0
  6. data/data/example/Procfile.without_colon +2 -0
  7. data/data/example/error +7 -0
  8. data/data/example/log/neverdie.log +4 -0
  9. data/data/example/spawnee +14 -0
  10. data/data/example/spawner +7 -0
  11. data/data/example/ticker +14 -0
  12. data/data/example/utf8 +11 -0
  13. data/data/export/bluepill/master.pill.erb +28 -0
  14. data/data/export/launchd/launchd.plist.erb +22 -0
  15. data/data/export/runit/log/run.erb +7 -0
  16. data/data/export/runit/run.erb +3 -0
  17. data/data/export/supervisord/app.conf.erb +27 -0
  18. data/data/export/upstart/master.conf.erb +8 -0
  19. data/data/export/upstart/process.conf.erb +7 -0
  20. data/data/export/upstart/process_master.conf.erb +2 -0
  21. data/lib/foreman/capistrano.rb +55 -0
  22. data/lib/foreman/cli.rb +140 -0
  23. data/lib/foreman/distribution.rb +9 -0
  24. data/lib/foreman/engine/cli.rb +104 -0
  25. data/lib/foreman/engine.rb +313 -0
  26. data/lib/foreman/env.rb +27 -0
  27. data/lib/foreman/export/base.rb +146 -0
  28. data/lib/foreman/export/bluepill.rb +12 -0
  29. data/lib/foreman/export/inittab.rb +33 -0
  30. data/lib/foreman/export/launchd.rb +15 -0
  31. data/lib/foreman/export/runit.rb +34 -0
  32. data/lib/foreman/export/supervisord.rb +16 -0
  33. data/lib/foreman/export/upstart.rb +25 -0
  34. data/lib/foreman/export.rb +34 -0
  35. data/lib/foreman/helpers.rb +45 -0
  36. data/lib/foreman/process.rb +102 -0
  37. data/lib/foreman/procfile.rb +92 -0
  38. data/lib/foreman/version.rb +5 -0
  39. data/lib/foreman.rb +23 -0
  40. data/man/foreman.1 +244 -0
  41. data/spec/foreman/cli_spec.rb +87 -0
  42. data/spec/foreman/engine_spec.rb +104 -0
  43. data/spec/foreman/export/base_spec.rb +19 -0
  44. data/spec/foreman/export/bluepill_spec.rb +37 -0
  45. data/spec/foreman/export/inittab_spec.rb +40 -0
  46. data/spec/foreman/export/launchd_spec.rb +21 -0
  47. data/spec/foreman/export/runit_spec.rb +36 -0
  48. data/spec/foreman/export/supervisord_spec.rb +36 -0
  49. data/spec/foreman/export/upstart_spec.rb +88 -0
  50. data/spec/foreman/export_spec.rb +24 -0
  51. data/spec/foreman/helpers_spec.rb +26 -0
  52. data/spec/foreman/process_spec.rb +48 -0
  53. data/spec/foreman/procfile_spec.rb +41 -0
  54. data/spec/foreman_spec.rb +16 -0
  55. data/spec/helper_spec.rb +18 -0
  56. data/spec/resources/Procfile +4 -0
  57. data/spec/resources/bin/echo +2 -0
  58. data/spec/resources/bin/env +2 -0
  59. data/spec/resources/bin/test +2 -0
  60. data/spec/resources/bin/utf8 +2 -0
  61. data/spec/resources/export/bluepill/app-concurrency.pill +49 -0
  62. data/spec/resources/export/bluepill/app.pill +46 -0
  63. data/spec/resources/export/inittab/inittab.concurrency +4 -0
  64. data/spec/resources/export/inittab/inittab.default +4 -0
  65. data/spec/resources/export/launchd/launchd-a.default +22 -0
  66. data/spec/resources/export/launchd/launchd-b.default +22 -0
  67. data/spec/resources/export/runit/app-alpha-1/log/run +7 -0
  68. data/spec/resources/export/runit/app-alpha-1/run +3 -0
  69. data/spec/resources/export/runit/app-alpha-2/log/run +7 -0
  70. data/spec/resources/export/runit/app-alpha-2/run +3 -0
  71. data/spec/resources/export/runit/app-bravo-1/log/run +7 -0
  72. data/spec/resources/export/runit/app-bravo-1/run +3 -0
  73. data/spec/resources/export/supervisord/app-alpha-1.conf +24 -0
  74. data/spec/resources/export/supervisord/app-alpha-2.conf +24 -0
  75. data/spec/resources/export/upstart/app-alpha-1.conf +5 -0
  76. data/spec/resources/export/upstart/app-alpha-2.conf +5 -0
  77. data/spec/resources/export/upstart/app-alpha.conf +2 -0
  78. data/spec/resources/export/upstart/app-bravo-1.conf +5 -0
  79. data/spec/resources/export/upstart/app-bravo.conf +2 -0
  80. data/spec/resources/export/upstart/app.conf +8 -0
  81. data/spec/spec_helper.rb +153 -0
  82. metadata +145 -0
@@ -0,0 +1,313 @@
1
+ require "foreman"
2
+ require "foreman/env"
3
+ require "foreman/process"
4
+ require "foreman/procfile"
5
+ require "tempfile"
6
+ require "timeout"
7
+ require "fileutils"
8
+ require "thread"
9
+
10
+ class Foreman::Engine
11
+
12
+ attr_reader :env
13
+ attr_reader :options
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 = {}
35
+ end
36
+
37
+ # Start the processes registered to this +Engine+
38
+ #
39
+ def start
40
+ trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
41
+ trap("INT") { puts "SIGINT received"; terminate_gracefully }
42
+ trap("HUP") { puts "SIGHUP received"; terminate_gracefully } if ::Signal.list.keys.include? 'HUP'
43
+
44
+ startup
45
+ spawn_processes
46
+ watch_for_output
47
+ sleep 0.1
48
+ watch_for_termination { terminate_gracefully }
49
+ shutdown
50
+ end
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
66
+ end
67
+
68
+ # Clear the processes registered to this +Engine+
69
+ #
70
+ def clear
71
+ @names = {}
72
+ @processes = []
73
+ end
74
+
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]
83
+ end
84
+ self
85
+ end
86
+
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
95
+ end
96
+
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
108
+ end
109
+ end
110
+ else
111
+ begin
112
+ Process.kill "-#{signal}", Process.pid
113
+ rescue Errno::ESRCH, Errno::EPERM
114
+ end
115
+ end
116
+ end
117
+
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])
124
+ end
125
+
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] }
132
+ end
133
+
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]
142
+ end
143
+
144
+ # Yield each +Process+ in order
145
+ #
146
+ def each_process
147
+ process_names.each do |name|
148
+ yield name, process(name)
149
+ end
150
+ end
151
+
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)
158
+ end
159
+
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)
172
+ end
173
+ end
174
+
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
186
+ end
187
+
188
+ private
189
+
190
+ ### Engine API ######################################################
191
+
192
+ def startup
193
+ raise TypeError, "must use a subclass of Foreman::Engine"
194
+ end
195
+
196
+ def output(name, data)
197
+ raise TypeError, "must use a subclass of Foreman::Engine"
198
+ end
199
+
200
+ def shutdown
201
+ raise TypeError, "must use a subclass of Foreman::Engine"
202
+ end
203
+
204
+ ## Helpers ##########################################################
205
+
206
+ def create_pipe
207
+ IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
208
+ end
209
+
210
+ def name_for(pid)
211
+ process, index = @running[pid]
212
+ [ @names[process], index.to_s ].compact.join(".")
213
+ end
214
+
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
222
+ end
223
+ end
224
+
225
+ def output_with_mutex(name, message)
226
+ @mutex.synchronize do
227
+ output name, message
228
+ end
229
+ end
230
+
231
+ def system(message)
232
+ output_with_mutex "system", message
233
+ end
234
+
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
244
+
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
251
+
252
+ ## Engine ###########################################################
253
+
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
267
+ end
268
+ end
269
+
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
278
+ end
279
+ rescue Exception => ex
280
+ puts ex.message
281
+ puts ex.backtrace
282
+ end
283
+ end
284
+ end
285
+
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"
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"
311
+ end
312
+
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
@@ -0,0 +1,146 @@
1
+ require "foreman/export"
2
+ require "ostruct"
3
+ require "pathname"
4
+ require "shellwords"
5
+
6
+ class Foreman::Export::Base
7
+
8
+ attr_reader :location
9
+ attr_reader :engine
10
+ attr_reader :options
11
+ attr_reader :formation
12
+
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
44
+ end
45
+
46
+ def export
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
63
+ end
64
+
65
+ private ######################################################################
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
+
79
+ def error(message)
80
+ raise Foreman::Export::Exception.new(message)
81
+ end
82
+
83
+ def say(message)
84
+ puts "[foreman export] %s" % message
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
96
+
97
+ # deprecated
98
+ def old_export_template(exporter, file, template_root)
99
+ if template_root && File.exist?(file_path = File.join(template_root, file))
100
+ File.read(file_path)
101
+ elsif File.exist?(file_path = File.expand_path(File.join("~/.foreman/templates", file)))
102
+ File.read(file_path)
103
+ else
104
+ File.read(File.expand_path("../../../../data/export/#{exporter}/#{file}", __FILE__))
105
+ end
106
+ end
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
+
136
+ def write_file(filename, contents)
137
+ say "writing: #{filename}"
138
+
139
+ filename = File.join(location, filename) unless Pathname.new(filename).absolute?
140
+
141
+ File.open(filename, "w") do |file|
142
+ file.puts contents
143
+ end
144
+ end
145
+
146
+ end
@@ -0,0 +1,12 @@
1
+ require "erb"
2
+ require "foreman/export"
3
+
4
+ class Foreman::Export::Bluepill < Foreman::Export::Base
5
+
6
+ def export
7
+ super
8
+ clean "#{location}/#{app}.pill"
9
+ write_template "bluepill/master.pill.erb", "#{app}.pill", binding
10
+ end
11
+
12
+ end
@@ -0,0 +1,33 @@
1
+ require "foreman/export"
2
+
3
+ class Foreman::Export::Inittab < Foreman::Export::Base
4
+
5
+ def export
6
+ error("Must specify a location") unless location
7
+
8
+ inittab = []
9
+ inittab << "# ----- foreman #{app} processes -----"
10
+
11
+ index = 1
12
+ engine.each_process do |name, process|
13
+ 1.upto(engine.formation[name]) do |num|
14
+ id = app.slice(0, 2).upcase + sprintf("%02d", index)
15
+ port = engine.port_for(process, num)
16
+ inittab << "#{id}:4:respawn:/bin/su - #{user} -c 'PORT=#{port} #{process.command} >> #{log}/#{name}-#{num}.log 2>&1'"
17
+ index += 1
18
+ end
19
+ end
20
+
21
+ inittab << "# ----- end foreman #{app} processes -----"
22
+
23
+ inittab = inittab.join("\n") + "\n"
24
+
25
+ if location == "-"
26
+ puts inittab
27
+ else
28
+ say "writing: #{location}"
29
+ File.open(location, "w") { |file| file.puts inittab }
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,15 @@
1
+ require "erb"
2
+ require "foreman/export"
3
+
4
+ class Foreman::Export::Launchd < Foreman::Export::Base
5
+
6
+ def export
7
+ super
8
+ engine.each_process do |name, process|
9
+ 1.upto(engine.formation[name]) do |num|
10
+ write_template "launchd/launchd.plist.erb", "#{app}-#{name}-#{num}.plist", binding
11
+ end
12
+ end
13
+ end
14
+
15
+ end
@@ -0,0 +1,34 @@
1
+ require "erb"
2
+ require "foreman/export"
3
+
4
+ class Foreman::Export::Runit < Foreman::Export::Base
5
+
6
+ ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
7
+
8
+ def export
9
+ super
10
+
11
+ engine.each_process do |name, process|
12
+ 1.upto(engine.formation[name]) do |num|
13
+ process_directory = "#{app}-#{name}-#{num}"
14
+
15
+ create_directory process_directory
16
+ create_directory "#{process_directory}/env"
17
+ create_directory "#{process_directory}/log"
18
+
19
+ write_template "runit/run.erb", "#{process_directory}/run", binding
20
+ chmod 0755, "#{process_directory}/run"
21
+
22
+ port = engine.port_for(process, num)
23
+ engine.env.merge("PORT" => port.to_s).each do |key, value|
24
+ write_file "#{process_directory}/env/#{key}", value
25
+ end
26
+
27
+ write_template "runit/log/run.erb", "#{process_directory}/log/run", binding
28
+ chmod 0755, "#{process_directory}/log/run"
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,16 @@
1
+ require "erb"
2
+ require "foreman/export"
3
+
4
+ class Foreman::Export::Supervisord < Foreman::Export::Base
5
+
6
+ def export
7
+ super
8
+
9
+ Dir["#{location}/#{app}*.conf"].each do |file|
10
+ clean file
11
+ end
12
+
13
+ write_template "supervisord/app.conf.erb", "#{app}.conf", binding
14
+ end
15
+
16
+ end
@@ -0,0 +1,25 @@
1
+ require "erb"
2
+ require "foreman/export"
3
+
4
+ class Foreman::Export::Upstart < Foreman::Export::Base
5
+
6
+ def export
7
+ super
8
+
9
+ Dir["#{location}/#{app}*.conf"].each do |file|
10
+ clean file
11
+ end
12
+
13
+ write_template "upstart/master.conf.erb", "#{app}.conf", binding
14
+
15
+ engine.each_process do |name, process|
16
+ next if engine.formation[name] < 1
17
+ write_template "upstart/process_master.conf.erb", "#{app}-#{name}.conf", binding
18
+
19
+ 1.upto(engine.formation[name]) do |num|
20
+ port = engine.port_for(process, num)
21
+ write_template "upstart/process.conf.erb", "#{app}-#{name}-#{num}.conf", binding
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,34 @@
1
+ require "foreman"
2
+ require "foreman/helpers"
3
+ require "pathname"
4
+
5
+ module Foreman::Export
6
+ extend Foreman::Helpers
7
+
8
+ class Exception < ::Exception; end
9
+
10
+ def self.formatter(format)
11
+ begin
12
+ require "foreman/export/#{ format.tr('-', '_') }"
13
+ classy_format = classify(format)
14
+ formatter = constantize("Foreman::Export::#{ classy_format }")
15
+ rescue NameError => ex
16
+ error "Unknown export format: #{format} (no class Foreman::Export::#{ classy_format })."
17
+ rescue LoadError => ex
18
+ error "Unknown export format: #{format} (unable to load file 'foreman/export/#{ format.tr('-', '_') }')."
19
+ end
20
+ end
21
+
22
+ def self.error(message)
23
+ raise Foreman::Export::Exception.new(message)
24
+ end
25
+
26
+ end
27
+
28
+ require "foreman/export/base"
29
+ require "foreman/export/inittab"
30
+ require "foreman/export/upstart"
31
+ require "foreman/export/bluepill"
32
+ require "foreman/export/runit"
33
+ require "foreman/export/supervisord"
34
+ require "foreman/export/launchd"
@@ -0,0 +1,45 @@
1
+ module Foreman::Helpers
2
+ # Copied whole sale from, https://github.com/defunkt/resque/
3
+
4
+ # Given a word with dashes, returns a camel cased version of it.
5
+ #
6
+ # classify('job-name') # => 'JobName'
7
+ def classify(dashed_word)
8
+ dashed_word.split('-').each { |part| part[0] = part[0].chr.upcase }.join
9
+ end # Tries to find a constant with the name specified in the argument string:
10
+
11
+ #
12
+ # constantize("Module") # => Module
13
+ # constantize("Test::Unit") # => Test::Unit
14
+ #
15
+ # The name is assumed to be the one of a top-level constant, no matter
16
+ # whether it starts with "::" or not. No lexical context is taken into
17
+ # account:
18
+ #
19
+ # C = 'outside'
20
+ # module M
21
+ # C = 'inside'
22
+ # C # => 'inside'
23
+ # constantize("C") # => 'outside', same as ::C
24
+ # end
25
+ #
26
+ # NameError is raised when the constant is unknown.
27
+ def constantize(camel_cased_word)
28
+ camel_cased_word = camel_cased_word.to_s
29
+
30
+ names = camel_cased_word.split('::')
31
+ names.shift if names.empty? || names.first.empty?
32
+
33
+ constant = Object
34
+ names.each do |name|
35
+ args = Module.method(:const_get).arity != 1 ? [false] : []
36
+
37
+ if constant.const_defined?(name, *args)
38
+ constant = constant.const_get(name)
39
+ else
40
+ constant = constant.const_missing(name)
41
+ end
42
+ end
43
+ constant
44
+ end
45
+ end