opswalrus 1.0.12 → 1.0.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d9c838dbf7a64b90664fa94e0b6579508e0967d20a2fa8d8779616fa36cf9b8f
4
- data.tar.gz: 9787c55859ab6ed5f5ceb50803c72df06f9d021e25dcbe7dc917db4f2f30b2ab
3
+ metadata.gz: 9ee2d0af10d38f7722a8af82b44af64f3c0d906de21b4425d472d4e27b9495b0
4
+ data.tar.gz: e42c56ffcdf0c1cf1b08e68771dcf46364439c057393ed325b5325964816d1a8
5
5
  SHA512:
6
- metadata.gz: 644d62c00d36280d5be184f8408c0fc8d5da05d02bae7207b6899d819c98c3f6456be34bb50db1ebfd4012419e6710e193d278c6065bdf2a5b5cc6025b291606
7
- data.tar.gz: a0a2f5663a4bc748feba02bf4609de61347a8ef5519420ba578dfade18c5fdb877f9aada5332bc705c3714db3284b0e137844fa43a56718c3a99d23ceda8b613
6
+ metadata.gz: 39a22eed3d76788bb6d70ad4f4700d512a4e9612614508e407ee9d0187bd2f0aab3593845f5f6a8adb91020d330bd7d955bbe21c869c3aa7e6c1185af4204b6a
7
+ data.tar.gz: 63e635e997cead804423e1c966e7e429538e625532277b98178c19186345455b469222d5eabd4b7829998690753ba8d95d09788248b167e706d3512d05ece6de
data/Dockerfile CHANGED
@@ -39,5 +39,5 @@ RUN git config --global --add safe.directory /workdir
39
39
 
40
40
  # Set the entrypoint to run the installed binary in /workdir
41
41
  # Example: docker run -it -v "$PWD:/workdir" ops
42
- # ENTRYPOINT ["ops"]
43
- CMD ["ops"]
42
+ ENTRYPOINT ["ops"]
43
+ # CMD ["ops"]
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- opswalrus (1.0.12)
4
+ opswalrus (1.0.14)
5
5
  bcrypt_pbkdf (~> 1.1)
6
6
  citrus (~> 3.0)
7
7
  ed25519 (~> 1.3)
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, tmp_dir = get_entry_point_ops_file_and_args(package_operation_and_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
- # if the ops file is part of a package, then set the package directory as the app's pwd
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(tmp_dir) if tmp_dir
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 with considered as relative to the package file's directory
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
- # puts "set pwd: #{ops_file.package_file.dirname}"
187
- set_pwd(ops_file.package_file.dirname)
188
- rebased_ops_file_relative_path = ops_file.ops_file_path.relative_path_from(ops_file.package_file.dirname)
189
- # note: rebased_ops_file_relative_path is a relative path that is relative to ops_file.package_file.dirname
190
- # puts "rebased path: #{rebased_ops_file_relative_path}"
191
- absolute_ops_file_path = ops_file.package_file.dirname.join(rebased_ops_file_relative_path)
192
- # puts "absolute path: #{absolute_ops_file_path}"
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, optional_tmp_dir ]
207
- # such that the third item - optional_tmp_dir - if present, should be deleted after the script has completed running
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
- tmp_dir = nil
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, tmp_dir]
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
- tmp_dir = Dir.mktmpdir.to_pathname # this is the temporary bundle root dir
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, tmp_dir)
275
+ DirZipper.unzip(first_filepath, tmp_bundle_root_dir)
229
276
 
230
- find_entry_point_ops_file_in_dir(tmp_dir, package_operation_and_args)
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, tmp_dir]
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, tmp_dir]
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
@@ -173,9 +173,8 @@ module OpsWalrus
173
173
  end
174
174
  end
175
175
 
176
- package_uri = package_url
177
- if Git.repo?(package_uri) # git repo
178
- download_git_package(package_url, version, destination_package_path)
176
+ if package_uri = Git.repo?(package_url) # git repo
177
+ download_git_package(package_uri, version, destination_package_path)
179
178
  end
180
179
  end
181
180
 
@@ -0,0 +1,7 @@
1
+ module OpsWalrus
2
+ class Error < StandardError
3
+ end
4
+
5
+ class SymbolResolutionError < Error
6
+ end
7
+ end
@@ -2,6 +2,7 @@ require "set"
2
2
  require "sshkit"
3
3
 
4
4
  require_relative "interaction_handlers"
5
+ require_relative "invocation"
5
6
 
6
7
  module OpsWalrus
7
8
 
@@ -48,6 +49,52 @@ module OpsWalrus
48
49
 
49
50
  # the subclasses of HostProxy will define methods that handle method dispatch via HostProxyOpsFileInvocationBuilder objects
50
51
  class HostProxy
