sshkit 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -3,6 +3,29 @@
3
3
  This file is written in reverse chronological order, newer releases will
4
4
  appear at the top.
5
5
 
6
+ ## 0.0.8
7
+
8
+ * Added DSL method `background()` this sends a task to the background using
9
+ `nohup` and redirects it's output to `/dev/null` so as to avoid littering
10
+ the filesystem with `nohup.out` files.
11
+
12
+ **Note:** Backgrounding a task won't work as you expect if you give it a
13
+ string, that is you must do `background(:sleep, 5)` and not `background("sleep 5")`
14
+ according to the rules by which a command is not processed in any way **if it
15
+ contains a spaca character in it's first argument**.
16
+
17
+ Usage Example:
18
+
19
+ on hosts do
20
+ background :rake, "assets:precompile" # typically takes 5 minutes!
21
+ end
22
+
23
+ **Further:** Many programs are badly behaved and no not work well with `nohup`
24
+ it has to do with the way nohup works, reopening the processe's file
25
+ descriptors and redirecting them. Programs that re-open, or otherwise
26
+ manipulate their own file descriptors may lock up when the SSH session is
27
+ disconnected, often they block writing to, or reading from stdin/out.
28
+
6
29
  ## 0.0.7
7
30
 
8
31
  * DSL method `execute()` will now raise `SSHKit::Command::Failed` when the
data/EXAMPLES.md CHANGED
@@ -65,6 +65,25 @@ leading slashes. It may be misleading as the `File.join()` is performed on the
65
65
  machine running the code, if that's a Windows box, the paths may be incorrectly
66
66
  joined according to the expectations of the machine receiving the commands.
67
67
 
68
+ ## Running a task in the background
69
+
70
+ on hosts do
71
+ in '/opt/sites/example.com' do
72
+ background :rails, :server
73
+ end
74
+ end
75
+
76
+ This will run something like `nohup /usr/bin/env rails server > /dev/null &`,
77
+ backgrounding the Rails process, and making sure we don't leave nohup log
78
+ files littering the filesystem.
79
+
80
+ **Note:** The `background()` method won't do what you expect if you pass a
81
+ string `sleep 5`, according to the rules of processing commands, you must call
82
+ `background(:sleep, "5")` (that is, command: sleep, args: 5).
83
+
84
+ **Further Note:** The background() task wraps the given command in `nohup .... &` under some
85
+ circumstances the program will hang anyway when the SSH session exits.
86
+
68
87
  ## Do not care about the host block
69
88
 
70
89
  on hosts do
data/README.md CHANGED
@@ -178,8 +178,8 @@ should be printed.
178
178
 
179
179
  * No handling of slow / timed out connections
180
180
  * No handling of slow / hung remote commands
181
- * No built-in way to background() something (execute and background the
182
- process).
181
+ * ~~No built-in way to background() something (execute and background the
182
+ process).~~
183
183
  * No environment handling (sshkit might not need to care)
184
184
  * ~~No arbitrary `Host` properties (example storing `roles` on servers, or other
185
185
  metadata that might be useful in the `on()` block)~~
@@ -190,10 +190,10 @@ should be printed.
190
190
  * No verbosity control, commands should have a `Logger::LEVEL` on them,
191
191
  user-generated should be at a high level, the commands auto-generated from
192
192
  the guards and checks from as() and within() should have a lower level.
193
- * Decide if `execute()` (and friends) should raise on non-zero exit statuses or
193
+ * ~~Decide if `execute()` (and friends) should raise on non-zero exit statuses or
194
194
  not, perhaps a family of similarly named bang methods should be the ones to
195
195
  raise. (Perhaps `test()` should be a way to `execute()` without raising, and
196
- `execute()` and friends should always raise)
196
+ `execute()` and friends should always raise)~~
197
197
  * It would be nice to be able to say `SSHKit.config.formatter = :pretty` and
198
198
  have that method setter do the legwork of updating `SSHKit.config.output` to
199
199
  be an instance of the correct formatter class wrapping the existing output
@@ -204,3 +204,6 @@ should be printed.
204
204
  easily be modified to look into some connection factory for it's objects,
