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
This commit is contained in:
@@ -18,6 +18,7 @@ group :development do
|
||||
gem 'rcov', '~> 0.9.8'
|
||||
gem 'rr', '~> 1.0.2'
|
||||
gem 'rspec', '~> 2.0'
|
||||
gem 'ZenTest'
|
||||
gem 'aws-s3'
|
||||
gem "rubyzip"
|
||||
end
|
||||
|
||||
@@ -8,6 +8,7 @@ PATH
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
ZenTest (4.6.2)
|
||||
aws-s3 (0.6.2)
|
||||
builder
|
||||
mime-types
|
||||
@@ -56,6 +57,7 @@ PLATFORMS
|
||||
x86-mingw32
|
||||
|
||||
DEPENDENCIES
|
||||
ZenTest
|
||||
aws-s3
|
||||
fakefs (~> 0.3.2)
|
||||
foreman!
|
||||
|
||||
@@ -5,7 +5,7 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
|
||||
|
||||
<% engine.procfile.entries.each do |process| %>
|
||||
<% 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|
|
||||
process.start_command = "<%= process.command.gsub("$PORT", port.to_s) %>"
|
||||
|
||||
|
||||
+13
-8
@@ -1,10 +1,14 @@
|
||||
require "foreman"
|
||||
require "foreman/helpers"
|
||||
require "foreman/engine"
|
||||
require "foreman/export"
|
||||
require "thor"
|
||||
require "yaml"
|
||||
|
||||
class Foreman::CLI < Thor
|
||||
include Foreman::Helpers
|
||||
|
||||
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
|
||||
|
||||
desc "start", "Start the application"
|
||||
|
||||
@@ -42,16 +46,17 @@ class Foreman::CLI < Thor
|
||||
def export(format, location=nil)
|
||||
check_procfile!
|
||||
|
||||
formatter = case format
|
||||
when "inittab" then Foreman::Export::Inittab
|
||||
when "upstart" then Foreman::Export::Upstart
|
||||
when "bluepill" then Foreman::Export::Bluepill
|
||||
when "runit" then Foreman::Export::Runit
|
||||
else error "Unknown export format: #{format}."
|
||||
begin
|
||||
require "foreman/export/#{ format.tr('-', '_') }"
|
||||
classy_format = classify(format)
|
||||
formatter = constantize("Foreman::Export::#{ classy_format }")
|
||||
rescue NameError => ex
|
||||
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
|
||||
|
||||
formatter.new(engine).export(location, options)
|
||||
|
||||
formatter.new(location, engine, options).export
|
||||
rescue Foreman::Export::Exception => ex
|
||||
error ex.message
|
||||
end
|
||||
|
||||
@@ -3,10 +3,17 @@ require "foreman/utils"
|
||||
|
||||
class Foreman::Export::Base
|
||||
|
||||
attr_reader :engine
|
||||
attr_reader :location, :engine, :app, :log, :port, :user, :template, :concurrency
|
||||
|
||||
def initialize(engine)
|
||||
@engine = engine
|
||||
def initialize(location, engine, options={})
|
||||
@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
|
||||
|
||||
def export
|
||||
|
||||
@@ -3,23 +3,21 @@ require "foreman/export"
|
||||
|
||||
class Foreman::Export::Bluepill < Foreman::Export::Base
|
||||
|
||||
def export(location, options={})
|
||||
def export
|
||||
error("Must specify a location") unless location
|
||||
|
||||
FileUtils.mkdir_p location
|
||||
|
||||
app = options[:app] || File.basename(engine.directory)
|
||||
user = options[:user] || app
|
||||
log_root = options[:log] || "/var/log/#{app}"
|
||||
template_root = options[:template]
|
||||
app = self.app || File.basename(engine.directory)
|
||||
user = self.user || app
|
||||
log_root = self.log || "/var/log/#{app}"
|
||||
template_root = self.template
|
||||
|
||||
Dir["#{location}/#{app}.pill"].each do |file|
|
||||
say "cleaning up: #{file}"
|
||||
FileUtils.rm(file)
|
||||
end
|
||||
|
||||
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
||||
|
||||
master_template = export_template("bluepill", "master.pill.erb", template_root)
|
||||
master_config = ERB.new(master_template).result(binding)
|
||||
write_file "#{location}/#{app}.pill", master_config
|
||||
|
||||
@@ -2,20 +2,18 @@ require "foreman/export"
|
||||
|
||||
class Foreman::Export::Inittab < Foreman::Export::Base
|
||||
|
||||
def export(fname=nil, options={})
|
||||
app = options[:app] || File.basename(engine.directory)
|
||||
user = options[:user] || app
|
||||
log_root = options[:log] || "/var/log/#{app}"
|
||||
|
||||
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
||||
def export
|
||||
app = self.app || File.basename(engine.directory)
|
||||
user = self.user || app
|
||||
log_root = self.log || "/var/log/#{app}"
|
||||
|
||||
inittab = []
|
||||
inittab << "# ----- foreman #{app} processes -----"
|
||||
|
||||
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)
|
||||
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'"
|
||||
index += 1
|
||||
end
|
||||
|
||||
@@ -4,21 +4,19 @@ require "foreman/export"
|
||||
class Foreman::Export::Runit < Foreman::Export::Base
|
||||
ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
|
||||
|
||||
def export(location, options={})
|
||||
def export
|
||||
error("Must specify a location") unless location
|
||||
|
||||
app = options[:app] || File.basename(engine.directory)
|
||||
user = options[:user] || app
|
||||
log_root = options[:log] || "/var/log/#{app}"
|
||||
template_root = options[:template]
|
||||
|
||||
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
||||
app = self.app || File.basename(engine.directory)
|
||||
user = self.user || app
|
||||
log_root = self.log || "/var/log/#{app}"
|
||||
template_root = self.template
|
||||
|
||||
run_template = export_template('runit', 'run.erb', template_root)
|
||||
log_run_template = export_template('runit', 'log_run.erb', template_root)
|
||||
|
||||
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_env_directory = "#{process_directory}/env"
|
||||
process_log_directory = "#{process_directory}/log"
|
||||
@@ -31,7 +29,7 @@ class Foreman::Export::Runit < Foreman::Export::Base
|
||||
write_file "#{process_directory}/run", 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}.
|
||||
merge(engine.environment).
|
||||
merge(inline_variables(process.command))
|
||||
|
||||
@@ -3,23 +3,21 @@ require "foreman/export"
|
||||
|
||||
class Foreman::Export::Upstart < Foreman::Export::Base
|
||||
|
||||
def export(location, options={})
|
||||
def export
|
||||
error("Must specify a location") unless location
|
||||
|
||||
FileUtils.mkdir_p location
|
||||
|
||||
app = options[:app] || File.basename(engine.directory)
|
||||
user = options[:user] || app
|
||||
log_root = options[:log] || "/var/log/#{app}"
|
||||
template_root = options[:template]
|
||||
app = self.app || File.basename(engine.directory)
|
||||
user = self.user || app
|
||||
log_root = self.log || "/var/log/#{app}"
|
||||
template_root = self.template
|
||||
|
||||
Dir["#{location}/#{app}*.conf"].each do |file|
|
||||
say "cleaning up: #{file}"
|
||||
FileUtils.rm(file)
|
||||
end
|
||||
|
||||
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
||||
|
||||
master_template = export_template("upstart", "master.conf.erb", template_root)
|
||||
master_config = ERB.new(master_template).result(binding)
|
||||
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)
|
||||
|
||||
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_config = ERB.new(process_master_template).result(binding)
|
||||
write_file "#{location}/#{app}-#{process.name}.conf", process_master_config
|
||||
|
||||
1.upto(concurrency[process.name]) do |num|
|
||||
port = engine.port_for(process, num, options[:port])
|
||||
1.upto(self.concurrency[process.name]) do |num|
|
||||
port = engine.port_for(process, num, self.port)
|
||||
process_config = ERB.new(process_template).result(binding)
|
||||
write_file "#{location}/#{app}-#{process.name}-#{num}.conf", process_config
|
||||
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
|
||||
@@ -30,7 +30,9 @@ describe "Foreman::CLI", :fakefs do
|
||||
it "respects --env" do
|
||||
write_procfile
|
||||
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 }
|
||||
end
|
||||
end
|
||||
@@ -49,7 +51,7 @@ describe "Foreman::CLI", :fakefs do
|
||||
|
||||
describe "with an invalid formatter" 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")
|
||||
end
|
||||
end
|
||||
@@ -60,7 +62,9 @@ describe "Foreman::CLI", :fakefs do
|
||||
|
||||
it "runs successfully" do
|
||||
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")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,20 +5,25 @@ require "tmpdir"
|
||||
|
||||
describe Foreman::Export::Bluepill, :fakefs do
|
||||
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
|
||||
let(:engine) { Foreman::Engine.new(procfile) }
|
||||
let(:bluepill) { Foreman::Export::Bluepill.new(engine) }
|
||||
let(:engine) { Foreman::Engine.new(procfile) }
|
||||
let(:options) { Hash.new }
|
||||
let(:bluepill) { Foreman::Export::Bluepill.new("/tmp/init", engine, options) }
|
||||
|
||||
before(:each) { load_export_templates_into_fakefs("bluepill") }
|
||||
before(:each) { stub(bluepill).say }
|
||||
|
||||
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"))
|
||||
end
|
||||
|
||||
it "exports to the filesystem with concurrency" do
|
||||
bluepill.export("/tmp/init", :concurrency => "alpha=2")
|
||||
context "with concurrency" do
|
||||
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
|
||||
|
||||
@@ -6,7 +6,7 @@ require "tmpdir"
|
||||
describe Foreman::Export::Runit, :fakefs do
|
||||
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile", 'bar=baz') }
|
||||
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) { stub(runit).say }
|
||||
@@ -14,7 +14,8 @@ describe Foreman::Export::Runit, :fakefs do
|
||||
|
||||
it "exports to the filesystem" do
|
||||
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/log/run").should ==
|
||||
@@ -35,6 +36,6 @@ describe Foreman::Export::Runit, :fakefs do
|
||||
end
|
||||
|
||||
it "creates a full path to the export directory" do
|
||||
expect { runit.export('/tmp/init', :concurrency => "alpha=2,bravo=1") }.to_not raise_error(Errno::ENOENT)
|
||||
expect { runit.export }.to_not raise_error(Errno::ENOENT)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,14 +5,15 @@ require "tmpdir"
|
||||
|
||||
describe Foreman::Export::Upstart, :fakefs do
|
||||
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
|
||||
let(:engine) { Foreman::Engine.new(procfile) }
|
||||
let(:upstart) { Foreman::Export::Upstart.new(engine) }
|
||||
let(:engine) { Foreman::Engine.new(procfile) }
|
||||
let(:options) { Hash.new }
|
||||
let(:upstart) { Foreman::Export::Upstart.new("/tmp/init", engine, options) }
|
||||
|
||||
before(:each) { load_export_templates_into_fakefs("upstart") }
|
||||
before(:each) { stub(upstart).say }
|
||||
|
||||
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-alpha.conf").should == example_export_file("upstart/app-alpha.conf")
|
||||
@@ -21,18 +22,23 @@ describe Foreman::Export::Upstart, :fakefs do
|
||||
File.read("/tmp/init/app-bravo-1.conf").should == example_export_file("upstart/app-bravo-1.conf")
|
||||
end
|
||||
|
||||
it "exports to the filesystem with concurrency" do
|
||||
upstart.export("/tmp/init", :concurrency => "alpha=2")
|
||||
context "with concurrency" do
|
||||
let(:options) { Hash[:concurrency => "alpha=2"] }
|
||||
|
||||
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-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
|
||||
it "exports to the filesystem with concurrency" do
|
||||
upstart.export
|
||||
|
||||
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-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
|
||||
|
||||
context "with alternate templates" do
|
||||
let(:template_root) { "/tmp/alternate" }
|
||||
let(:upstart) { Foreman::Export::Upstart.new("/tmp/init", engine, :template => template_root) }
|
||||
|
||||
before do
|
||||
FileUtils.mkdir_p template_root
|
||||
@@ -40,7 +46,7 @@ describe Foreman::Export::Upstart, :fakefs do
|
||||
end
|
||||
|
||||
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"
|
||||
end
|
||||
@@ -61,7 +67,7 @@ describe Foreman::Export::Upstart, :fakefs do
|
||||
end
|
||||
|
||||
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"
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user