sshkit 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3898c2edc5a5a2dc313f80f0170c5511f67358ee
4
- data.tar.gz: 2d9a40af7ea3e3f862d68c5bdc9b892f848cbbc6
3
+ metadata.gz: 6cd81576d8df6878627ee2ca1b298c2079698085
4
+ data.tar.gz: 9676306af0f0de95b974134d580fed271486004f
5
5
  SHA512:
6
- metadata.gz: 4d04c3dec016fdb417fb550b5f3a4d247a1d1165867e60457b5cf652d2a8b6660721a9e703442ba611c57397b1b85133ccb16b4c539f051cd9e3f251bed120e7
7
- data.tar.gz: 7389b19eb7a6f724207d5f00b889ff3ea93f1b70b48f02a4dc9a705d1d4563c3d4f09e510bf0b6f4791a33ccf8b6dc8b2c9eab37627c389ff7ab723c6d5a4b0a
6
+ metadata.gz: 4a0cfdd77807a8e280376f8f5a07b0f7bac96339091c67bb2c3e7e2424247e192436451ca189772352095f9132afc57e7c660381180812ca315c2ad0715e58f4
7
+ data.tar.gz: 0e4037910686419d816927cddae4a065021c8e7ebae2530c55d62ea299414310e1bb9fcb44fd88dabde85de069667e537aec5a4ff2dc5f9dff55cf9bc0bc592f
data/.gitignore CHANGED
@@ -1,5 +1,5 @@
1
1
  *.gem
2
2
  .yardoc
3
- .vagrant
3
+ .vagrant*
4
4
  test/tmp
5
5
  Gemfile.lock
@@ -3,5 +3,5 @@ rvm:
3
3
  - 2.0.0
4
4
  - 1.9.3
5
5
  - 1.9.2
6
- - rbx-19mode
6
+ - rbx
7
7
  script: "rake test:units"
@@ -3,29 +3,44 @@
3
3
  This file is written in reverse chronological order, newer releases will
4
4
  appear at the top.
5
5
 
6
+ ## (Unreleased)
7
+
8
+ * Add your entries here, remember to credit yourself however you want to be
9
+ credited!
10
+
11
+ ## 1.3.0
12
+
13
+ * Connection pooling. SSH connections are reused across multiple invocations
14
+ of `on()`, which can result in significant performance gains. See:
15
+ https://github.com/capistrano/sshkit/pull/70. Matt @mbrictson Brictson.
16
+ * Fixes to the Formatter::Dot and to the formatter class name resolver. @hab287:w
17
+ * Added the license to the Gemspec. @anatol.
18
+ * Fix :limit handling for the `in: :groups` run mode. Phil @phs Smith
19
+ * Doc fixes @seanhandley, @sergey-alekseev.
20
+
6
21
  ## 1.2.0
7
22
 
8
23
  * Support picking up a project local SSH config file, if a SSH config file
9
24
  exists at ./.ssh/config it will be merged with the ~/.ssh/config. This is
10
- ideal for defining project-local proxies/gateways, etc. Thanks to Alex
25
+ ideal for defining project-local proxies/gateways, etc. Thanks to Alex
11
26
  @0rca Vzorov.
