Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60fb125b51 | ||
|
|
69d596bb8b | ||
|
|
cbd5a6344c | ||
|
|
4230455859 | ||
|
|
b25dfee62d | ||
|
|
b73da71c9c | ||
|
|
43558758fc | ||
|
|
6da8aca609 | ||
|
|
9df93a64cc | ||
|
|
8fd9b753f4 | ||
|
|
7fc6d02e7b | ||
|
|
ddcaac8749 | ||
|
|
9db97abb10 | ||
|
|
21a041527c |
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
foreman (0.20.0)
|
foreman (0.21.0)
|
||||||
term-ansicolor (~> 1.0.5)
|
term-ansicolor (~> 1.0.5)
|
||||||
thor (>= 0.13.6)
|
thor (>= 0.13.6)
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ GEM
|
|||||||
crack
|
crack
|
||||||
rest-client
|
rest-client
|
||||||
thor
|
thor
|
||||||
rake (0.8.7)
|
rake (0.9.2)
|
||||||
rcov (0.9.8)
|
rcov (0.9.8)
|
||||||
rdiscount (1.6.5)
|
rdiscount (1.6.5)
|
||||||
rest-client (1.6.1)
|
rest-client (1.6.1)
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/bluepill.log") do |app|
|
||||||
|
|
||||||
|
app.uid = "<%= user %>"
|
||||||
|
app.gid = "<%= user %>"
|
||||||
|
|
||||||
|
<% engine.processes.values.each do |process| %>
|
||||||
|
<% 1.upto(concurrency[process.name]) do |num| %>
|
||||||
|
<% port = engine.port_for(process, num, options[:port]) %>
|
||||||
|
app.process("<%= process.name %>-<%=num%>") do |process|
|
||||||
|
process.start_command = "<%= process.command.gsub("$PORT", port.to_s) %>"
|
||||||
|
|
||||||
|
process.working_dir = "<%= engine.directory %>"
|
||||||
|
process.daemonize = true
|
||||||
|
process.environment = {"PORT" => "<%= port %>"}
|
||||||
|
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||||
|
|
||||||
|
process.stdout = process.stderr = "<%= log_root %>/<%= app %>-<%= process.name %>-<%=num%>.log"
|
||||||
|
|
||||||
|
process.monitor_children do |children|
|
||||||
|
children.stop_command "kill -QUIT {{PID}}"
|
||||||
|
end
|
||||||
|
|
||||||
|
process.group = "<%= app %>-<%= process.name %>"
|
||||||
|
end
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
end
|
||||||
@@ -2,4 +2,4 @@ start on starting <%= app %>-<%= process.name %>
|
|||||||
stop on stopping <%= app %>-<%= process.name %>
|
stop on stopping <%= app %>-<%= process.name %>
|
||||||
respawn
|
respawn
|
||||||
|
|
||||||
exec su - <%= user %> -c 'cd <%= engine.directory %>; export PORT=<%= port %>; <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1'
|
exec su - <%= user %> -c 'cd <%= engine.directory %>; export PORT=<%= port %>;<% engine.environment.each_pair do |var,env| %> export <%= var.upcase %>=<%= env %>; <% end %> <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1'
|
||||||
|
|||||||
+4
-3
@@ -18,9 +18,9 @@ class Foreman::CLI < Thor
|
|||||||
check_procfile!
|
check_procfile!
|
||||||
|
|
||||||
if process
|
if process
|
||||||
engine.execute(process, options)
|
engine.execute(process)
|
||||||
else
|
else
|
||||||
engine.start(options)
|
engine.start
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -40,6 +40,7 @@ class Foreman::CLI < Thor
|
|||||||
formatter = case format
|
formatter = case format
|
||||||
when "inittab" then Foreman::Export::Inittab
|
when "inittab" then Foreman::Export::Inittab
|
||||||
when "upstart" then Foreman::Export::Upstart
|
when "upstart" then Foreman::Export::Upstart
|
||||||
|
when "bluepill" then Foreman::Export::Bluepill
|
||||||
else error "Unknown export format: #{format}."
|
else error "Unknown export format: #{format}."
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -64,7 +65,7 @@ private ######################################################################
|
|||||||
end
|
end
|
||||||
|
|
||||||
def engine
|
def engine
|
||||||
@engine ||= Foreman::Engine.new(procfile)
|
@engine ||= Foreman::Engine.new(procfile, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def procfile
|
def procfile
|
||||||
|
|||||||
+14
-13
@@ -11,14 +11,18 @@ class Foreman::Engine
|
|||||||
|
|
||||||
attr_reader :procfile
|
attr_reader :procfile
|
||||||
attr_reader :directory
|
attr_reader :directory
|
||||||
|
attr_reader :environment
|
||||||
|
attr_reader :options
|
||||||
|
|
||||||
extend Term::ANSIColor
|
extend Term::ANSIColor
|
||||||
|
|
||||||
COLORS = [ cyan, yellow, green, magenta, red ]
|
COLORS = [ cyan, yellow, green, magenta, red ]
|
||||||
|
|
||||||
def initialize(procfile)
|
def initialize(procfile, options={})
|
||||||
@procfile = read_procfile(procfile)
|
@procfile = read_procfile(procfile)
|
||||||
@directory = File.expand_path(File.dirname(procfile))
|
@directory = File.expand_path(File.dirname(procfile))
|
||||||
|
@options = options
|
||||||
|
@environment = read_environment(options[:env])
|
||||||
end
|
end
|
||||||
|
|
||||||
def processes
|
def processes
|
||||||
@@ -50,13 +54,11 @@ class Foreman::Engine
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def start(options={})
|
def start
|
||||||
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, environment
|
fork process
|
||||||
end
|
end
|
||||||
|
|
||||||
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
||||||
@@ -65,10 +67,9 @@ class Foreman::Engine
|
|||||||
watch_for_termination
|
watch_for_termination
|
||||||
end
|
end
|
||||||
|
|
||||||
def execute(name, options={})
|
def execute(name)
|
||||||
environment = read_environment(options[:env])
|
|
||||||
|
|
||||||
fork processes[name], options, environment
|
fork processes[name]
|
||||||
|
|
||||||
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
||||||
trap("INT") { puts "SIGINT received"; terminate_gracefully }
|
trap("INT") { puts "SIGINT received"; terminate_gracefully }
|
||||||
@@ -84,16 +85,16 @@ class Foreman::Engine
|
|||||||
|
|
||||||
private ######################################################################
|
private ######################################################################
|
||||||
|
|
||||||
def fork(process, options={}, environment={})
|
def fork(process)
|
||||||
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]), environment)
|
fork_individual(process, num, port_for(process, num, @options[:port]))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fork_individual(process, num, port, environment)
|
def fork_individual(process, num, port)
|
||||||
environment.each { |k,v| ENV[k] = v }
|
@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}"
|
||||||
|
|||||||
@@ -7,3 +7,4 @@ end
|
|||||||
require "foreman/export/base"
|
require "foreman/export/base"
|
||||||
require "foreman/export/inittab"
|
require "foreman/export/inittab"
|
||||||
require "foreman/export/upstart"
|
require "foreman/export/upstart"
|
||||||
|
require "foreman/export/bluepill"
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
require "erb"
|
||||||
|
require "foreman/export"
|
||||||
|
|
||||||
|
class Foreman::Export::Bluepill < Foreman::Export::Base
|
||||||
|
|
||||||
|
def export(location, options={})
|
||||||
|
error("Must specify a location") unless location
|
||||||
|
|
||||||
|
FileUtils.mkdir_p location
|
||||||
|
|
||||||
|
app = options[:app] || File.basename(engine.directory)
|
||||||
|
user = options[:user] || app
|
||||||
|
log_root = options[:log] || "/var/log/#{app}"
|
||||||
|
template_root = options[:template]
|
||||||
|
|
||||||
|
Dir["#{location}/#{app}.pill"].each do |file|
|
||||||
|
say "cleaning up: #{file}"
|
||||||
|
FileUtils.rm(file)
|
||||||
|
end
|
||||||
|
|
||||||
|
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
||||||
|
|
||||||
|
master_template = export_template("bluepill", "master.pill.erb", template_root)
|
||||||
|
master_config = ERB.new(master_template).result(binding)
|
||||||
|
write_file "#{location}/#{app}.pill", master_config
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
module Foreman
|
module Foreman
|
||||||
|
|
||||||
VERSION = "0.20.0"
|
VERSION = "0.21.0"
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
+4
-1
@@ -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" "August 2011" "Foreman 0.19.0" "Foreman Manual"
|
.TH "FOREMAN" "1" "September 2011" "Foreman 0.21.0" "Foreman Manual"
|
||||||
.
|
.
|
||||||
.SH "NAME"
|
.SH "NAME"
|
||||||
\fBforeman\fR \- manage Procfile\-based applications
|
\fBforeman\fR \- manage Procfile\-based applications
|
||||||
@@ -79,6 +79,9 @@ Specify an alternate location for the application\'s Procfile\. This file\'s con
|
|||||||
foreman currently supports the following output formats:
|
foreman currently supports the following output formats:
|
||||||
.
|
.
|
||||||
.IP "\(bu" 4
|
.IP "\(bu" 4
|
||||||
|
bluepill
|
||||||
|
.
|
||||||
|
.IP "\(bu" 4
|
||||||
inittab
|
inittab
|
||||||
.
|
.
|
||||||
.IP "\(bu" 4
|
.IP "\(bu" 4
|
||||||
|
|||||||
@@ -78,6 +78,8 @@ These options control all modes of foreman's operation.
|
|||||||
|
|
||||||
foreman currently supports the following output formats:
|
foreman currently supports the following output formats:
|
||||||
|
|
||||||
|
* bluepill
|
||||||
|
|
||||||
* inittab
|
* inittab
|
||||||
|
|
||||||
* upstart
|
* upstart
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
+24
-16
@@ -2,7 +2,7 @@ require "spec_helper"
|
|||||||
require "foreman/engine"
|
require "foreman/engine"
|
||||||
|
|
||||||
describe "Foreman::Engine" do
|
describe "Foreman::Engine" do
|
||||||
subject { Foreman::Engine.new("Procfile") }
|
subject { Foreman::Engine.new("Procfile", {}) }
|
||||||
|
|
||||||
describe "initialize" do
|
describe "initialize" do
|
||||||
describe "without an existing Procfile" do
|
describe "without an existing Procfile" do
|
||||||
@@ -37,53 +37,61 @@ 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, {})
|
engine = Foreman::Engine.new("Procfile",:concurrency => "alpha=2")
|
||||||
mock(subject).fork_individual(subject.processes["alpha"], 2, 5001, {})
|
mock(engine).fork_individual(engine.processes["alpha"], 1, 5000)
|
||||||
mock(subject).fork_individual(subject.processes["bravo"], 1, 5100, {})
|
mock(engine).fork_individual(engine.processes["alpha"], 2, 5001)
|
||||||
mock(subject).watch_for_termination
|
mock(engine).fork_individual(engine.processes["bravo"], 1, 5100)
|
||||||
subject.start(:concurrency => "alpha=2")
|
mock(engine).watch_for_termination
|
||||||
|
engine.start
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
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
|
describe "environment" do
|
||||||
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
write_procfile
|
write_procfile
|
||||||
stub(Process).fork
|
stub(Process).fork
|
||||||
stub(subject).info
|
|
||||||
mock(subject).watch_for_termination
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should read if specified" do
|
it "should read if specified" do
|
||||||
File.open("/tmp/env", "w") { |f| f.puts("FOO=baz") }
|
File.open("/tmp/env", "w") { |f| f.puts("FOO=baz") }
|
||||||
subject.execute("alpha", :env => "/tmp/env")
|
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env")
|
||||||
|
stub(engine).info
|
||||||
|
mock(engine).watch_for_termination
|
||||||
|
engine.environment.should == {"FOO"=>"baz"}
|
||||||
|
engine.execute("alpha")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should fail if specified and doesnt exist" do
|
it "should fail if specified and doesnt exist" do
|
||||||
mock(subject).error("No such file: /tmp/env")
|
mock.instance_of(Foreman::Engine).error("No such file: /tmp/env")
|
||||||
subject.execute("alpha", :env => "/tmp/env")
|
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should read .env if none specified" do
|
it "should read .env if none specified" do
|
||||||
File.open(".env", "w") { |f| f.puts("FOO=qoo") }
|
File.open(".env", "w") { |f| f.puts("FOO=qoo") }
|
||||||
mock(subject).fork_individual(anything, anything, anything, { "FOO" => "qoo" })
|
engine = Foreman::Engine.new("Procfile")
|
||||||
subject.execute("bravo")
|
stub(engine).info
|
||||||
|
mock(engine).watch_for_termination
|
||||||
|
mock(engine).fork_individual(anything, anything, anything)
|
||||||
|
engine.environment.should == {"FOO"=>"qoo"}
|
||||||
|
engine.execute("bravo")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
require "spec_helper"
|
||||||
|
require "foreman/engine"
|
||||||
|
require "foreman/export/bluepill"
|
||||||
|
require "tmpdir"
|
||||||
|
|
||||||
|
describe Foreman::Export::Bluepill do
|
||||||
|
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
|
||||||
|
let(:engine) { Foreman::Engine.new(procfile) }
|
||||||
|
let(:bluepill) { Foreman::Export::Bluepill.new(engine) }
|
||||||
|
|
||||||
|
before(:each) { load_export_templates_into_fakefs("bluepill") }
|
||||||
|
before(:each) { stub(bluepill).say }
|
||||||
|
|
||||||
|
it "exports to the filesystem" do
|
||||||
|
bluepill.export("/tmp/init")
|
||||||
|
|
||||||
|
File.read("/tmp/init/app.pill").should == example_export_file("bluepill/app.pill")
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepill.log") do |app|
|
||||||
|
|
||||||
|
app.uid = "app"
|
||||||
|
app.gid = "app"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
app.process("alpha-1") do |process|
|
||||||
|
process.start_command = "./alpha"
|
||||||
|
|
||||||
|
process.working_dir = "/tmp/app"
|
||||||
|
process.daemonize = true
|
||||||
|
process.environment = {"PORT" => "5000"}
|
||||||
|
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||||
|
|
||||||
|
process.stdout = process.stderr = "/var/log/app/app-alpha-1.log"
|
||||||
|
|
||||||
|
process.monitor_children do |children|
|
||||||
|
children.stop_command "kill -QUIT {{PID}}"
|
||||||
|
end
|
||||||
|
|
||||||
|
process.group = "app-alpha"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
app.process("alpha-2") do |process|
|
||||||
|
process.start_command = "./alpha"
|
||||||
|
|
||||||
|
process.working_dir = "/tmp/app"
|
||||||
|
process.daemonize = true
|
||||||
|
process.environment = {"PORT" => "5001"}
|
||||||
|
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||||
|
|
||||||
|
process.stdout = process.stderr = "/var/log/app/app-alpha-2.log"
|
||||||
|
|
||||||
|
process.monitor_children do |children|
|
||||||
|
children.stop_command "kill -QUIT {{PID}}"
|
||||||
|
end
|
||||||
|
|
||||||
|
process.group = "app-alpha"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
app.process("bravo-1") do |process|
|
||||||
|
process.start_command = "./bravo"
|
||||||
|
|
||||||
|
process.working_dir = "/tmp/app"
|
||||||
|
process.daemonize = true
|
||||||
|
process.environment = {"PORT" => "5100"}
|
||||||
|
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||||
|
|
||||||
|
process.stdout = process.stderr = "/var/log/app/app-bravo-1.log"
|
||||||
|
|
||||||
|
process.monitor_children do |children|
|
||||||
|
children.stop_command "kill -QUIT {{PID}}"
|
||||||
|
end
|
||||||
|
|
||||||
|
process.group = "app-bravo"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user