opswalrus 1.0.50 → 1.0.52

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
  SHA256:
3
- metadata.gz: 20cc50a607aa6aec146b62eb5dbef09a908c5485f8e0d3dbc39cf5bf9a92895e
4
- data.tar.gz: b27cdf6497c28f6ced9be489365003bad03ddb02e6c1285e76c45e6a5ee5d52b
3
+ metadata.gz: b1ce678751d6d62f4a9b5e40eb6127c22e2e664c6641023ce8df4135609fb73e
4
+ data.tar.gz: a2747abc2711e77a47371f7c16d1b5104d70b10419b09cd3e992e892cea2d6ca
5
5
  SHA512:
6
- metadata.gz: ec22d55f8417db680b98d0651855fd83589f0b2428c26be7279f7a2b62582120f4eafdb473b4df9004f98934508cf46f9686a1a28fe8c2adf83eadc82b2a0a38
7
- data.tar.gz: 7eb26d1957c2c8115ccaa06b497418f635a4876279c7a4ee5e16eea10546d80303af209a585d4b69ad02f15cca5ce0ea468af86e974947c6283a7998bd8e6426
6
+ metadata.gz: 5ec4e2b6a547e394b0290a47dafa7cc13960a9c21cef7e1a7edc4d20900cd304a1e0c828025df68f8e7e2e6ce22e3f48882fe44efcaaf1a25e3c74d357981071
7
+ data.tar.gz: 386afc9a49856131615ff5c5e0c54a1934bf8458d3a77f7f2c6e1da6479343c54a0be8436f8a08beec4e89d6f1b9019e463bc96a5c169f639a7061838f8f240f
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- opswalrus (1.0.50)
4
+ opswalrus (1.0.52)
5
5
  bcrypt_pbkdf (~> 1.1)
6
6
  binding_of_caller (~> 1.0)
7
7
  citrus (~> 3.0)
@@ -0,0 +1,64 @@
1
+ params:
2
+ delay: integer? # default: 1 - 1 second delay before reboot
3
+ sync: boolean? # default: true - wait for the remote host to become available again before returning success/failure
4
+ timeout: integer? # default: 300 - 300 seconds (5 minutes)
5
+ ...
6
+
7
+ delay = params.delay.integer!(default: 1)
8
+ sync = params.sync.boolean!(default: true)
9
+ timeout = params.timeout.integer!(default: 300)
10
+
11
+ delay = 1 if delay < 1
12
+
13
+ ssh_noprep in: :sequence do
14
+ # ssh_noprep do
15
+
16
+ # survey of command options:
17
+ # sudo reboot
18
+ # sudo systemctl reboot
19
+
20
+ # desc "Rebooting #{to_s} (alias=#{self.alias})"
21
+ # reboot_success = sh? 'sudo /bin/sh -c "(sleep {{ delay }} && reboot) &"'.mustache
22
+ # puts reboot_success
23
+
24
+ # reconnect_time = nil
25
+ # reconnect_success = if sync
26
+ # desc "Waiting for #{to_s} (alias=#{self.alias}) to finish rebooting"
27
+ # initial_reconnect_delay = delay + 10
28
+ # sleep initial_reconnect_delay
29
+
30
+ # reconnected = false
31
+ # give_up = false
32
+ # t1 = Time.now
33
+ # until reconnected || give_up
34
+ # begin
35
+ # reconnected = sh?('true')
36
+ # # while trying to reconnect, we expect the following exceptions:
37
+ # # 1. Net::SSH::Disconnect < Net::SSH::Exception with message: "connection closed by remote host"
38
+ # # 2. Errno::ECONNRESET < SystemCallError with message: "Connection reset by peer"
39
+ # rescue Net::SSH::Disconnect, Errno::ECONNRESET => e
40
+ # # noop; we expect these while we're trying to reconnect
41
+ # rescue => e
42
+ # puts "#{e.class} < #{e.class.superclass}"
43
+ # puts e.message
44
+ # puts e.backtrace.take(5).join("\n")
45
+ # end
46
+
47
+ # wait_time_elapsed_in_seconds = Time.now - t1
48
+ # give_up = wait_time_elapsed_in_seconds > timeout
49
+ # sleep 5
50
+ # end
51
+ # reconnect_time = initial_reconnect_delay + (Time.now - t1)
52
+ # reconnected
53
+ # else
54
+ # false
55
+ # end
56
+
57
+ # {
58
+ # success: reboot_success && (sync == reconnect_success),
59
+ # rebooted: reboot_success,
60
+ # reconnected: reconnect_success,
61
+ # reboot_duration: reconnect_time
62
+ # }
63
+ reboot(delay: delay, sync: sync, timeout: timeout)
64
+ end
data/lib/opswalrus/app.rb CHANGED
@@ -243,6 +243,26 @@ module OpsWalrus
243
243
  1
