opswalrus 1.0.51 → 1.0.53
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/Gemfile.lock +73 -1
- data/lib/opswalrus/_reboot.ops +64 -0
- data/lib/opswalrus/_run_remote.ops +12 -0
- data/lib/opswalrus/app.rb +53 -11
- data/lib/opswalrus/cli.rb +51 -25
- data/lib/opswalrus/host.rb +109 -44
- data/lib/opswalrus/interaction_handlers.rb +2 -4
- data/lib/opswalrus/operation_runner.rb +6 -6
- data/lib/opswalrus/ops_file_script_dsl.rb +61 -29
- data/lib/opswalrus/patches.rb +2 -0
- data/lib/opswalrus/version.rb +1 -1
- data/opswalrus.gemspec +1 -0
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 007f1ac465143fdd629539509b94a8bf7230b034817526d1265768f97eae0786
|
4
|
+
data.tar.gz: 9d0cc607ce7c9fcd06d9e402254b3eb5a75a68687a1ee485aa915b3cd488e2b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83b67721de074efa3ad368ed21ec469d40928351c4ccc6f32d530e3bbda2cdd81b62f2b72692a4796b74d1efb7bbbfa31721db0eefb93864069dc277f8aa66fe
|
7
|
+
data.tar.gz: b54d628df099de1f4c2eea591b86449b5fd254d495ccae38f89dd95f5d1137171aaf9e6c1bd2023613f1c33561b5e49bd12d5696d6bc197bc065ead0a79aed1d
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
opswalrus (1.0.
|
4
|
+
opswalrus (1.0.53)
|
5
|
+
activesupport (~> 7.0)
|
5
6
|
bcrypt_pbkdf (~> 1.1)
|
6
7
|
binding_of_caller (~> 1.0)
|
7
8
|
citrus (~> 3.0)
|
@@ -17,28 +18,61 @@ PATH
|
|
17
18
|
GEM
|
18
19
|
remote: https://rubygems.org/
|
19
20
|
specs:
|
21
|
+
activesupport (7.0.8)
|
22
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
23
|
+
i18n (>= 1.6, < 2)
|
24
|
+
minitest (>= 5.1)
|
25
|
+
tzinfo (~> 2.0)
|
20
26
|
addressable (2.8.5)
|
21
27
|
public_suffix (>= 2.0.2, < 6.0)
|
28
|
+
ast (2.4.2)
|
29
|
+
backport (1.2.0)
|
30
|
+
base64 (0.1.1)
|
22
31
|
bcrypt_pbkdf (1.1.0)
|
32
|
+
benchmark (0.2.1)
|
23
33
|
binding_of_caller (1.0.0)
|
24
34
|
debug_inspector (>= 0.0.1)
|
25
35
|
citrus (3.0.2)
|
26
36
|
concurrent-ruby (1.2.2)
|
27
37
|
debug_inspector (1.1.0)
|
28
38
|
diff-lcs (1.5.0)
|
39
|
+
e2mmap (0.1.0)
|
29
40
|
ed25519 (1.3.0)
|
30
41
|
git (1.18.0)
|
31
42
|
addressable (~> 2.8)
|
32
43
|
rchardet (~> 1.8)
|
33
44
|
gli (2.21.1)
|
45
|
+
i18n (1.14.1)
|
46
|
+
concurrent-ruby (~> 1.0)
|
47
|
+
jaro_winkler (1.5.6)
|
48
|
+
json (2.6.3)
|
49
|
+
kramdown (2.4.0)
|
50
|
+
rexml
|
51
|
+
kramdown-parser-gfm (1.1.0)
|
52
|
+
kramdown (~> 2.0)
|
53
|
+
language_server-protocol (3.17.0.3)
|
54
|
+
minitest (5.20.0)
|
34
55
|
net-scp (4.0.0)
|
35
56
|
net-ssh (>= 2.6.5, < 8.0.0)
|
36
57
|
net-ssh (7.2.0)
|
58
|
+
nokogiri (1.15.4-x86_64-linux)
|
59
|
+
racc (~> 1.4)
|
60
|
+
parallel (1.23.0)
|
61
|
+
parser (3.2.2.3)
|
62
|
+
ast (~> 2.4.1)
|
63
|
+
racc
|
37
64
|
pastel (0.8.0)
|
38
65
|
tty-color (~> 0.5)
|
39
66
|
public_suffix (5.0.3)
|
67
|
+
racc (1.7.1)
|
68
|
+
rainbow (3.1.1)
|
40
69
|
rake (13.0.6)
|
70
|
+
rbs (2.8.4)
|
41
71
|
rchardet (1.8.0)
|
72
|
+
regexp_parser (2.8.1)
|
73
|
+
reverse_markdown (2.1.1)
|
74
|
+
nokogiri
|
75
|
+
rexml (3.2.6)
|
42
76
|
rspec (3.12.0)
|
43
77
|
rspec-core (~> 3.12.0)
|
44
78
|
rspec-expectations (~> 3.12.0)
|
@@ -52,12 +86,45 @@ GEM
|
|
52
86
|
diff-lcs (>= 1.2.0, < 2.0)
|
53
87
|
rspec-support (~> 3.12.0)
|
54
88
|
rspec-support (3.12.1)
|
89
|
+
rubocop (1.56.3)
|
90
|
+
base64 (~> 0.1.1)
|
91
|
+
json (~> 2.3)
|
92
|
+
language_server-protocol (>= 3.17.0)
|
93
|
+
parallel (~> 1.10)
|
94
|
+
parser (>= 3.2.2.3)
|
95
|
+
rainbow (>= 2.2.2, < 4.0)
|
96
|
+
regexp_parser (>= 1.8, < 3.0)
|
97
|
+
rexml (>= 3.2.5, < 4.0)
|
98
|
+
rubocop-ast (>= 1.28.1, < 2.0)
|
99
|
+
ruby-progressbar (~> 1.7)
|
100
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
101
|
+
rubocop-ast (1.29.0)
|
102
|
+
parser (>= 3.2.1.0)
|
103
|
+
ruby-progressbar (1.13.0)
|
55
104
|
rubyzip (2.3.2)
|
56
105
|
semantic_logger (4.14.0)
|
57
106
|
concurrent-ruby (~> 1.0)
|
107
|
+
solargraph (0.49.0)
|
108
|
+
backport (~> 1.2)
|
109
|
+
benchmark
|
110
|
+
bundler (~> 2.0)
|
111
|
+
diff-lcs (~> 1.4)
|
112
|
+
e2mmap
|
113
|
+
jaro_winkler (~> 1.5)
|
114
|
+
kramdown (~> 2.3)
|
115
|
+
kramdown-parser-gfm (~> 1.1)
|
116
|
+
parser (~> 3.0)
|
117
|
+
rbs (~> 2.0)
|
118
|
+
reverse_markdown (~> 2.0)
|
119
|
+
rubocop (~> 1.38)
|
120
|
+
thor (~> 1.0)
|
121
|
+
tilt (~> 2.0)
|
122
|
+
yard (~> 0.9, >= 0.9.24)
|
58
123
|
sshkit (1.21.5)
|
59
124
|
net-scp (>= 1.1.2)
|
60
125
|
net-ssh (>= 2.8.0)
|
126
|
+
thor (1.2.2)
|
127
|
+
tilt (2.3.0)
|
61
128
|
tty-color (0.6.0)
|
62
129
|
tty-cursor (0.7.1)
|
63
130
|
tty-editor (0.7.0)
|
@@ -70,7 +137,11 @@ GEM
|
|
70
137
|
tty-screen (~> 0.8)
|
71
138
|
wisper (~> 2.0)
|
72
139
|
tty-screen (0.8.1)
|
140
|
+
tzinfo (2.0.6)
|
141
|
+
concurrent-ruby (~> 1.0)
|
142
|
+
unicode-display_width (2.4.2)
|
73
143
|
wisper (2.0.1)
|
144
|
+
yard (0.9.34)
|
74
145
|
|
75
146
|
PLATFORMS
|
76
147
|
x86_64-linux
|
@@ -79,6 +150,7 @@ DEPENDENCIES
|
|
79
150
|
opswalrus!
|
80
151
|
rake (~> 13.0)
|
81
152
|
rspec (~> 3.0)
|
153
|
+
solargraph
|
82
154
|
|
83
155
|
BUNDLED WITH
|
84
156
|
2.4.10
|
@@ -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
|
@@ -0,0 +1,12 @@
|
|
1
|
+
params:
|
2
|
+
ops_file: OpsFile
|
3
|
+
operation_kv_args: array string
|
4
|
+
...
|
5
|
+
ssh in: :sequence do
|
6
|
+
# ssh_noprep do
|
7
|
+
puts params.stringify_keys!
|
8
|
+
desc "Running `#{params.ops_file.ops_file_path} #{params.operation_kv_args.join(' ')}` on #{to_s} (alias=#{self.alias})"
|
9
|
+
# run_ops(ops_command, ops_command_options = nil, command_arguments, in_bundle_root_dir: true, ops_prompt_for_sudo_password: false)
|
10
|
+
puts self.inspect
|
11
|
+
self._invoke_remote(params.ops_file, *params.operation_kv_args)
|
12
|
+
end
|
data/lib/opswalrus/app.rb
CHANGED
@@ -38,11 +38,11 @@ module OpsWalrus
|
|
38
38
|
attr_reader :identity_file_paths
|
39
39
|
|
40
40
|
def initialize(pwd = Dir.pwd)
|
41
|
-
SemanticLogger.default_level = :
|
41
|
+
SemanticLogger.default_level = :warn
|
42
42
|
# SemanticLogger.add_appender(file_name: 'development.log', formatter: :color) # Log to a file, and use the colorized formatter
|
43
43
|
SemanticLogger.add_appender(io: $stdout, formatter: :color) # Log errors and above to standard error:
|
44
44
|
@logger = SemanticLogger[OpsWalrus] # Logger.new($stdout, level: Logger::INFO)
|
45
|
-
@logger.level = :
|
45
|
+
@logger.level = :warn # :trace or 'trace'
|
46
46
|
|
47
47
|
# @logger.warn Style.yellow("warn"), foo: "bar", baz: {qux: "quux"}
|
48
48
|
# @logger.info Style.yellow("info"), foo: "bar", baz: {qux: "quux"}
|
@@ -216,18 +216,22 @@ module OpsWalrus
|
|
216
216
|
end
|
217
217
|
|
218
218
|
def bootstrap()
|
219
|
-
|
220
|
-
bootstrap_ops_file = OpsFile.new(self, __FILE__.to_pathname.dirname.join("_bootstrap.ops"))
|
221
|
-
op = OperationRunner.new(self, bootstrap_ops_file)
|
222
|
-
op.run([], params_json_hash: @params)
|
219
|
+
run_internal("_bootstrap.ops")
|
223
220
|
end
|
224
221
|
|
225
222
|
def shell(command)
|
223
|
+
run_internal("_shell.ops", {"command" => command})
|
224
|
+
end
|
225
|
+
|
226
|
+
def reboot()
|
227
|
+
run_internal("_reboot.ops")
|
228
|
+
end
|
229
|
+
|
230
|
+
def run_internal(ops_file_name, params = @params)
|
226
231
|
set_pwd(__FILE__.to_pathname.dirname)
|
227
|
-
|
228
|
-
op = OperationRunner.new(self,
|
229
|
-
|
230
|
-
result = op.run([], params_json_hash: {"command" => command})
|
232
|
+
internal_ops_file = OpsFile.new(self, __FILE__.to_pathname.dirname.join(ops_file_name))
|
233
|
+
op = OperationRunner.new(self, internal_ops_file)
|
234
|
+
result = op.run([], params_json_hash: params)
|
231
235
|
puts "result class=#{result.class}"
|
232
236
|
exit_status = result.exit_status
|
233
237
|
stdout = JSON.pretty_generate(result.value)
|
@@ -243,7 +247,45 @@ module OpsWalrus
|
|
243
247
|
1
|
244
248
|
end
|
245
249
|
|
246
|
-
#
|
250
|
+
# package_operation_and_args is of the form ["github.com/davidkellis/my-package/sub-package1", "operation1", "arg1:val1", "arg2:val2", "arg3:val3"]
|
251
|
+
# if the first argument is the path to a .ops file, then treat it as a local path, and add the containing package
|
252
|
+
# to the load path
|
253
|
+
# otherwise, copy the
|
254
|
+
# returns the exit status code that the script should terminate with
|
255
|
+
def run_remote(package_operation_and_args, update_bundle: false)
|
256
|
+
return 0 if package_operation_and_args.empty?
|
257
|
+
|
258
|
+
ops_file_path, operation_kv_args, tmp_bundle_root_dir = get_entry_point_ops_file_and_args(package_operation_and_args)
|
259
|
+
|
260
|
+
ops_file = load_entry_point_ops_file(ops_file_path, tmp_bundle_root_dir)
|
261
|
+
|
262
|
+
bundler.update if update_bundle
|
263
|
+
|
264
|
+
debug "Running: #{ops_file.ops_file_path}"
|
265
|
+
|
266
|
+
internal_ops_file = OpsFile.new(self, __FILE__.to_pathname.dirname.join("_run_remote.ops"))
|
267
|
+
|
268
|
+
op = OperationRunner.new(self, internal_ops_file)
|
269
|
+
result = op.run([], params_json_hash: {ops_file: ops_file, operation_kv_args: operation_kv_args})
|
270
|
+
exit_status = result.exit_status
|
271
|
+
|
272
|
+
debug "Op exit_status"
|
273
|
+
debug exit_status
|
274
|
+
|
275
|
+
debug "Op output"
|
276
|
+
debug JSON.pretty_generate(result.value)
|
277
|
+
|
278
|
+
puts JSON.pretty_generate(result.value)
|
279
|
+
|
280
|
+
exit_status
|
281
|
+
rescue Error => e
|
282
|
+
puts "Error: #{e.message}"
|
283
|
+
1
|
284
|
+
ensure
|
285
|
+
FileUtils.remove_entry(tmp_bundle_root_dir) if tmp_bundle_root_dir
|
286
|
+
end
|
287
|
+
|
288
|
+
# package_operation_and_args is of the form ["github.com/davidkellis/my-package/sub-package1", "operation1", "arg1:val1", "arg2:val2", "arg3:val3"]
|
247
289
|
# if the first argument is the path to a .ops file, then treat it as a local path, and add the containing package
|
248
290
|
# to the load path
|
249
291
|
# otherwise, copy the
|
data/lib/opswalrus/cli.rb
CHANGED
@@ -30,13 +30,11 @@ module OpsWalrus
|
|
30
30
|
|
31
31
|
program_desc 'ops is an operation runner'
|
32
32
|
|
33
|
-
switch
|
34
|
-
switch :
|
35
|
-
switch :
|
33
|
+
switch :loud, desc: "Verbose output"
|
34
|
+
switch :louder, desc: "Debug output"
|
35
|
+
switch :loudest, desc: "Trace output"
|
36
36
|
|
37
|
-
switch :
|
38
|
-
switch :dryrun, desc: "Perform a dry run"
|
39
|
-
switch :dry_run, desc: "Perform a dry run"
|
37
|
+
switch :dry, desc: "Perform a dry run"
|
40
38
|
|
41
39
|
flag [:h, :hosts], multiple: true, desc: "Specify the hosts.yaml file"
|
42
40
|
flag [:t, :tags], multiple: true, desc: "Specify a set of tags to filter the hosts by"
|
@@ -61,7 +59,7 @@ module OpsWalrus
|
|
61
59
|
hosts = global_options[:hosts]
|
62
60
|
tags = global_options[:tags]
|
63
61
|
|
64
|
-
$app.set_log_level(global_options[:
|
62
|
+
$app.set_log_level(global_options[:loudest] && :trace || global_options[:louder] && :debug || global_options[:loud] && :info || :warn)
|
65
63
|
|
66
64
|
$app.report_inventory(hosts, tags: tags)
|
67
65
|
end
|
@@ -123,12 +121,10 @@ module OpsWalrus
|
|
123
121
|
long_desc 'Bootstrap a set of hotss to run opswalrus: install dependencies, ruby, opswalrus gem'
|
124
122
|
command :bootstrap do |c|
|
125
123
|
# dry run
|
126
|
-
c.switch :
|
127
|
-
c.switch :dryrun, desc: "Perform a dry run"
|
128
|
-
c.switch :dry_run, desc: "Perform a dry run"
|
124
|
+
c.switch :dry, desc: "Perform a dry run"
|
129
125
|
|
130
126
|
c.action do |global_options, options, args|
|
131
|
-
$app.set_log_level(global_options[:
|
127
|
+
$app.set_log_level(global_options[:loudest] && :trace || global_options[:louder] && :debug || global_options[:loud] && :info || :warn)
|
132
128
|
|
133
129
|
hosts = global_options[:hosts]
|
134
130
|
$app.set_inventory_hosts(hosts)
|
@@ -141,7 +137,7 @@ module OpsWalrus
|
|
141
137
|
|
142
138
|
$app.set_identity_files(id_files)
|
143
139
|
|
144
|
-
dry_run = [:
|
140
|
+
dry_run = global_options[:dry] || options[:dry]
|
145
141
|
$app.dry_run! if dry_run
|
146
142
|
|
147
143
|
$app.bootstrap()
|
@@ -155,12 +151,10 @@ module OpsWalrus
|
|
155
151
|
c.flag [:u, :user], desc: "Specify the user that the operation will run as"
|
156
152
|
|
157
153
|
# dry run
|
158
|
-
c.switch :
|
159
|
-
c.switch :dryrun, desc: "Perform a dry run"
|
160
|
-
c.switch :dry_run, desc: "Perform a dry run"
|
154
|
+
c.switch :dry, desc: "Perform a dry run"
|
161
155
|
|
162
156
|
c.action do |global_options, options, args|
|
163
|
-
$app.set_log_level(global_options[:
|
157
|
+
$app.set_log_level(global_options[:loudest] && :trace || global_options[:louder] && :debug || global_options[:loud] && :info || :warn)
|
164
158
|
|
165
159
|
hosts = global_options[:hosts]
|
166
160
|
$app.set_inventory_hosts(hosts)
|
@@ -176,7 +170,7 @@ module OpsWalrus
|
|
176
170
|
|
177
171
|
$app.set_identity_files(id_files)
|
178
172
|
|
179
|
-
dry_run = [:
|
173
|
+
dry_run = global_options[:dry] || options[:dry]
|
180
174
|
$app.dry_run! if dry_run
|
181
175
|
|
182
176
|
if options[:pass]
|
@@ -189,6 +183,35 @@ module OpsWalrus
|
|
189
183
|
end
|
190
184
|
end
|
191
185
|
|
186
|
+
desc "Reboot one or more remote hosts"
|
187
|
+
long_desc 'Reboot one or more remote hosts'
|
188
|
+
command :reboot do |c|
|
189
|
+
# dry run
|
190
|
+
c.switch :dry, desc: "Perform a dry run"
|
191
|
+
|
192
|
+
c.action do |global_options, options, args|
|
193
|
+
$app.set_log_level(global_options[:loudest] && :trace || global_options[:louder] && :debug || global_options[:loud] && :info || :warn)
|
194
|
+
|
195
|
+
hosts = global_options[:hosts]
|
196
|
+
$app.set_inventory_hosts(hosts)
|
197
|
+
|
198
|
+
tags = global_options[:tags]
|
199
|
+
$app.set_inventory_tags(tags)
|
200
|
+
|
201
|
+
id_files = global_options[:id]
|
202
|
+
id_files = OpsWalrus.env_specified_age_ids if id_files.empty?
|
203
|
+
|
204
|
+
$app.set_identity_files(id_files)
|
205
|
+
|
206
|
+
dry_run = global_options[:dry] || options[:dry]
|
207
|
+
$app.dry_run! if dry_run
|
208
|
+
|
209
|
+
exit_status = $app.reboot()
|
210
|
+
|
211
|
+
exit_now!("error", exit_status) unless exit_status == 0
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
192
215
|
desc 'Run an operation from a package'
|
193
216
|
long_desc 'Run the specified operation found within the specified package'
|
194
217
|
arg 'args', :multiple
|
@@ -196,17 +219,16 @@ module OpsWalrus
|
|
196
219
|
c.switch [:b, :bundle], desc: "Update bundle prior to running the specified operation"
|
197
220
|
c.switch :pass, desc: "Prompt for a sudo password"
|
198
221
|
c.switch :script, desc: "Script mode"
|
222
|
+
c.switch [:r, :remote], desc: "Run the operation on the remote hosts"
|
199
223
|
|
200
224
|
c.flag [:u, :user], desc: "Specify the user that the operation will run as"
|
201
225
|
c.flag [:p, :params], desc: "Either specify a file that contains JSON OR specify a JSON encoded string. In both cases, the JSON represents the runtime arguments (i.e. the params) for the operation. The JSON string must conform to the params schema for the operation being run."
|
202
226
|
|
203
227
|
# dry run
|
204
|
-
c.switch :
|
205
|
-
c.switch :dryrun, desc: "Perform a dry run"
|
206
|
-
c.switch :dry_run, desc: "Perform a dry run"
|
228
|
+
c.switch :dry, desc: "Perform a dry run"
|
207
229
|
|
208
230
|
c.action do |global_options, options, args|
|
209
|
-
$app.set_log_level(global_options[:
|
231
|
+
$app.set_log_level(global_options[:loudest] && :trace || global_options[:louder] && :debug || global_options[:loud] && :info || :warn)
|
210
232
|
|
211
233
|
hosts = global_options[:hosts]
|
212
234
|
$app.set_inventory_hosts(hosts)
|
@@ -225,7 +247,7 @@ module OpsWalrus
|
|
225
247
|
|
226
248
|
$app.set_identity_files(id_files)
|
227
249
|
|
228
|
-
dry_run = [:
|
250
|
+
dry_run = global_options[:dry] || options[:dry]
|
229
251
|
$app.dry_run! if dry_run
|
230
252
|
|
231
253
|
if options[:pass]
|
@@ -236,7 +258,11 @@ module OpsWalrus
|
|
236
258
|
$app.script_mode!
|
237
259
|
end
|
238
260
|
|
239
|
-
exit_status =
|
261
|
+
exit_status = if options[:remote]
|
262
|
+
$app.run_remote(args, update_bundle: options[:bundle])
|
263
|
+
else
|
264
|
+
$app.run(args, update_bundle: options[:bundle])
|
265
|
+
end
|
240
266
|
|
241
267
|
exit_now!("error", exit_status) unless exit_status == 0
|
242
268
|
end
|
@@ -250,7 +276,7 @@ module OpsWalrus
|
|
250
276
|
long_desc 'Download and bundle the latest versions of dependencies for the current package'
|
251
277
|
c.command :update do |update|
|
252
278
|
update.action do |global_options, options, args|
|
253
|
-
$app.set_log_level(global_options[:
|
279
|
+
$app.set_log_level(global_options[:loudest] && :trace || global_options[:louder] && :debug || global_options[:loud] && :info || :warn)
|
254
280
|
|
255
281
|
$app.bundle_update
|
256
282
|
end
|
@@ -270,7 +296,7 @@ module OpsWalrus
|
|
270
296
|
unzip.flag [:o, :output], desc: "Specify the output directory"
|
271
297
|
|
272
298
|
unzip.action do |global_options, options, args|
|
273
|
-
$app.set_log_level(global_options[:
|
299
|
+
$app.set_log_level(global_options[:loudest] && :trace || global_options[:louder] && :debug || global_options[:loud] && :info || :warn)
|
274
300
|
|
275
301
|
output_dir = options[:output]
|
276
302
|
zip_file_path = args.first
|
data/lib/opswalrus/host.rb
CHANGED
@@ -81,6 +81,13 @@ module OpsWalrus
|
|
81
81
|
@_host.to_s
|
82
82
|
end
|
83
83
|
|
84
|
+
def _invoke_remote(ops_file, *args, **kwargs)
|
85
|
+
puts "boom"
|
86
|
+
is_invocation_a_call_to_package_in_bundle_dir = true
|
87
|
+
invocation_context = RemoteImportInvocationContext.new(@runtime_env, self, ops_file, is_invocation_a_call_to_package_in_bundle_dir, ops_prompt_for_sudo_password: !!ssh_password)
|
88
|
+
invocation_context._invoke(*args, **kwargs)
|
89
|
+
end
|
90
|
+
|
84
91
|
# returns [stdout, stderr, exit_status]
|
85
92
|
def _bootstrap_host(print_report = true)
|
86
93
|
# copy over bootstrap shell script
|
@@ -119,7 +126,7 @@ module OpsWalrus
|
|
119
126
|
raise Error, "Unable to upload ops bundle to remote host" unless upload_success
|
120
127
|
|
121
128
|
stdout, _stderr, exit_status = @_host.run_ops(:bundle, "unzip tmpops.zip", in_bundle_root_dir: false)
|
122
|
-
raise Error, "Unable to unzip ops bundle on remote host" unless exit_status == 0
|
129
|
+
raise Error, "Unable to unzip ops bundle on remote host: #{stdout}" unless exit_status == 0
|
123
130
|
tmp_bundle_root_dir = stdout.strip
|
124
131
|
@_host.set_ssh_session_tmp_bundle_root_dir(tmp_bundle_root_dir)
|
125
132
|
|
@@ -143,6 +150,58 @@ module OpsWalrus
|
|
143
150
|
|
144
151
|
|
145
152
|
module HostDSL
|
153
|
+
# delay: integer? # default: 1 - 1 second delay before reboot
|
154
|
+
# sync: boolean? # default: true - wait for the remote host to become available again before returning success/failure
|
155
|
+
# timeout: integer? # default: 300 - 300 seconds (5 minutes)
|
156
|
+
def reboot(delay: 1, sync: true, timeout: 300)
|
157
|
+
delay = 1 if delay < 1
|
158
|
+
|
159
|
+
desc "Rebooting #{to_s} (alias=#{self.alias})"
|
160
|
+
reboot_success = sh? 'sudo /bin/sh -c "(sleep {{ delay }} && reboot) &"'.mustache
|
161
|
+
puts reboot_success
|
162
|
+
|
163
|
+
reconnect_time = nil
|
164
|
+
reconnect_success = if sync
|
165
|
+
desc "Waiting for #{to_s} (alias=#{self.alias}) to finish rebooting"
|
166
|
+
initial_reconnect_delay = delay + 10
|
167
|
+
sleep initial_reconnect_delay
|
168
|
+
|
169
|
+
reconnected = false
|
170
|
+
give_up = false
|
171
|
+
t1 = Time.now
|
172
|
+
until reconnected || give_up
|
173
|
+
begin
|
174
|
+
reconnected = sh?('true')
|
175
|
+
# while trying to reconnect, we expect the following exceptions:
|
176
|
+
# 1. Net::SSH::Disconnect < Net::SSH::Exception with message: "connection closed by remote host"
|
177
|
+
# 2. Errno::ECONNRESET < SystemCallError with message: "Connection reset by peer"
|
178
|
+
# 3. Errno::ECONNREFUSED < SystemCallError with message: "Connection refused - connect(2) for 192.168.56.10:22"
|
179
|
+
rescue Net::SSH::Disconnect, Errno::ECONNRESET, Errno::ECONNREFUSED => e
|
180
|
+
# noop; we expect these while we're trying to reconnect
|
181
|
+
rescue => e
|
182
|
+
puts "#{e.class} < #{e.class.superclass}"
|
183
|
+
puts e.message
|
184
|
+
puts e.backtrace.take(5).join("\n")
|
185
|
+
end
|
186
|
+
|
187
|
+
wait_time_elapsed_in_seconds = Time.now - t1
|
188
|
+
give_up = wait_time_elapsed_in_seconds > timeout
|
189
|
+
sleep 5
|
190
|
+
end
|
191
|
+
reconnect_time = initial_reconnect_delay + (Time.now - t1)
|
192
|
+
reconnected
|
193
|
+
else
|
194
|
+
false
|
195
|
+
end
|
196
|
+
|
197
|
+
{
|
198
|
+
success: reboot_success && (sync == reconnect_success),
|
199
|
+
rebooted: reboot_success,
|
200
|
+
reconnected: reconnect_success,
|
201
|
+
reboot_duration: reconnect_time
|
202
|
+
}
|
203
|
+
end
|
204
|
+
|
146
205
|
# runs the given command
|
147
206
|
# returns the stdout from the command
|
148
207
|
def sh(desc_or_cmd = nil, cmd = nil, input: nil, &block)
|
@@ -163,7 +222,7 @@ module OpsWalrus
|
|
163
222
|
end
|
164
223
|
|
165
224
|
# returns the tuple: [stdout, stderr, exit_status]
|
166
|
-
def shell!(desc_or_cmd = nil, cmd = nil, block = nil, input: nil,
|
225
|
+
def shell!(desc_or_cmd = nil, cmd = nil, block = nil, input: nil, ops_prompt_for_sudo_password: false)
|
167
226
|
# description = nil
|
168
227
|
|
169
228
|
return ["", "", 0] if !desc_or_cmd && !cmd && !block # we were told to do nothing; like hitting enter at the bash prompt; we can do nothing successfully
|
@@ -179,7 +238,7 @@ module OpsWalrus
|
|
179
238
|
else
|
180
239
|
offset = 3 # 3, because 1 references the stack frame corresponding to the caller of WalrusLang.eval,
|
181
240
|
# 2 references the stack frame corresponding to the caller of shell!,
|
182
|
-
# and 3 references the stack frame corresponding to
|
241
|
+
# and 3 references the stack frame corresponding to the caller of either sh/sh?/shell
|
183
242
|
WalrusLang.eval(cmd, offset)
|
184
243
|
end
|
185
244
|
else
|
@@ -190,28 +249,24 @@ module OpsWalrus
|
|
190
249
|
#cmd = Shellwords.escape(cmd)
|
191
250
|
|
192
251
|
cmd_id = Random.uuid.split('-').first
|
193
|
-
# if App.instance.report_mode?
|
194
252
|
output_block = StringIO.open do |io|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
253
|
+
if App.instance.info? # this is true if log_level is trace, debug, info
|
254
|
+
io.print Style.blue(host)
|
255
|
+
io.print " (#{Style.blue(self.alias)})" if self.alias
|
256
|
+
io.print " | #{Style.magenta(description)}" if description
|
257
|
+
io.puts
|
258
|
+
io.print Style.yellow(cmd_id)
|
259
|
+
io.print Style.green.bold(" > ")
|
260
|
+
io.puts Style.yellow(cmd)
|
261
|
+
elsif App.instance.warn? && description
|
262
|
+
io.print Style.blue(host)
|
263
|
+
io.print " (#{Style.blue(self.alias)})" if self.alias
|
264
|
+
io.print " | #{Style.magenta(description)}" if description
|
265
|
+
io.puts
|
266
|
+
end
|
202
267
|
io.string
|
203
268
|
end
|
204
|
-
puts output_block
|
205
|
-
|
206
|
-
# puts Style.green("*" * 80)
|
207
|
-
# if self.alias
|
208
|
-
# print "[#{Style.blue(self.alias)} | #{Style.blue(host)}] "
|
209
|
-
# else
|
210
|
-
# print "[#{Style.blue(host)}] "
|
211
|
-
# end
|
212
|
-
# print "#{description}: " if description
|
213
|
-
# puts Style.yellow("[#{cmd_id}] #{cmd}")
|
214
|
-
# end
|
269
|
+
puts output_block unless output_block.empty?
|
215
270
|
|
216
271
|
return unless cmd && !cmd.strip.empty?
|
217
272
|
|
@@ -226,27 +281,37 @@ module OpsWalrus
|
|
226
281
|
seconds = t2 - t1
|
227
282
|
|
228
283
|
output_block = StringIO.open do |io|
|
229
|
-
if App.instance.info?
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
284
|
+
if App.instance.info? # this is true if log_level is trace, debug, info
|
285
|
+
if App.instance.trace?
|
286
|
+
io.puts Style.cyan(out)
|
287
|
+
io.puts Style.red(err)
|
288
|
+
elsif App.instance.debug?
|
289
|
+
io.puts Style.cyan(out)
|
290
|
+
io.puts Style.red(err)
|
291
|
+
elsif App.instance.info?
|
292
|
+
# io.puts Style.cyan(out)
|
293
|
+
# io.puts Style.red(err)
|
294
|
+
end
|
295
|
+
io.print Style.yellow(cmd_id)
|
296
|
+
io.print Style.blue(" | Finished in #{seconds} seconds with exit status ")
|
297
|
+
if exit_status == 0
|
298
|
+
io.puts Style.green("#{exit_status} (success)")
|
299
|
+
else
|
300
|
+
io.puts Style.red("#{exit_status} (failure)")
|
301
|
+
end
|
302
|
+
io.puts Style.green("*" * 80)
|
303
|
+
elsif App.instance.warn? && description
|
304
|
+
io.print Style.blue(" | Finished in #{seconds} seconds with exit status ")
|
305
|
+
if exit_status == 0
|
306
|
+
io.puts Style.green("#{exit_status} (success)")
|
307
|
+
else
|
308
|
+
io.puts Style.red("#{exit_status} (failure)")
|
309
|
+
end
|
310
|
+
io.puts Style.green("*" * 80)
|
245
311
|
end
|
246
|
-
io.puts Style.green("*" * 80)
|
247
312
|
io.string
|
248
313
|
end
|
249
|
-
puts output_block
|
314
|
+
puts output_block unless output_block.empty?
|
250
315
|
|
251
316
|
[out, err, exit_status]
|
252
317
|
end
|
@@ -262,18 +327,18 @@ module OpsWalrus
|
|
262
327
|
# cmd = "OPS_GEM=\"#{OPS_GEM}\" OPSWALRUS_LOCAL_HOSTNAME='#{local_hostname_for_remote_host}'; $OPS_GEM exec --conservative -g opswalrus ops"
|
263
328
|
cmd = "OPSWALRUS_LOCAL_HOSTNAME='#{local_hostname_for_remote_host}' eval #{OPS_CMD}"
|
264
329
|
if App.instance.trace?
|
265
|
-
cmd << " --
|
330
|
+
cmd << " --loudest"
|
266
331
|
elsif App.instance.debug?
|
267
|
-
cmd << " --
|
332
|
+
cmd << " --louder"
|
268
333
|
elsif App.instance.info?
|
269
|
-
cmd << " --
|
334
|
+
cmd << " --loud"
|
270
335
|
end
|
271
336
|
cmd << " #{ops_command.to_s}"
|
272
337
|
cmd << " #{ops_command_options.to_s}" if ops_command_options
|
273
338
|
cmd << " #{@tmp_bundle_root_dir}" if in_bundle_root_dir
|
274
339
|
cmd << " #{command_arguments}" unless command_arguments.empty?
|
275
340
|
|
276
|
-
shell!(cmd,
|
341
|
+
shell!(cmd, ops_prompt_for_sudo_password: ops_prompt_for_sudo_password)
|
277
342
|
end
|
278
343
|
|
279
344
|
def desc(msg)
|
@@ -8,9 +8,7 @@ module OpsWalrus
|
|
8
8
|
|
9
9
|
attr_accessor :input_mappings # Hash[ String | Regex => String ]
|
10
10
|
|
11
|
-
|
12
|
-
def initialize(mapping, log_level = nil)
|
13
|
-
@log_level = log_level
|
11
|
+
def initialize(mapping)
|
14
12
|
@input_mappings = mapping
|
15
13
|
end
|
16
14
|
|
@@ -63,7 +61,7 @@ module OpsWalrus
|
|
63
61
|
if new_mapping.empty? || new_mapping == @input_mappings
|
64
62
|
yield self
|
65
63
|
else
|
66
|
-
yield ScopedMappingInteractionHandler.new(new_mapping
|
64
|
+
yield ScopedMappingInteractionHandler.new(new_mapping)
|
67
65
|
end
|
68
66
|
end
|
69
67
|
|
@@ -110,12 +110,12 @@ module OpsWalrus
|
|
110
110
|
Invocation::Error.new(e)
|
111
111
|
end
|
112
112
|
|
113
|
-
if result.failure?
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
end
|
113
|
+
# if result.failure?
|
114
|
+
# App.instance.debug "Ops script error details:"
|
115
|
+
# App.instance.debug "Error: #{result.value}"
|
116
|
+
# App.instance.debug "Status code: #{result.exit_status}"
|
117
|
+
# App.instance.debug @entry_point_ops_file.script.to_s
|
118
|
+
# end
|
119
119
|
|
120
120
|
result
|
121
121
|
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
|
@@ -247,7 +263,7 @@ module OpsWalrus
|
|
247
263
|
end
|
248
264
|
|
249
265
|
# returns the tuple: [stdout, stderr, exit_status]
|
250
|
-
def shell!(desc_or_cmd = nil, cmd = nil, block = nil, input: nil
|
266
|
+
def shell!(desc_or_cmd = nil, cmd = nil, block = nil, input: nil)
|
251
267
|
return ["", "", 0] if !desc_or_cmd && !cmd && !block # we were told to do nothing; like hitting enter at the bash prompt; we can do nothing successfully
|
252
268
|
|
253
269
|
description = desc_or_cmd if cmd || block
|
@@ -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
|
@@ -269,7 +285,7 @@ module OpsWalrus
|
|
269
285
|
end
|
270
286
|
#cmd = Shellwords.escape(cmd)
|
271
287
|
|
272
|
-
report_on(@runtime_env.local_hostname, description, cmd
|
288
|
+
report_on(@runtime_env.local_hostname, description, cmd) do
|
273
289
|
if App.instance.dry_run?
|
274
290
|
["", "", 0]
|
275
291
|
else
|
@@ -284,19 +300,25 @@ module OpsWalrus
|
|
284
300
|
end
|
285
301
|
end
|
286
302
|
|
287
|
-
def report_on(hostname, description = nil, cmd
|
303
|
+
def report_on(hostname, description = nil, cmd)
|
288
304
|
cmd_id = Random.uuid.split('-').first
|
289
305
|
|
290
306
|
output_block = StringIO.open do |io|
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
307
|
+
if App.instance.info? # this is true if log_level is trace, debug, info
|
308
|
+
io.print Style.blue(hostname)
|
309
|
+
io.print " | #{Style.magenta(description)}" if description
|
310
|
+
io.puts
|
311
|
+
io.print Style.yellow(cmd_id)
|
312
|
+
io.print Style.green.bold(" > ")
|
313
|
+
io.puts Style.yellow(cmd)
|
314
|
+
elsif App.instance.warn? && description
|
315
|
+
io.print Style.blue(hostname)
|
316
|
+
io.print " | #{Style.magenta(description)}" if description
|
317
|
+
io.puts
|
318
|
+
end
|
297
319
|
io.string
|
298
320
|
end
|
299
|
-
puts output_block
|
321
|
+
puts output_block unless output_block.empty?
|
300
322
|
|
301
323
|
t1 = Time.now
|
302
324
|
out, err, exit_status = yield
|
@@ -304,27 +326,37 @@ module OpsWalrus
|
|
304
326
|
seconds = t2 - t1
|
305
327
|
|
306
328
|
output_block = StringIO.open do |io|
|
307
|
-
if App.instance.info?
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
329
|
+
if App.instance.info? # this is true if log_level is trace, debug, info
|
330
|
+
if App.instance.trace?
|
331
|
+
io.puts Style.cyan(out)
|
332
|
+
io.puts Style.red(err)
|
333
|
+
elsif App.instance.debug?
|
334
|
+
io.puts Style.cyan(out)
|
335
|
+
io.puts Style.red(err)
|
336
|
+
elsif App.instance.info?
|
337
|
+
# io.puts Style.cyan(out)
|
338
|
+
# io.puts Style.red(err)
|
339
|
+
end
|
340
|
+
io.print Style.yellow(cmd_id)
|
341
|
+
io.print Style.blue(" | Finished in #{seconds} seconds with exit status ")
|
342
|
+
if exit_status == 0
|
343
|
+
io.puts Style.green("#{exit_status} (success)")
|
344
|
+
else
|
345
|
+
io.puts Style.red("#{exit_status} (failure)")
|
346
|
+
end
|
347
|
+
io.puts Style.green("*" * 80)
|
348
|
+
elsif App.instance.warn? && description
|
349
|
+
io.print Style.blue(" | Finished in #{seconds} seconds with exit status ")
|
350
|
+
if exit_status == 0
|
351
|
+
io.puts Style.green("#{exit_status} (success)")
|
352
|
+
else
|
353
|
+
io.puts Style.red("#{exit_status} (failure)")
|
354
|
+
end
|
355
|
+
io.puts Style.green("*" * 80)
|
323
356
|
end
|
324
|
-
io.puts Style.green("*" * 80)
|
325
357
|
io.string
|
326
358
|
end
|
327
|
-
puts output_block
|
359
|
+
puts output_block unless output_block.empty?
|
328
360
|
|
329
361
|
[out, err, exit_status]
|
330
362
|
end
|
data/lib/opswalrus/patches.rb
CHANGED
data/lib/opswalrus/version.rb
CHANGED
data/opswalrus.gemspec
CHANGED
@@ -32,6 +32,7 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.require_paths = ["lib"]
|
33
33
|
|
34
34
|
# gem dependencies
|
35
|
+
spec.add_dependency "activesupport", "~> 7.0"
|
35
36
|
spec.add_dependency "binding_of_caller", "~> 1.0"
|
36
37
|
spec.add_dependency "citrus", "~> 3.0"
|
37
38
|
spec.add_dependency "gli", "~> 2.21"
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
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.53
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Ellis
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-09-
|
11
|
+
date: 2023-09-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '7.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '7.0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: binding_of_caller
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -185,6 +199,8 @@ files:
|
|
185
199
|
- exe/ops
|
186
200
|
- lib/opswalrus.rb
|
187
201
|
- lib/opswalrus/_bootstrap.ops
|
202
|
+
- lib/opswalrus/_reboot.ops
|
203
|
+
- lib/opswalrus/_run_remote.ops
|
188
204
|
- lib/opswalrus/_shell.ops
|
189
205
|
- lib/opswalrus/app.rb
|
190
206
|
- lib/opswalrus/bootstrap.sh
|