opswalrus 1.0.13 → 1.0.14
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/Dockerfile +2 -2
- data/Gemfile.lock +1 -1
- data/README.md +6 -4
- data/build.ops +2 -1
- data/lib/opswalrus/app.rb +74 -27
- data/lib/opswalrus/errors.rb +7 -0
- data/lib/opswalrus/host.rb +4 -0
- data/lib/opswalrus/invocation.rb +1 -442
- data/lib/opswalrus/operation_runner.rb +12 -12
- data/lib/opswalrus/ops_file_script.rb +7 -22
- data/lib/opswalrus/package_file.rb +11 -0
- data/lib/opswalrus/runtime_environment.rb +62 -11
- 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: 9ee2d0af10d38f7722a8af82b44af64f3c0d906de21b4425d472d4e27b9495b0
|
4
|
+
data.tar.gz: e42c56ffcdf0c1cf1b08e68771dcf46364439c057393ed325b5325964816d1a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39a22eed3d76788bb6d70ad4f4700d512a4e9612614508e407ee9d0187bd2f0aab3593845f5f6a8adb91020d330bd7d955bbe21c869c3aa7e6c1185af4204b6a
|
7
|
+
data.tar.gz: 63e635e997cead804423e1c966e7e429538e625532277b98178c19186345455b469222d5eabd4b7829998690753ba8d95d09788248b167e706d3512d05ece6de
|
data/Dockerfile
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -11,17 +11,19 @@ You have two options:
|
|
11
11
|
## Rubygems install
|
12
12
|
|
13
13
|
```shell
|
14
|
-
gem install opswalrus
|
14
|
+
❯ gem install opswalrus
|
15
15
|
|
16
|
-
ops version
|
16
|
+
❯ ops version
|
17
|
+
1.0.13
|
17
18
|
```
|
18
19
|
|
19
20
|
## Docker install
|
20
21
|
|
21
22
|
```shell
|
22
|
-
alias ops='docker run --rm -it -v $HOME/.ssh:/root/.ssh -v /var/run/docker.sock:/var/run/docker.sock -v ${PWD}/:/workdir ghcr.io/opswalrus/ops'
|
23
|
+
❯ alias ops='docker run --rm -it -v $HOME/.ssh:/root/.ssh -v /var/run/docker.sock:/var/run/docker.sock -v ${PWD}/:/workdir ghcr.io/opswalrus/ops'
|
23
24
|
|
24
|
-
ops version
|
25
|
+
❯ ops version
|
26
|
+
1.0.13
|
25
27
|
```
|
26
28
|
|
27
29
|
# Examples
|
data/build.ops
CHANGED
@@ -51,4 +51,5 @@ end
|
|
51
51
|
|
52
52
|
totp = sh("Get Rubygems OTP") { 'bw get totp Rubygems' }
|
53
53
|
sh("Push gem", input: {"You have enabled multi-factor authentication. Please enter OTP code." => "#{totp}\n"}) { 'gem push opswalrus-{{ version }}.gem' }
|
54
|
-
sh("Build docker image") { 'docker build -t opswalrus/ops:{{ version }} .' }
|
54
|
+
sh("Build docker image") { 'docker build -t ghcr.io/opswalrus/ops:latest -t ghcr.io/opswalrus/ops:{{ version }} -t opswalrus/ops:latest -t opswalrus/ops:{{ version }} .' }
|
55
|
+
sh("Push docker image to ghcr.io/opswalrus/ops") { 'docker push ghcr.io/opswalrus/ops:latest' }
|
data/lib/opswalrus/app.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
require "citrus"
|
2
2
|
require "io/console"
|
3
3
|
require "json"
|
4
|
+
require "logger"
|
4
5
|
require "random/formatter"
|
5
6
|
require "shellwords"
|
6
7
|
require "socket"
|
7
8
|
require "stringio"
|
8
9
|
require "yaml"
|
9
10
|
require "pathname"
|
11
|
+
require_relative "errors"
|
10
12
|
require_relative "patches"
|
11
13
|
require_relative "git"
|
12
14
|
require_relative "host"
|
@@ -18,9 +20,6 @@ require_relative "version"
|
|
18
20
|
|
19
21
|
|
20
22
|
module OpsWalrus
|
21
|
-
class Error < StandardError
|
22
|
-
end
|
23
|
-
|
24
23
|
class App
|
25
24
|
def self.instance(*args)
|
26
25
|
@instance ||= new(*args)
|
@@ -32,6 +31,8 @@ module OpsWalrus
|
|
32
31
|
attr_reader :local_hostname
|
33
32
|
|
34
33
|
def initialize(pwd = Dir.pwd)
|
34
|
+
@logger = Logger.new($stdout, level: Logger::INFO)
|
35
|
+
|
35
36
|
@verbose = false
|
36
37
|
@sudo_user = nil
|
37
38
|
@sudo_password = nil
|
@@ -89,6 +90,7 @@ module OpsWalrus
|
|
89
90
|
|
90
91
|
def set_verbose(verbose)
|
91
92
|
@verbose = verbose
|
93
|
+
@logger.debug! if verbose?
|
92
94
|
end
|
93
95
|
|
94
96
|
def verbose?
|
@@ -99,6 +101,22 @@ module OpsWalrus
|
|
99
101
|
@verbose == 2
|
100
102
|
end
|
101
103
|
|
104
|
+
def error(msg)
|
105
|
+
@logger.error(msg)
|
106
|
+
end
|
107
|
+
|
108
|
+
def warn(msg)
|
109
|
+
@logger.warn(msg)
|
110
|
+
end
|
111
|
+
|
112
|
+
def log(msg)
|
113
|
+
@logger.info(msg)
|
114
|
+
end
|
115
|
+
|
116
|
+
def debug(msg)
|
117
|
+
@logger.debug(msg)
|
118
|
+
end
|
119
|
+
|
102
120
|
def set_pwd(pwd)
|
103
121
|
@pwd = pwd.to_pathname
|
104
122
|
@bundler = Bundler.new(@pwd)
|
@@ -147,13 +165,9 @@ module OpsWalrus
|
|
147
165
|
def run(package_operation_and_args)
|
148
166
|
return 0 if package_operation_and_args.empty?
|
149
167
|
|
150
|
-
ops_file_path, operation_kv_args,
|
151
|
-
ops_file = OpsFile.new(self, ops_file_path)
|
168
|
+
ops_file_path, operation_kv_args, tmp_bundle_root_dir = get_entry_point_ops_file_and_args(package_operation_and_args)
|
152
169
|
|
153
|
-
|
154
|
-
if ops_file.package_file && ops_file.package_file.dirname.to_s !~ /#{Bundler::BUNDLE_DIR}/
|
155
|
-
ops_file = set_pwd_to_ops_file_package_directory(ops_file)
|
156
|
-
end
|
170
|
+
ops_file = load_entry_point_ops_file(ops_file_path, tmp_bundle_root_dir)
|
157
171
|
|
158
172
|
if @verbose
|
159
173
|
puts "Running: #{ops_file.ops_file_path}"
|
@@ -177,19 +191,52 @@ module OpsWalrus
|
|
177
191
|
|
178
192
|
exit_status
|
179
193
|
ensure
|
180
|
-
FileUtils.remove_entry(
|
194
|
+
FileUtils.remove_entry(tmp_bundle_root_dir) if tmp_bundle_root_dir
|
195
|
+
end
|
196
|
+
|
197
|
+
def load_entry_point_ops_file(ops_file_path, tmp_bundle_root_dir)
|
198
|
+
ops_file = OpsFile.new(self, ops_file_path)
|
199
|
+
|
200
|
+
# we are running the ops file from within a temporary bundle root directory created by unzipping a zip bundle workspace
|
201
|
+
if tmp_bundle_root_dir
|
202
|
+
return set_pwd_and_rebase_ops_file(tmp_bundle_root_dir, ops_file)
|
203
|
+
end
|
204
|
+
|
205
|
+
# if the ops file is contained within a bundle directory, then that means we're probably running this command invocation
|
206
|
+
# on a remote host, e.g. /home/linuxbrew/.linuxbrew/bin/gem exec -g opswalrus ops run --script /tmp/d20230822-18829-2j5ij2 opswalrus_bundle docker install install
|
207
|
+
# and the corresponding entry point, e.g. /tmp/d20230822-18829-2j5ij2/opswalrus_bundle/docker/install/install.ops
|
208
|
+
# is actually being run from a temporary zip bundle root directory
|
209
|
+
# so we want to set the app's pwd to be the parent directory of the Bundler::BUNDLE_DIR directory, e.g. /tmp/d20230822-18829-2j5ij2
|
210
|
+
if ops_file.ops_file_path.to_s =~ /#{Bundler::BUNDLE_DIR}/
|
211
|
+
return set_pwd_to_parent_of_bundle_dir(ops_file)
|
212
|
+
end
|
213
|
+
|
214
|
+
# if the ops file is part of a package, then set the package directory as the app's pwd
|
215
|
+
if ops_file.package_file && ops_file.package_file.dirname.to_s !~ /#{Bundler::BUNDLE_DIR}/
|
216
|
+
return set_pwd_to_ops_file_package_directory(ops_file)
|
217
|
+
end
|
218
|
+
|
219
|
+
ops_file
|
220
|
+
end
|
221
|
+
|
222
|
+
def set_pwd_to_parent_of_bundle_dir(ops_file)
|
223
|
+
match = /^(.*)#{Bundler::BUNDLE_DIR}.*$/.match(ops_file.ops_file_path.to_s)
|
224
|
+
parent_directory_path = match.captures.first.to_pathname.cleanpath
|
225
|
+
set_pwd_and_rebase_ops_file(parent_directory_path, ops_file)
|
181
226
|
end
|
182
227
|
|
183
228
|
# sets the App's pwd to the ops file's package directory and
|
184
|
-
# returns a new OpsFile that points at the revised pathname
|
229
|
+
# returns a new OpsFile that points at the revised pathname when considered as relative to the package file's directory
|
185
230
|
def set_pwd_to_ops_file_package_directory(ops_file)
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
231
|
+
set_pwd_and_rebase_ops_file(ops_file.package_file.dirname, ops_file)
|
232
|
+
end
|
233
|
+
|
234
|
+
# returns a new OpsFile that points at the revised pathname when considered as relative to the new working directory
|
235
|
+
def set_pwd_and_rebase_ops_file(new_working_directory, ops_file)
|
236
|
+
set_pwd(new_working_directory)
|
237
|
+
rebased_ops_file_relative_path = ops_file.ops_file_path.relative_path_from(new_working_directory)
|
238
|
+
# note: rebased_ops_file_relative_path is a relative path that is relative to new_working_directory
|
239
|
+
absolute_ops_file_path = new_working_directory.join(rebased_ops_file_relative_path)
|
193
240
|
OpsFile.new(self, absolute_ops_file_path)
|
194
241
|
end
|
195
242
|
|
@@ -203,18 +250,18 @@ module OpsWalrus
|
|
203
250
|
# - ["../../my-package/operation1", "arg1:val1", "arg2:val2", "arg3:val3"]
|
204
251
|
# - ["../../my-package/operation1"]
|
205
252
|
#
|
206
|
-
# returns 3-tuple of the form: [ ops_file_path, operation_kv_args,
|
207
|
-
# such that the third item -
|
253
|
+
# returns 3-tuple of the form: [ ops_file_path, operation_kv_args, optional_tmp_bundle_root_dir ]
|
254
|
+
# such that the third item - optional_tmp_bundle_root_dir - if present, should be deleted after the script has completed running
|
208
255
|
def get_entry_point_ops_file_and_args(package_operation_and_args)
|
209
256
|
package_operation_and_args = package_operation_and_args.dup
|
210
257
|
package_or_ops_file_reference = package_operation_and_args.slice!(0, 1).first
|
211
|
-
|
258
|
+
tmp_bundle_root_dir = nil
|
212
259
|
|
213
260
|
case
|
214
261
|
when Dir.exist?(package_or_ops_file_reference)
|
215
262
|
dir = package_or_ops_file_reference
|
216
263
|
ops_file_path, operation_kv_args = find_entry_point_ops_file_in_dir(dir, package_operation_and_args)
|
217
|
-
[ops_file_path, operation_kv_args,
|
264
|
+
[ops_file_path, operation_kv_args, tmp_bundle_root_dir]
|
218
265
|
when File.exist?(package_or_ops_file_reference)
|
219
266
|
first_filepath = package_or_ops_file_reference.to_pathname.realpath
|
220
267
|
|
@@ -222,18 +269,18 @@ module OpsWalrus
|
|
222
269
|
when ".ops"
|
223
270
|
[first_filepath, package_operation_and_args]
|
224
271
|
when ".zip"
|
225
|
-
|
272
|
+
tmp_bundle_root_dir = Dir.mktmpdir.to_pathname # this is the temporary bundle root dir
|
226
273
|
|
227
274
|
# unzip the bundle into the temp directory
|
228
|
-
DirZipper.unzip(first_filepath,
|
275
|
+
DirZipper.unzip(first_filepath, tmp_bundle_root_dir)
|
229
276
|
|
230
|
-
find_entry_point_ops_file_in_dir(
|
277
|
+
find_entry_point_ops_file_in_dir(tmp_bundle_root_dir, package_operation_and_args)
|
231
278
|
else
|
232
279
|
raise Error, "Unknown file type for entrypoint: #{first_filepath}"
|
233
280
|
end
|
234
281
|
|
235
282
|
# operation_kv_args = package_operation_and_args
|
236
|
-
[ops_file_path, operation_kv_args,
|
283
|
+
[ops_file_path, operation_kv_args, tmp_bundle_root_dir]
|
237
284
|
when repo_url = Git.repo?(package_or_ops_file_reference)
|
238
285
|
destination_package_path = bundler.download_git_package(repo_url)
|
239
286
|
|
@@ -241,7 +288,7 @@ module OpsWalrus
|
|
241
288
|
|
242
289
|
# for an original package_operation_and_args of ["github.com/davidkellis/my-package", "operation1", "arg1:val1", "arg2:val2", "arg3:val3"]
|
243
290
|
# we return: [ "#{pwd}/#{Bundler::BUNDLE_DIR}/github-com-davidkellis-my-package/operation1.ops", ["arg1:val1", "arg2:val2", "arg3:val3"] ]
|
244
|
-
[ops_file_path, operation_kv_args,
|
291
|
+
[ops_file_path, operation_kv_args, tmp_bundle_root_dir]
|
245
292
|
else
|
246
293
|
raise Error, "Unknown operation reference: #{package_or_ops_file_reference.inspect}"
|
247
294
|
end
|
data/lib/opswalrus/host.rb
CHANGED
@@ -173,6 +173,10 @@ module OpsWalrus
|
|
173
173
|
|
174
174
|
# the subclasses of this class will define methods that handle method dispatch via HostProxyOpsFileInvocationBuilder objects
|
175
175
|
|
176
|
+
def to_s
|
177
|
+
@_host.to_s
|
178
|
+
end
|
179
|
+
|
176
180
|
def method_missing(name, ...)
|
177
181
|
@_host.send(name, ...)
|
178
182
|
end
|
data/lib/opswalrus/invocation.rb
CHANGED
@@ -1,15 +1,3 @@
|
|
1
|
-
# require 'json'
|
2
|
-
# require 'set'
|
3
|
-
# require 'shellwords'
|
4
|
-
# require 'socket'
|
5
|
-
# require 'stringio'
|
6
|
-
|
7
|
-
# require 'sshkit'
|
8
|
-
# require 'sshkit/dsl'
|
9
|
-
|
10
|
-
# require_relative 'host'
|
11
|
-
# require_relative 'sshkit_ext'
|
12
|
-
# require_relative 'walrus_lang'
|
13
1
|
|
14
2
|
module OpsWalrus
|
15
3
|
|
@@ -127,7 +115,7 @@ module OpsWalrus
|
|
127
115
|
resolved_symbol = @namespace_or_ops_file.resolve_symbol(method_name)
|
128
116
|
if resolved_symbol.is_a? OpsFile
|
129
117
|
params_hash = resolved_symbol.build_params_hash(*args, **kwargs)
|
130
|
-
resolved_symbol.invoke(runtime_env, params_hash)
|
118
|
+
resolved_symbol.invoke(@runtime_env, params_hash)
|
131
119
|
else
|
132
120
|
self
|
133
121
|
end
|
@@ -142,434 +130,5 @@ module OpsWalrus
|
|
142
130
|
_resolve_method_and_invoke(name, *args, **kwargs)
|
143
131
|
end
|
144
132
|
end
|
145
|
-
# class ArrayOrHashNavigationProxy
|
146
|
-
# def initialize(array_or_hash)
|
147
|
-
# @obj = array_or_hash
|
148
|
-
# end
|
149
|
-
# def [](index, *args, **kwargs, &block)
|
150
|
-
# @obj.method(:[]).call(index, *args, **kwargs, &block)
|
151
|
-
# end
|
152
|
-
# def respond_to_missing?(method, *)
|
153
|
-
# @obj.is_a?(Hash) && @obj.respond_to?(method)
|
154
|
-
# end
|
155
|
-
# def method_missing(name, *args, **kwargs, &block)
|
156
|
-
# case @obj
|
157
|
-
# when Array
|
158
|
-
# @obj.method(name).call(*args, **kwargs, &block)
|
159
|
-
# when Hash
|
160
|
-
# if @obj.respond_to?(name)
|
161
|
-
# @obj.method(name).call(*args, **kwargs, &block)
|
162
|
-
# else
|
163
|
-
# value = self[name.to_s]
|
164
|
-
# case value
|
165
|
-
# when Array, Hash
|
166
|
-
# ArrayOrHashNavigationProxy.new(value)
|
167
|
-
# else
|
168
|
-
# value
|
169
|
-
# end
|
170
|
-
# end
|
171
|
-
# end
|
172
|
-
# end
|
173
|
-
# end
|
174
|
-
|
175
|
-
# class InvocationParams
|
176
|
-
# # params : Hash
|
177
|
-
# def initialize(params)
|
178
|
-
# @params = params
|
179
|
-
# end
|
180
|
-
|
181
|
-
# def [](key)
|
182
|
-
# key = key.to_s if key.is_a? Symbol
|
183
|
-
# @params[key]
|
184
|
-
# end
|
185
|
-
|
186
|
-
# def dig(*keys)
|
187
|
-
# # keys = keys.map {|key| key.is_a?(Integer) ? key : key.to_s }
|
188
|
-
# @params.dig(*keys)
|
189
|
-
# end
|
190
|
-
|
191
|
-
# def method_missing(name, *args, **kwargs, &block)
|
192
|
-
# if @params.respond_to?(name)
|
193
|
-
# @params.method(name).call(*args, **kwargs, &block)
|
194
|
-
# else
|
195
|
-
# value = self[name]
|
196
|
-
# case value
|
197
|
-
# when Array, Hash
|
198
|
-
# ArrayOrHashNavigationProxy.new(value)
|
199
|
-
# else
|
200
|
-
# value
|
201
|
-
# end
|
202
|
-
# end
|
203
|
-
# end
|
204
|
-
# end
|
205
|
-
|
206
|
-
|
207
|
-
# # BootstrapLinuxHostShellScript = <<~SCRIPT
|
208
|
-
# # #!/usr/bin/env bash
|
209
|
-
# # ...
|
210
|
-
# # SCRIPT
|
211
|
-
|
212
|
-
# module InvocationDSL
|
213
|
-
# def ssh(*args, **kwargs, &block)
|
214
|
-
# host_proxy_class = @ops_file_script.host_proxy_class
|
215
|
-
# hosts = inventory(*args, **kwargs).map {|host| host_proxy_class.new(host) }
|
216
|
-
# sshkit_hosts = hosts.map(&:sshkit_host)
|
217
|
-
# sshkit_host_to_ops_host_map = sshkit_hosts.zip(hosts).to_h
|
218
|
-
# runtime_env = @runtime_env
|
219
|
-
# local_host = self
|
220
|
-
# # bootstrap_shell_script = BootstrapLinuxHostShellScript
|
221
|
-
# # on sshkit_hosts do |sshkit_host|
|
222
|
-
# SSHKit::Coordinator.new(sshkit_hosts).each(in: kwargs[:in] || :parallel) do |sshkit_host|
|
223
|
-
|
224
|
-
# # in this context, self is an instance of one of the subclasses of SSHKit::Backend::Abstract, e.g. SSHKit::Backend::Netssh
|
225
|
-
|
226
|
-
# host = sshkit_host_to_ops_host_map[sshkit_host]
|
227
|
-
# # puts "#{host.alias} / #{host}:"
|
228
|
-
|
229
|
-
# begin
|
230
|
-
# host.set_runtime_env(runtime_env)
|
231
|
-
# host.set_ssh_session_connection(self) # self is an instance of one of the subclasses of SSHKit::Backend::Abstract, e.g. SSHKit::Backend::Netssh
|
232
|
-
|
233
|
-
# # copy over bootstrap shell script
|
234
|
-
# # io = StringIO.new(bootstrap_shell_script)
|
235
|
-
# io = File.open(__FILE__.to_pathname.dirname.join("bootstrap.sh"))
|
236
|
-
# upload_success = host.upload(io, "tmpopsbootstrap.sh")
|
237
|
-
# io.close
|
238
|
-
# raise Error, "Unable to upload bootstrap shell script to remote host" unless upload_success
|
239
|
-
# host.execute(:chmod, "755", "tmpopsbootstrap.sh")
|
240
|
-
# host.execute(:sh, "tmpopsbootstrap.sh")
|
241
|
-
|
242
|
-
# # copy over ops bundle zip file
|
243
|
-
# zip_bundle_path = runtime_env.zip_bundle_path
|
244
|
-
# upload_success = host.upload(zip_bundle_path, "tmpops.zip")
|
245
|
-
# raise Error, "Unable to upload ops bundle to remote host" unless upload_success
|
246
|
-
|
247
|
-
# stdout, stderr, exit_status = host.run_ops(:bundle, "unzip tmpops.zip", in_bundle_root_dir: false)
|
248
|
-
# raise Error, "Unable to unzip ops bundle on remote host" unless exit_status == 0
|
249
|
-
# tmp_bundle_root_dir = stdout.strip
|
250
|
-
# host.set_ssh_session_tmp_bundle_root_dir(tmp_bundle_root_dir)
|
251
|
-
|
252
|
-
# # we run the block in the context of the host, s.t. `self` within the block evaluates to `host`
|
253
|
-
# retval = host.instance_exec(local_host, &block) # host is passed as the argument to the block
|
254
|
-
|
255
|
-
# # puts retval.inspect
|
256
|
-
|
257
|
-
# # cleanup
|
258
|
-
# if tmp_bundle_root_dir =~ /tmp/ # sanity check the temp path before we blow away something we don't intend
|
259
|
-
# host.execute(:rm, "-rf", "tmpopsbootstrap.sh", "tmpops.zip", tmp_bundle_root_dir)
|
260
|
-
# else
|
261
|
-
# host.execute(:rm, "-rf", "tmpopsbootstrap.sh", "tmpops.zip")
|
262
|
-
# end
|
263
|
-
|
264
|
-
# retval
|
265
|
-
# rescue SSHKit::Command::Failed => e
|
266
|
-
# puts "[!] Command failed:"
|
267
|
-
# puts e.message
|
268
|
-
# rescue Net::SSH::ConnectionTimeout
|
269
|
-
# puts "[!] The host '#{host}' not alive!"
|
270
|
-
# rescue Net::SSH::Timeout
|
271
|
-
# puts "[!] The host '#{host}' disconnected/timeouted unexpectedly!"
|
272
|
-
# rescue Errno::ECONNREFUSED
|
273
|
-
# puts "[!] Incorrect port #{port} for #{host}"
|
274
|
-
# rescue Net::SSH::HostKeyMismatch => e
|
275
|
-
# puts "[!] The host fingerprint does not match the last observed fingerprint for #{host}"
|
276
|
-
# puts e.message
|
277
|
-
# puts "You might try `ssh-keygen -f ~/.ssh/known_hosts -R \"#{host}\"`"
|
278
|
-
# rescue Net::SSH::AuthenticationFailed
|
279
|
-
# puts "Wrong Password: #{host} | #{user}:#{password}"
|
280
|
-
# rescue Net::SSH::Authentication::DisallowedMethod
|
281
|
-
# puts "[!] The host '#{host}' doesn't accept password authentication method."
|
282
|
-
# rescue Errno::EHOSTUNREACH => e
|
283
|
-
# puts "[!] The host '#{host}' is unreachable"
|
284
|
-
# rescue => e
|
285
|
-
# puts e.class
|
286
|
-
# puts e.message
|
287
|
-
# # puts e.backtrace.join("\n")
|
288
|
-
# ensure
|
289
|
-
# host.clear_ssh_session
|
290
|
-
# end
|
291
|
-
# end
|
292
|
-
# end
|
293
|
-
|
294
|
-
# def inventory(*args, **kwargs)
|
295
|
-
# tags = args.map(&:to_s)
|
296
|
-
|
297
|
-
# kwargs = kwargs.transform_keys(&:to_s)
|
298
|
-
# tags.concat(kwargs["tags"]) if kwargs["tags"]
|
299
|
-
|
300
|
-
# @runtime_env.app.inventory(tags)
|
301
|
-
# end
|
302
|
-
|
303
|
-
# def exit(exit_status, message = nil)
|
304
|
-
# if message
|
305
|
-
# puts message
|
306
|
-
# end
|
307
|
-
# result = if exit_status == 0
|
308
|
-
# Invocation::Success.new(nil)
|
309
|
-
# else
|
310
|
-
# Invocation::Error.new(nil, exit_status)
|
311
|
-
# end
|
312
|
-
# throw :exit_now, result
|
313
|
-
# end
|
314
|
-
|
315
|
-
# def env(*keys)
|
316
|
-
# keys = keys.map(&:to_s)
|
317
|
-
# if keys.empty?
|
318
|
-
# @env
|
319
|
-
# else
|
320
|
-
# @env.dig(*keys)
|
321
|
-
# end
|
322
|
-
# end
|
323
|
-
|
324
|
-
# # currently, import may only be used to import a package that is referenced in the script's package file
|
325
|
-
# # I may decide to extend this to work with dynamic package references
|
326
|
-
# #
|
327
|
-
# # local_package_name is the local package name defined for the package dependency that is attempting to be referenced
|
328
|
-
# def import(local_package_name)
|
329
|
-
# local_package_name = local_package_name.to_s
|
330
|
-
# package_reference = @ops_file_script.ops_file.package_file&.dependency(local_package_name)
|
331
|
-
# raise Error, "Unknown package reference: #{local_package_name}" unless package_reference
|
332
|
-
# import_reference = PackageDependencyReference.new(local_package_name, package_reference)
|
333
|
-
# # puts "import: #{import_reference.inspect}"
|
334
|
-
# @runtime_env.resolve_import_reference(@ops_file_script.ops_file, import_reference)
|
335
|
-
# end
|
336
|
-
|
337
|
-
# def params(*keys, default: nil)
|
338
|
-
# keys = keys.map(&:to_s)
|
339
|
-
# if keys.empty?
|
340
|
-
# @params
|
341
|
-
# else
|
342
|
-
# @params.dig(*keys) || default
|
343
|
-
# end
|
344
|
-
# end
|
345
|
-
|
346
|
-
# # returns the stdout from the command
|
347
|
-
# def sh(desc_or_cmd = nil, cmd = nil, input: nil, &block)
|
348
|
-
# out, err, status = *shell!(desc_or_cmd, cmd, block, input: input)
|
349
|
-
# out
|
350
|
-
# end
|
351
|
-
|
352
|
-
# # returns the tuple: [stdout, stderr, exit_status]
|
353
|
-
# def shell(desc_or_cmd = nil, cmd = nil, input: nil, &block)
|
354
|
-
# shell!(desc_or_cmd, cmd, block, input: input)
|
355
|
-
# end
|
356
|
-
|
357
|
-
# # returns the tuple: [stdout, stderr, exit_status]
|
358
|
-
# def shell!(desc_or_cmd = nil, cmd = nil, block = nil, input: nil)
|
359
|
-
# # description = nil
|
360
|
-
|
361
|
-
# 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
|
362
|
-
|
363
|
-
# description = desc_or_cmd if cmd || block
|
364
|
-
# cmd = block.call if block
|
365
|
-
# cmd ||= desc_or_cmd
|
366
|
-
|
367
|
-
# cmd = WalrusLang.render(cmd, block.binding) if block && cmd =~ /{{.*}}/
|
368
|
-
|
369
|
-
# #cmd = Shellwords.escape(cmd)
|
370
|
-
|
371
|
-
# # puts "shell! self: #{self.inspect}"
|
372
|
-
|
373
|
-
# if App.instance.report_mode?
|
374
|
-
# print "[#{@runtime_env.local_hostname}] "
|
375
|
-
# print "#{description}: " if description
|
376
|
-
# puts cmd
|
377
|
-
# end
|
378
|
-
|
379
|
-
# return unless cmd && !cmd.strip.empty?
|
380
|
-
|
381
|
-
# sshkit_cmd = @runtime_env.handle_input(input) do |interaction_handler|
|
382
|
-
# # self is a Module instance that is serving as the evaluation context in an instance of a subclass of an Invocation; see Invocation#evaluate
|
383
|
-
# backend.execute_cmd(cmd, interaction_handler: interaction_handler, verbosity: :info)
|
384
|
-
# end
|
385
|
-
|
386
|
-
# [sshkit_cmd.full_stdout, sshkit_cmd.full_stderr, sshkit_cmd.exit_status]
|
387
|
-
# end
|
388
|
-
|
389
|
-
# # def init_brew
|
390
|
-
# # execute('eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"')
|
391
|
-
# # end
|
392
|
-
|
393
|
-
# end
|
394
|
-
|
395
|
-
# # An Invocation object represents a stack frame, and the params_hash represents the
|
396
|
-
# # arguments that the caller has supplied for that stack frame to reference
|
397
|
-
# class Invocation
|
398
|
-
# class Result
|
399
|
-
# attr_accessor :value
|
400
|
-
# attr_accessor :exit_status
|
401
|
-
# def initialize(value, exit_status = 0)
|
402
|
-
# @value = value
|
403
|
-
# @exit_status = exit_status
|
404
|
-
# end
|
405
|
-
# def success?
|
406
|
-
# !failure?
|
407
|
-
# end
|
408
|
-
# def failure?
|
409
|
-
# !success?
|
410
|
-
# end
|
411
|
-
# end
|
412
|
-
# class Success < Result
|
413
|
-
# def initialize(value)
|
414
|
-
# super(value, 0)
|
415
|
-
# end
|
416
|
-
# def success?
|
417
|
-
# true
|
418
|
-
# end
|
419
|
-
# end
|
420
|
-
# class Error < Result
|
421
|
-
# def initialize(value, exit_status = 1)
|
422
|
-
# super(value, exit_status == 0 ? 1 : exit_status)
|
423
|
-
# end
|
424
|
-
# def failure?
|
425
|
-
# true
|
426
|
-
# end
|
427
|
-
# end
|
428
|
-
|
429
|
-
|
430
|
-
# def self.define_invocation_class(ops_file)
|
431
|
-
# klass = Class.new(Invocation)
|
432
|
-
|
433
|
-
# methods_defined = Set.new
|
434
|
-
|
435
|
-
# # define methods for every import in the script
|
436
|
-
# ops_file.local_symbol_table.each do |symbol_name, import_reference|
|
437
|
-
# unless methods_defined.include? symbol_name
|
438
|
-
# klass.define_method(symbol_name) do |*args, **kwargs, &block|
|
439
|
-
# # puts "0" * 80
|
440
|
-
# # puts "@runtime_env.resolve_import_reference(@ops_file_script.ops_file, #{import_reference.inspect})"
|
441
|
-
# # puts @ops_file_script.ops_file.ops_file_path
|
442
|
-
# # puts symbol_name
|
443
|
-
# namespace_or_ops_file = @runtime_env.resolve_import_reference(@ops_file_script.ops_file, import_reference)
|
444
|
-
# # puts namespace_or_ops_file.inspect
|
445
|
-
# # puts "0" * 80
|
446
|
-
# case namespace_or_ops_file
|
447
|
-
# when Namespace
|
448
|
-
# namespace_or_ops_file
|
449
|
-
# when OpsFile
|
450
|
-
# params_hash = namespace_or_ops_file.build_params_hash(*args, **kwargs)
|
451
|
-
# namespace_or_ops_file.invoke(@runtime_env, params_hash)
|
452
|
-
# end
|
453
|
-
# end
|
454
|
-
# methods_defined << symbol_name
|
455
|
-
# end
|
456
|
-
# end
|
457
|
-
|
458
|
-
# # define methods for every Namespace or OpsFile within the namespace that the OpsFile resides within
|
459
|
-
# sibling_symbol_table = Set.new
|
460
|
-
# sibling_symbol_table |= ops_file.dirname.glob("*.ops").map {|ops_file_path| ops_file_path.basename(".ops").to_s } # OpsFiles
|
461
|
-
# sibling_symbol_table |= ops_file.dirname.glob("*").select(&:directory?).map {|dir_path| dir_path.basename.to_s } # Namespaces
|
462
|
-
# sibling_symbol_table.each do |symbol_name|
|
463
|
-
# unless methods_defined.include? symbol_name
|
464
|
-
# klass.define_method(symbol_name) do |*args, **kwargs, &block|
|
465
|
-
# # puts "0" * 80
|
466
|
-
# # puts "@runtime_env.resolve_symbol(@ops_file_script.ops_file, #{symbol_name})"
|
467
|
-
# # puts @ops_file_script.ops_file.ops_file_path
|
468
|
-
# # puts symbol_name
|
469
|
-
# namespace_or_ops_file = @runtime_env.resolve_sibling_symbol(@ops_file_script.ops_file, symbol_name)
|
470
|
-
# # puts namespace_or_ops_file.inspect
|
471
|
-
# # puts "0" * 80
|
472
|
-
# case namespace_or_ops_file
|
473
|
-
# when Namespace
|
474
|
-
# namespace_or_ops_file
|
475
|
-
# when OpsFile
|
476
|
-
# params_hash = namespace_or_ops_file.build_params_hash(*args, **kwargs)
|
477
|
-
# namespace_or_ops_file.invoke(@runtime_env, params_hash)
|
478
|
-
# end
|
479
|
-
# end
|
480
|
-
# methods_defined << symbol_name
|
481
|
-
# end
|
482
|
-
# end
|
483
|
-
|
484
|
-
# klass
|
485
|
-
# end
|
486
|
-
|
487
|
-
# include InvocationDSL
|
488
|
-
|
489
|
-
# def initialize(ops_file_script, runtime_env, params_hash)
|
490
|
-
# @ops_file_script = ops_file_script
|
491
|
-
# @runtime_env = runtime_env
|
492
|
-
# @params = InvocationParams.new(params_hash)
|
493
|
-
# end
|
494
|
-
|
495
|
-
# def backend
|
496
|
-
# @runtime_env.pty
|
497
|
-
# end
|
498
|
-
|
499
|
-
# def debug?
|
500
|
-
# @runtime_env.debug?
|
501
|
-
# end
|
502
|
-
|
503
|
-
# def verbose?
|
504
|
-
# @runtime_env.verbose?
|
505
|
-
# end
|
506
|
-
|
507
|
-
# # def evaluate
|
508
|
-
# # # the evaluation context needs to be a module with all of the following:
|
509
|
-
# # # - InvocationDSL methods
|
510
|
-
# # # - @ops_file_script
|
511
|
-
# # # - @runtime_env
|
512
|
-
# # # - @params
|
513
|
-
# # # - #backend
|
514
|
-
# # # - #debug?
|
515
|
-
# # # - #verbose?
|
516
|
-
# # # - all the dynamically defined methods in the subclass of Invocation
|
517
|
-
# # end
|
518
|
-
|
519
|
-
# def evaluate
|
520
|
-
# eval(@ops_file_script.script, nil, @ops_file_script.ops_file.ops_file_path.to_s, @ops_file_script.ops_file.script_line_offset)
|
521
|
-
# end
|
522
|
-
|
523
|
-
# # def evaluate
|
524
|
-
# # ops_file_script = @ops_file_script
|
525
|
-
# # runtime_env = @runtime_env
|
526
|
-
# # invocation_params = @params
|
527
|
-
|
528
|
-
# # # we use a Module as the evaluation context instead of this #evaluate method so that classes and other structures may be defined in an .ops file
|
529
|
-
# # evaluation_context = Module.new
|
530
|
-
# # evaluation_context.module_exec {
|
531
|
-
# # include InvocationDSL
|
532
|
-
|
533
|
-
# # @ops_file_script = ops_file_script
|
534
|
-
# # @runtime_env = runtime_env
|
535
|
-
# # @params = invocation_params
|
536
|
-
|
537
|
-
# # def backend
|
538
|
-
# # @runtime_env.pty
|
539
|
-
# # end
|
540
|
-
|
541
|
-
# # def debug?
|
542
|
-
# # @runtime_env.debug?
|
543
|
-
# # end
|
544
|
-
|
545
|
-
# # def verbose?
|
546
|
-
# # @runtime_env.verbose?
|
547
|
-
# # end
|
548
|
-
# # }
|
549
|
-
|
550
|
-
# # evaluation_context.module_eval(@ops_file_script.script, @ops_file_script.ops_file.ops_file_path.to_s, @ops_file_script.ops_file.script_line_offset)
|
551
|
-
# # end
|
552
|
-
|
553
|
-
# # def method_missing(name, *args, **kwargs, &block)
|
554
|
-
# # puts "1" * 80
|
555
|
-
# # import_reference = @ops_file_script.ops_file.resolve_import(name)
|
556
|
-
# # if import_reference
|
557
|
-
# # puts "2" * 80
|
558
|
-
# # resolved_value = @runtime_env.resolve_import_reference(@ops_file_script.ops_file, import_reference)
|
559
|
-
# # return resolved_value if resolved_value
|
560
|
-
# # end
|
561
133
|
|
562
|
-
# # puts "3" * 80
|
563
|
-
# # case namespace_or_ops_file = @runtime_env.resolve_symbol(@ops_file_script.ops_file, name.to_s)
|
564
|
-
# # when Namespace
|
565
|
-
# # puts "4" * 80
|
566
|
-
# # namespace_or_ops_file
|
567
|
-
# # when OpsFile
|
568
|
-
# # puts "5" * 80
|
569
|
-
# # namespace_or_ops_file.invoke(@runtime_env, *args, **kwargs, &block)
|
570
|
-
# # else
|
571
|
-
# # raise NoMethodError, "No method named '#{name}'"
|
572
|
-
# # end
|
573
|
-
# # end
|
574
|
-
# end
|
575
134
|
end
|
@@ -58,25 +58,25 @@ module OpsWalrus
|
|
58
58
|
Invocation::Success.new(ruby_script_return)
|
59
59
|
end
|
60
60
|
rescue SSHKit::Command::Failed => e
|
61
|
-
|
61
|
+
App.instance.error "[!] Command failed: #{e.message}"
|
62
62
|
rescue Error => e
|
63
|
-
|
64
|
-
|
65
|
-
|
63
|
+
App.instance.error "Error: Ops script crashed."
|
64
|
+
App.instance.error e.message
|
65
|
+
App.instance.error e.backtrace.take(5).join("\n")
|
66
66
|
Invocation::Error.new(e)
|
67
67
|
rescue => e
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
68
|
+
App.instance.error "Unhandled Error: Ops script crashed."
|
69
|
+
App.instance.error e.class
|
70
|
+
App.instance.error e.message
|
71
|
+
App.instance.error e.backtrace.take(10).join("\n")
|
72
72
|
Invocation::Error.new(e)
|
73
73
|
end
|
74
74
|
|
75
75
|
if app.debug? && result.failure?
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
76
|
+
App.instance.debug "Ops script error details:"
|
77
|
+
App.instance.debug "Error: #{result.value}"
|
78
|
+
App.instance.debug "Status code: #{result.exit_status}"
|
79
|
+
App.instance.debug @entry_point_ops_file.script
|
80
80
|
end
|
81
81
|
|
82
82
|
result
|
@@ -14,22 +14,15 @@ module OpsWalrus
|
|
14
14
|
# define methods for the OpsFile's local_symbol_table: local imports and private lib directory
|
15
15
|
ops_file.local_symbol_table.each do |symbol_name, import_reference|
|
16
16
|
unless methods_defined.include? symbol_name
|
17
|
-
|
17
|
+
App.instance.debug "defining method for local symbol table entry: #{symbol_name}"
|
18
18
|
klass.define_method(symbol_name) do |*args, **kwargs, &block|
|
19
|
-
|
19
|
+
App.instance.debug "resolving local symbol table entry: #{symbol_name}"
|
20
20
|
namespace_or_ops_file = @runtime_env.resolve_import_reference(ops_file, import_reference)
|
21
|
-
|
21
|
+
App.instance.debug "namespace_or_ops_file=#{namespace_or_ops_file.to_s}"
|
22
22
|
|
23
23
|
invocation_context = LocalImportInvocationContext.new(@runtime_env, namespace_or_ops_file)
|
24
24
|
invocation_context._invoke(*args, **kwargs)
|
25
25
|
|
26
|
-
# case namespace_or_ops_file
|
27
|
-
# when Namespace
|
28
|
-
# namespace_or_ops_file._invoke_if_namespace_has_ops_file_of_same_name(*args, **kwargs)
|
29
|
-
# when OpsFile
|
30
|
-
# params_hash = namespace_or_ops_file.build_params_hash(*args, **kwargs)
|
31
|
-
# namespace_or_ops_file.invoke(@runtime_env, params_hash)
|
32
|
-
# end
|
33
26
|
end
|
34
27
|
methods_defined << symbol_name
|
35
28
|
end
|
@@ -40,26 +33,18 @@ module OpsWalrus
|
|
40
33
|
sibling_symbol_table_names |= ops_file.dirname.glob("*.ops").map {|ops_file_path| ops_file_path.basename(".ops").to_s } # OpsFiles
|
41
34
|
sibling_symbol_table_names |= ops_file.dirname.glob("*").select(&:directory?).map {|dir_path| dir_path.basename.to_s } # Namespaces
|
42
35
|
# puts "sibling_symbol_table_names=#{sibling_symbol_table_names}"
|
43
|
-
|
36
|
+
App.instance.debug "methods_defined=#{methods_defined}"
|
44
37
|
sibling_symbol_table_names.each do |symbol_name|
|
45
38
|
unless methods_defined.include? symbol_name
|
46
|
-
|
39
|
+
App.instance.debug "defining method for implicit imports: #{symbol_name}"
|
47
40
|
klass.define_method(symbol_name) do |*args, **kwargs, &block|
|
48
|
-
|
41
|
+
App.instance.debug "resolving implicit import: #{symbol_name}"
|
49
42
|
namespace_or_ops_file = @runtime_env.resolve_sibling_symbol(ops_file, symbol_name)
|
50
|
-
|
43
|
+
App.instance.debug "namespace_or_ops_file=#{namespace_or_ops_file.to_s}"
|
51
44
|
|
52
45
|
invocation_context = LocalImportInvocationContext.new(@runtime_env, namespace_or_ops_file)
|
53
46
|
invocation_context._invoke(*args, **kwargs)
|
54
47
|
|
55
|
-
|
56
|
-
# case namespace_or_ops_file
|
57
|
-
# when Namespace
|
58
|
-
# namespace_or_ops_file._invoke_if_namespace_has_ops_file_of_same_name(*args, **kwargs)
|
59
|
-
# when OpsFile
|
60
|
-
# params_hash = namespace_or_ops_file.build_params_hash(*args, **kwargs)
|
61
|
-
# namespace_or_ops_file.invoke(@runtime_env, params_hash)
|
62
|
-
# end
|
63
48
|
end
|
64
49
|
methods_defined << symbol_name
|
65
50
|
end
|
@@ -37,6 +37,17 @@ module OpsWalrus
|
|
37
37
|
local_name
|
38
38
|
end
|
39
39
|
|
40
|
+
def to_s
|
41
|
+
"PackageReference(local_name=#{@local_name}, package_uri=#{@package_uri}, version=#{@version})"
|
42
|
+
end
|
43
|
+
|
44
|
+
def summary
|
45
|
+
if version
|
46
|
+
"#{package_uri}:#{version}"
|
47
|
+
else
|
48
|
+
package_uri
|
49
|
+
end
|
50
|
+
end
|
40
51
|
end
|
41
52
|
|
42
53
|
# these are dynamic package references defined at runtime when an OpsFile's imports are being evaluated.
|
@@ -23,6 +23,12 @@ module OpsWalrus
|
|
23
23
|
super(local_name)
|
24
24
|
@package_reference = package_reference
|
25
25
|
end
|
26
|
+
def to_s
|
27
|
+
"PackageDependencyReference(local_name=#{@local_name}, package_reference=#{@package_reference})"
|
28
|
+
end
|
29
|
+
def summary
|
30
|
+
"#{local_name}: #{package_reference.summary}"
|
31
|
+
end
|
26
32
|
end
|
27
33
|
|
28
34
|
class DirectoryReference < ImportReference
|
@@ -31,6 +37,12 @@ module OpsWalrus
|
|
31
37
|
super(local_name)
|
32
38
|
@dirname = dirname
|
33
39
|
end
|
40
|
+
def to_s
|
41
|
+
"DirectoryReference(local_name=#{@local_name}, dirname=#{@dirname})"
|
42
|
+
end
|
43
|
+
def summary
|
44
|
+
"#{local_name}: #{dirname}"
|
45
|
+
end
|
34
46
|
end
|
35
47
|
|
36
48
|
class DynamicPackageImportReference < ImportReference
|
@@ -39,6 +51,12 @@ module OpsWalrus
|
|
39
51
|
super(local_name)
|
40
52
|
@package_reference = package_reference
|
41
53
|
end
|
54
|
+
def to_s
|
55
|
+
"DynamicPackageImportReference(local_name=#{@local_name}, package_reference=#{@package_reference})"
|
56
|
+
end
|
57
|
+
def summary
|
58
|
+
"#{local_name}: #{package_reference.summary}"
|
59
|
+
end
|
42
60
|
end
|
43
61
|
|
44
62
|
class OpsFileReference < ImportReference
|
@@ -47,6 +65,12 @@ module OpsWalrus
|
|
47
65
|
super(local_name)
|
48
66
|
@ops_file_path = ops_file_path
|
49
67
|
end
|
68
|
+
def to_s
|
69
|
+
"DirectoryReference(local_name=#{@local_name}, ops_file_path=#{@ops_file_path})"
|
70
|
+
end
|
71
|
+
def summary
|
72
|
+
"#{local_name}: #{ops_file_path}"
|
73
|
+
end
|
50
74
|
end
|
51
75
|
|
52
76
|
# Namespace is really just a Map of symbol_name -> (Namespace | OpsFile) pairs
|
@@ -178,6 +202,10 @@ module OpsWalrus
|
|
178
202
|
path_map
|
179
203
|
end
|
180
204
|
|
205
|
+
def includes_path?(path)
|
206
|
+
!!@path_map[path]
|
207
|
+
end
|
208
|
+
|
181
209
|
def dynamically_add_new_package_dir(new_package_dir)
|
182
210
|
# patch the symbol resolution (namespace) tree
|
183
211
|
dir_basename = new_package_dir.basename
|
@@ -198,19 +226,22 @@ module OpsWalrus
|
|
198
226
|
|
199
227
|
# returns a Namespace or OpsFile
|
200
228
|
def resolve_symbol(origin_ops_file, symbol_name)
|
201
|
-
lookup_namespace(origin_ops_file)&.resolve_symbol(symbol_name)
|
229
|
+
resolved_namespace_or_ops_file = lookup_namespace(origin_ops_file)&.resolve_symbol(symbol_name)
|
230
|
+
App.instance.debug("LoadPath#resolve_symbol(#{origin_ops_file}, #{symbol_name}) -> #{resolved_namespace_or_ops_file}")
|
231
|
+
resolved_namespace_or_ops_file
|
202
232
|
end
|
203
233
|
|
204
234
|
# returns a Namespace | OpsFile
|
205
235
|
def resolve_import_reference(origin_ops_file, import_reference)
|
206
|
-
case import_reference
|
236
|
+
resolved_namespace_or_ops_file = case import_reference
|
207
237
|
when PackageDependencyReference
|
208
238
|
# puts "root namespace: #{@root_namespace.symbol_table}"
|
209
|
-
@root_namespace.resolve_symbol(import_reference.package_reference.import_resolution_dirname) # returns the Namespace associated with the bundled package
|
239
|
+
@root_namespace.resolve_symbol(import_reference.package_reference.import_resolution_dirname) # returns the Namespace associated with the bundled package import_resolution_dirname (i.e. the local name)
|
210
240
|
when DynamicPackageImportReference
|
211
241
|
dynamic_package_reference = import_reference.package_reference
|
212
242
|
@dynamic_package_additions_memo[dynamic_package_reference] ||= begin
|
213
243
|
# puts "Downloading dynamic package: #{dynamic_package_reference.inspect}"
|
244
|
+
App.instance.debug("Downloading dynamic package: #{dynamic_package_reference}")
|
214
245
|
dynamically_added_package_dir = @runtime_env.app.bundler.download_git_package(dynamic_package_reference.package_uri, dynamic_package_reference.version)
|
215
246
|
dynamically_add_new_package_dir(dynamically_added_package_dir)
|
216
247
|
dynamically_added_package_dir
|
@@ -221,6 +252,8 @@ module OpsWalrus
|
|
221
252
|
when OpsFileReference
|
222
253
|
@path_map[import_reference.ops_file_path]
|
223
254
|
end
|
255
|
+
App.instance.debug("LoadPath#resolve_import_reference(#{origin_ops_file}, #{import_reference}) -> #{resolved_namespace_or_ops_file}")
|
256
|
+
resolved_namespace_or_ops_file
|
224
257
|
end
|
225
258
|
end
|
226
259
|
|
@@ -315,23 +348,41 @@ module OpsWalrus
|
|
315
348
|
ops_file.invoke(self, params_hash)
|
316
349
|
end
|
317
350
|
|
351
|
+
def find_load_path_that_includes_path(path)
|
352
|
+
load_path = [@bundle_load_path, @app_load_path].find {|load_path| load_path.includes_path?(path) }
|
353
|
+
raise SymbolResolutionError, "No load path includes the path: #{path}" unless load_path
|
354
|
+
load_path
|
355
|
+
end
|
356
|
+
|
318
357
|
# returns a Namespace | OpsFile
|
319
358
|
def resolve_sibling_symbol(origin_ops_file, symbol_name)
|
320
|
-
|
359
|
+
# if the origin_ops_file's file path is contained within a Bundler::BUNDLE_DIR directory, then we want to consult the @bundle_load_path
|
360
|
+
# otherwise, we want to consult the @app_load_path
|
361
|
+
load_path = find_load_path_that_includes_path(origin_ops_file.ops_file_path)
|
362
|
+
namespace_or_ops_file = load_path.resolve_symbol(origin_ops_file, symbol_name)
|
363
|
+
raise SymbolResolutionError, "Symbol '#{symbol_name}' not in load path for #{origin_ops_file.ops_file_path}" unless namespace_or_ops_file
|
364
|
+
namespace_or_ops_file
|
321
365
|
end
|
322
366
|
|
323
367
|
# returns a Namespace | OpsFile
|
324
368
|
def resolve_import_reference(origin_ops_file, import_reference)
|
325
|
-
case import_reference
|
369
|
+
load_path = case import_reference
|
326
370
|
|
327
|
-
#
|
371
|
+
# We know we're dealing with a package dependency reference, so we want to do the lookup in the bundle load path, where package dependencies live.
|
372
|
+
# Package references are guaranteed to live in the bundle dir
|
328
373
|
when PackageDependencyReference, DynamicPackageImportReference
|
329
|
-
@bundle_load_path
|
330
|
-
|
331
|
-
|
332
|
-
when
|
333
|
-
|
374
|
+
@bundle_load_path
|
375
|
+
when DirectoryReference
|
376
|
+
find_load_path_that_includes_path(import_reference.dirname)
|
377
|
+
when OpsFileReference
|
378
|
+
find_load_path_that_includes_path(import_reference.ops_file_path)
|
334
379
|
end
|
380
|
+
|
381
|
+
namespace_or_ops_file = load_path.resolve_import_reference(origin_ops_file, import_reference)
|
382
|
+
|
383
|
+
raise SymbolResolutionError, "Import reference '#{import_reference.summary}' not in load path for #{origin_ops_file.ops_file_path}" unless namespace_or_ops_file
|
384
|
+
|
385
|
+
namespace_or_ops_file
|
335
386
|
end
|
336
387
|
|
337
388
|
end
|
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.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Ellis
|
@@ -132,6 +132,7 @@ files:
|
|
132
132
|
- lib/opswalrus/bootstrap.sh
|
133
133
|
- lib/opswalrus/bundler.rb
|
134
134
|
- lib/opswalrus/cli.rb
|
135
|
+
- lib/opswalrus/errors.rb
|
135
136
|
- lib/opswalrus/git.rb
|
136
137
|
- lib/opswalrus/host.rb
|
137
138
|
- lib/opswalrus/hosts_file.rb
|