244
244
  end
245
245
 
246
+ def reboot()
247
+ set_pwd(__FILE__.to_pathname.dirname)
248
+ shell_ops_file = OpsFile.new(self, __FILE__.to_pathname.dirname.join("_reboot.ops"))
249
+ op = OperationRunner.new(self, shell_ops_file)
250
+ result = op.run([], params_json_hash: @params)
251
+ puts "result class=#{result.class}"
252
+ exit_status = result.exit_status
253
+ stdout = JSON.pretty_generate(result.value)
254
+ output = if exit_status == 0
255
+ Style.green(stdout)
256
+ else
257
+ Style.red(stdout)
258
+ end
259
+ puts output
260
+ exit_status
261
+ rescue Error => e
262
+ puts "Error: #{e.message}"
263
+ 1
264
+ end
265
+
246
266
  # args is of the form ["github.com/davidkellis/my-package/sub-package1", "operation1", "arg1:val1", "arg2:val2", "arg3:val3"]
247
267
  # if the first argument is the path to a .ops file, then treat it as a local path, and add the containing package
248
268
  # to the load path
data/lib/opswalrus/cli.rb CHANGED
@@ -189,6 +189,37 @@ module OpsWalrus
189
189
  end
190
190
  end
191
191
 
192
+ desc "Reboot one or more remote hosts"
193
+ long_desc 'Reboot one or more remote hosts'
194
+ command :reboot do |c|
195
+ # dry run
196
+ c.switch :noop, desc: "Perform a dry run"
197
+ c.switch :dryrun, desc: "Perform a dry run"
198
+ c.switch :dry_run, desc: "Perform a dry run"
199
+
200
+ c.action do |global_options, options, args|
201
+ $app.set_log_level(global_options[:trace] && :trace || global_options[:debug] && :debug || global_options[:verbose] && :info || :warn)
202
+
203
+ hosts = global_options[:hosts]
204
+ $app.set_inventory_hosts(hosts)
205
+
206
+ tags = global_options[:tags]
207
+ $app.set_inventory_tags(tags)
208
+
209
+ id_files = global_options[:id]
210
+ id_files = OpsWalrus.env_specified_age_ids if id_files.empty?
211
+
212
+ $app.set_identity_files(id_files)
213
+
214
+ dry_run = [:noop, :dryrun, :dry_run].any? {|sym| global_options[sym] || options[sym] }
215
+ $app.dry_run! if dry_run
216
+
217
+ exit_status = $app.reboot()
218
+
219
+ exit_now!("error", exit_status) unless exit_status == 0
220
+ end
221
+ end
222
+
192
223
  desc 'Run an operation from a package'
193
224
  long_desc 'Run the specified operation found within the specified package'
194
225
  arg 'args', :multiple
@@ -127,11 +127,11 @@ module OpsWalrus
127
127
  retval = instance_exec(local_host, &block) # local_host is passed as the argument to the block
128
128
 
129
129
  # todo: cleanup
