Compare commits

...
25 Commits
Author SHA1 Message Date
David Dollar fc95936327 0.30.0 2011-12-22 16:34:02 -05:00
David Dollar 0c27f78d46 compatibility with ruby 1.8 2011-12-22 16:33:49 -05:00
David Dollar 356c61f471 0.29.0 2011-12-22 01:03:11 -05:00
David Dollar dcff4da220 0.28.0.pre2 2011-12-08 17:59:40 -08:00
David Dollar 888520ee99 fix pipe error 2011-12-08 17:59:27 -08:00
David Dollar c7b6b334fd 0.28.0.pre1 2011-12-08 17:54:19 -08:00
David Dollar f476920a05 Merge branch 'fork' 2011-12-08 17:53:50 -08:00
David Dollar 5436b68cf1 wip 2011-12-08 17:53:13 -08:00
David Dollar c9411cd2b1 wip 2011-12-08 17:18:21 -08:00
David Dollar 6e95d1ce94 wip 2011-12-08 16:57:33 -08:00
David Dollar c5548a345e wip 2011-12-08 16:19:19 -08:00
David Dollar f668b87660 wip 2011-12-08 14:04:29 -08:00
David Dollar 914a1ee958 0.27.0 2011-12-05 15:46:40 -05:00
David Dollar e1c2946718 add changelog 2011-12-05 15:46:22 -05:00
David Dollar 6160246da0 Merge pull request #103 from csquared/load_env_from_irb
Load env from irb
2011-12-05 12:37:05 -08:00
Chris Continanza 2e8030dbd4 refactor load_env to apply_environment 2011-12-05 12:36:23 -08:00
Chris Continanza 58e4cdafd7 rename load! to load_env! 2011-12-05 12:27:32 -08:00
Chris Continanza 44a5dff724 use ./.env as default 2011-12-05 11:37:51 -08:00
Chris Continanza e33921f083 load contents from env file 2011-12-03 15:43:51 -08:00
Chris Continanza 79cf541ebe refactor engine to expose env methods 2011-12-03 15:43:07 -08:00
David Dollar 39ace26ae1 disable email notifications 2011-11-14 12:23:50 -05:00
David Dollar c383359136 add travis config 2011-11-11 23:16:59 -05:00
David Dollar a5e094353c 0.26.1 2011-11-10 15:02:19 -05:00
David Dollar 12720b4c12 fix colors during execution of single process 2011-11-10 15:02:09 -05:00
David Dollar 1c732a4658 add runit export to docs 2011-11-08 18:00:23 -05:00
22 changed files with 324 additions and 161 deletions
+14
View File
@@ -0,0 +1,14 @@
script: bundle exec rake spec
rvm:
- 1.8.7
- 1.9.2
- 1.9.3
notifications:
email: false
webhooks:
on_success: always
on_failure: always
urls:
- https://dx-helper.herokuapp.com/travis
+11
View File
@@ -0,0 +1,11 @@
0.26.1 12/05/2011 6160246da0fafe9cf8fde188d94bbc6babc667dc
==========================================================
Merge pull request #103 from csquared/load_env_from_irb [David Dollar]
refactor load_env to apply_environment [Chris Continanza]
rename load! to load_env! [Chris Continanza]
use ./.env as default [Chris Continanza]
load contents from env file [Chris Continanza]
refactor engine to expose env methods [Chris Continanza]
disable email notifications [David Dollar]
add travis config [David Dollar]
+2 -2
View File
@@ -1,7 +1,7 @@
PATH PATH
remote: . remote: .
specs: specs:
foreman (0.26.0) foreman (0.30.0)
term-ansicolor (~> 1.0.5) term-ansicolor (~> 1.0.5)
thor (>= 0.13.6) thor (>= 0.13.6)
@@ -42,7 +42,7 @@ GEM
diff-lcs (~> 1.1.2) diff-lcs (~> 1.1.2)
rspec-mocks (2.6.0) rspec-mocks (2.6.0)
rubyzip (0.9.4) rubyzip (0.9.4)
term-ansicolor (1.0.6) term-ansicolor (1.0.7)
thor (0.14.6) thor (0.14.6)
xml-simple (1.0.15) xml-simple (1.0.15)
+19
View File
@@ -166,3 +166,22 @@ end
Dir[File.expand_path("../dist/**/*.rake", __FILE__)].each do |rake| Dir[File.expand_path("../dist/**/*.rake", __FILE__)].each do |rake|
import rake import rake
end end
task :changelog do
timestamp = Time.now.utc.strftime('%m/%d/%Y')
sha = `git log | head -1`.split(' ').last
changelog = ["#{version} #{timestamp} #{sha}"]
changelog << ('=' * changelog[0].length)
changelog << ''
last_sha = `cat Changelog | head -1`.split(' ').last
shortlog = `git log #{last_sha}..HEAD --pretty=format:'%s [%an]'`
changelog << shortlog.split("\n")
changelog.concat ['', '', '']
old_changelog = File.read('Changelog')
File.open('Changelog', 'w') do |file|
file.write(changelog.join("\n"))
file.write(old_changelog)
end
end
Executable
+2
View File
@@ -0,0 +1,2 @@
#!/bin/sh
exec $1 2>&1
+2 -2
View File
@@ -3,7 +3,7 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
app.uid = "<%= user %>" app.uid = "<%= user %>"
app.gid = "<%= user %>" app.gid = "<%= user %>"
<% engine.processes.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, options[:port]) %>
app.process("<%= process.name %>-<%=num%>") do |process| app.process("<%= process.name %>-<%=num%>") do |process|
@@ -19,7 +19,7 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
process.monitor_children do |children| process.monitor_children do |children|
children.stop_command "kill -QUIT {{PID}}" children.stop_command "kill -QUIT {{PID}}"
end end
process.group = "<%= app %>-<%= process.name %>" process.group = "<%= app %>-<%= process.name %>"
end end
<% end %> <% end %>
+10
View File
@@ -4,5 +4,15 @@ module Foreman
class AppDoesNotExist < Exception; end class AppDoesNotExist < Exception; end
# load contents of env_file into ENV
def self.load_env!(env_file = './.env')
require 'foreman/engine'
Foreman::Engine.load_env!(env_file)
end
def self.runner
File.expand_path("../../bin/runner", __FILE__)
end
end end
+5 -10
View File
@@ -8,20 +8,15 @@ class Foreman::CLI < Thor
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile" class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
desc "start [PROCESS]", "Start the application, or a specific process" desc "start", "Start the application"
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env" method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
method_option :port, :type => :numeric, :aliases => "-p" method_option :port, :type => :numeric, :aliases => "-p"
method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"' method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
def start(process=nil) def start
check_procfile! check_procfile!
engine.start
if process
engine.execute(process)
else
engine.start
end
end end
desc "export FORMAT LOCATION", "Export the application to another process management format" desc "export FORMAT LOCATION", "Export the application to another process management format"
@@ -55,8 +50,8 @@ class Foreman::CLI < Thor
desc "check", "Validate your application's Procfile" desc "check", "Validate your application's Procfile"
def check def check
error "no processes defined" unless engine.processes.length > 0 error "no processes defined" unless engine.procfile.entries.length > 0
display "valid procfile detected (#{engine.processes.map(&:name).join(', ')})" display "valid procfile detected (#{engine.procfile.process_names.join(', ')})"
end end
private ###################################################################### private ######################################################################
+102 -83
View File
@@ -12,7 +12,6 @@ class Foreman::Engine
attr_reader :procfile attr_reader :procfile
attr_reader :directory attr_reader :directory
attr_reader :environment
attr_reader :options attr_reader :options
extend Term::ANSIColor extend Term::ANSIColor
@@ -24,37 +23,27 @@ class Foreman::Engine
@directory = File.expand_path(File.dirname(procfile)) @directory = File.expand_path(File.dirname(procfile))
@options = options @options = options
@environment = read_environment_files(options[:env]) @environment = read_environment_files(options[:env])
@output_mutex = Mutex.new
end
def self.load_env!(env_file)
@environment = read_environment_files(env_file)
apply_environment!
end end
def start def start
proctitle "ruby: foreman master" proctitle "ruby: foreman master"
termtitle "#{File.basename(@directory)} - foreman (#{processes.size} processes)" termtitle "#{File.basename(@directory)} - foreman"
processes.each do |process|
process.color = next_color
fork process
end
trap("TERM") { puts "SIGTERM received"; terminate_gracefully } trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
trap("INT") { puts "SIGINT received"; terminate_gracefully } trap("INT") { puts "SIGINT received"; terminate_gracefully }
assign_colors
spawn_processes
watch_for_output
watch_for_termination watch_for_termination
end end
def execute(name)
error "no such process: #{name}" unless procfile[name]
fork procfile[name]
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
trap("INT") { puts "SIGINT received"; terminate_gracefully }
watch_for_termination
end
def processes
procfile.processes
end
def port_for(process, num, base_port=nil) def port_for(process, num, base_port=nil)
base_port ||= 5000 base_port ||= 5000
offset = procfile.process_names.index(process.name) * 100 offset = procfile.process_names.index(process.name) * 100
@@ -63,47 +52,20 @@ class Foreman::Engine
private ###################################################################### private ######################################################################
def fork(process) def spawn_processes
concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency]) concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency])
1.upto(concurrency[process.name]) do |num| procfile.entries.each do |entry|
fork_individual(process, num, port_for(process, num, @options[:port])) reader, writer = IO.pipe
entry.spawn(concurrency[entry.name], writer, @directory, @environment, base_port).each do |process|
running_processes[process.pid] = process
readers[process] = reader
end
end end
end end
def fork_individual(process, num, port) def base_port
@environment.each { |k,v| ENV[k] = v } options[:port] || 5000
ENV["PORT"] = port.to_s
ENV["PS"] = "#{process.name}.#{num}"
pid = Process.fork do
run(process)
end
info "started with pid #{pid}", process
running_processes[pid] = process
end
def run(process)
proctitle "ruby: foreman #{process.name}"
trap("SIGINT", "IGNORE")
begin
Dir.chdir directory do
PTY.spawn(process.command) do |stdin, stdout, pid|
trap("SIGTERM") { Process.kill("SIGTERM", pid) }
until stdin.eof?
info stdin.gets, process
end
end
end
rescue PTY::ChildExited, Interrupt, Errno::EIO, Errno::ENOENT
begin
info "process exiting", process
rescue Interrupt
end
end
end end
def kill_all(signal="SIGTERM") def kill_all(signal="SIGTERM")
@@ -115,27 +77,57 @@ private ######################################################################
def terminate_gracefully def terminate_gracefully
info "sending SIGTERM to all processes" info "sending SIGTERM to all processes"
kill_all "SIGTERM" kill_all "SIGTERM"
Timeout.timeout(3) { Process.waitall } Timeout.timeout(5) { Process.waitall }
rescue Timeout::Error rescue Timeout::Error
info "sending SIGKILL to all processes" info "sending SIGKILL to all processes"
kill_all "SIGKILL" kill_all "SIGKILL"
end end
def watch_for_output
Thread.new do
begin
loop do
rs, ws = IO.select(readers.values, [], [], 1)
(rs || []).each do |r|
ps, message = r.gets.split(",", 2)
color = colors[ps.split(".").first]
info message, ps, color
end
end
rescue Exception => ex
puts ex.message
puts ex.backtrace
end
end
end
def watch_for_termination def watch_for_termination
pid, status = Process.wait2 pid, status = Process.wait2
process = running_processes.delete(pid) process = running_processes.delete(pid)
info "process terminated", process info "process terminated", process.name
terminate_gracefully terminate_gracefully
kill_all kill_all
rescue Errno::ECHILD rescue Errno::ECHILD
end end
def info(message, process=nil) def info(message, name="system", color=Term::ANSIColor.white)
print process.color if process print color
print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(process)} | " print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
print Term::ANSIColor.reset print Term::ANSIColor.reset
print message.chomp print message.chomp
puts puts ""
end
def print(message=nil)
@output_mutex.synchronize do
$stdout.print message
end
end
def puts(message=nil)
@output_mutex.synchronize do
$stdout.puts message
end
end end
def error(message) def error(message)
@@ -151,9 +143,8 @@ private ######################################################################
end end
end end
def pad_process_name(process) def pad_process_name(name="system")
name = process ? "#{ENV["PS"]}" : "system" name.to_s.ljust(longest_process_name + 3) # add 3 for process number padding
name.ljust(longest_process_name + 3) # add 3 for process number padding
end end
def proctitle(title) def proctitle(title)
@@ -168,33 +159,61 @@ private ######################################################################
@running_processes ||= {} @running_processes ||= {}
end end
def readers
@readers ||= {}
end
def colors
@colors ||= {}
end
def assign_colors
procfile.entries.each do |entry|
colors[entry.name] = next_color
end
end
def process_by_reader(reader)
readers.invert[reader]
end
def next_color def next_color
@current_color ||= -1 @current_color ||= -1
@current_color += 1 @current_color += 1
@current_color >= COLORS.length ? "" : COLORS[@current_color] @current_color >= COLORS.length ? "" : COLORS[@current_color]
end end
def read_environment_files(filenames) module Env
environment = {} attr_reader :environment
(filenames || "").split(",").map(&:strip).each do |filename| def read_environment_files(filenames)
error "No such file: #{filename}" unless File.exists?(filename) environment = {}
environment.merge!(read_environment(filename))
end
environment.merge!(read_environment(".env")) unless filenames (filenames || "").split(",").map(&:strip).each do |filename|
environment error "No such file: #{filename}" unless File.exists?(filename)
end environment.merge!(read_environment(filename))
def read_environment(filename)
return {} unless File.exists?(filename)
File.read(filename).split("\n").inject({}) do |hash, line|
if line =~ /\A([A-Za-z_0-9]+)=(.*)\z/
hash[$1] = $2
end end
hash
environment.merge!(read_environment(".env")) unless filenames
environment
end
def read_environment(filename)
return {} unless File.exists?(filename)
File.read(filename).split("\n").inject({}) do |hash, line|
if line =~ /\A([A-Za-z_0-9]+)=(.*)\z/
hash[$1] = $2
end
hash
end
end
def apply_environment!
@environment.each { |k,v| ENV[k] = v }
end end
end end
include Env
extend Env
end end
+1 -1
View File
@@ -12,7 +12,7 @@ class Foreman::Export::Inittab < Foreman::Export::Base
inittab = [] inittab = []
inittab << "# ----- foreman #{app} processes -----" inittab << "# ----- foreman #{app} processes -----"
engine.processes.inject(1) do |index, process| engine.procfile.entries.inject(1) do |index, process|
1.upto(concurrency[process.name]) do |num| 1.upto(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, options[:port])
+16 -16
View File
@@ -3,58 +3,58 @@ 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(location, options={})
error("Must specify a location") unless location error("Must specify a location") unless location
app = options[:app] || File.basename(engine.directory) app = options[:app] || File.basename(engine.directory)
user = options[:user] || app user = options[:user] || app
log_root = options[:log] || "/var/log/#{app}" log_root = options[:log] || "/var/log/#{app}"
template_root = options[:template] template_root = options[:template]
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency]) 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.processes.each do |process| engine.procfile.entries.each do |process|
1.upto(concurrency[process.name]) do |num| 1.upto(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"
create_directory process_directory create_directory process_directory
create_directory process_env_directory create_directory process_env_directory
create_directory process_log_directory create_directory process_log_directory
run = ERB.new(run_template).result(binding) run = ERB.new(run_template).result(binding)
write_file "#{process_directory}/run", run write_file "#{process_directory}/run", run
port = engine.port_for(process, num, options[:port]) port = engine.port_for(process, num, options[: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))
environment_variables.each_pair do |var, env| environment_variables.each_pair do |var, env|
write_file "#{process_env_directory}/#{var.upcase}", env write_file "#{process_env_directory}/#{var.upcase}", env
end end
log_run = ERB.new(log_run_template).result(binding) log_run = ERB.new(log_run_template).result(binding)
write_file "#{process_log_directory}/run", log_run write_file "#{process_log_directory}/run", log_run
end end
end end
end end
private private
def create_directory(location) def create_directory(location)
say "creating: #{location}" say "creating: #{location}"
FileUtils.mkdir(location) FileUtils.mkdir(location)
end end
def inline_variables(command) def inline_variables(command)
variable_name_regex = variable_name_regex =
Hash[*command.scan(ENV_VARIABLE_REGEX).flatten] Hash[*command.scan(ENV_VARIABLE_REGEX).flatten]
end end
end end
+1 -1
View File
@@ -26,7 +26,7 @@ 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.processes.each do |process| engine.procfile.entries.each do |process|
next if (conc = concurrency[process.name]) < 1 next if (conc = 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)
+63 -6
View File
@@ -2,13 +2,70 @@ require "foreman"
class Foreman::Process class Foreman::Process
attr_reader :name attr_reader :entry
attr_reader :command attr_reader :num
attr_accessor :color attr_reader :pid
attr_reader :port
def initialize(name, command) def initialize(entry, num, port)
@name = name @entry = entry
@command = command @num = num
@port = port
end
def run(pipe, basedir, environment)
Dir.chdir(basedir) do
with_environment(environment.merge("PORT" => port.to_s)) do
run_process entry.command, pipe
end
end
end
def name
"%s.%s" % [ entry.name, num ]
end
private
def fork_with_io(command)
io = case RUBY_VERSION
when /^1\.9\./
IO.popen([Foreman.runner, replace_command_env(command)], "w+")
when /^1\.8\./
full_command = replace_command_env(command).gsub("'", "\\'")
IO.popen("#{Foreman.runner} '#{full_command}'", "w+")
else
raise "Unknown Ruby version: #{RUBY_VERSION}"
end
[ io, io.pid ]
end
def run_process(command, pipe)
io, @pid = fork_with_io(command)
trap("SIGTERM") { "got sigterm for %d" % @pid }
output pipe, "started with pid %d" % @pid
Thread.new do
until io.eof?
output pipe, io.gets
end
end
end
def output(pipe, message)
pipe.puts "%s,%s" % [ name, message ]
end
def replace_command_env(command)
command.gsub(/\$(\w+)/) { |e| ENV[e[1..-1]] }
end
def with_environment(environment)
old_env = ENV.each_pair.inject({}) { |h,(k,v)| h.update(k => v) }
environment.each { |k,v| ENV[k] = v }
ret = yield
ENV.clear
old_env.each { |k,v| ENV[k] = v}
ret
end end
end end
+9 -8
View File
@@ -1,4 +1,5 @@
require "foreman" require "foreman"
require "foreman/procfile_entry"
# A valid Procfile entry is captured by this regex. # A valid Procfile entry is captured by this regex.
# All other lines are ignored. # All other lines are ignored.
@@ -10,18 +11,18 @@ require "foreman"
# #
class Foreman::Procfile class Foreman::Procfile
attr_reader :processes attr_reader :entries
def initialize(filename) def initialize(filename)
@processes = parse_procfile(filename) @entries = parse_procfile(filename)
end
def process_names
processes.map(&:name)
end end
def [](name) def [](name)
processes.detect { |process| process.name == name } entries.detect { |entry| entry.name == name }
end
def process_names
entries.map(&:name)
end end
private private
@@ -29,7 +30,7 @@ private
def parse_procfile(filename) def parse_procfile(filename)
File.read(filename).split("\n").map do |line| File.read(filename).split("\n").map do |line|
if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/ if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
Foreman::Process.new($1, $2) Foreman::ProcfileEntry.new($1, $2)
end end
end.compact end.compact
end end
+22
View File
@@ -0,0 +1,22 @@
require "foreman"
class Foreman::ProcfileEntry
attr_reader :name
attr_reader :command
attr_accessor :color
def initialize(name, command)
@name = name
@command = command
end
def spawn(num, pipe, basedir, environment, base_port)
(1..num).to_a.map do |n|
process = Foreman::Process.new(self, n, base_port + (n-1))
process.run(pipe, basedir, environment)
process
end
end
end
+1 -1
View File
@@ -1,5 +1,5 @@
module Foreman module Foreman
VERSION = "0.26.0" VERSION = "0.30.0"
end end
+4 -1
View File
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3 .\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3
. .
.TH "FOREMAN" "1" "November 2011" "Foreman 0.25.0" "Foreman Manual" .TH "FOREMAN" "1" "November 2011" "Foreman 0.26.0" "Foreman Manual"
. .
.SH "NAME" .SH "NAME"
\fBforeman\fR \- manage Procfile\-based applications \fBforeman\fR \- manage Procfile\-based applications
@@ -85,6 +85,9 @@ bluepill
inittab inittab
. .
.IP "\(bu" 4 .IP "\(bu" 4
runit
.
.IP "\(bu" 4
upstart upstart
. .
.IP "" 0 .IP "" 0
+2
View File
@@ -83,6 +83,8 @@ foreman currently supports the following output formats:
* inittab * inittab
* runit
* upstart * upstart
## INITTAB EXPORT ## INITTAB EXPORT
+12 -25
View File
@@ -24,8 +24,9 @@ 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(subject).fork(subject.procfile["alpha"]) mock.instance_of(Foreman::Process).run_process("./alpha", is_a(IO))
mock(subject).fork(subject.procfile["bravo"]) mock.instance_of(Foreman::Process).run_process("./bravo", is_a(IO))
mock(subject).watch_for_output
mock(subject).watch_for_termination mock(subject).watch_for_termination
subject.start subject.start
end end
@@ -33,29 +34,14 @@ 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(engine).fork_individual(engine.procfile["alpha"], 1, 5000) mock.instance_of(Foreman::Process).run_process("./alpha", is_a(IO)).twice
mock(engine).fork_individual(engine.procfile["alpha"], 2, 5001) mock.instance_of(Foreman::Process).run_process("./bravo", is_a(IO))
mock(engine).fork_individual(engine.procfile["bravo"], 1, 5100) mock(engine).watch_for_output
mock(engine).watch_for_termination mock(engine).watch_for_termination
engine.start engine.start
end end
end end
describe "execute" do
it "runs the processes" do
write_procfile
mock(subject).fork(subject.procfile["alpha"])
mock(subject).watch_for_termination
subject.execute("alpha")
end
it "shows an error running a process that doesnt exist" do
write_procfile
mock(subject).puts("ERROR: no such process: foo")
lambda { subject.execute("foo") }.should raise_error(SystemExit)
end
end
describe "environment" do describe "environment" do
before(:each) do before(:each) do
write_procfile write_procfile
@@ -66,9 +52,10 @@ describe "Foreman::Engine" do
File.open("/tmp/env", "w") { |f| f.puts("FOO=baz") } File.open("/tmp/env", "w") { |f| f.puts("FOO=baz") }
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env") engine = Foreman::Engine.new("Procfile", :env => "/tmp/env")
stub(engine).info stub(engine).info
mock(engine).spawn_processes
mock(engine).watch_for_termination mock(engine).watch_for_termination
engine.environment.should == {"FOO"=>"baz"} engine.environment.should == {"FOO"=>"baz"}
engine.execute("alpha") engine.start
end end
it "should read more than one if specified" do it "should read more than one if specified" do
@@ -76,9 +63,10 @@ describe "Foreman::Engine" do
File.open("/tmp/env2", "w") { |f| f.puts("BAZ=qux") } File.open("/tmp/env2", "w") { |f| f.puts("BAZ=qux") }
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env1,/tmp/env2") engine = Foreman::Engine.new("Procfile", :env => "/tmp/env1,/tmp/env2")
stub(engine).info stub(engine).info
mock(engine).spawn_processes
mock(engine).watch_for_termination mock(engine).watch_for_termination
engine.environment.should == { "FOO"=>"bar", "BAZ"=>"qux" } engine.environment.should == { "FOO"=>"bar", "BAZ"=>"qux" }
engine.execute("alpha") engine.start
end end
it "should fail if specified and doesnt exist" do it "should fail if specified and doesnt exist" do
@@ -89,11 +77,10 @@ describe "Foreman::Engine" do
it "should read .env if none specified" do it "should read .env if none specified" do
File.open(".env", "w") { |f| f.puts("FOO=qoo") } File.open(".env", "w") { |f| f.puts("FOO=qoo") }
engine = Foreman::Engine.new("Procfile") engine = Foreman::Engine.new("Procfile")
stub(engine).info mock(engine).spawn_processes
mock(engine).watch_for_termination mock(engine).watch_for_termination
mock(engine).fork_individual(anything, anything, anything)
engine.environment.should == {"FOO"=>"qoo"} engine.environment.should == {"FOO"=>"qoo"}
engine.execute("bravo") engine.start
end end
end end
end end
+1 -2
View File
@@ -13,8 +13,7 @@ describe Foreman::Export::Bluepill do
it "exports to the filesystem" do it "exports to the filesystem" do
bluepill.export("/tmp/init", :concurrency => "alpha=2") bluepill.export("/tmp/init", :concurrency => "alpha=2")
File.read("/tmp/init/app.pill").should == example_export_file("bluepill/app.pill") File.read("/tmp/init/app.pill").should == example_export_file("bluepill/app.pill")
end end
end end
+22
View File
@@ -8,4 +8,26 @@ describe Foreman do
it { should be_a String } it { should be_a String }
end end
describe "::load_env!(env_file)" do
before do
FakeFS.activate!
end
after do
FakeFS.deactivate!
ENV['FOO'] = nil
end
it "should load env_file into ENV" do
File.open("/tmp/env1", "w") { |f| f.puts("FOO=bar") }
Foreman.load_env!("/tmp/env1")
ENV['FOO'].should == 'bar'
end
it "should assume env_file in ./.env" do
File.open("./.env", "w") { |f| f.puts("FOO=bar") }
Foreman.load_env!
ENV['FOO'].should == 'bar'
end
end
end end
+3 -3
View File
@@ -19,7 +19,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
process.monitor_children do |children| process.monitor_children do |children|
children.stop_command "kill -QUIT {{PID}}" children.stop_command "kill -QUIT {{PID}}"
end end
process.group = "app-alpha" process.group = "app-alpha"
end end
@@ -37,7 +37,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
process.monitor_children do |children| process.monitor_children do |children|
children.stop_command "kill -QUIT {{PID}}" children.stop_command "kill -QUIT {{PID}}"
end end
process.group = "app-alpha" process.group = "app-alpha"
end end
@@ -57,7 +57,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
process.monitor_children do |children| process.monitor_children do |children|
children.stop_command "kill -QUIT {{PID}}" children.stop_command "kill -QUIT {{PID}}"
end end
process.group = "app-bravo" process.group = "app-bravo"
end end