simple_gate 0.5.5
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/MIT-LICENSE +20 -0
- data/README.rdoc +126 -0
- data/Rakefile +49 -0
- data/bin/gate_cp +50 -0
- data/bin/simple_gate +56 -0
- data/lib/simple_gate.rb +87 -0
- data/lib/simple_gate/router.rb +50 -0
- data/lib/simple_gate/server_definition.rb +99 -0
- data/simple_gate.gemspec +38 -0
- data/spec/simple_gate/router_spec.rb +53 -0
- data/spec/simple_gate_spec.rb +6 -0
- data/spec/spec_helper.rb +2 -0
- metadata +101 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Wes Oldenbeuving
|
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.
|
data/README.rdoc
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
= SimpleGate
|
2
|
+
|
3
|
+
SimpleGate makes it possible to use net/ssh/gateway's capabilities in a simple
|
4
|
+
to use way. This makes it possible to write simple scripts that access servers that are located several hops inside a network.
|
5
|
+
|
6
|
+
The core gateway chaining logic was taken from Capistrano, but it has been
|
7
|
+
rewritten to be useful in a more generic way than Capistrano allows.
|
8
|
+
|
9
|
+
SimpleGate's Router class makes it possible to automatically find a route
|
10
|
+
through a maze of servers instead of having to manually define gateway chains.
|
11
|
+
An added benefit is that a change to one server's connections no longer
|
12
|
+
requires you to update all other servers if they relied on it as a gateway.
|
13
|
+
|
14
|
+
SimpleGate is simple, meaning it does not (yet) do fancy things such as using
|
15
|
+
~/.ssh. Because of this, you'll have to define ~/.servers.yml and add entries
|
16
|
+
to configure your servers. To use the routing functionality used by the
|
17
|
+
gate_cp and simple_gate executables, you have to define routes in
|
18
|
+
~/.servers.connections.yml
|
19
|
+
|
20
|
+
An example configuration for the servers 'foobar' and 'barfoo' would look be:
|
21
|
+
|
22
|
+
---
|
23
|
+
foobar:
|
24
|
+
address: "127.0.0.1"
|
25
|
+
username: "foo"
|
26
|
+
password: "bar
|
27
|
+
port: 22
|
28
|
+
barfoo:
|
29
|
+
address: "192.168.0.1"
|
30
|
+
username: "bar"
|
31
|
+
password: "foo
|
32
|
+
port: 22
|
33
|
+
|
34
|
+
Example of a ~/.servers.connections.yml file:
|
35
|
+
|
36
|
+
---
|
37
|
+
local:
|
38
|
+
- foo
|
39
|
+
foo:
|
40
|
+
- bar
|
41
|
+
- baz
|
42
|
+
bar:
|
43
|
+
- foobar
|
44
|
+
- foobaz
|
45
|
+
- barbaz
|
46
|
+
|
47
|
+
All keys are names of servers defined in ~/.servers.yml. The only special node
|
48
|
+
is "local", which is the starting point for all connections and not a real
|
49
|
+
connection.
|
50
|
+
|
51
|
+
== Recent changes
|
52
|
+
|
53
|
+
=== Version 0.5.4
|
54
|
+
|
55
|
+
* Fixed gemspec: add missing spec files and added missing gem dependencies
|
56
|
+
* Updated Rakefile with ruby 1.9 workaround and a missing file spotter
|
57
|
+
|
58
|
+
=== Version 0.5.3
|
59
|
+
|
60
|
+
* Added yardoc documentation to all classes.
|
61
|
+
* SimpleGate#through raises an error when no gates are chosen
|
62
|
+
* SimpleGate#through uses local variables instead of instance variables
|
63
|
+
* ServerDefinition#options is created on the fly
|
64
|
+
|
65
|
+
=== Version 0.5.2
|
66
|
+
|
67
|
+
* Added -V verbose flag to gate_cp
|
68
|
+
* Use STDERR for status messages. STDOUT is used to output SSH responses
|
69
|
+
* Updated executables to support direct server connections
|
70
|
+
* SimpleGate#through_to can connect directly to a server without gateways
|
71
|
+
|
72
|
+
=== Version 0.5.1
|
73
|
+
|
74
|
+
* Updated readme and simple_gate documentation
|
75
|
+
* Implemented infinite loop prevention code in Router#find
|
76
|
+
* Cleaned up Router#find to be more readable
|
77
|
+
* Added spec to show stack overflow occurs when a cyclical graph is used to
|
78
|
+
find a route
|
79
|
+
* Added specs for Router#find
|
80
|
+
* Added a -V (verbose) flag option to simple_gate
|
81
|
+
* Removed empty method definition
|
82
|
+
|
83
|
+
=== Version 0.5.0
|
84
|
+
|
85
|
+
* Added syntax explanation to gate_cp
|
86
|
+
* Added gate_cp, which copies one local file to a remote destination
|
87
|
+
|
88
|
+
=== Version 0.4.1
|
89
|
+
|
90
|
+
* simple_gate is now executable
|
91
|
+
|
92
|
+
=== Version 0.4.0
|
93
|
+
|
94
|
+
* Updated readme with version history for previous gem versions
|
95
|
+
* Added simple_gate executable to use SimpleGate's new Router class to find a path to a server and execute a command there.
|
96
|
+
|
97
|
+
=== Version 0.3.0
|
98
|
+
|
99
|
+
* Readme now has a project description and updated credits for a bit of Capistrano code
|
100
|
+
* Added mutator and documentation for ServerDefinition.servers
|
101
|
+
* Updated comment to make more sense
|
102
|
+
|
103
|
+
=== Version 0.2.0
|
104
|
+
|
105
|
+
* Implemented SimpleGate#through_to, which establishes a real ssh session through a number of gateways
|
106
|
+
|
107
|
+
=== Version 0.1.0
|
108
|
+
|
109
|
+
* Removed simple_gate executable
|
110
|
+
* Imported SimpleGate and ServerDefinition
|
111
|
+
|
112
|
+
=== Version 0.0.1
|
113
|
+
* Created project
|
114
|
+
|
115
|
+
== Installation
|
116
|
+
=== From git
|
117
|
+
From the project root, use rake to install.
|
118
|
+
git clone http://github.com/Narnach/simple_gate
|
119
|
+
cd simple_gate
|
120
|
+
rake install
|
121
|
+
This will build the gem and install it for you.
|
122
|
+
|
123
|
+
== About
|
124
|
+
simple_gate was created by Wes Oldenbeuving. It is licensed under the MIT license.
|
125
|
+
|
126
|
+
SimpleGate#through is based on GatewayConnectionFactory#initialize, which is part of Jamis Buck's Capistrano and is also licensed under the MIT license.
|
data/Rakefile
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require "rake"
|
2
|
+
require "rake/clean"
|
3
|
+
require "rake/gempackagetask"
|
4
|
+
require 'rubygems'
|
5
|
+
|
6
|
+
################################################################################
|
7
|
+
### Gem
|
8
|
+
################################################################################
|
9
|
+
|
10
|
+
begin
|
11
|
+
# Parse gemspec using the github safety level.
|
12
|
+
file = Dir['*.gemspec'].first
|
13
|
+
data = File.read(file)
|
14
|
+
spec = nil
|
15
|
+
# FIXME: Lowered SAFE from 3 to 2 to work with Ruby 1.9 due to rubygems
|
16
|
+
# performing a require internally
|
17
|
+
Thread.new { spec = eval("$SAFE = 2\n%s" % data)}.join
|
18
|
+
|
19
|
+
# Create the gem tasks
|
20
|
+
Rake::GemPackageTask.new(spec) do |package|
|
21
|
+
package.gem_spec = spec
|
22
|
+
end
|
23
|
+
rescue Exception => e
|
24
|
+
printf "WARNING: Error caught (%s): %s\n%s", e.class.name, e.message, e.backtrace[0...5].map {|l| ' %s' % l}.join("\n")
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Package and install the gem for the current version'
|
28
|
+
task :install => :gem do
|
29
|
+
system "sudo gem install -l pkg/%s-%s.gem" % [spec.name, spec.version]
|
30
|
+
end
|
31
|
+
|
32
|
+
desc 'Show files missing from gemspec'
|
33
|
+
task :diff do
|
34
|
+
files = %w[
|
35
|
+
Rakefile
|
36
|
+
*README*
|
37
|
+
*LICENSE*
|
38
|
+
*.gemspec
|
39
|
+
bin/*
|
40
|
+
lib/**/*
|
41
|
+
spec/**/*
|
42
|
+
].map {|pattern| Dir.glob(pattern)}.flatten.select{|f| File.file?(f)}
|
43
|
+
missing_files = files - spec.files
|
44
|
+
extra_files = spec.files - files
|
45
|
+
puts "File missing from the gemspec:"
|
46
|
+
puts missing_files.join(" ")
|
47
|
+
puts "Extra files not (yet) in the gemspec:"
|
48
|
+
puts extra_files.join(" ")
|
49
|
+
end
|
data/bin/gate_cp
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Copy a single local file to a remote server.
|
3
|
+
# Syntax: gate_cp [-V] <server> <source_file> <target_file>
|
4
|
+
# server: a server listed in ~/.servers.yml and reachable by a
|
5
|
+
# series of connections described in ~/.servers.connections.yml
|
6
|
+
# source_file: filename or full path+filename of local source file
|
7
|
+
# target_file: filename or full path+filename of target file
|
8
|
+
# Set -V for verbose output.
|
9
|
+
#
|
10
|
+
# Example: gate_cp foobar ~/foo.txt foo.txt
|
11
|
+
# This copies ~/foo.txt too the server foobar as foo.txt in the home dir
|
12
|
+
#
|
13
|
+
# Example: gate_cp foobar foo.txt /tmp/bar.txt
|
14
|
+
# This copies the local file foo.txt too the server foobar as /tmp/foo.txt
|
15
|
+
require 'simple_gate'
|
16
|
+
require 'simple_gate/router'
|
17
|
+
require 'net/sftp'
|
18
|
+
|
19
|
+
verbose = !ARGV.delete('-V').nil?
|
20
|
+
|
21
|
+
connections = YAML.load_file(File.expand_path('~/.servers.connections.yml'))
|
22
|
+
from = 'local'
|
23
|
+
target = ARGV.shift.to_s.strip
|
24
|
+
|
25
|
+
cmd = ARGV.join(" ")
|
26
|
+
if cmd.strip.size == 0
|
27
|
+
STDERR.puts "No command was given"
|
28
|
+
exit 1
|
29
|
+
end
|
30
|
+
|
31
|
+
router = Router.new(connections)
|
32
|
+
route = router.find(from, target)
|
33
|
+
if route.nil?
|
34
|
+
STDERR.puts "No route to #{target}"
|
35
|
+
exit 1
|
36
|
+
end
|
37
|
+
|
38
|
+
route.shift if route.first == 'local'
|
39
|
+
|
40
|
+
source = File.expand_path(ARGV.shift.to_s.strip)
|
41
|
+
target = ARGV.shift.to_s.strip
|
42
|
+
|
43
|
+
STDERR.puts "Connecting to #{route.last}, using #{route.size - 1} gateway(s)" if verbose
|
44
|
+
|
45
|
+
gate = SimpleGate.new(:verbose=>verbose)
|
46
|
+
gate.through_to(route) do |ssh|
|
47
|
+
STDERR.puts "Transferring: #{source} => #{target}" if verbose
|
48
|
+
ssh.sftp.upload!(source, target)
|
49
|
+
end
|
50
|
+
|
data/bin/simple_gate
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Syntax: simple_gate [-V] <server> <command>
|
3
|
+
# Finds a route to <server>, uses the neccesary gates to connect to it and
|
4
|
+
# executes <command> on that server. Set -V for verbose output.
|
5
|
+
#
|
6
|
+
# Uses ~/.servers.connections.yml and ~/.servers.yml to find the fastest path
|
7
|
+
# from the local server to a target server, establish a series of gateways
|
8
|
+
# and then execute a command on the remote server.
|
9
|
+
# Example of a ~/.servers.connections.yml file:
|
10
|
+
#
|
11
|
+
# ---
|
12
|
+
# local:
|
13
|
+
# - foo
|
14
|
+
# foo:
|
15
|
+
# - bar
|
16
|
+
# - baz
|
17
|
+
# bar:
|
18
|
+
# - foobar
|
19
|
+
# - foobaz
|
20
|
+
# - barbaz
|
21
|
+
#
|
22
|
+
# All keys are names of servers defined in ~/.servers.yml. The only special
|
23
|
+
# node is "local", which is the starting point for all connections and not a
|
24
|
+
# real connection.
|
25
|
+
|
26
|
+
require 'simple_gate'
|
27
|
+
require 'simple_gate/router'
|
28
|
+
|
29
|
+
verbose = !ARGV.delete('-V').nil?
|
30
|
+
|
31
|
+
connections = YAML.load_file(File.expand_path('~/.servers.connections.yml'))
|
32
|
+
from = 'local'
|
33
|
+
target = ARGV.shift.to_s.strip
|
34
|
+
|
35
|
+
cmd = ARGV.join(" ")
|
36
|
+
if cmd.strip.size == 0
|
37
|
+
STDERR.puts "No command was given"
|
38
|
+
exit 1
|
39
|
+
end
|
40
|
+
|
41
|
+
router = Router.new(connections)
|
42
|
+
route = router.find(from, target)
|
43
|
+
if route.nil?
|
44
|
+
STDERR.puts "No route to #{target}"
|
45
|
+
exit 1
|
46
|
+
end
|
47
|
+
|
48
|
+
route.shift if route.first == 'local'
|
49
|
+
|
50
|
+
STDERR.puts "Executing '#{cmd}' in #{route.last}, using #{route.size - 1} gateway(s)" if verbose
|
51
|
+
|
52
|
+
gate = SimpleGate.new(:verbose=>verbose)
|
53
|
+
gate.through_to(route) do |ssh|
|
54
|
+
puts ssh.exec!(cmd)
|
55
|
+
end
|
56
|
+
|
data/lib/simple_gate.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'net/ssh'
|
3
|
+
require 'net/ssh/gateway'
|
4
|
+
require 'activesupport'
|
5
|
+
require 'simple_gate/server_definition'
|
6
|
+
|
7
|
+
class SimpleGate
|
8
|
+
# Initialize a new SimpleGate
|
9
|
+
# @param [Hash] options Hash with options to configure SimpleGate. Defaults to set :verbose to false.
|
10
|
+
def initialize(options={})
|
11
|
+
@options = {
|
12
|
+
:verbose => false
|
13
|
+
}.merge(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Is the verbose option turned on?
|
17
|
+
def verbose?
|
18
|
+
@options[:verbose]
|
19
|
+
end
|
20
|
+
|
21
|
+
# Connect through a list of gateways to a target server.
|
22
|
+
# Treats the last 'gateway' as the target server and the others as gateways.
|
23
|
+
#
|
24
|
+
# @param [Array] *gateways A list of gateway server names that can be found using ServerDefinition.find(). Should have at least one server name.
|
25
|
+
# @yieldparam [Net::SSH::Connection::Session] session SSH Session to the target server.
|
26
|
+
# @raise [ArgumentError] When no gateways are chosen.
|
27
|
+
def through_to(*gateways)
|
28
|
+
gateways = gateways.flatten
|
29
|
+
raise ArgumentError.new("No target chosen") if gateways.size == 0
|
30
|
+
target = ServerDefinition.find(gateways.pop)
|
31
|
+
if gateways.size == 0
|
32
|
+
target.connection_info do |host, user, options|
|
33
|
+
Net::SSH.start(host,user,options) do |session|
|
34
|
+
yield(session)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
return
|
38
|
+
end
|
39
|
+
through(gateways) do |gate|
|
40
|
+
target.connection_info do |host, user, options|
|
41
|
+
gate.ssh(host, user, options) do |session|
|
42
|
+
yield(session)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
# Establish a series of gateways and yields the last one created.
|
50
|
+
# Will automatically shut down gateway connections when the block closes.
|
51
|
+
#
|
52
|
+
# Most of the code was taken from Capistrano and then changed to work
|
53
|
+
# outside of Capistrano.
|
54
|
+
#
|
55
|
+
# @param [Array] *gateways A list of gateway server names that can be found using ServerDefinition.find(). Should have at least one server name.
|
56
|
+
# @yieldparam [Net::SSH::Gateway] gateway Gateway object of the last tunnel endpoint.
|
57
|
+
# @raise [ArgumentError] When no gateways are chosen.
|
58
|
+
def through(*gateways)
|
59
|
+
Thread.abort_on_exception = true
|
60
|
+
open_connections = []
|
61
|
+
gateways = gateways.flatten.collect { |g| ServerDefinition.find(g) }
|
62
|
+
raise ArgumentError.new("No target chosen") if gateways.size == 0
|
63
|
+
tunnel = gateways[0].connection_info do |host, user, connect_options|
|
64
|
+
STDERR.puts "Setting up tunnel #{gateways[0]}" if verbose?
|
65
|
+
gw = Net::SSH::Gateway.new(host, user, connect_options)
|
66
|
+
open_connections << gw
|
67
|
+
gw
|
68
|
+
end
|
69
|
+
gateway = (gateways[1..-1]).inject(tunnel) do |tunnel, destination|
|
70
|
+
STDERR.puts "Connecting to #{destination}" if verbose?
|
71
|
+
tunnel_port = tunnel.open(destination.host, (destination.port || 22))
|
72
|
+
localhost_options = {:user => destination.user, :port => tunnel_port, :password => destination.password}
|
73
|
+
local_host = ServerDefinition.new("127.0.0.1", localhost_options)
|
74
|
+
local_host.connection_info do |host, user, connect_options|
|
75
|
+
STDERR.puts "Connecting using local info #{local_host}" if verbose?
|
76
|
+
gw = Net::SSH::Gateway.new(host, user, connect_options)
|
77
|
+
open_connections << gw
|
78
|
+
gw
|
79
|
+
end
|
80
|
+
end
|
81
|
+
yield(gateway)
|
82
|
+
ensure
|
83
|
+
while g = open_connections.pop
|
84
|
+
g.shutdown!
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class Router
|
2
|
+
# Graph of server connections.
|
3
|
+
attr_accessor :paths
|
4
|
+
|
5
|
+
# Create a new Router for a network of interconnected servers.
|
6
|
+
# An example connections graph with three servers 'foo', 'bar' and 'baz':
|
7
|
+
#
|
8
|
+
# router = Router.new
|
9
|
+
# # Define a graph: foo -> bar -> baz
|
10
|
+
# router.paths = {
|
11
|
+
# 'foo' => %w[bar],
|
12
|
+
# 'bar' => %w[baz]
|
13
|
+
# }
|
14
|
+
# router.find('foo', 'baz') #=> ['foo', 'bar', 'baz']
|
15
|
+
# @param [Hash] paths Graph of server connections.
|
16
|
+
# It's a Hash of Arrays, which contains strings. Keys are server
|
17
|
+
# names with a connection to other servers. Each of these servers is a
|
18
|
+
# string in the associated Array.
|
19
|
+
def initialize(paths={})
|
20
|
+
@paths = paths
|
21
|
+
end
|
22
|
+
|
23
|
+
# A simple recursive pathfinder.
|
24
|
+
# Uses a very naieve depth-first recursive full-graph search to
|
25
|
+
# find the shortest route.
|
26
|
+
# @param [String] start The node to start searching from for +target+.
|
27
|
+
# @param [String] target The node to look for. Once it is found, return the shortest route to it.
|
28
|
+
# @param [Array] current_route Internal variable that holds the route so far. Helps in preventing cyclic routes from being checked an infinite amount of times.
|
29
|
+
# @return [Array] The sequence of nodes that connect +start+ to +target+.
|
30
|
+
# @return [nil] When no route was found.
|
31
|
+
def find(start, target, current_route = [])
|
32
|
+
return [target] if start == target
|
33
|
+
return nil unless paths.has_key?(start)
|
34
|
+
|
35
|
+
# Map all possible paths to the target.
|
36
|
+
# Skip nodes we have already visited
|
37
|
+
next_nodes = paths[start] - current_route
|
38
|
+
routes = next_nodes.map do |next_node|
|
39
|
+
find(next_node, target, current_route + [start])
|
40
|
+
end
|
41
|
+
|
42
|
+
# Reduce the collection to the shortest path
|
43
|
+
shortest_route = routes.compact.inject(nil) {|shortest,possibility|
|
44
|
+
next possibility if shortest.nil?
|
45
|
+
possibility.size < shortest.size ? possibility : shortest
|
46
|
+
}
|
47
|
+
return [start] + shortest_route if shortest_route
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
class ServerDefinition
|
3
|
+
attr_accessor :host, :user, :port, :password, :auth_methods
|
4
|
+
|
5
|
+
# Create a new ServerDefinition.
|
6
|
+
#
|
7
|
+
# @param [String] host Name of server.
|
8
|
+
# @param [Hash] options Override the server's own configuration.
|
9
|
+
# @option options [Integer] port (22) Server port
|
10
|
+
# @option options [String] user SSH user
|
11
|
+
# @option options [String] password SSH password
|
12
|
+
def initialize(host, options = {})
|
13
|
+
self.host = host
|
14
|
+
self.user = options[:user]
|
15
|
+
self.password = options[:password]
|
16
|
+
self.port = options[:port] || 22
|
17
|
+
self.auth_methods = options[:auth_methods] || %w[password keyboard-interactive]
|
18
|
+
end
|
19
|
+
|
20
|
+
# SSH options
|
21
|
+
# @return [Hash]
|
22
|
+
def options
|
23
|
+
{
|
24
|
+
:user => user,
|
25
|
+
:password => password,
|
26
|
+
:port => port,
|
27
|
+
:auth_methods => auth_methods
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
# Yield connection information.
|
32
|
+
# @yieldparam [String] host SSH host
|
33
|
+
# @yieldparam [String] user SSH user
|
34
|
+
# @yieldparam [Hash] options SSH connection options
|
35
|
+
# @see ServerDefinition#initialize
|
36
|
+
# @see ServerDefinition#ssh_options
|
37
|
+
def connection_info(&block)
|
38
|
+
block.call(host, user, options)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Represent server definition as URL-like string
|
42
|
+
# @return [String]
|
43
|
+
def to_s
|
44
|
+
"#{user}:#{'*' * password.to_s.size}@#{host}:#{port}"
|
45
|
+
end
|
46
|
+
|
47
|
+
# Factory method that uses pre-defined server configurations.
|
48
|
+
# @return [ServerDefinition]
|
49
|
+
def self.lookup(server)
|
50
|
+
server = servers[server]
|
51
|
+
new(server['address'],{
|
52
|
+
:user => server['username'],
|
53
|
+
:password => server['password'],
|
54
|
+
:port => server['port']
|
55
|
+
})
|
56
|
+
end
|
57
|
+
|
58
|
+
# Factory method that chooses between a lookup and parse.
|
59
|
+
# @param [String] server Server name or ssh string
|
60
|
+
# @return [ServerDefinition]
|
61
|
+
# @see ServerDefinition.lookup
|
62
|
+
# @see ServerDefinition.parse
|
63
|
+
def self.find(server)
|
64
|
+
servers.has_key?(server) ? lookup(server) : parse(server)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Factory method that parses a connection string.
|
68
|
+
# @param [String] ssh_string String formatted as "user:password@host:port"
|
69
|
+
# @return [ServerDefinition]
|
70
|
+
def self.parse(ssh_string)
|
71
|
+
user, password, host, port = ssh_string.match /\A(.*?):(.*?)@(.*?):(\d*?)\Z/
|
72
|
+
new(host, :user => user, :password => password, :port => port)
|
73
|
+
end
|
74
|
+
|
75
|
+
class << self
|
76
|
+
attr_accessor :servers
|
77
|
+
|
78
|
+
# Access the pre-configured servers. ~/.servers.yml is parsed for this.
|
79
|
+
# An example entry for the servers 'foobar' and 'barfoo' would look like:
|
80
|
+
# ---
|
81
|
+
# foobar:
|
82
|
+
# address: "127.0.0.1"
|
83
|
+
# username: "foo"
|
84
|
+
# password: "bar
|
85
|
+
# port: 22
|
86
|
+
# barfoo:
|
87
|
+
# address: "192.168.0.1"
|
88
|
+
# username: "bar"
|
89
|
+
# password: "foo
|
90
|
+
# port: 22
|
91
|
+
#
|
92
|
+
# Since the parsed Hash of servers is cached, a value can be stored and
|
93
|
+
# the configuration file ignored if desired.
|
94
|
+
# @return [Hash] Server name to server configuration pairs.
|
95
|
+
def servers
|
96
|
+
@servers ||= YAML.load_file(File.expand_path('~/.servers.yml'))
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/simple_gate.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
# Project
|
3
|
+
s.name = 'simple_gate'
|
4
|
+
s.summary = "SimpleGate makes it possible to use net/ssh/gateway's capabilities in a simple to use way."
|
5
|
+
s.description = s.summary
|
6
|
+
s.version = '0.5.5'
|
7
|
+
s.date = '2010-03-29'
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Wes Oldenbeuving"]
|
10
|
+
s.email = "narnach@gmail.com"
|
11
|
+
s.homepage = "http://www.github.com/Narnach/simple_gate"
|
12
|
+
|
13
|
+
# Files
|
14
|
+
root_files = %w[MIT-LICENSE README.rdoc Rakefile simple_gate.gemspec]
|
15
|
+
bin_files = %w[simple_gate gate_cp]
|
16
|
+
lib_files = %w[simple_gate
|
17
|
+
simple_gate/server_definition
|
18
|
+
simple_gate/router]
|
19
|
+
test_files = %w[]
|
20
|
+
spec_files = %w[simple_gate simple_gate/router]
|
21
|
+
other_files = %w[spec/spec_helper.rb]
|
22
|
+
s.bindir = "bin"
|
23
|
+
s.require_path = "lib"
|
24
|
+
s.executables = bin_files
|
25
|
+
s.test_files = test_files.map {|f| 'test/%s_test.rb' % f} + spec_files.map {|f| 'spec/%s_spec.rb' % f}
|
26
|
+
s.files = root_files + bin_files.map {|f| 'bin/%s' % f} + lib_files.map {|f| 'lib/%s.rb' % f} + s.test_files + other_files
|
27
|
+
|
28
|
+
# rdoc
|
29
|
+
s.has_rdoc = true
|
30
|
+
s.extra_rdoc_files = %w[ README.rdoc MIT-LICENSE]
|
31
|
+
s.rdoc_options << '--inline-source' << '--line-numbers' << '--main' << 'README.rdoc'
|
32
|
+
|
33
|
+
# Requirements
|
34
|
+
s.required_ruby_version = ">= 1.8.0"
|
35
|
+
s.add_dependency 'net-ssh', ">= 2.0.0"
|
36
|
+
s.add_dependency 'net-ssh-gateway', ">= 1.0.0"
|
37
|
+
s.add_dependency 'activesupport', ">= 2.2.0"
|
38
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
|
+
require 'simple_gate/router'
|
3
|
+
|
4
|
+
describe Router do
|
5
|
+
describe "#find" do
|
6
|
+
it "should find a direct path from start to target" do
|
7
|
+
router = Router.new({
|
8
|
+
'start' => %w[target]
|
9
|
+
})
|
10
|
+
router.find('start','target').should == %w[start target]
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should find a path from start to target through an intermediate node" do
|
14
|
+
router = Router.new({
|
15
|
+
'start' => %w[node],
|
16
|
+
'node' => %w[target],
|
17
|
+
})
|
18
|
+
router.find('start','target').should == %w[start node target]
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should find the shortest path from start to target" do
|
22
|
+
router = Router.new({
|
23
|
+
'start' => %w[node node2 node3],
|
24
|
+
'node' => %w[node2],
|
25
|
+
'node2' => %w[node3],
|
26
|
+
'node3' => %w[target]
|
27
|
+
})
|
28
|
+
router.find('start','target').should == %w[start node3 target]
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should return nil if no route could be found' do
|
32
|
+
router = Router.new({
|
33
|
+
'start' => %w[],
|
34
|
+
})
|
35
|
+
router.find('start','target').should be_nil
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should return nil if the starting point can not be found' do
|
39
|
+
router = Router.new({})
|
40
|
+
router.find('start','target').should be_nil
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should not cause a stack overflow in a cyclical route graph' do
|
44
|
+
router = Router.new({
|
45
|
+
'start' => %w[node],
|
46
|
+
'node' => %w[node target],
|
47
|
+
})
|
48
|
+
lambda {
|
49
|
+
router.find('start','target').should == %w[start node target]
|
50
|
+
}.should_not raise_error(SystemStackError)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple_gate
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Wes Oldenbeuving
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-03-29 00:00:00 +02:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: net-ssh
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.0.0
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: net-ssh-gateway
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.0.0
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: activesupport
|
37
|
+
type: :runtime
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 2.2.0
|
44
|
+
version:
|
45
|
+
description: SimpleGate makes it possible to use net/ssh/gateway's capabilities in a simple to use way.
|
46
|
+
email: narnach@gmail.com
|
47
|
+
executables:
|
48
|
+
- simple_gate
|
49
|
+
- gate_cp
|
50
|
+
extensions: []
|
51
|
+
|
52
|
+
extra_rdoc_files:
|
53
|
+
- README.rdoc
|
54
|
+
- MIT-LICENSE
|
55
|
+
files:
|
56
|
+
- MIT-LICENSE
|
57
|
+
- README.rdoc
|
58
|
+
- Rakefile
|
59
|
+
- simple_gate.gemspec
|
60
|
+
- bin/simple_gate
|
61
|
+
- bin/gate_cp
|
62
|
+
- lib/simple_gate.rb
|
63
|
+
- lib/simple_gate/server_definition.rb
|
64
|
+
- lib/simple_gate/router.rb
|
65
|
+
- spec/simple_gate_spec.rb
|
66
|
+
- spec/simple_gate/router_spec.rb
|
67
|
+
- spec/spec_helper.rb
|
68
|
+
has_rdoc: true
|
69
|
+
homepage: http://www.github.com/Narnach/simple_gate
|
70
|
+
licenses: []
|
71
|
+
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options:
|
74
|
+
- --inline-source
|
75
|
+
- --line-numbers
|
76
|
+
- --main
|
77
|
+
- README.rdoc
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: 1.8.0
|
85
|
+
version:
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: "0"
|
91
|
+
version:
|
92
|
+
requirements: []
|
93
|
+
|
94
|
+
rubyforge_project:
|
95
|
+
rubygems_version: 1.3.5
|
96
|
+
signing_key:
|
97
|
+
specification_version: 3
|
98
|
+
summary: SimpleGate makes it possible to use net/ssh/gateway's capabilities in a simple to use way.
|
99
|
+
test_files:
|
100
|
+
- spec/simple_gate_spec.rb
|
101
|
+
- spec/simple_gate/router_spec.rb
|