Compare commits

...
53 Commits
Author SHA1 Message Date
dlage 199e7de97c update gems. 2014-03-17 20:12:11 +00:00
dlage defc40b911 Merge pull request #5 from phemmer/systemd-dependencies
export: fix systemd dependencies
2014-03-17 19:39:14 +00:00
dlage 37dcba0e33 Merge pull request #3 from phemmer/shellfix
Shellfix
2014-03-17 19:37:20 +00:00
dlage d205e3ba2e Merge pull request #4 from phemmer/run-nofork
don't fork on 'run'
2014-03-17 19:37:05 +00:00
dlage 9905334f01 Merge pull request #2 from ged/better-357-fix
A better fix for #357.
2014-03-17 19:29:45 +00:00
dlage dd292ed7da Merge pull request #1 from petergoldstein/feature/add_2_1_0
Feature/add 2 1 0
2014-03-17 19:25:46 +00:00
Patrick Hemmer 67ffbe2aa2 export: fix systemd dependencies
With the previous way systemd services were exported, you had to enable every single process one by one to get the service operational.

Consider the following Procfile:

    web: bundle exec thin start
    worker: bundle exec worker start

Exported with:

    foreman export systemd /usr/lib/systemd/system -a myapp -c worker=3

You will have to perform the following to start the service:

    systemctl enable myapp-web-1.service
    systemctl enable myapp-worker-1.service
    systemctl enable myapp-worker-2.service
    systemctl enable myapp-worker-3.service
    systemctl enable myapp-web.target
    systemctl enable myapp-worker.target
    systemctl start myapp.target

Thats 7 commands, and you have to know the names of each service. Nasty.

With the changes here, you will have to perform the following:

    systemctl enable myapp.target
    systemctl start myapp.target

You can also `systemctl start myapp.target` without enabling as well. Previously if you tried to start `myapp.target` without enabling each individual process it would not work.

---

Additionally, this change also adds the dependency to 'multi-user.target' (the default behavior for all services). This will properly shut down (or start) the app when the system changes runlevels. The previous method did not do this.
2014-02-17 00:40:39 -05:00
Peter M. Goldstein da8bba5941 Use a 1.9.2 compatible version of rdiscount 2014-01-13 17:38:11 -08:00
Peter M. Goldstein 2aa1cbf94f Bump up gem versions in Gemfile.lock. Minor changes to eliminate deprecation warnings. 2014-01-13 17:30:58 -08:00
Peter M. Goldstein 34a1163fe9 Add Ruby 2.1.0, clean up references in allow_failures to now un-tested rvms. 2014-01-13 15:28:47 -08:00
Patrick Hemmer 2e5f610b76 don't fork on 'run'
This removes the fork when doing `foreman run`. The fork results in an unnecessary process laying around. The parent process doesn't do anything other than fork and wait for the child to exit.
2013-11-26 00:09:19 -05:00
Michael Granger 20392d98a1 A better fix for #357.
This is a better fix for several reasons:

- It's more portable; the arguments/options to 'ps' vary widely across
  platforms.
- Killing children by forking yet more children (i.e., via backtick) is
  problematic when you're trying to kill processes because of resource
  starvation.
- Processes should not signal processes other than their own children,
  especially in a generic task-runner like Foreman. If the signal
  should propagate, then the sub-process should propagate signals to its
  children itself. There's no way to know what cleanup or preparation
  is necessary for a clean shutdown (e.g., SIGINT/SIGTERM), and
  in what order grandchild processes should be shut down to properly
  release locks, close files, etc.
- foreman-runner doesn't (shouldn't?) create grandchild processes; the
  last thing it does is `exec` the program it's launching, which
  will replace the program of the *current* process with the one
  being started up.
