diff --git a/lib/hydra/master.rb b/lib/hydra/master.rb index 8fb0903..fe3ff9e 100644 --- a/lib/hydra/master.rb +++ b/lib/hydra/master.rb @@ -67,6 +67,7 @@ module Hydra #:nodoc: @string_runner_event_listeners = Array( opts.fetch( 'runner_listeners' ) { nil } ) + @runner_log_file = opts.fetch('runner_log_file') { nil } @verbose = opts.fetch('verbose') { false } @autosort = opts.fetch('autosort') { true } @sync = opts.fetch('sync') { nil } @@ -163,7 +164,7 @@ module Hydra #:nodoc: pipe = Hydra::Pipe.new child = SafeFork.fork do pipe.identify_as_child - Hydra::Worker.new(:io => pipe, :runners => runners, :verbose => @verbose, :runner_listeners => @string_runner_event_listeners ) + Hydra::Worker.new(:io => pipe, :runners => runners, :verbose => @verbose, :runner_listeners => @string_runner_event_listeners, :runner_log_file => @runner_log_file ) end pipe.identify_as_parent @@ -175,7 +176,7 @@ module Hydra #:nodoc: runners = worker.fetch('runners') { raise "You must specify the number of runners" } command = worker.fetch('command') { - "RAILS_ENV=#{@environment} ruby -e \"require 'rubygems'; require 'hydra'; Hydra::Worker.new(:io => Hydra::Stdio.new, :runners => #{runners}, :verbose => #{@verbose}, :runner_listeners => \'#{@string_runner_event_listeners}\' );\"" + "RAILS_ENV=#{@environment} ruby -e \"require 'rubygems'; require 'hydra'; Hydra::Worker.new(:io => Hydra::Stdio.new, :runners => #{runners}, :verbose => #{@verbose}, :runner_listeners => \'#{@string_runner_event_listeners}\', :runner_log_file => \'#{@runner_log_file}\' );\"" } trace "Booting SSH worker" diff --git a/lib/hydra/runner.rb b/lib/hydra/runner.rb index dd22e1e..f96cc08 100644 --- a/lib/hydra/runner.rb +++ b/lib/hydra/runner.rb @@ -16,7 +16,7 @@ module Hydra #:nodoc: # Boot up a runner. It takes an IO object (generally a pipe from its # parent) to send it messages on which files to execute. def initialize(opts = {}) - redirect_output + redirect_output( opts.fetch( :runner_log_file ) { nil } ) reg_trap_sighup @io = opts.fetch(:io) { raise "No IO Object" } @@ -289,9 +289,8 @@ module Hydra #:nodoc: end.compact end - def redirect_output file_name = nil - file_name = 'log/hydra.log' if !file_name and File.exists? 'log/' - file_name = 'hydra.log' unless file_name + def redirect_output file_name + file_name = '/dev/null' unless file_name and !file_name.empty? $stderr = $stdout = File.open(file_name, 'a') end end diff --git a/lib/hydra/tasks.rb b/lib/hydra/tasks.rb index 2dc7309..72733d0 100644 --- a/lib/hydra/tasks.rb +++ b/lib/hydra/tasks.rb @@ -41,6 +41,10 @@ module Hydra #:nodoc: # Set to false if you don't want to show the total running time attr_accessor :show_time + # Set to a valid file path if you want to save the output of the runners + # in a log file + attr_accessor :runner_log_file + # # Search for the hydra config file def find_config_file @@ -98,7 +102,8 @@ module Hydra #:nodoc: :autosort => @autosort, :files => @files, :listeners => @listeners, - :environment => @environment + :environment => @environment, + :runner_log_file => @runner_log_file } if @config @opts.merge!(:config => @config) diff --git a/lib/hydra/worker.rb b/lib/hydra/worker.rb index 01350cd..df05c04 100644 --- a/lib/hydra/worker.rb +++ b/lib/hydra/worker.rb @@ -28,6 +28,7 @@ module Hydra #:nodoc: listener = eval(l) @runner_event_listeners << listener if listener.is_a?(Hydra::RunnerListener::Abstract) end + @runner_log_file = opts.fetch(:runner_log_file) { nil } boot_runners(opts.fetch(:runners) { 1 }) @io.write(Hydra::Messages::Worker::WorkerBegin.new) @@ -91,7 +92,7 @@ module Hydra #:nodoc: pipe = Hydra::Pipe.new child = SafeFork.fork do pipe.identify_as_child - Hydra::Runner.new(:io => pipe, :verbose => @verbose, :runner_listeners => @runner_event_listeners ) + Hydra::Runner.new(:io => pipe, :verbose => @verbose, :runner_listeners => @runner_event_listeners, :runner_log_file => @runner_log_file ) end pipe.identify_as_parent @runners << { :pid => child, :io => pipe, :idle => false } diff --git a/test/master_test.rb b/test/master_test.rb index 2881ada..2225e24 100644 --- a/test/master_test.rb +++ b/test/master_test.rb @@ -273,8 +273,82 @@ class MasterTest < Test::Unit::TestCase end end + context "redirecting runner's output and errors" do + setup do + # avoid having other tests interfering with us + sleep(0.2) + FileUtils.rm_f(target_file) + FileUtils.rm_f(runner_log_file) + FileUtils.rm_f("#{remote_dir_path}/#{runner_log_file}") + end + + teardown do + FileUtils.rm_f(target_file) + FileUtils.rm_f(runner_log_file) + FileUtils.rm_f("#{remote_dir_path}/#{runner_log_file}") + end + + should "create a runner log file when usign local worker and passing a log file name" do + @pid = Process.fork do + Hydra::Master.new( + :files => [test_file], + :runner_log_file => runner_log_file, + :verbose => false + ) + end + Process.waitpid @pid + + assert_file_exists target_file # ensure the test was successfully ran + assert_file_exists runner_log_file + end + + should "create a runner log file when usign remote worker and passing a log file name" do + @pid = Process.fork do + Hydra::Master.new( + :files => [test_file], + :workers => [{ + :type => :ssh, + :connect => 'localhost', + :directory => remote_dir_path, + :runners => 1 + }], + :verbose => false, + :runner_log_file => runner_log_file + ) + end + Process.waitpid @pid + + assert_file_exists target_file # ensure the test was successfully ran + assert_file_exists "#{remote_dir_path}/#{runner_log_file}" + end + + # should "NOT create a runner log file when passing a incorrect log file path, but it should run successfully" do + # @pid = Process.fork do + # Hydra::Master.new( + # :files => [test_file], + # :workers => [{ + # :type => :ssh, + # :connect => 'localhost', + # :directory => remote_dir_path, + # :runners => 1 + # }], + # :verbose => false, + # :runner_log_file => 'invalid-dir/runner.log' + # ) + # end + # Process.waitpid @pid + + # assert_file_exists target_file # ensure the test was successfully ran + # assert_file_exists runner_log_file + # end + end + private + def runner_log_file + "hydra_runner.log" + end + def add_infinite_worker_begin_to master_listener class << master_listener def worker_begin( worker )