Compare commits

...
14 Commits
Author SHA1 Message Date
David Dollar 58fc18d015 fix up readme 2011-05-13 12:08:41 -04:00
David Dollar f7c9802ef7 0.16.0 2011-05-13 12:05:58 -04:00
David Dollar 1c00d65f29 env is only available in foreground mode for now 2011-05-13 12:05:47 -04:00
David Dollar 9193a675a3 add support for process environments 2011-05-13 12:02:13 -04:00
David Dollar 8f0b14810c Merge pull request #20 from dpiddy/master
Change requires so export/upstart_spec can run on its own
2011-05-13 09:02:01 -07:00
Dan Peterson 124e27ed22 Change requires so export/upstart_spec can be run on its own. 2011-05-13 11:44:53 -03:00
David Dollar f34f161899 0.15.0 2011-05-12 13:05:43 -04:00
David Dollar 191581fe85 kill with TERM, even when INT is received by foreman 2011-05-12 13:05:25 -04:00
David Dollar b98d558bed add blog post to readme 2011-05-12 11:29:56 -04:00
David Dollar 180f63624e 0.14.0 2011-05-11 21:42:12 -04:00
David Dollar 13fd1188ad add export specs 2011-05-11 21:41:56 -04:00
David Dollar 92c2b15785 Merge pull request #18 from clifff/master
Upstart process su'ing into wrong dir
2011-05-11 18:04:18 -07:00
clifff 4a2d7565b7 Cleaner version of ensuring upstart process su goes to the correct dir 2011-05-11 18:51:31 -04:00
clifff 49720e0458 Moved upstart process 'chdir' into after su into user, since that will change dir to user home 2011-05-11 18:23:38 -04:00
22 changed files with 177 additions and 23 deletions
+1
View File
@@ -0,0 +1 @@
FOO=bar
+1 -1
View File
@@ -1,7 +1,7 @@
PATH PATH
remote: . remote: .
specs: specs:
foreman (0.13.1) foreman (0.16.0)
term-ansicolor (~> 1.0.5) term-ansicolor (~> 1.0.5)
thor (>= 0.13.6) thor (>= 0.13.6)
+20
View File
@@ -1,9 +1,29 @@
# Foreman # Foreman
## Installation
gem install foreman
## Description
http://blog.daviddollar.org/2011/05/06/introducing-foreman.html
## Manual ## Manual
See the [man page](http://ddollar.github.com/foreman) for usage. See the [man page](http://ddollar.github.com/foreman) for usage.
## Author
David Dollar
## Contributors
Adam Wiggins
clifff
Dan Peterson
Keith Rarick
Ricardo Chimal, Jr
## License ## License
MIT MIT
+1 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
while true while true
puts "tick: #{ARGV.inspect}" puts "tick: #{ARGV.inspect} -- FOO:#{ENV["FOO"]}"
sleep 1 sleep 1
end end
+1 -1
View File
@@ -3,4 +3,4 @@ stop on stopping <%= app %>-<%= process.name %>
respawn respawn
chdir <%= engine.directory %> chdir <%= engine.directory %>
exec su - <%= user %> -c 'export PORT=<%= port %>; <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1' exec su <%= user %> -c 'export PORT=<%= port %>; <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1'
+2 -2
View File
@@ -10,9 +10,9 @@ class Foreman::CLI < Thor
desc "start [PROCESS]", "Start the application, or a specific process" desc "start [PROCESS]", "Start the application, or a specific process"
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
method_option :port, :type => :numeric, :aliases => "-p" method_option :port, :type => :numeric, :aliases => "-p"
method_option :concurrency, :type => :string, :aliases => "-c", method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
:banner => '"alpha=5,bar=3"'
def start(process=nil) def start(process=nil)
check_procfile! check_procfile!
+34 -7
View File
@@ -50,23 +50,27 @@ class Foreman::Engine
end end
def start(options={}) def start(options={})
environment = read_environment(options[:env])
proctitle "ruby: foreman master" proctitle "ruby: foreman master"
processes_in_order.each do |name, process| processes_in_order.each do |name, process|
fork process, options fork process, options, environment
end end
trap("TERM") { puts "SIGTERM received"; kill_all("TERM") } trap("TERM") { puts "SIGTERM received"; kill_all("TERM") }
trap("INT") { puts "SIGINT received"; kill_all("INT") } trap("INT") { puts "SIGINT received"; kill_all("TERM") }
watch_for_termination watch_for_termination
end end
def execute(name, options={}) def execute(name, options={})
fork processes[name], options environment = read_environment(options[:env])
fork processes[name], options, environment
trap("TERM") { puts "SIGTERM received"; kill_all("TERM") } trap("TERM") { puts "SIGTERM received"; kill_all("TERM") }
trap("INT") { puts "SIGINT received"; kill_all("INT") } trap("INT") { puts "SIGINT received"; kill_all("TERM") }
watch_for_termination watch_for_termination
end end
@@ -79,15 +83,17 @@ class Foreman::Engine
private ###################################################################### private ######################################################################
def fork(process, options={}) def fork(process, options={}, environment={})
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency]) concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
1.upto(concurrency[process.name]) do |num| 1.upto(concurrency[process.name]) do |num|
fork_individual(process, num, port_for(process, num, options[:port])) fork_individual(process, num, port_for(process, num, options[:port]), environment)
end end
end end
def fork_individual(process, num, port) def fork_individual(process, num, port, environment)
environment.each { |k,v| ENV[k] = v }
ENV["PORT"] = port.to_s ENV["PORT"] = port.to_s
ENV["PS"] = "#{process.name}.#{num}" ENV["PS"] = "#{process.name}.#{num}"
@@ -136,6 +142,11 @@ private ######################################################################
puts puts
end end
def error(message)
puts "ERROR: #{message}"
exit 1
end
def longest_process_name def longest_process_name
@longest_process_name ||= begin @longest_process_name ||= begin
longest = processes.keys.map { |name| name.length }.sort.last longest = processes.keys.map { |name| name.length }.sort.last
@@ -190,4 +201,20 @@ private ######################################################################
puts "!!! e.g. web: thin start" puts "!!! e.g. web: thin start"
end end
def read_environment(filename)
error "No such file: #{filename}" if filename && !File.exists?(filename)
filename ||= ".env"
environment = {}
if File.exists?(filename)
File.read(filename).split("\n").each do |line|
if line =~ /\A([A-Za-z_]+)=(.*)\z/
environment[$1] = $2
end
end
end
environment
end
end end
+1
View File
@@ -4,5 +4,6 @@ module Foreman::Export
class Exception < ::Exception; end class Exception < ::Exception; end
end end
require "foreman/export/base"
require "foreman/export/inittab" require "foreman/export/inittab"
require "foreman/export/upstart" require "foreman/export/upstart"
+1 -1
View File
@@ -1,4 +1,4 @@
require "foreman/export/base" require "foreman/export"
class Foreman::Export::Inittab < Foreman::Export::Base class Foreman::Export::Inittab < Foreman::Export::Base
+1 -1
View File
@@ -1,5 +1,5 @@
require "erb" require "erb"
require "foreman/export/base" require "foreman/export"
class Foreman::Export::Upstart < Foreman::Export::Base class Foreman::Export::Upstart < Foreman::Export::Base
+1 -1
View File
@@ -1,5 +1,5 @@
module Foreman module Foreman
VERSION = "0.13.1" VERSION = "0.16.0"
end end
+5 -1
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 2011" "Foreman 0.13.0" "Foreman Manual" .TH "FOREMAN" "1" "May 2011" "Foreman 0.15.0" "Foreman Manual"
. .
.SH "NAME" .SH "NAME"
\fBforeman\fR \- manage Procfile\-based applications \fBforeman\fR \- manage Procfile\-based applications
@@ -68,6 +68,10 @@ 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\-e\fR, \fB\-\-env\fR
Specify a file containing the environment that should be set up for each child process\. The file should be key/value pairs separated by \fB=\fR, with one key/value pair per line\.
.
.TP
\fB\-f\fR, \fB\-\-procfile\fR \fB\-f\fR, \fB\-\-procfile\fR
Specify an alternate location for the application\'s Procfile\. This file\'s containing directory will be assumed to be the root directory of the application\. Specify an alternate location for the application\'s Procfile\. This file\'s containing directory will be assumed to be the root directory of the application\.
. .
+5
View File
@@ -66,6 +66,11 @@ 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.
* `-e`, `--env`:
Specify a file containing the environment that should be set up for each
child process. The file should be key/value pairs separated by `=`, with
one key/value pair per line.
* `-f`, `--procfile`: * `-f`, `--procfile`:
Specify an alternate location for the application's Procfile. This file's Specify an alternate location for the application's Procfile. This file's
containing directory will be assumed to be the root directory of the containing directory will be assumed to be the root directory of the
+31 -6
View File
@@ -37,17 +37,17 @@ describe "Foreman::Engine" do
describe "start" do describe "start" do
it "forks the processes" do it "forks the processes" do
write_procfile write_procfile
mock(subject).fork(subject.processes["alpha"], {}) mock(subject).fork(subject.processes["alpha"], {}, {})
mock(subject).fork(subject.processes["bravo"], {}) mock(subject).fork(subject.processes["bravo"], {}, {})
mock(subject).watch_for_termination mock(subject).watch_for_termination
subject.start subject.start
end end
it "handles concurrency" do it "handles concurrency" do
write_procfile write_procfile
mock(subject).fork_individual(subject.processes["alpha"], 1, 5000) mock(subject).fork_individual(subject.processes["alpha"], 1, 5000, {})
mock(subject).fork_individual(subject.processes["alpha"], 2, 5001) mock(subject).fork_individual(subject.processes["alpha"], 2, 5001, {})
mock(subject).fork_individual(subject.processes["bravo"], 1, 5100) mock(subject).fork_individual(subject.processes["bravo"], 1, 5100, {})
mock(subject).watch_for_termination mock(subject).watch_for_termination
subject.start(:concurrency => "alpha=2") subject.start(:concurrency => "alpha=2")
end end
@@ -56,9 +56,34 @@ describe "Foreman::Engine" do
describe "execute" do describe "execute" do
it "runs the processes" do it "runs the processes" do
write_procfile write_procfile
mock(subject).fork(subject.processes["alpha"], {}) mock(subject).fork(subject.processes["alpha"], {}, {})
mock(subject).watch_for_termination mock(subject).watch_for_termination
subject.execute("alpha") subject.execute("alpha")
end end
end end
describe "environment" do
before(:each) do
write_procfile
stub(Process).fork
stub(subject).info
mock(subject).watch_for_termination
end
it "should read if specified" do
File.open("/tmp/env", "w") { |f| f.puts("FOO=baz") }
subject.execute("alpha", :env => "/tmp/env")
end
it "should fail if specified and doesnt exist" do
mock(subject).error("No such file: /tmp/env")
subject.execute("alpha", :env => "/tmp/env")
end
it "should read .env if none specified" do
File.open(".env", "w") { |f| f.puts("FOO=qoo") }
mock(subject).fork_individual(anything, anything, anything, { "FOO" => "qoo" })
subject.execute("bravo")
end
end
end end
+20
View File
@@ -1,2 +1,22 @@
require "spec_helper" require "spec_helper"
require "foreman/engine"
require "foreman/export/upstart" require "foreman/export/upstart"
describe Foreman::Export::Upstart do
let(:engine) { Foreman::Engine.new(write_procfile) }
let(:upstart) { Foreman::Export::Upstart.new(engine) }
before(:each) { load_export_templates_into_fakefs("upstart") }
before(:each) { stub(upstart).say }
it "exports to the filesystem" do
upstart.export("/tmp/init")
File.read("/tmp/init/foreman.conf").should == example_export_file("upstart/foreman.conf")
File.read("/tmp/init/foreman-alpha.conf").should == example_export_file("upstart/foreman-alpha.conf")
File.read("/tmp/init/foreman-alpha-1.conf").should == example_export_file("upstart/foreman-alpha-1.conf")
File.read("/tmp/init/foreman-alpha-2.conf").should == example_export_file("upstart/foreman-alpha-2.conf")
File.read("/tmp/init/foreman-bravo.conf").should == example_export_file("upstart/foreman.bravo.conf")
File.read("/tmp/init/foreman-bravo-1.conf").should == example_export_file("upstart/foreman-bravo-1.conf")
end
end
@@ -0,0 +1,6 @@
start on starting foreman-alpha
stop on stopping foreman-alpha
respawn
chdir /Users/david/Code/foreman
exec su foreman -c 'export PORT=5000; ./alpha >> /var/log/foreman/alpha-1.log 2>&1'
@@ -0,0 +1,6 @@
start on starting foreman-alpha
stop on stopping foreman-alpha
respawn
chdir /Users/david/Code/foreman
exec su foreman -c 'export PORT=5001; ./alpha >> /var/log/foreman/alpha-2.log 2>&1'
@@ -0,0 +1,2 @@
start on starting foreman
stop on stopping foreman
@@ -0,0 +1,6 @@
start on starting foreman-bravo
stop on stopping foreman-bravo
respawn
chdir /Users/david/Code/foreman
exec su foreman -c 'export PORT=5100; ./bravo >> /var/log/foreman/bravo-1.log 2>&1'
@@ -0,0 +1,2 @@
start on starting foreman
stop on stopping foreman
@@ -0,0 +1,8 @@
pre-start script
bash << "EOF"
mkdir -p /var/log/foreman
chown -R foreman /var/log/foreman
EOF
end script
+22 -1
View File
@@ -3,7 +3,7 @@ require "rspec"
require "fakefs/safe" require "fakefs/safe"
require "fakefs/spec_helpers" require "fakefs/spec_helpers"
$:.unshift "lib" $:.unshift File.expand_path("../../lib", __FILE__)
def mock_error(subject, message) def mock_error(subject, message)
mock_exit do mock_exit do
@@ -29,6 +29,27 @@ def write_procfile(procfile="Procfile")
file.puts "alpha: ./alpha" file.puts "alpha: ./alpha"
file.puts "bravo: ./bravo" file.puts "bravo: ./bravo"
end end
File.expand_path(procfile)
end
def load_export_templates_into_fakefs(type)
FakeFS.deactivate!
files = Dir[File.expand_path("../../data/export/#{type}/**", __FILE__)].inject({}) do |hash, file|
hash.update(file => File.read(file))
end
FakeFS.activate!
files.each do |filename, contents|
File.open(filename, "w") do |f|
f.puts contents
end
end
end
def example_export_file(filename)
FakeFS.deactivate!
data = File.read(File.expand_path("../resources/export/#{filename}", __FILE__))
FakeFS.activate!
data
end end
Rspec.configure do |config| Rspec.configure do |config|