loom-core 0.0.1 → 0.0.2
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/.gitignore +1 -0
- data/lib/loom/dsl.rb +2 -0
- data/lib/loom/host_spec.rb +11 -1
- data/lib/loom/mods/action_proxy.rb +6 -6
- data/lib/loom/mods/module.rb +10 -0
- data/lib/loom/pattern/result_reporter.rb +8 -0
- data/lib/loom/runner.rb +4 -3
- data/lib/loom/shell/core.rb +45 -3
- data/lib/loom/shell/harness/session.rb +168 -0
- data/lib/loom/version.rb +1 -1
- data/lib/loomext/coremods/all.rb +1 -0
- data/lib/loomext/coremods/exec.rb +13 -1
- data/lib/loomext/coremods/files.rb +64 -6
- data/lib/loomext/coremods/package/adapter.rb +8 -4
- data/lib/loomext/coremods/system.rb +52 -0
- data/lib/loomext/coremods/vm/vbox.rb +1 -1
- data/loom.gemspec +7 -2
- data/scripts/harness.sh +2 -1
- metadata +37 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fca395ca95a28376331f0163c1748fcd3eb083c2f6d677627edb04a795c5b858
|
4
|
+
data.tar.gz: 29fc16366271bfe121eb57b7315daa87a6d8017210d41a061cdecc6665bb26bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 291da2a4d0b793019dd65c511dfc1caa113a3a12c3a7f2ab12e9b991a8e5ce09b4fba97fb150b15b01edd6c67f0ea0536f16fc00384bf80c6ec54f9b21c9c1c2
|
7
|
+
data.tar.gz: 0b18248b8cf5583a48fb3844c39e15b0db216ad6c6ab62a160b5013d05e6f8931bbf165c35c7e107ce3de28a88e7227662480fd263f11031fb5356da2729c5a6
|
data/.gitignore
CHANGED
data/lib/loom/dsl.rb
CHANGED
@@ -31,6 +31,7 @@ module Loom
|
|
31
31
|
sshkit_backend = self
|
32
32
|
|
33
33
|
begin
|
34
|
+
# TODO: document the reciever of this yield
|
34
35
|
yield sshkit_backend, host_spec
|
35
36
|
rescue SocketError => e
|
36
37
|
Loom.log.error "error connecting to host => #{host_spec.hostname}"
|
@@ -53,6 +54,7 @@ module Loom
|
|
53
54
|
end
|
54
55
|
end
|
55
56
|
|
57
|
+
# TODO: Why did I make this "shadow" class?
|
56
58
|
class SSHKitDSLShadow
|
57
59
|
extend SSHKit::DSL
|
58
60
|
end
|
data/lib/loom/host_spec.rb
CHANGED
@@ -11,6 +11,8 @@ module Loom
|
|
11
11
|
attr_accessor :disabled
|
12
12
|
attr_reader :sshkit_host
|
13
13
|
|
14
|
+
# TODO: change this to take an sshkit_host and make parse public. Stop calling parse from the
|
15
|
+
# ctor.
|
14
16
|
def initialize(host_string)
|
15
17
|
@sshkit_host = parse host_string
|
16
18
|
end
|
@@ -26,7 +28,15 @@ module Loom
|
|
26
28
|
|
27
29
|
private
|
28
30
|
def parse(host_string)
|
29
|
-
SSHKit::Host.new host_string
|
31
|
+
host = SSHKit::Host.new host_string
|
32
|
+
host.ssh_options = {
|
33
|
+
:auth_methods => ['publickey'],
|
34
|
+
:keys => ["~/.ssh/id_esd25519_2"],
|
35
|
+
# :verbose => :debug,
|
36
|
+
}
|
37
|
+
Loom.log.debug1(self) { "parsing hoststring[#{host_string}] => #{host}" }
|
38
|
+
Loom.log.debug1(self) { "netssh options for host[#{host}] => #{host.netssh_options}" }
|
39
|
+
host
|
30
40
|
end
|
31
41
|
end
|
32
42
|
end
|
@@ -41,11 +41,11 @@ module Loom::Mods
|
|
41
41
|
bound_action_name = tuple[1]
|
42
42
|
|
43
43
|
define_method public_action_name do |*args, &block|
|
44
|
-
# TODO: Effectively this is the API for all mods, but it's
|
45
|
-
# here in the middle of nowhere. Add documentation - or make
|
46
|
-
# easier to read.
|
47
|
-
Loom.log.
|
48
|
-
"proxy to mod #{
|
44
|
+
# TODO[P0]: Effectively this is the API for all mods, but it's
|
45
|
+
# burried here in the middle of nowhere. Add documentation - or make
|
46
|
+
# it easier to read.
|
47
|
+
Loom.log.debug(self) do
|
48
|
+
"proxy to mod action: #{public_action_name} => #{bound_action_name}, #{@mod}"
|
49
49
|
end
|
50
50
|
|
51
51
|
@mod.send bound_action_name, *args, &block
|
@@ -89,7 +89,7 @@ module Loom::Mods
|
|
89
89
|
def add_action(action_name, bound_method_name, namespace=nil)
|
90
90
|
if namespace.nil?
|
91
91
|
tuple = [action_name, bound_method_name]
|
92
|
-
@action_tuples << tuple unless namespace
|
92
|
+
@action_tuples << tuple unless namespace
|
93
93
|
else
|
94
94
|
# Adds an action name to a nested ActionMap
|
95
95
|
add_namespace(namespace).add_action action_name, bound_method_name
|
data/lib/loom/mods/module.rb
CHANGED
@@ -40,6 +40,9 @@ module Loom::Mods
|
|
40
40
|
else
|
41
41
|
Loom.log.debug3(self) { "initing action => #{args}" }
|
42
42
|
init_action *args, &pattern_block
|
43
|
+
|
44
|
+
# TODO: ooohhh... the action_proxy code path is fucking
|
45
|
+
# crazy. ActionProxy needs some documentation.
|
43
46
|
action_proxy
|
44
47
|
end
|
45
48
|
end
|
@@ -50,6 +53,8 @@ module Loom::Mods
|
|
50
53
|
Loom.log.debug2(self) { "registered mod => #{name}" }
|
51
54
|
|
52
55
|
if block_given?
|
56
|
+
# TODO: i forget what mod_blocks are. read through, remember, and
|
57
|
+
# document them.
|
53
58
|
Loom.log.debug2(self) { "acting as mod_block => #{name}:#{block}" }
|
54
59
|
define_method :mod_block, &block
|
55
60
|
end
|
@@ -83,6 +88,11 @@ module Loom::Mods
|
|
83
88
|
def bind_action(action_name, unbound_method, namespace=nil)
|
84
89
|
bound_method_name = [namespace, action_name].compact.join '_'
|
85
90
|
|
91
|
+
# TODO: document why the `define_method` calls in class only operate on
|
92
|
+
# the single mod instance, rather than adding each "bound_method_name"
|
93
|
+
# (e.g.) to each instance of Module. (actually I think it's because this
|
94
|
+
# is executing from the subclass (via import_actions), so it's only that
|
95
|
+
# class). in any case, add more docs and code pointers.
|
86
96
|
define_method bound_method_name do |*args, &block|
|
87
97
|
Loom.log.debug1(self) { "exec mod action #{self.class}##{bound_method_name}" }
|
88
98
|
|
@@ -44,6 +44,14 @@ module Loom::Pattern
|
|
44
44
|
report << "Completed in: %01.3fs" % @delta_t
|
45
45
|
|
46
46
|
cmds.find_all { |cmd| !cmd.is_test }.each do |cmd|
|
47
|
+
# TODO: this is a bit confusing for the user... when you cat a file from
|
48
|
+
# a loom pattern, the output of a command isn't visible unless -V is
|
49
|
+
# specified... not sure what to do here. I don't want to see the output
|
50
|
+
# of every command, and I don't really want to pipe more info through
|
51
|
+
# the `@shell_session.command_results` (e.g. should_report_result:)?
|
52
|
+
#
|
53
|
+
# Although.. maybe that's the better API? then the logic here can be
|
54
|
+
# moved and strategized per command/result/shell.
|
47
55
|
if !cmd.success? || @loom_config.run_verbose
|
48
56
|
report.concat generate_cmd_report(cmd)
|
49
57
|
end
|
data/lib/loom/runner.rb
CHANGED
@@ -165,8 +165,8 @@ module Loom
|
|
165
165
|
result_reporter = Loom::Pattern::ResultReporter.new(
|
166
166
|
@loom_config, pattern_ref.slug, hostname, shell_session)
|
167
167
|
|
168
|
-
# TODO: This is a crappy mechanism for tracking errors
|
169
|
-
# exception thrown inside of Shell when a command fails and pattern
|
168
|
+
# TODO: This is a crappy mechanism for tracking errors (hency my crappy error reporting :( ),
|
169
|
+
# there should be an exception thrown inside of Shell when a command fails and pattern
|
170
170
|
# execution should stop. All errors should come from exceptions.
|
171
171
|
run_failure = []
|
172
172
|
begin
|
@@ -175,7 +175,8 @@ module Loom
|
|
175
175
|
Loom.log.debug e.backtrace.join "\n\t"
|
176
176
|
run_failure << e
|
177
177
|
ensure
|
178
|
-
# TODO: this prints out [Result: OK] even if an exception is raised
|
178
|
+
# TODO[P0]: this prints out [Result: OK] even if an exception is raised... this is really
|
179
|
+
# annoying.
|
179
180
|
result_reporter.write_report
|
180
181
|
|
181
182
|
# TODO: this is not the correct error condition.
|
data/lib/loom/shell/core.rb
CHANGED
@@ -24,6 +24,10 @@ module Loom::Shell
|
|
24
24
|
|
25
25
|
attr_reader :session, :shell_api, :mod_loader, :dry_run
|
26
26
|
|
27
|
+
def is_sudo?
|
28
|
+
!@sudo_users.empty?
|
29
|
+
end
|
30
|
+
|
27
31
|
def local
|
28
32
|
@local ||= LocalShell.new @mod_loader, @session, @dry_run
|
29
33
|
end
|
@@ -96,6 +100,35 @@ module Loom::Shell
|
|
96
100
|
end
|
97
101
|
end
|
98
102
|
|
103
|
+
# def sudo(user=nil, *sudo_cmd, &block)
|
104
|
+
# # I'm trying to work around crappy double escaping issues caused by
|
105
|
+
# # sudo_legacy... but I'm failing
|
106
|
+
# if block_given?
|
107
|
+
# # TODO: this shit is broken with these errors:
|
108
|
+
# # $ ... 'You cannot switch to user '' using sudo, please check the sudoers file'
|
109
|
+
# #
|
110
|
+
# # Loom.log.debug3(self) { "sudo with SSHKit::Backend+as+" }
|
111
|
+
# # @sshkit_backend.as user, &block
|
112
|
+
|
113
|
+
# # Aside from probably being unsafe... this implementation leads to a
|
114
|
+
# # hung terminal after switching to root.... sigh....
|
115
|
+
# #
|
116
|
+
# # Loom.log.warn(self) { "fix sudo... this doesn't seem safe" }
|
117
|
+
# # execute "sudo", "su", user
|
118
|
+
# # begin
|
119
|
+
# # yield
|
120
|
+
# # ensure
|
121
|
+
# # execute "exit"
|
122
|
+
# # end
|
123
|
+
|
124
|
+
# Loom.log.debug3(self) { "sudo legacy... the other way" }
|
125
|
+
# sudo_stack_and_wrap(user, *sudo_cmd, &block)
|
126
|
+
# else
|
127
|
+
# Loom.log.debug3(self) { "sudo legacy" }
|
128
|
+
# sudo_stack_and_wrap(user, *sudo_cmd)
|
129
|
+
# end
|
130
|
+
# end
|
131
|
+
|
99
132
|
def cd(path, &block)
|
100
133
|
Loom.log.debug1(self) { "cd => #{path} #{block}" }
|
101
134
|
|
@@ -124,6 +157,10 @@ module Loom::Shell
|
|
124
157
|
execute cmd
|
125
158
|
end
|
126
159
|
|
160
|
+
def upload(local_path, remote_path)
|
161
|
+
@sshkit_backend.upload! local_path, remote_path
|
162
|
+
end
|
163
|
+
|
127
164
|
def execute(*cmd_parts, is_test: false, **cmd_opts)
|
128
165
|
cmd_parts.compact!
|
129
166
|
raise "empty command passed to execute" if cmd_parts.empty?
|
@@ -159,9 +196,10 @@ module Loom::Shell
|
|
159
196
|
"[%s]:$ %s" % [prompt_label, output]
|
160
197
|
end
|
161
198
|
|
162
|
-
def execute_internal(*cmd_parts,
|
199
|
+
def execute_internal(*cmd_parts, pipe_to: [])
|
163
200
|
primary_cmd = create_command *cmd_parts
|
164
|
-
|
201
|
+
# TODO: where is piped_cmds used?
|
202
|
+
piped_cmds = pipe_to.map { |cmd_parts| CmdWrapper.new *cmd_parts }
|
165
203
|
|
166
204
|
cmd = CmdPipeline.new([primary_cmd].concat(piped_cmds)).to_s
|
167
205
|
# Tests if the command looks like "echo\ hi", the trailing slash after
|
@@ -187,9 +225,13 @@ module Loom::Shell
|
|
187
225
|
# @return [String|Loom::Shell::CmdWrapper]
|
188
226
|
def create_command(*cmd_parts)
|
189
227
|
cmd_wrapper = if cmd_parts.is_a? CmdWrapper
|
228
|
+
Loom.log.debug3(self) { "existing cmd from args => #{cmd_parts}" }
|
190
229
|
cmd_parts
|
230
|
+
elsif cmd_parts[0].is_a? CmdWrapper
|
231
|
+
Loom.log.debug3(self) { "existing cmd from args[0] => #{cmd_parts[0]}" }
|
232
|
+
cmd_parts[0]
|
191
233
|
else
|
192
|
-
Loom.log.debug3(self) { "new cmd from
|
234
|
+
Loom.log.debug3(self) { "new cmd from args => #{cmd_parts}" }
|
193
235
|
CmdWrapper.new *cmd_parts
|
194
236
|
end
|
195
237
|
|
@@ -0,0 +1,168 @@
|
|
1
|
+
# 10/21/2018: I resurrected this from an auto save file from a LOONNGGGG time
|
2
|
+
# ago., not really sure exactly what the original purpose is, but it has the
|
3
|
+
# code for uploading the harness script. I believe this was working at one
|
4
|
+
# point, but that was long ago
|
5
|
+
|
6
|
+
module Loom
|
7
|
+
|
8
|
+
# Ensures loom is setup to run on the remote.
|
9
|
+
# - creates the boostrap loom directory
|
10
|
+
# - uploads the harness
|
11
|
+
# - creates a directoy for loom execution logging
|
12
|
+
class HostSession
|
13
|
+
|
14
|
+
HISTORY_FILE = "command.log"
|
15
|
+
|
16
|
+
# @param [Loom::HostSpec] host_spec
|
17
|
+
# @param [Loom::Config] loom_config
|
18
|
+
# @param [SSHKit::Backend::Abstract] sshkit_backend
|
19
|
+
def initialize(host_spec, loom_config, sshkit_backend)
|
20
|
+
@host_spec = host_spec
|
21
|
+
@sshkit_backend = sshkit_backend
|
22
|
+
@loom_config = loom_config
|
23
|
+
|
24
|
+
@remote_loom_root = loom_config.bootstrap_loom_root
|
25
|
+
@loom_user = loom_config.loom_user
|
26
|
+
|
27
|
+
@session_name = "session.%d" % Time.now.to_i
|
28
|
+
@disabled = false
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_reader :session_name
|
32
|
+
|
33
|
+
def bootstrap
|
34
|
+
ensure_loom_remote_dirs
|
35
|
+
ensure_harness_uploaded
|
36
|
+
log_to_command_history "begin loom execution"
|
37
|
+
log_to_command_history "start: " + Time.now.to_i.to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
def disabled?
|
41
|
+
@disabled
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param [Loom::Pattern::Reference] pattern_ref
|
45
|
+
# @return [Loom::Pattern::ExecResult]
|
46
|
+
def execute_pattern(pattern_ref)
|
47
|
+
shell = create_shell
|
48
|
+
|
49
|
+
shell_session = shell.session
|
50
|
+
result_reporter = Loom::Pattern::ResultReporter.new(
|
51
|
+
@loom_config, pattern_ref.slug, hostname, shell_session)
|
52
|
+
|
53
|
+
# TODO: This is a crappy mechanism for tracking errors, there should be an
|
54
|
+
# exception thrown inside of Shell when a command fails and pattern
|
55
|
+
# execution should stop. All errors should come from exceptions.
|
56
|
+
run_failure = []
|
57
|
+
begin
|
58
|
+
fact_set = collect_facts_for_host
|
59
|
+
pattern_ref.call(shell.shell_api, fact_set)
|
60
|
+
rescue => e
|
61
|
+
handle_host_failure e
|
62
|
+
ensure
|
63
|
+
# TODO: this prints out [Result: OK] even if an exception is raised
|
64
|
+
result_reporter.write_report
|
65
|
+
|
66
|
+
# TODO: this is not the correct error condition.
|
67
|
+
unless shell_session.success?
|
68
|
+
run_failure << result_reporter.failure_summary
|
69
|
+
handle_host_failure result_reporter.failure_summary
|
70
|
+
end
|
71
|
+
@result_reports << result_reporter
|
72
|
+
@run_failures << run_failure unless run_failure.empty?
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def handle_host_failure(error_or_message=nil)
|
77
|
+
Loom.log.debug { "handling host failure => #{hostname}" }
|
78
|
+
if error_or_message.respond_to? :backtrace
|
79
|
+
Loom.log.debug { e.backtrace.join "\n\t" }
|
80
|
+
end
|
81
|
+
|
82
|
+
message = if error_or_message.respond_to? :message
|
83
|
+
error_or_message.message
|
84
|
+
else
|
85
|
+
error_or_message
|
86
|
+
end
|
87
|
+
|
88
|
+
failure_strategy = @loom_config.run_failure_strategy.to_sym
|
89
|
+
case failure_strategy
|
90
|
+
when :exclude_host
|
91
|
+
Loom.log.warn "disabling host due to failure => #{message}"
|
92
|
+
@disabled = true
|
93
|
+
when :fail_fast
|
94
|
+
Loom.log.error "fail fast host failure => #{message}"
|
95
|
+
raise FailFastExecutionError, message
|
96
|
+
when :cowboy
|
97
|
+
# This is mostly for testing, don't use in prod.
|
98
|
+
Loom.log.warn "cowboy failure, wooohooo => #{message}"
|
99
|
+
else
|
100
|
+
raise ConfigError, "unknown failure_strategy: #{failure_stratgy}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
# @return [String]
|
107
|
+
def script_path
|
108
|
+
File.join @remote_loom_root, "scripts"
|
109
|
+
end
|
110
|
+
|
111
|
+
# @return [String]
|
112
|
+
def session_path
|
113
|
+
File.join @remote_loom_root, "run", @session_name
|
114
|
+
end
|
115
|
+
|
116
|
+
# @return [String]
|
117
|
+
def history_file
|
118
|
+
File.join session_name, HISTORY_FILE
|
119
|
+
end
|
120
|
+
|
121
|
+
def ensure_loom_remote_dirs
|
122
|
+
@sshkit_backend.as user: :root do
|
123
|
+
@sshkit_backend.execute :mkdir, '-p', @remote_loom_root
|
124
|
+
@sshkit_backend.execute :mkdir, '-p', script_path
|
125
|
+
@sshkit_backend.execute :mkdir, '-p', session_path
|
126
|
+
|
127
|
+
chown_opts = "-R %s:%s %s" % [@loom_user, @loom_user, @remote_loom_root]
|
128
|
+
@sshkit_backend.execute :chown, chown_opts
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def ensure_harness_uploaded
|
133
|
+
@sshkit_backend.upload! Loom::Resource::HARNESS, script_path
|
134
|
+
end
|
135
|
+
|
136
|
+
def create_command_history
|
137
|
+
@sshkit_backend :touch, history_file
|
138
|
+
end
|
139
|
+
|
140
|
+
def write_to_command_history(text)
|
141
|
+
@sshkit_backend :cat, "<<EOS\n#{text}\nEOS", ">>", history_file
|
142
|
+
end
|
143
|
+
|
144
|
+
def log_to_command_history(text)
|
145
|
+
write_to_command_history(
|
146
|
+
"[%s] %s: #{text}" % [Time.now.utc.to_s, hostname])
|
147
|
+
end
|
148
|
+
|
149
|
+
# The naming inconsistency w/ the missing underscore in hostname (vs
|
150
|
+
# host_spec, host_session, etc...) is confusing, but that's *nix's fault.
|
151
|
+
def hostname
|
152
|
+
@host_spec.hostname
|
153
|
+
end
|
154
|
+
|
155
|
+
def collect_facts_for_host
|
156
|
+
Loom.log.info "collecting facts for host => #{hostname}"
|
157
|
+
Loom::Facts.fact_set(@host_spec, create_shell, @loom_config)
|
158
|
+
end
|
159
|
+
|
160
|
+
def create_shell
|
161
|
+
Loom::Shell.create create_mod_loader, @sshkit_backend
|
162
|
+
end
|
163
|
+
|
164
|
+
def create_mod_loader
|
165
|
+
Loom::Mods::ModLoader.new loom_config
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
data/lib/loom/version.rb
CHANGED
data/lib/loomext/coremods/all.rb
CHANGED
@@ -31,11 +31,17 @@ module LoomExt::CoreMods
|
|
31
31
|
end
|
32
32
|
|
33
33
|
class Sudo < Loom::Mods::Module
|
34
|
-
register_mod :sudo do |user:
|
34
|
+
register_mod :sudo do |user: :root, cmd: nil, &block|
|
35
35
|
shell.sudo user, cmd, &block
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
+
class SudoCheck < Loom::Mods::Module
|
40
|
+
register_mod :is_sudo? do
|
41
|
+
shell.is_sudo?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
39
45
|
class Test < Loom::Mods::Module
|
40
46
|
register_mod :test do |*cmd|
|
41
47
|
shell.test *cmd
|
@@ -47,4 +53,10 @@ module LoomExt::CoreMods
|
|
47
53
|
raise FailError, message
|
48
54
|
end
|
49
55
|
end
|
56
|
+
|
57
|
+
class Upload < Loom::Mods::Module
|
58
|
+
register_mod :upload do |local_path, remote_path|
|
59
|
+
shell.upload local_path, remote_path
|
60
|
+
end
|
61
|
+
end
|
50
62
|
end
|
@@ -3,6 +3,8 @@ module LoomExt::CoreMods
|
|
3
3
|
|
4
4
|
register_mod :files
|
5
5
|
|
6
|
+
# TODO: document loom file statements like:
|
7
|
+
# `loom.files("some", "different/paths").cat`
|
6
8
|
def init_action(paths)
|
7
9
|
@paths = [paths].flatten.compact
|
8
10
|
end
|
@@ -45,6 +47,12 @@ module LoomExt::CoreMods
|
|
45
47
|
end
|
46
48
|
end
|
47
49
|
|
50
|
+
def mv(new_path)
|
51
|
+
each_path do |p|
|
52
|
+
shell.capture :mv, p, new_path
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
48
56
|
def match?(pattern: /./)
|
49
57
|
all = true
|
50
58
|
each_path do |p|
|
@@ -80,20 +88,70 @@ module LoomExt::CoreMods
|
|
80
88
|
each_path :action => :mkdir, :flags => flags
|
81
89
|
end
|
82
90
|
|
91
|
+
def ensure_line(line, sudo: false)
|
92
|
+
if loom.is_sudo?
|
93
|
+
Loom.log.warn "do not use files.ensure_line in sudo due to poor command escaping" +
|
94
|
+
": use files.ensure_line and pass sudo: true"
|
95
|
+
end
|
96
|
+
|
97
|
+
each_path do |p|
|
98
|
+
file = shell.capture :cat, p
|
99
|
+
|
100
|
+
unless file.match(line)
|
101
|
+
if sudo
|
102
|
+
sudo_append(line)
|
103
|
+
else
|
104
|
+
append(line)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# this is a hack to accomodate append being f'd inside sudo blocks
|
111
|
+
def sudo_append(text="")
|
112
|
+
if text.index "\n"
|
113
|
+
Loom.log.warn "append lines individually until cmd escaping is fixed.... "
|
114
|
+
end
|
115
|
+
|
116
|
+
each_path do |p|
|
117
|
+
text.each_line do |line|
|
118
|
+
loom.x "/bin/echo", "-e", line, :pipe_to => [[:sudo, :tee, "-a", p]]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
83
123
|
def append(text="")
|
124
|
+
if text.index "\n"
|
125
|
+
Loom.log.warn "append lines individually until cmd escaping is fixed.... "
|
126
|
+
end
|
127
|
+
|
128
|
+
if loom.is_sudo?
|
129
|
+
Loom.log.warn "do not use files.append in sudo" +
|
130
|
+
": use files.sudo_append due to poor command escaping"
|
131
|
+
end
|
132
|
+
|
84
133
|
each_path do |p|
|
85
|
-
|
134
|
+
# TODO: this shit is broken when escaped in a sudo command. This is
|
135
|
+
# why I began work on the harness.
|
136
|
+
# $ cd /home/pi; sudo -u root -- /bin/sh -c "/bin/echo -e 192.168.1.190 rp0\''; '\'" | tee -a /etc/hosts
|
137
|
+
#
|
138
|
+
text.each_line do |line|
|
139
|
+
loom.x :"/bin/echo", "-e", line, :pipe_to => [[:tee, "-a", p]]
|
140
|
+
end
|
86
141
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
142
|
+
# TODO: fix this broken shit w/ the harness, CmdRedirect and
|
143
|
+
# CmdWrapper are dogshit. This was an escaping attempt before harness
|
144
|
+
# script.
|
145
|
+
#
|
146
|
+
# redirect = Loom::Shell::CmdRedirect.append_stdout p
|
147
|
+
# cmd = Loom::Shell::CmdWrapper.new(
|
148
|
+
# :"/bin/echo", "-e", text, redirect: redirect)
|
91
149
|
end
|
92
150
|
end
|
93
151
|
|
94
152
|
def write(text="")
|
95
153
|
each_path do |p|
|
96
|
-
loom.x :"/bin/echo", "-e", text, :
|
154
|
+
loom.x :"/bin/echo", "-e", text, :pipe_to => [[:tee, p]]
|
97
155
|
end
|
98
156
|
end
|
99
157
|
|
@@ -54,7 +54,8 @@ module LoomExt::CoreMods
|
|
54
54
|
class AptAdapter < DpkgAdapter
|
55
55
|
|
56
56
|
def install(pkg_name)
|
57
|
-
loom.
|
57
|
+
loom.x "apt-get", "-y", "install", pkg_name
|
58
|
+
# loom.net.with_net { loom << "echo apt-get install #{pkg_name}" }
|
58
59
|
end
|
59
60
|
|
60
61
|
def uninstall(pkg_name)
|
@@ -62,11 +63,14 @@ module LoomExt::CoreMods
|
|
62
63
|
end
|
63
64
|
|
64
65
|
def update_cache
|
65
|
-
loom.net.with_net { loom << "apt update" }
|
66
|
+
# loom.net.with_net { loom << "apt update" }
|
67
|
+
loom.x "apt", "-y", "update"
|
66
68
|
end
|
67
69
|
|
68
|
-
def upgrade(pkg_name)
|
69
|
-
loom.net.with_net { loom << "apt upgrade" }
|
70
|
+
def upgrade(pkg_name = nil)
|
71
|
+
# loom.net.with_net { loom << "apt upgrade" }
|
72
|
+
args = ["apt-get", "-y", "upgrade", pkg_name].compact
|
73
|
+
loom.x(*args)
|
70
74
|
end
|
71
75
|
end
|
72
76
|
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module LoomExt::CoreMods
|
2
|
+
|
3
|
+
class Systemd < Loom::Mods::Module
|
4
|
+
|
5
|
+
register_mod :systemd
|
6
|
+
|
7
|
+
def do_systemctl(action, *args)
|
8
|
+
shell.execute "systemctl", action, *args
|
9
|
+
end
|
10
|
+
|
11
|
+
module Actions
|
12
|
+
|
13
|
+
def is_loaded?(unit)
|
14
|
+
status(unit).match? /^\s+Loaded:\sloaded\s/
|
15
|
+
end
|
16
|
+
|
17
|
+
def is_active?(unit)
|
18
|
+
status(unit).match? /^\s+Active:\sactive\s/
|
19
|
+
end
|
20
|
+
|
21
|
+
def status(unit)
|
22
|
+
do_systemctl "status", unit
|
23
|
+
end
|
24
|
+
|
25
|
+
def enable(unit)
|
26
|
+
do_systemctl "enable", unit
|
27
|
+
end
|
28
|
+
|
29
|
+
def start(unit)
|
30
|
+
do_systemctl "start", unit
|
31
|
+
end
|
32
|
+
|
33
|
+
def disable(unit)
|
34
|
+
do_systemctl "disable", unit
|
35
|
+
end
|
36
|
+
|
37
|
+
def restart(unit)
|
38
|
+
do_systemctl "restart", unit
|
39
|
+
end
|
40
|
+
|
41
|
+
def stop(unit)
|
42
|
+
do_systemctl "stop", unit
|
43
|
+
end
|
44
|
+
|
45
|
+
def link(path)
|
46
|
+
do_systemctl "link", path
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
Systemd.import_actions Systemd::Actions
|
52
|
+
end
|
data/loom.gemspec
CHANGED
@@ -18,6 +18,7 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.add_dependency 'sshkit', '~> 1.11'
|
19
19
|
s.add_dependency 'commander', '~> 4.4'
|
20
20
|
|
21
|
+
# *** Obsolete, see new issue below ***
|
21
22
|
# Need net-ssh beta and its explicit requirements until for ed25519
|
22
23
|
# elliptic curve key support
|
23
24
|
# https://github.com/net-ssh/net-ssh/issues/214
|
@@ -26,9 +27,13 @@ Gem::Specification.new do |s|
|
|
26
27
|
# release due to net-scp gem dependencies.
|
27
28
|
# I can manually `gem install net-ssh --version 4.0.0.beta3` for now.
|
28
29
|
# s.add_dependency 'net-ssh', '>= 4.0.0.beta3'
|
29
|
-
s.add_dependency 'net-ssh', '>=
|
30
|
+
s.add_dependency 'net-ssh', '>= 5'
|
30
31
|
s.add_dependency 'rbnacl-libsodium', '1.0.10'
|
31
|
-
s.add_dependency 'bcrypt_pbkdf',
|
32
|
+
s.add_dependency 'bcrypt_pbkdf', ">= 1.0", "< 2.0"
|
33
|
+
|
34
|
+
# New net-ssh requiremetns for ed25519
|
35
|
+
# https://github.com/net-ssh/net-ssh/issues/565
|
36
|
+
s.add_dependency 'ed25519', '>=1.0', '<2.0'
|
32
37
|
|
33
38
|
s.add_development_dependency 'bundler', '~> 1.13'
|
34
39
|
s.add_development_dependency 'rake', '~> 11.3'
|
data/scripts/harness.sh
CHANGED
@@ -28,7 +28,8 @@
|
|
28
28
|
# EOS
|
29
29
|
#
|
30
30
|
# There are 2 different shells that the harness deals with. The
|
31
|
-
# harness shell, and the command shell.
|
31
|
+
# harness shell, and the command shell. The point being, to isolate
|
32
|
+
# each environment and be independent of the other.
|
32
33
|
#
|
33
34
|
# The harness shell is the shell used to run the harness script (this
|
34
35
|
# file). Only POSIX features are supported in the harness
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: loom-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Erick Johnson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-10-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sshkit
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '5'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '5'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rbnacl-libsodium
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -70,16 +70,42 @@ dependencies:
|
|
70
70
|
name: bcrypt_pbkdf
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- -
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.0'
|
76
|
+
- - "<"
|
74
77
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
78
|
+
version: '2.0'
|
76
79
|
type: :runtime
|
77
80
|
prerelease: false
|
78
81
|
version_requirements: !ruby/object:Gem::Requirement
|
79
82
|
requirements:
|
80
|
-
- -
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '1.0'
|
86
|
+
- - "<"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '2.0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: ed25519
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '1.0'
|
96
|
+
- - "<"
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '2.0'
|
99
|
+
type: :runtime
|
100
|
+
prerelease: false
|
101
|
+
version_requirements: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '1.0'
|
106
|
+
- - "<"
|
81
107
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
108
|
+
version: '2.0'
|
83
109
|
- !ruby/object:Gem::Dependency
|
84
110
|
name: bundler
|
85
111
|
requirement: !ruby/object:Gem::Requirement
|
@@ -213,6 +239,7 @@ files:
|
|
213
239
|
- lib/loom/shell/cmd_result.rb
|
214
240
|
- lib/loom/shell/cmd_wrapper.rb
|
215
241
|
- lib/loom/shell/core.rb
|
242
|
+
- lib/loom/shell/harness/session.rb
|
216
243
|
- lib/loom/shell/harness_blob.rb
|
217
244
|
- lib/loom/shell/harness_command_builder.rb
|
218
245
|
- lib/loom/shell/session.rb
|
@@ -229,6 +256,7 @@ files:
|
|
229
256
|
- lib/loomext/coremods/net.rb
|
230
257
|
- lib/loomext/coremods/package/adapter.rb
|
231
258
|
- lib/loomext/coremods/package/package.rb
|
259
|
+
- lib/loomext/coremods/system.rb
|
232
260
|
- lib/loomext/coremods/user.rb
|
233
261
|
- lib/loomext/coremods/vm.rb
|
234
262
|
- lib/loomext/coremods/vm/all.rb
|
@@ -268,7 +296,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
268
296
|
version: '0'
|
269
297
|
requirements: []
|
270
298
|
rubyforge_project:
|
271
|
-
rubygems_version: 2.7.
|
299
|
+
rubygems_version: 2.7.7
|
272
300
|
signing_key:
|
273
301
|
specification_version: 4
|
274
302
|
summary: Repeatable management of remote hosts over SSH
|