relay 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2008 Michel Martens and Damian Janowski
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,69 @@
1
+ Relay
2
+ =====
3
+
4
+ Relay commands over SSH.
5
+
6
+ Description
7
+ -----------
8
+
9
+ Relay is a simple library that allows you to send commands over SSH.
10
+ It uses your own SSH, not a Ruby version, so you can profit from your
11
+ settings and public/private keys.
12
+
13
+ Usage
14
+ -----
15
+
16
+ To send a command to a server called `myserver`:
17
+
18
+ $ relay execute "ls -al" myserver
19
+ $ relay execute "cd foo; ls" myserver
20
+
21
+ If you want to send more commands, you can write a shell script:
22
+
23
+ $ cat recipe.sh
24
+ cd foo
25
+ ls
26
+ mkdir -p bar/baz
27
+
28
+ $ relay recipe.sh myserver
29
+
30
+ It will execute those commands on `myserver` and show the output.
31
+
32
+ This last form accepts one file as the recipe and one or many servers:
33
+
34
+ $ relay recipe.sh server1 server2 server3
35
+
36
+ You can also add your public key to a remote server's authorized keys file:
37
+
38
+ $ relay identify myserver
39
+
40
+ Installation
41
+ ------------
42
+
43
+ $ sudo gem install relay
44
+
45
+ License
46
+ -------
47
+
48
+ Copyright (c) 2009 Michel Martens and Damian Janowski
49
+
50
+ Permission is hereby granted, free of charge, to any person
51
+ obtaining a copy of this software and associated documentation
52
+ files (the "Software"), to deal in the Software without
53
+ restriction, including without limitation the rights to use,
54
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
55
+ copies of the Software, and to permit persons to whom the
56
+ Software is furnished to do so, subject to the following
57
+ conditions:
58
+
59
+ The above copyright notice and this permission notice shall be
60
+ included in all copies or substantial portions of the Software.
61
+
62
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
63
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
64
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
65
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
66
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
67
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
68
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
69
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,5 @@
1
+ task :test do
2
+ system "cd test && ruby relay_test.rb"
3
+ end
4
+
5
+ task :default => :test
@@ -0,0 +1,21 @@
1
+ #! /usr/bin/env ruby -rubygems
2
+
3
+ require File.join(File.dirname(__FILE__), "..", "lib", "relay")
4
+
5
+ # A way to extend Relay is to write tasks in a Thorfile in the project's root directory.
6
+ # Relay loads the Thorfile if there is one, and all the tasks that are declared in the
7
+ # class Relay become available.
8
+ if File.exists?("Thorfile")
9
+ load("Thorfile")
10
+ end
11
+
12
+ # Start the relay tasks.
13
+ if ARGV[0] && File.exists?(ARGV[0])
14
+ file = ARGV.shift
15
+
16
+ ARGV.each do |server|
17
+ Relay.new.recipe(file, server)
18
+ end
19
+ else
20
+ Relay.start
21
+ end
@@ -0,0 +1,71 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require "thor"
4
+ require "open3"
5
+
6
+ class Ssh
7
+ def initialize(server)
8
+ @server = server
9
+ end
10
+
11
+ def start
12
+ Open3.popen3("ssh -T #{@server}") do |stdin, stdout, stderr|
13
+ threads = []
14
+
15
+ @stdin, @stdout, @stderr = stdin, stdout, stderr
16
+
17
+ threads << Thread.new do
18
+ while line = stderr.gets
19
+ $stderr.puts(line)
20
+ end
21
+ end
22
+
23
+ threads << Thread.new do
24
+ while line = stdout.gets
25
+ $stdout.puts(line)
26
+ end
27
+ end
28
+
29
+ yield(self)
30
+ stdin.close
31
+ threads.each {|t| t.join }
32
+ end
33
+ end
34
+
35
+ def run(command)
36
+ puts "\e[1m\e[33m$ #{command.strip}\e[0m"
37
+ @stdin.puts(command)
38
+ end
39
+ end
40
+
41
+ class Relay < Thor
42
+
43
+ desc "identify SERVER", "Copies your public key to a remote server"
44
+ method_option :key, :type => :string, :aliases => "-k"
45
+ method_option :path, :type => :string, :aliases => "-p"
46
+ def identify(server)
47
+ path = options[:path] || "~/.ssh/authorized_keys"
48
+ keys = [options[:key], "~/.ssh/id_rsa.pub", "~/.ssh/id_dsa.pub"].compact
49
+ key = keys.find { |k| File.exists?(File.expand_path(k)) }
50
+
51
+ if system %Q{cat #{key} | ssh #{server} "cat >> #{path}"}
52
+ say_status :copied, "#{key} to #{server}:#{path}"
53
+ end
54
+ end
55
+
56
+ desc "recipe RECIPE SERVER", "Execute commands contained in RECIPE in the context of SERVER"
57
+ def recipe(recipe, server)
58
+ Ssh.new(server).start do |session|
59
+ File.readlines(recipe).each do |command|
60
+ session.run(command)
61
+ end
62
+ end
63
+ end
64
+
65
+ desc "execute COMMAND SERVER", "Execute COMMAND in the context of SERVER"
66
+ def execute(command, server)
67
+ Ssh.new(server).start do |session|
68
+ session.run(command)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,17 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "relay"
3
+ s.version = "0.0.2"
4
+ s.summary = "Relay commands over SSH"
5
+ s.description = "Relay allows you to execute remote commands via SSH with ease."
6
+ s.authors = ["Damian Janowski", "Michel Martens"]
7
+ s.email = ["djanowski@dimaion.com", "michel@soveran.com"]
8
+ s.homepage = "http://github.com/soveran/relay"
9
+
10
+ s.rubyforge_project = "relay"
11
+
12
+ s.executables << "relay"
13
+
14
+ s.add_dependency("thor", "~> 0.11")
15
+
16
+ s.files = ["LICENSE", "README.markdown", "Rakefile", "bin/relay", "lib/relay.rb", "relay.gemspec", "test/commands.rb", "test/relay_test.rb", "test/test_helper.rb"]
17
+ end
@@ -0,0 +1,72 @@
1
+ require "open3"
2
+ require "socket"
3
+
4
+ module Test::Commands
5
+ def sh(cmd)
6
+ out, err = nil
7
+
8
+ Open3.popen3(cmd) do |_in, _out, _err|
9
+ out = _out.read
10
+ err = _err.read
11
+ end
12
+
13
+ [out, err]
14
+ end
15
+
16
+ # Runs a command in the background, silencing all output.
17
+ # For debugging purposes, set the environment variable VERBOSE.
18
+ def sh_bg(cmd)
19
+ if ENV["VERBOSE"]
20
+ streams_to_silence = []
21
+ else
22
+ streams_to_silence = [$stdout, $stderr]
23
+ cmd = "#{cmd} 2>&1>/dev/null"
24
+ end
25
+
26
+ silence_stream(*streams_to_silence) do
27
+ (pid = fork) ? Process.detach(pid) : exec(cmd)
28
+ end
29
+ end
30
+
31
+ def listening?(host, port)
32
+ begin
33
+ socket = TCPSocket.new(host, port)
34
+ socket.close unless socket.nil?
35
+ true
36
+ rescue Errno::ECONNREFUSED,
37
+ Errno::EBADF, # Windows
38
+ Errno::EADDRNOTAVAIL # Windows
39
+ false
40
+ end
41
+ end
42
+
43
+ def wait_for_service(host, port, timeout = 3)
44
+ start_time = Time.now
45
+
46
+ until listening?(host, port)
47
+ if timeout && (Time.now > (start_time + timeout))
48
+ raise SocketError.new("Socket #{host}:#{port} did not open within #{timeout} seconds")
49
+ end
50
+ end
51
+
52
+ true
53
+ end
54
+
55
+ def suspects(port)
56
+ list = sh("lsof -i :#{port}").first.split("\n")[1..-1] || []
57
+ list.map {|s| s[/^.+? (\d+)/, 1] }
58
+ end
59
+
60
+ def silence_stream(*streams) #:yeild:
61
+ on_hold = streams.collect{ |stream| stream.dup }
62
+ streams.each do |stream|
63
+ stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
64
+ stream.sync = true
65
+ end
66
+ yield
67
+ ensure
68
+ streams.each_with_index do |stream, i|
69
+ stream.reopen(on_hold[i])
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,42 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class TestRelay < Test::Unit::TestCase
4
+ context "relay identify SERVER" do
5
+ should "copy the public key to SERVER" do
6
+ Dir.chdir(root("test", "tmp")) do
7
+ out, err = relay("identify localhost --path #{root("test", "tmp", "authorized_keys")}")
8
+
9
+ assert_match /copied/, out
10
+ assert File.exists?("authorized_keys")
11
+ assert File.read("authorized_keys")[/^ssh-/]
12
+ end
13
+ end
14
+
15
+ should "send a command to SERVER" do
16
+ Dir.chdir(root("test", "tmp")) do
17
+ FileUtils.touch("foobar")
18
+
19
+ out, err = relay("execute \"ls #{root("test", "tmp")}\" localhost")
20
+
21
+ assert_match /foobar/, out
22
+ end
23
+ end
24
+
25
+ should "relay a recipe of commands to SERVER" do
26
+ Dir.chdir(root("test", "tmp")) do
27
+ File.open("recipe.sh", "w") do |file|
28
+ file.puts "cd #{root("test", "tmp")}"
29
+ file.puts "ls -al > list"
30
+ file.puts "cat list"
31
+ end
32
+
33
+ out, err = relay("recipe.sh localhost")
34
+
35
+ assert out["$ cd #{root("test", "tmp")}"]
36
+ assert out["$ ls -al > list"]
37
+ assert out["$ cat list"]
38
+ assert out["recipe.sh"]
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,33 @@
1
+ require "rubygems"
2
+ require "contest"
3
+ require "fileutils"
4
+
5
+ ROOT = File.expand_path(File.join(File.dirname(__FILE__), ".."))
6
+
7
+ $:.unshift ROOT
8
+
9
+ require "test/commands"
10
+
11
+ class Test::Unit::TestCase
12
+ include Test::Commands
13
+
14
+ def root(*args)
15
+ File.join(ROOT, *args)
16
+ end
17
+
18
+ def setup
19
+ Dir[root("test", "tmp", "*")].each do |file|
20
+ FileUtils.rm(file)
21
+ end
22
+ end
23
+
24
+ def teardown
25
+ Dir[root("test", "tmp", "*")].each do |file|
26
+ FileUtils.rm(file)
27
+ end
28
+ end
29
+
30
+ def relay(args = nil)
31
+ sh("ruby -rubygems #{root "bin/relay"} #{args}")
32
+ end
33
+ end
metadata CHANGED
@@ -1,30 +1,50 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: relay
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
- - TBD
7
+ - Damian Janowski
8
+ - Michel Martens
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
12
 
12
- date: 2009-08-07 00:00:00 -03:00
13
+ date: 2009-11-21 00:00:00 -03:00
13
14
  default_executable:
14
- dependencies: []
15
-
16
- description: TBD
17
- email: TBD
18
- executables: []
19
-
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: thor
18
+ type: :runtime
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: "0.11"
25
+ version:
26
+ description: Relay allows you to execute remote commands via SSH with ease.
27
+ email:
28
+ - djanowski@dimaion.com
29
+ - michel@soveran.com
30
+ executables:
31
+ - relay
20
32
  extensions: []
21
33
 
22
34
  extra_rdoc_files: []
23
35
 
24
- files: []
25
-
36
+ files:
37
+ - LICENSE
38
+ - README.markdown
39
+ - Rakefile
40
+ - bin/relay
41
+ - lib/relay.rb
42
+ - relay.gemspec
43
+ - test/commands.rb
44
+ - test/relay_test.rb
45
+ - test/test_helper.rb
26
46
  has_rdoc: true
27
- homepage:
47
+ homepage: http://github.com/soveran/relay
28
48
  licenses: []
29
49
 
30
50
  post_install_message:
@@ -46,10 +66,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
46
66
  version:
47
67
  requirements: []
48
68
 
49
- rubyforge_project:
69
+ rubyforge_project: relay
50
70
  rubygems_version: 1.3.5
51
71
  signing_key:
52
72
  specification_version: 3
53
- summary: TBD
73
+ summary: Relay commands over SSH
54
74
  test_files: []
55
75