Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
ddollar committed Dec 9, 2011
1 parent c9411cd commit 5436b68
Show file tree
Hide file tree
Showing 13 changed files with 72 additions and 79 deletions.
3 changes: 0 additions & 3 deletions bin/runner
@@ -1,5 +1,2 @@
#!/bin/sh

echo "command[$*][$1]"

exec $1 2>&1
4 changes: 2 additions & 2 deletions data/export/bluepill/master.pill.erb
Expand Up @@ -3,7 +3,7 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
app.uid = "<%= user %>"
app.gid = "<%= user %>"

<% engine.processes.each do |process| %>
<% engine.procfile.entries.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|
Expand All @@ -19,7 +19,7 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
process.monitor_children do |children|
children.stop_command "kill -QUIT {{PID}}"
end

process.group = "<%= app %>-<%= process.name %>"
end
<% end %>
Expand Down
5 changes: 5 additions & 0 deletions lib/foreman.rb
Expand Up @@ -9,5 +9,10 @@ def self.load_env!(env_file = './.env')
require 'foreman/engine'
Foreman::Engine.load_env!(env_file)
end

def self.runner
File.expand_path("../../bin/runner", __FILE__)
end

end

15 changes: 5 additions & 10 deletions lib/foreman/cli.rb
Expand Up @@ -8,20 +8,15 @@ class Foreman::CLI < Thor

class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"

desc "start [PROCESS]", "Start the application, or a specific process"

This comment has been minimized.

Copy link
@betamatt

betamatt Jan 5, 2012

Contributor

Would you mind commenting on the removal of the application parameter? Is this ability enabled by some other means now or just removed?

desc "start", "Start the application"

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 :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'

def start(process=nil)
def start
check_procfile!

if process
engine.execute(process)
else
engine.start
end
engine.start
end

desc "export FORMAT LOCATION", "Export the application to another process management format"
Expand Down Expand Up @@ -55,8 +50,8 @@ def export(format, location=nil)
desc "check", "Validate your application's Procfile"

def check
error "no processes defined" unless engine.processes.length > 0
display "valid procfile detected (#{engine.processes.map(&:name).join(', ')})"
error "no processes defined" unless engine.procfile.entries.length > 0
display "valid procfile detected (#{engine.procfile.process_names.join(', ')})"
end

private ######################################################################
Expand Down
7 changes: 5 additions & 2 deletions lib/foreman/engine.rb
Expand Up @@ -57,16 +57,19 @@ def spawn_processes

procfile.entries.each do |entry|
reader, writer = IO.pipe
entry.spawn(concurrency[entry.name], writer, @directory, @environment).each do |process|
entry.spawn(concurrency[entry.name], writer, @directory, @environment, base_port).each do |process|
running_processes[process.pid] = process
readers[process] = reader
end
end
end

def base_port
options[:port] || 5000
end

def kill_all(signal="SIGTERM")
running_processes.each do |pid, process|
p [:killing, pid]
Process.kill(signal, pid) rescue Errno::ESRCH
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/foreman/export/inittab.rb
Expand Up @@ -12,7 +12,7 @@ def export(fname=nil, options={})
inittab = []
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|
id = app.slice(0, 2).upcase + sprintf("%02d", index)
port = engine.port_for(process, num, options[:port])
Expand Down
32 changes: 16 additions & 16 deletions lib/foreman/export/runit.rb
Expand Up @@ -3,58 +3,58 @@

class Foreman::Export::Runit < Foreman::Export::Base
ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/

def export(location, options={})
error("Must specify a location") unless location

app = options[:app] || File.basename(engine.directory)
user = options[:user] || app
log_root = options[:log] || "/var/log/#{app}"
template_root = options[:template]

concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])

