Compare commits

...

39 Commits

Author SHA1 Message Date
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
Austin
0fff148fe0 modified to use shellescape instead of gsub 2013-05-03 07:23:25 -07:00
Austin
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
39 changed files with 536 additions and 82 deletions

View File

@@ -2,7 +2,6 @@ script: bundle exec rake spec
matrix:
allow_failures:
- rvm: 1.8.7
- rvm: jruby
- rvm: rbx
- rvm: ree
@@ -16,6 +15,7 @@ notifications:
- http://dx-helper.herokuapp.com/travis
rvm:
- 1.8.7
- 1.9.2
- 1.9.3
- 2.0.0

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)
* Merge pull request #334 from ged/reentrant_signal_handlers [David Dollar]

View File

@@ -6,7 +6,7 @@ platform :mingw do
gem "win32console", "~> 1.3.0"
end
platform :jruby do
platform :jruby, :ruby_18 do
gem "posix-spawn", "~> 0.3.6"
end

View File

@@ -32,6 +32,7 @@ Manage Procfile-based applications
* [shoreman](https://github.com/hecticjeff/shoreman) - shell
* [honcho](https://github.com/nickstenning/honcho) - python
* [norman](https://github.com/josh/norman) - node.js
* [forego](https://github.com/ddollar/forego) - Go
## Authors

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]

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

View File

@@ -0,0 +1,2 @@
start on starting <%= app %>
stop on stopping <%= app %>

View File

@@ -0,0 +1 @@
[Unit]

View File

@@ -0,0 +1,18 @@
[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
[Install]
WantedBy=<%= app %>-<%= name %>.target

View File

@@ -0,0 +1,5 @@
[Unit]
StopWhenUnneeded=true
[Install]
WantedBy=<%= app %>.target

2
dist/tgz.rake vendored
View File

@@ -4,7 +4,7 @@ file pkg("foreman-#{version}.tgz") => distribution_files do |t|
assemble_distribution
assemble_gems
rm_f "bin/foreman"
assemble resource("tgz/foreman"), "foreman", 0755
assemble resource("tgz/foreman"), "bin/foreman", 0755
end
sh "tar czvf #{t.name} foreman"

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

View File

@@ -34,6 +34,7 @@ class Foreman::CLI < Thor
end
def start(process=nil)
require_posix_spawn_for_ruby_18!
check_procfile!
load_environment!
engine.load_procfile(procfile)
@@ -45,6 +46,7 @@ class Foreman::CLI < Thor
method_option :app, :type => :string, :aliases => "-a"
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 :port, :type => :numeric, :aliases => "-p"
method_option :user, :type => :string, :aliases => "-u"
@@ -137,6 +139,14 @@ private ######################################################################
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
case
when options[:procfile] then options[:procfile]

View File

@@ -302,6 +302,10 @@ private
def name_for(pid)
process, index = @running[pid]
name_for_index(process, index)
end
def name_for_index(process, index)
[ @names[process], index.to_s ].compact.join(".")
end
@@ -350,7 +354,8 @@ private
reader, writer = create_pipe
begin
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}"
rescue Errno::ENOENT
@@ -422,7 +427,7 @@ private
end
rescue Timeout::Error
system "sending SIGKILL to all processes"
killall "SIGKILL"
kill_children "SIGKILL"
end
end

View File

@@ -28,7 +28,9 @@ end
require "foreman/export/base"
require "foreman/export/inittab"
require "foreman/export/upstart"
require "foreman/export/daemon"
require "foreman/export/bluepill"
require "foreman/export/runit"
require "foreman/export/supervisord"
require "foreman/export/launchd"
require "foreman/export/systemd"

View File

@@ -47,7 +47,9 @@ class Foreman::Export::Base
error("Must specify a location") unless location
FileUtils.mkdir_p(location) rescue error("Could not create: #{location}")
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, run) rescue error("Could not chown #{run} to #{user}")
end
def app
@@ -58,6 +60,10 @@ class Foreman::Export::Base
options[:log] || "/var/log/#{app}"
end
def run
options[:run] || "/var/run/#{app}"
end
def user
options[:user] || app
end

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

View File

@@ -0,0 +1,25 @@
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
write_template "systemd/master.target.erb", "#{app}.target", binding
engine.each_process do |name, process|
next if engine.formation[name] < 1
write_template "systemd/process_master.target.erb", "#{app}-#{name}.target", binding
1.upto(engine.formation[name]) do |num|
port = engine.port_for(process, num)
write_template "systemd/process.service.erb", "#{app}-#{name}-#{num}.service", binding
end
end
end
end

View File

@@ -1,4 +1,5 @@
require "foreman"
require "shellwords"
class Foreman::Process
@@ -47,25 +48,18 @@ class Foreman::Process
def run(options={})
env = @options[:env].merge(options[:env] || {})
output = options[:output] || $stdout
runner = "#{Foreman.runner}".shellescape
if Foreman.windows?
Dir.chdir(cwd) do
Process.spawn env, expanded_command(env), :out => output, :err => output
end
elsif Foreman.jruby_18?
elsif Foreman.jruby_18? || Foreman.ruby_18?
require "posix/spawn"
wrapped_command = "#{Foreman.runner} -d '#{cwd}' -p -- #{command}"
POSIX::Spawn.spawn env, wrapped_command, :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
wrapped_command = "#{runner} -d '#{cwd.shellescape}' -p -- #{expanded_command(env)}"
POSIX::Spawn.spawn(*spawn_args(env, wrapped_command.shellsplit, {:out => output, :err => output}))
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
end
end
@@ -122,4 +116,14 @@ class Foreman::Process
File.expand_path(@options[:cwd] || ".")
end
private
def spawn_args(env, argv, options)
args = []
args << env
args += argv
args << options
args
end
end

View File

@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.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"
\fBforeman\fR \- manage Procfile\-based applications
@@ -107,9 +107,18 @@ bluepill
inittab
.
.IP "\(bu" 4
launchd
.
.IP "\(bu" 4
runit
.
.IP "\(bu" 4
supervisord
.
.IP "\(bu" 4
systemd
.
.IP "\(bu" 4
upstart
.
.IP "" 0
@@ -130,6 +139,18 @@ EX02:4:respawn:/bin/su \- example \-c \'PORT=5100 bundle exec rake jobs:work >>
.
.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"
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
.
.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
.

View File

@@ -101,8 +101,14 @@ foreman currently supports the following output formats:
* inittab
* launchd
* runit
* supervisord
* systemd
* upstart
## 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'
# ----- 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
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
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:
$ foreman check
@@ -168,7 +185,7 @@ Export the application in upstart format:
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

View File

@@ -44,6 +44,13 @@ describe "Foreman::CLI", :fakefs do
output.should =~ /test.1 \| testing/
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
@@ -93,4 +100,12 @@ describe "Foreman::CLI", :fakefs do
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

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

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

View File

@@ -41,7 +41,7 @@ describe Foreman::Process do
it "should output utf8 properly" do
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

View File

@@ -2,3 +2,4 @@ echo: bin/echo echoing
env: bin/env FOO
test: bin/test
utf8: bin/utf8
ps: bin/echo PS env var is $PS

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,2 @@
start on starting app
stop on stopping app

View File

@@ -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

View File

@@ -0,0 +1,2 @@
start on starting app
stop on stopping app

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]

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,5 @@
[Unit]
StopWhenUnneeded=true
[Install]
WantedBy=app.target

View File

@@ -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

View File

@@ -0,0 +1,5 @@
[Unit]
StopWhenUnneeded=true
[Install]
WantedBy=app.target

View File

@@ -0,0 +1 @@
[Unit]

View File

@@ -10,6 +10,15 @@ require "fakefs/spec_helpers"
$:.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)
lambda { yield }.should raise_error(Foreman::Export::Exception, message)
end
@@ -21,6 +30,10 @@ def mock_error(subject, message)
end
end
def make_pipe
IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
end
def foreman(args)
capture_stdout do
begin
@@ -31,14 +44,18 @@ def foreman(args)
end
def forked_foreman(args)
rd, wr = IO.pipe("BINARY")
Process.spawn("bundle exec bin/foreman #{args}", :out => wr, :err => wr)
rd, wr = make_pipe
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
rd.read
end
def fork_and_capture(&blk)
rd, wr = IO.pipe("BINARY")
rd, wr = make_pipe
pid = fork do
rd.close
wr.sync = true
@@ -57,7 +74,11 @@ def fork_and_capture(&blk)
end
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)
$?.exitstatus
end
@@ -141,7 +162,7 @@ end
def capture_stdout
old_stdout = $stdout.dup
rd, wr = IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
rd, wr = make_pipe
$stdout = wr
yield
wr.close
@@ -156,4 +177,5 @@ RSpec.configure do |config|
config.order = 'rand'
config.include FakeFS::SpecHelpers, :fakefs
config.mock_with :rr
config.backtrace_clean_patterns = []
end