sshkit 1.4.0 → 1.5.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: a17c8de5efa195cef64bac5bd868dce70e430939
4
- data.tar.gz: 90f2200bf01fa80d3549ed747a4e8ef545806ffe
3
+ metadata.gz: 2be29e1ebec8acbfa5605fddeb959a1803f06581
4
+ data.tar.gz: 51115f162047ee443d2383e05d49851ae1e9b056
5
5
  SHA512:
6
- metadata.gz: ce8989e38ff9dab3be7673183786c838ea9fa71d174dfec0c5ae0d812a47619fcc1a2b768939cb531b9cf8dbabd4993b3f46c60bee81d01c7aff9976d5b5d77f
7
- data.tar.gz: 906b9b7317960551ed9b137f9cf1611e11821d3a042cc0d990853cedfbc5e970c1b4a4d1a92a0b4896b662e162395fc3244b91db8f29ba92a52a596c9145cc06
6
+ metadata.gz: 8f0e074042930ca893c2953712bce4abb47bc04387d98b0339aed07df8e2dfccef783ecaf6696b00d4d1d9c88b8d51bc6982fd9bc60aaa6343950e9da02a3260
7
+ data.tar.gz: 5bf7f417df04ab339fe70dcb12377b248cf2eef18bf9a881ea9e23dd7eb2d62b717f6f2713dd68878173aef4f50af6a3920624fc6986b5972bce1b79e4672e7e
@@ -3,5 +3,4 @@ rvm:
3
3
  - 2.0.0
4
4
  - 1.9.3
5
5
  - 1.9.2
6
- - rbx
7
6
  script: "rake test:units"
@@ -8,11 +8,28 @@ appear at the top.
8
8
  * Add your entries here, remember to credit yourself however you want to be
9
9
  credited!
10
10
 
11
+ ## 1.5.0
12
+
13
+ * Deprecate background helper - too many badly behaved pseudo-daemons. Lee Hambley
14
+ * Don't colourize unless $stdout is a tty. Lee Hambley
15
+ * Remove out of date "Known Issues" section from README. Lee Hambley
16
+ * Dealy variable interpolation inside `as()` block. Nick Townsend
17
+ * Fixes for functional tests under modern Vagrant. Lewis Marshal
18
+ * Fixes for connection pooling. Chris Heald
19
+ * Add `localhost` hostname to local backend. Adam Mckaig
20
+ * Wrap execptions to include hostname. Brecht Hoflack
21
+ * Remove `shellwords` stdlib dependency Bruno Sutic
22
+ * Remove unused `cooldown` accessor. Bruno Sutic
23
+ * Replace Term::ANSIColor with a lighter solution. Tom Clements
24
+ * Documentation fixes. Matt Brictson
25
+
11
26
  ## 1.4.0
12
27
 
