Remote and Global tasks

This commit is contained in:
Nick Gauthier 2010-02-10 11:38:06 -05:00
parent 9b428bcf95
commit 6544e9eec3
2 changed files with 120 additions and 18 deletions

View File

@ -9,7 +9,7 @@ Gem::Specification.new do |s|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Nick Gauthier"] s.authors = ["Nick Gauthier"]
s.date = %q{2010-02-09} s.date = %q{2010-02-10}
s.description = %q{Spread your tests over multiple machines to test your code faster.} s.description = %q{Spread your tests over multiple machines to test your code faster.}
s.email = %q{nick@smartlogicsolutions.com} s.email = %q{nick@smartlogicsolutions.com}
s.extra_rdoc_files = [ s.extra_rdoc_files = [

View File

@ -1,8 +1,7 @@
require 'open3'
module Hydra #:nodoc: module Hydra #:nodoc:
# Define a test task that uses hydra to test the files. # Hydra Task Common attributes and methods
# class Task
# TODO: examples
class TestTask
# Name of the task. Default 'hydra' # Name of the task. Default 'hydra'
attr_accessor :name attr_accessor :name
@ -19,6 +18,35 @@ module Hydra #:nodoc:
# Path to the hydra config file. # Path to the hydra config file.
# If not set, it will check 'hydra.yml' and 'config/hydra.yml' # If not set, it will check 'hydra.yml' and 'config/hydra.yml'
attr_accessor :config attr_accessor :config
#
# Search for the hydra config file
def find_config_file
@config ||= 'hydra.yml'
return @config if File.exists?(@config)
@config = File.join('config', 'hydra.yml')
return @config if File.exists?(@config)
@config = nil
end
# Add files to test by passing in a string to be run through Dir.glob.
# For example:
#
# t.add_files 'test/units/*.rb'
def add_files(pattern)
@files += Dir.glob(pattern)
end
end
# Define a test task that uses hydra to test the files.
#
# Hydra::TestTask.new('hydra') do |t|
# t.add_files 'test/unit/**/*_test.rb'
# t.add_files 'test/functional/**/*_test.rb'
# t.add_files 'test/integration/**/*_test.rb'
# t.verbose = false # optionally set to true for lots of debug messages
# end
class TestTask < Hydra::Task
# Create a new HydraTestTask # Create a new HydraTestTask
def initialize(name = :hydra) def initialize(name = :hydra)
@ -45,6 +73,7 @@ module Hydra #:nodoc:
define define
end end
private
# Create the rake task defined by this HydraTestTask # Create the rake task defined by this HydraTestTask
def define def define
desc "Hydra Tests" + (@name == :hydra ? "" : " for #{@name}") desc "Hydra Tests" + (@name == :hydra ? "" : " for #{@name}")
@ -55,22 +84,95 @@ module Hydra #:nodoc:
exit(0) #bypass test on_exit output exit(0) #bypass test on_exit output
end end
end end
end
# Add files to test by passing in a string to be run through Dir.glob. # Setup a task that will be run across all remote workers
# Hydra::RemoteTask.new('db:reset')
#
# Then you can run:
# rake hydra:remote:db:reset
class RemoteTask < Hydra::Task
include Open3
# Create a new hydra remote task with the given name.
# The task will be named hydra:remote:<name>
def initialize(name)
@name = name
yield self if block_given?
@config = find_config_file
unless @config
$stderr.write "No config file. Can't run a remote task without remote workers\n"
return
end
define
end
private
def define
desc "Run #{@name} remotely on all workers"
task "hydra:remote:#{@name}" do
config = YAML.load_file(@config)
workers = config.fetch('workers') { [] }
workers = workers.select{|w| w['type'] == 'ssh'}
raise "No remote workers" if workers.empty?
workers.each do |worker|
$stdout.write "==== Hydra Running #{@name} on #{worker['connect']} ====\n"
ssh_opts = worker.fetch('ssh_opts') { '' }
writer, reader, error = popen3("ssh -tt #{ssh_opts} #{worker['connect']} ")
writer.write("cd #{worker['directory']}\n")
writer.write "echo BEGIN HYDRA\n"
writer.write("RAILS_ENV=test rake #{@name}\n")
writer.write "echo END HYDRA\n"
writer.write("exit\n")
writer.close
ignoring = true
while line = reader.gets
line.chomp!
if line =~ /echo END HYDRA$/
ignoring = true
end
$stdout.write "#{line}\n" unless ignoring
if line == 'BEGIN HYDRA'
ignoring = false
end
end
$stdout.write "\n==== Hydra Running #{@name} COMPLETE ====\n\n"
end
end
end
end
# A Hydra global task is a task that is run both locally and remotely.
#
# For example: # For example:
# #
# t.add_files 'test/units/*.rb' # Hydra::GlobalTask.new('db:reset')
def add_files(pattern) #
@files += Dir.glob(pattern) # Allows you to run:
#
# rake hydra:db:reset
#
# Then, db:reset will be run locally and on all remote workers. This
# makes it easy to setup your workers and run tasks all in a row.
#
# For example:
#
# rake hydra:db:reset hydra:factories hydra:tests
#
# Assuming you setup hydra:db:reset and hydra:db:factories as global
# tasks and hydra:tests as a Hydra::TestTask for all your tests
class GlobalTask < Hydra::Task
def initialize(name)
@name = name
define
end end
# Search for the hydra config file private
def find_config_file def define
@config ||= 'hydra.yml' Hydra::RemoteTask.new(@name)
return @config if File.exists?(@config) desc "Run #{@name.to_s} Locally and Remotely across all Workers"
@config = File.join('config', 'hydra.yml') task "hydra:#{@name.to_s}" => [@name.to_s, "hydra:remote:#{@name.to_s}"]
return @config if File.exists?(@config)
@config = nil
end end
end end
end end