2013-11-05 11:56:13 -08:00
Patrick Hemmer e8538d9f45 exec on upstart to prevent useless procs floating around 2013-10-02 20:41:51 -04:00
Patrick Hemmer 4a51a2aa8a specify a shell when using upstart 2013-10-02 20:08:47 -04:00
David Dollar 1905a7c310 Merge pull request #388 from brixen/patch-1
Update README.md
2013-09-04 16:49:36 -07:00
Brian Shirai 134b31f599 Update README.md 2013-09-04 16:47:09 -07:00
David Dollar 2b97cb458a Merge pull request #366 from wfarr/tgz-install-foreman-to-bin
Install tgz/foreman to bin/foreman in tgz package
2013-05-27 14:06:42 -07:00
Will Farrington 03d76d4254 Install tgz/foreman to bin/foreman in tgz package 2013-05-26 07:49:18 -07:00
David Dollar 133228f247 Merge pull request #305 from marclennox/master
Add start-stop-daemon export support with upstart
2013-05-03 13:27:54 -07:00
David Dollar bfba2cad71 Merge pull request #360 from kjwierenga/feature/require-posix-spawn
Feature/require posix spawn
2013-05-03 12:55:16 -07:00
Klaas Jan Wierenga e245026f65 Fail with an error on Ruby 1.8 when posix-spawn is not present. 2013-05-03 21:44:57 +02:00
Klaas Jan Wierenga ffc73366b2 Require and use 'posix/spawn' when running ruby 1.8 without using Foreman.ruby_18? (which is the subject under test). 2013-05-03 21:29:17 +02:00
Marc Lennox 284503899a Added start-stop-daemon support. 2013-05-03 12:54:08 -04:00
David Dollar 144be02bbd Merge pull request #353 from austiniam/master
added support for directories with single quote
2013-05-03 07:29:17 -07:00
Austin Cherry 9734a2ed65 fixed conflicts 2013-05-03 07:25:56 -07:00
AustinandAustin Cherry 0fff148fe0 modified to use shellescape instead of gsub 2013-05-03 07:23:25 -07:00
AustinandAustin Cherry d19a9aa043 added support for directories with single quotes. fixes #315 2013-05-03 07:22:16 -07:00
David Dollar c3abaad353 kill the children, not self 2013-05-03 09:54:20 -04:00
David Dollar b1f91d4505 fix docs, thanks to @Invizory 2013-05-03 09:49:31 -04:00
David Dollar 0c7b8ddd79 not sure how this snuck in, not in the exporter format 2013-05-03 09:44:25 -04:00
David Dollar 189b751d9f update docs 2013-05-03 09:44:25 -04:00
David Dollar 619bd03bb8 Merge pull request #340 from ldmosquera/set_env_var_with_process_name
Set FOREMAN_PROCESS_NAME env var for spawned procs
2013-05-03 06:42:04 -07:00
David Dollar fd836b46c0 Merge pull request #355 from JoeyButler/fix_typo_in_man
Fix typo in manual page.
2013-05-03 06:38:42 -07:00
David Dollar 2379259b33 Merge pull request #359 from kjwierenga/feature/make-ruby-18-compatible
Fix specs that were broken on ruby 1.8. Use POSIX::Spawn.spawn to make foreman robust on ruby 1.8.7.
2013-05-03 06:32:01 -07:00
Klaas Jan Wierenga baf842cdd4 The wrapped_command has spaces which triggers Ruby to fork a system shell (with /bin/sh -c). This causes orphaned processes on some systems (i.e. Linux). Fix this by splitting the command using String#shellsplit and using ruby's splat operator (*) to pass discrete arguments to spawn. 2013-05-02 16:30:39 +02:00
Klaas Jan Wierenga 5c06aaaa57 Enable ruby 1.8.7 building on Travis. All specs should pass now on ruby 1.8.7. 2013-05-02 12:49:05 +02:00
Klaas Jan Wierenga 75b782b664 Use POSIX::Spawn to make foreman ruby 1.8 compatible and have all specs passing. 2013-05-02 12:39:23 +02:00
Klaas Jan Wierenga 9b4bd10cdb Use pipe factory method to handle 1.8 incompatible signature for IO.pipe. 2013-05-02 12:37:06 +02:00
Klaas Jan Wierenga fff82dc685 Ruby 1.8 doesn't have the concept of string encodings. Compare to the string as is. 2013-05-02 11:00:47 +02:00
David Dollar da0a9f2de3 Merge pull request #294 from bfulton/master
add systemd export
2013-04-22 20:37:39 -07:00
Bright Fulton 7d77d8ff1a updated systemd export spec after rebasing included 5ef8bbdb 2013-04-20 13:48:27 -04:00
Joey Butler 51e181da29 Fix typo in manual page. 2013-04-17 10:47:03 -07:00
Leonardo Mosquera 9866a341ca Add unit test for PS env var 2013-04-16 22:31:34 -03:00
Leonardo Mosquera 90848e7dea Change FOREMAN_PROCESS_NAME to just PS 2013-04-16 22:31:15 -03:00
David Dollar a92e24b17c changelog 2013-04-15 15:35:21 -04:00
Bright Fulton 669a920c1e fix spec after d4ab495 2013-04-15 14:43:02 -04:00
Bright Fulton d357197718 better default for things which intentionally daemonize child processes, the default KillMode is control-group which survives daemonization 2013-04-15 14:43:02 -04:00
bfulton f6b57d7b92 rough draft for systemd export support
http://0pointer.de/blog/projects/systemd.html

