Compare commits

..

14 Commits

Author SHA1 Message Date
David Dollar
60fb125b51 update manual 2011-09-09 16:59:19 -04:00
David Dollar
69d596bb8b add bluepill export to docs 2011-09-09 16:59:09 -04:00
David Dollar
cbd5a6344c 0.21.0 2011-09-09 16:59:01 -04:00
David Dollar
4230455859 Merge branch 'master' of github.com:ddollar/foreman 2011-09-09 16:56:32 -04:00
David Dollar
b25dfee62d Merge pull request #59 from hunter/bluepill
Bluepill export
2011-09-09 13:56:02 -07:00
David Dollar
b73da71c9c update rake 2011-09-09 16:54:37 -04:00
David Dollar
43558758fc Merge pull request #67 from thommay/env-for-upstart
Env for upstart
2011-09-09 13:52:32 -07:00
Thom May
6da8aca609 foreman erb doesn't have the -%> extension enabled. 2011-09-07 15:40:33 +01:00
Thom May
9df93a64cc Export environment to upstart jobs 2011-09-07 15:40:17 +01:00
Thom May
8fd9b753f4 Actually test that the environment is set correctly 2011-09-06 15:59:26 +01:00
Thom May
7fc6d02e7b Read environment at initialisation
This allows us to expose the environment attribute from the engine
object and utilise it to build exported startup files.
2011-09-01 17:26:50 +01:00
Thom May
ddcaac8749 Move options into class initialisation 2011-09-01 17:06:37 +01:00
Hunter Nield
9db97abb10 Updates to get Bluepill export working & tweaks to output 2011-08-25 21:17:00 +10:00
Hunter Nield
21a041527c Added basic support for Bluepill 2011-08-22 21:24:34 +10:00
14 changed files with 195 additions and 38 deletions

View File

@@ -1,7 +1,7 @@
PATH
remote: .
specs:
foreman (0.20.0)
foreman (0.21.0)
term-ansicolor (~> 1.0.5)
thor (>= 0.13.6)
@@ -18,7 +18,7 @@ GEM
crack
rest-client
thor
rake (0.8.7)
rake (0.9.2)
rcov (0.9.8)
rdiscount (1.6.5)
rest-client (1.6.1)

View File

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

View File

@@ -2,4 +2,4 @@ start on starting <%= app %>-<%= process.name %>
stop on stopping <%= app %>-<%= process.name %>
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'

View File

@@ -18,9 +18,9 @@ class Foreman::CLI < Thor
check_procfile!
if process
engine.execute(process, options)
engine.execute(process)
else
engine.start(options)
engine.start
end
end
@@ -40,6 +40,7 @@ class Foreman::CLI < Thor
formatter = case format
when "inittab" then Foreman::Export::Inittab
when "upstart" then Foreman::Export::Upstart
when "bluepill" then Foreman::Export::Bluepill
else error "Unknown export format: #{format}."
end
@@ -64,7 +65,7 @@ private ######################################################################
end
def engine
@engine ||= Foreman::Engine.new(procfile)
@engine ||= Foreman::Engine.new(procfile, options)
end
def procfile

View File

@@ -11,14 +11,18 @@ class Foreman::Engine
attr_reader :procfile
attr_reader :directory
attr_reader :environment
attr_reader :options
extend Term::ANSIColor
COLORS = [ cyan, yellow, green, magenta, red ]
def initialize(procfile)
def initialize(procfile, options={})
@procfile = read_procfile(procfile)
@directory = File.expand_path(File.dirname(procfile))
@options = options
@environment = read_environment(options[:env])
end
def processes
@@ -50,13 +54,11 @@ class Foreman::Engine
end
end
def start(options={})
environment = read_environment(options[:env])
def start
proctitle "ruby: foreman master"
processes_in_order.each do |name, process|
fork process, options, environment
fork process
end
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
@@ -65,10 +67,9 @@ class Foreman::Engine
watch_for_termination
end
def execute(name, options={})
environment = read_environment(options[:env])
def execute(name)
fork processes[name], options, environment
fork processes[name]
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
trap("INT") { puts "SIGINT received"; terminate_gracefully }
@@ -84,16 +85,16 @@ class Foreman::Engine
private ######################################################################
def fork(process, options={}, environment={})
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
def fork(process)
concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency])
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
def fork_individual(process, num, port, environment)
environment.each { |k,v| ENV[k] = v }
def fork_individual(process, num, port)
@environment.each { |k,v| ENV[k] = v }
ENV["PORT"] = port.to_s
ENV["PS"] = "#{process.name}.#{num}"