12
- * Tests and general improvements to the Printer backends (mostly used
27
+ * Tests and general improvements to the Printer backends (mostly used
13
28
  internally). Thanks to Michael @miry Nikitochkin.
14
- * Update the net-scp dependency version. Thanks again to Michael @miry
29
+ * Update the net-scp dependency version. Thanks again to Michael @miry
15
30
  Nikitochkin.
16
31
  * Improved command map. This feature allows mapped variables to be pushed
17
32
  and unshifted onto the mapping so that the Capistrano extensions for
18
33
  rbenv and bundler, etc can work together. For discussion about the reasoning
19
- see https://github.com/capistrano/capistrano/issues/639 and
34
+ see https://github.com/capistrano/capistrano/issues/639 and
20
35
  https://github.com/capistrano/sshkit/pull/45. A big thanks to Kir @kirs
21
36
  Shatrov.
22
37
  * `test()` and `capture()` now behave as expected inside a `run_locally` block
23
38
  meaning that they now run on your local machine, rather than erring out. Thanks
24
39
  to Kentaro @kentaroi Imai.
25
- * The `:wait` option is now successfully passed to the runner now. Previously the
40
+ * The `:wait` option is now successfully passed to the runner now. Previously the
26
41
  `:wait` option was ignored. Thanks to Jordan @jhollinger Hollinger for catching
27
42
  the mistake in our test coverage.
28
- * Fixes and general improvements to the `download()` method which until now was
43
+ * Fixes and general improvements to the `download()` method which until now was
29
44
  quite naïve. Thanks to @chqr.
30
45
 
31
46
  ## 1.1.0
data/FAQ.md CHANGED
@@ -5,7 +5,7 @@ a toolkit for performing structured commands on groups of servers in a
5
5
  repeatable way.
6
6
 
7
7
  It provides concurrency handling, sane error checking and control flow that
8
- would otherwise be difficult to achive with pure *Net::SSH*.
8
+ would otherwise be difficult to achieve with pure *Net::SSH*.
9
9
 
10
10
  Since *Capistrano v3.0*, *SSHKit* is used by *Capistrano* to communicate with
11
11
  backend servers. Whilst Capistrano provides the structure for repeatable
@@ -24,7 +24,7 @@ descriptors which no longer exist.
24
24
 
25
25
  The following resources are worth a read to better understand what a process
26
26
  must do in order to daemonize reliably, not all processes perform all of the
27
- steps necassary:
27
+ steps necessary:
28
28
 
29
29
  * [http://stackoverflow.com/questions/881388/what-is-the-reason-for-performing-a-double-fork-when-creating-a-daemon]
30
30
 
data/Gemfile CHANGED
@@ -1,3 +1,8 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
+
5
+ platforms :rbx do
6
+ gem 'rubysl', '~> 2.0'
7
+ gem 'rubysl-json'
8
+ end
data/README.md CHANGED
@@ -55,13 +55,13 @@ Helpers such as `runner()` and `rake()` which expand to `execute(:rails, "runner
55
55
  Notice on the `on()` call the `in: :sequence` option, the following will do
56
56
  what you might expect:
57
57
 
58
- on(in: :parallel, limit: 2) { ...}
58
+ on(in: :parallel) { ... }
59
59
  on(in: :sequence, wait: 5) { ... }
60
60
  on(in: :groups, limit: 2, wait: 5) { ... }
61
61
 
62
- The default is to run `in: :parallel` with no limit, if you have 400 servers,
63
- this might be a problem, and you might better look at changing that to run in
64
- groups, or sequence.
62
+ The default is to run `in: :parallel` which has no limit. If you have 400 servers,
63
+ this might be a problem and you might better look at changing that to run in
64
+ `groups`, or `sequence`.
65
65
 
66
66
  Groups were designed in this case to relieve problems (mass Git checkouts)
67
67
  where you rely on a contested resource that you don't want to DDOS by hitting
@@ -194,6 +194,29 @@ and defaults to `Logger::INFO`.
194
194
 
195
195
  At present the `Logger::WARN`, `ERROR` and `FATAL` are not used.
196
196
 
197
+ ## Connection Pooling
198
+
199
+ SSHKit uses a simple connection pool (enabled by default) to reduce the
200
+ cost of negotiating a new SSH connection for every `on()` block. Depending on
201
+ usage and network conditions, this can add up to a significant time savings.
202
+ In one test, a basic `cap deploy` ran 15-20 seconds faster thanks to the
203
+ connection pooling added in recent versions of SSHKit.
204
+
205
+ To prevent connections from "going stale", an existing pooled connection will
206
+ be replaced with a new connection if it hasn't been used for more than 30
207
+ seconds. This timeout can be changed as follows:
208
+
209
+ ```ruby
210
+ SSHKit::Backend::Netssh.pool.idle_timeout = 60 # seconds
211
+ ```
212
+
213
+ If you suspect the connection pooling is causing problems, you can disable the
214
+ pooling behavior entirely by setting the idle_timeout to zero:
215
+
216
+ ```ruby
217
+ SSHKit::Backend::Netssh.pool.idle_timeout = 0 # disabled
218
+ ```
219
+
197
220
  ## Known Issues
198
221
 
199
222
  * No handling of slow / timed out connections
@@ -225,14 +248,14 @@ At present the `Logger::WARN`, `ERROR` and `FATAL` are not used.
225
248
  a string to/from a remote file.
226
249
  * No closing of connections, the abstract backend class should include a
227
250
  cleanup method which is empty but can be overriden by other implementations.
228
- * No conncetion pooling, the `connection` method of the NetSSH backend could
251
+ * ~~No conncetion pooling, the `connection` method of the NetSSH backend could
229
252
  easily be modified to look into some connection factory for it's objects,
230
- saving half a second when running lots of `on()` blocks.
253
+ saving half a second when running lots of `on()` blocks.~~
231
254
  * Documentation! (YARD style)
232
255
  * Wrap all commands in a known shell, that is that `execute('uptime')` should
233
256
  be converted into `sh -c 'uptime'` to ensure that we have a consistent shell
234
257
  experience.
235
- * There's no suitable host parser that accepts `Host.new('user@ip:port')`, it
236
- will decode a `user@hostname:port`, but IP addresses don't work.
258
+ * ~~There's no suitable host parser that accepts `Host.new('user@ip:port')`, it
259
+ will decode a `user@hostname:port`, but IP addresses don't work.~~
237
260
  * If Net::SSH raises `IOError` (as it does when authentication fails) this
238
261
  needs to be caught, and re-raised as some kind of ConnectionFailed error.
data/Rakefile CHANGED
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
- require 'debugger'
4
3
  require 'rake/testtask'
5
4
 
6
5
  namespace :test do
@@ -15,15 +14,9 @@ namespace :test do
15
14
  t.test_files = FileList['test/functional/**/test*.rb']
16
15
  end
17
16
 
18
- Rake::TestTask.new(:integration) do |t|
19
- t.libs << "test"
20
- t.test_files = FileList['test/integration/**/test*.rb']
21
- end
22
-
23
17
  task :test do
24
18
  Rake::Task['test:units'].execute
25
- Rake::Task['test:integration'].execute
26
- Rake::Task['test:functional'].execute unless ENV['TRAVIS']
19
+ Rake::Task['test:functional'].execute
27
20
  end
28
21
 
29
22
  task default: :test
@@ -1,19 +1,17 @@
1
- #
2
- # vi: set ft=ruby
3
- #
1
+ VAGRANTFILE_API_VERSION = "2"
4
2
 
5
- Vagrant::Config.run do |config|
3
+ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
4
+ config.vm.box = 'precise64'
5
+ config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/precise/current/precise-server-cloudimg-amd64-vagrant-disk1.box"
6
6
 
7
- config.vm.define :one do |one|
8
- one.vm.box = "precise64"
9
- end
10
-
11
- config.vm.define :two do |one|
12
- one.vm.box = "precise64"
13
- end
7
+ json_config_path = File.join("test", "boxes.json")
8
+ list = File.open(json_config_path).read
9
+ list = JSON.parse(list)
14
10
 
15
- config.vm.define :three do |one|
16
- one.vm.box = "precise64"
11
+ list.each do |vm|
12
+ config.vm.define vm["name"] do |web|
13
+ web.vm.box = "precise64"
14
+ web.vm.network "forwarded_port", guest: 22, host: vm["port"]
15
+ end
17
16
  end
18
-
19
17
  end
@@ -23,6 +23,7 @@ require_relative 'runners/group'
23
23
  require_relative 'runners/null'
24
24
 
25
25
  require_relative 'backends/abstract'
26
+ require_relative 'backends/connection_pool'
26
27
  require_relative 'backends/printer'
27
28
  require_relative 'backends/netssh'
28
29
  require_relative 'backends/local'
@@ -129,10 +129,6 @@ module SSHKit
129
129
  SSHKit::Command.new(*[*args, options.merge({in: @pwd.nil? ? nil : File.join(@pwd), env: @env, host: @host, user: @user, group: @group})])
130
130
  end
131
131
 
132
- def connection
133
- raise "No Connection Pool Implementation"
134
- end
135
-
136
132
  end
137
133
 
138
134
  end
@@ -0,0 +1,71 @@
1
+ require "monitor"
2
+
3
+ module SSHKit
4
+
5
+ module Backend
6
+
7
+ class ConnectionPool
8
+
9
+ attr_accessor :idle_timeout
10
+
11
+ def initialize
12
+ self.idle_timeout = 30
13
+ @connections = {}
14
+ @monitor = Monitor.new
15
+ end
16
+
17
+ def create_or_reuse_connection(*new_connection_args, &block)
18
+ # Optimization: completely bypass the pool if idle_timeout is zero.
19
+ return yield(*new_connection_args) if idle_timeout == 0
20
+
21
+ key = new_connection_args.to_s
22
+ entry = find_and_reject_invalid(key) { |e| e.expired? || e.closed? }
23
+
24
+ if entry.nil?
25
+ entry = store_entry(key, yield(*new_connection_args))
26
+ end
27
+
28
+ entry.expires_at = Time.now + idle_timeout if idle_timeout
29
+ entry.connection
30
+ end
31
+
32
+ private
33
+
34
+ def find_and_reject_invalid(key, &block)
35
+ synchronize do
36
+ entry = @connections[key]
37
+ invalid = entry && yield(entry)
38
+
39
+ @connections.delete(entry) if invalid
40
+
41
+ invalid ? nil : entry
42
+ end
43
+ end
44
+
45
+ def store_entry(key, connection)
46
+ synchronize do
47
+ @connections[key] = Entry.new(connection)
48
+ end
49
+ end
50
+
51
+ def synchronize(&block)
52
+ @monitor.synchronize(&block)
53
+ end
54
+
55
+
56
+ Entry = Struct.new(:connection) do
57
+ attr_accessor :expires_at
58
+
59
+ def expired?
60
+ expires_at && Time.now > expires_at
61
+ end
62
+
63
+ def closed?
64
+ connection.respond_to?(:closed?) && connection.closed?
65
+ end
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+ end
@@ -86,7 +86,11 @@ module SSHKit
86
86
  ssh.scp.download!(remote, local, options, &summarizer)
87
87
  end
88
88
 
89
+ @pool = SSHKit::Backend::ConnectionPool.new
90
+
89
91
  class << self
92
+ attr_accessor :pool
93
+
90
94
  def configure
91
95
  yield config
92
96
  end
@@ -166,10 +170,11 @@ module SSHKit
166
170
  def ssh
167
171
  @ssh ||= begin
168
172
  host.ssh_options ||= Netssh.config.ssh_options
169
- Net::SSH.start(
173
+ self.class.pool.create_or_reuse_connection(
170
174
  String(host.hostname),
171
175
  host.username,
172
- host.netssh_options
176
+ host.netssh_options,
177
+ &Net::SSH.method(:start)
173
178
  )
174
179
  end
175
180
  end
@@ -44,7 +44,9 @@ module SSHKit
44
44
  end
45
45
 
46
46
  def formatter(format)
47
- SSHKit::Formatter.const_get(format.capitalize).new($stdout)
47
+ SSHKit::Formatter.constants.each do |const|
48
+ return SSHKit::Formatter.const_get(const).new($stdout) if const.downcase.eql?(format.downcase)
49
+ end
48
50
  end
49
51
 
50
52
  end
@@ -7,7 +7,7 @@ module SSHKit
7
7
  class Dot < Abstract
8
8
 
9
9
  def write(obj)
10
- return unless obj === SSHKit::Command
10
+ return unless obj.is_a? SSHKit::Command
11
11
  if obj.finished?
12
12
  original_output << (obj.failure? ? c.red('.') : c.green('.'))
13
13
  end
@@ -12,7 +12,7 @@ module SSHKit
12
12
  end
13
13
  private
14
14
  def group_size
15
- @group_size ||= 2
15
+ @group_size || options[:limit] || 2
16
16
  end
17
17
  end
18
18
 
@@ -1,3 +1,3 @@
1
1
  module SSHKit
2
- VERSION = "1.2.0"
2
+ VERSION = "1.3.0"
3
3
  end
@@ -7,7 +7,8 @@ Gem::Specification.new do |gem|
7
7
  gem.email = ["lee.hambley@gmail.com", "seenmyfate@gmail.com"]
8
8
  gem.summary = %q{SSHKit makes it easy to write structured, testable SSH commands in Ruby}
9
9
  gem.description = %q{A comprehensive toolkit for remotely running commands in a structured manner on groups of servers.}
10
- gem.homepage = "http://wacku.github.com/sshkit"
10
+ gem.homepage = "http://github.com/capistrano/sshkit"
11
+ gem.license = "GPL3"
11
12
 
12
13
  gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
13
14
  gem.files = `git ls-files`.split("\n")
@@ -25,10 +26,5 @@ Gem::Specification.new do |gem|
25
26
  gem.add_development_dependency('turn')
26
27
  gem.add_development_dependency('unindent')
27
28
  gem.add_development_dependency('mocha')
28
- gem.add_development_dependency('debugger')
29
- gem.add_development_dependency('vagrant')
30
-
31
- gem.add_development_dependency('yard')
32
- gem.add_development_dependency('redcarpet')
33
29
 
34
30
  end
@@ -0,0 +1,14 @@
1
+ [
2
+ {
3
+ "name": "one",
4
+ "port": 3001
5
+ },
6
+ {
7
+ "name": "two",
8
+ "port": 3002
9
+ },
10
+ {
11
+ "name": "three",
12
+ "port": 3003
13
+ }
14
+ ]
@@ -28,7 +28,7 @@ module SSHKit
28
28
  end
29
29
 
30
30
  def a_host
31
- vm_hosts.first
31
+ VagrantWrapper.hosts["one"]
32
32
  end
33
33
 
34
34
  def printer
@@ -56,9 +56,9 @@ module SSHKit
56
56
  SSHKit.capture_output(dnull) do
57
57
  captured_command_result = ""
58
58
  Netssh.new(a_host) do |host|
59
- captured_command_result = capture(:hostname)
59
+ captured_command_result = capture(:uname)
60
60
  end.run
61
- assert_equal "lucid32", captured_command_result
61
+ assert_equal "Linux", captured_command_result
62
62
  end
63
63
  end
64
64
  end
@@ -1,49 +1,50 @@
1
1
  require 'rubygems'
2
- require 'bundler'
3
- begin
4
- Bundler.setup(:default, :development)
5
- rescue Bundler::BundlerError => e
6
- $stderr.puts e.message
7
- $stderr.puts "Run `bundle install` to install missing gems"
8
- exit e.status_code
9
- end
2
+ require 'bundler/setup'
10
3
  require 'tempfile'
11
4
  require 'minitest/unit'
12
5
  require 'mocha/setup'
13
6
  require 'turn'
14
7
  require 'unindent'
15
- require 'debugger'
16
- require 'vagrant'
17
8
  require 'stringio'
9
+ require 'json'
18
10
 
19
11
  $LOAD_PATH.unshift(File.dirname(__FILE__))
20
12
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
21
13
  require 'sshkit'
22
14
 
23
- module Vagrant
24
- module Communication
25
- class SSH
26
- def download(from)
27
- scp_connect do |scp|
28
- return StringIO.new.tap do |sio|
29
- scp.download!(from, sio)
15
+ class VagrantWrapper
16
+ class << self
17
+ def hosts
18
+ @vm_hosts ||= begin
19
+ result = {}
20
+ boxes_list.each do |vm|
21
+ host = SSHKit::Host.new("vagrant@localhost:#{vm["port"]}").tap do |h|
22
+ h.password = 'vagrant'
30
23
  end
24
+
25
+ result[vm["name"]] = host
31
26
  end
32
- rescue RuntimeError => e
33
- raise if e.message !~ /Permission denied/
34
- raise Vagrant::Errors::SCPPermissionDenied, :path => from.to_s
27
+
28
+ result
35
29
  end
36
- private
37
- def scp_connect
38
- connect do |connection|
39
- scp = Net::SCP.new(connection)
40
- return yield scp
41
- end
42
- rescue Net::SCP::Error => e
43
- raise Vagrant::Errors::SCPUnavailable if e.message =~ /\(127\)/
44
- raise
30
+ end
31
+
32
+ def running?
33
+ @running ||= begin
34
+ status = `#{vagrant_binary} status`
35
+ status.include?('running')
45
36
  end
46
37
  end
38
+
39
+ def boxes_list
40
+ json_config_path = File.join("test", "boxes.json")
41
+ boxes = File.open(json_config_path).read
42
+ JSON.parse(boxes)
43
+ end
44
+
45
+ def vagrant_binary
46
+ 'vagrant'
47
+ end
47
48
  end
48
49
  end
49
50
 
@@ -57,69 +58,52 @@ end
57
58
 
58
59
  class FunctionalTest < MiniTest::Unit::TestCase
59
60
 
60
- attr_accessor :venv
61
-
62
61
  def setup
63
- @venv = Vagrant::Environment.new
64
- venv.vms.each do |name, vm|
65
- warn "#{name} (of #{venv.vms.size}) needs to be booted, please wait" unless vm.state == :running
62
+ unless VagrantWrapper.running?
63
+ raise "Vagrant VMs are not running. Please, start it manually with `vagrant up`"
66
64
  end
67
- venv.cli "up"
68
65
  end
69
66
 
70
67
  private
71
68
 
72
- def vm_hosts
73
- venv.vms.collect do |name, vm|
74
- port = vm.env.config.for_vm(name).vm.forwarded_ports.first[:hostport]
75
- SSHKit::Host.new("vagrant@localhost:#{port}").tap do |h|
76
- h.password = 'vagrant'
77
- end
78
- end
79
- end
80
-
81
69
  def create_user_with_key(username, password = :secret)
82
70
  username, password = username.to_s, password.to_s
83
- keys = venv.vms.collect do |hostname, vm|
84
71
 
85
- # Remove the user, make it again, force-generate a key for him
86
- # short keys save us a few microseconds
87
- vm.channel.sudo("userdel -rf #{username}; true") # The `rescue nil` of the shell world
88
- vm.channel.sudo("useradd -m #{username}")
89
- vm.channel.sudo("echo y | ssh-keygen -b 1024 -f #{username} -N ''")
90
- vm.channel.sudo("chown vagrant:vagrant #{username}*")
91
- vm.channel.sudo("echo #{username}:#{password} | chpasswd")
72
+ keys = VagrantWrapper.hosts.collect do |name, host|
73
+ Net::SSH.start(host.hostname, host.user, port: host.port, password: host.password) do |ssh|
92
74
 
93
- # Make the .ssh directory, change the ownership and the
94
- vm.channel.sudo("mkdir -p /home/#{username}/.ssh")
95
- vm.channel.sudo("chown #{username}:#{username} /home/#{username}/.ssh")
96
- vm.channel.sudo("chmod 700 /home/#{username}/.ssh")
75
+ # Remove the user, make it again, force-generate a key for him
76
+ # short keys save us a few microseconds
77
+ ssh.exec!("sudo userdel -rf #{username}; true") # The `rescue nil` of the shell world
78
+ ssh.exec!("sudo useradd -m #{username}")
79
+ ssh.exec!("sudo echo y | ssh-keygen -b 1024 -f #{username} -N ''")
80
+ ssh.exec!("sudo chown vagrant:vagrant #{username}*")
81
+ ssh.exec!("sudo echo #{username}:#{password} | chpasswd")
97
82
 
98
- # Move the key to authorized keys and chown and chmod it
99
- vm.channel.sudo("cat #{username}.pub > /home/#{username}/.ssh/authorized_keys")
100
- vm.channel.sudo("chown #{username}:#{username} /home/#{username}/.ssh/authorized_keys")
101
- vm.channel.sudo("chmod 600 /home/#{username}/.ssh/authorized_keys")
83
+ # Make the .ssh directory, change the ownership and the
84
+ ssh.exec!("sudo mkdir -p /home/#{username}/.ssh")
85
+ ssh.exec!("sudo chown #{username}:#{username} /home/#{username}/.ssh")
86
+ ssh.exec!("sudo chmod 700 /home/#{username}/.ssh")
102
87
 
103
- sio = vm.channel.download("/home/vagrant/#{username}")
88
+ # Move the key to authorized keys and chown and chmod it
89
+ ssh.exec!("sudo cat #{username}.pub > /home/#{username}/.ssh/authorized_keys")
90
+ ssh.exec!("sudo chown #{username}:#{username} /home/#{username}/.ssh/authorized_keys")
91
+ ssh.exec!("sudo chmod 600 /home/#{username}/.ssh/authorized_keys")
104
92
 
105
- # Clean Up Files
106
- vm.channel.sudo("rm #{username} #{username}.pub")
93
+ key = ssh.exec!("cat /home/vagrant/#{username}")
107
94
 
108
- # Rewind the stringio, and read it as a string
109
- sio.tap { |s| s.rewind }.read
95
+ # Clean Up Files
96
+ ssh.exec!("sudo rm #{username} #{username}.pub")
110
97
 
98
+ key
99
+ end
111
100
  end
112
101
 
113
- Hash[venv.vms.collect { |n,vm| n }.zip(keys)]
114
-
102
+ Hash[VagrantWrapper.hosts.collect { |n, h| n.to_sym }.zip(keys)]
115
103
  end
116
104
 
117
105
  end
118
106
 
119
- class IntegrationTest < MiniTest::Unit::TestCase
120
-
121
- end
122
-
123
107
  #
124
108
  # Force colours in Autotest
125
109
  #
@@ -0,0 +1,77 @@
1
+ require 'helper'
2
+ require 'ostruct'
3
+
4
+ module SSHKit
5
+ module Backend
6
+ class TestConnectionPool < UnitTest
7
+
8
+ def pool
9
+ @pool ||= SSHKit::Backend::ConnectionPool.new
10
+ end
11
+
12
+ def connect
13
+ ->(*args) { Object.new }
14
+ end
15
+
16
+ def connect_and_close
17
+ ->(*args) { OpenStruct.new(:closed? => true) }
18
+ end
19
+
20
+ def echo_args
21
+ ->(*args) { args }
22
+ end
23
+
24
+ def test_default_idle_timeout
25
+ assert_equal 30, pool.idle_timeout
26
+ end
27
+
28
+ def test_connection_factory_receives_args
29
+ args = %w(a b c)
30
+ conn = pool.create_or_reuse_connection(*args, &echo_args)
31
+
32
+ assert_equal args, conn
33
+ end
34
+
35
+ def test_connections_are_reused
36
+ conn1 = pool.create_or_reuse_connection("conn", &connect)
37
+ conn2 = pool.create_or_reuse_connection("conn", &connect)
38
+
39
+ assert_equal conn1, conn2
40
+ end
41
+
42
+ def test_zero_idle_timeout_disables_resuse
43
+ pool.idle_timeout = 0
44
+
45
+ conn1 = pool.create_or_reuse_connection("conn", &connect)
46
+ conn2 = pool.create_or_reuse_connection("conn", &connect)
47
+
48
+ refute_equal conn1, conn2
49
+ end
50
+
51
+ def test_expired_connection_is_not_reused
52
+ pool.idle_timeout = 0.1
53
+
54
+ conn1 = pool.create_or_reuse_connection("conn", &connect)
55
+ sleep(pool.idle_timeout)
56
+ conn2 = pool.create_or_reuse_connection("conn", &connect)
57
+
58
+ refute_equal conn1, conn2
59
+ end
60
+
61
+ def test_closed_connection_is_not_reused
62
+ conn1 = pool.create_or_reuse_connection("conn", &connect_and_close)
63
+ conn2 = pool.create_or_reuse_connection("conn", &connect)
64
+
65
+ refute_equal conn1, conn2
66
+ end
67
+
68
+ def test_connections_with_different_args_are_not_reused
69
+ conn1 = pool.create_or_reuse_connection("conn1", &connect)
70
+ conn2 = pool.create_or_reuse_connection("conn2", &connect)
71
+
72
+ refute_equal conn1, conn2
73
+ end
74
+
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,65 @@
1
+ require 'helper'
2
+ require 'sshkit'
3
+
4
+ module SSHKit
5
+ class TestDot < UnitTest
6
+
7
+ def setup
8
+ SSHKit.config.output_verbosity = Logger::DEBUG
9
+ end
10
+
11
+ def output
12
+ @_output ||= String.new
13
+ end
14
+
15
+ def dot
16
+ @_dot ||= SSHKit::Formatter::Dot.new(output)
17
+ end
18
+
19
+ def teardown
20
+ remove_instance_variable :@_dot
21
+ remove_instance_variable :@_output
22
+ SSHKit.reset_configuration!
23
+ end
24
+
25
+ def test_logging_fatal
26
+ dot << SSHKit::LogMessage.new(Logger::FATAL, "Test")
27
+ assert_equal "", output.strip
28
+ end
29
+
30
+ def test_logging_error
31
+ dot << SSHKit::LogMessage.new(Logger::ERROR, "Test")
32
+ assert_equal "", output.strip
33
+ end
34
+
35
+ def test_logging_warn
36
+ dot << SSHKit::LogMessage.new(Logger::WARN, "Test")
37
+ assert_equal "", output.strip
38
+ end
39
+
40
+ def test_logging_info
41
+ dot << SSHKit::LogMessage.new(Logger::INFO, "Test")
42
+ assert_equal "", output.strip
43
+ end
44
+
45
+ def test_logging_debug
46
+ dot << SSHKit::LogMessage.new(Logger::DEBUG, "Test")
47
+ assert_equal "", output.strip
48
+ end
49
+
50
+ def test_command_success
51
+ command = SSHKit::Command.new(:ls)
52
+ command.exit_status = 0
53
+ dot << command
54
+ assert_equal "\e[32m.\e[0m", output.strip
55
+ end
56
+
57
+ def test_command_failure
58
+ command = SSHKit::Command.new(:ls, {raise_on_non_zero_exit: false})
59
+ command.exit_status = 1
60
+ dot << command
61
+ assert_equal "\e[31m.\e[0m", output.strip
62
+ end
63
+
64
+ end
65
+ end
@@ -51,10 +51,15 @@ module SSHKit
51
51
  assert_equal "/opt/sites/example/current/bin ruby", SSHKit.config.command_map[:ruby]
52
52
  end
53
53
 
54
- def test_setting_formatter
54
+ def test_setting_formatter_to_dot
55
55
  assert SSHKit.config.format = :dot
56
56
  assert SSHKit.config.output.is_a? SSHKit::Formatter::Dot
57
57
  end
58
+
59
+ def test_setting_formatter_to_blackhole
60
+ assert SSHKit.config.format = :BlackHole
61
+ assert SSHKit.config.output.is_a? SSHKit::Formatter::BlackHole
62
+ end
58
63
  end
59
64
 
60
65
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sshkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lee Hambley
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-11-22 00:00:00.000000000 Z
12
+ date: 2013-12-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: net-ssh
@@ -129,62 +129,6 @@ dependencies:
129
129
  - - '>='
130
130
  - !ruby/object:Gem::Version
131
131
  version: '0'
132
- - !ruby/object:Gem::Dependency
133
- name: debugger
134
- requirement: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - '>='
137
- - !ruby/object:Gem::Version
138
- version: '0'
139
- type: :development
140
- prerelease: false
141
- version_requirements: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - '>='
144
- - !ruby/object:Gem::Version
145
- version: '0'
146
- - !ruby/object:Gem::Dependency
147
- name: vagrant
148
- requirement: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - '>='
151
- - !ruby/object:Gem::Version
152
- version: '0'
153
- type: :development
154
- prerelease: false
155
- version_requirements: !ruby/object:Gem::Requirement
156
- requirements:
157
- - - '>='
158
- - !ruby/object:Gem::Version
159
- version: '0'
160
- - !ruby/object:Gem::Dependency
161
- name: yard
162
- requirement: !ruby/object:Gem::Requirement
163
- requirements:
164
- - - '>='
165
- - !ruby/object:Gem::Version
166
- version: '0'
167
- type: :development
168
- prerelease: false
169
- version_requirements: !ruby/object:Gem::Requirement
170
- requirements:
171
- - - '>='
172
- - !ruby/object:Gem::Version
173
- version: '0'
174
- - !ruby/object:Gem::Dependency
175
- name: redcarpet
176
- requirement: !ruby/object:Gem::Requirement
177
- requirements:
178
- - - '>='
179
- - !ruby/object:Gem::Version
180
- version: '0'
181
- type: :development
182
- prerelease: false
183
- version_requirements: !ruby/object:Gem::Requirement
184
- requirements:
185
- - - '>='
186
- - !ruby/object:Gem::Version
187
- version: '0'
188
132
  description: A comprehensive toolkit for remotely running commands in a structured
189
133
  manner on groups of servers.
190
134
  email:
@@ -214,6 +158,7 @@ files:
214
158
  - lib/sshkit.rb
215
159
  - lib/sshkit/all.rb
216
160
  - lib/sshkit/backends/abstract.rb
161
+ - lib/sshkit/backends/connection_pool.rb
217
162
  - lib/sshkit/backends/local.rb
218
163
  - lib/sshkit/backends/netssh.rb
219
164
  - lib/sshkit/backends/printer.rb
@@ -237,14 +182,17 @@ files:
237
182
  - lib/sshkit/runners/sequential.rb
238
183
  - lib/sshkit/version.rb
239
184
  - sshkit.gemspec
185
+ - test/boxes.json
240
186
  - test/functional/backends/test_local.rb
241
187
  - test/functional/backends/test_netssh.rb
242
188
  - test/functional/test_coordinator.rb
243
189
  - test/functional/test_ssh_server_comes_up_for_functional_tests.rb
244
190
  - test/helper.rb
191
+ - test/unit/backends/test_connection_pool.rb
245
192
  - test/unit/backends/test_netssh.rb
246
193
  - test/unit/backends/test_printer.rb
247
194
  - test/unit/core_ext/test_string.rb
195
+ - test/unit/formatters/test_dot.rb
248
196
  - test/unit/formatters/test_pretty.rb
249
197
  - test/unit/test_command.rb
250
198
  - test/unit/test_command_map.rb
@@ -252,8 +200,9 @@ files:
252
200
  - test/unit/test_coordinator.rb
253
201
  - test/unit/test_host.rb
254
202
  - test/unit/test_logger.rb
255
- homepage: http://wacku.github.com/sshkit
256
- licenses: []
203
+ homepage: http://github.com/capistrano/sshkit
204
+ licenses:
205
+ - GPL3
257
206
  metadata: {}
258
207
  post_install_message:
259
208
  rdoc_options: []
@@ -276,14 +225,17 @@ signing_key:
276
225
  specification_version: 4
277
226
  summary: SSHKit makes it easy to write structured, testable SSH commands in Ruby
278
227
  test_files:
228
+ - test/boxes.json
279
229
  - test/functional/backends/test_local.rb
280
230
  - test/functional/backends/test_netssh.rb
281
231
  - test/functional/test_coordinator.rb
282
232
  - test/functional/test_ssh_server_comes_up_for_functional_tests.rb
283
233
  - test/helper.rb
234
+ - test/unit/backends/test_connection_pool.rb
284
235
  - test/unit/backends/test_netssh.rb
285
236
  - test/unit/backends/test_printer.rb
286
237
  - test/unit/core_ext/test_string.rb
238
+ - test/unit/formatters/test_dot.rb
287
239
  - test/unit/formatters/test_pretty.rb
288
240
  - test/unit/test_command.rb
289
241
  - test/unit/test_command_map.rb