Compare commits

...
10 Commits
12 changed files with 67 additions and 188 deletions
+3 -137
View File
@@ -1,138 +1,4 @@
foreman(1) -- manage Procfile-based applications Foreman
================================================ =======
## SYNOPSIS See the [man page](http://ddollar.github.com/foreman) for usage.
`foreman start [process]`<br>
`foreman export <var>format</var> [location]`
## DESCRIPTION
**Foreman** is a manager for Procfile-based applications. Its aim is to
abstract away the details of the Procfile format, and allow you to either run
your application directly or export it to some other process management
format.
## RUNNING
`foreman start` is used to run your application directly from the command line.
If no additional parameters are passed, foreman will run one instance of each
type of process defined in your Procfile.
If a parameter is passed, foreman will run one instance of the specified
application type.
The following options control how the application is run:
* `-s`, `--screen`:
Run the application as a series of screen windows rather than interleaved
in stdout.
## EXPORTING
`foreman export` is used to export your application to another process
management format.
An location to export can be passed as an argument. This argument may be
either required or optional depending on the export format.
The following options control how the application is run:
* `-a`, `--app`:
Use this name rather than the application's root directory name as the
name of the application when exporting.
* `-c`, `--concurrency`:
Specify the number of each process type to run. The value passed in
should be in the format `process=num,process=num`
* `-l`, `--log`:
Specify the directory to place process logs in.
* `-p`, `--port`:
Specify which port to use as the base for this application. Should be
a multiple of 1000.
* `-u`, `--user`:
Specify the user the application should be run as. Defaults to the
app name
## OPTIONS
These options control all modes of foreman's operation.
* `-f`, `--procfile`:
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.
## EXPORT FORMATS
foreman currently supports the following output formats:
* inittab
* upstart
## INITTAB EXPORT
Will export a chunk of inittab-compatible configuration:
# ----- foreman example processes -----
EX01:4:respawn:/bin/su - example -c 'PORT=5000 bundle exec thin start >> /var/log/web-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 -----
## UPSTART EXPORT
Will create a series of upstart scripts in the location you specify. Scripts
will be structured to make the following commands valid:
`start appname`
`stop appname-processname`
`restart appname-processname-3`
## PROCFILE
A Procfile should contain both a name for the process and the command used
to run it.
web bundle exec thin start
job bundle exec rake jobs:work
## EXAMPLES
Start one instance of each process type, interleave the output on stdout:
$ foreman start
Export the application in upstart format:
$ foreman export upstart /etc/init
Run one process type from the application defined in a specific Procfile:
$ foreman start alpha -p ~/app/Procfile
## COPYRIGHT
Foreman is Copyright (C) 2010 David Dollar <http://daviddollar.org>
[SYNOPSIS]: #SYNOPSIS "SYNOPSIS"
[DESCRIPTION]: #DESCRIPTION "DESCRIPTION"
[RUNNING]: #RUNNING "RUNNING"
[EXPORTING]: #EXPORTING "EXPORTING"
[OPTIONS]: #OPTIONS "OPTIONS"
[EXPORT FORMATS]: #EXPORT-FORMATS "EXPORT FORMATS"
[INITTAB EXPORT]: #INITTAB-EXPORT "INITTAB EXPORT"
[UPSTART EXPORT]: #UPSTART-EXPORT "UPSTART EXPORT"
[PROCFILE]: #PROCFILE "PROCFILE"
[EXAMPLES]: #EXAMPLES "EXAMPLES"
[COPYRIGHT]: #COPYRIGHT "COPYRIGHT"
[foreman(1)]: foreman.1.html
-1
View File
@@ -29,7 +29,6 @@ task :man do
ENV['RONN_MANUAL'] = "Foreman Manual" ENV['RONN_MANUAL'] = "Foreman Manual"
ENV['RONN_ORGANIZATION'] = "Foreman #{Foreman::VERSION}" ENV['RONN_ORGANIZATION'] = "Foreman #{Foreman::VERSION}"
sh "ronn -w -s toc -r5 --markdown man/*.ronn" sh "ronn -w -s toc -r5 --markdown man/*.ronn"
sh "cp man/foreman.1.markdown README.markdown"
sh "git add README.markdown" sh "git add README.markdown"
sh "git commit -m 'update readme' || echo 'nothing to commit'" sh "git commit -m 'update readme' || echo 'nothing to commit'"
end end
+3 -3
View File
@@ -5,11 +5,11 @@
Gem::Specification.new do |s| Gem::Specification.new do |s|
s.name = %q{foreman} s.name = %q{foreman}
s.version = "0.5.1" s.version = "0.7.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["David Dollar"] s.authors = ["David Dollar"]
s.date = %q{2010-06-30} s.date = %q{2010-07-19}
s.default_executable = %q{foreman} s.default_executable = %q{foreman}
s.description = %q{Process manager for applications with multiple components} s.description = %q{Process manager for applications with multiple components}
s.email = %q{ddollar@gmail.com} s.email = %q{ddollar@gmail.com}
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
"lib/foreman/export/inittab.rb", "lib/foreman/export/inittab.rb",
"lib/foreman/export/upstart.rb", "lib/foreman/export/upstart.rb",
"lib/foreman/process.rb", "lib/foreman/process.rb",
"lib/foreman/utils.rb",
"spec/foreman/cli_spec.rb", "spec/foreman/cli_spec.rb",
"spec/foreman/engine_spec.rb", "spec/foreman/engine_spec.rb",
"spec/foreman/export/upstart_spec.rb", "spec/foreman/export/upstart_spec.rb",
@@ -39,7 +40,6 @@ Gem::Specification.new do |s|
"spec/foreman_spec.rb", "spec/foreman_spec.rb",
"spec/spec_helper.rb" "spec/spec_helper.rb"
] ]
s.has_rdoc = false
s.homepage = %q{http://github.com/ddollar/foreman} s.homepage = %q{http://github.com/ddollar/foreman}
s.rdoc_options = ["--charset=UTF-8"] s.rdoc_options = ["--charset=UTF-8"]
s.require_paths = ["lib"] s.require_paths = ["lib"]
+1 -1
View File
@@ -1,6 +1,6 @@
module Foreman module Foreman
VERSION = "0.5.1" VERSION = "0.7.0"
class AppDoesNotExist < Exception; end class AppDoesNotExist < Exception; end
+4 -5
View File
@@ -9,17 +9,16 @@ 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 :screen, :type => :boolean, :aliases => "-s" method_option :concurrency, :type => :string, :aliases => "-c",
:banner => '"alpha=5,bar=3"'
def start(process=nil) def start(process=nil)
check_procfile! check_procfile!
if process if process
engine.execute(process) engine.execute(process, options)
elsif options[:screen]
engine.screen
else else
engine.start engine.start(options)
end end
end end
+30 -19
View File
@@ -1,5 +1,6 @@
require "foreman" require "foreman"
require "foreman/process" require "foreman/process"
require "foreman/utils"
require "pty" require "pty"
require "tempfile" require "tempfile"
require "term/ansicolor" require "term/ansicolor"
@@ -12,28 +13,42 @@ class Foreman::Engine
extend Term::ANSIColor extend Term::ANSIColor
COLORS = [ cyan, yellow, green, magenta, on_blue ] COLORS = [ cyan, yellow, green, magenta, red ]
def initialize(procfile) def initialize(procfile)
@procfile = read_procfile(procfile) @procfile = read_procfile(procfile)
@directory = File.expand_path(File.dirname(procfile)) @directory = File.expand_path(File.dirname(procfile))
end end
def processes def processes(concurrency=nil)
@processes ||= begin @processes ||= begin
concurrency = Foreman::Utils.parse_concurrency(concurrency)
procfile.split("\n").inject({}) do |hash, line| procfile.split("\n").inject({}) do |hash, line|
next if line.strip == "" next if line.strip == ""
process = Foreman::Process.new(*line.split(" ", 2)) name, command = line.split(" ", 2)
process.color = next_color
hash.update(process.name => process) if concurrency[name] > 1 then
1.upto(concurrency[name]) do |num|
process = Foreman::Process.new("#{name}.#{num}", command)
process.color = next_color
hash[process.name] = process
end
else
process = Foreman::Process.new(name, command)
process.color = next_color
hash[process.name] = process
end
hash
end end
end end
end end
def start def start(options={})
proctitle "ruby: foreman master" proctitle "ruby: foreman master"
processes.each do |name, process| processes(options[:concurrency]).each do |name, process|
fork process fork process
end end
@@ -43,21 +58,17 @@ class Foreman::Engine
watch_for_termination watch_for_termination
end end
def screen def execute(name, options={})
tempfile = Tempfile.new("foreman") processes(options[:concurrency]).values.select do |process|
tempfile.puts "sessionname foreman" process.name =~ /\A#{name}\.?\d*\Z/
processes.each do |name, process| end.each do |process|
tempfile.puts "screen -t #{name} #{process.command}" fork process
end end
tempfile.close
system "screen -c #{tempfile.path}" trap("TERM") { kill_and_exit("TERM") }
trap("INT") { kill_and_exit("INT") }
tempfile.delete watch_for_termination
end
def execute(name)
run(processes[name], false)
end end
private ###################################################################### private ######################################################################
+1 -10
View File
@@ -1,4 +1,5 @@
require "foreman/export" require "foreman/export"
require "foreman/utils"
class Foreman::Export::Base class Foreman::Export::Base
@@ -26,16 +27,6 @@ private ######################################################################
File.read(File.expand_path("../../../../export/#{name}", __FILE__)) File.read(File.expand_path("../../../../export/#{name}", __FILE__))
end end
def parse_concurrency(concurrency)
@concurrency ||= begin
pairs = concurrency.to_s.gsub(/\s/, "").split(",")
pairs.inject(Hash.new(1)) do |hash, pair|
process, amount = pair.split("=")
hash.update(process => amount.to_i)
end
end
end
def port_for(base_port, app, num) def port_for(base_port, app, num)
base_port ||= 5000 base_port ||= 5000
offset = engine.processes.keys.sort.index(app) * 100 offset = engine.processes.keys.sort.index(app) * 100
+1 -1
View File
@@ -5,7 +5,7 @@ class Foreman::Export::Inittab < Foreman::Export::Base
def export(fname=nil, options={}) def export(fname=nil, options={})
app = options[:app] || File.basename(engine.directory) app = options[:app] || File.basename(engine.directory)
user = options[:user] || app user = options[:user] || app
log_root = options[:log] || "/var/log" log_root = options[:log] || "/var/log/#{app}"
concurrency = parse_concurrency(options[:concurrency]) concurrency = parse_concurrency(options[:concurrency])
+15
View File
@@ -0,0 +1,15 @@
require "foreman"
class Foreman::Utils
def self.parse_concurrency(concurrency)
@concurrency ||= begin
pairs = concurrency.to_s.gsub(/\s/, "").split(",")
pairs.inject(Hash.new(1)) do |hash, pair|
process, amount = pair.split("=")
hash.update(process => amount.to_i)
end
end
end
end
+3 -3
View File
@@ -25,9 +25,9 @@ application type.
The following options control how the application is run: The following options control how the application is run:
* `-s`, `--screen`: * `-c`, `--concurrency`:
Run the application as a series of screen windows rather than interleaved Specify the number of each process type to run. The value passed in
in stdout. should be in the format `process=num,process=num`
## EXPORTING ## EXPORTING
+2 -5
View File
@@ -19,7 +19,7 @@ describe "Foreman::CLI" do
it "runs successfully" do it "runs successfully" do
dont_allow(subject).error dont_allow(subject).error
mock.instance_of(Foreman::Engine).start mock.instance_of(Foreman::Engine).start({})
subject.start subject.start
end end
end end
@@ -51,10 +51,7 @@ describe "Foreman::CLI" do
it "runs successfully" do it "runs successfully" do
dont_allow(subject).error dont_allow(subject).error
mock.instance_of(Foreman::Export::Upstart).export("/tmp/foo", { mock.instance_of(Foreman::Export::Upstart).export("/tmp/foo", {})
:concurrency => nil,
:name => nil
})
subject.export("upstart", "/tmp/foo") subject.export("upstart", "/tmp/foo")
end end
end end
+4 -3
View File
@@ -10,7 +10,7 @@ describe "Foreman::Engine" do
lambda { subject }.should raise_error lambda { subject }.should raise_error
end end
end end
describe "with a Procfile" do describe "with a Procfile" do
it "reads the processes" do it "reads the processes" do
write_procfile write_procfile
@@ -19,7 +19,7 @@ describe "Foreman::Engine" do
end end
end end
end end
describe "start" do describe "start" do
it "forks the processes" do it "forks the processes" do
write_procfile write_procfile
@@ -33,7 +33,8 @@ 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).run(subject.processes["alpha"], false) mock(subject).fork(subject.processes["alpha"])
mock(subject).watch_for_termination
subject.execute("alpha") subject.execute("alpha")
end end
end end