28
+ https://github.com/capistrano/sshkit/compare/v1.3.0...v1.4.0
29
+
13
30
  * Removed `invoke` alias for [`SSHKit::Backend::Printer.execute`](https://github.com/capistrano/sshkit/blob/master/lib/sshkit/backends/printer.rb#L20). This is to prevent collisions with
14
31
  methods in capistrano with similar names, and to provide a cleaner API. See [capistrano issue 912](https://github.com/capistrano/capistrano/issues/912) and [issue 107](https://github.com/capistrano/sshkit/issues/107) for more details.
15
- * Connection pooling now uses a thread local to store connection pool, giving each thread its own connection pool. Thank you @mcbriston see [#101](https://github.com/capistrano/sshkit/pull/101) for more.
32
+ * Connection pooling now uses a thread local to store connection pool, giving each thread its own connection pool. Thank you @mbrictson see [#101](https://github.com/capistrano/sshkit/pull/101) for more.
16
33
  * Command map indifferent towards strings and symbols thanks to @thomasfedb see [#91](https://github.com/capistrano/sshkit/pull/91)
17
34
  * Moved vagrant wrapper to `support` directory, added ability to run tests with vagrant using ssh. @miry see [#64](https://github.com/capistrano/sshkit/pull/64)
18
35
  * Removed unnecessary require `require_relative '../sshkit'` in `lib/sshkit/dsl.rb` prevents warnings thanks @brabic.
@@ -20,6 +37,8 @@ appear at the top.
20
37
 
21
38
  ## 1.3.0
22
39
 
40
+ https://github.com/capistrano/sshkit/compare/v1.2.0...v1.3.0
41
+
23
42
  * Connection pooling. SSH connections are reused across multiple invocations
24
43
  of `on()`, which can result in significant performance gains. See:
25
44
  https://github.com/capistrano/sshkit/pull/70. Matt @mbrictson Brictson.
@@ -30,6 +49,8 @@ appear at the top.
30
49
 
31
50
  ## 1.2.0
32
51
 
52
+ https://github.com/capistrano/sshkit/compare/v1.1.0...v1.2.0
53
+
33
54
  * Support picking up a project local SSH config file, if a SSH config file
34
55
  exists at ./.ssh/config it will be merged with the ~/.ssh/config. This is
35
56
  ideal for defining project-local proxies/gateways, etc. Thanks to Alex
@@ -55,6 +76,8 @@ appear at the top.
55
76
 
56
77
  ## 1.1.0
57
78
 
79
+ https://github.com/capistrano/sshkit/compare/v1.0.0...v1.1.0
80
+
58
81
  * Please see the Git history. `git rebase` ate our changelog (we should have been
59
82
  more careful)
60
83
 
@@ -158,25 +158,6 @@ leading slashes. It may be misleading as the `File.join()` is performed on the
158
158
  machine running the code, if that's a Windows box, the paths may be incorrectly
159
159
  joined according to the expectations of the machine receiving the commands.
160
160
 
161
- ## Running a task in the background
162
-
163
- on hosts do
164
- within '/opt/sites/example.com' do
165
- background :rails, :server
166
- end
167
- end
168
-
169
- This will run something like `nohup /usr/bin/env rails server > /dev/null &`,
170
- backgrounding the Rails process, and making sure we don't leave nohup log
171
- files littering the filesystem.
172
-
173
- **Note:** The `background()` method won't do what you expect if you pass a
174
- string `sleep 5`, according to the rules of processing commands, you must call
175
- `background(:sleep, "5")` (that is, command: sleep, args: 5).
176
-
177
- **Further Note:** The background() task wraps the given command in `nohup .... &` under some
178
- circumstances the program will hang anyway when the SSH session exits.
179
-
180
161
  ## Do not care about the host block
181
162
 
182
163
  on hosts do
@@ -217,7 +198,7 @@ circumstances the program will hang anyway when the SSH session exits.
217
198
  execute!(:echo, '"Example Message!" 1>&2; false')
218
199
  end
219
200
 
220
- This will raise `SSHKit::Command:Failed` with the `#message` "Example Message!"`
201
+ This will raise `SSHKit::Command:Failed` with the `#message` "Example Message!"
221
202
  which will cause the command to abort.
222
203
 
223
204
  ## Make a test, or run a command which may fail without raising an error:
data/README.md CHANGED
@@ -201,15 +201,15 @@ first argument before attempting to find it in the *command map*.
201
201
 
202
202
  ![Example Output](https://raw.github.com/leehambley/sshkit/master/assets/images/example_output.png)
203
203
 
204
- By default, the output format is set to `:pretty`:
204
+ By default, the output format is set to `:pretty`:
205
205
 
206
206
  ```ruby
207
207
  SSHKit.config.format = :pretty
208
208
  ```
209
209
 
210
- However, if you prefer minimal output, `:dot` format will simply output red or green dots based on the success or failure of operations.
210
+ However, if you prefer minimal output, `:dot` format will simply output red or green dots based on the success or failure of operations.
211
211
 
212
- To output directly to $stdout without any formatting, you can use:
212
+ To output directly to $stdout without any formatting, you can use:
213
213
 
214
214
  ```ruby
215
215
  SSHKit.config.output = $stdout
@@ -248,46 +248,3 @@ pooling behaviour entirely by setting the idle_timeout to zero:
248
248
  ```ruby
249
249
  SSHKit::Backend::Netssh.pool.idle_timeout = 0 # disabled
250
250
  ```
251
-
252
- ## Known Issues
253
-
254
- * No handling of slow / timed out connections
255
- * No handling of slow / hung remote commands
256
- * ~~No built-in way to background() something (execute and background the
257
- process).~~
258
- * No environment handling (sshkit might not need to care)
259
- * ~~No arbitrary `Host` properties (example storing `roles` on servers, or other
260
- metadata that might be useful in the `on()` block)~~
261
- * ~~No log/warning facility (passing Log messages to the output would work)
262
- A log object could be made available globally which would emit a LogMessage
263
- type object which would be recognised by the formatters that need to care
264
- about them.~~
265
- * ~~No verbosity control, commands should have a `Logger::LEVEL` on them,
266
- user-generated should be at a high level, the commands auto-generated from
267
- the guards and checks from as() and within() should have a lower level.~~
268
- * ~~Decide if `execute()` (and friends) should raise on non-zero exit statuses or
269
- not, perhaps a family of similarly named bang methods should be the ones to
270
- raise. (Perhaps `test()` should be a way to `execute()` without raising, and
271
- `execute()` and friends should always raise)~~
272
- * ~~It would be nice to be able to say `SSHKit.config.formatter = :pretty` and
273
- have that method setter do the legwork of updating `SSHKit.config.output` to
274
- be an instance of the correct formatter class wrapping the existing output
275
- stream.~~
276
- * No "trace" level debugging for internal stuff, the debug level should be
277
- reserved for client-level debugging, with trace being (int -1) used
278
- internally for logging about connection opening, closing, timing out, etc.
279
- * No method for uploading or downloading files, or the same for saving/loading
280
- a string to/from a remote file.
281
- * No closing of connections, the abstract backend class should include a
282
- cleanup method which is empty but can be overridden by other implementations.
283
- * ~~No connection pooling, the `connection` method of the NetSSH backend could
284
- easily be modified to look into some connection factory for it's objects,
285
- saving half a second when running lots of `on()` blocks.~~
286
- * Documentation! (YARD style)
287
- * Wrap all commands in a known shell, that is that `execute('uptime')` should
288
- be converted into `sh -c 'uptime'` to ensure that we have a consistent shell
289
- experience.
290
- * ~~There's no suitable host parser that accepts `Host.new('user@ip:port')`, it
291
- will decode a `user@hostname:port`, but IP addresses don't work.~~
292
- * If Net::SSH raises `IOError` (as it does when authentication fails) this
293
- needs to be caught, and re-raised as some kind of ConnectionFailed error.
@@ -3,7 +3,7 @@ module SSHKit
3
3
  StandardError = Class.new(::StandardError)
4
4
 
5
5
  class << self
6
-
6
+
7
7
  attr_accessor :config
8
8
 
9
9
  def capture_output(io, &block)
@@ -27,7 +27,7 @@ module SSHKit
27
27
  def reset_configuration!
28
28
  @@config = nil
29
29
  end
30
-
30
+
31
31
  end
32
32
 
33
33
  end
@@ -3,11 +3,14 @@ require_relative '../core_ext/hash'
3
3
 
4
4
  require_relative 'host'
5
5
 
6
+ require_relative 'color'
6
7
  require_relative 'command'
7
8
  require_relative 'command_map'
8
9
  require_relative 'configuration'
9
10
  require_relative 'coordinator'
10
11
 
12
+ require_relative 'exception'
13
+
11
14
  require_relative 'logger'
12
15
  require_relative 'log_message'
13
16
 
@@ -54,10 +54,6 @@ module SSHKit
54
54
  raise MethodUnavailableError
55
55
  end
56
56
 
57
- def background(command, args=[])
58
- raise MethodUnavailableError
59
- end
60
-
61
57
  def test(command, args=[])
62
58
  raise MethodUnavailableError
63
59
  end
@@ -1,4 +1,4 @@
1
- require "monitor"
1
+ require "thread"
2
2
 
3
3
  module SSHKit
4
4
 
@@ -10,47 +10,53 @@ module SSHKit
10
10
 
11
11
  def initialize
12
12
  self.idle_timeout = 30
13
- @monitor = Monitor.new
13
+ @mutex = Mutex.new
14
+ @pool = {}
14
15
  end
15
16
 
16
- def create_or_reuse_connection(*new_connection_args, &block)
17
+ def checkout(*new_connection_args, &block)
17
18
  # Optimization: completely bypass the pool if idle_timeout is zero.
18
- return yield(*new_connection_args) if idle_timeout == 0
19
-
20
19
  key = new_connection_args.to_s
21
- entry = find_and_reject_invalid(key) { |e| e.expired? || e.closed? }
20
+ return create_new_entry(new_connection_args, key, &block) if idle_timeout == 0
22
21
 
23
- if entry.nil?
24
- entry = store_entry(key, yield(*new_connection_args))
25
- end
22
+ find_live_entry(key) || create_new_entry(new_connection_args, key, &block)
23
+ end
26
24
 
25
+ def checkin(entry)
27
26
  entry.expires_at = Time.now + idle_timeout if idle_timeout
28
- entry.connection
27
+ @mutex.synchronize do
28
+ @pool[entry.key] ||= []
29
+ @pool[entry.key] << entry
30
+ end
29
31
  end
30
32
 
31
- private
32
-
33
- def connections
34
- Thread.current[:sshkit_pool] ||= {}
33
+ def flush_connections
34
+ @mutex.synchronize { @pool.clear }
35
35
  end
36
36
 
37
- def find_and_reject_invalid(key, &block)
38
- entry = connections[key]
39
- invalid = entry && yield(entry)
40
-
41
- connections.delete(entry) if invalid
37
+ private
42
38
 
43
- invalid ? nil : entry
39
+ def find_live_entry(key)
40
+ @mutex.synchronize do
41
+ return nil unless @pool.key?(key)
42
+ while entry = @pool[key].shift
43
+ return entry if entry.live?
44
+ end
45
+ end
46
+ nil
44
47
  end
45
48
 
46
- def store_entry(key, connection)
47
- connections[key] = Entry.new(connection)
49
+ def create_new_entry(args, key, &block)
50
+ Entry.new block.call(*args), key
48
51
  end
49
52
 
50
-
51
- Entry = Struct.new(:connection) do
53
+ Entry = Struct.new(:connection, :key) do
52
54
  attr_accessor :expires_at
53
55
 
56
+ def live?
57
+ !expired? && !closed?
58
+ end
59
+
54
60
  def expired?
55
61
  expires_at && Time.now > expires_at
56
62
  end
@@ -61,6 +67,5 @@ module SSHKit
61
67
  end
62
68
 
63
69
  end
64
-
65
70
  end
66
71
  end
@@ -6,6 +6,7 @@ module SSHKit
6
6
  class Local < Printer
7
7
 
8
8
  def initialize(&block)
9
+ @host = Host.new(hostname: 'localhost') # just for logging
9
10
  @block = block
10
11
  end
11
12
 
@@ -67,6 +67,7 @@ module SSHKit
67
67
  end
68
68
 
69
69
  def background(*args)
70
+ warn "[Deprecated] The background method is deprecated. Blame badly behaved pseudo-daemons!"
70
71
  options = args.extract_options!.merge(run_in_background: true)
71
72
  _execute(*[*args, options]).success?
72
73
  end
@@ -78,12 +79,16 @@ module SSHKit
78
79
 
79
80
  def upload!(local, remote, options = {})
80
81
  summarizer = transfer_summarizer('Uploading')
81
- ssh.scp.upload!(local, remote, options, &summarizer)
82
+ with_ssh do |ssh|
83
+ ssh.scp.upload!(local, remote, options, &summarizer)
84
+ end
82
85
  end
83
86
 
84
87
  def download!(remote, local=nil, options = {})
85
88
  summarizer = transfer_summarizer('Downloading')
86
- ssh.scp.download!(remote, local, options, &summarizer)
89
+ with_ssh do |ssh|
90
+ ssh.scp.download!(remote, local, options, &summarizer)
91
+ end
87
92
  end
88
93
 
89
94
  @pool = SSHKit::Backend::ConnectionPool.new
@@ -123,59 +128,64 @@ module SSHKit
123
128
  command(*args).tap do |cmd|
124
129
  output << cmd
125
130
  cmd.started = true
126
- ssh.open_channel do |chan|
127
- chan.request_pty if Netssh.config.pty
128
- chan.exec cmd.to_command do |ch, success|
129
- chan.on_data do |ch, data|
130
- cmd.stdout = data
131
- cmd.full_stdout += data
132
- output << cmd
133
- end
134
- chan.on_extended_data do |ch, type, data|
135
- cmd.stderr = data
136
- cmd.full_stderr += data
137
- output << cmd
138
- end
139
- chan.on_request("exit-status") do |ch, data|
140
- cmd.stdout = ''
141
- cmd.stderr = ''
142
- cmd.exit_status = data.read_long
143
- output << cmd
144
- end
145
- #chan.on_request("exit-signal") do |ch, data|
146
- # # TODO: This gets called if the program is killed by a signal
147
- # # might also be a worthwhile thing to report
148
- # exit_signal = data.read_string.to_i
149
- # warn ">>> " + exit_signal.inspect
150
- # output << cmd
151
- #end
152
- chan.on_open_failed do |ch|
153
- # TODO: What do do here?
154
- # I think we should raise something
155
- end
156
- chan.on_process do |ch|
157
- # TODO: I don't know if this is useful
158
- end
159
- chan.on_eof do |ch|
160
- # TODO: chan sends EOF before the exit status has been
161
- # writtend
131
+ with_ssh do |ssh|
132
+ ssh.open_channel do |chan|
133
+ chan.request_pty if Netssh.config.pty
134
+ chan.exec cmd.to_command do |ch, success|
135
+ chan.on_data do |ch, data|
136
+ cmd.stdout = data
137
+ cmd.full_stdout += data
138
+ output << cmd
139
+ end
140
+ chan.on_extended_data do |ch, type, data|
141
+ cmd.stderr = data
142
+ cmd.full_stderr += data
143
+ output << cmd
144
+ end
145
+ chan.on_request("exit-status") do |ch, data|
146
+ cmd.stdout = ''
147
+ cmd.stderr = ''
148
+ cmd.exit_status = data.read_long
149
+ output << cmd
150
+ end
151
+ #chan.on_request("exit-signal") do |ch, data|
152
+ # # TODO: This gets called if the program is killed by a signal
153
+ # # might also be a worthwhile thing to report
154
+ # exit_signal = data.read_string.to_i
155
+ # warn ">>> " + exit_signal.inspect
156
+ # output << cmd
157
+ #end
158
+ chan.on_open_failed do |ch|
159
+ # TODO: What do do here?
160
+ # I think we should raise something
161
+ end
162
+ chan.on_process do |ch|
163
+ # TODO: I don't know if this is useful
164
+ end
165
+ chan.on_eof do |ch|
166
+ # TODO: chan sends EOF before the exit status has been
167
+ # writtend
168
+ end
162
169
  end
170
+ chan.wait
163
171
  end
164
- chan.wait
172
+ ssh.loop
165
173
  end
166
- ssh.loop
167
174
  end
168
175
  end
169
176
 
170
- def ssh
171
- @ssh ||= begin
172
- host.ssh_options ||= Netssh.config.ssh_options
173
- self.class.pool.create_or_reuse_connection(
174
- String(host.hostname),
175
- host.username,
176
- host.netssh_options,
177
- &Net::SSH.method(:start)
178
- )
177
+ def with_ssh
178
+ host.ssh_options ||= Netssh.config.ssh_options
179
+ conn = self.class.pool.checkout(
180
+ String(host.hostname),
181
+ host.username,
182
+ host.netssh_options,
183
+ &Net::SSH.method(:start)
184
+ )
185
+ begin
186
+ yield conn.connection
187
+ ensure
188
+ self.class.pool.checkin conn
179
189
  end
180
190
  end
181
191
 
@@ -0,0 +1,13 @@
1
+ require 'colorize'
2
+ module Color
3
+ STYLES = [String::COLORS, String::MODES].flat_map(&:keys)
4
+
5
+ STYLES.each do |style|
6
+ instance_eval %{
7
+ def #{style}(string='')
8
+ string = yield if block_given?
9
+ $stdout.tty? ? string.colorize(:#{style}) : string
10
+ end
11
+ }
12
+ end
13
+ end
@@ -1,4 +1,3 @@
1
- require 'shellwords'
2
1
  require 'digest/sha1'
3
2
  require 'securerandom'
4
3
 
@@ -164,7 +163,7 @@ module SSHKit
164
163
 
165
164
  def user(&block)
166
165
  return yield unless options[:user]
167
- "sudo su #{options[:user]} -c \"%s\"" % %Q{#{yield}}
166
+ "sudo su #{options[:user]} -c '%s'" % %Q{#{yield}}
168
167
  end
169
168
 
170
169
  def in_background(&block)
@@ -26,8 +26,6 @@ module SSHKit
26
26
 
27
27
  private
28
28
 
29
- attr_accessor :cooldown
30
-
31
29
  def default_options
32
30
  { in: :parallel }
33
31
  end
@@ -0,0 +1,21 @@
1
+ module SSHKit
2
+
3
+ module Runner
4
+
5
+ class ExecuteError < StandardError
6
+ attr_reader :cause
7
+
8
+ def initialize cause
9
+ @cause = cause
10
+ end
11
+
12
+ def backtrace
13
+ @cause.backtrace
14
+ end
15
+
16
+ def backtrace_locations
17
+ @cause.backtrace_locations
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,5 +1,3 @@
1
- require 'term/ansicolor'
2
-
3
1
  module SSHKit
4
2
 
5
3
  module Formatter
@@ -17,7 +15,7 @@ module SSHKit
17
15
  private
18
16
 
19
17
  def c
20
- @c ||= Term::ANSIColor
18
+ @c ||= Color
21
19
  end
22
20
 
23
21
  end
@@ -1,5 +1,3 @@
1
- require 'term/ansicolor'
2
-
3
1
  module SSHKit
4
2
 
5
3
  module Formatter
@@ -53,7 +51,7 @@ module SSHKit
53
51
  end
54
52
 
55
53
  def c
56
- @c ||= Term::ANSIColor
54
+ @c ||= Color
57
55
  end
58
56
 
59
57
  def uuid(obj)
@@ -61,9 +59,7 @@ module SSHKit
61
59
  end
62
60
 
63
61
  def level(verbosity)
64
- # Insane number here accounts for the control codes added
65
- # by term-ansicolor
66
- sprintf "%14s ", c.send(level_formatting(verbosity), level_names(verbosity))
62
+ c.send(level_formatting(verbosity), level_names(verbosity))
67
63
  end
68
64
 
69
65
  def level_formatting(level_num)
@@ -9,7 +9,12 @@ module SSHKit
9
9
  threads = []
10
10
  hosts.each do |host|
11
11
  threads << Thread.new(host) do |h|
12
- backend(h, &block).run
12
+ begin
13
+ backend(h, &block).run
14
+ rescue Exception => e
15
+ e2 = ExecuteError.new e
16
+ raise e2, "Exception while executing on host #{host}: #{e.message}"
17
+ end
13
18
  end
14
19
  end
15
20
  threads.map(&:join)
@@ -6,7 +6,12 @@ module SSHKit
6
6
  attr_writer :wait_interval
7
7
  def execute
8
8
  hosts.each do |host|
9
- backend(host, &block).run
9
+ begin
10
+ backend(host, &block).run
11
+ rescue Exception => e
12
+ e2 = ExecuteError.new e
13
+ raise e2, "Exception while executing on host #{host}: #{e.message}"
14
+ end
10
15
  sleep wait_interval
11
16
  end
12
17
  end
@@ -1,3 +1,3 @@
1
1
  module SSHKit
2
- VERSION = "1.4.0"
2
+ VERSION = "1.5.0"
3
3
  end
@@ -19,12 +19,11 @@ Gem::Specification.new do |gem|
19
19
 
20
20
  gem.add_runtime_dependency('net-ssh', '>= 2.8.0')
21
21
  gem.add_runtime_dependency('net-scp', '>= 1.1.2')
22
- gem.add_runtime_dependency('term-ansicolor')
22
+ gem.add_runtime_dependency('colorize')
23
23
 
24
24
  gem.add_development_dependency('minitest', ['>= 2.11.3', '< 2.12.0'])
25
25
  gem.add_development_dependency('rake')
26
26
  gem.add_development_dependency('turn')
27
27
  gem.add_development_dependency('unindent')
28
28
  gem.add_development_dependency('mocha')
29
-
30
29
  end
@@ -80,21 +80,6 @@ module SSHKit
80
80
  end.run
81
81
  end
82
82
 
83
- def test_backgrounding_a_process
84
- #SSHKit.config.output = SSHKit::Formatter::Pretty.new($stdout)
85
- process_list = ""
86
- time = Benchmark.measure do
87
- Netssh.new(a_host) do
88
- background :sleep, 5
89
- end.run
90
- Netssh.new(a_host) do
91
- process_list = capture :ps, "aux | grep sleep | grep -v grep; true"
92
- end.run
93
- end
94
- assert_operator time.real, :<, 1
95
- assert_match "sleep 5", process_list
96
- end
97
-
98
83
  def test_upload_file
99
84
  file_contents = ""
100
85
  file_name = File.join("/tmp", SecureRandom.uuid)
@@ -45,11 +45,10 @@ class VagrantWrapper
45
45
  host_options = {
46
46
  user: vm['user'] || 'vagrant',
47
47
  hostname: vm['hostname'] || 'localhost',
48
- port: vm['port'] || '22'
48
+ port: vm['port'] || '22',
49
+ password: vm['password'] || 'vagrant'
49
50
  }
50
51
 
51
-
52
-
53
52
  SSHKit::Host.new(host_options)
54
53
  end
55
54
  end
@@ -5,6 +5,10 @@ module SSHKit
5
5
  module Backend
6
6
  class TestConnectionPool < UnitTest
7
7
 
8
+ def setup
9
+ pool.flush_connections
10
+ end
11
+
8
12
  def pool
9
13
  @pool ||= SSHKit::Backend::ConnectionPool.new
10
14
  end
@@ -27,23 +31,54 @@ module SSHKit
27
31
 
28
32
  def test_connection_factory_receives_args
29
33
  args = %w(a b c)
30
- conn = pool.create_or_reuse_connection(*args, &echo_args)
34
+ conn = pool.checkout(*args, &echo_args)
31
35
 
32
- assert_equal args, conn
36
+ assert_equal args, conn.connection
33
37
  end
34
38
 
35
- def test_connections_are_reused
36
- conn1 = pool.create_or_reuse_connection("conn", &connect)
37
- conn2 = pool.create_or_reuse_connection("conn", &connect)
39
+ def test_connections_are_not_reused_if_not_checked_in
40
+ conn1 = pool.checkout("conn", &connect)
41
+ conn2 = pool.checkout("conn", &connect)
42
+
43
+ refute_equal conn1, conn2
44
+ end
45
+
46
+ def test_connections_are_reused_if_checked_in
47
+ conn1 = pool.checkout("conn", &connect)
48
+ pool.checkin conn1
49
+ conn2 = pool.checkout("conn", &connect)
38
50
 
39
51
  assert_equal conn1, conn2
40
52
  end
41
53
 
54
+ def test_connections_are_reused_across_threads_multiple_times
55
+ t1 = Thread.new {
56
+ Thread.current[:conn] = pool.checkout("conn", &connect)
57
+ pool.checkin Thread.current[:conn]
58
+ }.join
59
+
60
+ t2 = Thread.new {
61
+ Thread.current[:conn] = pool.checkout("conn", &connect)
62
+ pool.checkin Thread.current[:conn]
63
+ }.join
64
+
65
+ t3 = Thread.new {
66
+ Thread.current[:conn] = pool.checkout("conn", &connect)
67
+ pool.checkin Thread.current[:conn]
68
+ }.join
69
+
70
+ refute_equal t1[:conn], nil
71
+ assert_equal t1[:conn], t2[:conn]
72
+ assert_equal t2[:conn], t3[:conn]
73
+ end
74
+
42
75
  def test_zero_idle_timeout_disables_reuse
43
76
  pool.idle_timeout = 0
44
77
 
45
- conn1 = pool.create_or_reuse_connection("conn", &connect)
46
- conn2 = pool.create_or_reuse_connection("conn", &connect)
78
+ conn1 = pool.checkout("conn", &connect)
79
+ pool.checkin conn1
80
+
81
+ conn2 = pool.checkout("conn", &connect)
47
82
 
48
83
  refute_equal conn1, conn2
49
84
  end
@@ -51,25 +86,26 @@ module SSHKit
51
86
  def test_expired_connection_is_not_reused
52
87
  pool.idle_timeout = 0.1
53
88
 
54
- conn1 = pool.create_or_reuse_connection("conn", &connect)
89
+ conn1 = pool.checkout("conn", &connect)
90
+ pool.checkin conn1
55
91
  sleep(pool.idle_timeout)
56
- conn2 = pool.create_or_reuse_connection("conn", &connect)
92
+ conn2 = pool.checkout("conn", &connect)
57
93
 
58
94
  refute_equal conn1, conn2
59
95
  end
60
96
 
61
97
  def test_closed_connection_is_not_reused
62
- # Ensure there aren't any other open connections
63
- pool.flush_connections()
64
- conn1 = pool.create_or_reuse_connection("conn", &connect_and_close)
65
- conn2 = pool.create_or_reuse_connection("conn", &connect)
98
+ conn1 = pool.checkout("conn", &connect_and_close)
99
+ pool.checkin conn1
100
+ conn2 = pool.checkout("conn", &connect)
66
101
 
67
102
  refute_equal conn1, conn2
68
103
  end
69
104
 
70
105
  def test_connections_with_different_args_are_not_reused
71
- conn1 = pool.create_or_reuse_connection("conn1", &connect)
72
- conn2 = pool.create_or_reuse_connection("conn2", &connect)
106
+ conn1 = pool.checkout("conn1", &connect)
107
+ pool.checkin conn1
108
+ conn2 = pool.checkout("conn2", &connect)
73
109
 
74
110
  refute_equal conn1, conn2
75
111
  end
@@ -8,6 +8,10 @@ module SSHKit
8
8
  @local ||= Local.new
9
9
  end
10
10
 
11
+ def test_host
12
+ assert_equal 'localhost', local.host.to_s
13
+ end
14
+
11
15
  def test_execute
12
16
  assert_equal true, local.execute('uname -a')
13
17
  assert_equal true, local.execute
@@ -59,7 +59,7 @@ module SSHKit
59
59
  assert_equal false, backend.config.ssh_options[:forward_agent]
60
60
  assert_equal %w(publickey password), backend.config.ssh_options[:auth_methods]
61
61
  end
62
-
62
+
63
63
  def test_invoke_raises_no_method_error
64
64
  assert_raises NoMethodError do
65
65
  printer.invoke :echo
@@ -1,9 +1,9 @@
1
1
  class String
2
2
 
3
3
  def unindent
4
- indent = self.split("\n").select do |line|
4
+ indent = self.split("\n").select do |line|
5
5
  !line.strip.empty?
6
- end.map do |line|
6
+ end.map do |line|
7
7
  line.index(/[^\s]/)
8
8
  end.compact.min || 0
9
9
  self.gsub(/^[[:blank:]]{#{indent}}/, '')
@@ -46,19 +46,19 @@ module SSHKit
46
46
  dot << SSHKit::LogMessage.new(Logger::DEBUG, "Test")
47
47
  assert_equal "", output.strip
48
48
  end
49
-
49
+
50
50
  def test_command_success
51
51
  command = SSHKit::Command.new(:ls)
52
52
  command.exit_status = 0
53
53
  dot << command
54
- assert_equal "\e[32m.\e[0m", output.strip
54
+ assert_equal "\e[0;32;49m.\e[0m", output.strip
55
55
  end
56
-
56
+
57
57
  def test_command_failure
58
58
  command = SSHKit::Command.new(:ls, {raise_on_non_zero_exit: false})
59
59
  command.exit_status = 1
60
60
  dot << command
61
- assert_equal "\e[31m.\e[0m", output.strip
61
+ assert_equal "\e[0;31;49m.\e[0m", output.strip
62
62
  end
63
63
 
64
64
  end
@@ -24,27 +24,27 @@ module SSHKit
24
24
 
25
25
  def test_logging_fatal
26
26
  pretty << SSHKit::LogMessage.new(Logger::FATAL, "Test")
27
- assert_equal output.strip, " \e[31mFATAL\e[0m Test \n".strip
27
+ assert_equal output.strip, "\e[0;31;49mFATAL\e[0mTest"
28
28
  end
29
29
 
30
30
  def test_logging_error
31
31
  pretty << SSHKit::LogMessage.new(Logger::ERROR, "Test")
32
- assert_equal output.strip, " \e[31mERROR\e[0m Test \n".strip
32
+ assert_equal output.strip, "\e[0;31;49mERROR\e[0mTest"
33
33
  end
34
34
 
35
35
  def test_logging_warn
36
36
  pretty << SSHKit::LogMessage.new(Logger::WARN, "Test")
37
- assert_equal output.strip, " \e[33mWARN\e[0m Test \n".strip
37
+ assert_equal output.strip, "\e[0;33;49mWARN\e[0mTest".strip
38
38
  end
39
39
 
40
40
  def test_logging_info
41
41
  pretty << SSHKit::LogMessage.new(Logger::INFO, "Test")
42
- assert_equal output.strip, " \e[34mINFO\e[0m Test \n".strip
42
+ assert_equal output.strip, "\e[0;34;49mINFO\e[0mTest".strip
43
43
  end
44
44
 
45
45
  def test_logging_debug
46
46
  pretty << SSHKit::LogMessage.new(Logger::DEBUG, "Test")
47
- assert_equal output.strip, " \e[30mDEBUG\e[0m Test \n".strip
47
+ assert_equal output.strip, "\e[0;30;49mDEBUG\e[0mTest".strip
48
48
  end
49
49
 
50
50
  end
@@ -78,7 +78,7 @@ module SSHKit
78
78
 
79
79
  def test_working_as_a_given_user
80
80
  c = Command.new(:whoami, user: :anotheruser)
81
- assert_equal "sudo su anotheruser -c \"/usr/bin/env whoami\"", c.to_command
81
+ assert_equal "sudo su anotheruser -c '/usr/bin/env whoami'", c.to_command
82
82
  end
83
83
 
84
84
  def test_working_as_a_given_group
@@ -88,27 +88,7 @@ module SSHKit
88
88
 
89
89
  def test_working_as_a_given_user_and_group
90
90
  c = Command.new(:whoami, user: :anotheruser, group: :devvers)
91
- assert_equal "sudo su anotheruser -c \"sg devvers -c \\\"/usr/bin/env whoami\\\"\"", c.to_command
92
- end
93
-
94
- def test_backgrounding_a_task
95
- c = Command.new(:sleep, 15, run_in_background: true)
96
- assert_equal "( nohup /usr/bin/env sleep 15 > /dev/null & )", c.to_command
97
- end
98
-
99
- def test_backgrounding_a_task_as_a_given_user
100
- c = Command.new(:sleep, 15, run_in_background: true, user: :anotheruser)
101
- assert_equal "sudo su anotheruser -c \"( nohup /usr/bin/env sleep 15 > /dev/null & )\"", c.to_command
102
- end
103
-
104
- def test_backgrounding_a_task_as_a_given_user_with_env
105
- c = Command.new(:sleep, 15, run_in_background: true, user: :anotheruser, env: {a: :b})
106
- assert_equal "( A=b sudo su anotheruser -c \"( nohup /usr/bin/env sleep 15 > /dev/null & )\" )", c.to_command
107
- end
108
-
109
- def test_backgrounding_a_task_with_working_directory
110
- c = Command.new(:sleep, 15, run_in_background: true, in: '/opt')
111
- assert_equal "cd /opt && ( nohup /usr/bin/env sleep 15 > /dev/null & )", c.to_command
91
+ assert_equal "sudo su anotheruser -c 'sg devvers -c \\\"/usr/bin/env whoami\\\"'", c.to_command
112
92
  end
113
93
 
114
94
  def test_umask
@@ -126,13 +106,13 @@ module SSHKit
126
106
  def test_umask_with_working_directory_and_user
127
107
  SSHKit.config.umask = '007'
128
108
  c = Command.new(:touch, 'somefile', in: '/var', user: 'alice')
129
- assert_equal "cd /var && umask 007 && sudo su alice -c \"/usr/bin/env touch somefile\"", c.to_command
109
+ assert_equal "cd /var && umask 007 && sudo su alice -c '/usr/bin/env touch somefile'", c.to_command
130
110
  end
131
111
 
132
112
  def test_umask_with_env_and_working_directory_and_user
133
113
  SSHKit.config.umask = '007'
134
114
  c = Command.new(:touch, 'somefile', user: 'bob', env: {a: 'b'}, in: '/var')
135
- assert_equal "cd /var && umask 007 && ( A=b sudo su bob -c \"/usr/bin/env touch somefile\" )", c.to_command
115
+ assert_equal "cd /var && umask 007 && ( A=b sudo su bob -c '/usr/bin/env touch somefile' )", c.to_command
136
116
  end
137
117
 
138
118
  def test_verbosity_defaults_to_logger_info
@@ -51,6 +51,6 @@ module SSHKit
51
51
 
52
52
  assert_equal map[:rake], "/home/vagrant/.rbenv/bin/rbenv exec bundle exec rake"
53
53
  end
54
-
54
+
55
55
  end
56
56
  end
@@ -55,7 +55,7 @@ module SSHKit
55
55
  assert SSHKit.config.format = :dot
56
56
  assert SSHKit.config.output.is_a? SSHKit::Formatter::Dot
57
57
  end
58
-
58
+
59
59
  def test_setting_formatter_to_blackhole
60
60
  assert SSHKit.config.format = :BlackHole
61
61
  assert SSHKit.config.output.is_a? SSHKit::Formatter::BlackHole
@@ -83,8 +83,8 @@ module SSHKit
83
83
  1.example.com
84
84
  2.example.com
85
85
  3.example.com
86
- 4.example.com
87
- 5.example.com
86
+ 4.example.com
87
+ 5.example.com
88
88
  6.example.com
89
89
  }
90
90
  ).each in: :groups, &block_to_run
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.4.0
4
+ version: 1.5.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: 2014-04-15 00:00:00.000000000 Z
12
+ date: 2014-05-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: net-ssh
@@ -40,7 +40,7 @@ dependencies:
40
40
  - !ruby/object:Gem::Version
41
41
  version: 1.1.2
42
42
  - !ruby/object:Gem::Dependency
43
- name: term-ansicolor
43
+ name: colorize
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - '>='
@@ -163,11 +163,13 @@ files:
163
163
  - lib/sshkit/backends/netssh.rb
164
164
  - lib/sshkit/backends/printer.rb
165
165
  - lib/sshkit/backends/skipper.rb
166
+ - lib/sshkit/color.rb
166
167
  - lib/sshkit/command.rb
167
168
  - lib/sshkit/command_map.rb
168
169
  - lib/sshkit/configuration.rb
169
170
  - lib/sshkit/coordinator.rb
170
171
  - lib/sshkit/dsl.rb
172
+ - lib/sshkit/exception.rb
171
173
  - lib/sshkit/formatters/abstract.rb
172
174
  - lib/sshkit/formatters/black_hole.rb
173
175
  - lib/sshkit/formatters/dot.rb