205
205
  saving half a second when running lots of `on()` blocks.
206
206
  * Documentation! (YARD style)
207
+ * Wrap all commands in a known shell, that is that `execute('uptime')` should
208
+ be converted into `sh -c 'uptime'` to ensure that we have a consistent shell
209
+ experience.
@@ -25,6 +25,10 @@ module SSHKit
25
25
  raise MethodUnavailableError
26
26
  end
27
27
 
28
+ def background(command, args=[])
29
+ raise MethodUnavailableError
30
+ end
31
+
28
32
  def test(command, args=[])
29
33
  raise MethodUnavailableError
30
34
  end
@@ -20,6 +20,11 @@ module SSHKit
20
20
  _execute(*args).success?
21
21
  end
22
22
 
23
+ def background(*args)
24
+ options = args.extract_options!.merge(run_in_background: true)
25
+ _execute(*[*args, options]).success?
26
+ end
27
+
23
28
  def capture(*args)
24
29
  _execute(*args).stdout.strip
25
30
  end
@@ -128,11 +128,17 @@ module SSHKit
128
128
  if options[:user]
129
129
  cs << "sudo su #{options[:user]} -c "
130
130
  end
131
+ if options[:run_in_background]
132
+ cs << 'nohup '
133
+ end
131
134
  cs << SSHKit.config.command_map[command.to_sym]
132
135
  if args.any?
133
136
  cs << ' '
134
137
  cs << args.join(' ')
135
138
  end
139
+ if options[:run_in_background]
140
+ cs << ' > /dev/null &'
141
+ end
136
142
  if options[:env]
137
143
  cs << ' )'
138
144
  end
@@ -142,7 +148,7 @@ module SSHKit
142
148
  private
143
149
 
144
150
  def default_options
145
- { raise_on_non_zero_exit: true }
151
+ { raise_on_non_zero_exit: true, run_in_background: false }
146
152
  end
147
153
 
148
154
  def sanitize_command!
@@ -1,3 +1,3 @@
1
1
  module SSHKit
2
- VERSION = "0.0.7"
2
+ VERSION = "0.0.8"
3
3
  end
@@ -1,22 +1,10 @@
1
1
  require 'helper'
2
+ require 'benchmark'
2
3
 
3
4
  module SSHKit
4
5
 
5
6
  module Backend
6
7
 
7
- class ToSIoFormatter < StringIO
8
- extend Forwardable
9
- attr_reader :original_output
10
- def_delegators :@original_output, :read, :rewind
11
- def initialize(oio)
12
- @original_output = oio
13
- end
14
- def write(obj)
15
- warn "What: #{obj.to_hash}"
16
- original_output.write "> Executing #{obj}\n"
17
- end
18
- end
19
-
20
8
  class TestNetssh < FunctionalTest
21
9
 
22
10
  def setup
@@ -47,19 +35,18 @@ module SSHKit
47
35
  end
48
36
 
49
37
  def simple_netssh
50
- sio = ToSIoFormatter.new(StringIO.new)
51
38
  SSHKit.capture_output(sio) do
52
39
  printer.run
53
40
  end
54
41
  sio.rewind
55
42
  result = sio.read
56
43
  assert_equal <<-EOEXPECTED.unindent, result
57
- > Executing if test ! -d /opt/sites/example.com; then echo "Directory does not exist '/opt/sites/example.com'" 2>&1; false; fi
58
- > Executing cd /opt/sites/example.com && /usr/bin/env date
59
- > Executing cd /opt/sites/example.com && /usr/bin/env ls -l /some/directory
60
- > Executing if test ! -d /opt/sites/example.com/tmp; then echo "Directory does not exist '/opt/sites/example.com/tmp'" 2>&1; false; fi
61
- > Executing if ! sudo su -u root whoami > /dev/null; then echo "You cannot switch to user 'root' using sudo, please check the sudoers file" 2>&1; false; fi
62
- > Executing cd /opt/sites/example.com/tmp && ( RAILS_ENV=production ( sudo su -u root /usr/bin/env touch restart.txt ) )
44
+ if test ! -d /opt/sites/example.com; then echo "Directory does not exist '/opt/sites/example.com'" 2>&1; false; fi
45
+ cd /opt/sites/example.com && /usr/bin/env date
46
+ cd /opt/sites/example.com && /usr/bin/env ls -l /some/directory
47
+ if test ! -d /opt/sites/example.com/tmp; then echo "Directory does not exist '/opt/sites/example.com/tmp'" 2>&1; false; fi
48
+ if ! sudo su -u root whoami > /dev/null; then echo "You cannot switch to user 'root' using sudo, please check the sudoers file" 2>&1; false; fi
49
+ cd /opt/sites/example.com/tmp && ( RAILS_ENV=production ( sudo su -u root /usr/bin/env touch restart.txt ) )
63
50
  EOEXPECTED