130
- # if tmp_bundle_root_dir =~ /tmp/ # sanity check the temp path before we blow away something we don't intend
131
- # @_host.execute(:rm, "-rf", "tmpops.zip", tmp_bundle_root_dir)
132
- # else
133
- # @_host.execute(:rm, "-rf", "tmpops.zip")
134
- # end
130
+ if tmp_bundle_root_dir =~ /tmp/ # sanity check the temp path before we blow away something we don't intend
131
+ @_host.execute(:rm, "-rf", "tmpops.zip", tmp_bundle_root_dir)
132
+ else
133
+ @_host.execute(:rm, "-rf", "tmpops.zip")
134
+ end
135
135
 
136
136
  retval
137
137
  end
@@ -143,6 +143,57 @@ module OpsWalrus
143
143
 
144
144
 
145
145
  module HostDSL
146
+ # delay: integer? # default: 1 - 1 second delay before reboot
147
+ # sync: boolean? # default: true - wait for the remote host to become available again before returning success/failure
148
+ # timeout: integer? # default: 300 - 300 seconds (5 minutes)
149
+ def reboot(delay: 1, sync: true, timeout: 300)
150
+ delay = 1 if delay < 1
151
+
152
+ desc "Rebooting #{to_s} (alias=#{self.alias})"
153
+ reboot_success = sh? 'sudo /bin/sh -c "(sleep {{ delay }} && reboot) &"'.mustache
154
+ puts reboot_success
155
+
156
+ reconnect_time = nil
157
+ reconnect_success = if sync
158
+ desc "Waiting for #{to_s} (alias=#{self.alias}) to finish rebooting"
159
+ initial_reconnect_delay = delay + 10
160
+ sleep initial_reconnect_delay
161
+
162
+ reconnected = false
163
+ give_up = false
164
+ t1 = Time.now
165
+ until reconnected || give_up
166
+ begin
167
+ reconnected = sh?('true')
168
+ # while trying to reconnect, we expect the following exceptions:
169
+ # 1. Net::SSH::Disconnect < Net::SSH::Exception with message: "connection closed by remote host"
170
+ # 2. Errno::ECONNRESET < SystemCallError with message: "Connection reset by peer"
171
+ rescue Net::SSH::Disconnect, Errno::ECONNRESET => e
172
+ # noop; we expect these while we're trying to reconnect
173
+ rescue => e
174
+ puts "#{e.class} < #{e.class.superclass}"
175
+ puts e.message
176
+ puts e.backtrace.take(5).join("\n")
177
+ end
178
+
179
+ wait_time_elapsed_in_seconds = Time.now - t1
180
+ give_up = wait_time_elapsed_in_seconds > timeout
181
+ sleep 5
182
+ end
183
+ reconnect_time = initial_reconnect_delay + (Time.now - t1)
184
+ reconnected
185
+ else
186
+ false
187
+ end
188
+
189
+ {
190
+ success: reboot_success && (sync == reconnect_success),
191
+ rebooted: reboot_success,
192
+ reconnected: reconnect_success,
193
+ reboot_duration: reconnect_time
194
+ }
195
+ end
196
+
146
197
  # runs the given command
147
198
  # returns the stdout from the command
148
199
  def sh(desc_or_cmd = nil, cmd = nil, input: nil, &block)
@@ -179,7 +230,7 @@ module OpsWalrus
179
230
  else
180
231
  offset = 3 # 3, because 1 references the stack frame corresponding to the caller of WalrusLang.eval,
181
232
  # 2 references the stack frame corresponding to the caller of shell!,
182
- # and 3 references the stack frame corresponding to teh caller of either sh/sh?/shell
233
+ # and 3 references the stack frame corresponding to the caller of either sh/sh?/shell
183
234
  WalrusLang.eval(cmd, offset)
184
235
  end
185
236
  else
@@ -138,9 +138,9 @@ module OpsWalrus
138
138
  File.unlink(json_kwargs_tempfile) rescue nil
139
139
  end
140
140
  # todo: make sure this cleanup is present
141
- # if remote_json_kwargs_tempfile_basename
142
- # @host_proxy.execute(:rm, "-f", remote_json_kwargs_tempfile_basename)
143
- # end
141
+ if remote_json_kwargs_tempfile_basename
142
+ @host_proxy.execute(:rm, "-f", remote_json_kwargs_tempfile_basename)
143
+ end
144
144
  end
