hydra 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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