opswalrus 1.0.8 → 1.0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,446 @@
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
+
14
+ # module OpsWalrus
15
+
16
+ # class ArrayOrHashNavigationProxy
17
+ # def initialize(array_or_hash)
18
+ # @obj = array_or_hash
19
+ # end
20
+ # def [](index, *args, **kwargs, &block)
21
+ # @obj.method(:[]).call(index, *args, **kwargs, &block)
22
+ # end
23
+ # def respond_to_missing?(method, *)
24
+ # @obj.is_a?(Hash) && @obj.respond_to?(method)
25
+ # end
26
+ # def method_missing(name, *args, **kwargs, &block)
27
+ # case @obj
28
+ # when Array
29
+ # @obj.method(name).call(*args, **kwargs, &block)
30
+ # when Hash
31
+ # if @obj.respond_to?(name)
32
+ # @obj.method(name).call(*args, **kwargs, &block)
33
+ # else
34
+ # value = self[name.to_s]
35
+ # case value
36
+ # when Array, Hash
37
+ # ArrayOrHashNavigationProxy.new(value)
38
+ # else
39
+ # value
40
+ # end
41
+ # end
42
+ # end
43
+ # end
44
+ # end
45
+
46
+ # class InvocationParams
47
+ # # params : Hash
48
+ # def initialize(params)
49
+ # @params = params
50
+ # end
51
+
52
+ # def [](key)
53
+ # key = key.to_s if key.is_a? Symbol
54
+ # @params[key]
55
+ # end
56
+
57
+ # def dig(*keys)
58
+ # # keys = keys.map {|key| key.is_a?(Integer) ? key : key.to_s }
59
+ # @params.dig(*keys)
60
+ # end
61
+
62
+ # def method_missing(name, *args, **kwargs, &block)
63
+ # if @params.respond_to?(name)
64
+ # @params.method(name).call(*args, **kwargs, &block)
65
+ # else
66
+ # value = self[name]
67
+ # case value
68
+ # when Array, Hash
69
+ # ArrayOrHashNavigationProxy.new(value)
70
+ # else
71
+ # value
72
+ # end
73
+ # end
74
+ # end
75
+ # end
76
+
77
+
78
+ # # BootstrapLinuxHostShellScript = <<~SCRIPT
79
+ # # #!/usr/bin/env bash
80
+ # # ...
81
+ # # SCRIPT
82
+
83
+ # module InvocationDSL
84
+ # def ssh(*args, **kwargs, &block)
85
+ # host_proxy_class = @ops_file_script.host_proxy_class
86
+ # hosts = inventory(*args, **kwargs).map {|host| host_proxy_class.new(host) }
87
+ # sshkit_hosts = hosts.map(&:sshkit_host)
88
+ # sshkit_host_to_ops_host_map = sshkit_hosts.zip(hosts).to_h
89
+ # runtime_env = @runtime_env
90
+ # local_host = self
91
+ # # bootstrap_shell_script = BootstrapLinuxHostShellScript
92
+ # # on sshkit_hosts do |sshkit_host|
93
+ # SSHKit::Coordinator.new(sshkit_hosts).each(in: kwargs[:in] || :parallel) do |sshkit_host|
94
+
95
+ # # in this context, self is an instance of one of the subclasses of SSHKit::Backend::Abstract, e.g. SSHKit::Backend::Netssh
96
+
97
+ # host = sshkit_host_to_ops_host_map[sshkit_host]
98
+ # # puts "#{host.alias} / #{host}:"
99
+
100
+ # begin
101
+ # host.set_runtime_env(runtime_env)
102
+ # host.set_ssh_session_connection(self) # self is an instance of one of the subclasses of SSHKit::Backend::Abstract, e.g. SSHKit::Backend::Netssh
103
+
104
+ # # copy over bootstrap shell script
105
+ # # io = StringIO.new(bootstrap_shell_script)
106
+ # io = File.open(__FILE__.to_pathname.dirname.join("bootstrap.sh"))
107
+ # upload_success = host.upload(io, "tmpopsbootstrap.sh")
108
+ # io.close
109
+ # raise Error, "Unable to upload bootstrap shell script to remote host" unless upload_success
110
+ # host.execute(:chmod, "755", "tmpopsbootstrap.sh")
111
+ # host.execute(:sh, "tmpopsbootstrap.sh")
112
+
113
+ # # copy over ops bundle zip file
114
+ # zip_bundle_path = runtime_env.zip_bundle_path
115
+ # upload_success = host.upload(zip_bundle_path, "tmpops.zip")
116
+ # raise Error, "Unable to upload ops bundle to remote host" unless upload_success
117
+
118
+ # stdout, stderr, exit_status = host.run_ops(:bundle, "unzip tmpops.zip", in_bundle_root_dir: false)
119
+ # raise Error, "Unable to unzip ops bundle on remote host" unless exit_status == 0
120
+ # tmp_bundle_root_dir = stdout.strip
121
+ # host.set_ssh_session_tmp_bundle_root_dir(tmp_bundle_root_dir)
122
+
123
+ # # we run the block in the context of the host, s.t. `self` within the block evaluates to `host`
124
+ # retval = host.instance_exec(local_host, &block) # host is passed as the argument to the block
125
+
126
+ # # puts retval.inspect
127
+
128
+ # # cleanup
129
+ # if tmp_bundle_root_dir =~ /tmp/ # sanity check the temp path before we blow away something we don't intend
130
+ # host.execute(:rm, "-rf", "tmpopsbootstrap.sh", "tmpops.zip", tmp_bundle_root_dir)
131
+ # else
132
+ # host.execute(:rm, "-rf", "tmpopsbootstrap.sh", "tmpops.zip")
133
+ # end
134
+
135
+ # retval
136
+ # rescue SSHKit::Command::Failed => e
137
+ # puts "[!] Command failed:"
138
+ # puts e.message
139
+ # rescue Net::SSH::ConnectionTimeout
140
+ # puts "[!] The host '#{host}' not alive!"
141
+ # rescue Net::SSH::Timeout
142
+ # puts "[!] The host '#{host}' disconnected/timeouted unexpectedly!"
143
+ # rescue Errno::ECONNREFUSED
144
+ # puts "[!] Incorrect port #{port} for #{host}"
145
+ # rescue Net::SSH::HostKeyMismatch => e
146
+ # puts "[!] The host fingerprint does not match the last observed fingerprint for #{host}"
147
+ # puts e.message
148
+ # puts "You might try `ssh-keygen -f ~/.ssh/known_hosts -R \"#{host}\"`"
149
+ # rescue Net::SSH::AuthenticationFailed
150
+ # puts "Wrong Password: #{host} | #{user}:#{password}"
151
+ # rescue Net::SSH::Authentication::DisallowedMethod
152
+ # puts "[!] The host '#{host}' doesn't accept password authentication method."
153
+ # rescue Errno::EHOSTUNREACH => e
154
+ # puts "[!] The host '#{host}' is unreachable"
155
+ # rescue => e
156
+ # puts e.class
157
+ # puts e.message
158
+ # # puts e.backtrace.join("\n")
159
+ # ensure
160
+ # host.clear_ssh_session
161
+ # end
162
+ # end
163
+ # end
164
+
165
+ # def inventory(*args, **kwargs)
166
+ # tags = args.map(&:to_s)
167
+
168
+ # kwargs = kwargs.transform_keys(&:to_s)
169
+ # tags.concat(kwargs["tags"]) if kwargs["tags"]
170
+
171
+ # @runtime_env.app.inventory(tags)
172
+ # end
173
+
174
+ # def exit(exit_status, message = nil)
175
+ # if message
176
+ # puts message
177
+ # end
178
+ # result = if exit_status == 0
179
+ # Invocation::Success.new(nil)
180
+ # else
181
+ # Invocation::Error.new(nil, exit_status)
182
+ # end
183
+ # throw :exit_now, result
184
+ # end
185
+
186
+ # def env(*keys)
187
+ # keys = keys.map(&:to_s)
188
+ # if keys.empty?
189
+ # @env
190
+ # else
191
+ # @env.dig(*keys)
192
+ # end
193
+ # end
194
+
195
+ # # currently, import may only be used to import a package that is referenced in the script's package file
196
+ # # I may decide to extend this to work with dynamic package references
197
+ # #
198
+ # # local_package_name is the local package name defined for the package dependency that is attempting to be referenced
199
+ # def import(local_package_name)
200
+ # local_package_name = local_package_name.to_s
201
+ # package_reference = @ops_file_script.ops_file.package_file&.dependency(local_package_name)
202
+ # raise Error, "Unknown package reference: #{local_package_name}" unless package_reference
203
+ # import_reference = PackageDependencyReference.new(local_package_name, package_reference)
204
+ # # puts "import: #{import_reference.inspect}"
205
+ # @runtime_env.resolve_import_reference(@ops_file_script.ops_file, import_reference)
206
+ # end
207
+
208
+ # def params(*keys, default: nil)
209
+ # keys = keys.map(&:to_s)
210
+ # if keys.empty?
211
+ # @params
212
+ # else
213
+ # @params.dig(*keys) || default
214
+ # end
215
+ # end
216
+
217
+ # # returns the stdout from the command
218
+ # def sh(desc_or_cmd = nil, cmd = nil, input: nil, &block)
219
+ # out, err, status = *shell!(desc_or_cmd, cmd, block, input: input)
220
+ # out
221
+ # end
222
+
223
+ # # returns the tuple: [stdout, stderr, exit_status]
224
+ # def shell(desc_or_cmd = nil, cmd = nil, input: nil, &block)
225
+ # shell!(desc_or_cmd, cmd, block, input: input)
226
+ # end
227
+
228
+ # # returns the tuple: [stdout, stderr, exit_status]
229
+ # def shell!(desc_or_cmd = nil, cmd = nil, block = nil, input: nil)
230
+ # # description = nil
231
+
232
+ # 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
233
+
234
+ # description = desc_or_cmd if cmd || block
235
+ # cmd = block.call if block
236
+ # cmd ||= desc_or_cmd
237
+
238
+ # cmd = WalrusLang.render(cmd, block.binding) if block && cmd =~ /{{.*}}/
239
+
240
+ # #cmd = Shellwords.escape(cmd)
241
+
242
+ # # puts "shell! self: #{self.inspect}"
243
+
244
+ # if App.instance.report_mode?
245
+ # print "[#{@runtime_env.local_hostname}] "
246
+ # print "#{description}: " if description
247
+ # puts cmd
248
+ # end
249
+
250
+ # return unless cmd && !cmd.strip.empty?
251
+
252
+ # sshkit_cmd = @runtime_env.handle_input(input) do |interaction_handler|
253
+ # # self is a Module instance that is serving as the evaluation context in an instance of a subclass of an Invocation; see Invocation#evaluate
254
+ # backend.execute_cmd(cmd, interaction_handler: interaction_handler, verbosity: :info)
255
+ # end
256
+
257
+ # [sshkit_cmd.full_stdout, sshkit_cmd.full_stderr, sshkit_cmd.exit_status]
258
+ # end
259
+
260
+ # # def init_brew
261
+ # # execute('eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"')
262
+ # # end
263
+
264
+ # end
265
+
266
+ # # An Invocation object represents a stack frame, and the params_hash represents the
267
+ # # arguments that the caller has supplied for that stack frame to reference
268
+ # class Invocation
269
+ # class Result
270
+ # attr_accessor :value
271
+ # attr_accessor :exit_status
272
+ # def initialize(value, exit_status = 0)
273
+ # @value = value
274
+ # @exit_status = exit_status
275
+ # end
276
+ # def success?
277
+ # !failure?
278
+ # end
279
+ # def failure?
280
+ # !success?
281
+ # end
282
+ # end
283
+ # class Success < Result
284
+ # def initialize(value)
285
+ # super(value, 0)
286
+ # end
287
+ # def success?
288
+ # true
289
+ # end
290
+ # end
291
+ # class Error < Result
292
+ # def initialize(value, exit_status = 1)
293
+ # super(value, exit_status == 0 ? 1 : exit_status)
294
+ # end
295
+ # def failure?
296
+ # true
297
+ # end
298
+ # end
299
+
300
+
301
+ # def self.define_invocation_class(ops_file)
302
+ # klass = Class.new(Invocation)
303
+
304
+ # methods_defined = Set.new
305
+
306
+ # # define methods for every import in the script
307
+ # ops_file.local_symbol_table.each do |symbol_name, import_reference|
308
+ # unless methods_defined.include? symbol_name
309
+ # klass.define_method(symbol_name) do |*args, **kwargs, &block|
310
+ # # puts "0" * 80
311
+ # # puts "@runtime_env.resolve_import_reference(@ops_file_script.ops_file, #{import_reference.inspect})"
312
+ # # puts @ops_file_script.ops_file.ops_file_path
313
+ # # puts symbol_name
314
+ # namespace_or_ops_file = @runtime_env.resolve_import_reference(@ops_file_script.ops_file, import_reference)
315
+ # # puts namespace_or_ops_file.inspect
316
+ # # puts "0" * 80
317
+ # case namespace_or_ops_file
318
+ # when Namespace
319
+ # namespace_or_ops_file
320
+ # when OpsFile
321
+ # params_hash = namespace_or_ops_file.build_params_hash(*args, **kwargs)
322
+ # namespace_or_ops_file.invoke(@runtime_env, params_hash)
323
+ # end
324
+ # end
325
+ # methods_defined << symbol_name
326
+ # end
327
+ # end
328
+
329
+ # # define methods for every Namespace or OpsFile within the namespace that the OpsFile resides within
330
+ # sibling_symbol_table = Set.new
331
+ # sibling_symbol_table |= ops_file.dirname.glob("*.ops").map {|ops_file_path| ops_file_path.basename(".ops").to_s } # OpsFiles
332
+ # sibling_symbol_table |= ops_file.dirname.glob("*").select(&:directory?).map {|dir_path| dir_path.basename.to_s } # Namespaces
333
+ # sibling_symbol_table.each do |symbol_name|
334
+ # unless methods_defined.include? symbol_name
335
+ # klass.define_method(symbol_name) do |*args, **kwargs, &block|
336
+ # # puts "0" * 80
337
+ # # puts "@runtime_env.resolve_symbol(@ops_file_script.ops_file, #{symbol_name})"
338
+ # # puts @ops_file_script.ops_file.ops_file_path
339
+ # # puts symbol_name
340
+ # namespace_or_ops_file = @runtime_env.resolve_sibling_symbol(@ops_file_script.ops_file, symbol_name)
341
+ # # puts namespace_or_ops_file.inspect
342
+ # # puts "0" * 80
343
+ # case namespace_or_ops_file
344
+ # when Namespace
345
+ # namespace_or_ops_file
346
+ # when OpsFile
347
+ # params_hash = namespace_or_ops_file.build_params_hash(*args, **kwargs)
348
+ # namespace_or_ops_file.invoke(@runtime_env, params_hash)
349
+ # end
350
+ # end
351
+ # methods_defined << symbol_name
352
+ # end
353
+ # end
354
+
355
+ # klass
356
+ # end
357
+
358
+ # include InvocationDSL
359
+
360
+ # def initialize(ops_file_script, runtime_env, params_hash)
361
+ # @ops_file_script = ops_file_script
362
+ # @runtime_env = runtime_env
363
+ # @params = InvocationParams.new(params_hash)
364
+ # end
365
+
366
+ # def backend
367
+ # @runtime_env.pty
368
+ # end
369
+
370
+ # def debug?
371
+ # @runtime_env.debug?
372
+ # end
373
+
374
+ # def verbose?
375
+ # @runtime_env.verbose?
376
+ # end
377
+
378
+ # # def evaluate
379
+ # # # the evaluation context needs to be a module with all of the following:
380
+ # # # - InvocationDSL methods
381
+ # # # - @ops_file_script
382
+ # # # - @runtime_env
383
+ # # # - @params
384
+ # # # - #backend
385
+ # # # - #debug?
386
+ # # # - #verbose?
387
+ # # # - all the dynamically defined methods in the subclass of Invocation
388
+ # # end
389
+
390
+ # def evaluate
391
+ # eval(@ops_file_script.script, nil, @ops_file_script.ops_file.ops_file_path.to_s, @ops_file_script.ops_file.script_line_offset)
392
+ # end
393
+
394
+ # # def evaluate
395
+ # # ops_file_script = @ops_file_script
396
+ # # runtime_env = @runtime_env
397
+ # # invocation_params = @params
398
+
399
+ # # # 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
400
+ # # evaluation_context = Module.new
401
+ # # evaluation_context.module_exec {
402
+ # # include InvocationDSL
403
+
404
+ # # @ops_file_script = ops_file_script
405
+ # # @runtime_env = runtime_env
406
+ # # @params = invocation_params
407
+
408
+ # # def backend
409
+ # # @runtime_env.pty
410
+ # # end
411
+
412
+ # # def debug?
413
+ # # @runtime_env.debug?
414
+ # # end
415
+
416
+ # # def verbose?
417
+ # # @runtime_env.verbose?
418
+ # # end
419
+ # # }
420
+
421
+ # # 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)
422
+ # # end
423
+
424
+ # # def method_missing(name, *args, **kwargs, &block)
425
+ # # puts "1" * 80
426
+ # # import_reference = @ops_file_script.ops_file.resolve_import(name)
427
+ # # if import_reference
428
+ # # puts "2" * 80
429
+ # # resolved_value = @runtime_env.resolve_import_reference(@ops_file_script.ops_file, import_reference)
430
+ # # return resolved_value if resolved_value
431
+ # # end
432
+
433
+ # # puts "3" * 80
434
+ # # case namespace_or_ops_file = @runtime_env.resolve_symbol(@ops_file_script.ops_file, name.to_s)
435
+ # # when Namespace
436
+ # # puts "4" * 80
437
+ # # namespace_or_ops_file
438
+ # # when OpsFile
439
+ # # puts "5" * 80
440
+ # # namespace_or_ops_file.invoke(@runtime_env, *args, **kwargs, &block)
441
+ # # else
442
+ # # raise NoMethodError, "No method named '#{name}'"
443
+ # # end
444
+ # # end
445
+ # end
446
+ # end
@@ -30,7 +30,7 @@ module OpsWalrus
30
30
 
