Compare commits

...
30 Commits
Author SHA1 Message Date
David Dollar 8908a66e90 0.26.0 2011-11-08 17:59:15 -05:00
David Dollar f63c0b0ca0 Merge pull request #95 from mlangenberg/runit-export
Add runit export format.
2011-11-08 14:58:27 -08:00
Matthijs Langenberg 676d3ff8d1 Add runit export format.
Fix #28
2011-11-08 23:50:33 +01:00
Mark McGranaghan 615aada17e sketch terminal title
Conflicts:

	lib/foreman/engine.rb
2011-11-08 13:23:06 -05:00
David Dollar 2e1d1c7c15 update docs 2011-11-08 12:02:50 -05:00
David Dollar bf832ffd9f Merge pull request #82 from trileuco/master
Documentation of new process name restrictions
2011-11-08 09:01:56 -08:00
David Dollar b9bfede48a Merge pull request #83 from argami/master
Controlling exception on ruby1.9.3-Head
2011-11-08 09:00:47 -08:00
David Dollar bed8323029 ensure concurrency=0 is handled during export 2011-11-08 11:58:03 -05:00
David Dollar f6ef5a5b4f display error when process doesnt exist and is run by name 2011-11-08 11:54:55 -05:00
David Dollar f3c1e76860 Merge pull request #92 from iain/patch-1
Processes is not a Hash, but an Array.
2011-11-08 08:45:38 -08:00
Iain Hecker caa688cdc6 Processes is not a Hash, but an Array. 2011-10-27 00:28:06 +03:00
David Dollar c6a410b664 Merge pull request #87 from clowder/fix_specs_passing_by_accident
Removed memoization [Foreman::Utils.parse_concurrency]
2011-10-19 10:07:42 -07:00
Chris Lowder 02c8d2cb10 Memoizing at the Class level wreaks havoc on the specs. 2011-10-19 18:02:49 +01:00
David Dollar ada41ce311 0.25.0 2011-10-17 16:21:15 -04:00
David Dollar 8f1c752a77 Merge pull request #85 from hlegendre/master
Allow numbers in the ENV variable keys
2011-10-12 14:20:50 -07:00
Hugues Le Gendre ddf25fe0ea Numbers should be allowed in key names of ENV no ? 2011-10-12 17:06:37 +03:00
Marcos Muino Garcia 9dac91a624 Added Procfile process name format documentation 2011-10-11 11:14:57 +02:00
Gamaliel Toro cdaeb646ac - Controlling non-existing command in ruby1.9.3-HEAD 2011-10-10 23:35:51 +02:00
David Dollar 86e4251840 add ability to test full comamnd line 2011-10-04 11:30:28 -04:00
David Dollar ba18f7e589 Merge pull request #74 from tomafro/master
Allow export using a specified environment file, using the --env or -e option
2011-10-04 08:20:04 -07:00
David Dollar be73e8500f 0.24.0 2011-10-04 10:43:33 -04:00
David Dollar d26ca669a1 define procfile regex, refactoring 2011-10-04 10:38:13 -04:00
David Dollar a3e758ab6c 0.23.1 2011-10-04 09:56:39 -04:00
David Dollar 5dc232a7b1 Merge pull request #79 from fdr/runner-dead-code
Eliminate dead code
2011-10-04 05:14:50 -07:00
Dan Farina 4191cb7b9c Eliminate dead code
An oversight; this code should probably have been pruned in
20e598abcc.

