loom-core 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|