31
31
  # runtime_kv_args is an Array(String) of the form: ["arg1:val1", "arg2:val2", ...]
32
32
  # params_json_hash is a Hash representation of a JSON string
33
- def run(runtime_kv_args, params_json_hash: nil, verbose: false)
33
+ def run(runtime_kv_args, params_json_hash: nil)
34
34
  params_hash = runtime_kv_args.reduce(params_json_hash || {}) do |memo, kv_pair_string|
35
35
  str_key, str_value = kv_pair_string.split(":", 2)
36
36
  if pre_existing_value = memo[str_key]
@@ -43,7 +43,7 @@ module OpsWalrus
43
43
  memo
44
44
  end
45
45
 
46
- if verbose == 2
46
+ if app.debug?
47
47
  puts "Script:"
48
48
  puts @entry_point_ops_file.script
49
49
  end
@@ -72,7 +72,7 @@ module OpsWalrus
72
72
  Invocation::Error.new(e)
73
73
  end
74
74
 
75
- if verbose == 2 && result.failure?
75
+ if app.debug? && result.failure?
76
76
  puts "Ops script error details:"
77
77
  puts "Error: #{result.value}"
78
78
  puts "Status code: #{result.exit_status}"
@@ -1,6 +1,7 @@
1
1
  require 'pathname'
