Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fcfa913fb0 | ||
|
|
fc438472f9 | ||
|
|
fc95936327 | ||
|
|
0c27f78d46 | ||
|
|
356c61f471 | ||
|
|
dcff4da220 | ||
|
|
888520ee99 | ||
|
|
c7b6b334fd | ||
|
|
f476920a05 | ||
|
|
5436b68cf1 | ||
|
|
c9411cd2b1 | ||
|
|
6e95d1ce94 | ||
|
|
c5548a345e | ||
|
|
f668b87660 |
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
foreman (0.27.0)
|
foreman (0.30.1)
|
||||||
term-ansicolor (~> 1.0.5)
|
term-ansicolor (~> 1.0.5)
|
||||||
thor (>= 0.13.6)
|
thor (>= 0.13.6)
|
||||||
|
|
||||||
|
|||||||
Executable
+2
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
exec $1 2>&1
|
||||||
@@ -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.each do |process| %>
|
<% engine.procfile.entries.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|
|
||||||
@@ -19,7 +19,7 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
|
|||||||
process.monitor_children do |children|
|
process.monitor_children do |children|
|
||||||
children.stop_command "kill -QUIT {{PID}}"
|
children.stop_command "kill -QUIT {{PID}}"
|
||||||
end
|
end
|
||||||
|
|
||||||
process.group = "<%= app %>-<%= process.name %>"
|
process.group = "<%= app %>-<%= process.name %>"
|
||||||
end
|
end
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -9,5 +9,10 @@ module Foreman
|
|||||||
require 'foreman/engine'
|
require 'foreman/engine'
|
||||||
Foreman::Engine.load_env!(env_file)
|
Foreman::Engine.load_env!(env_file)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.runner
|
||||||
|
File.expand_path("../../bin/runner", __FILE__)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
+5
-10
@@ -8,20 +8,15 @@ class Foreman::CLI < Thor
|
|||||||
|
|
||||||
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
|
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
|
||||||
|
|
||||||
desc "start [PROCESS]", "Start the application, or a specific process"
|
desc "start", "Start the application"
|
||||||
|
|
||||||
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
|
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
|
||||||
method_option :port, :type => :numeric, :aliases => "-p"
|
method_option :port, :type => :numeric, :aliases => "-p"
|
||||||
method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
|
method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
|
||||||
|
|
||||||
def start(process=nil)
|
def start
|
||||||
check_procfile!
|
check_procfile!
|
||||||
|
engine.start
|
||||||
if process
|
|
||||||
engine.execute(process)
|
|
||||||
else
|
|
||||||
engine.start
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "export FORMAT LOCATION", "Export the application to another process management format"
|
desc "export FORMAT LOCATION", "Export the application to another process management format"
|
||||||
@@ -55,8 +50,8 @@ class Foreman::CLI < Thor
|
|||||||
desc "check", "Validate your application's Procfile"
|
desc "check", "Validate your application's Procfile"
|
||||||
|
|
||||||
def check
|
def check
|
||||||
error "no processes defined" unless engine.processes.length > 0
|
error "no processes defined" unless engine.procfile.entries.length > 0
|
||||||
display "valid procfile detected (#{engine.processes.map(&:name).join(', ')})"
|
display "valid procfile detected (#{engine.procfile.process_names.join(', ')})"
|
||||||
end
|
end
|
||||||
|
|
||||||
private ######################################################################
|
private ######################################################################
|
||||||
|
|||||||
+71
-66
@@ -7,6 +7,7 @@ require "tempfile"
|
|||||||
require "timeout"
|
require "timeout"
|
||||||
require "term/ansicolor"
|
require "term/ansicolor"
|
||||||
require "fileutils"
|
require "fileutils"
|
||||||
|
require "thread"
|
||||||
|
|
||||||
class Foreman::Engine
|
class Foreman::Engine
|
||||||
|
|
||||||
@@ -23,6 +24,7 @@ class Foreman::Engine
|
|||||||
@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])
|
||||||
|
@output_mutex = Mutex.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.load_env!(env_file)
|
def self.load_env!(env_file)
|
||||||
@@ -32,34 +34,17 @@ class Foreman::Engine
|
|||||||
|
|
||||||
def start
|
def start
|
||||||
proctitle "ruby: foreman master"
|
proctitle "ruby: foreman master"
|
||||||
termtitle "#{File.basename(@directory)} - foreman (#{processes.size} processes)"
|
termtitle "#{File.basename(@directory)} - foreman"
|
||||||
|
|
||||||
processes.each do |process|
|
|
||||||
process.color = next_color
|
|
||||||
fork process
|
|
||||||
end
|
|
||||||
|
|
||||||
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 }
|
||||||
|
|
||||||
|
assign_colors
|
||||||
|
spawn_processes
|
||||||
|
watch_for_output
|
||||||
watch_for_termination
|
watch_for_termination
|
||||||
end
|
end
|
||||||
|
|
||||||
def execute(name)
|
|
||||||
error "no such process: #{name}" unless process = procfile[name]
|
|
||||||
process.color = next_color
|
|
||||||
fork process
|
|
||||||
|
|
||||||
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
|
||||||
trap("INT") { puts "SIGINT received"; terminate_gracefully }
|
|
||||||
|
|
||||||
watch_for_termination
|
|
||||||
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 = procfile.process_names.index(process.name) * 100
|
offset = procfile.process_names.index(process.name) * 100
|
||||||
@@ -68,47 +53,20 @@ class Foreman::Engine
|
|||||||
|
|
||||||
private ######################################################################
|
private ######################################################################
|
||||||
|
|
||||||
def fork(process)
|
def spawn_processes
|
||||||
concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency])
|
concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency])
|
||||||
|
|
||||||
1.upto(concurrency[process.name]) do |num|
|
procfile.entries.each do |entry|
|
||||||
fork_individual(process, num, port_for(process, num, @options[:port]))
|
reader, writer = IO.pipe
|
||||||
|
entry.spawn(concurrency[entry.name], writer, @directory, @environment, base_port).each do |process|
|
||||||
|
running_processes[process.pid] = process
|
||||||
|
readers[process] = reader
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fork_individual(process, num, port)
|
def base_port
|
||||||
apply_environment!
|
options[:port] || 5000
|
||||||
|
|
||||||
ENV["PORT"] = port.to_s
|
|
||||||
ENV["PS"] = "#{process.name}.#{num}"
|
|
||||||
|
|
||||||
pid = Process.fork do
|
|
||||||
run(process)
|
|
||||||
end
|
|
||||||
|
|
||||||
info "started with pid #{pid}", process
|
|
||||||
running_processes[pid] = process
|
|
||||||
end
|
|
||||||
|
|
||||||
def run(process)
|
|
||||||
proctitle "ruby: foreman #{process.name}"
|
|
||||||
trap("SIGINT", "IGNORE")
|
|
||||||
|
|
||||||
begin
|
|
||||||
Dir.chdir directory do
|
|
||||||
PTY.spawn(process.command) do |stdin, stdout, pid|
|
|
||||||
trap("SIGTERM") { Process.kill("SIGTERM", pid) }
|
|
||||||
until stdin.eof?
|
|
||||||
info stdin.gets, process
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
rescue PTY::ChildExited, Interrupt, Errno::EIO, Errno::ENOENT
|
|
||||||
begin
|
|
||||||
info "process exiting", process
|
|
||||||
rescue Interrupt
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def kill_all(signal="SIGTERM")
|
def kill_all(signal="SIGTERM")
|
||||||
@@ -120,27 +78,57 @@ private ######################################################################
|
|||||||
def terminate_gracefully
|
def terminate_gracefully
|
||||||
info "sending SIGTERM to all processes"
|
info "sending SIGTERM to all processes"
|
||||||
kill_all "SIGTERM"
|
kill_all "SIGTERM"
|
||||||
Timeout.timeout(3) { Process.waitall }
|
Timeout.timeout(5) { Process.waitall }
|
||||||
rescue Timeout::Error
|
rescue Timeout::Error
|
||||||
info "sending SIGKILL to all processes"
|
info "sending SIGKILL to all processes"
|
||||||
kill_all "SIGKILL"
|
kill_all "SIGKILL"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def watch_for_output
|
||||||
|
Thread.new do
|
||||||
|
begin
|
||||||
|
loop do
|
||||||
|
rs, ws = IO.select(readers.values, [], [], 1)
|
||||||
|
(rs || []).each do |r|
|
||||||
|
ps, message = r.gets.split(",", 2)
|
||||||
|
color = colors[ps.split(".").first]
|
||||||
|
info message, ps, color
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue Exception => ex
|
||||||
|
puts ex.message
|
||||||
|
puts ex.backtrace
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def watch_for_termination
|
def watch_for_termination
|
||||||
pid, status = Process.wait2
|
pid, status = Process.wait2
|
||||||
process = running_processes.delete(pid)
|
process = running_processes.delete(pid)
|
||||||
info "process terminated", process
|
info "process terminated", process.name
|
||||||
terminate_gracefully
|
terminate_gracefully
|
||||||
kill_all
|
kill_all
|
||||||
rescue Errno::ECHILD
|
rescue Errno::ECHILD
|
||||||
end
|
end
|
||||||
|
|
||||||
def info(message, process=nil)
|
def info(message, name="system", color=Term::ANSIColor.white)
|
||||||
print process.color if process
|
print color
|
||||||
print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(process)} | "
|
print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
|
||||||
print Term::ANSIColor.reset
|
print Term::ANSIColor.reset
|
||||||
print message.chomp
|
print message.chomp
|
||||||
puts
|
puts ""
|
||||||
|
end
|
||||||
|
|
||||||
|
def print(message=nil)
|
||||||
|
@output_mutex.synchronize do
|
||||||
|
$stdout.print message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def puts(message=nil)
|
||||||
|
@output_mutex.synchronize do
|
||||||
|
$stdout.puts message
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def error(message)
|
def error(message)
|
||||||
@@ -156,9 +144,8 @@ private ######################################################################
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def pad_process_name(process)
|
def pad_process_name(name="system")
|
||||||
name = process ? "#{ENV["PS"]}" : "system"
|
name.to_s.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 proctitle(title)
|
def proctitle(title)
|
||||||
@@ -173,6 +160,24 @@ private ######################################################################
|
|||||||
@running_processes ||= {}
|
@running_processes ||= {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def readers
|
||||||
|
@readers ||= {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def colors
|
||||||
|
@colors ||= {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def assign_colors
|
||||||
|
procfile.entries.each do |entry|
|
||||||
|
colors[entry.name] = next_color
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_by_reader(reader)
|
||||||
|
readers.invert[reader]
|
||||||
|
end
|
||||||
|
|
||||||
def next_color
|
def next_color
|
||||||
@current_color ||= -1
|
@current_color ||= -1
|
||||||
@current_color += 1
|
@current_color += 1
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class Foreman::Export::Inittab < Foreman::Export::Base
|
|||||||
inittab = []
|
inittab = []
|
||||||
inittab << "# ----- foreman #{app} processes -----"
|
inittab << "# ----- foreman #{app} processes -----"
|
||||||
|
|
||||||
engine.processes.inject(1) do |index, process|
|
engine.procfile.entries.inject(1) do |index, process|
|
||||||
1.upto(concurrency[process.name]) do |num|
|
1.upto(concurrency[process.name]) do |num|
|
||||||
id = app.slice(0, 2).upcase + sprintf("%02d", index)
|
id = app.slice(0, 2).upcase + sprintf("%02d", index)
|
||||||
port = engine.port_for(process, num, options[:port])
|
port = engine.port_for(process, num, options[:port])
|
||||||
|
|||||||
+16
-16
@@ -3,58 +3,58 @@ require "foreman/export"
|
|||||||
|
|
||||||
class Foreman::Export::Runit < Foreman::Export::Base
|
class Foreman::Export::Runit < Foreman::Export::Base
|
||||||
ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
|
ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
|
||||||
|
|
||||||
def export(location, options={})
|
def export(location, options={})
|
||||||
error("Must specify a location") unless location
|
error("Must specify a location") unless location
|
||||||
|
|
||||||
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/#{app}"
|
log_root = options[:log] || "/var/log/#{app}"
|
||||||
template_root = options[:template]
|
template_root = options[:template]
|
||||||
|
|
||||||
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
||||||
|
|
||||||
run_template = export_template('runit', 'run.erb', template_root)
|
run_template = export_template('runit', 'run.erb', template_root)
|
||||||
log_run_template = export_template('runit', 'log_run.erb', template_root)
|
log_run_template = export_template('runit', 'log_run.erb', template_root)
|
||||||
|
|
||||||
engine.processes.each do |process|
|
engine.procfile.entries.each do |process|
|
||||||
1.upto(concurrency[process.name]) do |num|
|
1.upto(concurrency[process.name]) do |num|
|
||||||
process_directory = "#{location}/#{app}-#{process.name}-#{num}"
|
process_directory = "#{location}/#{app}-#{process.name}-#{num}"
|
||||||
process_env_directory = "#{process_directory}/env"
|
process_env_directory = "#{process_directory}/env"
|
||||||
process_log_directory = "#{process_directory}/log"
|
process_log_directory = "#{process_directory}/log"
|
||||||
|
|
||||||
create_directory process_directory
|
create_directory process_directory
|
||||||
create_directory process_env_directory
|
create_directory process_env_directory
|
||||||
create_directory process_log_directory
|
create_directory process_log_directory
|
||||||
|
|
||||||
run = ERB.new(run_template).result(binding)
|
run = ERB.new(run_template).result(binding)
|
||||||
write_file "#{process_directory}/run", run
|
write_file "#{process_directory}/run", run
|
||||||
|
|
||||||
port = engine.port_for(process, num, options[:port])
|
port = engine.port_for(process, num, options[:port])
|
||||||
environment_variables = {'PORT' => port}.
|
environment_variables = {'PORT' => port}.
|
||||||
merge(engine.environment).
|
merge(engine.environment).
|
||||||
merge(inline_variables(process.command))
|
merge(inline_variables(process.command))
|
||||||
|
|
||||||
environment_variables.each_pair do |var, env|
|
environment_variables.each_pair do |var, env|
|
||||||
write_file "#{process_env_directory}/#{var.upcase}", env
|
write_file "#{process_env_directory}/#{var.upcase}", env
|
||||||
end
|
end
|
||||||
|
|
||||||
log_run = ERB.new(log_run_template).result(binding)
|
log_run = ERB.new(log_run_template).result(binding)
|
||||||
write_file "#{process_log_directory}/run", log_run
|
write_file "#{process_log_directory}/run", log_run
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def create_directory(location)
|
def create_directory(location)
|
||||||
say "creating: #{location}"
|
say "creating: #{location}"
|
||||||
FileUtils.mkdir(location)
|
FileUtils.mkdir(location)
|
||||||
end
|
end
|
||||||
|
|
||||||
def inline_variables(command)
|
def inline_variables(command)
|
||||||
variable_name_regex =
|
variable_name_regex =
|
||||||
Hash[*command.scan(ENV_VARIABLE_REGEX).flatten]
|
Hash[*command.scan(ENV_VARIABLE_REGEX).flatten]
|
||||||
end
|
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.each do |process|
|
engine.procfile.entries.each do |process|
|
||||||
next if (conc = concurrency[process.name]) < 1
|
next if (conc = concurrency[process.name]) < 1
|
||||||
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)
|
||||||
|
|||||||
+63
-6
@@ -2,13 +2,70 @@ require "foreman"
|
|||||||
|
|
||||||
class Foreman::Process
|
class Foreman::Process
|
||||||
|
|
||||||
attr_reader :name
|
attr_reader :entry
|
||||||
attr_reader :command
|
attr_reader :num
|
||||||
attr_accessor :color
|
attr_reader :pid
|
||||||
|
attr_reader :port
|
||||||
|
|
||||||
def initialize(name, command)
|
def initialize(entry, num, port)
|
||||||
@name = name
|
@entry = entry
|
||||||
@command = command
|
@num = num
|
||||||
|
@port = port
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(pipe, basedir, environment)
|
||||||
|
Dir.chdir(basedir) do
|
||||||
|
with_environment(environment.merge("PORT" => port.to_s)) do
|
||||||
|
run_process entry.command, pipe
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def name
|
||||||
|
"%s.%s" % [ entry.name, num ]
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def fork_with_io(command)
|
||||||
|
io = case RUBY_VERSION
|
||||||
|
when /^1\.9\./
|
||||||
|
IO.popen([Foreman.runner, replace_command_env(command)], "w+")
|
||||||
|
when /^1\.8\./
|
||||||
|
full_command = replace_command_env(command).gsub("'", "\\'")
|
||||||
|
IO.popen("#{Foreman.runner} '#{full_command}'", "w+")
|
||||||
|
else
|
||||||
|
raise "Unknown Ruby version: #{RUBY_VERSION}"
|
||||||
|
end
|
||||||
|
[ io, io.pid ]
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_process(command, pipe)
|
||||||
|
io, @pid = fork_with_io(command)
|
||||||
|
trap("SIGTERM") { "got sigterm for %d" % @pid }
|
||||||
|
output pipe, "started with pid %d" % @pid
|
||||||
|
Thread.new do
|
||||||
|
until io.eof?
|
||||||
|
output pipe, io.gets
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def output(pipe, message)
|
||||||
|
pipe.puts "%s,%s" % [ name, message ]
|
||||||
|
end
|
||||||
|
|
||||||
|
def replace_command_env(command)
|
||||||
|
command.gsub(/\$(\w+)/) { |e| ENV[e[1..-1]] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def with_environment(environment)
|
||||||
|
old_env = ENV.each_pair.inject({}) { |h,(k,v)| h.update(k => v) }
|
||||||
|
environment.each { |k,v| ENV[k] = v }
|
||||||
|
ret = yield
|
||||||
|
ENV.clear
|
||||||
|
old_env.each { |k,v| ENV[k] = v}
|
||||||
|
ret
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
require "foreman"
|
require "foreman"
|
||||||
|
require "foreman/procfile_entry"
|
||||||
|
|
||||||
# A valid Procfile entry is captured by this regex.
|
# A valid Procfile entry is captured by this regex.
|
||||||
# All other lines are ignored.
|
# All other lines are ignored.
|
||||||
@@ -10,18 +11,18 @@ require "foreman"
|
|||||||
#
|
#
|
||||||
class Foreman::Procfile
|
class Foreman::Procfile
|
||||||
|
|
||||||
attr_reader :processes
|
attr_reader :entries
|
||||||
|
|
||||||
def initialize(filename)
|
def initialize(filename)
|
||||||
@processes = parse_procfile(filename)
|
@entries = parse_procfile(filename)
|
||||||
end
|
|
||||||
|
|
||||||
def process_names
|
|
||||||
processes.map(&:name)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def [](name)
|
def [](name)
|
||||||
processes.detect { |process| process.name == name }
|
entries.detect { |entry| entry.name == name }
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_names
|
||||||
|
entries.map(&:name)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@@ -29,7 +30,7 @@ private
|
|||||||
def parse_procfile(filename)
|
def parse_procfile(filename)
|
||||||
File.read(filename).split("\n").map do |line|
|
File.read(filename).split("\n").map do |line|
|
||||||
if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
|
if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
|
||||||
Foreman::Process.new($1, $2)
|
Foreman::ProcfileEntry.new($1, $2)
|
||||||
end
|
end
|
||||||
end.compact
|
end.compact
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
require "foreman"
|
||||||
|
|
||||||
|
class Foreman::ProcfileEntry
|
||||||
|
|
||||||
|
attr_reader :name
|
||||||
|
attr_reader :command
|
||||||
|
attr_accessor :color
|
||||||
|
|
||||||
|
def initialize(name, command)
|
||||||
|
@name = name
|
||||||
|
@command = command
|
||||||
|
end
|
||||||
|
|
||||||
|
def spawn(num, pipe, basedir, environment, base_port)
|
||||||
|
(1..num).to_a.map do |n|
|
||||||
|
process = Foreman::Process.new(self, n, base_port + (n-1))
|
||||||
|
process.run(pipe, basedir, environment)
|
||||||
|
process
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
module Foreman
|
module Foreman
|
||||||
|
|
||||||
VERSION = "0.27.0"
|
VERSION = "0.30.1"
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
+12
-25
@@ -24,8 +24,9 @@ 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.procfile["alpha"])
|
mock.instance_of(Foreman::Process).run_process("./alpha", is_a(IO))
|
||||||
mock(subject).fork(subject.procfile["bravo"])
|
mock.instance_of(Foreman::Process).run_process("./bravo", is_a(IO))
|
||||||
|
mock(subject).watch_for_output
|
||||||
mock(subject).watch_for_termination
|
mock(subject).watch_for_termination
|
||||||
subject.start
|
subject.start
|
||||||
end
|
end
|
||||||
@@ -33,29 +34,14 @@ 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.procfile["alpha"], 1, 5000)
|
mock.instance_of(Foreman::Process).run_process("./alpha", is_a(IO)).twice
|
||||||
mock(engine).fork_individual(engine.procfile["alpha"], 2, 5001)
|
mock.instance_of(Foreman::Process).run_process("./bravo", is_a(IO))
|
||||||
mock(engine).fork_individual(engine.procfile["bravo"], 1, 5100)
|
mock(engine).watch_for_output
|
||||||
mock(engine).watch_for_termination
|
mock(engine).watch_for_termination
|
||||||
engine.start
|
engine.start
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "execute" do
|
|
||||||
it "runs the processes" do
|
|
||||||
write_procfile
|
|
||||||
mock(subject).fork(subject.procfile["alpha"])
|
|
||||||
mock(subject).watch_for_termination
|
|
||||||
subject.execute("alpha")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "shows an error running a process that doesnt exist" do
|
|
||||||
write_procfile
|
|
||||||
mock(subject).puts("ERROR: no such process: foo")
|
|
||||||
lambda { subject.execute("foo") }.should raise_error(SystemExit)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "environment" do
|
describe "environment" do
|
||||||
before(:each) do
|
before(:each) do
|
||||||
write_procfile
|
write_procfile
|
||||||
@@ -66,9 +52,10 @@ describe "Foreman::Engine" do
|
|||||||
File.open("/tmp/env", "w") { |f| f.puts("FOO=baz") }
|
File.open("/tmp/env", "w") { |f| f.puts("FOO=baz") }
|
||||||
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env")
|
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env")
|
||||||
stub(engine).info
|
stub(engine).info
|
||||||
|
mock(engine).spawn_processes
|
||||||
mock(engine).watch_for_termination
|
mock(engine).watch_for_termination
|
||||||
engine.environment.should == {"FOO"=>"baz"}
|
engine.environment.should == {"FOO"=>"baz"}
|
||||||
engine.execute("alpha")
|
engine.start
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should read more than one if specified" do
|
it "should read more than one if specified" do
|
||||||
@@ -76,9 +63,10 @@ describe "Foreman::Engine" do
|
|||||||
File.open("/tmp/env2", "w") { |f| f.puts("BAZ=qux") }
|
File.open("/tmp/env2", "w") { |f| f.puts("BAZ=qux") }
|
||||||
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env1,/tmp/env2")
|
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env1,/tmp/env2")
|
||||||
stub(engine).info
|
stub(engine).info
|
||||||
|
mock(engine).spawn_processes
|
||||||
mock(engine).watch_for_termination
|
mock(engine).watch_for_termination
|
||||||
engine.environment.should == { "FOO"=>"bar", "BAZ"=>"qux" }
|
engine.environment.should == { "FOO"=>"bar", "BAZ"=>"qux" }
|
||||||
engine.execute("alpha")
|
engine.start
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should fail if specified and doesnt exist" do
|
it "should fail if specified and doesnt exist" do
|
||||||
@@ -89,11 +77,10 @@ describe "Foreman::Engine" do
|
|||||||
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") }
|
||||||
engine = Foreman::Engine.new("Procfile")
|
engine = Foreman::Engine.new("Procfile")
|
||||||
stub(engine).info
|
mock(engine).spawn_processes
|
||||||
mock(engine).watch_for_termination
|
mock(engine).watch_for_termination
|
||||||
mock(engine).fork_individual(anything, anything, anything)
|
|
||||||
engine.environment.should == {"FOO"=>"qoo"}
|
engine.environment.should == {"FOO"=>"qoo"}
|
||||||
engine.execute("bravo")
|
engine.start
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ describe Foreman::Export::Bluepill do
|
|||||||
|
|
||||||
it "exports to the filesystem" do
|
it "exports to the filesystem" do
|
||||||
bluepill.export("/tmp/init", :concurrency => "alpha=2")
|
bluepill.export("/tmp/init", :concurrency => "alpha=2")
|
||||||
|
|
||||||
File.read("/tmp/init/app.pill").should == example_export_file("bluepill/app.pill")
|
File.read("/tmp/init/app.pill").should == example_export_file("bluepill/app.pill")
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
|
|||||||
process.monitor_children do |children|
|
process.monitor_children do |children|
|
||||||
children.stop_command "kill -QUIT {{PID}}"
|
children.stop_command "kill -QUIT {{PID}}"
|
||||||
end
|
end
|
||||||
|
|
||||||
process.group = "app-alpha"
|
process.group = "app-alpha"
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
|
|||||||
process.monitor_children do |children|
|
process.monitor_children do |children|
|
||||||
children.stop_command "kill -QUIT {{PID}}"
|
children.stop_command "kill -QUIT {{PID}}"
|
||||||
end
|
end
|
||||||
|
|
||||||
process.group = "app-alpha"
|
process.group = "app-alpha"
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
|
|||||||
process.monitor_children do |children|
|
process.monitor_children do |children|
|
||||||
children.stop_command "kill -QUIT {{PID}}"
|
children.stop_command "kill -QUIT {{PID}}"
|
||||||
end
|
end
|
||||||
|
|
||||||
process.group = "app-bravo"
|
process.group = "app-bravo"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user