Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 54b40d7e64 | |||
| c10f51ad56 | |||
| 2d6cbc282d | |||
| 759eee41b7 | |||
| 5c2907575b | |||
| 733580dac6 | |||
| 2aef4b91ea | |||
| 6c07d8431f | |||
| 47edd88f6e | |||
| 16585d7993 | |||
| 1f746769b8 | |||
| eb325f7a49 | |||
| ff6a539b93 | |||
| 6be84449ea | |||
| ab0d95d446 | |||
| 68442bba75 | |||
| cfd361695d | |||
| 3582410c63 | |||
| e45c7c7623 | |||
| e9f75edaf3 | |||
| a9e7ed3100 | |||
| 1cc3f9112a | |||
| c1ef2ef8d5 | |||
| 634e045e5b | |||
| 69243df17e | |||
| 097e3ca51b | |||
| 0b4cff586c | |||
| 6e4224a0e7 | |||
| 5fdd935004 | |||
| d943912f82 | |||
| 279c8c03a9 | |||
| ac1098bd15 | |||
| d0eb079f38 | |||
| 269c6fe94a | |||
| 7954ab0a60 | |||
| 6478bb749f | |||
| 83814d62f8 | |||
| aeaf29e3aa | |||
| 40949e352f | |||
| 3ae466ad49 | |||
| 5ad20f962f | |||
| f1df0887cf |
+4
-3
@@ -1,3 +1,4 @@
|
||||
coverage
|
||||
example/log/*
|
||||
pkg
|
||||
/.bundle
|
||||
/coverage
|
||||
/man
|
||||
/pkg
|
||||
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
= Foreman
|
||||
|
||||
=== Procfile
|
||||
|
||||
alpha ./bin/alpha
|
||||
bravo ./bin/bravo some args
|
||||
charlie ./bin/charlie -n 5
|
||||
|
||||
== Development mode
|
||||
|
||||
=== Running
|
||||
|
||||
$ foreman start
|
||||
[foreman] [Tue May 18 01:27:08 UTC 2010] [alpha] started with pid 4393
|
||||
[foreman] [Tue May 18 01:27:08 UTC 2010] [bravo] started with pid 4394
|
||||
[foreman] [Tue May 18 01:27:08 UTC 2010] [charlie] started with pid 4395
|
||||
|
||||
=== Standardized Logging
|
||||
|
||||
log/alpha.log
|
||||
log/bravo.log
|
||||
log/charlie.log
|
||||
|
||||
== Upstart
|
||||
|
||||
=== Export to upstart scripts
|
||||
|
||||
$ foreman export sampleapp
|
||||
|
||||
$ initctl list | grep sampleapp
|
||||
sampleapp start/running
|
||||
sampleapp-alpha (1) start/running, process 4204
|
||||
sampleapp-bravo (1) start/running, process 4589
|
||||
sampleapp-charlie (1) start/running, process 4597
|
||||
|
||||
=== Change process concurrency levels
|
||||
|
||||
$ foreman scale sampleapp alpha 4
|
||||
sampleapp-alpha (2) start/running, process 4164
|
||||
sampleapp-alpha (3) start/running, process 4166
|
||||
sampleapp-alpha (4) start/running, process 4168
|
||||
|
||||
$ initctl list | grep sampleapp
|
||||
sampleapp start/running
|
||||
sampleapp-alpha (4) start/running, process 4168
|
||||
sampleapp-alpha (3) start/running, process 4166
|
||||
sampleapp-alpha (2) start/running, process 4164
|
||||
sampleapp-alpha (1) start/running, process 4204
|
||||
sampleapp-bravo (1) start/running, process 4589
|
||||
sampleapp-charlie (1) start/running, process 4597
|
||||
|
||||
$ foreman scale sampleapp alpha 1
|
||||
sampleapp-alpha stop/waiting
|
||||
sampleapp-alpha stop/waiting
|
||||
sampleapp-alpha stop/waiting
|
||||
|
||||
=== Good Upstart citizen
|
||||
|
||||
All Upstart commands work as expected
|
||||
|
||||
$ start sampleapp
|
||||
$ stop sampleapp
|
||||
$ restart sampleapp
|
||||
|
||||
=== Standardized Logging
|
||||
|
||||
/var/log/sampleapp/alpha.log
|
||||
/var/log/sampleapp/bravo.log
|
||||
/var/log/sampleapp/charlie.log
|
||||
|
||||
== License
|
||||
|
||||
MIT
|
||||
|
||||
== Copyright
|
||||
|
||||
(c) 2010 David Dollar
|
||||
@@ -1,60 +0,0 @@
|
||||
require "rubygems"
|
||||
require "rake"
|
||||
require "rspec/core/rake_task"
|
||||
|
||||
$:.unshift File.expand_path("../lib", __FILE__)
|
||||
require "foreman"
|
||||
|
||||
task :default => :spec
|
||||
|
||||
desc "Run all specs"
|
||||
Rspec::Core::RakeTask.new(:spec) do |t|
|
||||
t.pattern = 'spec/**/*_spec.rb'
|
||||
end
|
||||
|
||||
desc "Generate RCov code coverage report"
|
||||
task :rcov => "rcov:build" do
|
||||
%x{ open coverage/index.html }
|
||||
end
|
||||
|
||||
Rspec::Core::RakeTask.new("rcov:build") do |t|
|
||||
t.pattern = 'spec/**/*_spec.rb'
|
||||
t.rcov = true
|
||||
t.rcov_opts = [ "--exclude", Gem.default_dir , "--exclude", "spec" ]
|
||||
end
|
||||
|
||||
######################################################
|
||||
|
||||
begin
|
||||
require 'jeweler'
|
||||
Jeweler::Tasks.new do |s|
|
||||
s.name = "foreman"
|
||||
s.version = Foreman::VERSION
|
||||
|
||||
s.summary = "Process manager for applications with multiple components"
|
||||
s.description = s.summary
|
||||
s.author = "David Dollar"
|
||||
s.email = "ddollar@gmail.com"
|
||||
s.homepage = "http://github.com/ddollar/foreman"
|
||||
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.has_rdoc = false
|
||||
|
||||
s.files = %w(Rakefile README.md) + Dir["{bin,lib,spec}/**/*"]
|
||||
s.require_path = "lib"
|
||||
|
||||
# #s.bindir = "bin"
|
||||
# s.executables = Dir["bin/*"]
|
||||
s.default_executable = "foreman"
|
||||
|
||||
s.add_development_dependency 'fakefs', '~> 0.2.1'
|
||||
s.add_development_dependency 'rake', '~> 0.8.7'
|
||||
s.add_development_dependency 'rcov', '~> 0.9.8'
|
||||
s.add_development_dependency 'rr', '~> 0.10.11'
|
||||
s.add_development_dependency 'rspec', '~> 2.0.0'
|
||||
|
||||
s.add_dependency 'thor', '~> 0.13.6'
|
||||
end
|
||||
rescue LoadError
|
||||
puts "Jeweler not available. Install it with: sudo gem install jeweler"
|
||||
end
|
||||
@@ -1 +0,0 @@
|
||||
Autotest.add_discovery { "rspec2" }
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
$:.unshift File.expand_path("../../lib", __FILE__)
|
||||
|
||||
require "foreman/cli"
|
||||
|
||||
Foreman::CLI.start
|
||||
@@ -1,3 +0,0 @@
|
||||
neverdie ./never_die
|
||||
diealot ./die_alot
|
||||
error ./error
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
puts "sleeping for 2s then dying"
|
||||
sleep 2
|
||||
exit 0
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
puts "will error in 10s"
|
||||
sleep 10
|
||||
raise "Dying"
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
while true
|
||||
puts "tick"
|
||||
$stdout.flush
|
||||
sleep 5
|
||||
end
|
||||
@@ -1,84 +0,0 @@
|
||||
# Generated by jeweler
|
||||
# DO NOT EDIT THIS FILE DIRECTLY
|
||||
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = %q{foreman}
|
||||
s.version = "0.1.0"
|
||||
|
||||
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
||||
s.authors = ["David Dollar"]
|
||||
s.date = %q{2010-05-21}
|
||||
s.default_executable = %q{foreman}
|
||||
s.description = %q{Process manager for applications with multiple components}
|
||||
s.email = %q{ddollar@gmail.com}
|
||||
s.executables = ["foreman"]
|
||||
s.extra_rdoc_files = [
|
||||
"README.rdoc"
|
||||
]
|
||||
s.files = [
|
||||
"Rakefile",
|
||||
"bin/foreman",
|
||||
"lib/foreman.rb",
|
||||
"lib/foreman/cli.rb",
|
||||
"lib/foreman/configuration.rb",
|
||||
"lib/foreman/engine.rb",
|
||||
"lib/foreman/export.rb",
|
||||
"lib/foreman/export/upstart.rb",
|
||||
"lib/foreman/process.rb",
|
||||
"spec/foreman/cli_spec.rb",
|
||||
"spec/foreman/configuration_spec.rb",
|
||||
"spec/foreman/engine_spec.rb",
|
||||
"spec/foreman/export/upstart_spec.rb",
|
||||
"spec/foreman/export_spec.rb",
|
||||
"spec/foreman/process_spec.rb",
|
||||
"spec/foreman_spec.rb",
|
||||
"spec/spec_helper.rb"
|
||||
]
|
||||
s.has_rdoc = false
|
||||
s.homepage = %q{http://github.com/ddollar/foreman}
|
||||
s.rdoc_options = ["--charset=UTF-8"]
|
||||
s.require_paths = ["lib"]
|
||||
s.rubygems_version = %q{1.3.7}
|
||||
s.summary = %q{Process manager for applications with multiple components}
|
||||
s.test_files = [
|
||||
"spec/foreman/cli_spec.rb",
|
||||
"spec/foreman/configuration_spec.rb",
|
||||
"spec/foreman/engine_spec.rb",
|
||||
"spec/foreman/export/upstart_spec.rb",
|
||||
"spec/foreman/export_spec.rb",
|
||||
"spec/foreman/process_spec.rb",
|
||||
"spec/foreman_spec.rb",
|
||||
"spec/spec_helper.rb"
|
||||
]
|
||||
|
||||
if s.respond_to? :specification_version then
|
||||
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
||||
s.specification_version = 3
|
||||
|
||||
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
||||
s.add_development_dependency(%q<fakefs>, ["~> 0.2.1"])
|
||||
s.add_development_dependency(%q<rake>, ["~> 0.8.7"])
|
||||
s.add_development_dependency(%q<rcov>, ["~> 0.9.8"])
|
||||
s.add_development_dependency(%q<rr>, ["~> 0.10.11"])
|
||||
s.add_development_dependency(%q<rspec>, ["~> 2.0.0"])
|
||||
s.add_runtime_dependency(%q<thor>, ["~> 0.13.6"])
|
||||
else
|
||||
s.add_dependency(%q<fakefs>, ["~> 0.2.1"])
|
||||
s.add_dependency(%q<rake>, ["~> 0.8.7"])
|
||||
s.add_dependency(%q<rcov>, ["~> 0.9.8"])
|
||||
s.add_dependency(%q<rr>, ["~> 0.10.11"])
|
||||
s.add_dependency(%q<rspec>, ["~> 2.0.0"])
|
||||
s.add_dependency(%q<thor>, ["~> 0.13.6"])
|
||||
end
|
||||
else
|
||||
s.add_dependency(%q<fakefs>, ["~> 0.2.1"])
|
||||
s.add_dependency(%q<rake>, ["~> 0.8.7"])
|
||||
s.add_dependency(%q<rcov>, ["~> 0.9.8"])
|
||||
s.add_dependency(%q<rr>, ["~> 0.10.11"])
|
||||
s.add_dependency(%q<rspec>, ["~> 2.0.0"])
|
||||
s.add_dependency(%q<thor>, ["~> 0.13.6"])
|
||||
end
|
||||
end
|
||||
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv='content-type' value='text/html;charset=utf8'>
|
||||
<meta name='generator' value='Ronn/v0.7.3 (http://github.com/rtomayko/ronn/tree/0.7.3)'>
|
||||
<title>foreman(1) - manage Procfile-based applications</title>
|
||||
<style type='text/css' media='all'>
|
||||
/* style: man */
|
||||
body#manpage {margin:0}
|
||||
.mp {max-width:100ex;padding:0 9ex 1ex 4ex}
|
||||
.mp p,.mp pre,.mp ul,.mp ol,.mp dl {margin:0 0 20px 0}
|
||||
.mp h2 {margin:10px 0 0 0}
|
||||
.mp > p,.mp > pre,.mp > ul,.mp > ol,.mp > dl {margin-left:8ex}
|
||||
.mp h3 {margin:0 0 0 4ex}
|
||||
.mp dt {margin:0;clear:left}
|
||||
.mp dt.flush {float:left;width:8ex}
|
||||
.mp dd {margin:0 0 0 9ex}
|
||||
.mp h1,.mp h2,.mp h3,.mp h4 {clear:left}
|
||||
.mp pre {margin-bottom:20px}
|
||||
.mp pre+h2,.mp pre+h3 {margin-top:22px}
|
||||
.mp h2+pre,.mp h3+pre {margin-top:5px}
|
||||
.mp img {display:block;margin:auto}
|
||||
.mp h1.man-title {display:none}
|
||||
.mp,.mp code,.mp pre,.mp tt,.mp kbd,.mp samp,.mp h3,.mp h4 {font-family:monospace;font-size:14px;line-height:1.42857142857143}
|
||||
.mp h2 {font-size:16px;line-height:1.25}
|
||||
.mp h1 {font-size:20px;line-height:2}
|
||||
.mp {text-align:justify;background:#fff}
|
||||
.mp,.mp code,.mp pre,.mp pre code,.mp tt,.mp kbd,.mp samp {color:#131211}
|
||||
.mp h1,.mp h2,.mp h3,.mp h4 {color:#030201}
|
||||
.mp u {text-decoration:underline}
|
||||
.mp code,.mp strong,.mp b {font-weight:bold;color:#131211}
|
||||
.mp em,.mp var {font-style:italic;color:#232221;text-decoration:none}
|
||||
.mp a,.mp a:link,.mp a:hover,.mp a code,.mp a pre,.mp a tt,.mp a kbd,.mp a samp {color:#0000ff}
|
||||
.mp b.man-ref {font-weight:normal;color:#434241}
|
||||
.mp pre {padding:0 4ex}
|
||||
.mp pre code {font-weight:normal;color:#434241}
|
||||
.mp h2+pre,h3+pre {padding-left:0}
|
||||
ol.man-decor,ol.man-decor li {margin:3px 0 10px 0;padding:0;float:left;width:33%;list-style-type:none;text-transform:uppercase;color:#999;letter-spacing:1px}
|
||||
ol.man-decor {width:100%}
|
||||
ol.man-decor li.tl {text-align:left}
|
||||
ol.man-decor li.tc {text-align:center;letter-spacing:4px}
|
||||
ol.man-decor li.tr {text-align:right;float:right}
|
||||
</style>
|
||||
<style type='text/css' media='all'>
|
||||
/* style: toc */
|
||||
.man-navigation {display:block !important;position:fixed;top:0;left:113ex;height:100%;width:100%;padding:48px 0 0 0;border-left:1px solid #dbdbdb;background:#eee}
|
||||
.man-navigation a,.man-navigation a:hover,.man-navigation a:link,.man-navigation a:visited {display:block;margin:0;padding:5px 2px 5px 30px;color:#999;text-decoration:none}
|
||||
.man-navigation a:hover {color:#111;text-decoration:underline}
|
||||
</style>
|
||||
</head>
|
||||
<!--
|
||||
The following styles are deprecated and will be removed at some point:
|
||||
div#man, div#man ol.man, div#man ol.head, div#man ol.man.
|
||||
|
||||
The .man-page, .man-decor, .man-head, .man-foot, .man-title, and
|
||||
.man-navigation should be used instead.
|
||||
-->
|
||||
<body id='manpage'>
|
||||
<div class='mp' id='man'>
|
||||
|
||||
<div class='man-navigation' style='display:none'>
|
||||
<a href="#NAME">NAME</a>
|
||||
<a href="#SYNOPSIS">SYNOPSIS</a>
|
||||
<a href="#DESCRIPTION">DESCRIPTION</a>
|
||||
<a href="#RUNNING">RUNNING</a>
|
||||
<a href="#EXPORTING">EXPORTING</a>
|
||||
<a href="#GLOBAL-OPTIONS">GLOBAL OPTIONS</a>
|
||||
<a href="#EXPORT-FORMATS">EXPORT FORMATS</a>
|
||||
<a href="#INITTAB-EXPORT">INITTAB EXPORT</a>
|
||||
<a href="#UPSTART-EXPORT">UPSTART EXPORT</a>
|
||||
<a href="#PROCFILE">PROCFILE</a>
|
||||
<a href="#ENVIRONMENT">ENVIRONMENT</a>
|
||||
<a href="#DEFAULT-OPTIONS">DEFAULT OPTIONS</a>
|
||||
<a href="#EXAMPLES">EXAMPLES</a>
|
||||
<a href="#COPYRIGHT">COPYRIGHT</a>
|
||||
</div>
|
||||
|
||||
<ol class='man-decor man-head man head'>
|
||||
<li class='tl'>foreman(1)</li>
|
||||
<li class='tc'>Foreman Manual</li>
|
||||
<li class='tr'>foreman(1)</li>
|
||||
</ol>
|
||||
|
||||
<h2 id="NAME">NAME</h2>
|
||||
<p class="man-name">
|
||||
<code>foreman</code> - <span class="man-whatis">manage Procfile-based applications</span>
|
||||
</p>
|
||||
|
||||
<h2 id="SYNOPSIS">SYNOPSIS</h2>
|
||||
|
||||
<p><code>foreman start [process]</code><br />
|
||||
<code>foreman run <command></code><br />
|
||||
<code>foreman export <format> [location]</code></p>
|
||||
|
||||
<h2 id="DESCRIPTION">DESCRIPTION</h2>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<h2 id="RUNNING">RUNNING</h2>
|
||||
|
||||
<p><code>foreman start</code> is used to run your application directly from the command line.</p>
|
||||
|
||||
<p>If no additional parameters are passed, foreman will run one instance of each
|
||||
type of process defined in your Procfile.</p>
|
||||
|
||||
<p>If a parameter is passed, foreman will run one instance of the specified
|
||||
application type.</p>
|
||||
|
||||
<p>The following options control how the application is run:</p>
|
||||
|
||||
<dl>
|
||||
<dt><code>-c</code>, <code>--concurrency</code></dt><dd><p>Specify the number of each process type to run. The value passed in
|
||||
should be in the format <code>process=num,process=num</code></p></dd>
|
||||
<dt><code>-e</code>, <code>--env</code></dt><dd><p>Specify one or more .env files to load</p></dd>
|
||||
<dt><code>-f</code>, <code>--procfile</code></dt><dd><p>Specify an alternate Procfile to load, implies <code>-d</code> at the Procfile root.</p></dd>
|
||||
<dt><code>-p</code>, <code>--port</code></dt><dd><p>Specify which port to use as the base for this application. Should be
|
||||
a multiple of 1000.</p></dd>
|
||||
</dl>
|
||||
|
||||
|
||||
<p><code>foreman run</code> is used to run one-off commands using the same environment
|
||||
as your defined processes.</p>
|
||||
|
||||
<h2 id="EXPORTING">EXPORTING</h2>
|
||||
|
||||
<p><code>foreman export</code> is used to export your application to another process
|
||||
management format.</p>
|
||||
|
||||
<p>An location to export can be passed as an argument. This argument may be
|
||||
either required or optional depending on the export format.</p>
|
||||
|
||||
<p>The following options control how the application is run:</p>
|
||||
|
||||
<dl>
|
||||
<dt><code>-a</code>, <code>--app</code></dt><dd><p>Use this name rather than the application's root directory name as the
|
||||
name of the application when exporting.</p></dd>
|
||||
<dt><code>-c</code>, <code>--concurrency</code></dt><dd><p>Specify the number of each process type to run. The value passed in
|
||||
should be in the format <code>process=num,process=num</code></p></dd>
|
||||
<dt><code>-l</code>, <code>--log</code></dt><dd><p>Specify the directory to place process logs in.</p></dd>
|
||||
<dt><code>-p</code>, <code>--port</code></dt><dd><p>Specify which port to use as the base for this application. Should be
|
||||
a multiple of 1000.</p></dd>
|
||||
<dt><code>-t</code>, <code>--template</code></dt><dd><p>Specify an alternate template to use for creating export files.
|
||||
See <a href="https://github.com/ddollar/foreman/tree/master/data/export" data-bare-link="true">https://github.com/ddollar/foreman/tree/master/data/export</a> for examples.</p></dd>
|
||||
<dt><code>-u</code>, <code>--user</code></dt><dd><p>Specify the user the application should be run as. Defaults to the
|
||||
app name</p></dd>
|
||||
</dl>
|
||||
|
||||
|
||||
<h2 id="GLOBAL-OPTIONS">GLOBAL OPTIONS</h2>
|
||||
|
||||
<p>These options control all modes of foreman's operation.</p>
|
||||
|
||||
<dl>
|
||||
<dt><code>-d</code>, <code>--root</code></dt><dd><p>Specify an alternate application root. This defaults to the directory
|
||||
containing the Procfile.</p></dd>
|
||||
<dt><code>-e</code>, <code>--env</code></dt><dd><p>Specify an alternate environment file. You can specify more than one
|
||||
file by using: <code>--env file1,file2</code>.</p></dd>
|
||||
<dt><code>-f</code>, <code>--procfile</code></dt><dd><p>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.</p></dd>
|
||||
</dl>
|
||||
|
||||
|
||||
<h2 id="EXPORT-FORMATS">EXPORT FORMATS</h2>
|
||||
|
||||
<p>foreman currently supports the following output formats:</p>
|
||||
|
||||
<ul>
|
||||
<li><p>bluepill</p></li>
|
||||
<li><p>inittab</p></li>
|
||||
<li><p>runit</p></li>
|
||||
<li><p>upstart</p></li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h2 id="INITTAB-EXPORT">INITTAB EXPORT</h2>
|
||||
|
||||
<p>Will export a chunk of inittab-compatible configuration:</p>
|
||||
|
||||
<pre><code># ----- 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 -----
|
||||
</code></pre>
|
||||
|
||||
<h2 id="UPSTART-EXPORT">UPSTART EXPORT</h2>
|
||||
|
||||
<p>Will create a series of upstart scripts in the location you specify. Scripts
|
||||
will be structured to make the following commands valid:</p>
|
||||
|
||||
<p> <code>start appname</code></p>
|
||||
|
||||
<p> <code>stop appname-processname</code></p>
|
||||
|
||||
<p> <code>restart appname-processname-3</code></p>
|
||||
|
||||
<h2 id="PROCFILE">PROCFILE</h2>
|
||||
|
||||
<p>A Procfile should contain both a name for the process and the command used
|
||||
to run it.</p>
|
||||
|
||||
<pre><code>web: bundle exec thin start
|
||||
job: bundle exec rake jobs:work
|
||||
</code></pre>
|
||||
|
||||
<p>A process name may contain letters, numbers amd the underscore character.
|
||||
You can validate your Procfile format using the <code>check</code> command:</p>
|
||||
|
||||
<pre><code>$ foreman check
|
||||
</code></pre>
|
||||
|
||||
<h2 id="ENVIRONMENT">ENVIRONMENT</h2>
|
||||
|
||||
<p>If a <code>.env</code> file exists in the current directory, the default environment will
|
||||
be read from it. This file should contain key/value pairs, separated by <code>=</code>, with
|
||||
one key/value pair per line.</p>
|
||||
|
||||
<pre><code>FOO=bar
|
||||
BAZ=qux
|
||||
</code></pre>
|
||||
|
||||
<h2 id="DEFAULT-OPTIONS">DEFAULT OPTIONS</h2>
|
||||
|
||||
<p>If a <code>.foreman</code> file exists in the current directory, default options will
|
||||
be read from it. This file should be in YAML format with the long option
|
||||
name as keys. Example:</p>
|
||||
|
||||
<pre><code>concurrency: alpha=0,bravo=1
|
||||
port: 15000
|
||||
</code></pre>
|
||||
|
||||
<h2 id="EXAMPLES">EXAMPLES</h2>
|
||||
|
||||
<p>Start one instance of each process type, interleave the output on stdout:</p>
|
||||
|
||||
<pre><code>$ foreman start
|
||||
</code></pre>
|
||||
|
||||
<p>Export the application in upstart format:</p>
|
||||
|
||||
<pre><code>$ foreman export upstart /etc/init
|
||||
</code></pre>
|
||||
|
||||
<p>Run one process type from the application defined in a specific Procfile:</p>
|
||||
|
||||
<pre><code>$ foreman start alpha -p ~/myapp/Procfile
|
||||
</code></pre>
|
||||
|
||||
<h2 id="COPYRIGHT">COPYRIGHT</h2>
|
||||
|
||||
<p>Foreman is Copyright (C) 2010 David Dollar <a href="http://daviddollar.org" data-bare-link="true">http://daviddollar.org</a></p>
|
||||
|
||||
|
||||
<ol class='man-decor man-foot man foot'>
|
||||
<li class='tl'>Foreman 0.63.0</li>
|
||||
<li class='tc'>April 2013</li>
|
||||
<li class='tr'>foreman(1)</li>
|
||||
</ol>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,8 +0,0 @@
|
||||
module Foreman
|
||||
|
||||
VERSION = "0.1.0"
|
||||
|
||||
class AppDoesNotExist < Exception; end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
require "foreman"
|
||||
require "foreman/configuration"
|
||||
require "foreman/engine"
|
||||
require "foreman/export"
|
||||
require "thor"
|
||||
|
||||
class Foreman::CLI < Thor
|
||||
|
||||
desc "start [PROCFILE]", "Run the app described in PROCFILE"
|
||||
|
||||
def start(procfile="Procfile")
|
||||
error "#{procfile} does not exist." unless procfile_exists?(procfile)
|
||||
Foreman::Engine.new(procfile).start
|
||||
end
|
||||
|
||||
desc "export APP [PROCFILE] [FORMAT]", "Export the app described in PROCFILE as APP to another FORMAT"
|
||||
|
||||
def export(app, procfile="Procfile", format="upstart")
|
||||
error "#{procfile} does not exist." unless procfile_exists?(procfile)
|
||||
|
||||
formatter = case format
|
||||
when "upstart" then Foreman::Export::Upstart
|
||||
else error "Unknown export format: #{format}."
|
||||
end
|
||||
|
||||
formatter.new(Foreman::Engine.new(procfile)).export(app)
|
||||
end
|
||||
|
||||
desc "scale APP PROCESS AMOUNT", "Change the concurrency of a given process type"
|
||||
|
||||
def scale(app, process, amount)
|
||||
config = Foreman::Configuration.new(app)
|
||||
error "No such process: #{process}." unless config.processes[process]
|
||||
config.scale(process, amount)
|
||||
end
|
||||
|
||||
private ######################################################################
|
||||
|
||||
def error(message)
|
||||
puts "ERROR: #{message}"
|
||||
exit 1
|
||||
end
|
||||
|
||||
def procfile_exists?(procfile)
|
||||
File.exist?(procfile)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,55 +0,0 @@
|
||||
require "foreman"
|
||||
|
||||
class Foreman::Configuration
|
||||
|
||||
attr_reader :app
|
||||
attr_reader :processes
|
||||
|
||||
def initialize(app)
|
||||
@app = app
|
||||
@processes = {}
|
||||
read_initial_config
|
||||
end
|
||||
|
||||
def scale(process, amount)
|
||||
old_amount = processes[process].to_i
|
||||
processes[process] = amount.to_i
|
||||
amount = amount.to_i
|
||||
|
||||
if (old_amount < amount)
|
||||
((old_amount + 1) .. amount).each { |num| system "start #{app}-#{process} NUM=#{num}" }
|
||||
elsif (amount < old_amount)
|
||||
((amount + 1) .. old_amount).each { |num| system "stop #{app}-#{process} NUM=#{num}" }
|
||||
end
|
||||
|
||||
write
|
||||
end
|
||||
|
||||
def write
|
||||
write_file "/etc/foreman/#{app}.conf", <<-UPSTART_CONFIG
|
||||
#{app}_processes="#{processes.keys.sort.join(' ')}"
|
||||
#{processes.keys.sort.map { |k| "#{app}_#{k}=\"#{processes[k]}\"" }.join("\n")}
|
||||
UPSTART_CONFIG
|
||||
end
|
||||
|
||||
private ######################################################################
|
||||
|
||||
def read_initial_config
|
||||
config = File.read("/etc/foreman/#{app}.conf").split("\n").inject({}) do |accum, line|
|
||||
key, value = line.match(/^(.+?)\s*=\s*"(.+?)"\s*$/).captures
|
||||
#accum.update(parts(1) => parts(2))
|
||||
accum.update(key => value)
|
||||
end
|
||||
config["#{app}_processes"].split(" ").each do |process|
|
||||
processes[process] = config["#{app}_#{process}"].to_i
|
||||
end
|
||||
rescue Errno::ENOENT
|
||||
end
|
||||
|
||||
def write_file(filename, contents)
|
||||
File.open(filename, "w") do |file|
|
||||
file.puts contents
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,95 +0,0 @@
|
||||
require "foreman"
|
||||
require "foreman/process"
|
||||
|
||||
class Foreman::Engine
|
||||
|
||||
attr_reader :procfile
|
||||
attr_reader :directory
|
||||
|
||||
def initialize(procfile)
|
||||
@procfile = read_procfile(procfile)
|
||||
@directory = File.expand_path(File.dirname(procfile))
|
||||
end
|
||||
|
||||
def processes
|
||||
@processes ||= begin
|
||||
procfile.split("\n").inject({}) do |hash, line|
|
||||
next if line.strip == ""
|
||||
process = Foreman::Process.new(*line.split(" ", 2))
|
||||
hash.update(process.name => process)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def start
|
||||
proctitle "ruby: foreman master"
|
||||
|
||||
processes.each do |name, process|
|
||||
fork process
|
||||
end
|
||||
|
||||
trap("TERM") { kill_and_exit("TERM") }
|
||||
trap("INT") { kill_and_exit("INT") }
|
||||
|
||||
run_loop
|
||||
end
|
||||
|
||||
private ######################################################################
|
||||
|
||||
def fork(process)
|
||||
pid = Process.fork do
|
||||
proctitle "ruby: foreman #{process.name}"
|
||||
|
||||
Dir.chdir directory do
|
||||
FileUtils.mkdir_p "log"
|
||||
system "#{process.command} >>log/#{process.name}.log 2>&1"
|
||||
exit $?.exitstatus || 255
|
||||
end
|
||||
end
|
||||
|
||||
info "started with pid #{pid}", process
|
||||
running_processes[pid] = process
|
||||
end
|
||||
|
||||
def kill_and_exit(signal="TERM")
|
||||
info "termination requested"
|
||||
running_processes.each do |pid, process|
|
||||
info "killing pid #{pid}", process
|
||||
Process.kill(signal, pid)
|
||||
end
|
||||
exit 0
|
||||
end
|
||||
|
||||
def info(message, process=nil)
|
||||
puts "[foreman] [#{Time.now.utc}] [#{process ? process.name : "system"}] #{message}"
|
||||
end
|
||||
|
||||
def print_info
|
||||
info "currently running processes:"
|
||||
running_processes.each do |pid, process|
|
||||
info "pid #{pid}", process
|
||||
end
|
||||
end
|
||||
|
||||
def proctitle(title)
|
||||
$0 = title
|
||||
end
|
||||
|
||||
def read_procfile(procfile)
|
||||
File.read(procfile)
|
||||
end
|
||||
|
||||
def run_loop
|
||||
while true
|
||||
pid, status = Process.wait2
|
||||
process = running_processes.delete(pid)
|
||||
info "exited with code #{status}", process
|
||||
fork process
|
||||
end
|
||||
end
|
||||
|
||||
def running_processes
|
||||
@running_processes ||= {}
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,6 +0,0 @@
|
||||
require "foreman"
|
||||
|
||||
module Foreman::Export
|
||||
end
|
||||
|
||||
require "foreman/export/upstart"
|
||||
@@ -1,66 +0,0 @@
|
||||
require "foreman/configuration"
|
||||
require "foreman/export"
|
||||
|
||||
class Foreman::Export::Upstart
|
||||
|
||||
attr_reader :engine
|
||||
|
||||
def initialize(engine)
|
||||
@engine = engine
|
||||
end
|
||||
|
||||
def export(app)
|
||||
FileUtils.mkdir_p "/etc/foreman"
|
||||
FileUtils.mkdir_p "/etc/init"
|
||||
|
||||
config = Foreman::Configuration.new(app)
|
||||
|
||||
write_file "/etc/init/#{app}.conf", <<-UPSTART_MASTER
|
||||
pre-start script
|
||||
|
||||
bash << "EOF"
|
||||
mkdir -p /var/log/#{app}
|
||||
|
||||
if [ -f /etc/foreman/#{app}.conf ]; then
|
||||
source /etc/foreman/#{app}.conf
|
||||
fi
|
||||
|
||||
for process in $( echo "$#{app}_processes" ); do
|
||||
process_count_config="#{app}_$process"
|
||||
process_count=${!process_count_config}
|
||||
|
||||
for ((i=1; i<=${process_count:=1}; i+=1)); do
|
||||
start #{app}-$process NUM=$i
|
||||
done
|
||||
done
|
||||
EOF
|
||||
|
||||
end script
|
||||
UPSTART_MASTER
|
||||
|
||||
engine.processes.values.each do |process|
|
||||
write_file "/etc/init/#{app}-#{process.name}.conf", <<-UPSTART_CHILD
|
||||
instance $NUM
|
||||
stop on stopping #{app}
|
||||
respawn
|
||||
|
||||
chdir #{engine.directory}
|
||||
exec #{process.command} >>/var/log/#{app}/#{process.name}.log 2>&1
|
||||
UPSTART_CHILD
|
||||
end
|
||||
|
||||
engine.processes.each do |name, process|
|
||||
config.scale(name, 1)
|
||||
end
|
||||
config.write
|
||||
end
|
||||
|
||||
private ######################################################################
|
||||
|
||||
def write_file(filename, contents)
|
||||
File.open(filename, "w") do |file|
|
||||
file.puts contents
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,13 +0,0 @@
|
||||
require "foreman"
|
||||
|
||||
class Foreman::Process
|
||||
|
||||
attr_reader :name
|
||||
attr_reader :command
|
||||
|
||||
def initialize(name, command)
|
||||
@name = name
|
||||
@command = command
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,88 +0,0 @@
|
||||
require "spec_helper"
|
||||
require "foreman/cli"
|
||||
|
||||
describe "Foreman::CLI" do
|
||||
subject { Foreman::CLI.new }
|
||||
|
||||
describe "start" do
|
||||
#let(:engine) { stub_engine }
|
||||
|
||||
describe "with a non-existent Procifile" do
|
||||
it "prints an error" do
|
||||
mock_error(subject, "Procfile does not exist.") do
|
||||
dont_allow.instance_of(Foreman::Engine).start
|
||||
subject.start
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a Procfile" do
|
||||
before(:each) { write_procfile }
|
||||
|
||||
it "runs successfully" do
|
||||
dont_allow(subject).error
|
||||
mock.instance_of(Foreman::Engine).start
|
||||
subject.start
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "export" do
|
||||
describe "with a non-existent Procifile" do
|
||||
it "prints an error" do
|
||||
mock_error(subject, "Procfile does not exist.") do
|
||||
dont_allow.instance_of(Foreman::Engine).export
|
||||
subject.export("testapp")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a Procfile" do
|
||||
before(:each) { write_procfile }
|
||||
|
||||
describe "with an invalid formatter" do
|
||||
it "prints an error" do
|
||||
mock_error(subject, "Unknown export format: invalidformatter.") do
|
||||
subject.export("testapp", "Procfile", "invalidformatter")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a valid config" do
|
||||
before(:each) { write_foreman_config("testapp") }
|
||||
|
||||
it "runs successfully" do
|
||||
dont_allow(subject).error
|
||||
subject.export("testapp")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "scale" do
|
||||
describe "without an existing configuration" do
|
||||
it "displays an error" do
|
||||
mock_error(subject, "No such process: alpha.") do
|
||||
subject.scale("testapp", "alpha", "2")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "with an existing configuration" do
|
||||
before(:each) { write_foreman_config("testapp") }
|
||||
|
||||
it "scales a process that exists" do
|
||||
mock.instance_of(Foreman::Configuration).scale("alpha", "2")
|
||||
subject.scale("testapp", "alpha", "2")
|
||||
end
|
||||
|
||||
it "errors if a process that does not exist is specified" do
|
||||
mock_error(subject, "No such process: invalidprocess.") do
|
||||
dont_allow.instance_of(Foreman::Configuration).scale
|
||||
subject.scale("testapp", "invalidprocess", "2")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,49 +0,0 @@
|
||||
require "spec_helper"
|
||||
require "foreman/configuration"
|
||||
|
||||
describe "Foreman::Configuration" do
|
||||
subject { Foreman::Configuration.new("testapp") }
|
||||
|
||||
describe "initialize" do
|
||||
describe "without an existing config" do
|
||||
it "has no processes" do
|
||||
subject.processes.length.should == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "with an existing config" do
|
||||
it "has processes" do
|
||||
write_foreman_config("testapp")
|
||||
subject.processes["alpha"].should == 1
|
||||
subject.processes["bravo"].should == 2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "scale" do
|
||||
before(:each) { write_foreman_config("testapp") }
|
||||
|
||||
it "can scale up" do
|
||||
mock(subject).system("start testapp-alpha NUM=2")
|
||||
mock(subject).system("start testapp-alpha NUM=3")
|
||||
subject.scale("alpha", 3)
|
||||
end
|
||||
|
||||
it "can scale down" do
|
||||
mock(subject).system("stop testapp-bravo NUM=2")
|
||||
subject.scale("bravo", 1)
|
||||
end
|
||||
end
|
||||
|
||||
describe "wite" do
|
||||
it "can write a configuration file" do
|
||||
subject.scale("charlie", 3)
|
||||
subject.scale("delta", 4)
|
||||
File.read("/etc/foreman/testapp.conf").should == <<-FOREMAN_CONFIG
|
||||
testapp_processes="charlie delta"
|
||||
testapp_charlie="3"
|
||||
testapp_delta="4"
|
||||
FOREMAN_CONFIG
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,32 +0,0 @@
|
||||
require "spec_helper"
|
||||
require "foreman/engine"
|
||||
|
||||
describe "Foreman::Engine" do
|
||||
subject { Foreman::Engine.new("Procfile") }
|
||||
|
||||
describe "initialize" do
|
||||
describe "without an existing Procfile" do
|
||||
it "raises an error" do
|
||||
lambda { subject }.should raise_error
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a Procfile" do
|
||||
it "reads the processes" do
|
||||
write_procfile
|
||||
subject.processes["alpha"].command.should == "./alpha"
|
||||
subject.processes["bravo"].command.should == "./bravo"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "start" do
|
||||
it "forks the processes" do
|
||||
write_procfile
|
||||
mock(subject).fork(subject.processes["alpha"])
|
||||
mock(subject).fork(subject.processes["bravo"])
|
||||
mock(subject).run_loop
|
||||
subject.start
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,2 +0,0 @@
|
||||
require "spec_helper"
|
||||
require "foreman/export/upstart"
|
||||
@@ -1,2 +0,0 @@
|
||||
require "spec_helper"
|
||||
require "foreman/export"
|
||||
@@ -1,2 +0,0 @@
|
||||
require "spec_helper"
|
||||
require "foreman/process"
|
||||
@@ -1,11 +0,0 @@
|
||||
require "spec_helper"
|
||||
require "foreman"
|
||||
|
||||
describe Foreman do
|
||||
|
||||
describe "VERSION" do
|
||||
subject { Foreman::VERSION }
|
||||
it { should be_a String }
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,36 +0,0 @@
|
||||
require "fakefs/spec_helpers"
|
||||
require "rspec"
|
||||
|
||||
$:.unshift "lib"
|
||||
|
||||
def mock_error(subject, message)
|
||||
mock_exit do
|
||||
mock(subject).puts("ERROR: #{message}")
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def mock_exit(&block)
|
||||
block.should raise_error(SystemExit)
|
||||
end
|
||||
|
||||
def write_foreman_config(app)
|
||||
File.open("/etc/foreman/#{app}.conf", "w") do |file|
|
||||
file.puts %{#{app}_processes="alpha bravo"}
|
||||
file.puts %{#{app}_alpha="1"}
|
||||
file.puts %{#{app}_bravo="2"}
|
||||
end
|
||||
end
|
||||
|
||||
def write_procfile(procfile="Procfile")
|
||||
File.open(procfile, "w") do |file|
|
||||
file.puts "alpha ./alpha"
|
||||
file.puts "bravo ./bravo"
|
||||
end
|
||||
end
|
||||
|
||||
Rspec.configure do |config|
|
||||
config.color_enabled = true
|
||||
config.include FakeFS::SpecHelpers
|
||||
config.mock_with :rr
|
||||
end
|
||||
Reference in New Issue
Block a user