2
2
  require 'yaml'
3
3
  require_relative 'git'
4
+ require_relative 'host'
4
5
  require_relative 'ops_file_script'
5
6
 
6
7
  module OpsWalrus
@@ -9,7 +10,6 @@ module OpsWalrus
9
10
  attr_accessor :app
10
11
  attr_accessor :ops_file_path
11
12
  attr_accessor :yaml
12
- attr_accessor :script
13
13
 
14
14
  def initialize(app, ops_file_path)
15
15
  @app = app
@@ -24,32 +24,46 @@ module OpsWalrus
24
24
  self.class == other.class && self.hash == other.hash
25
25
  end
26
26
 
27
+ def host_proxy_class
28
+ @host_proxy_class || (lazy_load_file && @host_proxy_class)
29
+ end
30
+
27
31
  def yaml
28
- @yaml || (load_file && @yaml)
32
+ @yaml || (lazy_load_file && @yaml)
29
33
  end
30
34
 
31
35
  def script_line_offset
32
- @script_line_offset || (load_file && @script_line_offset)
36
+ @script_line_offset || (lazy_load_file && @script_line_offset)
33
37
  end
34
38
 
39
+ # returns a subclass of OpsFileScript
40
+ def script_class
41
+ @script_class || (lazy_load_file && @script_class)
42
+ end
43
+
44
+ # returns an instance of the class returned by #script_class
35
45
  def script
