Compare commits

...
Author SHA1 Message Date
David Dollar a04032454a 0.37.0.pre4 2012-01-24 08:03:07 -08:00
David Dollar 29c94c785d Merge pull request #130 from clowder/foreman
---

Because `Your::Runit::Env != My::Runit::Env` I thought that it would be nice to be able to include your own export format as a another gem along side Foreman; similar to MultiJson’s engines but using constantize because we are specifying formats via the command line.

Cheers,
Chris
2012-01-24 08:00:12 -08:00
Chris Lowder a0a2dd9454 Don't expose the options hash. 2012-01-24 15:10:25 +00:00
Chris Lowder 1b701cddf3 Simplify subclassing by adding all arguments to the initializer. Also, clean up the method signatures for existing exporters. 2012-01-24 15:10:18 +00:00
Chris Lowder 4080b3f1f2 Add ZenTest as a development dependency, there is an autotest folder but the gem is missing. 2012-01-24 14:37:57 +00:00
Chris Lowder 84c49ae2b8 Attempt to require the custom export class. 2012-01-24 14:36:38 +00:00
Chris Lowder d54b46806c Catching more than we need to. 2012-01-24 14:36:38 +00:00
Chris Lowder bc1c5e4c74 Extract commonality into the base class, make life easy for our plugin writers. 2012-01-24 14:36:38 +00:00
Chris Lowder ed4a32518f Removing the hard coding of export formats allowing the user to 'plug-in' their own export format. 2012-01-24 14:31:45 +00:00
David Dollar c7167e1c83 Update README.markdown 2012-01-23 14:20:45 -08:00
David Dollar db93432118 Revert "try on more rubies"
This reverts commit 5eada6b7cc.
2012-01-23 00:45:25 -05:00
David Dollar 5eada6b7cc try on more rubies 2012-01-23 00:43:25 -05:00
David Dollar 5e46b1f43a fix webhook url 2012-01-23 00:41:10 -05:00
David Dollar ef723f5831 Update README.markdown 2012-01-23 00:37:58 -05:00
David Dollar 52e24ef64b Update README.markdown 2012-01-23 00:31:57 -05:00
David Dollar 0b1daf1927 ensure we have non-nil data, fixes #111 2012-01-22 22:01:03 -05:00
David Dollar e0d84a56d7 make sure error method exists. fixes #104 2012-01-22 21:55:55 -05:00
David Dollar 7ee2edcc60 Merge pull request #136 from fnichol/foreman
---

Simple enough, but this helps when re-exporting runit services. Cheers, and thanks!

Conflicts:
	spec/foreman/export/runit_spec.rb
2012-01-22 21:46:57 -05:00
David Dollar d428ac5356 remove other from install instructions 2012-01-22 21:42:51 -05:00
David Dollar 5351b49fee cleanup 2012-01-22 21:41:10 -05:00
David Dollar 6ebe76d8c1 Revert "tweak authors"
This reverts commit 7c43c672c9
2012-01-22 21:39:24 -05:00
David Dollar 7c43c672c9 tweak authors 2012-01-22 21:39:09 -05:00
David Dollar 41d9050ae3 fix authors 2012-01-22 21:38:17 -05:00
David Dollar 09e9cefa3a readme tweaks 2012-01-22 21:36:38 -05:00
David Dollar 85efe5c1ba readme tweaks 2012-01-22 21:36:24 -05:00
David Dollar c21634c04e fix up authors 2012-01-22 21:34:23 -05:00
David Dollar 6d4f3476f1 Update README.markdown 2012-01-22 21:32:33 -05:00
David Dollar 724812d6e3 fix up specs 2012-01-22 21:17:54 -05:00
David Dollar ce6ec4848d we're not chdiring any more 2012-01-22 21:17:38 -05:00
David Dollar a37a097f73 use strings rather than symbols to better emulate the real thing 2012-01-22 21:17:18 -05:00
David Dollar f28725bdac Merge pull request #139 from brainopia/foreman
---

If you remember Ive wanted to implement a prototype of process-tree monitor (and therefore restore support for complex commands), but decided to start with specing current behavior and marking desired behavior as pending specs.

This pull-request is a first pass. Its mainly Foreman::Process specs and a bit of refactoring. Specs for Foreman::Engine will be coming next (theyll cover a wide ground of possible uses). Afterwards I will replace machinery a bit to remove pending specs and ensure the rest is still passing on all available to me platforms.

