Compare commits

..
4 Commits
Author SHA1 Message Date
David Dollar 9b704ad852 0.48.0.pre2 2012-06-17 22:02:14 -04:00
David Dollar 43428175c3 allow color to be forced on 2012-06-17 22:02:00 -04:00
David Dollar 87f65e489c terminate gracefully if stdout goes away 2012-06-17 21:55:28 -04:00
David Dollar e5521f2cbb always flush output 2012-06-17 21:55:19 -04:00
70 changed files with 189 additions and 1265 deletions
+5 -2
View File
@@ -2,7 +2,10 @@ script: bundle exec rake spec
matrix: matrix:
allow_failures: allow_failures:
- rvm: 1.8.7
- rvm: jruby - rvm: jruby
- rvm: rbx
- rvm: ree
notifications: notifications:
email: false email: false
@@ -16,6 +19,6 @@ rvm:
- 1.8.7 - 1.8.7
- 1.9.2 - 1.9.2
- 1.9.3 - 1.9.3
- 2.0.0
- 2.1.0
- jruby - jruby
- rbx
- ree
+4 -135
View File
@@ -1,139 +1,8 @@
## 0.63.0 (2013-04-15) ## 0.48.0.pre1 (2012-06-11)
* Revert "Ensure foreman is the process group leader" [John Griffin] * Massive refactoring for programmatic control and stability [David Dollar]
* remove posix-spawn dependency as it does not work in jruby 1.7.3 [Andrew Brown & Corey Downing] * Procfile commands with shell interpolations now work again [David Dollar]
* Replace Foreman::Env with dotenv [Brandon Keepers] * Stop trying to test on Ruby 1.8 [David Dollar]
* [foreman-runner] fix sourcing as . is rarely in PATH [Barry Allard]
* Fixed specs to pass. [Kentaro Kuribayashi]
* Permit underscore for command name in Procfile. [Kentaro Kuribayashi]
* Update man/foreman.1 [Patrick Ellis]
* Remove tmux option from man page [Donald Plummer]
* Prevent upstart export from deleting similarly named upstart files [Andy Morris]
* Add MIT license text [Per Andersson]
* use "start|stop\ on runlevel [x]" for upstart config [Nick Messick]
## 0.62.0 (2013-03-08)
* Merge pull request #334 from ged/reentrant_signal_handlers [David Dollar]
* Merge pull request #335 from ged/20_encoding_fix [David Dollar]
* Try to allow children to shut down gracefully [Michael Granger]
* Add deferred signal-handling (fixes #332). [Michael Granger]
* Fix spec encoding problem under Ruby 2.0.0. [Michael Granger]
* add ruby 2.0 to travis [David Dollar]
* Merge pull request #327 from patheticpat/master [David Dollar]
* Fixed a typo in cli options description [Michael Kaiser]
* handled by mingw now [David Dollar]
## 0.61.0 (2013-01-14)
* Fix bug in color definitons [nseo]
* Fix for high CPU load when processes close output [Pavel Forkert]
* Ensure foreman is the process group leader [Christos Trochalakis]
* Don't ignore blank lines in the output [Matt Venables]
* Add license to gemspec [petedmarsh]
* Since JRuby 1.9 doesn't require posix/spawn, only follow that path if JRuby is loaded and running in 1.8 mode. [Adam Hutchison]
* Remove explicit requirement on rubygems. [Cyril Rohr]
* Dont use shared_path variable before multistage has a chance at it [Aditya Sanghi]
* Strip Windows Line Endings [Paul Morton]
* Fix man page: --directory is actually --root. [Evan Jones]
* Add timeout switch to CLI [Paulo Luis Franchini Casaretto]
* Remove expectation of double quotes around environment variables from test comparisons [Kevin McAllister]
* Remove explicit wrapping of Shellwords.escape in double quotes [Kevin McAllister]
## 0.60.2 (2012-10-08)
* Fix for nil value on io select loop, fixes #260 [Silvio Relli]
## 0.60.1 (2012-10-08)
* sleep on select() to avoid spinning the cpu [Silvio Relli]
## 0.60.0 (2012-09-25)
* foreman run can run things from the Procfile like heroku run. [Dan Peterson]
## 0.59.0 (2012-09-15)
* Use /bin/sh instead of bash for foreman-runner [Jeremy Evans]
## 0.58.0 (2012-09-14)
* dont set HOME [David Dollar]
* Add StandardOutPath to launchd export [Aaron Kalin]
* Add command argument string splitting [Aaron Kalin]
* Cleanup launchd exporter [Aaron Kalin]
* Enable trim_mode via '-' in ERB templates [Aaron Kalin]
* Add support for setting environment variables [Aaron Kalin]
* foreman run should exit with the same code as its command [Omar Khan]
* Handle multiline strings in .env file [Szymon Nowak]
* Use path and env variables in the inittab export [Indrek Juhkam]
* fixed the directory option [Arnaud Lachaume]
* Add capistrano export support [Daniel Farrell]
## 0.57.0 (2012-08-21)
* fix startup checks for upstart exporter [Aditya Sanghi]
## 0.56.0 (2012-08-19)
* read .profile, not .profile.d [David Dollar]
## 0.55.0 (2012-08-14)
* use a forked process to exec a run with environment [David Dollar]
## 0.54.0 (2012-08-14)
* use Foreman::Process to extract command running [David Dollar]
* changed to check env for bash [brntbeer]
## 0.53.0 (2012-07-24)
* put app root in $HOME [David Dollar]
## 0.52.0 (2012-07-24)
* wrap command in a runner that sources .profile.d scripts [David Dollar]
* fix upstart export specs [David Dollar]
* Make upstart export start/stop with network [Daniel Farrell]
## 0.51.0 (2012-07-11)
* dont try to colorize windows [David Dollar]
## 0.50.0 (2012-07-11)
* handle windows [David Dollar]
## 0.49.0 (2012-07-11)
* 1.8 compatibility [David Dollar]
* use one pgroup for all of foreman and kill that since ruby 1.8 sucks at pgroups [David Dollar]
* better debugging [David Dollar]
## 0.48.0 (2012-07-10)
* allow old exporter format to work, but with deprecation warning [David Dollar]
* remove debugging code [David Dollar]
* Merge pull request #219 from MarkDBlackwell/patch-1 [David Dollar]
* Avoid crash by verifying the existence of SIGHUP before accessing it. [Mark D. Blackwell]
* allow color to be forced on [David Dollar]
* terminate gracefully if stdout goes away [David Dollar]
* always flush output [David Dollar]
* Merge pull request #212 from morgoth/added-version-command [David Dollar]
* added command for displaying foreman version [Wojciech Wnętrzak]
* Merge pull request #211 from morgoth/fixed-yaml-usage [David Dollar]
* fixed using YAML [Wojciech Wnętrzak]
* test on more things, but don't fail [David Dollar]
* changelog [David Dollar]
* 0.48.0.pre1 [David Dollar]
* foreman doesn't work on ruby 1.8, may try to fix later [David Dollar]
* use bash [David Dollar]
* massive refactoring for programmatic control and stability [David Dollar]
* Merge pull request #164 from hsume2/master [David Dollar]
* Only run tmux specs if tmux is installed [Henry Hsu]
* Do not assume BUNDLE_GEMFILE [Henry Hsu]
* Add support for starting procfile in tmux session [Henry Hsu]
## 0.47.0 (2012-06-07) ## 0.47.0 (2012-06-07)
+2 -4
View File
@@ -6,7 +6,7 @@ platform :mingw do
gem "win32console", "~> 1.3.0" gem "win32console", "~> 1.3.0"
end end
platform :jruby, :ruby_18 do platform :jruby do
gem "posix-spawn", "~> 0.3.6" gem "posix-spawn", "~> 0.3.6"
end end
@@ -18,8 +18,6 @@ group :development do
gem 'rr', '~> 1.0.2' gem 'rr', '~> 1.0.2'
gem 'rspec', '~> 2.0' gem 'rspec', '~> 2.0'
gem "simplecov", :require => false gem "simplecov", :require => false
gem 'timecop', '0.6.1' gem 'timecop'
gem 'yard' gem 'yard'
gem 'mime-types', '~> 1.25.1'
gem 'rdiscount', '~> 1.6.8'
end end
+29 -35
View File
@@ -1,53 +1,49 @@
PATH PATH
remote: . remote: .
specs: specs:
foreman (0.63.0) foreman (0.48.0.pre2)
dotenv (>= 0.7)
thor (>= 0.13.6) thor (>= 0.13.6)
GEM GEM
remote: http://rubygems.org/ remote: http://rubygems.org/
specs: specs:
aws-s3 (0.6.3) aws-s3 (0.6.2)
builder builder
mime-types mime-types
xml-simple xml-simple
builder (3.2.2) builder (3.0.0)
diff-lcs (1.2.5) diff-lcs (1.1.3)
docile (1.1.3)
dotenv (0.10.0)
fakefs (0.3.2) fakefs (0.3.2)
hpricot (0.8.6) hpricot (0.8.6)
hpricot (0.8.6-java) hpricot (0.8.6-java)
mime-types (1.25.1) mime-types (1.16)
multi_json (1.9.0) multi_json (1.0.4)
mustache (0.99.5) mustache (0.11.2)
posix-spawn (0.3.8) posix-spawn (0.3.6)
rake (10.1.1) rake (0.9.2.2)
rdiscount (1.6.8) rdiscount (1.6.5)
ronn (0.7.3) ronn (0.7.3)
hpricot (>= 0.8.2) hpricot (>= 0.8.2)
mustache (>= 0.7.0) mustache (>= 0.7.0)
rdiscount (>= 1.5.8) rdiscount (>= 1.5.8)
rr (1.0.5) rr (1.0.2)
rspec (2.14.1) rspec (2.8.0)
rspec-core (~> 2.14.0) rspec-core (~> 2.8.0)
rspec-expectations (~> 2.14.0) rspec-expectations (~> 2.8.0)
rspec-mocks (~> 2.14.0) rspec-mocks (~> 2.8.0)
rspec-core (2.14.8) rspec-core (2.8.0)
rspec-expectations (2.14.5) rspec-expectations (2.8.0)
diff-lcs (>= 1.1.3, < 2.0) diff-lcs (~> 1.1.2)
rspec-mocks (2.14.6) rspec-mocks (2.8.0)
simplecov (0.8.2) simplecov (0.5.4)
docile (~> 1.1.0) multi_json (~> 1.0.3)
multi_json simplecov-html (~> 0.5.3)
simplecov-html (~> 0.8.0) simplecov-html (0.5.3)
simplecov-html (0.8.0) thor (0.15.2)
thor (0.18.1) timecop (0.3.5)
timecop (0.6.1) win32console (1.3.0-x86-mingw32)
win32console (1.3.2-x86-mingw32) xml-simple (1.0.15)
xml-simple (1.1.3) yard (0.8.2)
yard (0.8.7.3)
PLATFORMS PLATFORMS
java java
@@ -58,14 +54,12 @@ DEPENDENCIES
aws-s3 aws-s3
fakefs (~> 0.3.2) fakefs (~> 0.3.2)
foreman! foreman!
mime-types (~> 1.25.1)
posix-spawn (~> 0.3.6) posix-spawn (~> 0.3.6)
rake rake
rdiscount (~> 1.6.8)
ronn ronn
rr (~> 1.0.2) rr (~> 1.0.2)
rspec (~> 2.0) rspec (~> 2.0)
simplecov simplecov
timecop (= 0.6.1) timecop
win32console (~> 1.3.0) win32console (~> 1.3.0)
yard yard
-19
View File
@@ -1,19 +0,0 @@
Copyright (c) 2012 David Dollar
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+1 -4
View File
@@ -32,7 +32,6 @@ Manage Procfile-based applications
* [shoreman](https://github.com/hecticjeff/shoreman) - shell * [shoreman](https://github.com/hecticjeff/shoreman) - shell
* [honcho](https://github.com/nickstenning/honcho) - python * [honcho](https://github.com/nickstenning/honcho) - python
* [norman](https://github.com/josh/norman) - node.js * [norman](https://github.com/josh/norman) - node.js
* [forego](https://github.com/ddollar/forego) - Go
## Authors ## Authors
@@ -44,6 +43,4 @@ David Dollar
## License ## License
Foreman is licensed under the MIT license. MIT
See LICENSE for the full license text.
+2 -11
View File
@@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
# #
#/ Usage: foreman-runner [-d <dir>] [-p] <command> [<args>...] #/ Usage: foreman-runner [-d <dir>] <command> [<args>...]
#/ #/
#/ Run a command with exec, optionally changing directory first #/ Run a command with exec, optionally changing directory first
@@ -16,12 +16,9 @@ usage() {
exit exit
} }
read_profile="" while getopts ":hd:" OPT; do
while getopts ":hd:p" OPT; do
case $OPT in case $OPT in
d) cd "$OPTARG" ;; d) cd "$OPTARG" ;;
p) read_profile="1" ;;
h) usage ;; h) usage ;;
\?) error "invalid option: -$OPTARG" ;; \?) error "invalid option: -$OPTARG" ;;
:) error "option -$OPTARG requires an argument" ;; :) error "option -$OPTARG requires an argument" ;;
@@ -32,10 +29,4 @@ shift $((OPTIND-1))
[ -z "$1" ] && usage [ -z "$1" ] && usage
if [ "$read_profile" = "1" ]; then
if [ -f .profile ]; then
. ./.profile
fi
fi
exec "$@" exec "$@"
-2
View File
@@ -1,2 +0,0 @@
#!/bin/sh
export FOO=bar
+1 -1
View File
@@ -9,6 +9,6 @@ sigterm() {
#trap sigterm SIGTERM #trap sigterm SIGTERM
while true; do while true; do
echo "$NAME: ping $$" echo "$NAME: ping"
sleep 1 sleep 1
done done
-14
View File
@@ -1,14 +0,0 @@
pre-start script
bash << "EOF"
mkdir -p <%= log %>
chown -R <%= user %> <%= log %>
mkdir -p <%= run %>
chown -R <%= user %> <%= run %>
EOF
end script
start on runlevel [2345]
stop on runlevel [016]
-8
View File
@@ -1,8 +0,0 @@
start on starting <%= app %>-<%= name.gsub('_', '-') %>
stop on stopping <%= app %>-<%= name.gsub('_', '-') %>
respawn
env PORT=<%= port %><% engine.env.each_pair do |var, env| %>
env <%= var.upcase %>=<%= env %><% end %>
exec start-stop-daemon --start --chuid <%= user %> --chdir <%= engine.root %> --make-pidfile --pidfile <%= run %>/<%= app %>-<%= name %>-<%= num %>.pid --exec <%= executable %><%= arguments %> >> <%= log %>/<%= app %>-<%= name %>-<%= num %>.log 2>&1
@@ -1,2 +0,0 @@
start on starting <%= app %>
stop on stopping <%= app %>
+1 -12
View File
@@ -4,25 +4,14 @@
<dict> <dict>
<key>Label</key> <key>Label</key>
<string><%= "#{app}-#{name}-#{num}" %></string> <string><%= "#{app}-#{name}-#{num}" %></string>
<key>EnvironmentVariables</key>
<dict>
<%- engine.env.merge("PORT" => port).each_pair do |var,env| -%>
<key><%= var.upcase %></key>
<string><%= env %></string>
<%- end -%>
</dict>
<key>ProgramArguments</key> <key>ProgramArguments</key>
<array> <array>
<%- command_args.each do |command| -%> <string><%= process.command %></string>
<string><%= command %></string>
<%- end -%>
</array> </array>
<key>KeepAlive</key> <key>KeepAlive</key>
<true/> <true/>
<key>RunAtLoad</key> <key>RunAtLoad</key>
<true/> <true/>
<key>StandardOutPath</key>
<string><%= log %>/<%= app %>-<%= name %>-<%=num%>.log</string>
<key>StandardErrorPath</key> <key>StandardErrorPath</key>
<string><%= log %>/<%= app %>-<%= name %>-<%=num%>.log</string> <string><%= log %>/<%= app %>-<%= name %>-<%=num%>.log</string>
<key>UserName</key> <key>UserName</key>
-6
View File
@@ -1,6 +0,0 @@
[Unit]
StopWhenUnneeded=true
Wants=<%= process_master_names.join(' ') %>
[Install]
WantedBy=multi-user.target
-15
View File
@@ -1,15 +0,0 @@
[Unit]
StopWhenUnneeded=true
[Service]
User=<%= user %>
WorkingDirectory=<%= engine.root %>
Environment=PORT=<%= port %><% engine.env.each_pair do |var,env| %>
Environment=<%= var.upcase %>=<%= env %><% end %>
ExecStart=/bin/bash -lc '<%= process.command %>'
Restart=always
StandardInput=null
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=%n
KillMode=process
@@ -1,3 +0,0 @@
[Unit]
StopWhenUnneeded=true
Wants=<%= process_names.join(' ') %>
-4
View File
@@ -6,7 +6,3 @@ bash << "EOF"
EOF EOF
end script end script
start on runlevel [2345]
stop on runlevel [016]
+1 -1
View File
@@ -2,4 +2,4 @@ start on starting <%= app %>-<%= name %>
stop on stopping <%= app %>-<%= name %> stop on stopping <%= app %>-<%= name %>
respawn respawn
exec su - <%= user %> -s /bin/sh -c 'cd <%= engine.root %>; export PORT=<%= port %>;<% engine.env.each_pair do |var,env| %> export <%= var.upcase %>=<%= shell_quote(env) %>; <% end %> exec <%= process.command %> >> <%= log %>/<%=name%>-<%=num%>.log 2>&1' exec su - <%= user %> -c 'cd <%= engine.root %>; export PORT=<%= port %>;<% engine.env.each_pair do |var,env| %> export <%= var.upcase %>=<%= shell_quote(env) %>; <% end %> <%= process.command %> >> <%= log %>/<%=name%>-<%=num%>.log 2>&1'
+1 -1
View File
@@ -4,7 +4,7 @@ file pkg("foreman-#{version}.tgz") => distribution_files do |t|
assemble_distribution assemble_distribution
assemble_gems assemble_gems
rm_f "bin/foreman" rm_f "bin/foreman"
assemble resource("tgz/foreman"), "bin/foreman", 0755 assemble resource("tgz/foreman"), "foreman", 0755
end end
sh "tar czvf #{t.name} foreman" sh "tar czvf #{t.name} foreman"
+1 -2
View File
@@ -3,7 +3,6 @@ require "foreman/version"
Gem::Specification.new do |gem| Gem::Specification.new do |gem|
gem.name = "foreman" gem.name = "foreman"
gem.license = "MIT"
gem.version = Foreman::VERSION gem.version = Foreman::VERSION
gem.author = "David Dollar" gem.author = "David Dollar"
@@ -18,9 +17,9 @@ Gem::Specification.new do |gem|
gem.files << "man/foreman.1" gem.files << "man/foreman.1"
gem.add_dependency 'thor', '>= 0.13.6' gem.add_dependency 'thor', '>= 0.13.6'
gem.add_dependency 'dotenv', '>= 0.7'
if ENV["PLATFORM"] == "java" if ENV["PLATFORM"] == "java"
gem.add_dependency "posix-spawn", "~> 0.3.6"
gem.platform = Gem::Platform.new("java") gem.platform = Gem::Platform.new("java")
end end
+2 -6
View File
@@ -8,12 +8,8 @@ module Foreman
File.expand_path("../../bin/foreman-runner", __FILE__) File.expand_path("../../bin/foreman-runner", __FILE__)
end end
def self.jruby_18? def self.jruby?
defined?(RUBY_PLATFORM) and RUBY_PLATFORM == "java" and ruby_18? defined?(RUBY_PLATFORM) and RUBY_PLATFORM == "java"
end
def self.ruby_18?
defined?(RUBY_VERSION) and RUBY_VERSION =~ /^1\.8\.\d+/
end end
def self.windows? def self.windows?
+3 -34
View File
@@ -3,17 +3,13 @@ require "foreman/helpers"
require "foreman/engine" require "foreman/engine"
require "foreman/engine/cli" require "foreman/engine/cli"
require "foreman/export" require "foreman/export"
require "foreman/version"
require "shellwords" require "shellwords"
require "yaml"
require "thor" require "thor"
class Foreman::CLI < Thor class Foreman::CLI < Thor
include Foreman::Helpers include Foreman::Helpers
map ["-v", "--version"] => :version
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile" class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
class_option :root, :type => :string, :aliases => "-d", :desc => "Default: Procfile directory" class_option :root, :type => :string, :aliases => "-d", :desc => "Default: Procfile directory"
@@ -23,7 +19,6 @@ class Foreman::CLI < Thor
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 :formation, :type => :string, :aliases => "-m", :banner => '"alpha=5,bar=3"' method_option :formation, :type => :string, :aliases => "-m", :banner => '"alpha=5,bar=3"'
method_option :port, :type => :numeric, :aliases => "-p" method_option :port, :type => :numeric, :aliases => "-p"
method_option :timeout, :type => :numeric, :aliases => "-t", :desc => "Specify the amount of time (in seconds) processes have to shutdown gracefully before receiving a SIGKILL, defaults to 5."
class << self class << self
# Hackery. Take the run method away from Thor so that we can redefine it. # Hackery. Take the run method away from Thor so that we can redefine it.
@@ -34,7 +29,6 @@ class Foreman::CLI < Thor
end end
def start(process=nil) def start(process=nil)
require_posix_spawn_for_ruby_18!
check_procfile! check_procfile!
load_environment! load_environment!
engine.load_procfile(procfile) engine.load_procfile(procfile)
@@ -46,7 +40,6 @@ 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 :run, :type => :string, :aliases => "-r", :desc => "Specify the pid file directory, defaults to /var/run/<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 :user, :type => :string, :aliases => "-u" method_option :user, :type => :string, :aliases => "-u"
@@ -78,18 +71,8 @@ class Foreman::CLI < Thor
def run(*args) def run(*args)
load_environment! load_environment!
if File.exist?(procfile)
engine.load_procfile(procfile)
end
begin begin
engine.env.each { |k,v| ENV[k] = v } exec engine.env, args.shelljoin
if args.size == 1 && process = engine.process(args.first)
process.exec(:env => engine.env)
else
exec args.shelljoin
end
rescue Errno::EACCES rescue Errno::EACCES
error "not executable: #{args.first}" error "not executable: #{args.first}"
rescue Errno::ENOENT rescue Errno::ENOENT
@@ -97,12 +80,6 @@ class Foreman::CLI < Thor
end end
end end
desc "version", "Display Foreman gem version"
def version
puts Foreman::VERSION
end
no_tasks do no_tasks do
def engine def engine
@engine ||= begin @engine ||= begin
@@ -135,18 +112,10 @@ private ######################################################################
end end
end end
def require_posix_spawn_for_ruby_18!
begin
Kernel.require 'posix/spawn' # Use Kernel explicitly so we can mock the require call in the spec
rescue LoadError
error "foreman requires gem `posix-spawn` on Ruby #{RUBY_VERSION}. Please `gem install posix-spawn`."
end if Foreman.ruby_18?
end
def procfile def procfile
case case
when options[:procfile] then options[:procfile] when options[:procfile] then options[:procfile]
when options[:root] then File.expand_path(File.join(options[:root], "Procfile")) when options[:root] then File.expand_path(File.join(options[:app_root], "Procfile"))
else "Procfile" else "Procfile"
end end
end end
@@ -154,7 +123,7 @@ private ######################################################################
def options def options
original_options = super original_options = super
return original_options unless File.exists?(".foreman") return original_options unless File.exists?(".foreman")
defaults = ::YAML::load_file(".foreman") || {} defaults = YAML::load_file(".foreman") || {}
Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options)) Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options))
end end
+32 -165
View File
@@ -1,7 +1,7 @@
require "foreman" require "foreman"
require "foreman/env"
require "foreman/process" require "foreman/process"
require "foreman/procfile" require "foreman/procfile"
require "dotenv"
require "tempfile" require "tempfile"
require "timeout" require "timeout"
require "fileutils" require "fileutils"
@@ -9,10 +9,6 @@ require "thread"
class Foreman::Engine class Foreman::Engine
# The signals that the engine cares about.
#
HANDLED_SIGNALS = [ :TERM, :INT, :HUP ]
attr_reader :env attr_reader :env
attr_reader :options attr_reader :options
attr_reader :processes attr_reader :processes
@@ -28,8 +24,7 @@ class Foreman::Engine
def initialize(options={}) def initialize(options={})
@options = options.dup @options = options.dup
@options[:formation] ||= (options[:concurrency] || "all=1") @options[:formation] ||= "all=1"
@options[:timeout] ||= 5
@env = {} @env = {}
@mutex = Mutex.new @mutex = Mutex.new
@@ -37,22 +32,15 @@ class Foreman::Engine
@processes = [] @processes = []
@running = {} @running = {}
@readers = {} @readers = {}
# Self-pipe for deferred signal-handling (ala djb: http://cr.yp.to/docs/selfpipe.html)
reader, writer = create_pipe
reader.close_on_exec = true if reader.respond_to?(:close_on_exec)
writer.close_on_exec = true if writer.respond_to?(:close_on_exec)
@selfpipe = { :reader => reader, :writer => writer }
# Set up a global signal queue
# http://blog.rubybestpractices.com/posts/ewong/016-Implementing-Signal-Handlers.html
Thread.main[:signal_queue] = []
end end
# Start the processes registered to this +Engine+ # Start the processes registered to this +Engine+
# #
def start def start
register_signal_handlers trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
trap("INT") { puts "SIGINT received"; terminate_gracefully }
trap("HUP") { puts "SIGHUP received"; terminate_gracefully }
startup startup
spawn_processes spawn_processes
watch_for_output watch_for_output
@@ -61,74 +49,6 @@ class Foreman::Engine
shutdown shutdown
end end
# Set up deferred signal handlers
#
def register_signal_handlers
HANDLED_SIGNALS.each do |sig|
if ::Signal.list.include? sig.to_s
trap(sig) { Thread.main[:signal_queue] << sig ; notice_signal }
end
end
end
# Unregister deferred signal handlers
#
def restore_default_signal_handlers
HANDLED_SIGNALS.each do |sig|
trap(sig, :DEFAULT) if ::Signal.list.include? sig.to_s
end
end
# Wake the main thread up via the selfpipe when there's a signal
#
def notice_signal
@selfpipe[:writer].write_nonblock( '.' )
rescue Errno::EAGAIN
# Ignore writes that would block
rescue Errno::EINT
# Retry if another signal arrived while writing
retry
end
# Invoke the real handler for signal +sig+. This shouldn't be called directly
# by signal handlers, as it might invoke code which isn't re-entrant.
#
# @param [Symbol] sig the name of the signal to be handled
#
def handle_signal(sig)
case sig
when :TERM
handle_term_signal
when :INT
handle_interrupt
when :HUP
handle_hangup
else
system "unhandled signal #{sig}"
end
end
# Handle a TERM signal
#
def handle_term_signal
puts "SIGTERM received"
terminate_gracefully
end
# Handle an INT signal
#
def handle_interrupt
puts "SIGINT received"
terminate_gracefully
end
# Handle a HUP signal
#
def handle_hangup
puts "SIGHUP received"
terminate_gracefully
end
# Register a process to be run by this +Engine+ # Register a process to be run by this +Engine+
# #
# @param [String] name A name for this process # @param [String] name A name for this process
@@ -169,31 +89,23 @@ class Foreman::Engine
# @param [String] filename A .env file to load into the environment # @param [String] filename A .env file to load into the environment
# #
def load_env(filename) def load_env(filename)
@env.update Dotenv::Environment.new(filename) Foreman::Env.new(filename).entries do |name, value|
end @env[name] = value
# Send a signal to all processes started by this +Engine+
#
# @param [String] signal The signal to send to each process
#
def kill_children( signal="SIGTERM" )
@running.each do |pid, (process, index)|
system "sending #{signal} to #{name_for(pid)} at pid #{pid}"
begin
Process.kill( signal, pid )
rescue Errno::ESRCH, Errno::EPERM => err
system " %p when sending signal %p to pid %d: %s" %
[ err.class, signal, pid, err.message ]
end
end end
end end
# Send a signal to the whole process group. # Send a signal to all processesstarted by this +Engine+
# #
# @param [String] signal The signal to send # @param [String] signal The signal to send to each process
# #
def killall(signal="SIGTERM") def killall(signal="SIGTERM")
kill_children(signal) @running.each do |pid, (process, index)|
system "sending #{signal} to #{name_for(pid)} at pid #{pid}"
begin
Process.kill(signal, -1 * pid)
rescue Errno::ESRCH, Errno::EPERM
end
end
end end
# Get the process formation # Get the process formation
@@ -245,25 +157,8 @@ class Foreman::Engine
# #
# @returns [Fixnum] port The port to use for this instance of this process # @returns [Fixnum] port The port to use for this instance of this process
# #
def port_for(process, instance, base=nil) def port_for(process, instance)
if base base_port + (@processes.index(process) * 100) + (instance - 1)
base + (@processes.index(process.process) * 100) + (instance - 1)
else
base_port + (@processes.index(process) * 100) + (instance - 1)
end
end
# Get the base port for this foreman instance
#
# @returns [Fixnum] port The base port
#
def base_port
(options[:port] || env["PORT"] || ENV["PORT"] || 5000).to_i
end
# deprecated
def environment
env
end end
private private
@@ -284,21 +179,21 @@ private
## Helpers ########################################################## ## Helpers ##########################################################
def base_port
(options[:port] || env["PORT"] || ENV["PORT"] || 5000).to_i
end
def create_pipe def create_pipe
IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY") IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
end end
def name_for(pid) def name_for(pid)
process, index = @running[pid] process, index = @running[pid]
name_for_index(process, index)
end
def name_for_index(process, index)
[ @names[process], index.to_s ].compact.join(".") [ @names[process], index.to_s ].compact.join(".")
end end
def parse_formation(formation) def parse_formation(formation)
pairs = formation.to_s.gsub(/\s/, "").split(",") pairs = @options[:formation].to_s.gsub(/\s/, "").split(",")
pairs.inject(Hash.new(0)) do |ax, pair| pairs.inject(Hash.new(0)) do |ax, pair|
process, amount = pair.split("=") process, amount = pair.split("=")
@@ -341,10 +236,7 @@ private
1.upto(formation[@names[process]]) do |n| 1.upto(formation[@names[process]]) do |n|
reader, writer = create_pipe reader, writer = create_pipe
begin begin
pid = process.run(:output => writer, :env => { pid = process.run(:output => writer, :env => { "PORT" => port_for(process, n).to_s })
"PORT" => port_for(process, n).to_s,
"PS" => name_for_index(process, n)
})
writer.puts "started with pid #{pid}" writer.puts "started with pid #{pid}"
rescue Errno::ENOENT rescue Errno::ENOENT
writer.puts "unknown command: #{process.command}" writer.puts "unknown command: #{process.command}"
@@ -359,28 +251,9 @@ private
Thread.new do Thread.new do
begin begin
loop do loop do
io = IO.select([@selfpipe[:reader]] + @readers.values, nil, nil, 30) (IO.select(@readers.values).first || []).each do |reader|
data = reader.gets
begin output_with_mutex name_for(@readers.key(reader)), data
@selfpipe[:reader].read_nonblock(11)
rescue Errno::EAGAIN, Errno::EINTR => err
# ignore
end
# Look for any signals that arrived and handle them
while sig = Thread.main[:signal_queue].shift
self.handle_signal(sig)
end
(io.nil? ? [] : io.first).each do |reader|
next if reader == @selfpipe[:reader]
if reader.eof?
@readers.delete_if { |key, value| value == reader }
else
data = reader.gets
output_with_mutex name_for(@readers.invert[reader]), data
end
end end
end end
rescue Exception => ex rescue Exception => ex
@@ -401,21 +274,15 @@ private
def terminate_gracefully def terminate_gracefully
return if @terminating return if @terminating
restore_default_signal_handlers
@terminating = true @terminating = true
if Foreman.windows? system "sending SIGTERM to all processes"
system "sending SIGKILL to all processes" killall "SIGTERM"
kill_children "SIGKILL" Timeout.timeout(5) do
else
system "sending SIGTERM to all processes"
kill_children "SIGTERM"
end
Timeout.timeout(options[:timeout]) do
watch_for_termination while @running.length > 0 watch_for_termination while @running.length > 0
end end
rescue Timeout::Error rescue Timeout::Error
system "sending SIGKILL to all processes" system "sending SIGKILL to all processes"
kill_children "SIGKILL" killall "SIGKILL"
end end
end end
+6 -7
View File
@@ -31,7 +31,6 @@ class Foreman::Engine::CLI < Foreman::Engine
def color? def color?
return true if @@color_force return true if @@color_force
return false if Foreman.windows?
return false unless self.respond_to?(:isatty) return false unless self.respond_to?(:isatty)
self.isatty && ENV["TERM"] self.isatty && ENV["TERM"]
end end
@@ -44,17 +43,17 @@ class Foreman::Engine::CLI < Foreman::Engine
end end
FOREMAN_COLORS = %w( cyan yellow green magenta red blue bright_cyan bright_yellow FOREMAN_COLORS = %w( cyan yellow green magenta red blue intense_cyan intense_yellow
bright_green bright_magenta bright_red bright_blue ) intense_green intense_magenta intense_red, intense_blue )
def startup def startup
@colors = map_colors @colors = map_colors
proctitle "foreman: master" unless Foreman.windows? proctitle "foreman: master"
Color.enable($stdout, options[:color])
end end
def output(name, data) def output(name, data)
data.to_s.lines.map(&:chomp).each do |message| data.to_s.chomp.split("\n").each do |message|
Color.enable($stdout, options[:color]) unless $stdout.respond_to?(:color?)
output = "" output = ""
output += $stdout.color(@colors[name.split(".").first].to_sym) output += $stdout.color(@colors[name.split(".").first].to_sym)
output += "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | " output += "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
@@ -89,7 +88,7 @@ private
@names.values.each_with_index do |name, index| @names.values.each_with_index do |name, index|
colors[name] = FOREMAN_COLORS[index % FOREMAN_COLORS.length] colors[name] = FOREMAN_COLORS[index % FOREMAN_COLORS.length]
end end
colors["system"] = "bright_white" colors["system"] = "intense_white"
colors colors
end end
+27
View File
@@ -0,0 +1,27 @@
require "foreman"
class Foreman::Env
attr_reader :entries
def initialize(filename)
@entries = File.read(filename).split("\n").inject({}) do |ax, line|
if line =~ /\A([A-Za-z_0-9]+)=(.*)\z/
key = $1
case val = $2
when /\A'(.*)'\z/ then ax[key] = $1
when /\A"(.*)"\z/ then ax[key] = $1.gsub(/\\(.)/, '\1')
else ax[key] = val
end
end
ax
end
end
def entries
@entries.each do |key, value|
yield key, value
end
end
end
+1 -3
View File
@@ -1,6 +1,5 @@
require "foreman" require "foreman"
require "foreman/helpers" require "foreman/helpers"
require "pathname"
module Foreman::Export module Foreman::Export
extend Foreman::Helpers extend Foreman::Helpers
@@ -28,9 +27,8 @@ end
require "foreman/export/base" require "foreman/export/base"
require "foreman/export/inittab" require "foreman/export/inittab"
require "foreman/export/upstart" require "foreman/export/upstart"
require "foreman/export/daemon"
require "foreman/export/bluepill" require "foreman/export/bluepill"
require "foreman/export/runit" require "foreman/export/runit"
require "foreman/export/supervisord" require "foreman/export/supervisord"
require "foreman/export/launchd" require "foreman/export/launchd"
require "foreman/export/systemd"
+10 -73
View File
@@ -1,6 +1,4 @@
require "foreman/export" require "foreman/export"
require "ostruct"
require "pathname"
require "shellwords" require "shellwords"
class Foreman::Export::Base class Foreman::Export::Base
@@ -10,46 +8,18 @@ class Foreman::Export::Base
attr_reader :options attr_reader :options
attr_reader :formation attr_reader :formation
# deprecated
attr_reader :port
def initialize(location, engine, options={}) def initialize(location, engine, options={})
@location = location @location = location
@engine = engine @engine = engine
@options = options.dup @options = options.dup
@formation = engine.formation @formation = engine.formation
# deprecated
def port
Foreman::Export::Base.warn_deprecation!
engine.base_port
end
# deprecated
def template
Foreman::Export::Base.warn_deprecation!
options[:template]
end
# deprecated
def @engine.procfile
Foreman::Export::Base.warn_deprecation!
@processes.map do |process|
OpenStruct.new(
:name => @names[process],
:process => process
)
end
end
end end
def export def export
error("Must specify a location") unless location error("Must specify a location") unless location
FileUtils.mkdir_p(location) rescue error("Could not create: #{location}") FileUtils.mkdir_p(location) rescue error("Could not create: #{location}")
FileUtils.mkdir_p(log) rescue error("Could not create: #{log}") FileUtils.mkdir_p(log) rescue error("Could not create: #{log}")
FileUtils.mkdir_p(run) rescue error("Could not create: #{run}")
FileUtils.chown(user, nil, log) rescue error("Could not chown #{log} to #{user}") FileUtils.chown(user, nil, log) rescue error("Could not chown #{log} to #{user}")
FileUtils.chown(user, nil, run) rescue error("Could not chown #{run} to #{user}")
end end
def app def app
@@ -60,28 +30,12 @@ class Foreman::Export::Base
options[:log] || "/var/log/#{app}" options[:log] || "/var/log/#{app}"
end end
def run
options[:run] || "/var/run/#{app}"
end
def user def user
options[:user] || app options[:user] || app
end end
private ###################################################################### private ######################################################################
def self.warn_deprecation!
@@deprecation_warned ||= false
return if @@deprecation_warned
puts "WARNING: Using deprecated exporter interface. Please update your exporter"
puts "the interface shown in the upstart exporter:"
puts
puts "https://github.com/ddollar/foreman/blob/master/lib/foreman/export/upstart.rb"
puts "https://github.com/ddollar/foreman/blob/master/data/export/upstart/process.conf.erb"
puts
@@deprecation_warned = true
end
def error(message) def error(message)
raise Foreman::Export::Exception.new(message) raise Foreman::Export::Exception.new(message)
end end
@@ -97,35 +51,20 @@ private ######################################################################
end end
def shell_quote(value) def shell_quote(value)
Shellwords.escape(value) '"' + Shellwords.escape(value) + '"'
end end
# deprecated def export_template(name)
def old_export_template(exporter, file, template_root) name_without_first = name.split("/")[1..-1].join("/")
if template_root && File.exist?(file_path = File.join(template_root, file)) matchers = []
File.read(file_path) matchers << File.join(options[:template], name_without_first) if options[:template]
elsif File.exist?(file_path = File.expand_path(File.join("~/.foreman/templates", file))) matchers << File.expand_path("~/.foreman/templates/#{name}")
File.read(file_path) matchers << File.expand_path("../../../../data/export/#{name}", __FILE__)
else File.read(matchers.detect { |m| File.exists?(m) })
File.read(File.expand_path("../../../../data/export/#{exporter}/#{file}", __FILE__))
end
end
def export_template(name, file=nil, template_root=nil)
if file && template_root
old_export_template name, file, template_root
else
name_without_first = name.split("/")[1..-1].join("/")
matchers = []
matchers << File.join(options[:template], name_without_first) if options[:template]
matchers << File.expand_path("~/.foreman/templates/#{name}")
matchers << File.expand_path("../../../../data/export/#{name}", __FILE__)
File.read(matchers.detect { |m| File.exists?(m) })
end
end end
def write_template(name, target, binding) def write_template(name, target, binding)
compiled = ERB.new(export_template(name), nil, '-').result(binding) compiled = ERB.new(export_template(name)).result(binding)
write_file target, compiled write_file target, compiled
end end
@@ -142,9 +81,7 @@ private ######################################################################
def write_file(filename, contents) def write_file(filename, contents)
say "writing: #{filename}" say "writing: #{filename}"
filename = File.join(location, filename) unless Pathname.new(filename).absolute? File.open(File.join(location, filename), "w") do |file|
File.open(filename, "w") do |file|
file.puts contents file.puts contents
end end
end end
-28
View File
@@ -1,28 +0,0 @@
require "erb"
require "foreman/export"
class Foreman::Export::Daemon < Foreman::Export::Base
def export
super
(Dir["#{location}/#{app}-*.conf"] << "#{location}/#{app}.conf").each do |file|
clean file
end
write_template "daemon/master.conf.erb", "#{app}.conf", binding
engine.each_process do |name, process|
next if engine.formation[name] < 1
write_template "daemon/process_master.conf.erb", "#{app}-#{name}.conf", binding
1.upto(engine.formation[name]) do |num|
port = engine.port_for(process, num)
arguments = process.command.split(" ")
executable = arguments.slice!(0)
arguments = arguments.size > 0 ? " -- #{arguments.join(' ')}" : ""
write_template "daemon/process.conf.erb", "#{app}-#{name}-#{num}.conf", binding
end
end
end
end
+1 -10
View File
@@ -13,16 +13,7 @@ class Foreman::Export::Inittab < Foreman::Export::Base
1.upto(engine.formation[name]) do |num| 1.upto(engine.formation[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) port = engine.port_for(process, num)
inittab << "#{id}:4:respawn:/bin/su - #{user} -c 'PORT=#{port} #{process.command} >> #{log}/#{name}-#{num}.log 2>&1'"
commands = []
commands << "cd #{engine.root}"
commands << "export PORT=#{port}"
engine.env.each_pair do |var, env|
commands << "export #{var.upcase}=#{shell_quote(env)}"
end
commands << "#{process.command} >> #{log}/#{name}-#{num}.log 2>&1"
inittab << "#{id}:4:respawn:/bin/su - #{user} -c '#{commands.join(";")}'"
index += 1 index += 1
end end
end end
-2
View File
@@ -7,8 +7,6 @@ class Foreman::Export::Launchd < Foreman::Export::Base
super super
engine.each_process do |name, process| engine.each_process do |name, process|
1.upto(engine.formation[name]) do |num| 1.upto(engine.formation[name]) do |num|
port = engine.port_for(process, num)
command_args = process.command.split(" ")
write_template "launchd/launchd.plist.erb", "#{app}-#{name}-#{num}.plist", binding write_template "launchd/launchd.plist.erb", "#{app}-#{name}-#{num}.plist", binding
end end
end end
-32
View File
@@ -1,32 +0,0 @@
require "erb"
require "foreman/export"
class Foreman::Export::Systemd < Foreman::Export::Base
def export
super
Dir["#{location}/#{app}*.target"].concat(Dir["#{location}/#{app}*.service"]).each do |file|
clean file
end
process_master_names = []
engine.each_process do |name, process|
next if engine.formation[name] < 1
process_names = []
1.upto(engine.formation[name]) do |num|
port = engine.port_for(process, num)
write_template "systemd/process.service.erb", "#{app}-#{name}-#{num}.service", binding
process_names << "#{app}-#{name}-#{num}.service"
end
write_template "systemd/process_master.target.erb", "#{app}-#{name}.target", binding
process_master_names << "#{app}-#{name}.target"
end
write_template "systemd/master.target.erb", "#{app}.target", binding
end
end
+1 -1
View File
@@ -6,7 +6,7 @@ class Foreman::Export::Upstart < Foreman::Export::Base
def export def export
super super
(Dir["#{location}/#{app}-*.conf"] << "#{location}/#{app}.conf").each do |file| Dir["#{location}/#{app}*.conf"].each do |file|
clean file clean file
end end
+14 -58
View File
@@ -1,5 +1,5 @@
require "foreman" require "foreman"
require "shellwords" require "rubygems"
class Foreman::Process class Foreman::Process
@@ -21,21 +21,6 @@ class Foreman::Process
@options[:env] ||= {} @options[:env] ||= {}
end end
# Get environment-expanded command for a +Process+
#
# @param [Hash] custom_env ({}) Environment variables to merge with defaults
#
# @return [String] The expanded command
#
def expanded_command(custom_env={})
env = @options[:env].merge(custom_env)
expanded_command = command.dup
env.each do |key, val|
expanded_command.gsub!("$#{key}", val)
end
expanded_command
end
# Run a +Process+ # Run a +Process+
# #
# @param [Hash] options # @param [Hash] options
@@ -46,48 +31,31 @@ class Foreman::Process
# @returns [Fixnum] pid The +pid+ of the process # @returns [Fixnum] pid The +pid+ of the process
# #
def run(options={}) def run(options={})
env = @options[:env].merge(options[:env] || {}) env = options[:env] ? @options[:env].merge(options[:env]) : @options[:env]
output = options[:output] || $stdout output = options[:output] || $stdout
runner = "#{Foreman.runner}".shellescape
if Foreman.windows? if Foreman.windows?
Dir.chdir(cwd) do Dir.chdir(cwd) do
Process.spawn env, expanded_command(env), :out => output, :err => output Process.spawn env, command, :out => output, :err => output, :new_pgroup => true
end
elsif Foreman.jruby?
Dir.chdir(cwd) do
require "posix/spawn"
POSIX::Spawn.spawn env, command, :out => output, :err => output, :pgroup => 0
end end
elsif Foreman.jruby_18? || Foreman.ruby_18?
require "posix/spawn"
wrapped_command = "#{runner} -d '#{cwd.shellescape}' -p -- #{expanded_command(env)}"
POSIX::Spawn.spawn(*spawn_args(env, wrapped_command.shellsplit, {:out => output, :err => output}))
else else
wrapped_command = "#{runner} -d '#{cwd.shellescape}' -p -- #{command}" Dir.chdir(cwd) do
Process.spawn env, wrapped_command, :out => output, :err => output Process.spawn env, command, :out => output, :err => output, :pgroup => 0
end
end end
end end
# Exec a +Process+
#
# @param [Hash] options
#
# @option options :env ({}) Environment variables to set for this execution
#
# @return Does not return
def exec(options={})
env = @options[:env].merge(options[:env] || {})
env.each { |k, v| ENV[k] = v }
Dir.chdir(cwd)
Kernel.exec expanded_command(env)
end
# Send a signal to this +Process+ # Send a signal to this +Process+
# #
# @param [String] signal The signal to send # @param [String] signal The signal to send
# #
def kill(signal) def kill(signal)
if Foreman.windows? pid && Process.kill(signal, -1 * pid)
pid && Process.kill(signal, pid)
else
pid && Process.kill("-#{signal}", pid)
end
rescue Errno::ESRCH rescue Errno::ESRCH
false false
end end
@@ -108,22 +76,10 @@ class Foreman::Process
!alive? !alive?
end end
# Returns the working directory for this +Process+
#
# @returns [String]
#
def cwd
File.expand_path(@options[:cwd] || ".")
end
private private
def spawn_args(env, argv, options) def cwd
args = [] @options[:cwd] || "."
args << env
args += argv
args << options
args
end end
end end
+2 -2
View File
@@ -82,8 +82,8 @@ class Foreman::Procfile
private private
def parse(filename) def parse(filename)
File.read(filename).gsub("\r\n","\n").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*(.+)$/
[$1, $2] [$1, $2]
end end
end.compact end.compact
+1 -1
View File
@@ -1,5 +1,5 @@
module Foreman module Foreman
VERSION = "0.63.0" VERSION = "0.48.0.pre2"
end end
+8 -25
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" "May 2013" "Foreman 0.63.0" "Foreman Manual" .TH "FOREMAN" "1" "April 2012" "Foreman 0.46.0" "Foreman Manual"
. .
.SH "NAME" .SH "NAME"
\fBforeman\fR \- manage Procfile\-based applications \fBforeman\fR \- manage Procfile\-based applications
@@ -46,6 +46,10 @@ Specify an alternate Procfile to load, implies \fB\-d\fR at the Procfile root\.
\fB\-p\fR, \fB\-\-port\fR \fB\-p\fR, \fB\-\-port\fR
Specify which port to use as the base for this application\. Should be a multiple of 1000\. Specify which port to use as the base for this application\. Should be a multiple of 1000\.
. .
.TP
\fB\-t\fR, \fB\-\-tmux\fR
Runs the processes in a tmux session\. Creates one window for each process and an extra window containing the output of each window (requires gawk)\.
.
.P .P
\fBforeman run\fR is used to run one\-off commands using the same environment as your defined processes\. \fBforeman run\fR is used to run one\-off commands using the same environment as your defined processes\.
. .
@@ -86,7 +90,7 @@ Specify the user the application should be run as\. Defaults to the app name
These options control all modes of foreman\'s operation\. These options control all modes of foreman\'s operation\.
. .
.TP .TP
\fB\-d\fR, \fB\-\-root\fR \fB\-d\fR, \fB\-\-directory\fR
Specify an alternate application root\. This defaults to the directory containing the Procfile\. Specify an alternate application root\. This defaults to the directory containing the Procfile\.
. .
.TP .TP
@@ -107,18 +111,9 @@ bluepill
inittab inittab
. .
.IP "\(bu" 4 .IP "\(bu" 4
launchd
.
.IP "\(bu" 4
runit runit
. .
.IP "\(bu" 4 .IP "\(bu" 4
supervisord
.
.IP "\(bu" 4
systemd
.
.IP "\(bu" 4
upstart upstart
. .
.IP "" 0 .IP "" 0
@@ -139,18 +134,6 @@ EX02:4:respawn:/bin/su \- example \-c \'PORT=5100 bundle exec rake jobs:work >>
. .
.IP "" 0 .IP "" 0
. .
.SH "SYSTEMD EXPORT"
Will create a series of systemd scripts in the location you specify\. Scripts will be structured to make the following commands valid:
.
.P
\fBsystemctl start appname\.target\fR
.
.P
\fBsystemctl stop appname\-processname\.target\fR
.
.P
\fBsystemctl restart appname\-processname\-3\.service\fR
.
.SH "UPSTART EXPORT" .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: Will create a series of upstart scripts in the location you specify\. Scripts will be structured to make the following commands valid:
. .
@@ -178,7 +161,7 @@ job: bundle exec rake jobs:work
.IP "" 0 .IP "" 0
. .
.P .P
A process name may contain letters, numbers and the underscore character\. 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
. .
@@ -251,7 +234,7 @@ Run one process type from the application defined in a specific Procfile:
. .
.nf .nf
$ foreman start alpha \-f ~/myapp/Procfile $ foreman start alpha \-p ~/myapp/Procfile
. .
.fi .fi
. .
+7 -20
View File
@@ -40,6 +40,10 @@ The following options control how the application is run:
Specify which port to use as the base for this application. Should be Specify which port to use as the base for this application. Should be
a multiple of 1000. a multiple of 1000.
* `-t`, `--tmux`:
Runs the processes in a tmux session. Creates one window for each process
and an extra window containing the output of each window (requires gawk).
`foreman run` is used to run one-off commands using the same environment `foreman run` is used to run one-off commands using the same environment
as your defined processes. as your defined processes.
@@ -80,7 +84,7 @@ 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.
* `-d`, `--root`: * `-d`, `--directory`:
Specify an alternate application root. This defaults to the directory Specify an alternate application root. This defaults to the directory
containing the Procfile. containing the Procfile.
@@ -101,14 +105,8 @@ foreman currently supports the following output formats:
* inittab * inittab
* launchd
* runit * runit
* supervisord
* systemd
* upstart * upstart
## INITTAB EXPORT ## INITTAB EXPORT
@@ -120,17 +118,6 @@ Will export a chunk of inittab-compatible configuration:
EX02:4:respawn:/bin/su - example -c 'PORT=5100 bundle exec rake jobs:work >> /var/log/job-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 ----- # ----- end foreman example processes -----
## SYSTEMD EXPORT
Will create a series of systemd scripts in the location you specify. Scripts
will be structured to make the following commands valid:
`systemctl start appname.target`
`systemctl stop appname-processname.target`
`systemctl restart appname-processname-3.service`
## UPSTART EXPORT ## UPSTART EXPORT
Will create a series of upstart scripts in the location you specify. Scripts Will create a series of upstart scripts in the location you specify. Scripts
@@ -150,7 +137,7 @@ 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
A process name may contain letters, numbers and the underscore character. A process name may contain letters, numbers amd the underscore character.
You can validate your Procfile format using the `check` command: You can validate your Procfile format using the `check` command:
$ foreman check $ foreman check
@@ -185,7 +172,7 @@ Export the application in upstart format:
Run one process type from the application defined in a specific Procfile: Run one process type from the application defined in a specific Procfile:
$ foreman start alpha -f ~/myapp/Procfile $ foreman start alpha -p ~/myapp/Procfile
## COPYRIGHT ## COPYRIGHT
+1 -35
View File
@@ -44,20 +44,13 @@ describe "Foreman::CLI", :fakefs do
output.should =~ /test.1 \| testing/ output.should =~ /test.1 \| testing/
end end
end end
it "sets PS variable with the process name" do
without_fakefs do
output = foreman("start -f #{resource_path("Procfile")}")
output.should =~ /ps.1 \| PS env var is ps.1/
end
end
end end
end end
describe "check" do describe "check" do
it "with a valid Procfile displays the jobs" do it "with a valid Procfile displays the jobs" do
write_procfile write_procfile
foreman("check").should == "valid procfile detected (alpha, bravo, foo_bar, foo-bar)\n" foreman("check").should == "valid procfile detected (alpha, bravo)\n"
end end
it "with a blank Procfile displays an error" do it "with a blank Procfile displays an error" do
@@ -79,33 +72,6 @@ describe "Foreman::CLI", :fakefs do
it "includes the environment" do it "includes the environment" do
forked_foreman("run #{resource_path("bin/env FOO")} -e #{resource_path(".env")}").should == "bar\n" forked_foreman("run #{resource_path("bin/env FOO")} -e #{resource_path(".env")}").should == "bar\n"
end end
it "can run a command from the Procfile" do
forked_foreman("run -f #{resource_path("Procfile")} test").should == "testing\n"
end
it "exits with the same exit code as the command" do
fork_and_get_exitstatus("run echo 1").should == 0
fork_and_get_exitstatus("run date 'invalid_date'").should == 1
end
end end
describe "version" do
it "displays gem version" do
foreman("version").chomp.should == Foreman::VERSION
end
it "displays gem version on shortcut command" do
foreman("-v").chomp.should == Foreman::VERSION
end
end
describe "when posix-spawn is not present on ruby 1.8" do
it "should fail with an error" do
mock(Kernel).require('posix/spawn') { raise LoadError }
output = foreman("start -f #{resource_path("Procfile")}")
output.should == "ERROR: foreman requires gem `posix-spawn` on Ruby #{RUBY_VERSION}. Please `gem install posix-spawn`.\n"
end
end if running_ruby_18?
end end
-8
View File
@@ -90,14 +90,6 @@ describe "Foreman::Engine", :fakefs do
subject.env["OTHER"].should == 'escaped"quote' subject.env["OTHER"].should == 'escaped"quote'
end end
it "should handle multiline strings" do
File.open("/tmp/env", "w") do |f|
f.puts 'FOO="bar\nbaz"'
end
subject.load_env "/tmp/env"
subject.env["FOO"].should == "bar\nbaz"
end
it "should fail if specified and doesnt exist" do it "should fail if specified and doesnt exist" do
lambda { subject.load_env "/tmp/env" }.should raise_error(Errno::ENOENT) lambda { subject.load_env "/tmp/env" }.should raise_error(Errno::ENOENT)
end end
-97
View File
@@ -1,97 +0,0 @@
require "spec_helper"
require "foreman/engine"
require "foreman/export/daemon"
require "tmpdir"
describe Foreman::Export::Daemon, :fakefs do
let(:procfile) { write_procfile("/tmp/app/Procfile") }
let(:formation) { nil }
let(:engine) { Foreman::Engine.new(:formation => formation).load_procfile(procfile) }
let(:options) { Hash.new }
let(:daemon) { Foreman::Export::Daemon.new("/tmp/init", engine, options) }
before(:each) { load_export_templates_into_fakefs("daemon") }
before(:each) { stub(daemon).say }
it "exports to the filesystem" do
daemon.export
File.read("/tmp/init/app.conf").should == example_export_file("daemon/app.conf")
File.read("/tmp/init/app-alpha.conf").should == example_export_file("daemon/app-alpha.conf")
File.read("/tmp/init/app-alpha-1.conf").should == example_export_file("daemon/app-alpha-1.conf")
File.read("/tmp/init/app-bravo.conf").should == example_export_file("daemon/app-bravo.conf")
File.read("/tmp/init/app-bravo-1.conf").should == example_export_file("daemon/app-bravo-1.conf")
end
it "cleans up if exporting into an existing dir" do
mock(FileUtils).rm("/tmp/init/app.conf")
mock(FileUtils).rm("/tmp/init/app-alpha.conf")
mock(FileUtils).rm("/tmp/init/app-alpha-1.conf")
mock(FileUtils).rm("/tmp/init/app-bravo.conf")
mock(FileUtils).rm("/tmp/init/app-bravo-1.conf")
mock(FileUtils).rm("/tmp/init/app-foo-bar.conf")
mock(FileUtils).rm("/tmp/init/app-foo-bar-1.conf")
mock(FileUtils).rm("/tmp/init/app-foo_bar.conf")
mock(FileUtils).rm("/tmp/init/app-foo_bar-1.conf")
daemon.export
daemon.export
end
it "does not delete exported files for similarly named applications" do
FileUtils.mkdir_p "/tmp/init"
["app2", "app2-alpha", "app2-alpha-1"].each do |name|
path = "/tmp/init/#{name}.conf"
FileUtils.touch(path)
dont_allow(FileUtils).rm(path)
end
daemon.export
end
context "with a formation" do
let(:formation) { "alpha=2" }
it "exports to the filesystem with concurrency" do
daemon.export
File.read("/tmp/init/app.conf").should == example_export_file("daemon/app.conf")
File.read("/tmp/init/app-alpha.conf").should == example_export_file("daemon/app-alpha.conf")
File.read("/tmp/init/app-alpha-1.conf").should == example_export_file("daemon/app-alpha-1.conf")
File.read("/tmp/init/app-alpha-2.conf").should == example_export_file("daemon/app-alpha-2.conf")
File.exists?("/tmp/init/app-bravo-1.conf").should == false
end
end
context "with alternate templates" do
let(:template) { "/tmp/alternate" }
let(:options) { { :app => "app", :template => template } }
before do
FileUtils.mkdir_p template
File.open("#{template}/master.conf.erb", "w") { |f| f.puts "alternate_template" }
end
it "can export with alternate template files" do
daemon.export
File.read("/tmp/init/app.conf").should == "alternate_template\n"
end
end
context "with alternate templates from home dir" do
before do
FileUtils.mkdir_p File.expand_path("~/.foreman/templates/daemon")
File.open(File.expand_path("~/.foreman/templates/daemon/master.conf.erb"), "w") do |file|
file.puts "default_alternate_template"
end
end
it "can export with alternate template files" do
daemon.export
File.read("/tmp/init/app.conf").should == "default_alternate_template\n"
end
end
end
-10
View File
@@ -18,14 +18,4 @@ describe Foreman::Export::Launchd, :fakefs do
File.read("/tmp/init/app-bravo-1.plist").should == example_export_file("launchd/launchd-b.default") File.read("/tmp/init/app-bravo-1.plist").should == example_export_file("launchd/launchd-b.default")
end end
context "with multiple command arguments" do
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile", "charlie") }
it "splits each command argument" do
launchd.export
File.read("/tmp/init/app-alpha-1.plist").should == example_export_file("launchd/launchd-c.default")
end
end
end end
+1 -1
View File
@@ -31,6 +31,6 @@ describe Foreman::Export::Runit, :fakefs do
end end
it "creates a full path to the export directory" do it "creates a full path to the export directory" do
expect { runit.export }.to_not raise_error expect { runit.export }.to_not raise_error(Errno::ENOENT)
end end
end end
-91
View File
@@ -1,91 +0,0 @@
require "spec_helper"
require "foreman/engine"
require "foreman/export/systemd"
require "tmpdir"
describe Foreman::Export::Systemd, :fakefs do
let(:procfile) { write_procfile("/tmp/app/Procfile") }
let(:formation) { nil }
let(:engine) { Foreman::Engine.new(:formation => formation).load_procfile(procfile) }
let(:options) { Hash.new }
let(:systemd) { Foreman::Export::Systemd.new("/tmp/init", engine, options) }
before(:each) { load_export_templates_into_fakefs("systemd") }
before(:each) { stub(systemd).say }
it "exports to the filesystem" do
systemd.export
File.read("/tmp/init/app.target").should == example_export_file("systemd/app.target")
File.read("/tmp/init/app-alpha.target").should == example_export_file("systemd/app-alpha.target")
File.read("/tmp/init/app-alpha-1.service").should == example_export_file("systemd/app-alpha-1.service")
File.read("/tmp/init/app-bravo.target").should == example_export_file("systemd/app-bravo.target")
File.read("/tmp/init/app-bravo-1.service").should == example_export_file("systemd/app-bravo-1.service")
end
it "cleans up if exporting into an existing dir" do
mock(FileUtils).rm("/tmp/init/app.target")
mock(FileUtils).rm("/tmp/init/app-alpha.target")
mock(FileUtils).rm("/tmp/init/app-alpha-1.service")
mock(FileUtils).rm("/tmp/init/app-bravo.target")
mock(FileUtils).rm("/tmp/init/app-bravo-1.service")
mock(FileUtils).rm("/tmp/init/app-foo-bar.target")
mock(FileUtils).rm("/tmp/init/app-foo-bar-1.service")
mock(FileUtils).rm("/tmp/init/app-foo_bar.target")
mock(FileUtils).rm("/tmp/init/app-foo_bar-1.service")
systemd.export
systemd.export
end
it "includes environment variables" do
engine.env['KEY'] = 'some "value"'
systemd.export
File.read("/tmp/init/app-alpha-1.service").should =~ /KEY=some "value"$/
end
context "with a formation" do
let(:formation) { "alpha=2" }
it "exports to the filesystem with concurrency" do
systemd.export
File.read("/tmp/init/app.target").should == example_export_file("systemd/app.target")
File.read("/tmp/init/app-alpha.target").should == example_export_file("systemd/app-alpha.target")
File.read("/tmp/init/app-alpha-1.service").should == example_export_file("systemd/app-alpha-1.service")
File.read("/tmp/init/app-alpha-2.service").should == example_export_file("systemd/app-alpha-2.service")
File.exists?("/tmp/init/app-bravo-1.service").should == false
end
end
context "with alternate templates" do
let(:template) { "/tmp/alternate" }
let(:options) { { :app => "app", :template => template } }
before do
FileUtils.mkdir_p template
File.open("#{template}/master.target.erb", "w") { |f| f.puts "alternate_template" }
end
it "can export with alternate template files" do
systemd.export
File.read("/tmp/init/app.target").should == "alternate_template\n"
end
end
context "with alternate templates from home dir" do
before do
FileUtils.mkdir_p File.expand_path("~/.foreman/templates/systemd")
File.open(File.expand_path("~/.foreman/templates/systemd/master.target.erb"), "w") do |file|
file.puts "default_alternate_template"
end
end
it "can export with alternate template files" do
systemd.export
File.read("/tmp/init/app.target").should == "default_alternate_template\n"
end
end
end
+1 -17
View File
@@ -29,32 +29,16 @@ describe Foreman::Export::Upstart, :fakefs do
mock(FileUtils).rm("/tmp/init/app-alpha-1.conf") mock(FileUtils).rm("/tmp/init/app-alpha-1.conf")
mock(FileUtils).rm("/tmp/init/app-bravo.conf") mock(FileUtils).rm("/tmp/init/app-bravo.conf")
mock(FileUtils).rm("/tmp/init/app-bravo-1.conf") mock(FileUtils).rm("/tmp/init/app-bravo-1.conf")
mock(FileUtils).rm("/tmp/init/app-foo-bar.conf")
mock(FileUtils).rm("/tmp/init/app-foo-bar-1.conf")
mock(FileUtils).rm("/tmp/init/app-foo_bar.conf")
mock(FileUtils).rm("/tmp/init/app-foo_bar-1.conf")
upstart.export upstart.export
upstart.export upstart.export
end end
it "does not delete exported files for similarly named applications" do
FileUtils.mkdir_p "/tmp/init"
["app2", "app2-alpha", "app2-alpha-1"].each do |name|
path = "/tmp/init/#{name}.conf"
FileUtils.touch(path)
dont_allow(FileUtils).rm(path)
end
upstart.export
end
it "quotes and escapes environment variables" do it "quotes and escapes environment variables" do
engine.env['KEY'] = 'd"\|d' engine.env['KEY'] = 'd"\|d'
upstart.export upstart.export
"foobarfoo".should include "bar" "foobarfoo".should include "bar"
File.read("/tmp/init/app-alpha-1.conf").should =~ /KEY=d\\"\\\\\\\|d/ File.read("/tmp/init/app-alpha-1.conf").should =~ /KEY="d\\"\\\\\\\|d/
end end
context "with a formation" do context "with a formation" do
+1 -1
View File
@@ -41,7 +41,7 @@ describe Foreman::Process do
it "should output utf8 properly" do it "should output utf8 properly" do
process = Foreman::Process.new(resource_path("bin/utf8")) process = Foreman::Process.new(resource_path("bin/utf8"))
run(process).should == (Foreman.ruby_18? ? "\xFF\x03\n" : "\xFF\x03\n".force_encoding('binary')) run(process).should == "\xFF\x03\n"
end end
end end
+2 -4
View File
@@ -16,10 +16,8 @@ describe Foreman::Procfile, :fakefs do
it "loads a passed-in Procfile" do it "loads a passed-in Procfile" do
write_procfile write_procfile
procfile = Foreman::Procfile.new("Procfile") procfile = Foreman::Procfile.new("Procfile")
procfile["alpha"].should == "./alpha" procfile["alpha"].should == "./alpha"
procfile["bravo"].should == "./bravo" procfile["bravo"].should == "./bravo"
procfile["foo-bar"].should == "./foo-bar"
procfile["foo_bar"].should == "./foo_bar"
end end
it "can have a process appended to it" do it "can have a process appended to it" do
-1
View File
@@ -2,4 +2,3 @@ echo: bin/echo echoing
env: bin/env FOO env: bin/env FOO
test: bin/test test: bin/test
utf8: bin/utf8 utf8: bin/utf8
ps: bin/echo PS env var is $PS
-35
View File
@@ -42,40 +42,5 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
process.group = "app-bravo" process.group = "app-bravo"
end end
app.process("foo_bar-1") do |process|
process.start_command = "./foo_bar"
process.working_dir = "/tmp/app"
process.daemonize = true
process.environment = {"PORT"=>"5200"}
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
process.stop_grace_time = 45.seconds
process.stdout = process.stderr = "/var/log/app/app-foo_bar-1.log"
process.monitor_children do |children|
children.stop_command "kill {{PID}}"
end
process.group = "app-foo_bar"
end
app.process("foo-bar-1") do |process|
process.start_command = "./foo-bar"
process.working_dir = "/tmp/app"
process.daemonize = true
process.environment = {"PORT"=>"5300"}
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
process.stop_grace_time = 45.seconds
process.stdout = process.stderr = "/var/log/app/app-foo-bar-1.log"
process.monitor_children do |children|
children.stop_command "kill {{PID}}"
end
process.group = "app-foo-bar"
end
end end
@@ -1,7 +0,0 @@
start on starting app-alpha
stop on stopping app-alpha
respawn
env PORT=5000
exec start-stop-daemon --start --chuid app --chdir /tmp/app --make-pidfile --pidfile /var/run/app/app-alpha-1.pid --exec ./alpha >> /var/log/app/app-alpha-1.log 2>&1
@@ -1,7 +0,0 @@
start on starting app-alpha
stop on stopping app-alpha
respawn
env PORT=5001
exec start-stop-daemon --start --chuid app --chdir /tmp/app --make-pidfile --pidfile /var/run/app/app-alpha-2.pid --exec ./alpha >> /var/log/app/app-alpha-2.log 2>&1
@@ -1,2 +0,0 @@
start on starting app
stop on stopping app
@@ -1,7 +0,0 @@
start on starting app-bravo
stop on stopping app-bravo
respawn
env PORT=5100
exec start-stop-daemon --start --chuid app --chdir /tmp/app --make-pidfile --pidfile /var/run/app/app-bravo-1.pid --exec ./bravo >> /var/log/app/app-bravo-1.log 2>&1
@@ -1,2 +0,0 @@
start on starting app
stop on stopping app
-14
View File
@@ -1,14 +0,0 @@
pre-start script
bash << "EOF"
mkdir -p /var/log/app
chown -R app /var/log/app
mkdir -p /var/run/app
chown -R app /var/run/app
EOF
end script
start on runlevel [2345]
stop on runlevel [016]
@@ -1,4 +1,4 @@
# ----- foreman app processes ----- # ----- foreman app processes -----
AP01:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5000;./alpha >> /var/log/app/alpha-1.log 2>&1' AP01:4:respawn:/bin/su - app -c 'PORT=5000 ./alpha >> /var/log/app/alpha-1.log 2>&1'
AP02:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5001;./alpha >> /var/log/app/alpha-2.log 2>&1' AP02:4:respawn:/bin/su - app -c 'PORT=5001 ./alpha >> /var/log/app/alpha-2.log 2>&1'
# ----- end foreman app processes ----- # ----- end foreman app processes -----
@@ -1,6 +1,4 @@
# ----- foreman app processes ----- # ----- foreman app processes -----
AP01:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5000;./alpha >> /var/log/app/alpha-1.log 2>&1' AP01:4:respawn:/bin/su - app -c 'PORT=5000 ./alpha >> /var/log/app/alpha-1.log 2>&1'
AP02:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5100;./bravo >> /var/log/app/bravo-1.log 2>&1' AP02:4:respawn:/bin/su - app -c 'PORT=5100 ./bravo >> /var/log/app/bravo-1.log 2>&1'
AP03:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5200;./foo_bar >> /var/log/app/foo_bar-1.log 2>&1'
AP04:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5300;./foo-bar >> /var/log/app/foo-bar-1.log 2>&1'
# ----- end foreman app processes ----- # ----- end foreman app processes -----
@@ -4,11 +4,6 @@
<dict> <dict>
<key>Label</key> <key>Label</key>
<string>app-alpha-1</string> <string>app-alpha-1</string>
<key>EnvironmentVariables</key>
<dict>
<key>PORT</key>
<string>5000</string>
</dict>
<key>ProgramArguments</key> <key>ProgramArguments</key>
<array> <array>
<string>./alpha</string> <string>./alpha</string>
@@ -17,8 +12,6 @@
<true/> <true/>
<key>RunAtLoad</key> <key>RunAtLoad</key>
<true/> <true/>
<key>StandardOutPath</key>
<string>/var/log/app/app-alpha-1.log</string>
<key>StandardErrorPath</key> <key>StandardErrorPath</key>
<string>/var/log/app/app-alpha-1.log</string> <string>/var/log/app/app-alpha-1.log</string>
<key>UserName</key> <key>UserName</key>
@@ -4,11 +4,6 @@
<dict> <dict>
<key>Label</key> <key>Label</key>
<string>app-bravo-1</string> <string>app-bravo-1</string>
<key>EnvironmentVariables</key>
<dict>
<key>PORT</key>
<string>5100</string>
</dict>
<key>ProgramArguments</key> <key>ProgramArguments</key>
<array> <array>
<string>./bravo</string> <string>./bravo</string>
@@ -17,8 +12,6 @@
<true/> <true/>
<key>RunAtLoad</key> <key>RunAtLoad</key>
<true/> <true/>
<key>StandardOutPath</key>
<string>/var/log/app/app-bravo-1.log</string>
<key>StandardErrorPath</key> <key>StandardErrorPath</key>
<string>/var/log/app/app-bravo-1.log</string> <string>/var/log/app/app-bravo-1.log</string>
<key>UserName</key> <key>UserName</key>
@@ -1,30 +0,0 @@
<?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>EnvironmentVariables</key>
<dict>
<key>PORT</key>
<string>5000</string>
</dict>
<key>ProgramArguments</key>
<array>
<string>./alpha</string>
<string>charlie</string>
</array>
<key>KeepAlive</key>
<true/>
<key>RunAtLoad</key>
<true/>
<key>StandardOutPath</key>
<string>/var/log/app/app-alpha-1.log</string>
<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>
@@ -8,7 +8,7 @@ stdout_logfile=/var/log/app/alpha-1.log
stderr_logfile=/var/log/app/alpha-1.error.log stderr_logfile=/var/log/app/alpha-1.error.log
user=app user=app
directory=/tmp/app directory=/tmp/app
environment=PORT=5000 environment=PORT="5000"
[program:app-bravo-1] [program:app-bravo-1]
command=./bravo command=./bravo
autostart=true autostart=true
@@ -18,27 +18,7 @@ stdout_logfile=/var/log/app/bravo-1.log
stderr_logfile=/var/log/app/bravo-1.error.log stderr_logfile=/var/log/app/bravo-1.error.log
user=app user=app
directory=/tmp/app directory=/tmp/app
environment=PORT=5100 environment=PORT="5100"
[program:app-foo_bar-1]
command=./foo_bar
autostart=true
autorestart=true
stopsignal=QUIT
stdout_logfile=/var/log/app/foo_bar-1.log
stderr_logfile=/var/log/app/foo_bar-1.error.log
user=app
directory=/tmp/app
environment=PORT=5200
[program:app-foo-bar-1]
command=./foo-bar
autostart=true
autorestart=true
stopsignal=QUIT
stdout_logfile=/var/log/app/foo-bar-1.log
stderr_logfile=/var/log/app/foo-bar-1.error.log
user=app
directory=/tmp/app
environment=PORT=5300
[group:app] [group:app]
programs=app-alpha-1,app-bravo-1,app-foo_bar-1,app-foo-bar-1 programs=app-alpha-1,app-bravo-1
@@ -8,7 +8,7 @@ stdout_logfile=/var/log/app/alpha-1.log
stderr_logfile=/var/log/app/alpha-1.error.log stderr_logfile=/var/log/app/alpha-1.error.log
user=app user=app
directory=/tmp/app directory=/tmp/app
environment=PORT=5000 environment=PORT="5000"
[program:app-alpha-2] [program:app-alpha-2]
command=./alpha command=./alpha
autostart=true autostart=true
@@ -18,7 +18,7 @@ stdout_logfile=/var/log/app/alpha-2.log
stderr_logfile=/var/log/app/alpha-2.error.log stderr_logfile=/var/log/app/alpha-2.error.log
user=app user=app
directory=/tmp/app directory=/tmp/app
environment=PORT=5001 environment=PORT="5001"
[group:app] [group:app]
programs=app-alpha-1,app-alpha-2 programs=app-alpha-1,app-alpha-2
@@ -1,17 +0,0 @@
[Unit]
StopWhenUnneeded=true
[Service]
User=app
WorkingDirectory=/tmp/app
Environment=PORT=5000
ExecStart=/bin/bash -lc './alpha'
Restart=always
StandardInput=null
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=%n
KillMode=process
[Install]
WantedBy=app-alpha.target
@@ -1,17 +0,0 @@
[Unit]
StopWhenUnneeded=true
[Service]
User=app
WorkingDirectory=/tmp/app
Environment=PORT=5001
ExecStart=/bin/bash -lc './alpha'
Restart=always
StandardInput=null
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=%n
KillMode=process
[Install]
WantedBy=app-alpha.target
@@ -1,5 +0,0 @@
[Unit]
StopWhenUnneeded=true
[Install]
WantedBy=app.target
@@ -1,17 +0,0 @@
[Unit]
StopWhenUnneeded=true
[Service]
User=app
WorkingDirectory=/tmp/app
Environment=PORT=5100
ExecStart=/bin/bash -lc './bravo'
Restart=always
StandardInput=null
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=%n
KillMode=process
[Install]
WantedBy=app-bravo.target
@@ -1,5 +0,0 @@
[Unit]
StopWhenUnneeded=true
[Install]
WantedBy=app.target
-1
View File
@@ -1 +0,0 @@
[Unit]
-4
View File
@@ -6,7 +6,3 @@ bash << "EOF"
EOF EOF
end script end script
start on runlevel [2345]
stop on runlevel [016]
+7 -34
View File
@@ -1,3 +1,5 @@
require "rubygems"
require "simplecov" require "simplecov"
SimpleCov.start do SimpleCov.start do
add_filter "/spec/" add_filter "/spec/"
@@ -10,15 +12,6 @@ require "fakefs/spec_helpers"
$:.unshift File.expand_path("../../lib", __FILE__) $:.unshift File.expand_path("../../lib", __FILE__)
begin
def running_ruby_18?
defined?(RUBY_VERSION) and RUBY_VERSION =~ /^1\.8\.\d+/
end
require 'posix/spawn' if running_ruby_18?
rescue LoadError
STDERR.puts "WARNING: foreman requires gem `posix-spawn` on Ruby #{RUBY_VERSION}. Please `gem install posix-spawn`."
end
def mock_export_error(message) def mock_export_error(message)
lambda { yield }.should raise_error(Foreman::Export::Exception, message) lambda { yield }.should raise_error(Foreman::Export::Exception, message)
end end
@@ -30,10 +23,6 @@ def mock_error(subject, message)
end end
end end
def make_pipe
IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
end
def foreman(args) def foreman(args)
capture_stdout do capture_stdout do
begin begin
@@ -44,18 +33,14 @@ def foreman(args)
end end
def forked_foreman(args) def forked_foreman(args)
rd, wr = make_pipe rd, wr = IO.pipe("BINARY")
if running_ruby_18? Process.spawn("bundle exec bin/foreman #{args}", :out => wr, :err => wr)
POSIX::Spawn.spawn({}, "bundle exec bin/foreman #{args}", :out => wr, :err => wr)
else
Process.spawn("bundle exec bin/foreman #{args}", :out => wr, :err => wr)
end
wr.close wr.close
rd.read rd.read
end end
def fork_and_capture(&blk) def fork_and_capture(&blk)
rd, wr = make_pipe rd, wr = IO.pipe("BINARY")
pid = fork do pid = fork do
rd.close rd.close
wr.sync = true wr.sync = true
@@ -69,20 +54,11 @@ def fork_and_capture(&blk)
Process.wait pid Process.wait pid
buffer = "" buffer = ""
until rd.eof? until rd.eof?
p [:foo]
buffer += rd.gets buffer += rd.gets
end end
end end
def fork_and_get_exitstatus(args)
pid = if running_ruby_18?
POSIX::Spawn.spawn({}, "bundle exec bin/foreman #{args}", :out => "/dev/null", :err => "/dev/null")
else
Process.spawn("bundle exec bin/foreman #{args}", :out => "/dev/null", :err => "/dev/null")
end
Process.wait(pid)
$?.exitstatus
end
def mock_exit(&block) def mock_exit(&block)
block.should raise_error(SystemExit) block.should raise_error(SystemExit)
end end
@@ -100,8 +76,6 @@ def write_procfile(procfile="Procfile", alpha_env="")
file.puts "alpha: ./alpha" + " #{alpha_env}".rstrip file.puts "alpha: ./alpha" + " #{alpha_env}".rstrip
file.puts "\n" file.puts "\n"
file.puts "bravo:\t./bravo" file.puts "bravo:\t./bravo"
file.puts "foo_bar:\t./foo_bar"
file.puts "foo-bar:\t./foo-bar"
end end
File.expand_path(procfile) File.expand_path(procfile)
end end
@@ -162,7 +136,7 @@ end
def capture_stdout def capture_stdout
old_stdout = $stdout.dup old_stdout = $stdout.dup
rd, wr = make_pipe rd, wr = IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
$stdout = wr $stdout = wr
yield yield
wr.close wr.close
@@ -177,5 +151,4 @@ RSpec.configure do |config|
config.order = 'rand' config.order = 'rand'
config.include FakeFS::SpecHelpers, :fakefs config.include FakeFS::SpecHelpers, :fakefs
config.mock_with :rr config.mock_with :rr
config.backtrace_exclusion_patterns = []
end end
+2 -1
View File
@@ -32,7 +32,7 @@ def latest_release
end end
def newer_release def newer_release
tags = %x{ git tag --contains v#{latest_release} | grep -v pre }.split("\n").sort_by do |tag| tags = %x{ git tag --contains v#{latest_release} }.split("\n").sort_by do |tag|
Gem::Version.new(tag[1..-1]) Gem::Version.new(tag[1..-1])
end end
tags[1] tags[1]
@@ -60,6 +60,7 @@ end
desc "Cut a release" desc "Cut a release"
task :release do task :release do
Rake::Task["authors"].invoke
Rake::Task["changelog"].invoke Rake::Task["changelog"].invoke
Rake::Task["pages"].invoke Rake::Task["pages"].invoke
end end