added ssh library

This commit is contained in:
Nick Gauthier 2010-01-26 15:31:03 -05:00
parent 72b15af79f
commit 8c0359ed4c
8 changed files with 162 additions and 17 deletions

View File

@ -11,7 +11,7 @@ begin
gem.homepage = "http://github.com/ngauthier/hydra" gem.homepage = "http://github.com/ngauthier/hydra"
gem.authors = ["Nick Gauthier"] gem.authors = ["Nick Gauthier"]
gem.add_development_dependency "shoulda", "= 2.10.3" gem.add_development_dependency "shoulda", "= 2.10.3"
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings gem.add_dependency "net-ssh", "= 2.0.19"
end end
Jeweler::GemcutterTasks.new Jeweler::GemcutterTasks.new
rescue LoadError rescue LoadError
@ -34,7 +34,7 @@ begin
end end
rescue LoadError rescue LoadError
task :rcov do task :rcov do
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov" abort "RCov is not available. In order to run rcov, you must: gem install rcov"
end end
end end

62
hydra.gemspec Normal file
View File

@ -0,0 +1,62 @@
# Generated by jeweler
# DO NOT EDIT THIS FILE DIRECTLY
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = %q{hydra}
s.version = "0.0.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Nick Gauthier"]
s.date = %q{2010-01-26}
s.description = %q{TODO: longer description of your gem}
s.email = %q{nick@smartlogicsolutions.com}
s.extra_rdoc_files = [
"LICENSE",
"README.rdoc"
]
s.files = [
".document",
".gitignore",
"LICENSE",
"README.rdoc",
"Rakefile",
"VERSION",
"lib/hydra.rb",
"lib/hydra/pipe.rb",
"lib/hydra/ssh.rb",
"test/echo_the_dolphin.rb",
"test/helper.rb",
"test/test_pipe.rb",
"test/test_ssh.rb"
]
s.homepage = %q{http://github.com/ngauthier/hydra}
s.rdoc_options = ["--charset=UTF-8"]
s.require_paths = ["lib"]
s.rubygems_version = %q{1.3.5}
s.summary = %q{Distributed testing toolkit}
s.test_files = [
"test/test_ssh.rb",
"test/helper.rb",
"test/test_pipe.rb",
"test/echo_the_dolphin.rb"
]
if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 3
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
s.add_development_dependency(%q<shoulda>, ["= 2.10.3"])
s.add_runtime_dependency(%q<net-ssh>, ["= 2.0.19"])
else
s.add_dependency(%q<shoulda>, ["= 2.10.3"])
s.add_dependency(%q<net-ssh>, ["= 2.0.19"])
end
else
s.add_dependency(%q<shoulda>, ["= 2.10.3"])
s.add_dependency(%q<net-ssh>, ["= 2.0.19"])
end
end

View File

@ -1 +1,2 @@
require 'hydra/pipe' require 'hydra/pipe'
require 'hydra/ssh'

View File

@ -16,8 +16,13 @@ module Hydra #:nodoc:
# #
# A pipe is actually two pipes: # A pipe is actually two pipes:
# #
# Parent === Pipe 1 ==> Child # Parent == Pipe 1 ==> Child
# Parent <== Pipe 2 === Child # Parent <== Pipe 2 == Child
#
# It's like if you had two cardboard tubes and you were using
# them to drop balls with messages in them between processes.
# One tube is for sending from parent to child, and the other
# tube is for sending from child to parent.
class Pipe class Pipe
# Creates a new uninitialized pipe pair. # Creates a new uninitialized pipe pair.
def initialize def initialize
@ -43,15 +48,6 @@ module Hydra #:nodoc:
end end
end end
# Returns true if there is nothing to read (right now). However it is
# not exactly eof, if the other side writes, this will return false.
#
# It's a good way to tell if there is anything to process right now,
# otherwise, you can sleep.
def eof?
@reader.eof?
end
# Identify this side of the pipe as the child. # Identify this side of the pipe as the child.
def identify_as_child def identify_as_child
@parent_write.close @parent_write.close

48
lib/hydra/ssh.rb Normal file
View File

@ -0,0 +1,48 @@
require 'open3'
module Hydra #:nodoc:
# Read and write with an ssh connection. For example:
# @ssh = Hydra::SSH.new('nick@nite')
# @ssh.write("echo hi")
# puts @ssh.gets
# => hi
#
# You can also use this to launch an interactive process. For
# example:
# @ssh = Hydra::SSH.new('nick@nite')
# @ssh.write('irb')
# @ssh.write("5+3")
# @ssh.gets
# => "5+3\n" # because irb echoes commands
# @ssh.gets
# => "8" # the output from irb
class SSH
include Open3
# Initialize new SSH connection. The single parameters is passed
# directly to ssh for starting a connection. So you can do:
# Hydra::SSH.new('localhost')
# Hydra::SSH.new('user@server.com')
# Hydra::SSH.new('-p 3022 user@server.com')
# etc..
def initialize(connection_options)
@stdin, @stdout, @stderr = popen3("ssh #{connection_options}")
end
# Write a string to ssh. This method returns the string passed to
# ssh. Note that if you do not add a newline at the end, it adds
# one for you, and the modified string is returned
def write(str)
unless str =~ /\n$/
str += "\n"
end
@stdin.write(str)
return str
end
# Read a line from ssh. This call blocks when there is nothing
# to read.
def gets
@stdout.gets.chomp
end
end
end

11
test/echo_the_dolphin.rb Normal file
View File

@ -0,0 +1,11 @@
#!/usr/bin/env ruby
# read lines from stdin
# echo each line back
# on EOF, quit nicely
$stdout.sync = true
while line = $stdin.gets
$stdout.write(line)
end

View File

@ -1,6 +1,6 @@
require 'helper' require File.join(File.dirname(__FILE__), 'helper')
class TestPipeDream < Test::Unit::TestCase class TestPipe < Test::Unit::TestCase
context "a pipe" do context "a pipe" do
setup do setup do
@pipe = Hydra::Pipe.new @pipe = Hydra::Pipe.new
@ -16,9 +16,7 @@ class TestPipeDream < Test::Unit::TestCase
@pipe.identify_as_parent @pipe.identify_as_parent
@pipe.write "Test Message\n" @pipe.write "Test Message\n"
assert_equal "Message Received\n", @pipe.gets assert_equal "Message Received\n", @pipe.gets
assert !@pipe.eof?
assert_equal "Second Message\n", @pipe.gets assert_equal "Second Message\n", @pipe.gets
assert @pipe.eof?
assert_raise Hydra::PipeError::Broken do assert_raise Hydra::PipeError::Broken do
@pipe.write "anybody home?" @pipe.write "anybody home?"
end end

29
test/test_ssh.rb Normal file
View File

@ -0,0 +1,29 @@
require File.join(File.dirname(__FILE__), 'helper')
class TestSSH < Test::Unit::TestCase
context "an ssh connection" do
setup do
@ssh = Hydra::SSH.new('localhost')
end
should "be able to execute a command" do
@ssh.write "echo hi"
assert_equal "hi", @ssh.gets
end
should "be able to execute a command with a newline" do
@ssh.write "echo hi\n"
assert_equal "hi", @ssh.gets
end
should "be able to communicate with a process" do
pwd = File.dirname(__FILE__)
echo_the_dolphin = File.expand_path(
File.join(File.dirname(__FILE__), 'echo_the_dolphin.rb')
)
@ssh.write('ruby -e "puts \'Hello\'"')
assert_equal "Hello", @ssh.gets
@ssh.write("ruby #{echo_the_dolphin}")
@ssh.write("Hello Echo!")
assert_equal "Hello Echo!", @ssh.gets
end
end
end