If you have any notices, Ill gladly hear them.
2012-01-22 20:58:51 -05:00
David Dollar 51eee011ce 0.37.0.pre3 2012-01-22 20:40:50 -05:00
David Dollar 6c8c848f54 normalize platform names 2012-01-22 20:33:14 -05:00
David Dollar f60c4cb767 windows support 2012-01-22 20:33:14 -05:00
David Dollar 4ad49cb058 add windows support 2012-01-22 20:33:13 -05:00
David Dollar e993af2b20 dont need pty 2012-01-22 20:33:13 -05:00
David Dollar d7000bccfa fix specs 2012-01-22 19:14:24 -05:00
brainopia ade0005a92 Add specs for Foreman::Process#run 2012-01-22 22:07:53 +04:00
brainopia 241b91a0d5 Implement Foreman::Process#kill,detach,alive?,dead? 2012-01-22 22:07:01 +04:00
brainopia 047f106d48 - Use explicit fakefs tag in specs
- Clean up trailing whitespace
2012-01-22 15:00:43 +04:00
brainopia df043e60d8 Simplify Foreman::Process#with_environment 2012-01-22 08:57:09 +04:00
brainopia 158c184f8c Add specs for options of Foreman::Process#run 2012-01-22 08:55:45 +04:00
brainopia 2ed1fe8d44 Add specs for initialization of Foreman::Process 2012-01-22 08:54:42 +04:00
Fletcher Nichol e76f3533dc runit creates a full path to export directory. 2012-01-18 20:13:08 -07:00
30 changed files with 422 additions and 177 deletions
-1
View File
@@ -1 +0,0 @@
FOO=bar
+1 -1
View File
@@ -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
+5
View File
@@ -2,6 +2,10 @@ source "http://rubygems.org"
gemspec gemspec
platform :mingw do
gem "win32console", "~> 1.3.0"
end
platform :jruby do platform :jruby do
gem "posix-spawn", "~> 0.3.6" gem "posix-spawn", "~> 0.3.6"
end end
@@ -14,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
+6 -1
View File
@@ -1,13 +1,14 @@
PATH PATH
remote: . remote: .
specs: specs:
foreman (0.37.0.pre2) 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
@@ -47,13 +48,16 @@ GEM
rubyzip (0.9.4) rubyzip (0.9.4)
term-ansicolor (1.0.7) term-ansicolor (1.0.7)
thor (0.14.6) thor (0.14.6)
win32console (1.3.0-x86-mingw32)
xml-simple (1.0.15) xml-simple (1.0.15)
PLATFORMS PLATFORMS
java java
ruby ruby
x86-mingw32
DEPENDENCIES DEPENDENCIES
ZenTest
aws-s3 aws-s3
fakefs (~> 0.3.2) fakefs (~> 0.3.2)
foreman! foreman!
@@ -65,3 +69,4 @@ DEPENDENCIES
rr (~> 1.0.2) rr (~> 1.0.2)
rspec (~> 2.0) rspec (~> 2.0)
rubyzip rubyzip
win32console (~> 1.3.0)
+23 -34
View File
@@ -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
+5
View File
@@ -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"
+1 -1
View File
@@ -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) %>"
+1 -1
View File
@@ -1,5 +1,5 @@
file pkg("foreman-#{version}-jruby.gem") => distribution_files do |t| file pkg("foreman-#{version}-jruby.gem") => distribution_files do |t|
sh "env PLATFORM=jruby gem build foreman.gemspec" sh "env PLATFORM=java gem build foreman.gemspec"
sh "mv foreman-#{version}-java.gem #{t.name}" sh "mv foreman-#{version}-java.gem #{t.name}"
end end
+14
View File
@@ -0,0 +1,14 @@
file pkg("foreman-#{version}-mingw32.gem") => distribution_files do |t|
sh "env PLATFORM=mingw32 gem build foreman.gemspec"
sh "mv foreman-#{version}-mingw32.gem #{t.name}"
end
task "mingw32:build" => pkg("foreman-#{version}-mingw32.gem")
task "mingw32:clean" do
clean pkg("foreman-#{version}-mingw32.gem")
end
task "mingw32:release" => "mingw32:build" do |t|
sh "parka push -f #{pkg("foreman-#{version}-mingw32.gem")}"
end
+6 -1
View File
@@ -19,8 +19,13 @@ Gem::Specification.new do |gem|
gem.add_dependency 'term-ansicolor', '~> 1.0.7' gem.add_dependency 'term-ansicolor', '~> 1.0.7'
gem.add_dependency 'thor', '>= 0.13.6' gem.add_dependency 'thor', '>= 0.13.6'
if ENV["PLATFORM"] == "jruby" if ENV["PLATFORM"] == "java"
gem.add_dependency "posix-spawn", "~> 0.3.6" gem.add_dependency "posix-spawn", "~> 0.3.6"
gem.platform = Gem::Platform.new("java") gem.platform = Gem::Platform.new("java")
end end
if ENV["PLATFORM"] == "mingw32"
gem.add_dependency "win32console", "~> 1.3.0"
gem.platform = Gem::Platform.new("mingw32")
end
end end
+8 -1
View File
@@ -14,5 +14,12 @@ module Foreman
File.expand_path("../../bin/runner", __FILE__) File.expand_path("../../bin/runner", __FILE__)
end end
end def self.jruby?
defined?(RUBY_PLATFORM) and RUBY_PLATFORM == "java"
end
def self.windows?
defined?(RUBY_PLATFORM) and RUBY_PLATFORM =~ /(win|w)32$/
end
end
+13 -8
View File
@@ -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
+11 -9
View File
@@ -2,7 +2,6 @@ require "foreman"
require "foreman/process" require "foreman/process"
require "foreman/procfile" require "foreman/procfile"
require "foreman/utils" require "foreman/utils"
require "pty"
require "tempfile" require "tempfile"
require "timeout" require "timeout"
require "term/ansicolor" require "term/ansicolor"
@@ -74,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
@@ -89,11 +88,14 @@ private ######################################################################
def watch_for_output def watch_for_output
Thread.new do Thread.new do
require "win32console" if Foreman.windows?
begin begin
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
@@ -156,7 +153,7 @@ private ######################################################################
end end
def termtitle(title) def termtitle(title)
printf("\033]0;#{title}\007") printf("\033]0;#{title}\007") unless Foreman.windows?
end end
def running_processes def running_processes
@@ -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
+10 -3
View File
@@ -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
+5 -7
View File
@@ -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
+6 -8
View File
@@ -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
+8 -10
View File
@@ -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)
+8 -10
View File
@@ -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
+50
View File
@@ -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
+29 -14
View File
@@ -24,29 +24,46 @@ class Foreman::Process
"%s.%s" % [ entry.name, num ] "%s.%s" % [ entry.name, num ]
end end
private def kill(signal)
pid && Process.kill(signal, pid)
def jruby? rescue Errno::ESRCH
defined?(RUBY_PLATFORM) and RUBY_PLATFORM == "java" false
end end
def detach
pid && Process.detach(pid)
end
def alive?
kill(0)
end
def dead?
!alive?
end
private
def fork_with_io(command, basedir) def fork_with_io(command, basedir)
reader, writer = IO.pipe reader, writer = IO.pipe
command = replace_command_env(command) command = replace_command_env(command)
pid = if jruby? pid = if Foreman.windows?
Dir.chdir(basedir) do
Process.spawn command, :out => writer, :err => writer
end
elsif Foreman.jruby?
require "posix/spawn" require "posix/spawn"
POSIX::Spawn.spawn(Foreman.runner, "-d", basedir, command, { POSIX::Spawn.spawn(Foreman.runner, "-d", basedir, command, {
:out => writer, :err => writer :out => writer, :err => writer
}) })
else else
fork do fork do
trap("INT", "IGNORE")
writer.sync = true writer.sync = true
$stdout.reopen writer $stdout.reopen writer
$stderr.reopen writer $stderr.reopen writer
reader.close reader.close
exec Foreman.runner, "-d", basedir, command exec Foreman.runner, "-d", basedir, command
end end
end end
[ reader, pid ] [ reader, pid ]
end end
@@ -70,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 -1
View File
@@ -1,5 +1,5 @@
module Foreman module Foreman
VERSION = "0.37.0.pre2" VERSION = "0.37.0.pre4"
end end
+8 -4
View File
@@ -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
+5 -5
View File
@@ -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
@@ -24,8 +24,8 @@ describe "Foreman::Engine" do
describe "start" do describe "start" do
it "forks the processes" do it "forks the processes" do
write_procfile write_procfile
mock.instance_of(Foreman::Process).run_process("./alpha", is_a(IO)) mock.instance_of(Foreman::Process).run_process(Dir.pwd, "./alpha", is_a(IO))
mock.instance_of(Foreman::Process).run_process("./bravo", is_a(IO)) mock.instance_of(Foreman::Process).run_process(Dir.pwd, "./bravo", is_a(IO))
mock(subject).watch_for_output mock(subject).watch_for_output
mock(subject).watch_for_termination mock(subject).watch_for_termination
subject.start subject.start
@@ -34,8 +34,8 @@ describe "Foreman::Engine" do
it "handles concurrency" do it "handles concurrency" do
write_procfile write_procfile
engine = Foreman::Engine.new("Procfile",:concurrency => "alpha=2") engine = Foreman::Engine.new("Procfile",:concurrency => "alpha=2")
mock.instance_of(Foreman::Process).run_process("./alpha", is_a(IO)).twice mock.instance_of(Foreman::Process).run_process(Dir.pwd, "./alpha", is_a(IO)).twice
mock.instance_of(Foreman::Process).run_process("./bravo", is_a(IO)).never mock.instance_of(Foreman::Process).run_process(Dir.pwd, "./bravo", is_a(IO)).never
mock(engine).watch_for_output mock(engine).watch_for_output
mock(engine).watch_for_termination mock(engine).watch_for_termination
engine.start engine.start
+12 -7
View File
@@ -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
+8 -3
View File
@@ -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
+19 -13
View File
@@ -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
+131 -2
View File
@@ -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
+1 -6
View File
@@ -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
View File
@@ -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