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 +4 -4
- data/Gemfile.lock +1 -1
- data/lib/opswalrus/_reboot.ops +64 -0
- data/lib/opswalrus/app.rb +20 -0
- data/lib/opswalrus/cli.rb +31 -0
- data/lib/opswalrus/host.rb +57 -6
- data/lib/opswalrus/invocation.rb +3 -3
- data/lib/opswalrus/ops_file_script_dsl.rb +17 -1
- data/lib/opswalrus/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b1ce678751d6d62f4a9b5e40eb6127c22e2e664c6641023ce8df4135609fb73e
|
4
|
+
data.tar.gz: a2747abc2711e77a47371f7c16d1b5104d70b10419b09cd3e992e892cea2d6ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ec4e2b6a547e394b0290a47dafa7cc13960a9c21cef7e1a7edc4d20900cd304a1e0c828025df68f8e7e2e6ce22e3f48882fe44efcaaf1a25e3c74d357981071
|
7
|
+
data.tar.gz: 386afc9a49856131615ff5c5e0c54a1934bf8458d3a77f7f2c6e1da6479343c54a0be8436f8a08beec4e89d6f1b9019e463bc96a5c169f639a7061838f8f240f
|
data/Gemfile.lock
CHANGED
@@ -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
|
data/lib/opswalrus/host.rb
CHANGED
@@ -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
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
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
|
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
|
data/lib/opswalrus/invocation.rb
CHANGED
@@ -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
|
-
|
142
|
-
|
143
|
-
|
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
|
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
|
data/lib/opswalrus/version.rb
CHANGED
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.
|
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
|