hydra 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/TODO ADDED
@@ -0,0 +1,54 @@
1
+ # runners = []
2
+ # cores.each do |c|
3
+ # in = pipes[c][0]
4
+ # out = pipes[c][1]
5
+ # runners << TestRunner.new(in, out)
6
+ # end
7
+ #
8
+ # files = [ ... ]
9
+ # results = []
10
+ #
11
+ # runners.each do |r|
12
+ # Thread.new do
13
+ # while !files.empty?
14
+ # results << r.run_file(files.pop)
15
+ # end
16
+ # r.shutdown
17
+ # end
18
+ # end
19
+ #
20
+ # puts results.join("\n")
21
+ #
22
+
23
+
24
+ # Master
25
+ # boot up workers
26
+ # listen for worker messages
27
+ # add worker messages to message queue
28
+ # process message queue
29
+ # "reply" to a message allows sending a message back down to worker
30
+ #
31
+ # When worker asks for file but no files left, send shutdown message to worker
32
+ # when worker connection breaks, end thread
33
+ # wait on all threads
34
+ # when all threads are done, all workers must be done
35
+ #
36
+ #
37
+ # Worker
38
+ # boot up runners
39
+ # listen for runner messages
40
+ # add runner messages to message queue
41
+ # process message queue
42
+ # "reply" to a message allows sending message back down to runner
43
+ #
44
+ # when a runner asks for file but master responds with shutdown, mark self
45
+ # as terminated, shut down runners. Any runner that asks for a file is
46
+ # auto-terminated
47
+ # wait for runner threads to finish
48
+ # then exit, breaking master connection
49
+ #
50
+ # Runner
51
+ # when booted, ask for a file
52
+ # then process messages on the queue
53
+ # when it's a file, run it and send a results message
54
+ # when it's a shutdown, break main loop
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.1
1
+ 0.2.0
@@ -5,16 +5,17 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{hydra}
8
- s.version = "0.1.1"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Nick Gauthier"]
12
- s.date = %q{2010-01-26}
12
+ s.date = %q{2010-01-27}
13
13
  s.description = %q{Spread your tests over multiple machines to test your code faster.}
14
14
  s.email = %q{nick@smartlogicsolutions.com}
15
15
  s.extra_rdoc_files = [
16
16
  "LICENSE",
17
- "README.rdoc"
17
+ "README.rdoc",
18
+ "TODO"
18
19
  ]
19
20
  s.files = [
20
21
  ".document",
@@ -22,14 +23,20 @@ Gem::Specification.new do |s|
22
23
  "LICENSE",
23
24
  "README.rdoc",
24
25
  "Rakefile",
26
+ "TODO",
25
27
  "VERSION",
26
28
  "hydra.gemspec",
27
29
  "lib/hydra.rb",
30
+ "lib/hydra/io.rb",
31
+ "lib/hydra/message.rb",
32
+ "lib/hydra/message/runner_requests_file.rb",
28
33
  "lib/hydra/pipe.rb",
34
+ "lib/hydra/runner.rb",
29
35
  "lib/hydra/ssh.rb",
30
36
  "test/echo_the_dolphin.rb",
31
37
  "test/helper.rb",
32
38
  "test/test_pipe.rb",
39
+ "test/test_runner.rb",
33
40
  "test/test_ssh.rb"
34
41
  ]
35
42
  s.homepage = %q{http://github.com/ngauthier/hydra}
@@ -41,6 +48,7 @@ Gem::Specification.new do |s|
41
48
  "test/test_ssh.rb",
42
49
  "test/helper.rb",
43
50
  "test/test_pipe.rb",
51
+ "test/test_runner.rb",
44
52
  "test/echo_the_dolphin.rb"
45
53
  ]
46
54
 
@@ -1,2 +1,5 @@
1
1
  require 'hydra/pipe'
2
2
  require 'hydra/ssh'