36
- @script || (load_file && @script)
46
+ @script || (lazy_load_file && @script)
37
47
  end
38
48
 
39
- def load_file
49
+ def lazy_load_file
50
+ # puts "OpsFile#lazy_load_file for #{ops_file_path}"
51
+ # puts caller
40
52
  yaml, ruby_script = if @ops_file_path.exist?
41
53
  parse(File.read(@ops_file_path))
42
54
  end || ["", ""]
43
55
  @script_line_offset = yaml.lines.size + 1 # +1 to account for the ... line
44
56
  @yaml = YAML.load(yaml) || {} # post_invariant: @yaml is a Hash
45
- @script = OpsFileScript.new(self, ruby_script)
57
+ @script_class = OpsFileScript.define_for(self, ruby_script)
58
+ @script = @script_class.new(self, ruby_script)
59
+ @host_proxy_class = HostProxy.define_host_proxy_class(self)
46
60
  end
47
61
 
48
62
  def parse(script_string)
49
63
  file_halves = script_string.split(/^\.\.\.$/, 2)
50
64
  case file_halves.count
51
65
  when 1
52
- yaml, ruby_script = "", file_halves
66
+ yaml, ruby_script = "", file_halves.first
53
67
  when 2
54
68
  yaml, ruby_script = *file_halves
