From 5436b68cf158ee2a8209399d669847fda4c5f336 Mon Sep 17 00:00:00 2001 From: David Dollar Date: Thu, 8 Dec 2011 17:53:13 -0800 Subject: [PATCH] wip --- bin/runner | 3 -- data/export/bluepill/master.pill.erb | 4 +-- lib/foreman.rb | 5 ++++ lib/foreman/cli.rb | 15 ++++------ lib/foreman/engine.rb | 7 +++-- lib/foreman/export/inittab.rb | 2 +- lib/foreman/export/runit.rb | 32 ++++++++++----------- lib/foreman/export/upstart.rb | 2 +- lib/foreman/process.rb | 31 +++++++++++++-------- lib/foreman/procfile_entry.rb | 4 +-- spec/foreman/engine_spec.rb | 37 ++++++++----------------- spec/foreman/export/bluepill_spec.rb | 3 +- spec/resources/export/bluepill/app.pill | 6 ++-- 13 files changed, 72 insertions(+), 79 deletions(-) diff --git a/bin/runner b/bin/runner index 80fabc9..69011a5 100755 --- a/bin/runner +++ b/bin/runner @@ -1,5 +1,2 @@ #!/bin/sh - -echo "command[$*][$1]" - exec $1 2>&1 diff --git a/data/export/bluepill/master.pill.erb b/data/export/bluepill/master.pill.erb index fbe11e1..2b56d9d 100644 --- a/data/export/bluepill/master.pill.erb +++ b/data/export/bluepill/master.pill.erb @@ -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| @@ -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 %> diff --git a/lib/foreman.rb b/lib/foreman.rb index 422bdd4..3c8f011 100644 --- a/lib/foreman.rb +++ b/lib/foreman.rb @@ -9,5 +9,10 @@ module Foreman require 'foreman/engine' Foreman::Engine.load_env!(env_file) end + + def self.runner + File.expand_path("../../bin/runner", __FILE__) + end + end diff --git a/lib/foreman/cli.rb b/lib/foreman/cli.rb index a32e156..0e82a04 100644 --- a/lib/foreman/cli.rb +++ b/lib/foreman/cli.rb @@ -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" + 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" @@ -55,8 +50,8 @@ class Foreman::CLI < Thor 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 ###################################################################### diff --git a/lib/foreman/engine.rb b/lib/foreman/engine.rb index 5d0d75e..1e622dc 100644 --- a/lib/foreman/engine.rb +++ b/lib/foreman/engine.rb @@ -57,16 +57,19 @@ private ###################################################################### 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 diff --git a/lib/foreman/export/inittab.rb b/lib/foreman/export/inittab.rb index 8dd4811..e8fb7a8 100644 --- a/lib/foreman/export/inittab.rb +++ b/lib/foreman/export/inittab.rb @@ -12,7 +12,7 @@ class Foreman::Export::Inittab < Foreman::Export::Base 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]) diff --git a/lib/foreman/export/runit.rb b/lib/foreman/export/runit.rb index 43807ab..bc7a5f5 100644 --- a/lib/foreman/export/runit.rb +++ b/lib/foreman/export/runit.rb @@ -3,58 +3,58 @@ require "foreman/export" 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 \ No newline at end of file +end diff --git a/lib/foreman/export/upstart.rb b/lib/foreman/export/upstart.rb index 47f8df4..9ae9717 100644 --- a/lib/foreman/export/upstart.rb +++ b/lib/foreman/export/upstart.rb @@ -26,7 +26,7 @@ class Foreman::Export::Upstart < Foreman::Export::Base 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) diff --git a/lib/foreman/process.rb b/lib/foreman/process.rb index 71b8e74..f505584 100644 --- a/lib/foreman/process.rb +++ b/lib/foreman/process.rb @@ -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 @@ -33,11 +27,24 @@ class Foreman::Process 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) diff --git a/lib/foreman/procfile_entry.rb b/lib/foreman/procfile_entry.rb index a7fd4f1..1d2223d 100644 --- a/lib/foreman/procfile_entry.rb +++ b/lib/foreman/procfile_entry.rb @@ -11,9 +11,9 @@ class Foreman::ProcfileEntry @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 diff --git a/spec/foreman/engine_spec.rb b/spec/foreman/engine_spec.rb index 7e60100..42dc939 100644 --- a/spec/foreman/engine_spec.rb +++ b/spec/foreman/engine_spec.rb @@ -24,8 +24,9 @@ describe "Foreman::Engine" do 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 @@ -33,29 +34,14 @@ describe "Foreman::Engine" do 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 @@ -66,9 +52,10 @@ describe "Foreman::Engine" do 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 @@ -76,9 +63,10 @@ describe "Foreman::Engine" do 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 @@ -89,11 +77,10 @@ describe "Foreman::Engine" do 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 diff --git a/spec/foreman/export/bluepill_spec.rb b/spec/foreman/export/bluepill_spec.rb index dd71d87..b90536c 100644 --- a/spec/foreman/export/bluepill_spec.rb +++ b/spec/foreman/export/bluepill_spec.rb @@ -13,8 +13,7 @@ describe Foreman::Export::Bluepill do 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 \ No newline at end of file +end diff --git a/spec/resources/export/bluepill/app.pill b/spec/resources/export/bluepill/app.pill index bef2f13..6916f9e 100644 --- a/spec/resources/export/bluepill/app.pill +++ b/spec/resources/export/bluepill/app.pill @@ -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 @@ -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 @@ -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