3
+ require 'hydra/message'
4
+ require 'hydra/runner'
5
+
@@ -0,0 +1,34 @@
1
+ module Hydra #:nodoc:
2
+ module MessagingIO
3
+ # Read a line from the input IO object.
4
+ def gets
5
+ raise IOError unless @reader
6
+ message = @reader.gets
7
+ return nil unless message
8
+ return Message.build(eval(message.chomp))
9
+ end
10
+
11
+ # Write a line to the output IO object
12
+ def write(message)
13
+ raise IOError unless @writer
14
+ raise UnprocessableMessage unless message.is_a?(Hydra::Message)
15
+ begin
16
+ @writer.write(message.serialize+"\n")
17
+ rescue Errno::EPIPE
18
+ raise IOError
19
+ end
20
+ end
21
+
22
+ def close
23
+ @reader.close if @reader
24
+ @writer.close if @writer
25
+ end
26
+
27
+ class UnprocessableMessage < RuntimeError
28
+ attr_accessor :message
29
+ def initialize(message = "Message expected")
30
+ @message = message
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,18 @@
1
+ module Hydra #:nodoc:
2
+ class Message #:nodoc:
3
+ def initialize(opts = {})
4
+ opts.each do |k,v|
5
+ self.send(k,v)
6
+ end
7
+ end
8
+ def self.build(hash)
9
+ hash.delete(:class).new(hash)
10
+ end
11
+
12
+ def serialize(opts = {})
13
+ opts[:class] = self.class
14
+ opts.inspect
15
+ end
16
+ end
17
+ end
18
+ require 'hydra/message/runner_requests_file'
@@ -0,0 +1,7 @@
1
+ module Hydra #:nodoc:
2
+ module Messages #:nodoc:
3
+ # Message indicating that a Runner needs a file to run
4
+ class RunnerRequestsFile < Hydra::Message
5
+ end
6
+ end
7
+ end
@@ -1,3 +1,4 @@
1
+ require 'hydra/io'
1
2
  module Hydra #:nodoc:
2
3
  # Read and write between two processes via pipes. For example:
3
4
  # @pipe = Hydra::Pipe.new
@@ -24,31 +25,11 @@ module Hydra #:nodoc:
24
25
  # One tube is for sending from parent to child, and the other
25
26
  # tube is for sending from child to parent.
26
27
  class Pipe
28
+ include Hydra::MessagingIO
27
29
  # Creates a new uninitialized pipe pair.
28
30
  def initialize
29
31
  @child_read, @parent_write = IO.pipe
30
32
  @parent_read, @child_write = IO.pipe
31
- [@parent_write, @child_write].each{|io| io.sync = true}
32
- end
33
-
34
- # Read a line from a pipe. It will have a trailing newline.
35
- def gets
36
- force_identification
37
- @reader.gets.chomp
38
- end
39
-
40
- # Write a line to a pipe. It must have a trailing newline.
41
- def write(str)
42
- force_identification
43
- unless str =~ /\n$/
44
- str += "\n"
45
- end
46
- begin
47
- @writer.write(str)
48
- return str
49
- rescue Errno::EPIPE
50
- raise Hydra::PipeError::Broken
51
- end
52
33
  end
53
34
 
54
35
  # Identify this side of the pipe as the child.
@@ -66,43 +47,5 @@ module Hydra #:nodoc:
66
47
  @reader = @parent_read
67
48
  @writer = @parent_write
68
49
  end
69
-
70
- # closes the pipes. Once a pipe is closed on one end, the other
71
- # end will get a PipeError::Broken if it tries to write.
72
- def close
73
- done_reading
74
- done_writing
75
- end
76
-
77
- private
78
- def done_writing #:nodoc:
79
- @writer.close unless @writer.closed?
80
- end
81
-
82
- def done_reading #:nodoc:
83
- @reader.close unless @reader.closed?
84
- end
85
-
86
- def force_identification #:nodoc:
87
- raise PipeError::Unidentified if @reader.nil? or @writer.nil?
88
- end
89
- end
90
-
91
- module PipeError #:nodoc:
92
- # Raised if you try to read or write to a pipe when it is unidentified.
93
- # Use identify_as_parent and identify_as_child to identify a pipe.
94
- class Unidentified < RuntimeError
95
- def message #:nodoc:
96
- "Must identify as child or parent"
97
- end
98
- end
99
- # Raised when a pipe has been broken between two processes.
100
- # This happens when a process exits, and is a signal that
101
- # there is no more data to communicate.
102
- class Broken < RuntimeError
103
- def message #:nodoc:
104
- "Other side closed the connection"
105
- end
106
- end
107
50
  end
108
51
  end
@@ -0,0 +1,8 @@
1
+ module Hydra #:nodoc:
2
+ class Runner
3
+ def initialize(io)
4
+ @io = io
5
+ @io.write Hydra::Messages::RunnerRequestsFile.new
6
+ end
7
+ end
8
+ end
@@ -1,4 +1,5 @@
1
1
  require 'open3'
2
+ require 'hydra/io'
2
3
  module Hydra #:nodoc:
