simple_gate 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
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
+
@@ -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
@@ -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
@@ -0,0 +1,6 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+ require 'simple_gate'
3
+
4
+ describe SimpleGate do
5
+ it 'should be tested'
6
+ end
@@ -0,0 +1,2 @@
1
+ lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(lib_dir)
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