Signed-off-by: Dan Farina <drfarina@acm.org>
2011-10-04 00:19:04 -07:00
David Dollar 90d4dffb82 tweak docs 2011-09-16 18:46:10 -04:00
David Dollar 823f307abc doc updates 2011-09-16 18:42:57 -04:00
David Dollar ed44a11e21 0.23.0 2011-09-16 18:25:02 -04:00
David Dollar 5719f4fc72 allow multiple environment files to be specified 2011-09-16 18:24:38 -04:00
Tom Ward 47008fb921 Allow export using a specified environment file, using the --env or -e option 2011-09-16 08:31:29 +01:00
30 changed files with 293 additions and 130 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ group :development do
gem 'parka' gem 'parka'
gem 'rake' gem 'rake'
gem 'ronn' gem 'ronn'
gem 'fakefs', '~> 0.2.1' gem 'fakefs', '~> 0.3.2'
gem 'rcov', '~> 0.9.8' gem 'rcov', '~> 0.9.8'
gem 'rr', '~> 1.0.2' gem 'rr', '~> 1.0.2'
gem 'rspec', '~> 2.6.0' gem 'rspec', '~> 2.6.0'
+3 -3
View File
@@ -1,7 +1,7 @@
PATH PATH
remote: . remote: .
specs: specs:
foreman (0.22.0) foreman (0.26.0)
term-ansicolor (~> 1.0.5) term-ansicolor (~> 1.0.5)
thor (>= 0.13.6) thor (>= 0.13.6)
@@ -15,7 +15,7 @@ GEM
builder (3.0.0) builder (3.0.0)
crack (0.1.8) crack (0.1.8)
diff-lcs (1.1.2) diff-lcs (1.1.2)
fakefs (0.2.1) fakefs (0.3.2)
hpricot (0.8.2) hpricot (0.8.2)
mime-types (1.16) mime-types (1.16)
mustache (0.11.2) mustache (0.11.2)
@@ -51,7 +51,7 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
aws-s3 aws-s3
fakefs (~> 0.2.1) fakefs (~> 0.3.2)
foreman! foreman!
parka parka
rake rake
+1 -1
View File
@@ -1,2 +1,2 @@
ticker: ruby ./ticker $PORT ticker: ruby ./ticker $PORT
error : ruby ./error error: ruby ./error
+1 -1
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.values.each do |process| %> <% engine.processes.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|
+7
View File
@@ -0,0 +1,7 @@
#!/bin/sh
set -e
LOG=<%= log_root %>/<%= process.name %>-<%= num %>
test -d "$LOG" || mkdir -p m2750 "$LOG" && chown <%= user %> "$LOG"
exec chpst -u <%= user %> svlogd "$LOG"
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
cd <%= engine.directory %>
exec chpst -u <%= user %> -e <%= process_env_directory %> <%= process.command %>
+4 -3
View File
@@ -28,6 +28,7 @@ class Foreman::CLI < Thor
method_option :app, :type => :string, :aliases => "-a" method_option :app, :type => :string, :aliases => "-a"
method_option :log, :type => :string, :aliases => "-l" method_option :log, :type => :string, :aliases => "-l"
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 :user, :type => :string, :aliases => "-u" method_option :user, :type => :string, :aliases => "-u"
method_option :template, :type => :string, :aliases => "-t" method_option :template, :type => :string, :aliases => "-t"
@@ -41,6 +42,7 @@ class Foreman::CLI < Thor
when "inittab" then Foreman::Export::Inittab when "inittab" then Foreman::Export::Inittab
when "upstart" then Foreman::Export::Upstart when "upstart" then Foreman::Export::Upstart
when "bluepill" then Foreman::Export::Bluepill when "bluepill" then Foreman::Export::Bluepill
when "runit" then Foreman::Export::Runit
else error "Unknown export format: #{format}." else error "Unknown export format: #{format}."
end end
@@ -53,9 +55,8 @@ class Foreman::CLI < Thor
desc "check", "Validate your application's Procfile" desc "check", "Validate your application's Procfile"
def check def check
processes = engine.processes_in_order.map { |p| p.first } error "no processes defined" unless engine.processes.length > 0
error "no processes defined" unless processes.length > 0 display "valid procfile detected (#{engine.processes.map(&:name).join(', ')})"
display "valid procfile detected (#{processes.join(', ')})"
end end
private ###################################################################### private ######################################################################
+48 -82
View File
@@ -1,5 +1,6 @@
require "foreman" require "foreman"
require "foreman/process" require "foreman/process"
require "foreman/procfile"
require "foreman/utils" require "foreman/utils"
require "pty" require "pty"
require "tempfile" require "tempfile"
@@ -19,45 +20,18 @@ class Foreman::Engine
COLORS = [ cyan, yellow, green, magenta, red ] COLORS = [ cyan, yellow, green, magenta, red ]
def initialize(procfile, options={}) def initialize(procfile, options={})
@procfile = read_procfile(procfile) @procfile = Foreman::Procfile.new(procfile)
@directory = File.expand_path(File.dirname(procfile)) @directory = File.expand_path(File.dirname(procfile))
@options = options @options = options
@environment = read_environment(options[:env]) @environment = read_environment_files(options[:env])
end
def processes
@processes ||= begin
@order = []
procfile.split("\n").inject({}) do |hash, line|
next hash if line.strip == ""
name, command = line.split(/\s*:\s+/, 2)
unless command
warn_deprecated_procfile!
name, command = line.split(/ +/, 2)
end
process = Foreman::Process.new(name, command)
process.color = next_color
@order << process.name
hash.update(process.name => process)
end
end
end
def process_order
processes
@order.uniq
end
def processes_in_order
process_order.map do |name|
[name, processes[name]]
end
end end
def start def start
proctitle "ruby: foreman master" proctitle "ruby: foreman master"
termtitle "#{File.basename(@directory)} - foreman (#{processes.size} processes)"
processes_in_order.each do |name, process| processes.each do |process|
process.color = next_color
fork process fork process
end end
@@ -68,8 +42,8 @@ class Foreman::Engine
end end
def execute(name) def execute(name)
error "no such process: #{name}" unless procfile[name]
fork processes[name] fork procfile[name]
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 }
@@ -77,9 +51,13 @@ class Foreman::Engine
watch_for_termination watch_for_termination
end 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 = processes_in_order.map { |p| p.first }.index(process.name) * 100 offset = procfile.process_names.index(process.name) * 100
base_port.to_i + offset + num - 1 base_port.to_i + offset + num - 1
end end
@@ -120,7 +98,7 @@ private ######################################################################
end end
end end
end end
rescue PTY::ChildExited, Interrupt, Errno::EIO rescue PTY::ChildExited, Interrupt, Errno::EIO, Errno::ENOENT
begin begin
info "process exiting", process info "process exiting", process
rescue Interrupt rescue Interrupt
@@ -134,6 +112,24 @@ private ######################################################################
end end
end end
def terminate_gracefully
info "sending SIGTERM to all processes"
kill_all "SIGTERM"
Timeout.timeout(3) { Process.waitall }
rescue Timeout::Error
info "sending SIGKILL to all processes"
kill_all "SIGKILL"
end
def watch_for_termination
pid, status = Process.wait2
process = running_processes.delete(pid)
info "process terminated", process
terminate_gracefully
kill_all
rescue Errno::ECHILD
end
def info(message, process=nil) def info(message, process=nil)
print process.color if process print process.color if process
print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(process)} | " print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(process)} | "
@@ -149,7 +145,7 @@ private ######################################################################
def longest_process_name def longest_process_name
@longest_process_name ||= begin @longest_process_name ||= begin
longest = processes.keys.map { |name| name.length }.sort.last longest = procfile.process_names.map { |name| name.length }.sort.last
longest = 6 if longest < 6 # system longest = 6 if longest < 6 # system
longest longest
end end
@@ -160,28 +156,12 @@ private ######################################################################
name.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 print_info
info "currently running processes:"
running_processes.each do |pid, process|
info "pid #{pid}", process
end
end
def proctitle(title) def proctitle(title)
$0 = title $0 = title
end end
def read_procfile(procfile) def termtitle(title)
File.read(procfile) printf("\033]0;#{title}\007")
end
def watch_for_termination
pid, status = Process.wait2
process = running_processes.delete(pid)
info "process terminated", process
terminate_gracefully
kill_all
rescue Errno::ECHILD
end end
def running_processes def running_processes
@@ -194,41 +174,27 @@ private ######################################################################
@current_color >= COLORS.length ? "" : COLORS[@current_color] @current_color >= COLORS.length ? "" : COLORS[@current_color]
end end
def warn_deprecated_procfile! def read_environment_files(filenames)
return if @already_warned_deprecated
@already_warned_deprecated = true
puts "!!! This format of Procfile is deprecated, and will not work starting in v0.12"
puts "!!! Use a colon to separate the process name from the command"
puts "!!! e.g. web: thin start"
end
def read_environment(filename)
error "No such file: #{filename}" if filename && !File.exists?(filename)
filename ||= ".env"
environment = {} environment = {}
if File.exists?(filename) (filenames || "").split(",").map(&:strip).each do |filename|
File.read(filename).split("\n").each do |line| error "No such file: #{filename}" unless File.exists?(filename)
if line =~ /\A([A-Za-z_]+)=(.*)\z/ environment.merge!(read_environment(filename))
environment[$1] = $2
end
end
end end
environment.merge!(read_environment(".env")) unless filenames
environment environment
end end
def runner def read_environment(filename)
File.expand_path("../../../bin/foreman-runner", __FILE__) return {} unless File.exists?(filename)
end
def terminate_gracefully File.read(filename).split("\n").inject({}) do |hash, line|
info "sending SIGTERM to all processes" if line =~ /\A([A-Za-z_0-9]+)=(.*)\z/
kill_all "SIGTERM" hash[$1] = $2
Timeout.timeout(3) { Process.waitall } end
rescue Timeout::Error hash
info "sending SIGKILL to all processes" end
kill_all "SIGKILL"
end end
end end
+1
View File
@@ -8,3 +8,4 @@ require "foreman/export/base"
require "foreman/export/inittab" require "foreman/export/inittab"
require "foreman/export/upstart" require "foreman/export/upstart"
require "foreman/export/bluepill" require "foreman/export/bluepill"
require "foreman/export/runit"
-1
View File
@@ -23,7 +23,6 @@ class Foreman::Export::Bluepill < Foreman::Export::Base
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
end end
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.values.inject(1) do |index, process| engine.processes.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])
+60
View File
@@ -0,0 +1,60 @@
require "erb"
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={})
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])
run_template = export_template('runit', 'run.erb', template_root)
log_run_template = export_template('runit', 'log_run.erb', template_root)
engine.processes.each do |process|
1.upto(concurrency[process.name]) do |num|
process_directory = "#{location}/#{app}-#{process.name}-#{num}"
process_env_directory = "#{process_directory}/env"
process_log_directory = "#{process_directory}/log"
create_directory process_directory
create_directory process_env_directory
create_directory process_log_directory
run = ERB.new(run_template).result(binding)
write_file "#{process_directory}/run", run
port = engine.port_for(process, num, options[:port])
environment_variables = {'PORT' => port}.
merge(engine.environment).
merge(inline_variables(process.command))
environment_variables.each_pair do |var, env|
write_file "#{process_env_directory}/#{var.upcase}", env
end
log_run = ERB.new(log_run_template).result(binding)
write_file "#{process_log_directory}/run", log_run
end
end
end
private
def create_directory(location)
say "creating: #{location}"
FileUtils.mkdir(location)
end
def inline_variables(command)
variable_name_regex =
Hash[*command.scan(ENV_VARIABLE_REGEX).flatten]
end
end
+2 -1
View File
@@ -26,7 +26,8 @@ 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.values.each do |process| engine.processes.each do |process|
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)
write_file "#{location}/#{app}-#{process.name}.conf", process_master_config write_file "#{location}/#{app}-#{process.name}.conf", process_master_config
+37
View File
@@ -0,0 +1,37 @@
require "foreman"
# A valid Procfile entry is captured by this regex.
# All other lines are ignored.
#
# /^([A-Za-z0-9_]+):\s*(.+)$/
#
# $1 = name
# $2 = command
#
class Foreman::Procfile
attr_reader :processes
def initialize(filename)
@processes = parse_procfile(filename)
end
def process_names
processes.map(&:name)
end
def [](name)
processes.detect { |process| process.name == name }
end
private
def parse_procfile(filename)
File.read(filename).split("\n").map do |line|
if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
Foreman::Process.new($1, $2)
end
end.compact
end
end
+1 -1
View File
@@ -3,7 +3,7 @@ require "foreman"
class Foreman::Utils class Foreman::Utils
def self.parse_concurrency(concurrency) def self.parse_concurrency(concurrency)
@concurrency ||= begin begin
pairs = concurrency.to_s.gsub(/\s/, "").split(",") pairs = concurrency.to_s.gsub(/\s/, "").split(",")
pairs.inject(Hash.new(1)) do |hash, pair| pairs.inject(Hash.new(1)) do |hash, pair|
process, amount = pair.split("=") process, amount = pair.split("=")
+1 -1
View File
@@ -1,5 +1,5 @@
module Foreman module Foreman
VERSION = "0.22.0" VERSION = "0.26.0"
end end
+4 -4
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" "September 2011" "Foreman 0.21.0" "Foreman Manual" .TH "FOREMAN" "1" "November 2011" "Foreman 0.25.0" "Foreman Manual"
. .
.SH "NAME" .SH "NAME"
\fBforeman\fR \- manage Procfile\-based applications \fBforeman\fR \- manage Procfile\-based applications
@@ -69,7 +69,7 @@ These options control all modes of foreman\'s operation\.
. .
.TP .TP
\fB\-e\fR, \fB\-\-env\fR \fB\-e\fR, \fB\-\-env\fR
Specify an alternate environment file\. Specify an alternate environment file\. You can specify more than one file by using: \fB\-\-env file1,file2\fR\.
. .
.TP .TP
\fB\-f\fR, \fB\-\-procfile\fR \fB\-f\fR, \fB\-\-procfile\fR
@@ -132,7 +132,7 @@ job: bundle exec rake jobs:work
.IP "" 0 .IP "" 0
. .
.P .P
You can validate your Procfile format using the \fBcheck\fR command A process name may contain letters, numbers amd the underscore character\. You can validate your Procfile format using the \fBcheck\fR command:
. .
.IP "" 4 .IP "" 4
. .
@@ -165,7 +165,7 @@ If a \fB\.foreman\fR file exists in the current directory, default options will
. .
.nf .nf
concurrency: alpha=0 concurrency: alpha=0,bravo=1
port: 15000 port: 15000
. .
.fi .fi
+5 -3
View File
@@ -67,7 +67,8 @@ The following options control how the application is run:
These options control all modes of foreman's operation. These options control all modes of foreman's operation.
* `-e`, `--env`: * `-e`, `--env`:
Specify an alternate environment file. Specify an alternate environment file. You can specify more than one
file by using: `--env file1,file2`.
* `-f`, `--procfile`: * `-f`, `--procfile`:
Specify an alternate location for the application's Procfile. This file's Specify an alternate location for the application's Procfile. This file's
@@ -112,7 +113,8 @@ to run it.
web: bundle exec thin start web: bundle exec thin start
job: bundle exec rake jobs:work job: bundle exec rake jobs:work
You can validate your Procfile format using the `check` command A process name may contain letters, numbers amd the underscore character.
You can validate your Procfile format using the `check` command:
$ foreman check $ foreman check
@@ -131,7 +133,7 @@ If a `.foreman` file exists in the current directory, default options will
be read from it. This file should be in YAML format with the long option be read from it. This file should be in YAML format with the long option
name as keys. Example: name as keys. Example:
concurrency: alpha=0 concurrency: alpha=0,bravo=1
port: 15000 port: 15000
## EXAMPLES ## EXAMPLES
+9
View File
@@ -26,6 +26,15 @@ describe "Foreman::CLI" do
end end
describe "export" do describe "export" do
describe "options" do
it "respects --env" do
write_procfile
write_env("envfile")
mock.instance_of(Foreman::Export::Upstart).export("/upstart", { "env" => "envfile" })
foreman %{ export upstart /upstart --env envfile }
end
end
describe "with a non-existent Procfile" do describe "with a non-existent Procfile" do
it "prints an error" do it "prints an error" do
mock_error(subject, "Procfile does not exist.") do mock_error(subject, "Procfile does not exist.") do
+24 -22
View File
@@ -15,21 +15,8 @@ describe "Foreman::Engine" do
before { write_procfile } before { write_procfile }
it "reads the processes" do it "reads the processes" do
subject.processes["alpha"].command.should == "./alpha" subject.procfile["alpha"].command.should == "./alpha"
subject.processes["bravo"].command.should == "./bravo" subject.procfile["bravo"].command.should == "./bravo"
end
end
describe "with a deprecated Procfile" do
before do
File.open("Procfile", "w") do |file|
file.puts "name command"
end
end
it "should print a deprecation warning" do
mock(subject).warn_deprecated_procfile!
subject.processes.length.should == 1
end end
end end
end end
@@ -37,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(subject).fork(subject.processes["alpha"]) mock(subject).fork(subject.procfile["alpha"])
mock(subject).fork(subject.processes["bravo"]) mock(subject).fork(subject.procfile["bravo"])
mock(subject).watch_for_termination mock(subject).watch_for_termination
subject.start subject.start
end end
@@ -46,9 +33,9 @@ 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.processes["alpha"], 1, 5000) mock(engine).fork_individual(engine.procfile["alpha"], 1, 5000)
mock(engine).fork_individual(engine.processes["alpha"], 2, 5001) mock(engine).fork_individual(engine.procfile["alpha"], 2, 5001)
mock(engine).fork_individual(engine.processes["bravo"], 1, 5100) mock(engine).fork_individual(engine.procfile["bravo"], 1, 5100)
mock(engine).watch_for_termination mock(engine).watch_for_termination
engine.start engine.start
end end
@@ -57,14 +44,19 @@ describe "Foreman::Engine" do
describe "execute" do describe "execute" do
it "runs the processes" do it "runs the processes" do
write_procfile write_procfile
mock(subject).fork(subject.processes["alpha"]) mock(subject).fork(subject.procfile["alpha"])
mock(subject).watch_for_termination mock(subject).watch_for_termination
subject.execute("alpha") subject.execute("alpha")
end 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 end
describe "environment" do describe "environment" do
before(:each) do before(:each) do
write_procfile write_procfile
stub(Process).fork stub(Process).fork
@@ -79,6 +71,16 @@ describe "Foreman::Engine" do
engine.execute("alpha") engine.execute("alpha")
end end
it "should read more than one if specified" do
File.open("/tmp/env1", "w") { |f| f.puts("FOO=bar") }
File.open("/tmp/env2", "w") { |f| f.puts("BAZ=qux") }
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env1,/tmp/env2")
stub(engine).info
mock(engine).watch_for_termination
engine.environment.should == { "FOO"=>"bar", "BAZ"=>"qux" }
engine.execute("alpha")
end
it "should fail if specified and doesnt exist" do it "should fail if specified and doesnt exist" do
mock.instance_of(Foreman::Engine).error("No such file: /tmp/env") mock.instance_of(Foreman::Engine).error("No such file: /tmp/env")
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env") engine = Foreman::Engine.new("Procfile", :env => "/tmp/env")
+1 -1
View File
@@ -12,7 +12,7 @@ describe Foreman::Export::Bluepill do
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("/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
+35
View File
@@ -0,0 +1,35 @@
require "spec_helper"
require "foreman/engine"
require "foreman/export/runit"
require "tmpdir"
describe Foreman::Export::Runit 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) }
before(:each) { load_export_templates_into_fakefs("runit") }
before(:each) { stub(runit).say }
it "exports to the filesystem" do
FileUtils.mkdir_p('/tmp/init')
runit.export('/tmp/init', :concurrency => 'alpha=2')
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 ==
example_export_file('runit/app-alpha-1-log-run')
File.read("/tmp/init/app-alpha-1/env/PORT").should == "5000\n"
File.read("/tmp/init/app-alpha-1/env/BAR").should == "baz\n"
File.read("/tmp/init/app-alpha-2/run").should == example_export_file('runit/app-alpha-2-run')
File.read("/tmp/init/app-alpha-2/log/run").should ==
example_export_file('runit/app-alpha-2-log-run')
File.read("/tmp/init/app-alpha-2/env/PORT").should == "5001\n"
File.read("/tmp/init/app-alpha-2/env/BAR").should == "baz\n"
File.read("/tmp/init/app-bravo-1/run").should == example_export_file('runit/app-bravo-1-run')
File.read("/tmp/init/app-bravo-1/log/run").should ==
example_export_file('runit/app-bravo-1-log-run')
File.read("/tmp/init/app-bravo-1/env/PORT").should == "5100\n"
end
end
+1 -1
View File
@@ -12,7 +12,7 @@ describe Foreman::Export::Upstart do
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("/tmp/init", :concurrency => "alpha=2")
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")
@@ -0,0 +1,7 @@
#!/bin/sh
set -e
LOG=/var/log/app/alpha-1
test -d "$LOG" || mkdir -p m2750 "$LOG" && chown app "$LOG"
exec chpst -u app svlogd "$LOG"
@@ -0,0 +1,3 @@
#!/bin/sh
cd /tmp/app
exec chpst -u app -e /tmp/init/app-alpha-1/env ./alpha bar=baz
@@ -0,0 +1,7 @@
#!/bin/sh
set -e
LOG=/var/log/app/alpha-2
test -d "$LOG" || mkdir -p m2750 "$LOG" && chown app "$LOG"
exec chpst -u app svlogd "$LOG"
@@ -0,0 +1,3 @@
#!/bin/sh
cd /tmp/app
exec chpst -u app -e /tmp/init/app-alpha-2/env ./alpha bar=baz
@@ -0,0 +1,7 @@
#!/bin/sh
set -e
LOG=/var/log/app/bravo-1
test -d "$LOG" || mkdir -p m2750 "$LOG" && chown app "$LOG"
exec chpst -u app svlogd "$LOG"
@@ -0,0 +1,3 @@
#!/bin/sh
cd /tmp/app
exec chpst -u app -e /tmp/init/app-bravo-1/env ./bravo
+12 -2
View File
@@ -12,6 +12,10 @@ def mock_error(subject, message)
end end
end end
def foreman(args)
Foreman::CLI.start(args.split(" "))
end
def mock_exit(&block) def mock_exit(&block)
block.should raise_error(SystemExit) block.should raise_error(SystemExit)
end end
@@ -24,15 +28,21 @@ def write_foreman_config(app)
end end
end end
def write_procfile(procfile="Procfile") def write_procfile(procfile="Procfile", alpha_env="")
File.open(procfile, "w") do |file| File.open(procfile, "w") do |file|
file.puts "alpha: ./alpha" file.puts "alpha: ./alpha" + " #{alpha_env}".rstrip
file.puts "\n" file.puts "\n"
file.puts "bravo:\t./bravo" file.puts "bravo:\t./bravo"
end end
File.expand_path(procfile) File.expand_path(procfile)
end end
def write_env(env=".env")
File.open(env, "w") do |file|
file.puts "FOO=bar"
end
end
def load_export_templates_into_fakefs(type) def load_export_templates_into_fakefs(type)
FakeFS.deactivate! FakeFS.deactivate!
files = Dir[File.expand_path("../../data/export/#{type}/**", __FILE__)].inject({}) do |hash, file| files = Dir[File.expand_path("../../data/export/#{type}/**", __FILE__)].inject({}) do |hash, file|