3
4
  # Read and write with an ssh connection. For example:
4
5
  # @ssh = Hydra::SSH.new('nick@nite')
@@ -17,6 +18,7 @@ module Hydra #:nodoc:
17
18
  # => "8" # the output from irb
18
19
  class SSH
19
20
  include Open3
21
+ include Hydra::MessagingIO
20
22
 
21
23
  # Initialize new SSH connection. The single parameters is passed
22
24
  # directly to ssh for starting a connection. So you can do:
@@ -24,25 +26,10 @@ module Hydra #:nodoc:
24
26
  # Hydra::SSH.new('user@server.com')
25
27
  # Hydra::SSH.new('-p 3022 user@server.com')
26
28
  # etc..
27
- def initialize(connection_options)
28
- @stdin, @stdout, @stderr = popen3("ssh #{connection_options}")
29
- end
30
-
31
- # Write a string to ssh. This method returns the string passed to
32
- # ssh. Note that if you do not add a newline at the end, it adds
33
- # one for you, and the modified string is returned
34
- def write(str)
35
- unless str =~ /\n$/
36
- str += "\n"
37
- end
38
- @stdin.write(str)
39
- return str
40
- end
41
-
42
- # Read a line from ssh. This call blocks when there is nothing
43
- # to read.
44
- def gets
45
- @stdout.gets.chomp
29
+ def initialize(connection_options, directory, command)
30
+ @writer, @reader, @error = popen3("ssh #{connection_options}")
31
+ @writer.write("cd #{directory}\n")
32
+ @writer.write(command+"\n")
46
33
  end
47
34
  end
48
35
  end
@@ -1,10 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
- # read lines from stdin
3
- # echo each line back
4
- # on EOF, quit nicely
5
-
2
+ # Echoes back to the sender
6
3
  $stdout.sync = true
7
-
8
4
  while line = $stdin.gets
9
5
  $stdout.write(line)
10
6
  end
@@ -8,3 +8,18 @@ require 'hydra'
8
8
 
9
9
  class Test::Unit::TestCase
10
10
  end
11
+
12
+ module Hydra #:nodoc:
13
+ module Messages #:nodoc:
14
+ class TestMessage < Hydra::Message
15
+ attr_accessor :text
16
+ def initialize(opts = {})
17
+ @text = opts.fetch(:text){ "test" }
18
+ end
19
+ def serialize
20
+ super(:text => @text)
21
+ end
22
+ end
23
+ end
24
+ end
25
+
@@ -5,40 +5,34 @@ class TestPipe < Test::Unit::TestCase
5
5
  setup do
6
6
  @pipe = Hydra::Pipe.new
7
7
  end
8
+ teardown do
9
+ @pipe.close
10
+ end
8
11
  should "be able to write messages" do
9
- Process.fork do
12
+ child = Process.fork do
10
13
  @pipe.identify_as_child
11
- assert_equal "Test Message", @pipe.gets
12
- @pipe.write "Message Received"
13
- @pipe.write "Second Message"
14
- @pipe.close
14
+ assert_equal "Test Message", @pipe.gets.text
15
+ @pipe.write Hydra::Messages::TestMessage.new(:text => "Message Received")
16
+ @pipe.write Hydra::Messages::TestMessage.new(:text => "Second Message")
15
17
  end
16
18
  @pipe.identify_as_parent
17
- @pipe.write "Test Message"
18
- assert_equal "Message Received", @pipe.gets
19
- assert_equal "Second Message", @pipe.gets
20
- assert_raise Hydra::PipeError::Broken do
21
- @pipe.write "anybody home?"
19
+ @pipe.write Hydra::Messages::TestMessage.new(:text => "Test Message")
20
+ assert_equal "Message Received", @pipe.gets.text
21
+ assert_equal "Second Message", @pipe.gets.text
22
+ Process.wait(child) #ensure it quits, so there is nothing to write to
23
+ assert_raise IOError do
24
+ @pipe.write Hydra::Messages::TestMessage.new(:text => "anyone there?")
22
25
  end
23
- @pipe.close
24
26
  end
25
27
  should "not allow writing if unidentified" do
26
- assert_raise Hydra::PipeError::Unidentified do
27
- @pipe.write "hey\n"
28
+ assert_raise IOError do
29
+ @pipe.write Hydra::Messages::TestMessage.new(:text => "Test Message")
28
30
  end
29
31
  end
30
32
  should "not allow reading if unidentified" do
