diff --git a/lib/foreman/cli.rb b/lib/foreman/cli.rb index 48d00d8..14b413f 100644 --- a/lib/foreman/cli.rb +++ b/lib/foreman/cli.rb @@ -9,17 +9,19 @@ class Foreman::CLI < Thor desc "start [PROCESS]", "Start the application, or a specific process" - method_option :screen, :type => :boolean, :aliases => "-s" + method_option :screen, :type => :boolean, :aliases => "-s" + method_option :concurrency, :type => :string, :aliases => "-c", + :banner => '"alpha=5,bar=3"' def start(process=nil) check_procfile! if process - engine.execute(process) + engine.execute(process, options) elsif options[:screen] engine.screen else - engine.start + engine.start(options) end end diff --git a/lib/foreman/engine.rb b/lib/foreman/engine.rb index a871fbe..9b4f532 100644 --- a/lib/foreman/engine.rb +++ b/lib/foreman/engine.rb @@ -1,5 +1,6 @@ require "foreman" require "foreman/process" +require "foreman/utils" require "pty" require "tempfile" require "term/ansicolor" @@ -12,28 +13,42 @@ class Foreman::Engine extend Term::ANSIColor - COLORS = [ cyan, yellow, green, magenta, on_blue ] + COLORS = [ cyan, yellow, green, magenta, red ] def initialize(procfile) @procfile = read_procfile(procfile) @directory = File.expand_path(File.dirname(procfile)) end - def processes + def processes(concurrency=nil) @processes ||= begin + concurrency = Foreman::Utils.parse_concurrency(concurrency) + procfile.split("\n").inject({}) do |hash, line| next if line.strip == "" - process = Foreman::Process.new(*line.split(" ", 2)) - process.color = next_color - hash.update(process.name => process) + name, command = line.split(" ", 2) + + if concurrency[name] > 1 then + 1.upto(concurrency[name]) do |num| + process = Foreman::Process.new("#{name}.#{num}", command) + process.color = next_color + hash[process.name] = process + end + else + process = Foreman::Process.new(name, command) + process.color = next_color + hash[process.name] = process + end + + hash end end end - def start + def start(options={}) proctitle "ruby: foreman master" - processes.each do |name, process| + processes(options[:concurrency]).each do |name, process| fork process end @@ -56,8 +71,17 @@ class Foreman::Engine tempfile.delete end - def execute(name) - run(processes[name], false) + def execute(name, options={}) + processes(options[:concurrency]).values.select do |process| + process.name =~ /\A#{name}\.?\d*\Z/ + end.each do |process| + fork process + end + + trap("TERM") { kill_and_exit("TERM") } + trap("INT") { kill_and_exit("INT") } + + watch_for_termination end private ###################################################################### diff --git a/lib/foreman/export/base.rb b/lib/foreman/export/base.rb index 3af0f18..4854587 100644 --- a/lib/foreman/export/base.rb +++ b/lib/foreman/export/base.rb @@ -1,4 +1,5 @@ require "foreman/export" +require "foreman/utils" class Foreman::Export::Base @@ -26,16 +27,6 @@ private ###################################################################### File.read(File.expand_path("../../../../export/#{name}", __FILE__)) end - def parse_concurrency(concurrency) - @concurrency ||= begin - pairs = concurrency.to_s.gsub(/\s/, "").split(",") - pairs.inject(Hash.new(1)) do |hash, pair| - process, amount = pair.split("=") - hash.update(process => amount.to_i) - end - end - end - def port_for(base_port, app, num) base_port ||= 5000 offset = engine.processes.keys.sort.index(app) * 100 diff --git a/lib/foreman/utils.rb b/lib/foreman/utils.rb new file mode 100644 index 0000000..1970bd4 --- /dev/null +++ b/lib/foreman/utils.rb @@ -0,0 +1,15 @@ +require "foreman" + +class Foreman::Utils + + def self.parse_concurrency(concurrency) + @concurrency ||= begin + pairs = concurrency.to_s.gsub(/\s/, "").split(",") + pairs.inject(Hash.new(1)) do |hash, pair| + process, amount = pair.split("=") + hash.update(process => amount.to_i) + end + end + end + +end diff --git a/man/foreman.1.ronn b/man/foreman.1.ronn index 4160813..3f7b2d3 100644 --- a/man/foreman.1.ronn +++ b/man/foreman.1.ronn @@ -25,6 +25,10 @@ application type. The following options control how the application is run: + * `-c`, `--concurrency`: + Specify the number of each process type to run. The value passed in + should be in the format `process=num,process=num` + * `-s`, `--screen`: Run the application as a series of screen windows rather than interleaved in stdout. diff --git a/spec/foreman/cli_spec.rb b/spec/foreman/cli_spec.rb index 382436f..a1b8305 100644 --- a/spec/foreman/cli_spec.rb +++ b/spec/foreman/cli_spec.rb @@ -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 diff --git a/spec/foreman/engine_spec.rb b/spec/foreman/engine_spec.rb index 3debe6e..18c73f6 100644 --- a/spec/foreman/engine_spec.rb +++ b/spec/foreman/engine_spec.rb @@ -33,7 +33,8 @@ describe "Foreman::Engine" do describe "execute" do it "runs the processes" do write_procfile - mock(subject).run(subject.processes["alpha"], false) + mock(subject).fork(subject.processes["alpha"]) + mock(subject).watch_for_termination subject.execute("alpha") end end