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 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