This adds support for exporting systemd targets and services.  The
structure is based on the existing upstart support.

Quality is draft and expected to refine in the coming weeks.

One Foremanism that is not respected by these export templates is the
usual log output location, instead stdout and stderr go to syslog.
2013-04-15 14:43:02 -04:00
Austin e79588fd40 modified to use shellescape instead of gsub 2013-04-15 08:01:24 -07:00
Austin 6611d818b1 third time is the charm. :) 2013-04-11 09:14:01 -07:00
Austin ad4d59ae14 fixes #315 2013-04-11 08:49:36 -07:00
Austin 434f30fe42 added support for directories with single quotes. fixes #315 2013-04-11 08:44:16 -07:00
Leonardo Mosquera 95a1d49e9d Set FOREMAN_PROCESS_NAME env var for spawned procs
This way, processes can identify themselves to metrics logging systems.
2013-03-14 21:34:39 -03:00
42 changed files with 600 additions and 150 deletions
+2 -3
View File
@@ -2,10 +2,7 @@ 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,7 +13,9 @@ notifications:
- http://dx-helper.herokuapp.com/travis - http://dx-helper.herokuapp.com/travis
rvm: rvm:
- 1.8.7
- 1.9.2 - 1.9.2
- 1.9.3 - 1.9.3
- 2.0.0 - 2.0.0
- 2.1.0
- jruby - jruby
+14
View File
@@ -1,3 +1,17 @@
## 0.63.0 (2013-04-15)
* Revert "Ensure foreman is the process group leader" [John Griffin]
* remove posix-spawn dependency as it does not work in jruby 1.7.3 [Andrew Brown & Corey Downing]
* Replace Foreman::Env with dotenv [Brandon Keepers]
* [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) ## 0.62.0 (2013-03-08)
* Merge pull request #334 from ged/reentrant_signal_handlers [David Dollar] * Merge pull request #334 from ged/reentrant_signal_handlers [David Dollar]
+4 -2
View File
@@ -6,7 +6,7 @@ platform :mingw do
gem "win32console", "~> 1.3.0" gem "win32console", "~> 1.3.0"
end end
platform :jruby do platform :jruby, :ruby_18 do
gem "posix-spawn", "~> 0.3.6" gem "posix-spawn", "~> 0.3.6"
end end
@@ -18,6 +18,8 @@ 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' gem 'timecop', '0.6.1'
gem 'yard' gem 'yard'
gem 'mime-types', '~> 1.25.1'
gem 'rdiscount', '~> 1.6.8'
end end
+33 -29
View File
@@ -8,44 +8,46 @@ PATH
GEM GEM
remote: http://rubygems.org/ remote: http://rubygems.org/
specs: specs:
aws-s3 (0.6.2) aws-s3 (0.6.3)
builder builder
mime-types mime-types
xml-simple xml-simple
builder (3.0.0) builder (3.2.2)
diff-lcs (1.1.3) diff-lcs (1.2.5)
dotenv (0.7.0) 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.16) mime-types (1.25.1)
multi_json (1.0.4) multi_json (1.9.0)
mustache (0.11.2) mustache (0.99.5)
posix-spawn (0.3.6) posix-spawn (0.3.8)
rake (0.9.2.2) rake (10.1.1)
rdiscount (1.6.5) rdiscount (1.6.8)
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.2) rr (1.0.5)
rspec (2.8.0) rspec (2.14.1)
rspec-core (~> 2.8.0) rspec-core (~> 2.14.0)
rspec-expectations (~> 2.8.0) rspec-expectations (~> 2.14.0)
rspec-mocks (~> 2.8.0) rspec-mocks (~> 2.14.0)
rspec-core (2.8.0) rspec-core (2.14.8)
rspec-expectations (2.8.0) rspec-expectations (2.14.5)
diff-lcs (~> 1.1.2) diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.8.0) rspec-mocks (2.14.6)
simplecov (0.5.4) simplecov (0.8.2)
multi_json (~> 1.0.3) docile (~> 1.1.0)
simplecov-html (~> 0.5.3) multi_json
simplecov-html (0.5.3) simplecov-html (~> 0.8.0)
thor (0.16.0) simplecov-html (0.8.0)
timecop (0.3.5) thor (0.18.1)
win32console (1.3.0-x86-mingw32) timecop (0.6.1)
xml-simple (1.0.15) win32console (1.3.2-x86-mingw32)
yard (0.8.2) xml-simple (1.1.3)
yard (0.8.7.3)
PLATFORMS PLATFORMS
java java
@@ -56,12 +58,14 @@ 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 timecop (= 0.6.1)
win32console (~> 1.3.0) win32console (~> 1.3.0)
yard yard
+1
View File
@@ -32,6 +32,7 @@ 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
+14
View File
@@ -0,0 +1,14 @@
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
@@ -0,0 +1,8 @@
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
@@ -0,0 +1,2 @@
start on starting <%= app %>
stop on stopping <%= app %>
+6
View File
@@ -0,0 +1,6 @@
[Unit]
StopWhenUnneeded=true
Wants=<%= process_master_names.join(' ') %>
[Install]
WantedBy=multi-user.target
+15
View File
@@ -0,0 +1,15 @@
[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
@@ -0,0 +1,3 @@
[Unit]
StopWhenUnneeded=true
Wants=<%= process_names.join(' ') %>
+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 %> -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' 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'
+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"), "foreman", 0755 assemble resource("tgz/foreman"), "bin/foreman", 0755
end end
sh "tar czvf #{t.name} foreman" sh "tar czvf #{t.name} foreman"
-54
View File
@@ -1,54 +0,0 @@
if defined?(Capistrano)
Capistrano::Configuration.instance(:must_exist).load do
namespace :foreman do
desc <<-DESC
Export the Procfile to upstart. Will use sudo if available.
You can override any of these defaults by setting the variables shown below.
set :foreman_format, "upstart"
set :foreman_location, "/etc/init"
set :foreman_procfile, "Procfile"
set :foreman_app, application
set :foreman_user, user
set :foreman_log, 'shared_path/log'
set :foreman_concurrency, false
DESC
task :export, :roles => :app do
bundle_cmd = fetch(:bundle_cmd, "bundle")
foreman_format = fetch(:foreman_format, "upstart")
foreman_location = fetch(:foreman_location, "/etc/init")
foreman_procfile = fetch(:foreman_procfile, "Procfile")
foreman_app = fetch(:foreman_app, application)
foreman_user = fetch(:foreman_user, user)
foreman_log = fetch(:foreman_log, "#{shared_path}/log")
foreman_concurrency = fetch(:foreman_concurrency, false)
args = ["#{foreman_format} #{foreman_location}"]
args << "-f #{foreman_procfile}"
args << "-a #{foreman_app}"
args << "-u #{foreman_user}"
args << "-l #{foreman_log}"
args << "-c #{foreman_concurrency}" if foreman_concurrency
run "cd #{release_path} && #{sudo} #{bundle_cmd} exec foreman export #{args.join(' ')}"
end
desc "Start the application services"
task :start, :roles => :app do
run "#{sudo} start #{application}"
end
desc "Stop the application services"
task :stop, :roles => :app do
run "#{sudo} stop #{application}"
end
desc "Restart the application services"
task :restart, :roles => :app do
run "#{sudo} start #{application} || #{sudo} restart #{application}"
end
end
end
end
+20 -14
View File
@@ -34,6 +34,7 @@ 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)
@@ -45,6 +46,7 @@ class Foreman::CLI < Thor
method_option :app, :type => :string, :aliases => "-a" method_option :app, :type => :string, :aliases => "-a"
method_option :log, :type => :string, :aliases => "-l" method_option :log, :type => :string, :aliases => "-l"
method_option :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"
@@ -81,22 +83,18 @@ class Foreman::CLI < Thor
engine.load_procfile(procfile) engine.load_procfile(procfile)
end end
pid = fork do begin
begin engine.env.each { |k,v| ENV[k] = v }
engine.env.each { |k,v| ENV[k] = v } if args.size == 1 && process = engine.process(args.first)
if args.size == 1 && process = engine.process(args.first) process.exec(:env => engine.env)
process.exec(:env => engine.env) else
else exec args.shelljoin
exec args.shelljoin
end
rescue Errno::EACCES
error "not executable: #{args.first}"
rescue Errno::ENOENT
error "command not found: #{args.first}"
end end
rescue Errno::EACCES
error "not executable: #{args.first}"
rescue Errno::ENOENT
error "command not found: #{args.first}"
end end
Process.wait(pid)
exit $?.exitstatus
end end
desc "version", "Display Foreman gem version" desc "version", "Display Foreman gem version"
@@ -137,6 +135,14 @@ 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]
+15 -22
View File
@@ -176,19 +176,14 @@ class Foreman::Engine
# #
# @param [String] signal The signal to send to each process # @param [String] signal The signal to send to each process
# #
def kill_children(signal="SIGTERM") def kill_children( signal="SIGTERM" )
if Foreman.windows? @running.each do |pid, (process, index)|
@running.each do |pid, (process, index)| system "sending #{signal} to #{name_for(pid)} at pid #{pid}"
system "sending #{signal} to #{name_for(pid)} at pid #{pid}"
begin
Process.kill(signal, pid)
rescue Errno::ESRCH, Errno::EPERM
end
end
else
begin begin
Process.kill signal, *@running.keys unless @running.empty? Process.kill( signal, pid )
rescue Errno::ESRCH, Errno::EPERM 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 end
@@ -198,14 +193,7 @@ class Foreman::Engine
# @param [String] signal The signal to send # @param [String] signal The signal to send
# #
def killall(signal="SIGTERM") def killall(signal="SIGTERM")
if Foreman.windows? kill_children(signal)
kill_children(signal)
else
begin
Process.kill "-#{signal}", Process.pid
rescue Errno::ESRCH, Errno::EPERM
end
end
end end
# Get the process formation # Get the process formation
@@ -302,6 +290,10 @@ private
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
@@ -350,7 +342,8 @@ private
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
@@ -422,7 +415,7 @@ private
end end
rescue Timeout::Error rescue Timeout::Error
system "sending SIGKILL to all processes" system "sending SIGKILL to all processes"
killall "SIGKILL" kill_children "SIGKILL"
end end
end end
+2
View File
@@ -28,7 +28,9 @@ 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"
+6
View File
@@ -47,7 +47,9 @@ class Foreman::Export::Base
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
@@ -58,6 +60,10 @@ 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
+28
View File
@@ -0,0 +1,28 @@
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
+32
View File
@@ -0,0 +1,32 @@
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
+17 -13
View File
@@ -1,4 +1,5 @@
require "foreman" require "foreman"
require "shellwords"
class Foreman::Process class Foreman::Process
@@ -47,25 +48,18 @@ class Foreman::Process
def run(options={}) def run(options={})
env = @options[:env].merge(options[:env] || {}) env = @options[:env].merge(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, expanded_command(env), :out => output, :err => output
end end
elsif Foreman.jruby_18? elsif Foreman.jruby_18? || Foreman.ruby_18?
require "posix/spawn" require "posix/spawn"
wrapped_command = "#{Foreman.runner} -d '#{cwd}' -p -- #{command}" wrapped_command = "#{runner} -d '#{cwd.shellescape}' -p -- #{expanded_command(env)}"
POSIX::Spawn.spawn env, wrapped_command, :out => output, :err => output POSIX::Spawn.spawn(*spawn_args(env, wrapped_command.shellsplit, {:out => output, :err => output}))
elsif Foreman.ruby_18?
fork do
$stdout.reopen output
$stderr.reopen output
env.each { |k,v| ENV[k] = v }
wrapped_command = "#{Foreman.runner} -d '#{cwd}' -p -- #{command}"
Kernel.exec wrapped_command
end
else else
wrapped_command = "#{Foreman.runner} -d '#{cwd}' -p -- #{command}" wrapped_command = "#{runner} -d '#{cwd.shellescape}' -p -- #{command}"
Process.spawn env, wrapped_command, :out => output, :err => output Process.spawn env, wrapped_command, :out => output, :err => output
end end
end end
@@ -122,4 +116,14 @@ class Foreman::Process
File.expand_path(@options[:cwd] || ".") File.expand_path(@options[:cwd] || ".")
end end
private
def spawn_args(env, argv, options)
args = []
args << env
args += argv
args << options
args
end
end end
+23 -2
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" "January 2013" "Foreman 0.61.0" "Foreman Manual" .TH "FOREMAN" "1" "May 2013" "Foreman 0.63.0" "Foreman Manual"
. .
.SH "NAME" .SH "NAME"
\fBforeman\fR \- manage Procfile\-based applications \fBforeman\fR \- manage Procfile\-based applications
@@ -107,9 +107,18 @@ 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
@@ -130,6 +139,18 @@ 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:
. .
@@ -157,7 +178,7 @@ job: bundle exec rake jobs:work
.IP "" 0 .IP "" 0
. .
.P .P
A process name may contain letters, numbers amd the underscore character\. You can validate your Procfile format using the \fBcheck\fR command: A process name may contain letters, numbers and the underscore character\. You can validate your Procfile format using the \fBcheck\fR command:
. .
.IP "" 4 .IP "" 4
. .
+19 -2
View File
@@ -101,8 +101,14 @@ foreman currently supports the following output formats:
* inittab * inittab
* launchd
* runit * runit
* supervisord
* systemd
* upstart * upstart
## INITTAB EXPORT ## INITTAB EXPORT
@@ -114,6 +120,17 @@ 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
@@ -133,7 +150,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 amd the underscore character. A process name may contain letters, numbers and 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
@@ -168,7 +185,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 -p ~/myapp/Procfile $ foreman start alpha -f ~/myapp/Procfile
## COPYRIGHT ## COPYRIGHT
+15
View File
@@ -44,6 +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
@@ -93,4 +100,12 @@ describe "Foreman::CLI", :fakefs do
end end
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
+97
View File
@@ -0,0 +1,97 @@
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
+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(Errno::ENOENT) expect { runit.export }.to_not raise_error
end end
end end
+91
View File
@@ -0,0 +1,91 @@
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 -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 == "\xFF\x03\n".force_encoding('binary') run(process).should == (Foreman.ruby_18? ? "\xFF\x03\n" : "\xFF\x03\n".force_encoding('binary'))
end end
end end
+1
View File
@@ -2,3 +2,4 @@ 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
@@ -0,0 +1,7 @@
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
@@ -0,0 +1,7 @@
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
@@ -0,0 +1,2 @@
start on starting app
stop on stopping app
@@ -0,0 +1,7 @@
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
@@ -0,0 +1,2 @@
start on starting app
stop on stopping app
+14
View File
@@ -0,0 +1,14 @@
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]
@@ -0,0 +1,17 @@
[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
@@ -0,0 +1,17 @@
[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
@@ -0,0 +1,5 @@
[Unit]
StopWhenUnneeded=true
[Install]
WantedBy=app.target
@@ -0,0 +1,17 @@
[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
@@ -0,0 +1,5 @@
[Unit]
StopWhenUnneeded=true
[Install]
WantedBy=app.target
+1
View File
@@ -0,0 +1 @@
[Unit]
+27 -5
View File
@@ -10,6 +10,15 @@ 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
@@ -21,6 +30,10 @@ 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
@@ -31,14 +44,18 @@ def foreman(args)
end end
def forked_foreman(args) def forked_foreman(args)
rd, wr = IO.pipe("BINARY") rd, wr = make_pipe
Process.spawn("bundle exec bin/foreman #{args}", :out => wr, :err => wr) if running_ruby_18?
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 = IO.pipe("BINARY") rd, wr = make_pipe
pid = fork do pid = fork do
rd.close rd.close
wr.sync = true wr.sync = true
@@ -57,7 +74,11 @@ def fork_and_capture(&blk)
end end
def fork_and_get_exitstatus(args) def fork_and_get_exitstatus(args)
pid = Process.spawn("bundle exec bin/foreman #{args}", :out => "/dev/null", :err => "/dev/null") 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) Process.wait(pid)
$?.exitstatus $?.exitstatus
end end
@@ -141,7 +162,7 @@ end
def capture_stdout def capture_stdout
old_stdout = $stdout.dup old_stdout = $stdout.dup
rd, wr = IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY") rd, wr = make_pipe
$stdout = wr $stdout = wr
yield yield
wr.close wr.close
@@ -156,4 +177,5 @@ 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