55
69
  else
@@ -59,7 +73,7 @@ module OpsWalrus
59
73
  end
60
74
 
61
75
  def params
62
- yaml["params"]
76
+ yaml["params"] || {}
63
77
  end
64
78
 
65
79
  def output
@@ -97,28 +111,7 @@ module OpsWalrus
97
111
  # imports:
98
112
  # my_package: my_package
99
113
  in String => import_str
100
- case
101
- when package_reference = package_file&.dependency(import_str) # package dependency reference
102
- # in this context, import_str is the local package name documented in the package's dependencies
103
- PackageDependencyReference.new(local_name, package_reference)
104
- when import_str.to_pathname.exist? # path reference
105
- path = import_str.to_pathname
106
- case
107
- when path.directory?
108
- DirectoryReference.new(local_name, path.realpath)
109
- when path.file? && path.extname.downcase == ".ops"
110
- OpsFileReference.new(local_name, path.realpath)
111
- else
112
- raise Error, "Unknown import reference: #{local_name} -> #{import_str.inspect}"
113
- end
114
- when Git.repo?(import_str) # ops file has imported an ad-hoc git repo
115
- package_uri = import_str
116
- destination_package_path = app.bundler.dynamic_package_path_for_git_package(package_uri)
117
- # puts "DynamicPackageImportReference: #{local_name} -> #{destination_package_path}"
118
- DynamicPackageImportReference.new(local_name, DynamicPackageReference.new(local_name, package_uri, nil))
119
- else
120
- raise Error, "Unknown import reference: #{local_name}: #{yaml_import_reference.inspect}"
121
- end
114
+ import_string_to_import_reference(local_name, import_str)
122
115
 
