opswalrus 1.0.5 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +8 -8
- data/build.ops +20 -0
- data/lib/opswalrus/app.rb +16 -39
- data/lib/opswalrus/bundler.rb +19 -12
- data/lib/opswalrus/git.rb +22 -0
- data/lib/opswalrus/host.rb +4 -3
- data/lib/opswalrus/operation_runner.rb +1 -1
- data/lib/opswalrus/ops_file.rb +21 -34
- data/lib/opswalrus/ops_file_script.rb +10 -3
- data/lib/opswalrus/package_file.rb +11 -2
- data/lib/opswalrus/runtime_environment.rb +34 -2
- data/lib/opswalrus/version.rb +1 -1
- data/lib/opswalrus/walrus_lang.rb +1 -1
- data/opswalrus.gemspec +8 -8
- metadata +4 -5
- data/lib/opswalrus/bootstrap_linux_host1.sh +0 -12
- data/lib/opswalrus/bootstrap_linux_host2.sh +0 -37
- data/lib/opswalrus/bootstrap_linux_host3.sh +0 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c6a371b7cd41b84c0ea93f89180204ea580b2a8d20c9d8686639663e6bf31379
|
|
4
|
+
data.tar.gz: b27a653685068ba101cb89990a3fcd74b4eab39337f9592f35b9cc41f0014a48
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5928f1f07bc4ddd13f630c3967a73a196ec21999cbc1cf5d30247bd24dbec940c7fe207ff37520158b787ef635a70b41add3c6f3614d1000881ddda509361f3d
|
|
7
|
+
data.tar.gz: e89db2ddc7e4b97dfcd2df4e01d2a525e0a5a8757283d1de2f136c8dd3fa64a4f04aa6daaf1efe81d812c622dc2572df996b19aa66dcb9cf664826b4c38a82b9
|
data/Gemfile.lock
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
opswalrus (1.0.
|
|
5
|
-
bcrypt_pbkdf
|
|
6
|
-
citrus
|
|
7
|
-
ed25519
|
|
8
|
-
git
|
|
9
|
-
gli
|
|
10
|
-
rubyzip
|
|
11
|
-
sshkit
|
|
4
|
+
opswalrus (1.0.6)
|
|
5
|
+
bcrypt_pbkdf (~> 1.1)
|
|
6
|
+
citrus (~> 3.0)
|
|
7
|
+
ed25519 (~> 1.3)
|
|
8
|
+
git (~> 1.18)
|
|
9
|
+
gli (~> 2.21)
|
|
10
|
+
rubyzip (~> 2.3)
|
|
11
|
+
sshkit (~> 1.21)
|
|
12
12
|
|
|
13
13
|
GEM
|
|
14
14
|
remote: https://rubygems.org/
|
data/build.ops
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
params:
|
|
2
|
+
version: string
|
|
3
|
+
|
|
4
|
+
imports:
|
|
5
|
+
core: "https://github.com/opswalrus/core.git"
|
|
6
|
+
...
|
|
7
|
+
|
|
8
|
+
version = params.version
|
|
9
|
+
|
|
10
|
+
template = <<TEMPLATE
|
|
11
|
+
module OpsWalrus
|
|
12
|
+
VERSION = "{{ version }}"
|
|
13
|
+
end
|
|
14
|
+
TEMPLATE
|
|
15
|
+
|
|
16
|
+
puts "Write version.rb for version #{version}"
|
|
17
|
+
core.template.write(path: "./lib/opswalrus/version.rb", template: template, variables: {version: version})
|
|
18
|
+
|
|
19
|
+
sh("Build gem") { 'gem build opswalrus.gemspec' }
|
|
20
|
+
sh("Build docker image") { 'docker build -t opswalrus/ops:{{ version }} .' }
|
data/lib/opswalrus/app.rb
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require "citrus"
|
|
2
|
-
require "git"
|
|
3
2
|
require "io/console"
|
|
4
3
|
require "json"
|
|
5
4
|
require "random/formatter"
|
|
@@ -8,6 +7,7 @@ require "socket"
|
|
|
8
7
|
require "stringio"
|
|
9
8
|
require "yaml"
|
|
10
9
|
require "pathname"
|
|
10
|
+
require_relative "git"
|
|
11
11
|
require_relative "host"
|
|
12
12
|
require_relative "hosts_file"
|
|
13
13
|
require_relative "operation_runner"
|
|
@@ -158,24 +158,11 @@ module OpsWalrus
|
|
|
158
158
|
ops_file = OpsFile.new(self, ops_file_path)
|
|
159
159
|
|
|
160
160
|
# if the ops file is part of a package, then set the package directory as the app's pwd
|
|
161
|
-
# puts "run1: #{ops_file.ops_file_path}"
|
|
162
161
|
if ops_file.package_file && ops_file.package_file.dirname.to_s !~ /#{Bundler::BUNDLE_DIR}/
|
|
163
|
-
|
|
164
|
-
set_pwd(ops_file.package_file.dirname)
|
|
165
|
-
rebased_ops_file_relative_path = ops_file.ops_file_path.relative_path_from(ops_file.package_file.dirname)
|
|
166
|
-
# note: rebased_ops_file_relative_path is a relative path that is relative to ops_file.package_file.dirname
|
|
167
|
-
# puts "rebased path: #{rebased_ops_file_relative_path}"
|
|
168
|
-
absolute_ops_file_path = ops_file.package_file.dirname.join(rebased_ops_file_relative_path)
|
|
169
|
-
# puts "absolute path: #{absolute_ops_file_path}"
|
|
170
|
-
ops_file = OpsFile.new(self, absolute_ops_file_path)
|
|
162
|
+
ops_file = set_pwd_to_ops_file_package_directory(ops_file)
|
|
171
163
|
end
|
|
172
|
-
# puts "run2: #{ops_file.ops_file_path}"
|
|
173
164
|
|
|
174
165
|
op = OperationRunner.new(self, ops_file)
|
|
175
|
-
# if op.requires_sudo?
|
|
176
|
-
# prompt_sudo_password unless sudo_password
|
|
177
|
-
# end
|
|
178
|
-
# exit_status, out, err, script_output_structure = op.run(operation_kv_args, params_json_hash: @params, verbose: @verbose)
|
|
179
166
|
result = op.run(operation_kv_args, params_json_hash: @params, verbose: @verbose)
|
|
180
167
|
exit_status = result.exit_status
|
|
181
168
|
|
|
@@ -183,14 +170,7 @@ module OpsWalrus
|
|
|
183
170
|
puts "Op exit_status"
|
|
184
171
|
puts exit_status
|
|
185
172
|
|
|
186
|
-
# puts "Op stdout"
|
|
187
|
-
# puts out
|
|
188
|
-
|
|
189
|
-
# puts "Op stderr"
|
|
190
|
-
# puts err
|
|
191
|
-
|
|
192
173
|
puts "Op output"
|
|
193
|
-
# puts script_output_structure ? JSON.pretty_generate(script_output_structure) : nil.inspect
|
|
194
174
|
puts JSON.pretty_generate(result.value)
|
|
195
175
|
end
|
|
196
176
|
|
|
@@ -203,6 +183,19 @@ module OpsWalrus
|
|
|
203
183
|
FileUtils.remove_entry(tmp_dir) if tmp_dir
|
|
204
184
|
end
|
|
205
185
|
|
|
186
|
+
# sets the App's pwd to the ops file's package directory and
|
|
187
|
+
# returns a new OpsFile that points at the revised pathname with considered as relative to the package file's directory
|
|
188
|
+
def set_pwd_to_ops_file_package_directory(ops_file)
|
|
189
|
+
# puts "set pwd: #{ops_file.package_file.dirname}"
|
|
190
|
+
set_pwd(ops_file.package_file.dirname)
|
|
191
|
+
rebased_ops_file_relative_path = ops_file.ops_file_path.relative_path_from(ops_file.package_file.dirname)
|
|
192
|
+
# note: rebased_ops_file_relative_path is a relative path that is relative to ops_file.package_file.dirname
|
|
193
|
+
# puts "rebased path: #{rebased_ops_file_relative_path}"
|
|
194
|
+
absolute_ops_file_path = ops_file.package_file.dirname.join(rebased_ops_file_relative_path)
|
|
195
|
+
# puts "absolute path: #{absolute_ops_file_path}"
|
|
196
|
+
OpsFile.new(self, absolute_ops_file_path)
|
|
197
|
+
end
|
|
198
|
+
|
|
206
199
|
# package_operation_and_args can take one of the following forms:
|
|
207
200
|
# - ["github.com/davidkellis/my-package", "operation1", "arg1:val1", "arg2:val2", "arg3:val3"]
|
|
208
201
|
# - ["foo.zip", "foo/myfile.ops", "arg1:val1", "arg2:val2", "arg3:val3"]
|
|
@@ -244,7 +237,7 @@ module OpsWalrus
|
|
|
244
237
|
|
|
245
238
|
# operation_kv_args = package_operation_and_args
|
|
246
239
|
[ops_file_path, operation_kv_args, tmp_dir]
|
|
247
|
-
when repo_url =
|
|
240
|
+
when repo_url = Git.repo?(package_or_ops_file_reference)
|
|
248
241
|
destination_package_path = bundler.download_git_package(repo_url)
|
|
249
242
|
|
|
250
243
|
ops_file_path, operation_kv_args = find_entry_point_ops_file_in_dir(destination_package_path, package_operation_and_args)
|
|
@@ -301,22 +294,6 @@ module OpsWalrus
|
|
|
301
294
|
[ops_file_path, operation_kv_args]
|
|
302
295
|
end
|
|
303
296
|
|
|
304
|
-
# git_repo?("davidkellis/arborist") -> "https://github.com/davidkellis/arborist"
|
|
305
|
-
# returns the repo URL
|
|
306
|
-
def git_repo?(repo_reference)
|
|
307
|
-
candidate_repo_references = [
|
|
308
|
-
repo_reference,
|
|
309
|
-
repo_reference =~ /(\.(com|net|org|dev|io|local))\// && "https://#{repo_reference}",
|
|
310
|
-
repo_reference !~ /github\.com\// && repo_reference =~ /^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}\/([\w\.@\:\-~]+)$/i && "https://github.com/#{repo_reference}" # this regex is from https://www.npmjs.com/package/github-username-regex and https://www.debuggex.com/r/H4kRw1G0YPyBFjfm
|
|
311
|
-
].compact
|
|
312
|
-
working_repo_reference = candidate_repo_references.find {|reference| Git.ls_remote(reference) rescue nil }
|
|
313
|
-
working_repo_reference
|
|
314
|
-
end
|
|
315
|
-
|
|
316
|
-
# def is_dir_git_repo?(dir_path)
|
|
317
|
-
# Git.ls_remote(reference) rescue nil
|
|
318
|
-
# end
|
|
319
|
-
|
|
320
297
|
def bundle_status
|
|
321
298
|
end
|
|
322
299
|
|
data/lib/opswalrus/bundler.rb
CHANGED
|
@@ -22,8 +22,8 @@ module OpsWalrus
|
|
|
22
22
|
@bundle_dir
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
def
|
|
26
|
-
FileUtils.mkdir_p(@bundle_dir)
|
|
25
|
+
def ensure_pwd_bundle_directory_exists
|
|
26
|
+
FileUtils.mkdir_p(@bundle_dir) unless @bundle_dir.exist?
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
# # returns the OpsFile within the bundle directory that represents the given ops_file (which is outside of the bundle directory)
|
|
@@ -50,7 +50,7 @@ module OpsWalrus
|
|
|
50
50
|
# end
|
|
51
51
|
|
|
52
52
|
def update
|
|
53
|
-
|
|
53
|
+
ensure_pwd_bundle_directory_exists
|
|
54
54
|
|
|
55
55
|
package_yaml_files = pwd.glob("./**/package.yaml") - pwd.glob("./**/#{BUNDLE_DIR}/**/package.yaml")
|
|
56
56
|
package_files_within_pwd = package_yaml_files.map {|path| PackageFile.new(path.realpath) }
|
|
@@ -83,7 +83,7 @@ module OpsWalrus
|
|
|
83
83
|
|
|
84
84
|
# returns the self_pkg directory within the bundle directory
|
|
85
85
|
# def include_directory_in_bundle_as_self_pkg(dirname = pwd)
|
|
86
|
-
#
|
|
86
|
+
# ensure_pwd_bundle_directory_exists
|
|
87
87
|
|
|
88
88
|
# destination_package_path = @bundle_dir.join("self_pkg")
|
|
89
89
|
|
|
@@ -103,7 +103,7 @@ module OpsWalrus
|
|
|
103
103
|
# This method downloads a package_url that is a dependency referenced in the specified package_file
|
|
104
104
|
# returns the destination directory that the package was downloaded to
|
|
105
105
|
def download_package(package_file, package_reference)
|
|
106
|
-
|
|
106
|
+
ensure_pwd_bundle_directory_exists
|
|
107
107
|
|
|
108
108
|
local_name = package_reference.local_name
|
|
109
109
|
package_url = package_reference.package_uri
|
|
@@ -144,19 +144,26 @@ module OpsWalrus
|
|
|
144
144
|
end
|
|
145
145
|
|
|
146
146
|
def download_git_package(package_url, version = nil, destination_package_path = nil)
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
147
|
+
ensure_pwd_bundle_directory_exists
|
|
148
|
+
|
|
149
|
+
destination_package_path ||= dynamic_package_path_for_git_package(package_url, version)
|
|
150
|
+
|
|
151
|
+
return destination_package_path if destination_package_path.exist?
|
|
152
|
+
|
|
152
153
|
if version
|
|
153
|
-
Git.clone(package_url, destination_package_path, branch: version, config: ['submodule.recurse=true'])
|
|
154
|
+
::Git.clone(package_url, destination_package_path, branch: version, config: ['submodule.recurse=true'])
|
|
154
155
|
else
|
|
155
|
-
Git.clone(package_url, destination_package_path, config: ['submodule.recurse=true'])
|
|
156
|
+
::Git.clone(package_url, destination_package_path, config: ['submodule.recurse=true'])
|
|
156
157
|
end
|
|
158
|
+
|
|
157
159
|
destination_package_path
|
|
158
160
|
end
|
|
159
161
|
|
|
162
|
+
def dynamic_package_path_for_git_package(package_url, version = nil)
|
|
163
|
+
package_reference_dirname = sanitize_path(package_url)
|
|
164
|
+
bundle_dir.join(package_reference_dirname)
|
|
165
|
+
end
|
|
166
|
+
|
|
160
167
|
def sanitize_path(path)
|
|
161
168
|
# found this at https://apidock.com/rails/v5.2.3/ActiveStorage/Filename/sanitized
|
|
162
169
|
path.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "�").strip.tr("\u{202E}%$|:;/\t\r\n\\", "-")
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require "git"
|
|
2
|
+
|
|
3
|
+
module OpsWalrus
|
|
4
|
+
class Git
|
|
5
|
+
# repo?("davidkellis/arborist") -> "https://github.com/davidkellis/arborist"
|
|
6
|
+
# returns the repo URL or directory path
|
|
7
|
+
def self.repo?(repo_reference)
|
|
8
|
+
if Dir.exist?(repo_reference)
|
|
9
|
+
::Git.ls_remote(repo_reference) && repo_reference rescue nil
|
|
10
|
+
else
|
|
11
|
+
candidate_repo_references = [
|
|
12
|
+
repo_reference,
|
|
13
|
+
repo_reference =~ /(\.(com|net|org|dev|io|local))\// && "https://#{repo_reference}",
|
|
14
|
+
repo_reference !~ /github\.com\// && repo_reference =~ /^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}\/([\w\.@\:\-~]+)$/i && "https://github.com/#{repo_reference}" # this regex is from https://www.npmjs.com/package/github-username-regex and https://www.debuggex.com/r/H4kRw1G0YPyBFjfm
|
|
15
|
+
].compact
|
|
16
|
+
working_repo_reference = candidate_repo_references.find {|reference| ::Git.ls_remote(reference) rescue nil }
|
|
17
|
+
working_repo_reference
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
end
|
data/lib/opswalrus/host.rb
CHANGED
|
@@ -27,6 +27,10 @@ module OpsWalrus
|
|
|
27
27
|
cmd = block.call if block
|
|
28
28
|
cmd ||= desc_or_cmd
|
|
29
29
|
|
|
30
|
+
cmd = WalrusLang.render(cmd, block.binding) if block && cmd =~ /{{.*}}/
|
|
31
|
+
|
|
32
|
+
#cmd = Shellwords.escape(cmd)
|
|
33
|
+
|
|
30
34
|
if self.alias
|
|
31
35
|
print "[#{self.alias} | #{host}] "
|
|
32
36
|
else
|
|
@@ -35,11 +39,8 @@ module OpsWalrus
|
|
|
35
39
|
print "#{description}: " if description
|
|
36
40
|
puts cmd
|
|
37
41
|
|
|
38
|
-
cmd = WalrusLang.render(cmd, block.binding) if block && cmd =~ /{{.*}}/
|
|
39
42
|
return unless cmd && !cmd.strip.empty?
|
|
40
43
|
|
|
41
|
-
#cmd = Shellwords.escape(cmd)
|
|
42
|
-
|
|
43
44
|
# puts "shell: #{cmd}"
|
|
44
45
|
# puts "shell: #{cmd.inspect}"
|
|
45
46
|
# puts "sudo_password: #{sudo_password}"
|
data/lib/opswalrus/ops_file.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'pathname'
|
|
2
2
|
require 'yaml'
|
|
3
|
+
require_relative 'git'
|
|
3
4
|
require_relative 'ops_file_script'
|
|
4
5
|
|
|
5
6
|
module OpsWalrus
|
|
@@ -91,12 +92,16 @@ module OpsWalrus
|
|
|
91
92
|
imports_hash.map do |local_name, yaml_import_reference|
|
|
92
93
|
local_name = local_name.to_s
|
|
93
94
|
import_reference = case yaml_import_reference
|
|
95
|
+
|
|
96
|
+
# when the imports line says:
|
|
97
|
+
# imports:
|
|
98
|
+
# my_package: my_package
|
|
94
99
|
in String => import_str
|
|
95
100
|
case
|
|
96
101
|
when package_reference = package_file&.dependency(import_str) # package dependency reference
|
|
97
102
|
# in this context, import_str is the local package name documented in the package's dependencies
|
|
98
103
|
PackageDependencyReference.new(local_name, package_reference)
|
|
99
|
-
when import_str.to_pathname.exist?
|
|
104
|
+
when import_str.to_pathname.exist? # path reference
|
|
100
105
|
path = import_str.to_pathname
|
|
101
106
|
case
|
|
102
107
|
when path.directory?
|
|
@@ -106,13 +111,26 @@ module OpsWalrus
|
|
|
106
111
|
else
|
|
107
112
|
raise Error, "Unknown import reference: #{local_name} -> #{import_str.inspect}"
|
|
108
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}"
|
|
109
121
|
end
|
|
110
|
-
|
|
122
|
+
|
|
123
|
+
# when the imports line says:
|
|
124
|
+
# imports:
|
|
125
|
+
# my_package:
|
|
126
|
+
# url: https://...
|
|
127
|
+
# version: 2.1
|
|
128
|
+
# in Hash => package_defn
|
|
111
129
|
# url = package_defn["url"]
|
|
112
130
|
# version = package_defn["version"]
|
|
113
131
|
# PackageReference.new(local_name, url, version&.to_s)
|
|
114
132
|
else
|
|
115
|
-
raise Error, "Unknown
|
|
133
|
+
raise Error, "Unknown import reference: #{local_name}: #{yaml_import_reference.inspect}"
|
|
116
134
|
end
|
|
117
135
|
[local_name, import_reference]
|
|
118
136
|
end.to_h
|
|
@@ -205,36 +223,5 @@ module OpsWalrus
|
|
|
205
223
|
dirname.glob("*").select(&:directory?)
|
|
206
224
|
end
|
|
207
225
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
def supporting_library_include_dir_and_require_lib
|
|
216
|
-
if Dir.exist?(ops_file_helper_library_directory)
|
|
217
|
-
[ops_file_helper_library_directory, ops_file_helper_library_basename]
|
|
218
|
-
elsif File.exist?(ops_file_sibling_helper_library_file)
|
|
219
|
-
[dirname, ops_file_helper_library_basename]
|
|
220
|
-
else
|
|
221
|
-
[nil, nil]
|
|
222
|
-
end
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
def ops_file_helper_library_basename
|
|
226
|
-
basename.sub_ext(".rb")
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
# "/home/david/sync/projects/ops/ops/core/host/info.ops" => "/home/david/sync/projects/ops/ops/core/host/info"
|
|
230
|
-
def ops_file_helper_library_directory
|
|
231
|
-
File.join(dirname, basename)
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
# "/home/david/sync/projects/ops/ops/core/host/info.ops" => "/home/david/sync/projects/ops/ops/core/host/info.rb"
|
|
235
|
-
def ops_file_sibling_helper_library_file
|
|
236
|
-
"#{ops_file_helper_library_directory}.rb"
|
|
237
|
-
end
|
|
238
|
-
|
|
239
226
|
end
|
|
240
227
|
end
|
|
@@ -20,6 +20,9 @@ module OpsWalrus
|
|
|
20
20
|
def [](index, *args, **kwargs, &block)
|
|
21
21
|
@obj.method(:[]).call(index, *args, **kwargs, &block)
|
|
22
22
|
end
|
|
23
|
+
def respond_to_missing?(method, *)
|
|
24
|
+
@obj.is_a?(Hash) && @obj.respond_to?(method)
|
|
25
|
+
end
|
|
23
26
|
def method_missing(name, *args, **kwargs, &block)
|
|
24
27
|
case @obj
|
|
25
28
|
when Array
|
|
@@ -214,6 +217,9 @@ module OpsWalrus
|
|
|
214
217
|
end
|
|
215
218
|
end
|
|
216
219
|
|
|
220
|
+
# currently, import may only be used to import a package that is referenced in the script's package file
|
|
221
|
+
# I may decide to extend this to work with dynamic package references
|
|
222
|
+
#
|
|
217
223
|
# local_package_name is the local package name defined for the package dependency that is attempting to be referenced
|
|
218
224
|
def import(local_package_name)
|
|
219
225
|
local_package_name = local_package_name.to_s
|
|
@@ -254,17 +260,18 @@ module OpsWalrus
|
|
|
254
260
|
cmd = block.call if block
|
|
255
261
|
cmd ||= desc_or_cmd
|
|
256
262
|
|
|
263
|
+
cmd = WalrusLang.render(cmd, block.binding) if block && cmd =~ /{{.*}}/
|
|
264
|
+
|
|
265
|
+
#cmd = Shellwords.escape(cmd)
|
|
266
|
+
|
|
257
267
|
# puts "shell! self: #{self.inspect}"
|
|
258
268
|
|
|
259
269
|
print "[#{@runtime_env.local_hostname}] "
|
|
260
270
|
print "#{description}: " if description
|
|
261
271
|
puts cmd
|
|
262
272
|
|
|
263
|
-
cmd = WalrusLang.render(cmd, block.binding) if block && cmd =~ /{{.*}}/
|
|
264
273
|
return unless cmd && !cmd.strip.empty?
|
|
265
274
|
|
|
266
|
-
#cmd = Shellwords.escape(cmd)
|
|
267
|
-
|
|
268
275
|
sudo_password = @runtime_env.sudo_password
|
|
269
276
|
sudo_password &&= sudo_password.gsub(/\n+$/,'') # remove trailing newlines from sudo_password
|
|
270
277
|
|
|
@@ -5,6 +5,7 @@ require_relative 'bundler'
|
|
|
5
5
|
|
|
6
6
|
module OpsWalrus
|
|
7
7
|
|
|
8
|
+
# these are static package references defined ahead of time in the package file
|
|
8
9
|
class PackageReference
|
|
9
10
|
attr_accessor :local_name
|
|
10
11
|
attr_accessor :package_uri
|
|
@@ -23,7 +24,7 @@ module OpsWalrus
|
|
|
23
24
|
path.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "�").strip.tr("\u{202E}%$|:;/\t\r\n\\", "-")
|
|
24
25
|
end
|
|
25
26
|
|
|
26
|
-
# important: the
|
|
27
|
+
# important: the import_resolution_dirname implemented as the local_name is critical because Bundler#download_package downloads
|
|
27
28
|
# package dependencies to the name that this method returns, which must match the package reference's local name
|
|
28
29
|
# so that later, when the package is being looked up on the load path (in LoadPath#resolve_import_reference),
|
|
29
30
|
# the package reference's referenced git repo or file path may not exist or be available, and so the package
|
|
@@ -32,12 +33,20 @@ module OpsWalrus
|
|
|
32
33
|
# If this implementation changes, then Bundler#download_package and LoadPath#resolve_import_reference must also
|
|
33
34
|
# change in order for the three things to reconcile with respect to one another, since all three bits of logic are
|
|
34
35
|
# what make bundling package dependencies and loading them function properly.
|
|
35
|
-
def
|
|
36
|
+
def import_resolution_dirname
|
|
36
37
|
local_name
|
|
37
38
|
end
|
|
38
39
|
|
|
39
40
|
end
|
|
40
41
|
|
|
42
|
+
# these are dynamic package references defined at runtime when an OpsFile's imports are being evaluated.
|
|
43
|
+
# this will usually be the case when an ops file does not belong to a package
|
|
44
|
+
class DynamicPackageReference < PackageReference
|
|
45
|
+
def import_resolution_dirname
|
|
46
|
+
sanitized_package_uri
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
41
50
|
class PackageFile
|
|
42
51
|
attr_accessor :package_file_path
|
|
43
52
|
attr_accessor :yaml
|
|
@@ -32,6 +32,14 @@ module OpsWalrus
|
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
class DynamicPackageImportReference < ImportReference
|
|
36
|
+
attr_accessor :package_reference
|
|
37
|
+
def initialize(local_name, package_reference)
|
|
38
|
+
super(local_name)
|
|
39
|
+
@package_reference = package_reference
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
35
43
|
class OpsFileReference < ImportReference
|
|
36
44
|
attr_accessor :ops_file_path
|
|
37
45
|
def initialize(local_name, ops_file_path)
|
|
@@ -86,6 +94,8 @@ module OpsWalrus
|
|
|
86
94
|
@dir = dir
|
|
87
95
|
@root_namespace = build_symbol_resolution_tree(@dir)
|
|
88
96
|
@path_map = build_path_map(@root_namespace)
|
|
97
|
+
|
|
98
|
+
@dynamic_package_additions_memo = {}
|
|
89
99
|
end
|
|
90
100
|
|
|
91
101
|
# returns a tree of Namespace -> {Namespace* -> {Namespace* -> ..., OpsFile*}, OpsFile*}
|
|
@@ -129,6 +139,19 @@ module OpsWalrus
|
|
|
129
139
|
path_map
|
|
130
140
|
end
|
|
131
141
|
|
|
142
|
+
def dynamically_add_new_package_dir(new_package_dir)
|
|
143
|
+
# patch the symbol resolution (namespace) tree
|
|
144
|
+
dir_basename = new_package_dir.basename
|
|
145
|
+
unless @root_namespace.resolve_symbol(dir_basename)
|
|
146
|
+
new_child_namespace = build_symbol_resolution_tree(new_package_dir)
|
|
147
|
+
@root_namespace.add(dir_basename, new_child_namespace)
|
|
148
|
+
|
|
149
|
+
# patch the path map
|
|
150
|
+
new_partial_path_map = build_path_map(new_child_namespace)
|
|
151
|
+
@path_map.merge!(new_partial_path_map)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
132
155
|
# returns a Namespace
|
|
133
156
|
def lookup_namespace(ops_file)
|
|
134
157
|
@path_map[ops_file.dirname]
|
|
@@ -144,7 +167,16 @@ module OpsWalrus
|
|
|
144
167
|
case import_reference
|
|
145
168
|
when PackageDependencyReference
|
|
146
169
|
# puts "root namespace: #{@root_namespace.symbol_table}"
|
|
147
|
-
@root_namespace.resolve_symbol(import_reference.package_reference.
|
|
170
|
+
@root_namespace.resolve_symbol(import_reference.package_reference.import_resolution_dirname) # returns the Namespace associated with the bundled package dirname (i.e. the local name)
|
|
171
|
+
when DynamicPackageImportReference
|
|
172
|
+
dynamic_package_reference = import_reference.package_reference
|
|
173
|
+
@dynamic_package_additions_memo[dynamic_package_reference] ||= begin
|
|
174
|
+
# puts "Downloading dynamic package: #{dynamic_package_reference.inspect}"
|
|
175
|
+
dynamically_added_package_dir = @runtime_env.app.bundler.download_git_package(dynamic_package_reference.package_uri, dynamic_package_reference.version)
|
|
176
|
+
dynamically_add_new_package_dir(dynamically_added_package_dir)
|
|
177
|
+
dynamically_added_package_dir
|
|
178
|
+
end
|
|
179
|
+
@root_namespace.resolve_symbol(import_reference.package_reference.import_resolution_dirname) # returns the Namespace associated with the bundled package dirname (i.e. the sanitized package uri)
|
|
148
180
|
when DirectoryReference
|
|
149
181
|
@path_map[import_reference.dirname]
|
|
150
182
|
when OpsFileReference
|
|
@@ -244,7 +276,7 @@ module OpsWalrus
|
|
|
244
276
|
case import_reference
|
|
245
277
|
|
|
246
278
|
# 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
|
|
247
|
-
when PackageDependencyReference
|
|
279
|
+
when PackageDependencyReference, DynamicPackageImportReference
|
|
248
280
|
@bundle_load_path.resolve_import_reference(origin_ops_file, import_reference)
|
|
249
281
|
|
|
250
282
|
# we know we're dealing with a directory reference or OpsFile reference outside of the bundle dir, so we want to do the lookup in the app load path
|
data/lib/opswalrus/version.rb
CHANGED
|
@@ -57,7 +57,7 @@ module WalrusLang
|
|
|
57
57
|
|
|
58
58
|
# binding_obj : Binding | Hash
|
|
59
59
|
def self.render(template, binding_obj)
|
|
60
|
-
binding_obj = binding_obj.to_binding if binding_obj.
|
|
60
|
+
binding_obj = binding_obj.to_binding if binding_obj.respond_to?(:to_binding)
|
|
61
61
|
ast = WalrusLang::Parser.parse(template)
|
|
62
62
|
ast.render(binding_obj)
|
|
63
63
|
end
|
data/opswalrus.gemspec
CHANGED
|
@@ -11,10 +11,10 @@ Gem::Specification.new do |spec|
|
|
|
11
11
|
spec.summary = "opswalrus is a tool that runs scripts against a fleet of hosts"
|
|
12
12
|
spec.description = "opswalrus is a tool that runs scripts against a fleet of hosts hosts. It's kind of like Ansible, but aims to be simpler to use."
|
|
13
13
|
spec.homepage = "https://github.com/opswalrus/opswalrus"
|
|
14
|
-
spec.license =
|
|
14
|
+
spec.license = "EPL-2.0"
|
|
15
15
|
spec.required_ruby_version = ">= 2.6.0"
|
|
16
16
|
|
|
17
|
-
# spec.metadata["allowed_push_host"] = "TODO: Set to your gem server
|
|
17
|
+
# spec.metadata["allowed_push_host"] = "TODO: Set to your gem server - https://example.com"
|
|
18
18
|
|
|
19
19
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
20
20
|
spec.metadata["source_code_uri"] = "https://github.com/opswalrus/opswalrus"
|
|
@@ -33,13 +33,13 @@ Gem::Specification.new do |spec|
|
|
|
33
33
|
|
|
34
34
|
# gem dependencies
|
|
35
35
|
spec.add_dependency "citrus", "~> 3.0"
|
|
36
|
-
spec.add_dependency "gli",
|
|
37
|
-
spec.add_dependency "git",
|
|
38
|
-
spec.add_dependency "rubyzip",
|
|
36
|
+
spec.add_dependency "gli", "~> 2.21"
|
|
37
|
+
spec.add_dependency "git", "~> 1.18"
|
|
38
|
+
spec.add_dependency "rubyzip", "~> 2.3"
|
|
39
39
|
|
|
40
|
-
spec.add_dependency "bcrypt_pbkdf",
|
|
41
|
-
spec.add_dependency "ed25519",
|
|
42
|
-
spec.add_dependency "sshkit",
|
|
40
|
+
spec.add_dependency "bcrypt_pbkdf", "~> 1.1"
|
|
41
|
+
spec.add_dependency "ed25519", "~> 1.3"
|
|
42
|
+
spec.add_dependency "sshkit", "~> 1.21"
|
|
43
43
|
|
|
44
44
|
# For more information and examples about making a new gem, check out our
|
|
45
45
|
# guide at: https://bundler.io/guides/creating_gem.html
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
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.6
|
|
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-08-
|
|
11
|
+
date: 2023-08-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: citrus
|
|
@@ -124,15 +124,14 @@ files:
|
|
|
124
124
|
- LICENSE
|
|
125
125
|
- README.md
|
|
126
126
|
- Rakefile
|
|
127
|
+
- build.ops
|
|
127
128
|
- exe/ops
|
|
128
129
|
- lib/opswalrus.rb
|
|
129
130
|
- lib/opswalrus/app.rb
|
|
130
131
|
- lib/opswalrus/bootstrap.sh
|
|
131
|
-
- lib/opswalrus/bootstrap_linux_host1.sh
|
|
132
|
-
- lib/opswalrus/bootstrap_linux_host2.sh
|
|
133
|
-
- lib/opswalrus/bootstrap_linux_host3.sh
|
|
134
132
|
- lib/opswalrus/bundler.rb
|
|
135
133
|
- lib/opswalrus/cli.rb
|
|
134
|
+
- lib/opswalrus/git.rb
|
|
136
135
|
- lib/opswalrus/host.rb
|
|
137
136
|
- lib/opswalrus/hosts_file.rb
|
|
138
137
|
- lib/opswalrus/interaction_handlers.rb
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
|
|
3
|
-
# update package list
|
|
4
|
-
sudo apt update -y
|
|
5
|
-
|
|
6
|
-
# update OS
|
|
7
|
-
# sudo DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confold" -o Dpkg::Options::="--force-confdef" dist-upgrade -q -y --allow-downgrades --allow-remove-essential --allow-change-held-packages
|
|
8
|
-
|
|
9
|
-
if [ -f /var/run/reboot-required ]; then
|
|
10
|
-
echo 'A system reboot is required!'
|
|
11
|
-
sudo reboot
|
|
12
|
-
fi
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
|
|
3
|
-
# there are probably some services that need restarting because they're using old libraries, so we'll just do the easy thing and reboot
|
|
4
|
-
sudo DEBIAN_FRONTEND=noninteractive apt install -yq needrestart
|
|
5
|
-
|
|
6
|
-
# install basic development tools
|
|
7
|
-
sudo DEBIAN_FRONTEND=noninteractive apt install -yq build-essential
|
|
8
|
-
|
|
9
|
-
# install ruby dependencies
|
|
10
|
-
sudo DEBIAN_FRONTEND=noninteractive apt install -yq autoconf patch rustc libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libgmp-dev libncurses5-dev libffi-dev libgdbm6 libgdbm-dev libdb-dev uuid-dev
|
|
11
|
-
|
|
12
|
-
# restart services that need it
|
|
13
|
-
sudo needrestart -q -r a
|
|
14
|
-
|
|
15
|
-
# vagrant@ubuntu-jammy:~$ sudo needrestart -q -r a
|
|
16
|
-
# systemctl restart unattended-upgrades.service
|
|
17
|
-
|
|
18
|
-
# vagrant@ubuntu-jammy:~$ sudo needrestart -r l
|
|
19
|
-
# Scanning processes...
|
|
20
|
-
# Scanning candidates...
|
|
21
|
-
# Scanning linux images...
|
|
22
|
-
#
|
|
23
|
-
# Running kernel seems to be up-to-date.
|
|
24
|
-
#
|
|
25
|
-
# Services to be restarted:
|
|
26
|
-
#
|
|
27
|
-
# Service restarts being deferred:
|
|
28
|
-
# systemctl restart unattended-upgrades.service
|
|
29
|
-
#
|
|
30
|
-
# No containers need to be restarted.
|
|
31
|
-
#
|
|
32
|
-
# No user sessions are running outdated binaries.
|
|
33
|
-
#
|
|
34
|
-
# No VM guests are running outdated hypervisor (qemu) binaries on this host.
|
|
35
|
-
|
|
36
|
-
# reboot just in case
|
|
37
|
-
# sudo reboot
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
|
|
3
|
-
# install homebrew
|
|
4
|
-
NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
|
5
|
-
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
|
|
6
|
-
|
|
7
|
-
# install gcc, rust, ruby
|
|
8
|
-
brew install gcc
|
|
9
|
-
brew install rust
|
|
10
|
-
brew install ruby
|
|
11
|
-
|
|
12
|
-
# brew install rtx
|
|
13
|
-
# eval "$(rtx activate bash)" # register a shell hook
|
|
14
|
-
# rtx use -g ruby@3.2 # install ruby via rtx
|
|
15
|
-
|
|
16
|
-
# download frum for ruby version management
|
|
17
|
-
# curl -L -o frum.tar.gz https://github.com/TaKO8Ki/frum/releases/download/v0.1.2/frum-v0.1.2-x86_64-unknown-linux-musl.tar.gz
|
|
18
|
-
# tar -zxf frum.tar.gz
|
|
19
|
-
# mv frum-v0.1.2-x86_64-unknown-linux-musl/frum ~/bin
|
|
20
|
-
# chmod 755 ~/bin/frum
|
|
21
|
-
|