Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be73e8500f | ||
|
|
d26ca669a1 |
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
foreman (0.23.1)
|
foreman (0.24.0)
|
||||||
term-ansicolor (~> 1.0.5)
|
term-ansicolor (~> 1.0.5)
|
||||||
thor (>= 0.13.6)
|
thor (>= 0.13.6)
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
ticker: ruby ./ticker $PORT
|
ticker: ruby ./ticker $PORT
|
||||||
error : ruby ./error
|
error: ruby ./error
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
|
|||||||
app.uid = "<%= user %>"
|
app.uid = "<%= user %>"
|
||||||
app.gid = "<%= user %>"
|
app.gid = "<%= user %>"
|
||||||
|
|
||||||
<% engine.processes.values.each do |process| %>
|
<% engine.processes.each do |process| %>
|
||||||
<% 1.upto(concurrency[process.name]) do |num| %>
|
<% 1.upto(concurrency[process.name]) do |num| %>
|
||||||
<% port = engine.port_for(process, num, options[:port]) %>
|
<% port = engine.port_for(process, num, options[:port]) %>
|
||||||
app.process("<%= process.name %>-<%=num%>") do |process|
|
app.process("<%= process.name %>-<%=num%>") do |process|
|
||||||
@@ -24,4 +24,4 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
|
|||||||
end
|
end
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
end
|
end
|
||||||
|
|||||||
+2
-3
@@ -53,9 +53,8 @@ class Foreman::CLI < Thor
|
|||||||
desc "check", "Validate your application's Procfile"
|
desc "check", "Validate your application's Procfile"
|
||||||
|
|
||||||
def check
|
def check
|
||||||
processes = engine.processes_in_order.map { |p| p.first }
|
error "no processes defined" unless engine.processes.length > 0
|
||||||
error "no processes defined" unless processes.length > 0
|
display "valid procfile detected (#{engine.processes.map(&:name).join(', ')})"
|
||||||
display "valid procfile detected (#{processes.join(', ')})"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private ######################################################################
|
private ######################################################################
|
||||||
|
|||||||
+29
-72
@@ -1,5 +1,6 @@
|
|||||||
require "foreman"
|
require "foreman"
|
||||||
require "foreman/process"
|
require "foreman/process"
|
||||||
|
require "foreman/procfile"
|
||||||
require "foreman/utils"
|
require "foreman/utils"
|
||||||
require "pty"
|
require "pty"
|
||||||
require "tempfile"
|
require "tempfile"
|
||||||
@@ -19,45 +20,17 @@ class Foreman::Engine
|
|||||||
COLORS = [ cyan, yellow, green, magenta, red ]
|
COLORS = [ cyan, yellow, green, magenta, red ]
|
||||||
|
|
||||||
def initialize(procfile, options={})
|
def initialize(procfile, options={})
|
||||||
@procfile = read_procfile(procfile)
|
@procfile = Foreman::Procfile.new(procfile)
|
||||||
@directory = File.expand_path(File.dirname(procfile))
|
@directory = File.expand_path(File.dirname(procfile))
|
||||||
@options = options
|
@options = options
|
||||||
@environment = read_environment_files(options[:env])
|
@environment = read_environment_files(options[:env])
|
||||||
end
|
end
|
||||||
|
|
||||||
def processes
|
|
||||||
@processes ||= begin
|
|
||||||
@order = []
|
|
||||||
procfile.split("\n").inject({}) do |hash, line|
|
|
||||||
next hash if line.strip == ""
|
|
||||||
name, command = line.split(/\s*:\s+/, 2)
|
|
||||||
unless command
|
|
||||||
warn_deprecated_procfile!
|
|
||||||
name, command = line.split(/ +/, 2)
|
|
||||||
end
|
|
||||||
process = Foreman::Process.new(name, command)
|
|
||||||
process.color = next_color
|
|
||||||
@order << process.name
|
|
||||||
hash.update(process.name => process)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def process_order
|
|
||||||
processes
|
|
||||||
@order.uniq
|
|
||||||
end
|
|
||||||
|
|
||||||
def processes_in_order
|
|
||||||
process_order.map do |name|
|
|
||||||
[name, processes[name]]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def start
|
def start
|
||||||
proctitle "ruby: foreman master"
|
proctitle "ruby: foreman master"
|
||||||
|
|
||||||
processes_in_order.each do |name, process|
|
processes.each do |process|
|
||||||
|
process.color = next_color
|
||||||
fork process
|
fork process
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -68,8 +41,7 @@ class Foreman::Engine
|
|||||||
end
|
end
|
||||||
|
|
||||||
def execute(name)
|
def execute(name)
|
||||||
|
fork procfile[name]
|
||||||
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 }
|
||||||
@@ -77,9 +49,13 @@ class Foreman::Engine
|
|||||||
watch_for_termination
|
watch_for_termination
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def processes
|
||||||
|
procfile.processes
|
||||||
|
end
|
||||||
|
|
||||||
def port_for(process, num, base_port=nil)
|
def port_for(process, num, base_port=nil)
|
||||||
base_port ||= 5000
|
base_port ||= 5000
|
||||||
offset = processes_in_order.map { |p| p.first }.index(process.name) * 100
|
offset = procfile.process_names.index(process.name) * 100
|
||||||
base_port.to_i + offset + num - 1
|
base_port.to_i + offset + num - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -134,6 +110,24 @@ private ######################################################################
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def terminate_gracefully
|
||||||
|
info "sending SIGTERM to all processes"
|
||||||
|
kill_all "SIGTERM"
|
||||||
|
Timeout.timeout(3) { Process.waitall }
|
||||||
|
rescue Timeout::Error
|
||||||
|
info "sending SIGKILL to all processes"
|
||||||
|
kill_all "SIGKILL"
|
||||||
|
end
|
||||||
|
|
||||||
|
def watch_for_termination
|
||||||
|
pid, status = Process.wait2
|
||||||
|
process = running_processes.delete(pid)
|
||||||
|
info "process terminated", process
|
||||||
|
terminate_gracefully
|
||||||
|
kill_all
|
||||||
|
rescue Errno::ECHILD
|
||||||
|
end
|
||||||
|
|
||||||
def info(message, process=nil)
|
def info(message, process=nil)
|
||||||
print process.color if process
|
print process.color if process
|
||||||
print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(process)} | "
|
print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(process)} | "
|
||||||
@@ -149,7 +143,7 @@ private ######################################################################
|
|||||||
|
|
||||||
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 = procfile.process_names.map { |name| name.length }.sort.last
|
||||||
longest = 6 if longest < 6 # system
|
longest = 6 if longest < 6 # system
|
||||||
longest
|
longest
|
||||||
end
|
end
|
||||||
@@ -160,30 +154,10 @@ private ######################################################################
|
|||||||
name.ljust(longest_process_name + 3) # add 3 for process number padding
|
name.ljust(longest_process_name + 3) # add 3 for process number padding
|
||||||
end
|
end
|
||||||
|
|
||||||
def print_info
|
|
||||||
info "currently running processes:"
|
|
||||||
running_processes.each do |pid, process|
|
|
||||||
info "pid #{pid}", process
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def proctitle(title)
|
def proctitle(title)
|
||||||
$0 = title
|
$0 = title
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_procfile(procfile)
|
|
||||||
File.read(procfile)
|
|
||||||
end
|
|
||||||
|
|
||||||
def watch_for_termination
|
|
||||||
pid, status = Process.wait2
|
|
||||||
process = running_processes.delete(pid)
|
|
||||||
info "process terminated", process
|
|
||||||
terminate_gracefully
|
|
||||||
kill_all
|
|
||||||
rescue Errno::ECHILD
|
|
||||||
end
|
|
||||||
|
|
||||||
def running_processes
|
def running_processes
|
||||||
@running_processes ||= {}
|
@running_processes ||= {}
|
||||||
end
|
end
|
||||||
@@ -194,14 +168,6 @@ private ######################################################################
|
|||||||
@current_color >= COLORS.length ? "" : COLORS[@current_color]
|
@current_color >= COLORS.length ? "" : COLORS[@current_color]
|
||||||
end
|
end
|
||||||
|
|
||||||
def warn_deprecated_procfile!
|
|
||||||
return if @already_warned_deprecated
|
|
||||||
@already_warned_deprecated = true
|
|
||||||
puts "!!! This format of Procfile is deprecated, and will not work starting in v0.12"
|
|
||||||
puts "!!! Use a colon to separate the process name from the command"
|
|
||||||
puts "!!! e.g. web: thin start"
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_environment_files(filenames)
|
def read_environment_files(filenames)
|
||||||
environment = {}
|
environment = {}
|
||||||
|
|
||||||
@@ -225,13 +191,4 @@ private ######################################################################
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def terminate_gracefully
|
|
||||||
info "sending SIGTERM to all processes"
|
|
||||||
kill_all "SIGTERM"
|
|
||||||
Timeout.timeout(3) { Process.waitall }
|
|
||||||
rescue Timeout::Error
|
|
||||||
info "sending SIGKILL to all processes"
|
|
||||||
kill_all "SIGKILL"
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class Foreman::Export::Upstart < Foreman::Export::Base
|
|||||||
|
|
||||||
process_template = export_template("upstart", "process.conf.erb", template_root)
|
process_template = export_template("upstart", "process.conf.erb", template_root)
|
||||||
|
|
||||||
engine.processes.values.each do |process|
|
engine.processes.each do |process|
|
||||||
process_master_template = export_template("upstart", "process_master.conf.erb", template_root)
|
process_master_template = export_template("upstart", "process_master.conf.erb", template_root)
|
||||||
process_master_config = ERB.new(process_master_template).result(binding)
|
process_master_config = ERB.new(process_master_template).result(binding)
|
||||||
write_file "#{location}/#{app}-#{process.name}.conf", process_master_config
|
write_file "#{location}/#{app}-#{process.name}.conf", process_master_config
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
require "foreman"
|
||||||
|
|
||||||
|
# A valid Procfile entry is captured by this regex.
|
||||||
|
# All other lines are ignored.
|
||||||
|
#
|
||||||
|
# /^([A-Za-z0-9_]+):\s*(.+)$/
|
||||||
|
#
|
||||||
|
# $1 = name
|
||||||
|
# $2 = command
|
||||||
|
#
|
||||||
|
class Foreman::Procfile
|
||||||
|
|
||||||
|
attr_reader :processes
|
||||||
|
|
||||||
|
def initialize(filename)
|
||||||
|
@processes = parse_procfile(filename)
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_names
|
||||||
|
processes.map(&:name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def [](name)
|
||||||
|
processes.detect { |process| process.name == name }
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def parse_procfile(filename)
|
||||||
|
File.read(filename).split("\n").map do |line|
|
||||||
|
if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
|
||||||
|
Foreman::Process.new($1, $2)
|
||||||
|
end
|
||||||
|
end.compact
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
module Foreman
|
module Foreman
|
||||||
|
|
||||||
VERSION = "0.23.1"
|
VERSION = "0.24.0"
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,21 +15,8 @@ describe "Foreman::Engine" do
|
|||||||
before { write_procfile }
|
before { write_procfile }
|
||||||
|
|
||||||
it "reads the processes" do
|
it "reads the processes" do
|
||||||
subject.processes["alpha"].command.should == "./alpha"
|
subject.procfile["alpha"].command.should == "./alpha"
|
||||||
subject.processes["bravo"].command.should == "./bravo"
|
subject.procfile["bravo"].command.should == "./bravo"
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "with a deprecated Procfile" do
|
|
||||||
before do
|
|
||||||
File.open("Procfile", "w") do |file|
|
|
||||||
file.puts "name command"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should print a deprecation warning" do
|
|
||||||
mock(subject).warn_deprecated_procfile!
|
|
||||||
subject.processes.length.should == 1
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -37,8 +24,8 @@ 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.procfile["alpha"])
|
||||||
mock(subject).fork(subject.processes["bravo"])
|
mock(subject).fork(subject.procfile["bravo"])
|
||||||
mock(subject).watch_for_termination
|
mock(subject).watch_for_termination
|
||||||
subject.start
|
subject.start
|
||||||
end
|
end
|
||||||
@@ -46,9 +33,9 @@ describe "Foreman::Engine" do
|
|||||||
it "handles concurrency" do
|
it "handles concurrency" do
|
||||||
write_procfile
|
write_procfile
|
||||||
engine = Foreman::Engine.new("Procfile",: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.procfile["alpha"], 1, 5000)
|
||||||
mock(engine).fork_individual(engine.processes["alpha"], 2, 5001)
|
mock(engine).fork_individual(engine.procfile["alpha"], 2, 5001)
|
||||||
mock(engine).fork_individual(engine.processes["bravo"], 1, 5100)
|
mock(engine).fork_individual(engine.procfile["bravo"], 1, 5100)
|
||||||
mock(engine).watch_for_termination
|
mock(engine).watch_for_termination
|
||||||
engine.start
|
engine.start
|
||||||
end
|
end
|
||||||
@@ -57,14 +44,13 @@ 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.procfile["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
|
||||||
|
|||||||
Reference in New Issue
Block a user