64
51
  end
65
52
 
@@ -90,6 +77,21 @@ module SSHKit
90
77
  end.run
91
78
  end
92
79
 
80
+ def test_backgrounding_a_process
81
+ #SSHKit.config.output = SSHKit::Formatter::Pretty.new($stdout)
82
+ process_list = ""
83
+ time = Benchmark.measure do
84
+ Netssh.new(a_host) do
85
+ background :sleep, 5
86
+ end.run
87
+ Netssh.new(a_host) do
88
+ process_list = capture :ps, "aux | grep sleep | grep -v grep; true"
89
+ end.run
90
+ end
91
+ assert_operator time.real, :<, 1
92
+ assert_match "sleep 5", process_list
93
+ end
94
+
93
95
  end
94
96
 
95
97
  end
@@ -51,6 +51,21 @@ module SSHKit
51
51
  assert_equal "sudo su anotheruser -c /usr/bin/env whoami", String(c)
52
52
  end
53
53
 
54
+ def test_backgrounding_a_task
55
+ c = Command.new(:sleep, 15, run_in_background: true)
56
+ assert_equal "nohup /usr/bin/env sleep 15 > /dev/null &", String(c)
57
+ end
58
+
59
+ def test_backgrounding_a_task_as_a_given_user
60
+ c = Command.new(:sleep, 15, run_in_background: true, user: :anotheruser)
61
+ assert_equal "sudo su anotheruser -c nohup /usr/bin/env sleep 15 > /dev/null &", String(c)
62
+ end
63
+
64
+ def test_backgrounding_a_task_as_a_given_user_with_env
65
+ c = Command.new(:sleep, 15, run_in_background: true, user: :anotheruser, env: {a: :b})
66
+ assert_equal "( A=b sudo su anotheruser -c nohup /usr/bin/env sleep 15 > /dev/null & )", String(c)
67
+ end
68
+
54
69
  def test_complete?
55
70
  c = Command.new(:whoami, raise_on_non_zero_exit: false)
56
71
  refute c.complete?
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: 0.0.7
4
+ version: 0.0.8
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -256,10 +256,10 @@ files:
256
256
  - lib/sshkit/runners/sequential.rb
257
257
  - lib/sshkit/version.rb
258
258
  - sshkit.gemspec
259
+ - test/functional/backends/test_netssh.rb
259
260
  - test/functional/test_coordinator.rb
260
261
  - test/functional/test_ssh_server_comes_up_for_functional_tests.rb
261
262
  - test/helper.rb
262
- - test/integration/backends/test_netssh.rb
263
263
  - test/unit/backends/test_netssh.rb
264
264
  - test/unit/backends/test_printer.rb
265
265
  - test/unit/core_ext/test_string.rb
@@ -292,10 +292,10 @@ signing_key:
292
292
  specification_version: 3
293
293
  summary: SSHKit makes it easy to write structured, testable SSH commands in Ruby
294
294
  test_files:
295
+ - test/functional/backends/test_netssh.rb
295
296
  - test/functional/test_coordinator.rb
296
297
  - test/functional/test_ssh_server_comes_up_for_functional_tests.rb
297
298
  - test/helper.rb
298
- - test/integration/backends/test_netssh.rb
299
299
  - test/unit/backends/test_netssh.rb
300
300
  - test/unit/backends/test_printer.rb
301
301
  - test/unit/core_ext/test_string.rb