run_template = export_template('runit', '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|
process_directory = "#{location}/#{app}-#{process.name}-#{num}"
process_env_directory = "#{process_directory}/env"
process_log_directory = "#{process_directory}/log"

create_directory process_directory
create_directory process_env_directory
create_directory process_log_directory

run = ERB.new(run_template).result(binding)
write_file "#{process_directory}/run", run

port = engine.port_for(process, num, options[:port])
environment_variables = {'PORT' => port}.
merge(engine.environment).
merge(inline_variables(process.command))

environment_variables.each_pair do |var, env|
write_file "#{process_env_directory}/#{var.upcase}", env
end

log_run = ERB.new(log_run_template).result(binding)
write_file "#{process_log_directory}/run", log_run

end
end

end

private
def create_directory(location)
say "creating: #{location}"
FileUtils.mkdir(location)
end

def inline_variables(command)
variable_name_regex =
variable_name_regex =
Hash[*command.scan(ENV_VARIABLE_REGEX).flatten]
end
end
end
2 changes: 1 addition & 1 deletion lib/foreman/export/upstart.rb
Expand Up @@ -26,7 +26,7 @@ def export(location, options={})

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
process_master_template = export_template("upstart", "process_master.conf.erb", template_root)
process_master_config = ERB.new(process_master_template).result(binding)
Expand Down
31 changes: 19 additions & 12 deletions lib/foreman/process.rb
Expand Up @@ -5,24 +5,18 @@ class Foreman::Process
attr_reader :entry
attr_reader :num
attr_reader :pid
attr_reader :port

def initialize(entry, num)
def initialize(entry, num, port)
@entry = entry
@num = num
@port = port
end

def run(pipe, basedir, environment)
Dir.chdir(basedir) do
with_environment(environment) do
io = IO.popen(["/Users/david/Code/foreman/bin/runner", "#{entry.command}"], "w+")
@pid = io.pid
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
with_environment(environment.merge("PORT" => port.to_s)) do
run_process entry.command
end
end
end
Expand All @@ -33,11 +27,24 @@ def name

private

def run_process(command)
io = IO.popen([Foreman.runner, replace_command_env(command)], "w+")
@pid = io.pid
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
def replace_command_env(command)
command.gsub(/\$(\w+)/) { |e| ENV[e[1..-1]] }
end

def with_environment(environment)
Expand Down
4 changes: 2 additions & 2 deletions lib/foreman/procfile_entry.rb
Expand Up @@ -11,9 +11,9 @@ def initialize(name, command)
@command = command
end

def spawn(num, pipe, basedir, environment)
def spawn(num, pipe, basedir, environment, base_port)
(1..num).to_a.map do |n|
process = Foreman::Process.new(self, n)
process = Foreman::Process.new(self, n, base_port + (n-1))
process.run(pipe, basedir, environment)
process
end
Expand Down
37 changes: 12 additions & 25 deletions spec/foreman/engine_spec.rb
Expand Up @@ -24,38 +24,24 @@
describe "start" do
it "forks the processes" do
write_procfile
mock(subject).fork(subject.procfile["alpha"])
mock(subject).fork(subject.procfile["bravo"])
mock.instance_of(Foreman::Process).run_process("./alpha")
mock.instance_of(Foreman::Process).run_process("./bravo")
mock(subject).watch_for_output
mock(subject).watch_for_termination
subject.start
end

it "handles concurrency" do
write_procfile
engine = Foreman::Engine.new("Procfile",:concurrency => "alpha=2")
mock(engine).fork_individual(engine.procfile["alpha"], 1, 5000)
mock(engine).fork_individual(engine.procfile["alpha"], 2, 5001)
mock(engine).fork_individual(engine.procfile["bravo"], 1, 5100)
mock.instance_of(Foreman::Process).run_process("./alpha").twice
mock.instance_of(Foreman::Process).run_process("./bravo")
mock(engine).watch_for_output
mock(engine).watch_for_termination
engine.start
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
before(:each) do
write_procfile
Expand All @@ -66,19 +52,21 @@
File.open("/tmp/env", "w") { |f| f.puts("FOO=baz") }
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env")
stub(engine).info
mock(engine).spawn_processes
mock(engine).watch_for_termination
engine.environment.should == {"FOO"=>"baz"}
engine.execute("alpha")
engine.start
end

it "should read more than one if specified" do
File.open("/tmp/env1", "w") { |f| f.puts("FOO=bar") }
File.open("/tmp/env2", "w") { |f| f.puts("BAZ=qux") }
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env1,/tmp/env2")
stub(engine).info
mock(engine).spawn_processes
mock(engine).watch_for_termination
engine.environment.should == { "FOO"=>"bar", "BAZ"=>"qux" }
engine.execute("alpha")
engine.start
end

it "should fail if specified and doesnt exist" do
Expand All @@ -89,11 +77,10 @@
it "should read .env if none specified" do
File.open(".env", "w") { |f| f.puts("FOO=qoo") }
engine = Foreman::Engine.new("Procfile")
stub(engine).info
mock(engine).spawn_processes
mock(engine).watch_for_termination
mock(engine).fork_individual(anything, anything, anything)
engine.environment.should == {"FOO"=>"qoo"}
engine.execute("bravo")
engine.start
end
end
end
3 changes: 1 addition & 2 deletions spec/foreman/export/bluepill_spec.rb
Expand Up @@ -13,8 +13,7 @@

it "exports to the filesystem" do
bluepill.export("/tmp/init", :concurrency => "alpha=2")

File.read("/tmp/init/app.pill").should == example_export_file("bluepill/app.pill")
end

end
end
6 changes: 3 additions & 3 deletions spec/resources/export/bluepill/app.pill
Expand Up @@ -19,7 +19,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
process.monitor_children do |children|
children.stop_command "kill -QUIT {{PID}}"
end

process.group = "app-alpha"
end

Expand All @@ -37,7 +37,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
process.monitor_children do |children|
children.stop_command "kill -QUIT {{PID}}"
end

process.group = "app-alpha"
end

Expand All @@ -57,7 +57,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
process.monitor_children do |children|
children.stop_command "kill -QUIT {{PID}}"
end

process.group = "app-bravo"
end

Expand Down

2 comments on commit 5436b68

@h0jeZvgoxFepBQ2C
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you remove this command? There are several people which need this: #113

@ddollar
Copy link
Owner Author

@ddollar ddollar commented on 5436b68 Jan 5, 2012

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Starting a specific process got killed as part of a major refactor. I'll get it back in ASAP

Please sign in to comment.