52
+ # def self.define_host_proxy_class(ops_file)
53
+ # klass = Class.new(HostProxy)
54
+
55
+ # methods_defined = Set.new
56
+
57
+ # # define methods for every import in the script
58
+ # ops_file.local_symbol_table.each do |symbol_name, import_reference|
59
+ # unless methods_defined.include? symbol_name
60
+ # # puts "1. defining: #{symbol_name}(...)"
61
+ # klass.define_method(symbol_name) do |*args, **kwargs, &block|
62
+ # invocation_builder = case import_reference
63
+ # # we know we're dealing with a package dependency reference, so we want to run an ops file contained within the bundle directory,
64
+ # # therefore, we want to reference the specified ops file with respect to the bundle dir
65
+ # when PackageDependencyReference
66
+ # HostProxyOpsFileInvocationBuilder.new(self, true)
67
+
68
+ # # we know we're dealing with a directory reference or OpsFile reference outside of the bundle dir, so we want to reference
69
+ # # the specified ops file with respect to the root directory, and not with respect to the bundle dir
70
+ # when DirectoryReference, OpsFileReference
71
+ # HostProxyOpsFileInvocationBuilder.new(self, false)
72
+ # end
73
+
74
+ # invocation_builder.send(symbol_name, *args, **kwargs, &block)
75
+ # end
76
+ # methods_defined << symbol_name
77
+ # end
78
+ # end
79
+
80
+ # # define methods for every Namespace or OpsFile within the namespace that the OpsFile resides within
81
+ # sibling_symbol_table = Set.new
82
+ # sibling_symbol_table |= ops_file.dirname.glob("*.ops").map {|ops_file_path| ops_file_path.basename(".ops").to_s } # OpsFiles
83
+ # sibling_symbol_table |= ops_file.dirname.glob("*").select(&:directory?).map {|dir_path| dir_path.basename.to_s } # Namespaces
84
+ # sibling_symbol_table.each do |symbol_name|
85
+ # unless methods_defined.include? symbol_name
86
+ # # puts "2. defining: #{symbol_name}(...)"
87
+ # klass.define_method(symbol_name) do |*args, **kwargs, &block|
88
+ # invocation_builder = HostProxyOpsFileInvocationBuilder.new(self, false)
89
+ # invocation_builder.invoke(symbol_name, *args, **kwargs, &block)
90
+ # end
91
+ # methods_defined << symbol_name
92
+ # end
93
+ # end
94
+
95
+ # klass
96
+ # end
97
+
51
98
  def self.define_host_proxy_class(ops_file)
52
99
  klass = Class.new(HostProxy)
53
100
 
@@ -56,21 +103,40 @@ module OpsWalrus
56
103
  # define methods for every import in the script
57
104
  ops_file.local_symbol_table.each do |symbol_name, import_reference|
58
105
  unless methods_defined.include? symbol_name
59
- # puts "1. defining: #{symbol_name}(...)"
60
106
  klass.define_method(symbol_name) do |*args, **kwargs, &block|
61
- invocation_builder = case import_reference
107
+ # puts "resolving local symbol table entry: #{symbol_name}"
108
+ namespace_or_ops_file = @runtime_env.resolve_import_reference(ops_file, import_reference)
109
+ # puts "namespace_or_ops_file=#{namespace_or_ops_file.to_s}"
110
+
111
+ invocation_context = case import_reference
62
112
  # we know we're dealing with a package dependency reference, so we want to run an ops file contained within the bundle directory,
63
113
  # therefore, we want to reference the specified ops file with respect to the bundle dir
64
114
  when PackageDependencyReference
65
- HostProxyOpsFileInvocationBuilder.new(self, true)
115
+ RemoteImportInvocationContext.new(@runtime_env, self, namespace_or_ops_file, true)
66
116
 
67
117
  # we know we're dealing with a directory reference or OpsFile reference outside of the bundle dir, so we want to reference
68
118
  # the specified ops file with respect to the root directory, and not with respect to the bundle dir
69
119
  when DirectoryReference, OpsFileReference
70
- HostProxyOpsFileInvocationBuilder.new(self, false)
120
+ RemoteImportInvocationContext.new(@runtime_env, self, namespace_or_ops_file, false)
71
121
  end
72
122
 
73
- invocation_builder.send(symbol_name, *args, **kwargs, &block)
123
+ invocation_context._invoke(*args, **kwargs)
124
+
125
+
126
+
127
+ # invocation_builder = case import_reference
128
+ # # we know we're dealing with a package dependency reference, so we want to run an ops file contained within the bundle directory,
129
+ # # therefore, we want to reference the specified ops file with respect to the bundle dir
130
+ # when PackageDependencyReference
131
+ # HostProxyOpsFileInvocationBuilder.new(self, true)
132
+
133
+ # # we know we're dealing with a directory reference or OpsFile reference outside of the bundle dir, so we want to reference
134
+ # # the specified ops file with respect to the root directory, and not with respect to the bundle dir
135
+ # when DirectoryReference, OpsFileReference
136
+ # HostProxyOpsFileInvocationBuilder.new(self, false)
137
+ # end
138
+
139
+ # invocation_builder.send(symbol_name, *args, **kwargs, &block)
74
140
  end
75
141
  methods_defined << symbol_name
76
142
  end
@@ -84,8 +150,11 @@ module OpsWalrus
84
150
  unless methods_defined.include? symbol_name
85
151
  # puts "2. defining: #{symbol_name}(...)"
86
152
  klass.define_method(symbol_name) do |*args, **kwargs, &block|
87
- invocation_builder = HostProxyOpsFileInvocationBuilder.new(self, false)
88
- invocation_builder.invoke(symbol_name, *args, **kwargs, &block)
153
+ # invocation_builder = HostProxyOpsFileInvocationBuilder.new(self, false)
154
+ # invocation_builder.invoke(symbol_name, *args, **kwargs, &block)
155
+
156
+ invocation_context = RemoteImportInvocationContext.new(@runtime_env, self, namespace_or_ops_file, false)
157
+ invocation_context._invoke(*args, **kwargs)
89
158
  end
90
159
  methods_defined << symbol_name
91
160
  end
@@ -97,12 +166,17 @@ module OpsWalrus
97
166
 
98
167
  attr_accessor :_host
99
168
 
100
- def initialize(host)
169
+ def initialize(runtime_env, host)
101
170
  @_host = host
171
+ @runtime_env = runtime_env
102
172
  end
103
173
 
104
174
  # the subclasses of this class will define methods that handle method dispatch via HostProxyOpsFileInvocationBuilder objects
105
175
 
176
+ def to_s
177
+ @_host.to_s
178
+ end
179
+
106
180
  def method_missing(name, ...)
107
181
  @_host.send(name, ...)
108
182
  end