sshkit 1.4.0 → 1.5.0

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