31
- assert_raise Hydra::PipeError::Unidentified do
33
+ assert_raise IOError do
32
34
  @pipe.gets
33
35
  end
34
36
  end
35
- should "handle newlines" do
36
- Process.fork do
37
- @pipe.identify_as_child
38
- @pipe.write "Message\n"
39
- end
40
- @pipe.identify_as_parent
41
- assert_equal "Message", @pipe.gets
42
- end
43
37
  end
44
38
  end
@@ -0,0 +1,24 @@
1
+ require File.join(File.dirname(__FILE__), 'helper')
2
+
3
+ class TestRunner < Test::Unit::TestCase
4
+ context "a test runner" do
5
+ setup do
6
+ @pipe = Hydra::Pipe.new
7
+ @child = Process.fork do
8
+ @pipe.identify_as_child
9
+ Hydra::Runner.new(@pipe)
10
+ end
11
+ @pipe.identify_as_parent
12
+ end
13
+ teardown do
14
+ Process.wait(@child)
15
+ end
16
+ should "request a file on boot" do
17
+ assert @pipe.gets.is_a?(Hydra::Messages::RunnerRequestsFile)
18
+ end
19
+ should "return a result message after processing a file" do
20
+
21
+ end
22
+ should "terminate when sent a shutdown message"
23
+ end
24
+ end
@@ -3,27 +3,19 @@ require File.join(File.dirname(__FILE__), 'helper')
3
3
  class TestSSH < Test::Unit::TestCase
4
4
  context "an ssh connection" do
5
5
  setup do
6
- @ssh = Hydra::SSH.new('localhost')
7
- end
8
- should "be able to execute a command" do
9
- @ssh.write "echo hi"
10
- assert_equal "hi", @ssh.gets
6
+ @ssh = Hydra::SSH.new(
7
+ 'localhost', # connect to this machine
8
+ File.expand_path(File.join(File.dirname(__FILE__))), # move to the test directory
9
+ "ruby ./echo_the_dolphin.rb"
10
+ )
11
+ @message = Hydra::Messages::TestMessage.new
11
12
  end
12
- should "be able to execute a command with a newline" do
13
- @ssh.write "echo hi\n"
14
- assert_equal "hi", @ssh.gets
13
+ teardown do
14
+ @ssh.close
15
15
  end
16
- should "be able to communicate with a process" do
17
- pwd = File.dirname(__FILE__)
18
- echo_the_dolphin = File.expand_path(
19
- File.join(File.dirname(__FILE__), 'echo_the_dolphin.rb')
20
- )
21
- @ssh.write('ruby -e "puts \'Hello\'"')
22
- assert_equal "Hello", @ssh.gets
23
-
24
- @ssh.write("ruby #{echo_the_dolphin}")
25
- @ssh.write("Hello Echo!")
26
- assert_equal "Hello Echo!", @ssh.gets
16
+ should "be able to execute a command" do
17
+ @ssh.write @message
18
+ assert_equal @message.text, @ssh.gets.text
27
19
  end
28
20
  end
29
21
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hydra
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Gauthier
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-26 00:00:00 -05:00
12
+ date: 2010-01-27 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -41,20 +41,27 @@ extensions: []
41
41
  extra_rdoc_files:
42
42
  - LICENSE
43
43
  - README.rdoc
44
+ - TODO
44
45
  files:
45
46
  - .document
46
47
  - .gitignore
47
48
  - LICENSE
48
49
  - README.rdoc
49
50
  - Rakefile
51
+ - TODO
50
52
  - VERSION
51
53
  - hydra.gemspec
52
54
  - lib/hydra.rb
55
+ - lib/hydra/io.rb
56
+ - lib/hydra/message.rb
57
+ - lib/hydra/message/runner_requests_file.rb
53
58
  - lib/hydra/pipe.rb
59
+ - lib/hydra/runner.rb
54
60
  - lib/hydra/ssh.rb
55
61
  - test/echo_the_dolphin.rb
56
62
  - test/helper.rb
57
63
  - test/test_pipe.rb
64
+ - test/test_runner.rb
58
65
  - test/test_ssh.rb
59
66
  has_rdoc: true
60
67
  homepage: http://github.com/ngauthier/hydra
@@ -88,4 +95,5 @@ test_files:
88
95
  - test/test_ssh.rb
89
96
  - test/helper.rb
90
97
  - test/test_pipe.rb
98
+ - test/test_runner.rb
91
99
  - test/echo_the_dolphin.rb