Compare commits
34 Commits
v0.12.0.pr
...
v0.18.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd234b8044 | ||
|
|
f2f09554c8 | ||
|
|
6c465b4ef1 | ||
|
|
c419f8213b | ||
|
|
7f61fb61ea | ||
|
|
577a5c7c5c | ||
|
|
56940c56d9 | ||
|
|
f308ad886d | ||
|
|
55375b9bde | ||
|
|
85fcccffa8 | ||
|
|
58fc18d015 | ||
|
|
f7c9802ef7 | ||
|
|
1c00d65f29 | ||
|
|
9193a675a3 | ||
|
|
8f0b14810c | ||
|
|
124e27ed22 | ||
|
|
f34f161899 | ||
|
|
191581fe85 | ||
|
|
b98d558bed | ||
|
|
180f63624e | ||
|
|
13fd1188ad | ||
|
|
92c2b15785 | ||
|
|
4a2d7565b7 | ||
|
|
49720e0458 | ||
|
|
c4de19f4da | ||
|
|
490c8b73bf | ||
|
|
f622f43cf4 | ||
|
|
9ab701f5b9 | ||
|
|
1a5d2428b9 | ||
|
|
564aa740e1 | ||
|
|
95115f4e78 | ||
|
|
e5828f8442 | ||
|
|
f121d04bf6 | ||
|
|
3dc113fcd4 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,7 +1,6 @@
|
||||
.bundle
|
||||
coverage
|
||||
example/log/*
|
||||
man/*.?
|
||||
man/*.html
|
||||
man/*.markdown
|
||||
pkg
|
||||
|
||||
17
Gemfile
17
Gemfile
@@ -1,18 +1,3 @@
|
||||
source "http://rubygems.org"
|
||||
|
||||
group :development do
|
||||
gem 'parka'
|
||||
gem 'rake'
|
||||
gem 'ronn'
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'fakefs', '~> 0.2.1'
|
||||
gem 'rcov', '~> 0.9.8'
|
||||
gem 'rr', '~> 1.0.2'
|
||||
gem 'rspec', '~> 2.0.0'
|
||||
end
|
||||
|
||||
gem 'json', '~> 1.5.1'
|
||||
gem 'term-ansicolor', '~> 1.0.5'
|
||||
gem 'thor', '>= 0.13.6'
|
||||
gemspec
|
||||
|
||||
18
Gemfile.lock
18
Gemfile.lock
@@ -1,19 +1,27 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
foreman (0.18.0)
|
||||
term-ansicolor (~> 1.0.5)
|
||||
thor (>= 0.13.6)
|
||||
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
crack (0.1.8)
|
||||
diff-lcs (1.1.2)
|
||||
fakefs (0.2.1)
|
||||
hpricot (0.8.2)
|
||||
json (1.5.1)
|
||||
mime-types (1.16)
|
||||
mustache (0.11.2)
|
||||
parka (0.3.1)
|
||||
parka (0.6.2)
|
||||
crack
|
||||
rest-client
|
||||
thor
|
||||
rake (0.8.7)
|
||||
rcov (0.9.8)
|
||||
rdiscount (1.6.5)
|
||||
rest-client (1.6.0)
|
||||
rest-client (1.6.1)
|
||||
mime-types (>= 1.16)
|
||||
ronn (0.7.3)
|
||||
hpricot (>= 0.8.2)
|
||||
@@ -38,12 +46,10 @@ PLATFORMS
|
||||
|
||||
DEPENDENCIES
|
||||
fakefs (~> 0.2.1)
|
||||
json (~> 1.5.1)
|
||||
foreman!
|
||||
parka
|
||||
rake
|
||||
rcov (~> 0.9.8)
|
||||
ronn
|
||||
rr (~> 1.0.2)
|
||||
rspec (~> 2.0.0)
|
||||
term-ansicolor (~> 1.0.5)
|
||||
thor (>= 0.13.6)
|
||||
|
||||
@@ -1,9 +1,30 @@
|
||||
# Foreman
|
||||
|
||||
## Installation
|
||||
|
||||
gem install foreman
|
||||
|
||||
## Description
|
||||
|
||||
http://blog.daviddollar.org/2011/05/06/introducing-foreman.html
|
||||
|
||||
## Manual
|
||||
|
||||
See the [man page](http://ddollar.github.com/foreman) for usage.
|
||||
|
||||
## Authorship
|
||||
|
||||
Created by David Dollar
|
||||
|
||||
Patches contributed by:
|
||||
|
||||
* Adam Wiggins
|
||||
* clifff
|
||||
* Dan Peterson
|
||||
* Jay Zeschin
|
||||
* Keith Rarick
|
||||
* Ricardo Chimal, Jr
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
8
Rakefile
8
Rakefile
@@ -33,19 +33,23 @@ task :man do
|
||||
ENV['RONN_MANUAL'] = "Foreman Manual"
|
||||
ENV['RONN_ORGANIZATION'] = "Foreman #{Foreman::VERSION}"
|
||||
sh "ronn -w -s toc -r5 --markdown man/*.ronn"
|
||||
end
|
||||
|
||||
desc "Commit the manual to git"
|
||||
task "man:commit" => :man do
|
||||
sh "git add README.markdown"
|
||||
sh "git commit -m 'update readme' || echo 'nothing to commit'"
|
||||
end
|
||||
|
||||
desc "Generate the Github docs"
|
||||
task :pages => :man do
|
||||
task :pages => "man:commit" do
|
||||
sh %{
|
||||
cp man/foreman.1.html /tmp/foreman.1.html
|
||||
git checkout gh-pages
|
||||
rm ./index.html
|
||||
cp /tmp/foreman.1.html ./index.html
|
||||
git add -u index.html
|
||||
git commit -m "rebuilding man page"
|
||||
git commit -m "saving man page to github docs"
|
||||
git push origin -f gh-pages
|
||||
git checkout master
|
||||
}
|
||||
|
||||
2
bin/foreman-runner
Executable file
2
bin/foreman-runner
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env ruby
|
||||
exec ARGV.join(" ")
|
||||
@@ -1,6 +1,12 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
%w( SIGINT SIGTERM SIGHUP ).each do |signal|
|
||||
trap(signal) do
|
||||
puts "received #{signal} but i'm ignoring it!"
|
||||
end
|
||||
end
|
||||
|
||||
while true
|
||||
puts "tick: #{ARGV.inspect}"
|
||||
puts "tick: #{ARGV.inspect} -- FOO:#{ENV["FOO"]}"
|
||||
sleep 1
|
||||
end
|
||||
|
||||
@@ -2,5 +2,4 @@ start on starting <%= app %>-<%= process.name %>
|
||||
stop on stopping <%= app %>-<%= process.name %>
|
||||
respawn
|
||||
|
||||
chdir <%= engine.directory %>
|
||||
exec su - <%= user %> -c 'export PORT=<%= port %>; <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1'
|
||||
exec su - <%= user %> -c 'cd <%= engine.directory %>; export PORT=<%= port %>; <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1'
|
||||
|
||||
@@ -16,10 +16,10 @@ Gem::Specification.new do |gem|
|
||||
gem.files = Dir["**/*"].select { |d| d =~ %r{^(README|bin/|data/|ext/|lib/|spec/|test/)} }
|
||||
gem.files << "man/foreman.1"
|
||||
|
||||
gem.add_dependency 'json', '~> 1.5.1'
|
||||
gem.add_dependency 'term-ansicolor', '~> 1.0.5'
|
||||
gem.add_dependency 'thor', '>= 0.13.6'
|
||||
|
||||
gem.add_development_dependency 'parka'
|
||||
gem.add_development_dependency 'rake'
|
||||
gem.add_development_dependency 'ronn'
|
||||
gem.add_development_dependency 'fakefs', '~> 0.2.1'
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
require "foreman/version"
|
||||
|
||||
module Foreman
|
||||
|
||||
class AppDoesNotExist < Exception; end
|
||||
|
||||
@@ -2,6 +2,7 @@ require "foreman"
|
||||
require "foreman/engine"
|
||||
require "foreman/export"
|
||||
require "thor"
|
||||
require "yaml"
|
||||
|
||||
class Foreman::CLI < Thor
|
||||
|
||||
@@ -9,9 +10,9 @@ class Foreman::CLI < Thor
|
||||
|
||||
desc "start [PROCESS]", "Start the application, or a specific process"
|
||||
|
||||
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 :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)
|
||||
check_procfile!
|
||||
@@ -36,7 +37,6 @@ class Foreman::CLI < Thor
|
||||
check_procfile!
|
||||
|
||||
formatter = case format
|
||||
when "json" then Foreman::Export::JSON
|
||||
when "inittab" then Foreman::Export::Inittab
|
||||
when "upstart" then Foreman::Export::Upstart
|
||||
else error "Unknown export format: #{format}."
|
||||
|
||||
@@ -3,6 +3,7 @@ require "foreman/process"
|
||||
require "foreman/utils"
|
||||
require "pty"
|
||||
require "tempfile"
|
||||
require "timeout"
|
||||
require "term/ansicolor"
|
||||
require "fileutils"
|
||||
|
||||
@@ -50,23 +51,27 @@ class Foreman::Engine
|
||||
end
|
||||
|
||||
def start(options={})
|
||||
environment = read_environment(options[:env])
|
||||
|
||||
proctitle "ruby: foreman master"
|
||||
|
||||
processes_in_order.each do |name, process|
|
||||
fork process, options
|
||||
fork process, options, environment
|
||||
end
|
||||
|
||||
trap("TERM") { puts "SIGTERM received"; kill_all("TERM") }
|
||||
trap("INT") { puts "SIGINT received"; kill_all("INT") }
|
||||
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
||||
trap("INT") { puts "SIGINT received"; terminate_gracefully }
|
||||
|
||||
watch_for_termination
|
||||
end
|
||||
|
||||
def execute(name, options={})
|
||||
fork processes[name], options
|
||||
environment = read_environment(options[:env])
|
||||
|
||||
trap("TERM") { puts "SIGTERM received"; kill_all("TERM") }
|
||||
trap("INT") { puts "SIGINT received"; kill_all("INT") }
|
||||
fork processes[name], options, environment
|
||||
|
||||
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
||||
trap("INT") { puts "SIGINT received"; terminate_gracefully }
|
||||
|
||||
watch_for_termination
|
||||
end
|
||||
@@ -79,15 +84,17 @@ class Foreman::Engine
|
||||
|
||||
private ######################################################################
|
||||
|
||||
def fork(process, options={})
|
||||
def fork(process, options={}, environment={})
|
||||
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
||||
|
||||
1.upto(concurrency[process.name]) do |num|
|
||||
fork_individual(process, num, port_for(process, num, options[:port]))
|
||||
fork_individual(process, num, port_for(process, num, options[:port]), environment)
|
||||
end
|
||||
end
|
||||
|
||||
def fork_individual(process, num, port)
|
||||
def fork_individual(process, num, port, environment)
|
||||
environment.each { |k,v| ENV[k] = v }
|
||||
|
||||
ENV["PORT"] = port.to_s
|
||||
ENV["PS"] = "#{process.name}.#{num}"
|
||||
|
||||
@@ -101,12 +108,12 @@ private ######################################################################
|
||||
|
||||
def run(process)
|
||||
proctitle "ruby: foreman #{process.name}"
|
||||
trap("SIGINT", "IGNORE")
|
||||
|
||||
begin
|
||||
Dir.chdir directory do
|
||||
command = process.command
|
||||
|
||||
PTY.spawn("#{process.command} 2>&1") do |stdin, stdout, pid|
|
||||
PTY.spawn(runner, process.command) do |stdin, stdout, pid|
|
||||
trap("SIGTERM") { Process.kill("SIGTERM", pid) }
|
||||
until stdin.eof?
|
||||
info stdin.gets, process
|
||||
end
|
||||
@@ -120,11 +127,9 @@ private ######################################################################
|
||||
end
|
||||
end
|
||||
|
||||
def kill_all(signal="TERM")
|
||||
info "terminating"
|
||||
def kill_all(signal="SIGTERM")
|
||||
running_processes.each do |pid, process|
|
||||
info "killing #{process.name} in pid #{pid}"
|
||||
Process.kill(signal, pid)
|
||||
Process.kill(signal, pid) rescue Errno::ESRCH
|
||||
end
|
||||
end
|
||||
|
||||
@@ -136,6 +141,11 @@ private ######################################################################
|
||||
puts
|
||||
end
|
||||
|
||||
def error(message)
|
||||
puts "ERROR: #{message}"
|
||||
exit 1
|
||||
end
|
||||
|
||||
def longest_process_name
|
||||
@longest_process_name ||= begin
|
||||
longest = processes.keys.map { |name| name.length }.sort.last
|
||||
@@ -168,8 +178,9 @@ private ######################################################################
|
||||
pid, status = Process.wait2
|
||||
process = running_processes.delete(pid)
|
||||
info "process terminated", process
|
||||
terminate_gracefully
|
||||
kill_all
|
||||
Process.waitall
|
||||
rescue Errno::ECHILD
|
||||
end
|
||||
|
||||
def running_processes
|
||||
@@ -190,4 +201,33 @@ private ######################################################################
|
||||
puts "!!! e.g. web: thin start"
|
||||
end
|
||||
|
||||
def read_environment(filename)
|
||||
error "No such file: #{filename}" if filename && !File.exists?(filename)
|
||||
filename ||= ".env"
|
||||
environment = {}
|
||||
|
||||
if File.exists?(filename)
|
||||
File.read(filename).split("\n").each do |line|
|
||||
if line =~ /\A([A-Za-z_]+)=(.*)\z/
|
||||
environment[$1] = $2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
environment
|
||||
end
|
||||
|
||||
def runner
|
||||
File.expand_path("../../../bin/foreman-runner", __FILE__)
|
||||
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
|
||||
|
||||
end
|
||||
|
||||
@@ -4,6 +4,6 @@ module Foreman::Export
|
||||
class Exception < ::Exception; end
|
||||
end
|
||||
|
||||
require "foreman/export/json"
|
||||
require "foreman/export/base"
|
||||
require "foreman/export/inittab"
|
||||
require "foreman/export/upstart"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require "foreman/export/base"
|
||||
require "foreman/export"
|
||||
|
||||
class Foreman::Export::Inittab < Foreman::Export::Base
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
require "foreman/export/base"
|
||||
require "json"
|
||||
|
||||
class Foreman::Export::JSON < Foreman::Export::Base
|
||||
|
||||
def export(fname=nil, options={})
|
||||
processes = engine.processes.values.inject({}) do |hash, process|
|
||||
hash.update(process.name => { "command" => process.command })
|
||||
end
|
||||
puts processes.to_json
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,5 +1,5 @@
|
||||
require "erb"
|
||||
require "foreman/export/base"
|
||||
require "foreman/export"
|
||||
|
||||
class Foreman::Export::Upstart < Foreman::Export::Base
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module Foreman
|
||||
|
||||
VERSION = "0.12.0.pre1"
|
||||
VERSION = "0.18.0"
|
||||
|
||||
end
|
||||
|
||||
198
man/foreman.1
Normal file
198
man/foreman.1
Normal file
@@ -0,0 +1,198 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "FOREMAN" "1" "May 2011" "Foreman 0.15.0" "Foreman Manual"
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBforeman\fR \- manage Procfile\-based applications
|
||||
.
|
||||
.SH "SYNOPSIS"
|
||||
\fBforeman start [process]\fR
|
||||
.
|
||||
.br
|
||||
\fBforeman export <format> [location]\fR
|
||||
.
|
||||
.SH "DESCRIPTION"
|
||||
\fBForeman\fR is a manager for Procfile\-based applications\. Its aim is to abstract away the details of the Procfile format, and allow you to either run your application directly or export it to some other process management format\.
|
||||
.
|
||||
.SH "RUNNING"
|
||||
\fBforeman start\fR is used to run your application directly from the command line\.
|
||||
.
|
||||
.P
|
||||
If no additional parameters are passed, foreman will run one instance of each type of process defined in your Procfile\.
|
||||
.
|
||||
.P
|
||||
If a parameter is passed, foreman will run one instance of the specified application type\.
|
||||
.
|
||||
.P
|
||||
The following options control how the application is run:
|
||||
.
|
||||
.TP
|
||||
\fB\-c\fR, \fB\-\-concurrency\fR
|
||||
Specify the number of each process type to run\. The value passed in should be in the format \fBprocess=num,process=num\fR
|
||||
.
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-port\fR
|
||||
Specify which port to use as the base for this application\. Should be a multiple of 1000\.
|
||||
.
|
||||
.SH "EXPORTING"
|
||||
\fBforeman export\fR is used to export your application to another process management format\.
|
||||
.
|
||||
.P
|
||||
An location to export can be passed as an argument\. This argument may be either required or optional depending on the export format\.
|
||||
.
|
||||
.P
|
||||
The following options control how the application is run:
|
||||
.
|
||||
.TP
|
||||
\fB\-a\fR, \fB\-\-app\fR
|
||||
Use this name rather than the application\'s root directory name as the name of the application when exporting\.
|
||||
.
|
||||
.TP
|
||||
\fB\-c\fR, \fB\-\-concurrency\fR
|
||||
Specify the number of each process type to run\. The value passed in should be in the format \fBprocess=num,process=num\fR
|
||||
.
|
||||
.TP
|
||||
\fB\-l\fR, \fB\-\-log\fR
|
||||
Specify the directory to place process logs in\.
|
||||
.
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-port\fR
|
||||
Specify which port to use as the base for this application\. Should be a multiple of 1000\.
|
||||
.
|
||||
.TP
|
||||
\fB\-u\fR, \fB\-\-user\fR
|
||||
Specify the user the application should be run as\. Defaults to the app name
|
||||
.
|
||||
.SH "OPTIONS"
|
||||
These options control all modes of foreman\'s operation\.
|
||||
.
|
||||
.TP
|
||||
\fB\-e\fR, \fB\-\-env\fR
|
||||
Specify a file containing the environment that should be set up for each child process\. The file should be key/value pairs separated by \fB=\fR, with one key/value pair per line\.
|
||||
.
|
||||
.TP
|
||||
\fB\-f\fR, \fB\-\-procfile\fR
|
||||
Specify an alternate location for the application\'s Procfile\. This file\'s containing directory will be assumed to be the root directory of the application\.
|
||||
.
|
||||
.SH "EXPORT FORMATS"
|
||||
foreman currently supports the following output formats:
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
inittab
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
upstart
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "INITTAB EXPORT"
|
||||
Will export a chunk of inittab\-compatible configuration:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
# \-\-\-\-\- foreman example processes \-\-\-\-\-
|
||||
EX01:4:respawn:/bin/su \- example \-c \'PORT=5000 bundle exec thin start >> /var/log/web\-1\.log 2>&1\'
|
||||
EX02:4:respawn:/bin/su \- example \-c \'PORT=5100 bundle exec rake jobs:work >> /var/log/job\-1\.log 2>&1\'
|
||||
# \-\-\-\-\- end foreman example processes \-\-\-\-\-
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "UPSTART EXPORT"
|
||||
Will create a series of upstart scripts in the location you specify\. Scripts will be structured to make the following commands valid:
|
||||
.
|
||||
.P
|
||||
\fBstart appname\fR
|
||||
.
|
||||
.P
|
||||
\fBstop appname\-processname\fR
|
||||
.
|
||||
.P
|
||||
\fBrestart appname\-processname\-3\fR
|
||||
.
|
||||
.SH "PROCFILE"
|
||||
A Procfile should contain both a name for the process and the command used to run it\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
web: bundle exec thin start
|
||||
job: bundle exec rake jobs:work
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
You can validate your Procfile format using the \fBcheck\fR command
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ foreman check
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "DEFAULT OPTIONS"
|
||||
If a \fB\.foreman\fR file exists in the current directory, default options will be read from it\. This file should be in YAML format with the long option name as keys\. Example:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
concurrency: alpha=0
|
||||
port: 15000
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "EXAMPLES"
|
||||
Start one instance of each process type, interleave the output on stdout:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ foreman start
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Export the application in upstart format:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ foreman export upstart /etc/init
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Run one process type from the application defined in a specific Procfile:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ foreman start alpha \-p ~/myapp/Procfile
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "COPYRIGHT"
|
||||
Foreman is Copyright (C) 2010 David Dollar \fIhttp://daviddollar\.org\fR
|
||||
@@ -66,6 +66,11 @@ The following options control how the application is run:
|
||||
|
||||
These options control all modes of foreman's operation.
|
||||
|
||||
* `-e`, `--env`:
|
||||
Specify a file containing the environment that should be set up for each
|
||||
child process. The file should be key/value pairs separated by `=`, with
|
||||
one key/value pair per line.
|
||||
|
||||
* `-f`, `--procfile`:
|
||||
Specify an alternate location for the application's Procfile. This file's
|
||||
containing directory will be assumed to be the root directory of the
|
||||
@@ -75,18 +80,10 @@ These options control all modes of foreman's operation.
|
||||
|
||||
foreman currently supports the following output formats:
|
||||
|
||||
* json
|
||||
|
||||
* inittab
|
||||
|
||||
* upstart
|
||||
|
||||
## JSON EXPORT
|
||||
|
||||
Will export your processes as JSON:
|
||||
|
||||
{ "web": { "command": "bundle exec thin start" } }
|
||||
|
||||
## INITTAB EXPORT
|
||||
|
||||
Will export a chunk of inittab-compatible configuration:
|
||||
|
||||
@@ -37,17 +37,17 @@ describe "Foreman::Engine" do
|
||||
describe "start" do
|
||||
it "forks the processes" do
|
||||
write_procfile
|
||||
mock(subject).fork(subject.processes["alpha"], {})
|
||||
mock(subject).fork(subject.processes["bravo"], {})
|
||||
mock(subject).fork(subject.processes["alpha"], {}, {})
|
||||
mock(subject).fork(subject.processes["bravo"], {}, {})
|
||||
mock(subject).watch_for_termination
|
||||
subject.start
|
||||
end
|
||||
|
||||
it "handles concurrency" do
|
||||
write_procfile
|
||||
mock(subject).fork_individual(subject.processes["alpha"], 1, 5000)
|
||||
mock(subject).fork_individual(subject.processes["alpha"], 2, 5001)
|
||||
mock(subject).fork_individual(subject.processes["bravo"], 1, 5100)
|
||||
mock(subject).fork_individual(subject.processes["alpha"], 1, 5000, {})
|
||||
mock(subject).fork_individual(subject.processes["alpha"], 2, 5001, {})
|
||||
mock(subject).fork_individual(subject.processes["bravo"], 1, 5100, {})
|
||||
mock(subject).watch_for_termination
|
||||
subject.start(:concurrency => "alpha=2")
|
||||
end
|
||||
@@ -56,9 +56,34 @@ describe "Foreman::Engine" do
|
||||
describe "execute" do
|
||||
it "runs the processes" do
|
||||
write_procfile
|
||||
mock(subject).fork(subject.processes["alpha"], {})
|
||||
mock(subject).fork(subject.processes["alpha"], {}, {})
|
||||
mock(subject).watch_for_termination
|
||||
subject.execute("alpha")
|
||||
end
|
||||
end
|
||||
|
||||
describe "environment" do
|
||||
before(:each) do
|
||||
write_procfile
|
||||
stub(Process).fork
|
||||
stub(subject).info
|
||||
mock(subject).watch_for_termination
|
||||
end
|
||||
|
||||
it "should read if specified" do
|
||||
File.open("/tmp/env", "w") { |f| f.puts("FOO=baz") }
|
||||
subject.execute("alpha", :env => "/tmp/env")
|
||||
end
|
||||
|
||||
it "should fail if specified and doesnt exist" do
|
||||
mock(subject).error("No such file: /tmp/env")
|
||||
subject.execute("alpha", :env => "/tmp/env")
|
||||
end
|
||||
|
||||
it "should read .env if none specified" do
|
||||
File.open(".env", "w") { |f| f.puts("FOO=qoo") }
|
||||
mock(subject).fork_individual(anything, anything, anything, { "FOO" => "qoo" })
|
||||
subject.execute("bravo")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,2 +1,24 @@
|
||||
require "spec_helper"
|
||||
require "foreman/engine"
|
||||
require "foreman/export/upstart"
|
||||
require "tmpdir"
|
||||
|
||||
describe Foreman::Export::Upstart 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) }
|
||||
|
||||
before(:each) { load_export_templates_into_fakefs("upstart") }
|
||||
before(:each) { stub(upstart).say }
|
||||
|
||||
it "exports to the filesystem" do
|
||||
upstart.export("/tmp/init")
|
||||
|
||||
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.read("/tmp/init/app-bravo.conf").should == example_export_file("upstart/app-bravo.conf")
|
||||
File.read("/tmp/init/app-bravo-1.conf").should == example_export_file("upstart/app-bravo-1.conf")
|
||||
end
|
||||
end
|
||||
|
||||
5
spec/resources/export/upstart/app-alpha-1.conf
Normal file
5
spec/resources/export/upstart/app-alpha-1.conf
Normal file
@@ -0,0 +1,5 @@
|
||||
start on starting app-alpha
|
||||
stop on stopping app-alpha
|
||||
respawn
|
||||
|
||||
exec su - app -c 'cd /tmp/app; export PORT=5000; ./alpha >> /var/log/app/alpha-1.log 2>&1'
|
||||
5
spec/resources/export/upstart/app-alpha-2.conf
Normal file
5
spec/resources/export/upstart/app-alpha-2.conf
Normal file
@@ -0,0 +1,5 @@
|
||||
start on starting app-alpha
|
||||
stop on stopping app-alpha
|
||||
respawn
|
||||
|
||||
exec su - app -c 'cd /tmp/app; export PORT=5001; ./alpha >> /var/log/app/alpha-2.log 2>&1'
|
||||
2
spec/resources/export/upstart/app-alpha.conf
Normal file
2
spec/resources/export/upstart/app-alpha.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
start on starting app
|
||||
stop on stopping app
|
||||
5
spec/resources/export/upstart/app-bravo-1.conf
Normal file
5
spec/resources/export/upstart/app-bravo-1.conf
Normal file
@@ -0,0 +1,5 @@
|
||||
start on starting app-bravo
|
||||
stop on stopping app-bravo
|
||||
respawn
|
||||
|
||||
exec su - app -c 'cd /tmp/app; export PORT=5100; ./bravo >> /var/log/app/bravo-1.log 2>&1'
|
||||
2
spec/resources/export/upstart/app-bravo.conf
Normal file
2
spec/resources/export/upstart/app-bravo.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
start on starting app
|
||||
stop on stopping app
|
||||
8
spec/resources/export/upstart/app.conf
Normal file
8
spec/resources/export/upstart/app.conf
Normal file
@@ -0,0 +1,8 @@
|
||||
pre-start script
|
||||
|
||||
bash << "EOF"
|
||||
mkdir -p /var/log/app
|
||||
chown -R app /var/log/app
|
||||
EOF
|
||||
|
||||
end script
|
||||
@@ -3,7 +3,7 @@ require "rspec"
|
||||
require "fakefs/safe"
|
||||
require "fakefs/spec_helpers"
|
||||
|
||||
$:.unshift "lib"
|
||||
$:.unshift File.expand_path("../../lib", __FILE__)
|
||||
|
||||
def mock_error(subject, message)
|
||||
mock_exit do
|
||||
@@ -29,6 +29,27 @@ def write_procfile(procfile="Procfile")
|
||||
file.puts "alpha: ./alpha"
|
||||
file.puts "bravo: ./bravo"
|
||||
end
|
||||
File.expand_path(procfile)
|
||||
end
|
||||
|
||||
def load_export_templates_into_fakefs(type)
|
||||
FakeFS.deactivate!
|
||||
files = Dir[File.expand_path("../../data/export/#{type}/**", __FILE__)].inject({}) do |hash, file|
|
||||
hash.update(file => File.read(file))
|
||||
end
|
||||
FakeFS.activate!
|
||||
files.each do |filename, contents|
|
||||
File.open(filename, "w") do |f|
|
||||
f.puts contents
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def example_export_file(filename)
|
||||
FakeFS.deactivate!
|
||||
data = File.read(File.expand_path("../resources/export/#{filename}", __FILE__))
|
||||
FakeFS.activate!
|
||||
data
|
||||
end
|
||||
|
||||
Rspec.configure do |config|
|
||||
|
||||
Reference in New Issue
Block a user