Compare commits
38
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a04032454a | ||
|
|
29c94c785d | ||
|
|
a0a2dd9454 | ||
|
|
1b701cddf3 | ||
|
|
4080b3f1f2 | ||
|
|
84c49ae2b8 | ||
|
|
d54b46806c | ||
|
|
bc1c5e4c74 | ||
|
|
ed4a32518f | ||
|
|
c7167e1c83 | ||
|
|
db93432118 | ||
|
|
5eada6b7cc | ||
|
|
5e46b1f43a | ||
|
|
ef723f5831 | ||
|
|
52e24ef64b | ||
|
|
0b1daf1927 | ||
|
|
e0d84a56d7 | ||
|
|
7ee2edcc60 | ||
|
|
d428ac5356 | ||
|
|
5351b49fee | ||
|
|
6ebe76d8c1 | ||
|
|
7c43c672c9 | ||
|
|
41d9050ae3 | ||
|
|
09e9cefa3a | ||
|
|
85efe5c1ba | ||
|
|
c21634c04e | ||
|
|
6d4f3476f1 | ||
|
|
724812d6e3 | ||
|
|
ce6ec4848d | ||
|
|
a37a097f73 | ||
|
|
f28725bdac | ||
|
|
ade0005a92 | ||
|
|
241b91a0d5 | ||
|
|
047f106d48 | ||
|
|
df043e60d8 | ||
|
|
158c184f8c | ||
|
|
2ed1fe8d44 | ||
|
|
e76f3533dc |
+1
-1
@@ -11,4 +11,4 @@ notifications:
|
|||||||
on_success: always
|
on_success: always
|
||||||
on_failure: always
|
on_failure: always
|
||||||
urls:
|
urls:
|
||||||
- https://dx-helper.herokuapp.com/travis
|
- http://dx-helper.herokuapp.com/travis
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ group :development do
|
|||||||
gem 'rcov', '~> 0.9.8'
|
gem 'rcov', '~> 0.9.8'
|
||||||
gem 'rr', '~> 1.0.2'
|
gem 'rr', '~> 1.0.2'
|
||||||
gem 'rspec', '~> 2.0'
|
gem 'rspec', '~> 2.0'
|
||||||
|
gem 'ZenTest'
|
||||||
gem 'aws-s3'
|
gem 'aws-s3'
|
||||||
gem "rubyzip"
|
gem "rubyzip"
|
||||||
end
|
end
|
||||||
|
|||||||
+3
-1
@@ -1,13 +1,14 @@
|
|||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
foreman (0.37.0.pre3)
|
foreman (0.37.0.pre4)
|
||||||
term-ansicolor (~> 1.0.7)
|
term-ansicolor (~> 1.0.7)
|
||||||
thor (>= 0.13.6)
|
thor (>= 0.13.6)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: http://rubygems.org/
|
remote: http://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
|
ZenTest (4.6.2)
|
||||||
aws-s3 (0.6.2)
|
aws-s3 (0.6.2)
|
||||||
builder
|
builder
|
||||||
mime-types
|
mime-types
|
||||||
@@ -56,6 +57,7 @@ PLATFORMS
|
|||||||
x86-mingw32
|
x86-mingw32
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
ZenTest
|
||||||
aws-s3
|
aws-s3
|
||||||
fakefs (~> 0.3.2)
|
fakefs (~> 0.3.2)
|
||||||
foreman!
|
foreman!
|
||||||
|
|||||||
+23
-34
@@ -1,49 +1,38 @@
|
|||||||
# Foreman
|
# Foreman
|
||||||
|
|
||||||
## Installation
|
Manage Procfile-based applications
|
||||||
|
|
||||||
* Rubygems
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>If you have...</th>
|
||||||
|
<th>Install with...</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Ruby (MRI, JRuby, Windows)</td>
|
||||||
|
<td><pre>$ gem install foreman</pre></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Mac OS X</td>
|
||||||
|
<td><a href="http://assets.foreman.io/foreman/foreman.pkg">foreman.pkg</a></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
gem install foreman
|
## Getting Started
|
||||||
|
|
||||||
* OSX
|
* http://blog.daviddollar.org/2011/05/06/introducing-foreman.html
|
||||||
|
|
||||||
http://assets.foreman.io/foreman/foreman.pkg
|
## Documentation
|
||||||
|
|
||||||
* Standalone Tarball
|
|
||||||
|
|
||||||
http://assets.foreman.io/foreman/foreman.tgz
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
http://blog.daviddollar.org/2011/05/06/introducing-foreman.html
|
|
||||||
|
|
||||||
## Manual
|
|
||||||
|
|
||||||
* [man page](http://ddollar.github.com/foreman)
|
* [man page](http://ddollar.github.com/foreman)
|
||||||
* [wiki](http://github.com/ddollar/foreman/wiki)
|
* [wiki](http://github.com/ddollar/foreman/wiki)
|
||||||
|
|
||||||
## Authorship
|
## Authors
|
||||||
|
|
||||||
Created by David Dollar
|
#### Created and maintained by
|
||||||
|
David Dollar
|
||||||
|
|
||||||
Patches contributed by:
|
#### Patches contributed by
|
||||||
|
Adam Wiggins, Chris Continanza, Chris Lowder, Craig R Webster, Dan Farina, Dan Peterson, David Dollar, Fletcher Nichol, Gabriel Burt, Gamaliel Toro, Greg Reinacker, Hugues Le Gendre, Hunter Nield, Iain Hecker, Jay Zeschin, Keith Rarick, Khaja Minhajuddin, Lincoln Stoll, Marcos Muino Garcia, Mark McGranaghan, Matt Griffin, Matt Haynes, Matthijs Langenberg, Michael Dwan, Michael van Rooijen, Mike Javorski, Nathan Broadbent, Nathan L Smith, Nick Zadrozny, Phil Hagelberg, Ricardo Chimal, Jr, Thom May, Tom Ward, brainopia, clifff, jc00ke
|
||||||
* Adam Wiggins
|
|
||||||
* Dan Peterson
|
|
||||||
* Hunter Nield
|
|
||||||
* Jay Zeschin
|
|
||||||
* Keith Rarick
|
|
||||||
* Khaja Minhajuddin
|
|
||||||
* Matt Haynes
|
|
||||||
* Michael van Rooijen
|
|
||||||
* Mike Javorski
|
|
||||||
* Nathan L Smith
|
|
||||||
* Nick Zadrozny
|
|
||||||
* Ricardo Chimal, Jr
|
|
||||||
* Thom May
|
|
||||||
* clifff
|
|
||||||
* Greg Reinacker
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ task :pages => "man:commit" do
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
task :authors do
|
||||||
|
authors = %x{ git log --pretty=format:"%an" | sort -u }.split("\n")
|
||||||
|
puts authors.join(", ")
|
||||||
|
end
|
||||||
|
|
||||||
## dist
|
## dist
|
||||||
|
|
||||||
require "erb"
|
require "erb"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
|
|||||||
|
|
||||||
<% engine.procfile.entries.each do |process| %>
|
<% engine.procfile.entries.each do |process| %>
|
||||||
<% 1.upto(concurrency[process.name]) do |num| %>
|
<% 1.upto(concurrency[process.name]) do |num| %>
|
||||||
<% port = engine.port_for(process, num, options[:port]) %>
|
<% port = engine.port_for(process, num, self.port) %>
|
||||||
app.process("<%= process.name %>-<%=num%>") do |process|
|
app.process("<%= process.name %>-<%=num%>") do |process|
|
||||||
process.start_command = "<%= process.command.gsub("$PORT", port.to_s) %>"
|
process.start_command = "<%= process.command.gsub("$PORT", port.to_s) %>"
|
||||||
|
|
||||||
|
|||||||
+13
-8
@@ -1,10 +1,14 @@
|
|||||||
require "foreman"
|
require "foreman"
|
||||||
|
require "foreman/helpers"
|
||||||
require "foreman/engine"
|
require "foreman/engine"
|
||||||
require "foreman/export"
|
require "foreman/export"
|
||||||
require "thor"
|
require "thor"
|
||||||
require "yaml"
|
require "yaml"
|
||||||
|
|
||||||
class Foreman::CLI < Thor
|
class Foreman::CLI < Thor
|
||||||
|
include Foreman::Helpers
|
||||||
|
|
||||||
|
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
|
||||||
|
|
||||||
desc "start", "Start the application"
|
desc "start", "Start the application"
|
||||||
|
|
||||||
@@ -42,16 +46,17 @@ class Foreman::CLI < Thor
|
|||||||
def export(format, location=nil)
|
def export(format, location=nil)
|
||||||
check_procfile!
|
check_procfile!
|
||||||
|
|
||||||
formatter = case format
|
begin
|
||||||
when "inittab" then Foreman::Export::Inittab
|
require "foreman/export/#{ format.tr('-', '_') }"
|
||||||
when "upstart" then Foreman::Export::Upstart
|
classy_format = classify(format)
|
||||||
when "bluepill" then Foreman::Export::Bluepill
|
formatter = constantize("Foreman::Export::#{ classy_format }")
|
||||||
when "runit" then Foreman::Export::Runit
|
rescue NameError => ex
|
||||||
else error "Unknown export format: #{format}."
|
error "Unknown export format: #{format} (no class Foreman::Export::#{ classy_format })."
|
||||||
|
rescue LoadError => ex
|
||||||
|
error "Unknown export format: #{format} (unable to load file 'foreman/export/#{ format.tr('-', '_') }')."
|
||||||
end
|
end
|
||||||
|
|
||||||
formatter.new(engine).export(location, options)
|
formatter.new(location, engine, options).export
|
||||||
|
|
||||||
rescue Foreman::Export::Exception => ex
|
rescue Foreman::Export::Exception => ex
|
||||||
error ex.message
|
error ex.message
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ private ######################################################################
|
|||||||
def kill_all(signal="SIGTERM")
|
def kill_all(signal="SIGTERM")
|
||||||
running_processes.each do |pid, process|
|
running_processes.each do |pid, process|
|
||||||
info "sending #{signal} to pid #{pid}"
|
info "sending #{signal} to pid #{pid}"
|
||||||
Process.kill(signal, pid) rescue Errno::ESRCH
|
process.kill signal
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -93,7 +93,9 @@ private ######################################################################
|
|||||||
loop do
|
loop do
|
||||||
rs, ws = IO.select(readers.values, [], [], 1)
|
rs, ws = IO.select(readers.values, [], [], 1)
|
||||||
(rs || []).each do |r|
|
(rs || []).each do |r|
|
||||||
ps, message = r.gets.split(",", 2)
|
data = r.gets
|
||||||
|
next unless data
|
||||||
|
ps, message = data.split(",", 2)
|
||||||
color = colors[ps.split(".").first]
|
color = colors[ps.split(".").first]
|
||||||
info message, ps, color
|
info message, ps, color
|
||||||
end
|
end
|
||||||
@@ -134,11 +136,6 @@ private ######################################################################
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def error(message)
|
|
||||||
puts "ERROR: #{message}"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
def longest_process_name
|
def longest_process_name
|
||||||
@longest_process_name ||= begin
|
@longest_process_name ||= begin
|
||||||
longest = procfile.process_names.map { |name| name.length }.sort.last
|
longest = procfile.process_names.map { |name| name.length }.sort.last
|
||||||
@@ -217,6 +214,11 @@ private ######################################################################
|
|||||||
def apply_environment!
|
def apply_environment!
|
||||||
@environment.each { |k,v| ENV[k] = v }
|
@environment.each { |k,v| ENV[k] = v }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def error(message)
|
||||||
|
puts "ERROR: #{message}"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
include Env
|
include Env
|
||||||
|
|||||||
@@ -3,10 +3,17 @@ require "foreman/utils"
|
|||||||
|
|
||||||
class Foreman::Export::Base
|
class Foreman::Export::Base
|
||||||
|
|
||||||
attr_reader :engine
|
attr_reader :location, :engine, :app, :log, :port, :user, :template, :concurrency
|
||||||
|
|
||||||
def initialize(engine)
|
def initialize(location, engine, options={})
|
||||||
@engine = engine
|
@location = location
|
||||||
|
@engine = engine
|
||||||
|
@app = options[:app]
|
||||||
|
@log = options[:log]
|
||||||
|
@port = options[:port]
|
||||||
|
@user = options[:user]
|
||||||
|
@template = options[:template]
|
||||||
|
@concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
||||||
end
|
end
|
||||||
|
|
||||||
def export
|
def export
|
||||||
|
|||||||
@@ -3,23 +3,21 @@ require "foreman/export"
|
|||||||
|
|
||||||
class Foreman::Export::Bluepill < Foreman::Export::Base
|
class Foreman::Export::Bluepill < Foreman::Export::Base
|
||||||
|
|
||||||
def export(location, options={})
|
def export
|
||||||
error("Must specify a location") unless location
|
error("Must specify a location") unless location
|
||||||
|
|
||||||
FileUtils.mkdir_p location
|
FileUtils.mkdir_p location
|
||||||
|
|
||||||
app = options[:app] || File.basename(engine.directory)
|
app = self.app || File.basename(engine.directory)
|
||||||
user = options[:user] || app
|
user = self.user || app
|
||||||
log_root = options[:log] || "/var/log/#{app}"
|
log_root = self.log || "/var/log/#{app}"
|
||||||
template_root = options[:template]
|
template_root = self.template
|
||||||
|
|
||||||
Dir["#{location}/#{app}.pill"].each do |file|
|
Dir["#{location}/#{app}.pill"].each do |file|
|
||||||
say "cleaning up: #{file}"
|
say "cleaning up: #{file}"
|
||||||
FileUtils.rm(file)
|
FileUtils.rm(file)
|
||||||
end
|
end
|
||||||
|
|
||||||
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
|
||||||
|
|
||||||
master_template = export_template("bluepill", "master.pill.erb", template_root)
|
master_template = export_template("bluepill", "master.pill.erb", template_root)
|
||||||
master_config = ERB.new(master_template).result(binding)
|
master_config = ERB.new(master_template).result(binding)
|
||||||
write_file "#{location}/#{app}.pill", master_config
|
write_file "#{location}/#{app}.pill", master_config
|
||||||
|
|||||||
@@ -2,20 +2,18 @@ require "foreman/export"
|
|||||||
|
|
||||||
class Foreman::Export::Inittab < Foreman::Export::Base
|
class Foreman::Export::Inittab < Foreman::Export::Base
|
||||||
|
|
||||||
def export(fname=nil, options={})
|
def export
|
||||||
app = options[:app] || File.basename(engine.directory)
|
app = self.app || File.basename(engine.directory)
|
||||||
user = options[:user] || app
|
user = self.user || app
|
||||||
log_root = options[:log] || "/var/log/#{app}"
|
log_root = self.log || "/var/log/#{app}"
|
||||||
|
|
||||||
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
|
||||||
|
|
||||||
inittab = []
|
inittab = []
|
||||||
inittab << "# ----- foreman #{app} processes -----"
|
inittab << "# ----- foreman #{app} processes -----"
|
||||||
|
|
||||||
engine.procfile.entries.inject(1) do |index, process|
|
engine.procfile.entries.inject(1) do |index, process|
|
||||||
1.upto(concurrency[process.name]) do |num|
|
1.upto(self.concurrency[process.name]) do |num|
|
||||||
id = app.slice(0, 2).upcase + sprintf("%02d", index)
|
id = app.slice(0, 2).upcase + sprintf("%02d", index)
|
||||||
port = engine.port_for(process, num, options[:port])
|
port = engine.port_for(process, num, self.port)
|
||||||
inittab << "#{id}:4:respawn:/bin/su - #{user} -c 'PORT=#{port} #{process.command} >> #{log_root}/#{process.name}-#{num}.log 2>&1'"
|
inittab << "#{id}:4:respawn:/bin/su - #{user} -c 'PORT=#{port} #{process.command} >> #{log_root}/#{process.name}-#{num}.log 2>&1'"
|
||||||
index += 1
|
index += 1
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,21 +4,19 @@ require "foreman/export"
|
|||||||
class Foreman::Export::Runit < Foreman::Export::Base
|
class Foreman::Export::Runit < Foreman::Export::Base
|
||||||
ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
|
ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
|
||||||
|
|
||||||
def export(location, options={})
|
def export
|
||||||
error("Must specify a location") unless location
|
error("Must specify a location") unless location
|
||||||
|
|
||||||
app = options[:app] || File.basename(engine.directory)
|
app = self.app || File.basename(engine.directory)
|
||||||
user = options[:user] || app
|
user = self.user || app
|
||||||
log_root = options[:log] || "/var/log/#{app}"
|
log_root = self.log || "/var/log/#{app}"
|
||||||
template_root = options[:template]
|
template_root = self.template
|
||||||
|
|
||||||
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
|
||||||
|
|
||||||
run_template = export_template('runit', 'run.erb', template_root)
|
run_template = export_template('runit', 'run.erb', template_root)
|
||||||
log_run_template = export_template('runit', 'log_run.erb', template_root)
|
log_run_template = export_template('runit', 'log_run.erb', template_root)
|
||||||
|
|
||||||
engine.procfile.entries.each do |process|
|
engine.procfile.entries.each do |process|
|
||||||
1.upto(concurrency[process.name]) do |num|
|
1.upto(self.concurrency[process.name]) do |num|
|
||||||
process_directory = "#{location}/#{app}-#{process.name}-#{num}"
|
process_directory = "#{location}/#{app}-#{process.name}-#{num}"
|
||||||
process_env_directory = "#{process_directory}/env"
|
process_env_directory = "#{process_directory}/env"
|
||||||
process_log_directory = "#{process_directory}/log"
|
process_log_directory = "#{process_directory}/log"
|
||||||
@@ -31,7 +29,7 @@ class Foreman::Export::Runit < Foreman::Export::Base
|
|||||||
write_file "#{process_directory}/run", run
|
write_file "#{process_directory}/run", run
|
||||||
FileUtils.chmod 0755, "#{process_directory}/run"
|
FileUtils.chmod 0755, "#{process_directory}/run"
|
||||||
|
|
||||||
port = engine.port_for(process, num, options[:port])
|
port = engine.port_for(process, num, self.port)
|
||||||
environment_variables = {'PORT' => port}.
|
environment_variables = {'PORT' => port}.
|
||||||
merge(engine.environment).
|
merge(engine.environment).
|
||||||
merge(inline_variables(process.command))
|
merge(inline_variables(process.command))
|
||||||
@@ -51,7 +49,7 @@ class Foreman::Export::Runit < Foreman::Export::Base
|
|||||||
private
|
private
|
||||||
def create_directory(location)
|
def create_directory(location)
|
||||||
say "creating: #{location}"
|
say "creating: #{location}"
|
||||||
FileUtils.mkdir(location)
|
FileUtils.mkdir_p(location)
|
||||||
end
|
end
|
||||||
|
|
||||||
def inline_variables(command)
|
def inline_variables(command)
|
||||||
|
|||||||
@@ -3,23 +3,21 @@ require "foreman/export"
|
|||||||
|
|
||||||
class Foreman::Export::Upstart < Foreman::Export::Base
|
class Foreman::Export::Upstart < Foreman::Export::Base
|
||||||
|
|
||||||
def export(location, options={})
|
def export
|
||||||
error("Must specify a location") unless location
|
error("Must specify a location") unless location
|
||||||
|
|
||||||
FileUtils.mkdir_p location
|
FileUtils.mkdir_p location
|
||||||
|
|
||||||
app = options[:app] || File.basename(engine.directory)
|
app = self.app || File.basename(engine.directory)
|
||||||
user = options[:user] || app
|
user = self.user || app
|
||||||
log_root = options[:log] || "/var/log/#{app}"
|
log_root = self.log || "/var/log/#{app}"
|
||||||
template_root = options[:template]
|
template_root = self.template
|
||||||
|
|
||||||
Dir["#{location}/#{app}*.conf"].each do |file|
|
Dir["#{location}/#{app}*.conf"].each do |file|
|
||||||
say "cleaning up: #{file}"
|
say "cleaning up: #{file}"
|
||||||
FileUtils.rm(file)
|
FileUtils.rm(file)
|
||||||
end
|
end
|
||||||
|
|
||||||
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
|
||||||
|
|
||||||
master_template = export_template("upstart", "master.conf.erb", template_root)
|
master_template = export_template("upstart", "master.conf.erb", template_root)
|
||||||
master_config = ERB.new(master_template).result(binding)
|
master_config = ERB.new(master_template).result(binding)
|
||||||
write_file "#{location}/#{app}.conf", master_config
|
write_file "#{location}/#{app}.conf", master_config
|
||||||
@@ -27,13 +25,13 @@ class Foreman::Export::Upstart < Foreman::Export::Base
|
|||||||
process_template = export_template("upstart", "process.conf.erb", template_root)
|
process_template = export_template("upstart", "process.conf.erb", template_root)
|
||||||
|
|
||||||
engine.procfile.entries.each do |process|
|
engine.procfile.entries.each do |process|
|
||||||
next if (conc = concurrency[process.name]) < 1
|
next if (conc = self.concurrency[process.name]) < 1
|
||||||
process_master_template = export_template("upstart", "process_master.conf.erb", template_root)
|
process_master_template = export_template("upstart", "process_master.conf.erb", template_root)
|
||||||
process_master_config = ERB.new(process_master_template).result(binding)
|
process_master_config = ERB.new(process_master_template).result(binding)
|
||||||
write_file "#{location}/#{app}-#{process.name}.conf", process_master_config
|
write_file "#{location}/#{app}-#{process.name}.conf", process_master_config
|
||||||
|
|
||||||
1.upto(concurrency[process.name]) do |num|
|
1.upto(self.concurrency[process.name]) do |num|
|
||||||
port = engine.port_for(process, num, options[:port])
|
port = engine.port_for(process, num, self.port)
|
||||||
process_config = ERB.new(process_template).result(binding)
|
process_config = ERB.new(process_template).result(binding)
|
||||||
write_file "#{location}/#{app}-#{process.name}-#{num}.conf", process_config
|
write_file "#{location}/#{app}-#{process.name}-#{num}.conf", process_config
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
module Foreman::Helpers
|
||||||
|
# Copied whole sale from, https://github.com/defunkt/resque/
|
||||||
|
|
||||||
|
# Given a word with dashes, returns a camel cased version of it.
|
||||||
|
#
|
||||||
|
# classify('job-name') # => 'JobName'
|
||||||
|
def classify(dashed_word)
|
||||||
|
dashed_word.split('-').each { |part| part[0] = part[0].chr.upcase }.join
|
||||||
|
end
|
||||||
|
|
||||||
|
# Tries to find a constant with the name specified in the argument string:
|
||||||
|
#
|
||||||
|
# constantize("Module") # => Module
|
||||||
|
# constantize("Test::Unit") # => Test::Unit
|
||||||
|
#
|
||||||
|
# The name is assumed to be the one of a top-level constant, no matter
|
||||||
|
# whether it starts with "::" or not. No lexical context is taken into
|
||||||
|
# account:
|
||||||
|
#
|
||||||
|
# C = 'outside'
|
||||||
|
# module M
|
||||||
|
# C = 'inside'
|
||||||
|
# C # => 'inside'
|
||||||
|
# constantize("C") # => 'outside', same as ::C
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# NameError is raised when the constant is unknown.
|
||||||
|
def constantize(camel_cased_word)
|
||||||
|
camel_cased_word = camel_cased_word.to_s
|
||||||
|
|
||||||
|
if camel_cased_word.include?('-')
|
||||||
|
camel_cased_word = classify(camel_cased_word)
|
||||||
|
end
|
||||||
|
|
||||||
|
names = camel_cased_word.split('::')
|
||||||
|
names.shift if names.empty? || names.first.empty?
|
||||||
|
|
||||||
|
constant = Object
|
||||||
|
names.each do |name|
|
||||||
|
args = Module.method(:const_get).arity != 1 ? [false] : []
|
||||||
|
|
||||||
|
if constant.const_defined?(name, *args)
|
||||||
|
constant = constant.const_get(name)
|
||||||
|
else
|
||||||
|
constant = constant.const_missing(name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
constant
|
||||||
|
end
|
||||||
|
end
|
||||||
+23
-7
@@ -24,6 +24,24 @@ class Foreman::Process
|
|||||||
"%s.%s" % [ entry.name, num ]
|
"%s.%s" % [ entry.name, num ]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def kill(signal)
|
||||||
|
pid && Process.kill(signal, pid)
|
||||||
|
rescue Errno::ESRCH
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def detach
|
||||||
|
pid && Process.detach(pid)
|
||||||
|
end
|
||||||
|
|
||||||
|
def alive?
|
||||||
|
kill(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
def dead?
|
||||||
|
!alive?
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def fork_with_io(command, basedir)
|
def fork_with_io(command, basedir)
|
||||||
@@ -69,12 +87,10 @@ private
|
|||||||
end
|
end
|
||||||
|
|
||||||
def with_environment(environment)
|
def with_environment(environment)
|
||||||
old_env = ENV.each_pair.inject({}) { |h,(k,v)| h.update(k => v) }
|
original = ENV.to_hash
|
||||||
environment.each { |k,v| ENV[k] = v }
|
ENV.update environment
|
||||||
ret = yield
|
yield
|
||||||
ENV.clear
|
ensure
|
||||||
old_env.each { |k,v| ENV[k] = v}
|
ENV.replace original
|
||||||
ret
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
module Foreman
|
module Foreman
|
||||||
|
|
||||||
VERSION = "0.37.0.pre3"
|
VERSION = "0.37.0.pre4"
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
require "spec_helper"
|
require "spec_helper"
|
||||||
require "foreman/cli"
|
require "foreman/cli"
|
||||||
|
|
||||||
describe "Foreman::CLI" do
|
describe "Foreman::CLI", :fakefs do
|
||||||
subject { Foreman::CLI.new }
|
subject { Foreman::CLI.new }
|
||||||
|
|
||||||
describe "start" do
|
describe "start" do
|
||||||
@@ -30,7 +30,9 @@ describe "Foreman::CLI" do
|
|||||||
it "respects --env" do
|
it "respects --env" do
|
||||||
write_procfile
|
write_procfile
|
||||||
write_env("envfile")
|
write_env("envfile")
|
||||||
mock.instance_of(Foreman::Export::Upstart).export("/upstart", { "env" => "envfile" })
|
mock_export = mock(Foreman::Export::Upstart)
|
||||||
|
mock(Foreman::Export::Upstart).new("/upstart", is_a(Foreman::Engine), { "env" => "envfile" }) { mock_export }
|
||||||
|
mock_export.export
|
||||||
foreman %{ export upstart /upstart --env envfile }
|
foreman %{ export upstart /upstart --env envfile }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -49,7 +51,7 @@ describe "Foreman::CLI" do
|
|||||||
|
|
||||||
describe "with an invalid formatter" do
|
describe "with an invalid formatter" do
|
||||||
it "prints an error" do
|
it "prints an error" do
|
||||||
mock_error(subject, "Unknown export format: invalidformatter.") do
|
mock_error(subject, "Unknown export format: invalidformatter (unable to load file 'foreman/export/invalidformatter').") do
|
||||||
subject.export("invalidformatter")
|
subject.export("invalidformatter")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -60,7 +62,9 @@ describe "Foreman::CLI" do
|
|||||||
|
|
||||||
it "runs successfully" do
|
it "runs successfully" do
|
||||||
dont_allow(subject).error
|
dont_allow(subject).error
|
||||||
mock.instance_of(Foreman::Export::Upstart).export("/tmp/foo", {})
|
mock_export = mock(Foreman::Export::Upstart)
|
||||||
|
mock(Foreman::Export::Upstart).new("/tmp/foo", is_a(Foreman::Engine), {}) { mock_export }
|
||||||
|
mock_export.export
|
||||||
subject.export("upstart", "/tmp/foo")
|
subject.export("upstart", "/tmp/foo")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
require "spec_helper"
|
require "spec_helper"
|
||||||
require "foreman/engine"
|
require "foreman/engine"
|
||||||
|
|
||||||
describe "Foreman::Engine" do
|
describe "Foreman::Engine", :fakefs do
|
||||||
subject { Foreman::Engine.new("Procfile", {}) }
|
subject { Foreman::Engine.new("Procfile", {}) }
|
||||||
|
|
||||||
describe "initialize" do
|
describe "initialize" do
|
||||||
|
|||||||
@@ -3,22 +3,27 @@ require "foreman/engine"
|
|||||||
require "foreman/export/bluepill"
|
require "foreman/export/bluepill"
|
||||||
require "tmpdir"
|
require "tmpdir"
|
||||||
|
|
||||||
describe Foreman::Export::Bluepill do
|
describe Foreman::Export::Bluepill, :fakefs do
|
||||||
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
|
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
|
||||||
let(:engine) { Foreman::Engine.new(procfile) }
|
let(:engine) { Foreman::Engine.new(procfile) }
|
||||||
let(:bluepill) { Foreman::Export::Bluepill.new(engine) }
|
let(:options) { Hash.new }
|
||||||
|
let(:bluepill) { Foreman::Export::Bluepill.new("/tmp/init", engine, options) }
|
||||||
|
|
||||||
before(:each) { load_export_templates_into_fakefs("bluepill") }
|
before(:each) { load_export_templates_into_fakefs("bluepill") }
|
||||||
before(:each) { stub(bluepill).say }
|
before(:each) { stub(bluepill).say }
|
||||||
|
|
||||||
it "exports to the filesystem" do
|
it "exports to the filesystem" do
|
||||||
bluepill.export("/tmp/init")
|
bluepill.export
|
||||||
normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app.pill"))
|
normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app.pill"))
|
||||||
end
|
end
|
||||||
|
|
||||||
it "exports to the filesystem with concurrency" do
|
context "with concurrency" do
|
||||||
bluepill.export("/tmp/init", :concurrency => "alpha=2")
|
let(:options) { Hash[:concurrency => "alpha=2"] }
|
||||||
|
|
||||||
normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app-concurrency.pill"))
|
it "exports to the filesystem with concurrency" do
|
||||||
|
bluepill.export
|
||||||
|
normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app-concurrency.pill"))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -3,10 +3,10 @@ require "foreman/engine"
|
|||||||
require "foreman/export/runit"
|
require "foreman/export/runit"
|
||||||
require "tmpdir"
|
require "tmpdir"
|
||||||
|
|
||||||
describe Foreman::Export::Runit do
|
describe Foreman::Export::Runit, :fakefs do
|
||||||
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile", 'bar=baz') }
|
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile", 'bar=baz') }
|
||||||
let(:engine) { Foreman::Engine.new(procfile) }
|
let(:engine) { Foreman::Engine.new(procfile) }
|
||||||
let(:runit) { Foreman::Export::Runit.new(engine) }
|
let(:runit) { Foreman::Export::Runit.new('/tmp/init', engine, :concurrency => 'alpha=2,bravo=1') }
|
||||||
|
|
||||||
before(:each) { load_export_templates_into_fakefs("runit") }
|
before(:each) { load_export_templates_into_fakefs("runit") }
|
||||||
before(:each) { stub(runit).say }
|
before(:each) { stub(runit).say }
|
||||||
@@ -14,7 +14,8 @@ describe Foreman::Export::Runit do
|
|||||||
|
|
||||||
it "exports to the filesystem" do
|
it "exports to the filesystem" do
|
||||||
FileUtils.mkdir_p('/tmp/init')
|
FileUtils.mkdir_p('/tmp/init')
|
||||||
runit.export('/tmp/init', :concurrency => "alpha=2,bravo=1")
|
|
||||||
|
runit.export
|
||||||
|
|
||||||
File.read("/tmp/init/app-alpha-1/run").should == example_export_file('runit/app-alpha-1-run')
|
File.read("/tmp/init/app-alpha-1/run").should == example_export_file('runit/app-alpha-1-run')
|
||||||
File.read("/tmp/init/app-alpha-1/log/run").should ==
|
File.read("/tmp/init/app-alpha-1/log/run").should ==
|
||||||
@@ -33,4 +34,8 @@ describe Foreman::Export::Runit do
|
|||||||
example_export_file('runit/app-bravo-1-log-run')
|
example_export_file('runit/app-bravo-1-log-run')
|
||||||
File.read("/tmp/init/app-bravo-1/env/PORT").should == "5100\n"
|
File.read("/tmp/init/app-bravo-1/env/PORT").should == "5100\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "creates a full path to the export directory" do
|
||||||
|
expect { runit.export }.to_not raise_error(Errno::ENOENT)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
@@ -3,16 +3,17 @@ require "foreman/engine"
|
|||||||
require "foreman/export/upstart"
|
require "foreman/export/upstart"
|
||||||
require "tmpdir"
|
require "tmpdir"
|
||||||
|
|
||||||
describe Foreman::Export::Upstart do
|
describe Foreman::Export::Upstart, :fakefs do
|
||||||
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
|
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
|
||||||
let(:engine) { Foreman::Engine.new(procfile) }
|
let(:engine) { Foreman::Engine.new(procfile) }
|
||||||
let(:upstart) { Foreman::Export::Upstart.new(engine) }
|
let(:options) { Hash.new }
|
||||||
|
let(:upstart) { Foreman::Export::Upstart.new("/tmp/init", engine, options) }
|
||||||
|
|
||||||
before(:each) { load_export_templates_into_fakefs("upstart") }
|
before(:each) { load_export_templates_into_fakefs("upstart") }
|
||||||
before(:each) { stub(upstart).say }
|
before(:each) { stub(upstart).say }
|
||||||
|
|
||||||
it "exports to the filesystem" do
|
it "exports to the filesystem" do
|
||||||
upstart.export("/tmp/init")
|
upstart.export
|
||||||
|
|
||||||
File.read("/tmp/init/app.conf").should == example_export_file("upstart/app.conf")
|
File.read("/tmp/init/app.conf").should == example_export_file("upstart/app.conf")
|
||||||
File.read("/tmp/init/app-alpha.conf").should == example_export_file("upstart/app-alpha.conf")
|
File.read("/tmp/init/app-alpha.conf").should == example_export_file("upstart/app-alpha.conf")
|
||||||
@@ -21,18 +22,23 @@ describe Foreman::Export::Upstart do
|
|||||||
File.read("/tmp/init/app-bravo-1.conf").should == example_export_file("upstart/app-bravo-1.conf")
|
File.read("/tmp/init/app-bravo-1.conf").should == example_export_file("upstart/app-bravo-1.conf")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "exports to the filesystem with concurrency" do
|
context "with concurrency" do
|
||||||
upstart.export("/tmp/init", :concurrency => "alpha=2")
|
let(:options) { Hash[:concurrency => "alpha=2"] }
|
||||||
|
|
||||||
File.read("/tmp/init/app.conf").should == example_export_file("upstart/app.conf")
|
it "exports to the filesystem with concurrency" do
|
||||||
File.read("/tmp/init/app-alpha.conf").should == example_export_file("upstart/app-alpha.conf")
|
upstart.export
|
||||||
File.read("/tmp/init/app-alpha-1.conf").should == example_export_file("upstart/app-alpha-1.conf")
|
|
||||||
File.read("/tmp/init/app-alpha-2.conf").should == example_export_file("upstart/app-alpha-2.conf")
|
File.read("/tmp/init/app.conf").should == example_export_file("upstart/app.conf")
|
||||||
File.exists?("/tmp/init/app-bravo-1.conf").should == false
|
File.read("/tmp/init/app-alpha.conf").should == example_export_file("upstart/app-alpha.conf")
|
||||||
|
File.read("/tmp/init/app-alpha-1.conf").should == example_export_file("upstart/app-alpha-1.conf")
|
||||||
|
File.read("/tmp/init/app-alpha-2.conf").should == example_export_file("upstart/app-alpha-2.conf")
|
||||||
|
File.exists?("/tmp/init/app-bravo-1.conf").should == false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with alternate templates" do
|
context "with alternate templates" do
|
||||||
let(:template_root) { "/tmp/alternate" }
|
let(:template_root) { "/tmp/alternate" }
|
||||||
|
let(:upstart) { Foreman::Export::Upstart.new("/tmp/init", engine, :template => template_root) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
FileUtils.mkdir_p template_root
|
FileUtils.mkdir_p template_root
|
||||||
@@ -40,7 +46,7 @@ describe Foreman::Export::Upstart do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "can export with alternate template files" do
|
it "can export with alternate template files" do
|
||||||
upstart.export("/tmp/init", :template => template_root)
|
upstart.export
|
||||||
|
|
||||||
File.read("/tmp/init/app.conf").should == "alternate_template\n"
|
File.read("/tmp/init/app.conf").should == "alternate_template\n"
|
||||||
end
|
end
|
||||||
@@ -61,7 +67,7 @@ describe Foreman::Export::Upstart do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "can export with alternate template files" do
|
it "can export with alternate template files" do
|
||||||
upstart.export("/tmp/init")
|
upstart.export
|
||||||
|
|
||||||
File.read("/tmp/init/app.conf").should == "default_alternate_template\n"
|
File.read("/tmp/init/app.conf").should == "default_alternate_template\n"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,2 +1,131 @@
|
|||||||
require "spec_helper"
|
require 'spec_helper'
|
||||||
require "foreman/process"
|
require 'foreman/process'
|
||||||
|
require 'ostruct'
|
||||||
|
require 'timeout'
|
||||||
|
require 'tmpdir'
|
||||||
|
|
||||||
|
describe Foreman::Process do
|
||||||
|
subject { described_class.new entry, number, port }
|
||||||
|
|
||||||
|
let(:number) { 1 }
|
||||||
|
let(:port) { 777 }
|
||||||
|
let(:command) { "script" }
|
||||||
|
let(:name) { "foobar" }
|
||||||
|
let(:entry) { OpenStruct.new :name => name, :command => command }
|
||||||
|
|
||||||
|
its(:entry) { entry }
|
||||||
|
its(:num) { number }
|
||||||
|
its(:port) { port }
|
||||||
|
its(:name) { "#{name}.#{port}" }
|
||||||
|
its(:pid) { nil }
|
||||||
|
|
||||||
|
describe '#run' do
|
||||||
|
let(:pipe) { :pipe }
|
||||||
|
let(:basedir) { Dir.mktmpdir }
|
||||||
|
let(:env) {{ 'foo' => 'bar' }}
|
||||||
|
let(:init_delta) { 0.1 }
|
||||||
|
|
||||||
|
after { FileUtils.remove_entry_secure basedir }
|
||||||
|
|
||||||
|
def run(cmd=command)
|
||||||
|
entry.command = cmd
|
||||||
|
subject.run pipe, basedir, env
|
||||||
|
subject.detach && sleep(init_delta)
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_file(executable, code)
|
||||||
|
file = File.open("#{basedir}/script", 'w') {|it| it << code }
|
||||||
|
run "#{executable} #{file.path}"
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'options' do
|
||||||
|
it 'should set PORT for environment' do
|
||||||
|
mock(subject).run_process(basedir, command, pipe) do
|
||||||
|
ENV['PORT'].should == port.to_s
|
||||||
|
end
|
||||||
|
run
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should set custom variables for environment' do
|
||||||
|
mock(subject).run_process(basedir, command, pipe) do
|
||||||
|
ENV['foo'].should == 'bar'
|
||||||
|
end
|
||||||
|
run
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should restore environment afterwards' do
|
||||||
|
mock(subject).run_process(basedir, command, pipe)
|
||||||
|
run
|
||||||
|
ENV.should_not include('PORT', 'foo')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'process' do
|
||||||
|
around do |spec|
|
||||||
|
IO.pipe do |reader, writer|
|
||||||
|
@reader, @writer = reader, writer
|
||||||
|
spec.run
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:pipe) { @writer }
|
||||||
|
let(:output) { @reader.read_nonblock 1024 }
|
||||||
|
|
||||||
|
it 'should not block' do
|
||||||
|
expect {
|
||||||
|
Timeout.timeout(2*init_delta) { run 'sleep 2' }
|
||||||
|
}.should_not raise_exception
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should be alive' do
|
||||||
|
run 'sleep 1'
|
||||||
|
subject.should be_alive
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should be dead' do
|
||||||
|
run 'exit'
|
||||||
|
subject.should be_dead
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should be killable' do
|
||||||
|
run 'sleep 1'
|
||||||
|
subject.kill 'TERM'
|
||||||
|
subject.should be_dead
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should send different signals' do
|
||||||
|
run_file 'ruby', <<-CODE
|
||||||
|
trap "TERM", "IGNORE"
|
||||||
|
loop { sleep 1 }
|
||||||
|
CODE
|
||||||
|
sleep 1 # wait for ruby to start
|
||||||
|
subject.should be_alive
|
||||||
|
subject.kill 'TERM'
|
||||||
|
subject.should be_alive
|
||||||
|
subject.kill 'KILL'
|
||||||
|
subject.should be_dead
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should redirect stdout' do
|
||||||
|
run 'echo hey'
|
||||||
|
output.should include('hey')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should redirect stderr' do
|
||||||
|
run 'echo hey >2'
|
||||||
|
output.should include('hey')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should handle variables' do
|
||||||
|
run 'echo $PORT'
|
||||||
|
output.should include('777')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should handle arguments' do
|
||||||
|
pending
|
||||||
|
run %{ sh -c "trap '' TERM; sleep 10" }
|
||||||
|
subject.should be_alive
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|||||||
@@ -8,13 +8,8 @@ describe Foreman do
|
|||||||
it { should be_a String }
|
it { should be_a String }
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "::load_env!(env_file)" do
|
describe "::load_env!(env_file)", :fakefs do
|
||||||
before do
|
|
||||||
FakeFS.activate!
|
|
||||||
end
|
|
||||||
|
|
||||||
after do
|
after do
|
||||||
FakeFS.deactivate!
|
|
||||||
ENV['FOO'] = nil
|
ENV['FOO'] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -78,8 +78,9 @@ def normalize_space(s)
|
|||||||
end
|
end
|
||||||
|
|
||||||
RSpec.configure do |config|
|
RSpec.configure do |config|
|
||||||
|
config.treat_symbols_as_metadata_keys_with_true_values = true
|
||||||
config.color_enabled = true
|
config.color_enabled = true
|
||||||
config.order = 'rand'
|
config.order = 'rand'
|
||||||
config.include FakeFS::SpecHelpers
|
config.include FakeFS::SpecHelpers, :fakefs
|
||||||
config.mock_with :rr
|
config.mock_with :rr
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user