Compare commits
75 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a87a882e60 | ||
|
|
6ba9252d0f | ||
|
|
efd9e2119b | ||
|
|
bf9bdbf118 | ||
|
|
929a138e54 | ||
|
|
d6514b4f33 | ||
|
|
8696a36833 | ||
|
|
3ea5de42aa | ||
|
|
4a13122082 | ||
|
|
9b2987c3f0 | ||
|
|
6274f99225 | ||
|
|
0b0324fed9 | ||
|
|
32db70b778 | ||
|
|
3db1ad6fbc | ||
|
|
003b466a17 | ||
|
|
2a896a0fb5 | ||
|
|
6a8c81a38b | ||
|
|
a83dab363e | ||
|
|
53e0f4ecf9 | ||
|
|
e0fe5baf1b | ||
|
|
1aa1f15b8f | ||
|
|
fa46a605bb | ||
|
|
3077857ab7 | ||
|
|
08aa8f9d5d | ||
|
|
771489dec9 | ||
|
|
053ae8f0be | ||
|
|
af58af1c60 | ||
|
|
9075d93370 | ||
|
|
0c2e9df722 | ||
|
|
047f1ff5c4 | ||
|
|
26b54a62c5 | ||
|
|
7b85ad7c1a | ||
|
|
91a87049db | ||
|
|
f46408e8be | ||
|
|
93f04e42ac | ||
|
|
97c4582acc | ||
|
|
5689d75a87 | ||
|
|
1325b6750e | ||
|
|
638005403f | ||
|
|
55f274532f | ||
|
|
66f76c2036 | ||
|
|
865cabb525 | ||
|
|
7c3c4bc58f | ||
|
|
2dbe8c733b | ||
|
|
84352b82cc | ||
|
|
383c1f87af | ||
|
|
ce3003b026 | ||
|
|
e06f4b2f9e | ||
|
|
b721fd894e | ||
|
|
b8ea6fd4b3 | ||
|
|
dbda63263b | ||
|
|
93cdc31be0 | ||
|
|
4dfbe46690 | ||
|
|
0033f9caeb | ||
|
|
74839800a9 | ||
|
|
b75337e21e | ||
|
|
d94f941189 | ||
|
|
0b34f067cb | ||
|
|
dbe51832b0 | ||
|
|
3a2a53be95 | ||
|
|
b2bf95479e | ||
|
|
48f764e347 | ||
|
|
de62d0655e | ||
|
|
38aecff886 | ||
|
|
e4a3215257 | ||
|
|
c705b5fbef | ||
|
|
2fcb64959b | ||
|
|
69216b4c5e | ||
|
|
5d2930745a | ||
|
|
a5465bf55e | ||
|
|
8fc3d1ef24 | ||
|
|
d33e4fb0ed | ||
|
|
1217ef1b56 | ||
|
|
c9943d70ec | ||
|
|
08dca57eb4 |
32
Changelog.md
32
Changelog.md
@@ -1,3 +1,35 @@
|
||||
## 0.46.0 (2012-05-02)
|
||||
|
||||
* Add Profile load/write/append API [Michael Granger]
|
||||
* Guard against missing Procfile in engine.rb [Brian Kaney]
|
||||
|
||||
## 0.45.0 (2012-04-26)
|
||||
|
||||
* create and chown log dir in upstart export. [Phil Hagelberg]
|
||||
* remove parka from dist files [David Dollar]
|
||||
|
||||
## 0.44.0 (2012-04-23)
|
||||
|
||||
* make var output order repeatable in supervisord export [David Dollar]
|
||||
* make --procfile and --app-root influence each other in a more intuitive way [David Dollar]
|
||||
* Look for .env and app_root in the same dir as the Procfile. [Phil Hagelberg]
|
||||
|
||||
## 0.43.0 (2012-04-20)
|
||||
|
||||
* wrap supervisord env vars in quotes [Raphael Randschau]
|
||||
|
||||
## 0.42.0 (2012-04-18)
|
||||
|
||||
* Move read_environment to a public class method. [Phil Hagelberg]
|
||||
* Drop parka dependency [Phil Hagelberg]
|
||||
* add group support for supervisord [Raphael Randschau]
|
||||
* fix enviroment export [Raphael Randschau]
|
||||
|
||||
## 0.41.0 (2012-03-16)
|
||||
|
||||
* replace term-ansicolor with built-in colorization [David Dollar]
|
||||
* supervisord export template [Raphael Randschau]
|
||||
|
||||
## 0.40.0 (2012-02-24)
|
||||
|
||||
* support various quoting styles in .env [David Dollar]
|
||||
|
||||
1
Gemfile
1
Gemfile
@@ -12,7 +12,6 @@ end
|
||||
|
||||
group :development do
|
||||
gem 'aws-s3'
|
||||
gem 'parka'
|
||||
gem 'rake'
|
||||
gem 'ronn'
|
||||
gem 'fakefs', '~> 0.3.2'
|
||||
|
||||
10
Gemfile.lock
10
Gemfile.lock
@@ -1,7 +1,7 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
foreman (0.41.0)
|
||||
foreman (0.47.0)
|
||||
thor (>= 0.13.6)
|
||||
|
||||
GEM
|
||||
@@ -12,7 +12,6 @@ GEM
|
||||
mime-types
|
||||
xml-simple
|
||||
builder (3.0.0)
|
||||
crack (0.1.8)
|
||||
diff-lcs (1.1.3)
|
||||
fakefs (0.3.2)
|
||||
hpricot (0.8.6)
|
||||
@@ -20,15 +19,9 @@ GEM
|
||||
mime-types (1.16)
|
||||
multi_json (1.0.4)
|
||||
mustache (0.11.2)
|
||||
parka (0.6.2)
|
||||
crack
|
||||
rest-client
|
||||
thor
|
||||
posix-spawn (0.3.6)
|
||||
rake (0.9.2.2)
|
||||
rdiscount (1.6.5)
|
||||
rest-client (1.6.1)
|
||||
mime-types (>= 1.16)
|
||||
ronn (0.7.3)
|
||||
hpricot (>= 0.8.2)
|
||||
mustache (>= 0.7.0)
|
||||
@@ -59,7 +52,6 @@ DEPENDENCIES
|
||||
aws-s3
|
||||
fakefs (~> 0.3.2)
|
||||
foreman!
|
||||
parka
|
||||
posix-spawn (~> 0.3.6)
|
||||
rake
|
||||
ronn
|
||||
|
||||
@@ -27,6 +27,12 @@ Manage Procfile-based applications
|
||||
* [wiki](http://github.com/ddollar/foreman/wiki)
|
||||
* [changelog](https://github.com/ddollar/foreman/blob/master/Changelog.md)
|
||||
|
||||
## Ports
|
||||
|
||||
* [shoreman](https://github.com/hecticjeff/shoreman) - shell
|
||||
* [honcho](https://github.com/nickstenning/honcho) - python
|
||||
* [norman](https://github.com/josh/norman) - node.js
|
||||
|
||||
## Authors
|
||||
|
||||
#### Created and maintained by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
#/ Usage: foreman-runner [-d <dir>] <command>
|
||||
#/ Usage: foreman-runner [-d <dir>] <command> [<args>...]
|
||||
#/
|
||||
#/ Run a command with exec, optionally changing directory first
|
||||
|
||||
@@ -27,10 +27,6 @@ done
|
||||
|
||||
shift $((OPTIND-1))
|
||||
|
||||
command=$1
|
||||
[ -z "$1" ] && usage
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
exec $1
|
||||
exec "$@"
|
||||
|
||||
@@ -11,8 +11,9 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
|
||||
|
||||
process.working_dir = "<%= engine.directory %>"
|
||||
process.daemonize = true
|
||||
process.environment = {"PORT" => "<%= port %>"}
|
||||
process.environment = {"PORT" => "<%= port %>"<% engine.environment.each_pair do |var,env| %> , "<%= var.upcase %>" => "<%= env %>" <% end %>}
|
||||
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||
process.stop_grace_time = 45.seconds
|
||||
|
||||
process.stdout = process.stderr = "<%= log_root %>/<%= app %>-<%= process.name %>-<%=num%>.log"
|
||||
|
||||
|
||||
22
data/export/launchd/launchd.plist.erb
Normal file
22
data/export/launchd/launchd.plist.erb
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string><%= "#{app}-#{process.name}-#{num}" %></string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string><%= process.command %></string>
|
||||
</array>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>StandardErrorPath</key>
|
||||
<string><%= log_root %>/<%= app %>-<%= process.name %>-<%=num%>.log</string>
|
||||
<key>UserName</key>
|
||||
<string><%= user %></string>
|
||||
<key>WorkingDirectory</key>
|
||||
<string><%= engine.directory %></string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,12 +1,15 @@
|
||||
<%
|
||||
app_names = []
|
||||
engine.procfile.entries.each do |process|
|
||||
next if (conc = self.concurrency[process.name]) < 1
|
||||
1.upto(self.concurrency[process.name]) do |num|
|
||||
port = engine.port_for(process, num, self.port)
|
||||
name = if (conc > 1); "#{process.name}-#{num}" else process.name; end
|
||||
environment = (engine.environment.each_pair { |var,env| "#{var.upcase}=#{env}" }.to_a << "PORT=#{port}")
|
||||
environment = (engine.environment.keys.sort.map{ |var| %{#{var.upcase}="#{engine.environment[var]}"} } + [%{PORT="#{port}"}])
|
||||
app_name = "#{app}-#{name}"
|
||||
app_names << app_name
|
||||
%>
|
||||
[program:<%= app %>-<%= name %>]
|
||||
[program:<%= app_name %>]
|
||||
command=<%= process.command %>
|
||||
autostart=true
|
||||
autorestart=true
|
||||
@@ -18,4 +21,7 @@ directory=<%= engine.directory %>
|
||||
environment=<%= environment.join(',') %><%
|
||||
end
|
||||
end
|
||||
%>
|
||||
%>
|
||||
|
||||
[group:<%= app %>]
|
||||
programs=<%= app_names.join(',') %>
|
||||
|
||||
@@ -2,4 +2,4 @@ start on starting <%= app %>-<%= process.name %>
|
||||
stop on stopping <%= app %>-<%= process.name %>
|
||||
respawn
|
||||
|
||||
exec su - <%= user %> -c 'cd <%= engine.directory %>; export PORT=<%= port %>;<% engine.environment.each_pair do |var,env| %> export <%= var.upcase %>=<%= env %>; <% end %> <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1'
|
||||
exec su - <%= user %> -c 'cd <%= engine.directory %>; export PORT=<%= port %>;<% engine.environment.each_pair do |var,env| %> export <%= var.upcase %>=<%= shell_quote(env) %>; <% end %> <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1'
|
||||
|
||||
2
dist/gem.rake
vendored
2
dist/gem.rake
vendored
@@ -10,5 +10,5 @@ task "gem:clean" do
|
||||
end
|
||||
|
||||
task "gem:release" => "gem:build" do |t|
|
||||
sh "parka push -f #{pkg("foreman-#{version}.gem")}"
|
||||
sh "gem push #{pkg("foreman-#{version}.gem")} || echo 'error'"
|
||||
end
|
||||
|
||||
2
dist/jruby.rake
vendored
2
dist/jruby.rake
vendored
@@ -12,5 +12,5 @@ task "jruby:clean" do
|
||||
end
|
||||
|
||||
task "jruby:release" => "jruby:build" do |t|
|
||||
sh "parka push -f #{pkg("foreman-#{version}-jruby.gem")}"
|
||||
sh "gem push #{pkg("foreman-#{version}-jruby.gem")} || echo 'error'"
|
||||
end
|
||||
|
||||
2
dist/mingw32.rake
vendored
2
dist/mingw32.rake
vendored
@@ -12,5 +12,5 @@ task "mingw32:clean" do
|
||||
end
|
||||
|
||||
task "mingw32:release" => "mingw32:build" do |t|
|
||||
sh "parka push -f #{pkg("foreman-#{version}-mingw32.gem")}"
|
||||
sh "gem push #{pkg("foreman-#{version}-mingw32.gem")} || echo 'error'"
|
||||
end
|
||||
|
||||
@@ -2,6 +2,7 @@ require "foreman"
|
||||
require "foreman/helpers"
|
||||
require "foreman/engine"
|
||||
require "foreman/export"
|
||||
require "shellwords"
|
||||
require "thor"
|
||||
require "yaml"
|
||||
|
||||
@@ -59,12 +60,12 @@ class Foreman::CLI < Thor
|
||||
puts "valid procfile detected (#{engine.procfile.process_names.join(', ')})"
|
||||
end
|
||||
|
||||
desc "run COMMAND", "Run a command using your application's environment"
|
||||
desc "run COMMAND [ARGS...]", "Run a command using your application's environment"
|
||||
|
||||
def run(*args)
|
||||
engine.apply_environment!
|
||||
begin
|
||||
exec args.join(" ")
|
||||
exec args.shelljoin
|
||||
rescue Errno::EACCES
|
||||
error "not executable: #{args.first}"
|
||||
rescue Errno::ENOENT
|
||||
@@ -83,7 +84,11 @@ private ######################################################################
|
||||
end
|
||||
|
||||
def procfile
|
||||
options[:procfile] || "Procfile"
|
||||
case
|
||||
when options[:procfile] then options[:procfile]
|
||||
when options[:app_root] then File.expand_path(File.join(options[:app_root], "Procfile"))
|
||||
else "Procfile"
|
||||
end
|
||||
end
|
||||
|
||||
def error(message)
|
||||
|
||||
@@ -21,11 +21,13 @@ class Foreman::Engine
|
||||
Foreman::Color.enable($stdout)
|
||||
|
||||
def initialize(procfile, options={})
|
||||
@procfile = Foreman::Procfile.new(procfile)
|
||||
@procfile = Foreman::Procfile.new(procfile) if File.exists?(procfile)
|
||||
@directory = options[:app_root] || File.expand_path(File.dirname(procfile))
|
||||
@options = options.dup
|
||||
@environment = read_environment_files(options[:env])
|
||||
@output_mutex = Mutex.new
|
||||
|
||||
@options[:env] ||= default_env
|
||||
@environment = read_environment_files(@options[:env])
|
||||
end
|
||||
|
||||
def start
|
||||
@@ -34,6 +36,7 @@ class Foreman::Engine
|
||||
|
||||
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
||||
trap("INT") { puts "SIGINT received"; terminate_gracefully }
|
||||
trap("HUP") { puts "SIGHUP received"; terminate_gracefully }
|
||||
|
||||
assign_colors
|
||||
spawn_processes
|
||||
@@ -51,6 +54,22 @@ class Foreman::Engine
|
||||
environment.each { |k,v| ENV[k] = v }
|
||||
end
|
||||
|
||||
def self.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/
|
||||
key, val = [$1, $2]
|
||||
case val
|
||||
when /\A'(.*)'\z/ then hash[key] = $1
|
||||
when /\A"(.*)"\z/ then hash[key] = $1.gsub(/\\(.)/, '\1')
|
||||
else hash[key] = val
|
||||
end
|
||||
end
|
||||
hash
|
||||
end
|
||||
end
|
||||
|
||||
private ######################################################################
|
||||
|
||||
def spawn_processes
|
||||
@@ -66,7 +85,7 @@ private ######################################################################
|
||||
end
|
||||
|
||||
def base_port
|
||||
options[:port] || 5000
|
||||
options[:port] || environment["PORT"] || ENV["PORT"] || 5000
|
||||
end
|
||||
|
||||
def kill_all(signal="SIGTERM")
|
||||
@@ -195,26 +214,15 @@ private ######################################################################
|
||||
|
||||
(filenames || "").split(",").map(&:strip).each do |filename|
|
||||
error "No such file: #{filename}" unless File.exists?(filename)
|
||||
environment.merge!(read_environment(filename))
|
||||
environment.merge!(Foreman::Engine.read_environment(filename))
|
||||
end
|
||||
|
||||
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/
|
||||
key, val = [$1, $2]
|
||||
case val
|
||||
when /\A'(.*)'\z/ then hash[key] = $1
|
||||
when /\A"(.*)"\z/ then hash[key] = $1.gsub(/\\(.)/, '\1')
|
||||
else hash[key] = val
|
||||
end
|
||||
end
|
||||
hash
|
||||
end
|
||||
def default_env
|
||||
env = File.join(directory, ".env")
|
||||
File.exists?(env) ? env : ""
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -31,4 +31,5 @@ require "foreman/export/upstart"
|
||||
require "foreman/export/bluepill"
|
||||
require "foreman/export/runit"
|
||||
require "foreman/export/supervisord"
|
||||
require "foreman/export/launchd"
|
||||
|
||||
|
||||
@@ -48,4 +48,19 @@ private ######################################################################
|
||||
end
|
||||
end
|
||||
|
||||
# Quote a string to be used on the command line. Backslashes are escapde to \\ and quotes
|
||||
# escaped to \"
|
||||
#
|
||||
# str - string to be quoted
|
||||
#
|
||||
# Examples
|
||||
#
|
||||
# shell_quote("FB|123\"\\1")
|
||||
# # => "\"FB|123\"\\"\\\\1\""
|
||||
#
|
||||
# Returns the the escaped string surrounded by quotes
|
||||
def shell_quote(str)
|
||||
"\"#{str.gsub(/\\/){ '\\\\' }.gsub(/["]/){ "\\\"" }}\""
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
27
lib/foreman/export/launchd.rb
Normal file
27
lib/foreman/export/launchd.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
require "erb"
|
||||
require "foreman/export"
|
||||
|
||||
class Foreman::Export::Launchd < Foreman::Export::Base
|
||||
|
||||
def export
|
||||
error("Must specify a location") unless location
|
||||
|
||||
app = self.app || File.basename(engine.directory)
|
||||
user = self.user || app
|
||||
log_root = self.log || "/var/log/#{app}"
|
||||
template_root = self.template
|
||||
|
||||
FileUtils.mkdir_p(location)
|
||||
|
||||
engine.procfile.entries.each do |process|
|
||||
1.upto(self.concurrency[process.name]) do |num|
|
||||
|
||||
master_template = export_template("launchd", "launchd.plist.erb", template_root)
|
||||
master_config = ERB.new(master_template).result(binding)
|
||||
write_file "#{location}/#{app}-#{process.name}-#{num}.plist", master_config
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -36,6 +36,8 @@ class Foreman::Export::Upstart < Foreman::Export::Base
|
||||
write_file "#{location}/#{app}-#{process.name}-#{num}.conf", process_config
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
FileUtils.mkdir_p(log_root) rescue error "could not create #{log_root}"
|
||||
FileUtils.chown(user, nil, log_root) rescue error "could not chown #{log_root} to #{user}"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -62,7 +62,7 @@ private
|
||||
$stdout.reopen writer
|
||||
$stderr.reopen writer
|
||||
reader.close
|
||||
exec Foreman.runner, "-d", basedir, command
|
||||
exec Foreman.runner, "-d", basedir, *command.shellsplit
|
||||
end
|
||||
end
|
||||
[ reader, pid ]
|
||||
|
||||
@@ -13,8 +13,9 @@ class Foreman::Procfile
|
||||
|
||||
attr_reader :entries
|
||||
|
||||
def initialize(filename)
|
||||
@entries = parse_procfile(filename)
|
||||
def initialize(filename=nil)
|
||||
@entries = []
|
||||
load(filename) if filename
|
||||
end
|
||||
|
||||
def [](name)
|
||||
@@ -25,12 +26,31 @@ class Foreman::Procfile
|
||||
entries.map(&:name)
|
||||
end
|
||||
|
||||
private
|
||||
def load(filename)
|
||||
entries.clear
|
||||
parse_procfile(filename)
|
||||
end
|
||||
|
||||
def write(filename)
|
||||
File.open(filename, 'w') do |io|
|
||||
entries.each do |ent|
|
||||
io.puts(ent)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def <<(entry)
|
||||
entries << Foreman::ProcfileEntry.new(*entry)
|
||||
self
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
|
||||
def parse_procfile(filename)
|
||||
File.read(filename).split("\n").map do |line|
|
||||
if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
|
||||
Foreman::ProcfileEntry.new($1, $2)
|
||||
self << [ $1, $2 ]
|
||||
end
|
||||
end.compact
|
||||
end
|
||||
|
||||
@@ -19,4 +19,8 @@ class Foreman::ProcfileEntry
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{name}: #{command}"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module Foreman
|
||||
|
||||
VERSION = "0.41.0"
|
||||
VERSION = "0.47.0"
|
||||
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "FOREMAN" "1" "February 2012" "Foreman 0.39.0" "Foreman Manual"
|
||||
.TH "FOREMAN" "1" "April 2012" "Foreman 0.46.0" "Foreman Manual"
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBforeman\fR \- manage Procfile\-based applications
|
||||
@@ -35,6 +35,14 @@ The following options control how the application is run:
|
||||
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\-e\fR, \fB\-\-env\fR
|
||||
Specify one or more \.env files to load
|
||||
.
|
||||
.TP
|
||||
\fB\-f\fR, \fB\-\-procfile\fR
|
||||
Specify an alternate Procfile to load, implies \fB\-d\fR at the Procfile root\.
|
||||
.
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-port\fR
|
||||
Specify which port to use as the base for this application\. Should be a multiple of 1000\.
|
||||
.
|
||||
|
||||
@@ -30,6 +30,12 @@ The following options control how the application is run:
|
||||
Specify the number of each process type to run. The value passed in
|
||||
should be in the format `process=num,process=num`
|
||||
|
||||
* `-e`, `--env`:
|
||||
Specify one or more .env files to load
|
||||
|
||||
* `-f`, `--procfile`:
|
||||
Specify an alternate Procfile to load, implies `-d` at the Procfile root.
|
||||
|
||||
* `-p`, `--port`:
|
||||
Specify which port to use as the base for this application. Should be
|
||||
a multiple of 1000.
|
||||
|
||||
@@ -34,6 +34,15 @@ describe "Foreman::CLI", :fakefs do
|
||||
subject.start("alpha")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with an alternate root" do
|
||||
it "reads the Procfile from that root" do
|
||||
write_procfile "/some/app/Procfile"
|
||||
mock(Foreman::Procfile).new("/some/app/Procfile")
|
||||
mock.instance_of(Foreman::Engine).start
|
||||
foreman %{ start -d /some/app }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "export" do
|
||||
@@ -135,7 +144,7 @@ describe "Foreman::CLI", :fakefs do
|
||||
before { write_procfile }
|
||||
|
||||
describe "and a command" do
|
||||
let(:command) { ["ls", "-l"] }
|
||||
let(:command) { ["ls", "-l", "foo bar"] }
|
||||
|
||||
before(:each) do
|
||||
stub(subject).exec
|
||||
@@ -151,8 +160,8 @@ describe "Foreman::CLI", :fakefs do
|
||||
ENV["FOO"].should be_nil
|
||||
end
|
||||
|
||||
it "should runute the command as a string" do
|
||||
mock(subject).exec(command.join(" "))
|
||||
it "should exec the argument list as a shell command" do
|
||||
mock(subject).exec(command.shelljoin)
|
||||
subject.run *command
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,12 +12,6 @@ describe "Foreman::Engine", :fakefs do
|
||||
end
|
||||
|
||||
describe "initialize" do
|
||||
describe "without an existing Procfile" do
|
||||
it "raises an error" do
|
||||
lambda { subject }.should raise_error
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a Procfile" do
|
||||
before { write_procfile }
|
||||
|
||||
@@ -49,6 +43,14 @@ describe "Foreman::Engine", :fakefs do
|
||||
end
|
||||
end
|
||||
|
||||
describe "directories" do
|
||||
it "has the directory default relative to the Procfile" do
|
||||
write_procfile "/some/app/Procfile"
|
||||
engine = Foreman::Engine.new("/some/app/Procfile")
|
||||
engine.directory.should == "/some/app"
|
||||
end
|
||||
end
|
||||
|
||||
describe "environment" do
|
||||
before(:each) do
|
||||
write_procfile
|
||||
@@ -97,6 +99,22 @@ describe "Foreman::Engine", :fakefs do
|
||||
engine.environment.should == {"FOO"=>"qoo"}
|
||||
engine.start
|
||||
end
|
||||
|
||||
it "should set port from .env if specified" do
|
||||
File.open(".env", "w") { |f| f.puts("PORT=8017") }
|
||||
engine = Foreman::Engine.new("Procfile")
|
||||
engine.send(:base_port).should == "8017"
|
||||
engine.start
|
||||
end
|
||||
|
||||
it "should be loaded relative to the Procfile" do
|
||||
FileUtils.mkdir_p "/some/app"
|
||||
File.open("/some/app/.env", "w") { |f| f.puts("FOO=qoo") }
|
||||
write_procfile "/some/app/Procfile"
|
||||
engine = Foreman::Engine.new("/some/app/Procfile")
|
||||
engine.environment.should == {"FOO"=>"qoo"}
|
||||
engine.start
|
||||
end
|
||||
end
|
||||
|
||||
describe "utf8" do
|
||||
|
||||
24
spec/foreman/export/launchd_spec.rb
Normal file
24
spec/foreman/export/launchd_spec.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
require "spec_helper"
|
||||
require "foreman/engine"
|
||||
require "foreman/export/launchd"
|
||||
require "tmpdir"
|
||||
|
||||
describe Foreman::Export::Launchd, :fakefs do
|
||||
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
|
||||
let(:engine) { Foreman::Engine.new(procfile) }
|
||||
let(:options) { Hash.new }
|
||||
let(:launchd) { Foreman::Export::Launchd.new("/tmp/init", engine, options) }
|
||||
|
||||
before(:each) { load_export_templates_into_fakefs("launchd") }
|
||||
before(:each) { stub(launchd).say }
|
||||
|
||||
it "exports to the filesystem" do
|
||||
launchd.export
|
||||
|
||||
normalize_space(File.read("/tmp/init/app-alpha-1.plist")).should == normalize_space(example_export_file("launchd/launchd-a.default"))
|
||||
|
||||
normalize_space(File.read("/tmp/init/app-bravo-1.plist")).should == normalize_space(example_export_file("launchd/launchd-b.default"))
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -15,12 +15,11 @@ describe Foreman::Export::Supervisord, :fakefs do
|
||||
it "exports to the filesystem" do
|
||||
supervisord.export
|
||||
|
||||
File.read("/tmp/init/app.conf").should == example_export_file("supervisord/app.conf")
|
||||
File.read("/tmp/init/app.conf").should == example_export_file("supervisord/app.conf")
|
||||
end
|
||||
|
||||
it "cleans up if exporting into an existing dir" do
|
||||
mock(FileUtils).rm("/tmp/init/app.conf")
|
||||
|
||||
supervisord.export
|
||||
supervisord.export
|
||||
end
|
||||
@@ -30,8 +29,7 @@ describe Foreman::Export::Supervisord, :fakefs do
|
||||
|
||||
it "exports to the filesystem with concurrency" do
|
||||
supervisord.export
|
||||
|
||||
File.read("/tmp/init/app.conf").should == example_export_file("supervisord/app-alpha-2.conf")
|
||||
File.read("/tmp/init/app.conf").should == example_export_file("supervisord/app-alpha-2.conf")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -46,7 +44,6 @@ describe Foreman::Export::Supervisord, :fakefs do
|
||||
|
||||
it "can export with alternate template files" do
|
||||
supervisord.export
|
||||
|
||||
File.read("/tmp/init/app.conf").should == "alternate_template\n"
|
||||
end
|
||||
end
|
||||
@@ -67,9 +64,22 @@ describe Foreman::Export::Supervisord, :fakefs do
|
||||
|
||||
it "can export with alternate template files" do
|
||||
supervisord.export
|
||||
|
||||
File.read("/tmp/init/app.conf").should == "default_alternate_template\n"
|
||||
end
|
||||
end
|
||||
|
||||
context "environment export" do
|
||||
it "correctly translates environment when exporting" do
|
||||
File.open("/tmp/supervisord_env", "w") { |f| f.puts("QUEUE=fastqueue,slowqueue\nVERBOSE=1") }
|
||||
|
||||
engine = Foreman::Engine.new(procfile,:env => "/tmp/supervisord_env")
|
||||
supervisor = Foreman::Export::Supervisord.new("/tmp/init", engine, options)
|
||||
stub(supervisor).say
|
||||
|
||||
supervisor.export
|
||||
|
||||
File.read("/tmp/init/app.conf").should == example_export_file("supervisord/app-env-with-comma.conf")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -33,6 +33,12 @@ describe Foreman::Export::Upstart, :fakefs do
|
||||
upstart.export
|
||||
end
|
||||
|
||||
it "quotes and escapes environment variables" do
|
||||
engine.environment['KEY'] = 'd"\|d'
|
||||
upstart.export
|
||||
File.read("/tmp/init/app-alpha-1.conf").should include('KEY="d\"\\\\|d"')
|
||||
end
|
||||
|
||||
context "with concurrency" do
|
||||
let(:options) { Hash[:concurrency => "alpha=2"] }
|
||||
|
||||
|
||||
@@ -121,11 +121,25 @@ describe Foreman::Process do
|
||||
output.should include('777')
|
||||
end
|
||||
|
||||
it 'should handle arguments' do
|
||||
pending
|
||||
it 'should handle multi-word arguments (old test)' do
|
||||
# TODO: This test used to be marked pending; it now passes,
|
||||
# but is very slow. The next test is a fast replacement.
|
||||
run %{ sh -c "trap '' TERM; sleep 10" }
|
||||
subject.should be_alive
|
||||
end
|
||||
|
||||
it 'should handle multi-word arguments' do
|
||||
# We have to be a little clever here since Foreman will always
|
||||
# print a status message that includes the command.
|
||||
run %{ sh -c 'echo abcdef | tr a-c x | tr d-f y' }
|
||||
output.should include('xxxyyy')
|
||||
end
|
||||
|
||||
it 'should not clobber "$x"-subexpressions' do
|
||||
pending 'this conflicts with the variable interpolation hack'
|
||||
run %{ sh -c 'echo \$abcdef | tr \$ %' }
|
||||
output.should include('%abcdef')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
13
spec/foreman/procfile_entry_spec.rb
Normal file
13
spec/foreman/procfile_entry_spec.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
require 'spec_helper'
|
||||
require 'foreman/procfile_entry'
|
||||
require 'pathname'
|
||||
require 'tmpdir'
|
||||
|
||||
describe Foreman::ProcfileEntry do
|
||||
subject { described_class.new('alpha', './alpha') }
|
||||
|
||||
it "stringifies as a Procfile line" do
|
||||
subject.to_s.should == 'alpha: ./alpha'
|
||||
end
|
||||
|
||||
end
|
||||
31
spec/foreman/procfile_spec.rb
Normal file
31
spec/foreman/procfile_spec.rb
Normal file
@@ -0,0 +1,31 @@
|
||||
require 'spec_helper'
|
||||
require 'foreman/procfile'
|
||||
require 'pathname'
|
||||
require 'tmpdir'
|
||||
|
||||
describe Foreman::Procfile do
|
||||
subject { described_class.new }
|
||||
|
||||
let(:testdir) { Pathname(Dir.tmpdir) }
|
||||
let(:procfile) { testdir + 'Procfile' }
|
||||
|
||||
it "can have a process appended to it" do
|
||||
subject << ['alpha', './alpha']
|
||||
subject['alpha'].should be_a(Foreman::ProcfileEntry)
|
||||
end
|
||||
|
||||
it "can write itself out to a file" do
|
||||
subject << ['alpha', './alpha']
|
||||
subject.write(procfile)
|
||||
procfile.read.should == "alpha: ./alpha\n"
|
||||
end
|
||||
|
||||
it "can re-read entries from a file" do
|
||||
procfile.open('w') { |io| io.puts "gamma: ./radiation", "theta: ./rate" }
|
||||
subject << ['alpha', './alpha']
|
||||
subject.load(procfile)
|
||||
subject.process_names.should have(2).members
|
||||
subject.process_names.should include('gamma', 'theta')
|
||||
end
|
||||
|
||||
end
|
||||
@@ -13,6 +13,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
|
||||
process.daemonize = true
|
||||
process.environment = {"PORT" => "5000"}
|
||||
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||
process.stop_grace_time = 45.seconds
|
||||
|
||||
process.stdout = process.stderr = "/var/log/app/app-alpha-1.log"
|
||||
|
||||
@@ -31,6 +32,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
|
||||
process.daemonize = true
|
||||
process.environment = {"PORT" => "5001"}
|
||||
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||
process.stop_grace_time = 45.seconds
|
||||
|
||||
process.stdout = process.stderr = "/var/log/app/app-alpha-2.log"
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
|
||||
process.daemonize = true
|
||||
process.environment = {"PORT" => "5000"}
|
||||
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||
process.stop_grace_time = 45.seconds
|
||||
|
||||
process.stdout = process.stderr = "/var/log/app/app-alpha-1.log"
|
||||
|
||||
@@ -30,6 +31,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
|
||||
process.daemonize = true
|
||||
process.environment = {"PORT" => "5100"}
|
||||
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||
process.stop_grace_time = 45.seconds
|
||||
|
||||
process.stdout = process.stderr = "/var/log/app/app-bravo-1.log"
|
||||
|
||||
|
||||
22
spec/resources/export/launchd/launchd-a.default
Normal file
22
spec/resources/export/launchd/launchd-a.default
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>app-alpha-1</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>./alpha</string>
|
||||
</array>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/var/log/app/app-alpha-1.log</string>
|
||||
<key>UserName</key>
|
||||
<string>app</string>
|
||||
<key>WorkingDirectory</key>
|
||||
<string>/tmp/app</string>
|
||||
</dict>
|
||||
</plist>
|
||||
22
spec/resources/export/launchd/launchd-b.default
Normal file
22
spec/resources/export/launchd/launchd-b.default
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>app-bravo-1</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>./bravo</string>
|
||||
</array>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/var/log/app/app-bravo-1.log</string>
|
||||
<key>UserName</key>
|
||||
<string>app</string>
|
||||
<key>WorkingDirectory</key>
|
||||
<string>/tmp/app</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -8,7 +8,7 @@ stdout_logfile=/var/log/app/alpha-1-out.log
|
||||
stderr_logfile=/var/log/app/alpha-1-err.log
|
||||
user=app
|
||||
directory=/tmp/app
|
||||
environment=PORT=5000
|
||||
environment=PORT="5000"
|
||||
[program:app-alpha-2]
|
||||
command=./alpha
|
||||
autostart=true
|
||||
@@ -18,4 +18,7 @@ stdout_logfile=/var/log/app/alpha-2-out.log
|
||||
stderr_logfile=/var/log/app/alpha-2-err.log
|
||||
user=app
|
||||
directory=/tmp/app
|
||||
environment=PORT=5001
|
||||
environment=PORT="5001"
|
||||
|
||||
[group:app]
|
||||
programs=app-alpha-1,app-alpha-2
|
||||
|
||||
24
spec/resources/export/supervisord/app-env-with-comma.conf
Normal file
24
spec/resources/export/supervisord/app-env-with-comma.conf
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
[program:app-alpha]
|
||||
command=./alpha
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stopsignal=QUIT
|
||||
stdout_logfile=/var/log/app/alpha-1-out.log
|
||||
stderr_logfile=/var/log/app/alpha-1-err.log
|
||||
user=app
|
||||
directory=/tmp/app
|
||||
environment=QUEUE="fastqueue,slowqueue",VERBOSE="1",PORT="5000"
|
||||
[program:app-bravo]
|
||||
command=./bravo
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stopsignal=QUIT
|
||||
stdout_logfile=/var/log/app/bravo-1-out.log
|
||||
stderr_logfile=/var/log/app/bravo-1-err.log
|
||||
user=app
|
||||
directory=/tmp/app
|
||||
environment=QUEUE="fastqueue,slowqueue",VERBOSE="1",PORT="5100"
|
||||
|
||||
[group:app]
|
||||
programs=app-alpha,app-bravo
|
||||
21
spec/resources/export/supervisord/app-env.conf
Normal file
21
spec/resources/export/supervisord/app-env.conf
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
[program:app-alpha]
|
||||
command=./alpha
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stopsignal=QUIT
|
||||
stdout_logfile=/var/log/app/alpha-1-out.log
|
||||
stderr_logfile=/var/log/app/alpha-1-err.log
|
||||
user=app
|
||||
directory=/tmp/app
|
||||
environment=FOO="bar",PORT="5000"
|
||||
[program:app-bravo]
|
||||
command=./bravo
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stopsignal=QUIT
|
||||
stdout_logfile=/var/log/app/bravo-1-out.log
|
||||
stderr_logfile=/var/log/app/bravo-1-err.log
|
||||
user=app
|
||||
directory=/tmp/app
|
||||
environment=FOO="bar",PORT="5100"
|
||||
@@ -8,7 +8,7 @@ stdout_logfile=/var/log/app/alpha-1-out.log
|
||||
stderr_logfile=/var/log/app/alpha-1-err.log
|
||||
user=app
|
||||
directory=/tmp/app
|
||||
environment=PORT=5000
|
||||
environment=PORT="5000"
|
||||
[program:app-bravo]
|
||||
command=./bravo
|
||||
autostart=true
|
||||
@@ -18,4 +18,7 @@ stdout_logfile=/var/log/app/bravo-1-out.log
|
||||
stderr_logfile=/var/log/app/bravo-1-err.log
|
||||
user=app
|
||||
directory=/tmp/app
|
||||
environment=PORT=5100
|
||||
environment=PORT="5100"
|
||||
|
||||
[group:app]
|
||||
programs=app-alpha,app-bravo
|
||||
|
||||
Reference in New Issue
Block a user