View File

@@ -7,3 +7,4 @@ end
require "foreman/export/base"
require "foreman/export/inittab"
require "foreman/export/upstart"
require "foreman/export/bluepill"

View File

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

View File

@@ -1,5 +1,5 @@
module Foreman
VERSION = "0.20.0"
VERSION = "0.21.0"
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" "August 2011" "Foreman 0.19.0" "Foreman Manual"
.TH "FOREMAN" "1" "September 2011" "Foreman 0.21.0" "Foreman Manual"
.
.SH "NAME"
\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:
.
.IP "\(bu" 4
bluepill
.
.IP "\(bu" 4
inittab
.
.IP "\(bu" 4

View File

@@ -78,6 +78,8 @@ These options control all modes of foreman's operation.
foreman currently supports the following output formats:
* bluepill
* inittab
* upstart

View File

@@ -19,7 +19,7 @@ describe "Foreman::CLI" do
it "runs successfully" do
dont_allow(subject).error
mock.instance_of(Foreman::Engine).start({})
mock.instance_of(Foreman::Engine).start
subject.start
end
end

View File

@@ -2,7 +2,7 @@ require "spec_helper"
require "foreman/engine"
describe "Foreman::Engine" do
subject { Foreman::Engine.new("Procfile") }
subject { Foreman::Engine.new("Procfile", {}) }
describe "initialize" do
describe "without an existing Procfile" do
@@ -37,53 +37,61 @@ describe "Foreman::Engine" do
describe "start" do
it "forks the processes" do
write_procfile
mock(subject).fork(subject.processes["alpha"], {}, {})
mock(subject).fork(subject.processes["bravo"], {}, {})
mock(subject).fork(subject.processes["alpha"])
mock(subject).fork(subject.processes["bravo"])
mock(subject).watch_for_termination
subject.start
end
it "handles concurrency" do
write_procfile
mock(subject).fork_individual(subject.processes["alpha"], 1, 5000, {})
mock(subject).fork_individual(subject.processes["alpha"], 2, 5001, {})
mock(subject).fork_individual(subject.processes["bravo"], 1, 5100, {})
mock(subject).watch_for_termination
subject.start(:concurrency => "alpha=2")
engine = Foreman::Engine.new("Procfile",:concurrency => "alpha=2")
mock(engine).fork_individual(engine.processes["alpha"], 1, 5000)
mock(engine).fork_individual(engine.processes["alpha"], 2, 5001)
mock(engine).fork_individual(engine.processes["bravo"], 1, 5100)
mock(engine).watch_for_termination
engine.start
end
end
describe "execute" do
it "runs the processes" do
write_procfile
mock(subject).fork(subject.processes["alpha"], {}, {})
mock(subject).fork(subject.processes["alpha"])
mock(subject).watch_for_termination
subject.execute("alpha")
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")
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
it "should fail if specified and doesnt exist" do
mock(subject).error("No such file: /tmp/env")
subject.execute("alpha", :env => "/tmp/env")
mock.instance_of(Foreman::Engine).error("No such file: /tmp/env")
engine = Foreman::Engine.new("Procfile", :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")
engine = Foreman::Engine.new("Procfile")
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

View File

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

View File

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