145
145
  end
146
146
  end
@@ -55,6 +55,9 @@ module OpsWalrus
55
55
  sshkit_hosts = hosts.map(&:sshkit_host)
56
56
  sshkit_host_to_ops_host_map = sshkit_hosts.zip(hosts).to_h
57
57
  ops_file_script = local_host = self
58
+
59
+ results_lock = Thread::Mutex.new
60
+ results = {}
58
61
  # on sshkit_hosts do |sshkit_host|
59
62
  SSHKit::Coordinator.new(sshkit_hosts).each(in: kwargs[:in] || :parallel) do |sshkit_host|
60
63
  # in this context, self is an instance of one of the subclasses of SSHKit::Backend::Abstract, e.g. SSHKit::Backend::Netssh
@@ -73,6 +76,10 @@ module OpsWalrus
73
76
  # we run the block in the context of the host proxy object, s.t. `self` within the block evaluates to the host proxy object
74
77
  retval = host.instance_exec(local_host, &block) # local_host is passed as the argument to the block
75
78
 
79
+ results_lock.synchronize do
80
+ results[host] = retval
81
+ end
82
+
76
83
  retval
77
84
  rescue SSHKit::Command::Failed => e
78
85
  App.instance.error "[!] Command failed:"
@@ -102,6 +109,7 @@ module OpsWalrus
102
109
  end
103
110
  end # runtime_env.handle_input
104
111
  end # SSHKit::Coordinator
112
+ results
105
113
  end # def ssh
106
114
 
107
115
  def ssh(*args, **kwargs, &block)
@@ -111,6 +119,9 @@ module OpsWalrus
111
119
  sshkit_hosts = hosts.map(&:sshkit_host)
112
120
  sshkit_host_to_ops_host_map = sshkit_hosts.zip(hosts).to_h
113
121
  ops_file_script = local_host = self
122
+
123
+ results_lock = Thread::Mutex.new
124
+ results = {}
114
125
  # bootstrap_shell_script = BootstrapLinuxHostShellScript
115
126
  # on sshkit_hosts do |sshkit_host|
116
127
  SSHKit::Coordinator.new(sshkit_hosts).each(in: kwargs[:in] || :parallel) do |sshkit_host|
@@ -134,6 +145,10 @@ module OpsWalrus
134
145
  puts "Failed to bootstrap #{host}. Unable to run operation."
135
146
  end
136
147
 
148
+ results_lock.synchronize do
149
+ results[host] = retval
150
+ end
151
+
137
152
  retval
138
153
  rescue SSHKit::Command::Failed => e
139
154
  App.instance.error "[!] Command failed:"
@@ -163,6 +178,7 @@ module OpsWalrus
163
178
  end
164
179
  end # runtime_env.handle_input
165
180
  end # SSHKit::Coordinator
181
+ results
166
182
  end # def ssh
167
183
 
168
184
  def current_dir
@@ -261,7 +277,7 @@ module OpsWalrus
261
277
  else
262
278
  offset = 3 # 3, because 1 references the stack frame corresponding to the caller of WalrusLang.eval,
263
279
  # 2 references the stack frame corresponding to the caller of shell!,
264
- # and 3 references the stack frame corresponding to teh caller of either sh/sh?/shell
280
+ # and 3 references the stack frame corresponding to the caller of either sh/sh?/shell
265
281
  WalrusLang.eval(cmd, offset)
266
282
  end
267
283
  else
@@ -1,3 +1,3 @@
1
1
  module OpsWalrus
2
- VERSION = "1.0.50"
2
+ VERSION = "1.0.52"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opswalrus
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.50
4
+ version: 1.0.52
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Ellis
@@ -185,6 +185,7 @@ files:
185
185
  - exe/ops
186
186
  - lib/opswalrus.rb
187
187
  - lib/opswalrus/_bootstrap.ops
188
+ - lib/opswalrus/_reboot.ops
188
189
  - lib/opswalrus/_shell.ops
189
190
  - lib/opswalrus/app.rb
190
191
  - lib/opswalrus/bootstrap.sh