hydra 0.1.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.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Nick Gauthier
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,9 @@
1
+ = Hydra
2
+
3
+ A distributed testing toolkit.
4
+
5
+ Heavily under development and not ready for public use.
6
+
7
+ == Copyright
8
+
9
+ Copyright (c) 2010 Nick Gauthier. See LICENSE for details.
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "hydra"
8
+ gem.summary = %Q{Distributed testing toolkit}
9
+ gem.description = %Q{Spread your tests over multiple machines to test your code faster.}
10
+ gem.email = "nick@smartlogicsolutions.com"
11
+ gem.homepage = "http://github.com/ngauthier/hydra"
12
+ gem.authors = ["Nick Gauthier"]
13
+ gem.add_development_dependency "shoulda", "= 2.10.3"
14
+ gem.add_dependency "net-ssh", "= 2.0.19"
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/test_*.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ begin
29
+ require 'rcov/rcovtask'
30
+ Rcov::RcovTask.new do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/test_*.rb'
33
+ test.verbose = true
34
+ end
35
+ rescue LoadError
36
+ task :rcov do
37
+ abort "RCov is not available. In order to run rcov, you must: gem install rcov"
38
+ end
39
+ end
40
+
41
+ task :test => :check_dependencies
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "hydra #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,63 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{hydra}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Nick Gauthier"]
12
+ s.date = %q{2010-01-26}
13
+ s.description = %q{Spread your tests over multiple machines to test your code faster.}
14
+ s.email = %q{nick@smartlogicsolutions.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "hydra.gemspec",
27
+ "lib/hydra.rb",
28
+ "lib/hydra/pipe.rb",
29
+ "lib/hydra/ssh.rb",
30
+ "test/echo_the_dolphin.rb",
31
+ "test/helper.rb",
32
+ "test/test_pipe.rb",
33
+ "test/test_ssh.rb"
34
+ ]
35
+ s.homepage = %q{http://github.com/ngauthier/hydra}
36
+ s.rdoc_options = ["--charset=UTF-8"]
37
+ s.require_paths = ["lib"]
38
+ s.rubygems_version = %q{1.3.5}
39
+ s.summary = %q{Distributed testing toolkit}
40
+ s.test_files = [
41
+ "test/test_ssh.rb",
42
+ "test/helper.rb",
43
+ "test/test_pipe.rb",
44
+ "test/echo_the_dolphin.rb"
45
+ ]
46
+
47
+ if s.respond_to? :specification_version then
48
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
49
+ s.specification_version = 3
50
+
51
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
52
+ s.add_development_dependency(%q<shoulda>, ["= 2.10.3"])
53
+ s.add_runtime_dependency(%q<net-ssh>, ["= 2.0.19"])
54
+ else
55
+ s.add_dependency(%q<shoulda>, ["= 2.10.3"])
56
+ s.add_dependency(%q<net-ssh>, ["= 2.0.19"])
57
+ end
58
+ else
59
+ s.add_dependency(%q<shoulda>, ["= 2.10.3"])
60
+ s.add_dependency(%q<net-ssh>, ["= 2.0.19"])
61
+ end
62
+ end
63
+
@@ -0,0 +1,2 @@
1
+ require 'hydra/pipe'
2
+ require 'hydra/ssh'
@@ -0,0 +1,105 @@
1
+ module Hydra #:nodoc:
2
+ # Read and write between two processes via pipes. For example:
3
+ # @pipe = Hydra::Pipe.new
4
+ # Process.fork do
5
+ # @pipe.identify_as_child
6
+ # sleep(1)
7
+ # puts "A message from my parent:\n#{@pipe.gets}"
8
+ # @pipe.close
9
+ # end
10
+ # @pipe.identify_as_parent
11
+ # @pipe.write "Hello, Child!"
12
+ # @pipe.close
13
+ # When the process forks, the pipe is copied. When a pipe is
14
+ # identified as a parent or child, it is choosing which ends
15
+ # of the pipe to use.
16
+ #
17
+ # A pipe is actually two pipes:
18
+ #
19
+ # Parent == Pipe 1 ==> Child
20
+ # Parent <== Pipe 2 == Child
21
+ #
22
+ # It's like if you had two cardboard tubes and you were using
23
+ # them to drop balls with messages in them between processes.
24
+ # One tube is for sending from parent to child, and the other
25
+ # tube is for sending from child to parent.
26
+ class Pipe
27
+ # Creates a new uninitialized pipe pair.
28
+ def initialize
29
+ @child_read, @parent_write = IO.pipe
30
+ @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
38
+ end
39
+
40
+ # Write a line to a pipe. It must have a trailing newline.
41
+ def write(str)
42
+ force_identification
43
+ begin
44
+ @writer.write(str)
45
+ return str
46
+ rescue Errno::EPIPE
47
+ raise Hydra::PipeError::Broken
48
+ end
49
+ end
50
+
51
+ # Identify this side of the pipe as the child.
52
+ def identify_as_child
53
+ @parent_write.close
54
+ @parent_read.close
55
+ @reader = @child_read
56
+ @writer = @child_write
57
+ end
58
+
59
+ # Identify this side of the pipe as the parent
60
+ def identify_as_parent
61
+ @child_write.close
62
+ @child_read.close
63
+ @reader = @parent_read
64
+ @writer = @parent_write
65
+ end
66
+
67
+ # closes the pipes. Once a pipe is closed on one end, the other
68
+ # end will get a PipeError::Broken if it tries to write.
69
+ def close
70
+ done_reading
71
+ done_writing
72
+ end
73
+
74
+ private
75
+ def done_writing #:nodoc:
76
+ @writer.close unless @writer.closed?
77
+ end
78
+
79
+ def done_reading #:nodoc:
80
+ @reader.close unless @reader.closed?
81
+ end
82
+
83
+ def force_identification #:nodoc:
84
+ raise PipeError::Unidentified if @reader.nil? or @writer.nil?
85
+ end
86
+ end
87
+
88
+ module PipeError #:nodoc:
89
+ # Raised if you try to read or write to a pipe when it is unidentified.
90
+ # Use identify_as_parent and identify_as_child to identify a pipe.
91
+ class Unidentified < RuntimeError
92
+ def message #:nodoc:
93
+ "Must identify as child or parent"
94
+ end
95
+ end
96
+ # Raised when a pipe has been broken between two processes.
97
+ # This happens when a process exits, and is a signal that
98
+ # there is no more data to communicate.
99
+ class Broken < RuntimeError
100
+ def message #:nodoc:
101
+ "Other side closed the connection"
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,48 @@
1
+ require 'open3'
2
+ module Hydra #:nodoc:
3
+ # Read and write with an ssh connection. For example:
4
+ # @ssh = Hydra::SSH.new('nick@nite')
5
+ # @ssh.write("echo hi")
6
+ # puts @ssh.gets
7
+ # => hi
8
+ #
9
+ # You can also use this to launch an interactive process. For
10
+ # example:
11
+ # @ssh = Hydra::SSH.new('nick@nite')
12
+ # @ssh.write('irb')
13
+ # @ssh.write("5+3")
14
+ # @ssh.gets
15
+ # => "5+3\n" # because irb echoes commands
16
+ # @ssh.gets
17
+ # => "8" # the output from irb
18
+ class SSH
19
+ include Open3
20
+
21
+ # Initialize new SSH connection. The single parameters is passed
22
+ # directly to ssh for starting a connection. So you can do:
23
+ # Hydra::SSH.new('localhost')
24
+ # Hydra::SSH.new('user@server.com')
25
+ # Hydra::SSH.new('-p 3022 user@server.com')
26
+ # 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
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # read lines from stdin
3
+ # echo each line back
4
+ # on EOF, quit nicely
5
+
6
+ $stdout.sync = true
7
+
8
+ while line = $stdin.gets
9
+ $stdout.write(line)
10
+ end
11
+
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'hydra'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,36 @@
1
+ require File.join(File.dirname(__FILE__), 'helper')
2
+
3
+ class TestPipe < Test::Unit::TestCase
4
+ context "a pipe" do
5
+ setup do
6
+ @pipe = Hydra::Pipe.new
7
+ end
8
+ should "be able to write messages" do
9
+ Process.fork do
10
+ @pipe.identify_as_child
11
+ assert_equal "Test Message\n", @pipe.gets
12
+ @pipe.write "Message Received\n"
13
+ @pipe.write "Second Message\n"
14
+ @pipe.close
15
+ end
16
+ @pipe.identify_as_parent
17
+ @pipe.write "Test Message\n"
18
+ assert_equal "Message Received\n", @pipe.gets
19
+ assert_equal "Second Message\n", @pipe.gets
20
+ assert_raise Hydra::PipeError::Broken do
21
+ @pipe.write "anybody home?"
22
+ end
23
+ @pipe.close
24
+ end
25
+ should "not allow writing if unidentified" do
26
+ assert_raise Hydra::PipeError::Unidentified do
27
+ @pipe.write "hey\n"
28
+ end
29
+ end
30
+ should "not allow reading if unidentified" do
31
+ assert_raise Hydra::PipeError::Unidentified do
32
+ @pipe.gets
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,29 @@
1
+ require File.join(File.dirname(__FILE__), 'helper')
2
+
3
+ class TestSSH < Test::Unit::TestCase
4
+ context "an ssh connection" do
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
11
+ 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
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
27
+ end
28
+ end
29
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hydra
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nick Gauthier
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-26 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: shoulda
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - "="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.10.3
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: net-ssh
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.0.19
34
+ version:
35
+ description: Spread your tests over multiple machines to test your code faster.
36
+ email: nick@smartlogicsolutions.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - LICENSE
43
+ - README.rdoc
44
+ files:
45
+ - .document
46
+ - .gitignore
47
+ - LICENSE
48
+ - README.rdoc
49
+ - Rakefile
50
+ - VERSION
51
+ - hydra.gemspec
52
+ - lib/hydra.rb
53
+ - lib/hydra/pipe.rb
54
+ - lib/hydra/ssh.rb
55
+ - test/echo_the_dolphin.rb
56
+ - test/helper.rb
57
+ - test/test_pipe.rb
58
+ - test/test_ssh.rb
59
+ has_rdoc: true
60
+ homepage: http://github.com/ngauthier/hydra
61
+ licenses: []
62
+
63
+ post_install_message:
64
+ rdoc_options:
65
+ - --charset=UTF-8
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: "0"
73
+ version:
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ version:
80
+ requirements: []
81
+
82
+ rubyforge_project:
83
+ rubygems_version: 1.3.5
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: Distributed testing toolkit
87
+ test_files:
88
+ - test/test_ssh.rb
89
+ - test/helper.rb
90
+ - test/test_pipe.rb
91
+ - test/echo_the_dolphin.rb