opswalrus 1.0.51 → 1.0.52
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 +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 +52 -1
- 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
|
@@ -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
|
|
@@ -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
|