123
116
  # when the imports line says:
124
117
  # imports:
@@ -137,9 +130,51 @@ module OpsWalrus
137
130
  end
138
131
  end
139
132
 
133
+ def import_string_to_import_reference(local_name, import_str)
134
+ if package_reference = package_file&.dependency(import_str) # package dependency reference
135
+ # in this context, import_str is the local package name documented in the package's dependencies
136
+ return PackageDependencyReference.new(local_name, package_reference)
137
+ end
138
+
139
+ import_path = import_str.to_pathname
140
+ if import_path.absolute? && import_path.exist? # absolute path reference
141
+ return case
142
+ when import_path.directory?
143
+ DirectoryReference.new(local_name, import_path.realpath)
144
+ when import_path.file? && import_path.extname.downcase == ".ops"
145
+ OpsFileReference.new(local_name, import_path.realpath)
146
+ else
147
+ raise Error, "Unknown import reference for absolute path: #{local_name}: #{import_path}"
148
+ end
149
+ end
150
+ if import_path.relative? # relative path reference
151
+ rebased_path = dirname.join(import_path)
152
+ if rebased_path.exist?
153
+ return case
154
+ when rebased_path.directory?
155
+ DirectoryReference.new(local_name, rebased_path.realpath)
156
+ when rebased_path.file? && rebased_path.extname.downcase == ".ops"
157
+ OpsFileReference.new(local_name, rebased_path.realpath)
158
+ else
159
+ raise Error, "Unknown import reference for relative path: #{local_name}: #{import_path}"
160
+ end
161
+ elsif rebased_path.sub_ext(".ops").exist?
162
+ return OpsFileReference.new(local_name, rebased_path.sub_ext(".ops").realpath)
163
+ end
164
+ end
165
+
166
+ package_uri = import_str
167
+ if Git.repo?(package_uri) # ops file has imported an ad-hoc git repo
168
+ destination_package_path = app.bundler.dynamic_package_path_for_git_package(package_uri)
169
+ # puts "DynamicPackageImportReference: #{local_name} -> #{destination_package_path}"
170
+ return DynamicPackageImportReference.new(local_name, DynamicPackageReference.new(local_name, package_uri, nil))
171
+ end
172
+
173
+ raise Error, "Unknown import reference: #{local_name}: #{import_str.inspect}"
174
+ end
175
+
140
176
  def invoke(runtime_env, params_hash)
141
- # puts "invoking: #{ops_file_path}"
142
- script.invoke(runtime_env, params_hash)
177
+ script._invoke(runtime_env, params_hash)
143
178
  end
144
179
 
145
180
  def build_params_hash(*args, **kwargs)
@@ -223,5 +258,9 @@ module OpsWalrus
223
258
  dirname.glob("*").select(&:directory?)
224
259
  end
225
260
 
261
+ def to_s
262
+ "OpsFile: #{ops_file_path}"
263
+ end
264
+
226
265
  end
227
266
  end