loom-core 0.0.1

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.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +99 -0
  6. data/Guardfile +54 -0
  7. data/Rakefile +6 -0
  8. data/bin/loom +185 -0
  9. data/lib/env/development.rb +1 -0
  10. data/lib/loom.rb +44 -0
  11. data/lib/loom/all.rb +20 -0
  12. data/lib/loom/config.rb +106 -0
  13. data/lib/loom/core_ext.rb +37 -0
  14. data/lib/loom/dsl.rb +60 -0
  15. data/lib/loom/facts.rb +13 -0
  16. data/lib/loom/facts/all.rb +2 -0
  17. data/lib/loom/facts/fact_file_provider.rb +86 -0
  18. data/lib/loom/facts/fact_set.rb +138 -0
  19. data/lib/loom/host_spec.rb +32 -0
  20. data/lib/loom/inventory.rb +124 -0
  21. data/lib/loom/logger.rb +141 -0
  22. data/lib/loom/method_signature.rb +174 -0
  23. data/lib/loom/mods.rb +4 -0
  24. data/lib/loom/mods/action_proxy.rb +105 -0
  25. data/lib/loom/mods/all.rb +3 -0
  26. data/lib/loom/mods/mod_loader.rb +80 -0
  27. data/lib/loom/mods/module.rb +113 -0
  28. data/lib/loom/pattern.rb +15 -0
  29. data/lib/loom/pattern/all.rb +7 -0
  30. data/lib/loom/pattern/definition_context.rb +74 -0
  31. data/lib/loom/pattern/dsl.rb +176 -0
  32. data/lib/loom/pattern/hook.rb +28 -0
  33. data/lib/loom/pattern/loader.rb +48 -0
  34. data/lib/loom/pattern/reference.rb +71 -0
  35. data/lib/loom/pattern/reference_set.rb +169 -0
  36. data/lib/loom/pattern/result_reporter.rb +77 -0
  37. data/lib/loom/runner.rb +209 -0
  38. data/lib/loom/shell.rb +12 -0
  39. data/lib/loom/shell/all.rb +10 -0
  40. data/lib/loom/shell/api.rb +48 -0
  41. data/lib/loom/shell/cmd_result.rb +33 -0
  42. data/lib/loom/shell/cmd_wrapper.rb +164 -0
  43. data/lib/loom/shell/core.rb +226 -0
  44. data/lib/loom/shell/harness_blob.rb +26 -0
  45. data/lib/loom/shell/harness_command_builder.rb +50 -0
  46. data/lib/loom/shell/session.rb +25 -0
  47. data/lib/loom/trap.rb +44 -0
  48. data/lib/loom/version.rb +3 -0
  49. data/lib/loomext/all.rb +4 -0
  50. data/lib/loomext/corefacts.rb +6 -0
  51. data/lib/loomext/corefacts/all.rb +8 -0
  52. data/lib/loomext/corefacts/facter_provider.rb +24 -0
  53. data/lib/loomext/coremods.rb +5 -0
  54. data/lib/loomext/coremods/all.rb +13 -0
  55. data/lib/loomext/coremods/exec.rb +50 -0
  56. data/lib/loomext/coremods/files.rb +104 -0
  57. data/lib/loomext/coremods/net.rb +33 -0
  58. data/lib/loomext/coremods/package/adapter.rb +100 -0
  59. data/lib/loomext/coremods/package/package.rb +62 -0
  60. data/lib/loomext/coremods/user.rb +82 -0
  61. data/lib/loomext/coremods/vm.rb +0 -0
  62. data/lib/loomext/coremods/vm/all.rb +6 -0
  63. data/lib/loomext/coremods/vm/vbox.rb +84 -0
  64. data/loom.gemspec +39 -0
  65. data/loom/inventory.yml +13 -0
  66. data/scripts/harness.sh +242 -0
  67. data/spec/loom/host_spec_spec.rb +101 -0
  68. data/spec/loom/inventory_spec.rb +154 -0
  69. data/spec/loom/method_signature_spec.rb +275 -0
  70. data/spec/loom/pattern/dsl_spec.rb +207 -0
  71. data/spec/loom/shell/cmd_wrapper_spec.rb +239 -0
  72. data/spec/loom/shell/harness_blob_spec.rb +42 -0
  73. data/spec/loom/shell/harness_command_builder_spec.rb +36 -0
  74. data/spec/runloom.sh +35 -0
  75. data/spec/scripts/harness_spec.rb +385 -0
  76. data/spec/spec_helper.rb +94 -0
  77. data/spec/test.loom +370 -0
  78. data/spec/test_loom_spec.rb +57 -0
  79. metadata +287 -0
@@ -0,0 +1,36 @@
1
+ describe Loom::Shell::HarnessCommandBuilder do
2
+
3
+ let(:cmd) do
4
+ <<CMD
5
+ cd /tmp
6
+ pwd
7
+ CMD
8
+ end
9
+ let(:harness_blob) { Loom::Shell::HarnessBlob.new cmd }
10
+
11
+ subject { Loom::Shell::HarnessCommandBuilder.new harness_blob }
12
+
13
+ context "#run" do
14
+ it "builds run commands for the harness" do
15
+ expected_cmd = [
16
+ "./scripts/harness.sh",
17
+ "--run 2>/dev/null",
18
+ "-",
19
+ harness_blob.checksum,
20
+ "--cmd_shell /bin/dash",
21
+ "--record_file /opt/loom/commands",
22
+ "<<'[\\w]+'\n"
23
+ ].join " "
24
+
25
+ expected_cmd << harness_blob.encoded_script + "\n[\\w]+"
26
+
27
+ expect(subject.run_cmd).to match /^#{expected_cmd}/
28
+ end
29
+
30
+ it "is shell executable" do
31
+ result = %x{#{subject.run_cmd}}
32
+ expect(result).to eq "/tmp\n"
33
+ expect($?.exitstatus).to be 0
34
+ end
35
+ end
36
+ end
data/spec/runloom.sh ADDED
@@ -0,0 +1,35 @@
1
+ #!/bin/sh
2
+
3
+ spec_file="spec/test.loom"
4
+ all_patterns=$(bin/loom patterns --print \
5
+ -t \
6
+ -l ${spec_file})
7
+
8
+ addl_args="${@}"
9
+
10
+ bin/loom weave ${all_patterns} \
11
+ -t \
12
+ -l ${spec_file} \
13
+ -X log_level=info \
14
+ -H vm0.local \
15
+ -X sshkit_log_level=warn \
16
+ -X log_device=stderr \
17
+ -X run_failure_strategy=cowboy \
18
+ ${addl_args}
19
+ rc=$?
20
+
21
+ # expect exit code 104 (100 for patterns execution error + # expected failures):
22
+ # exec:fail_soft
23
+ # exec:fail_hard
24
+ # exec:timeout_fail
25
+ # net:check_net_fail
26
+
27
+ if [ "${rc}" = "104" ]; then
28
+ # runall.sh exits succesfully on this failure because we expect
29
+ # the "fail" patterns to fail
30
+ echo "success with exit code ${rc}"
31
+ exit 0
32
+ else
33
+ echo "failed with exit code ${rc}"
34
+ exit $rc
35
+ fi
@@ -0,0 +1,385 @@
1
+ require 'base64'
2
+ require 'tmpdir'
3
+
4
+ describe 'loom harness script' do
5
+
6
+ HARNESS = "./scripts/harness.sh"
7
+
8
+ # Set debug_script to true in a context to see STDERR debugging.
9
+ let(:debug_script) { false }
10
+
11
+ def run_script(cmd, *args, stdin: nil, harness_shell: :bash)
12
+ stderr_redirect = debug_script ? "" : "2>/dev/null"
13
+ cmd = if stdin
14
+ stdin = stdin.rstrip + "\n"
15
+ heredoc = "<<'SH_EOS'\n#{stdin}\nSH_EOS"
16
+ "#{HARNESS} #{cmd} - #{args.join " "} #{heredoc}"
17
+ else
18
+ "#{HARNESS} #{cmd} #{args.join " "}"
19
+ end
20
+
21
+ harness_cmd = "(#{harness_shell} -) #{stderr_redirect} " +
22
+ "<<'HARNESS_EOS'\n#{cmd}\nHARNESS_EOS"
23
+ if debug_script
24
+ puts "[harness-cmd]$ #{harness_cmd}"
25
+ end
26
+
27
+ %x{#{harness_cmd}}
28
+ end
29
+
30
+ let(:cmd) { 'echo hi there | (echo "from subshell"; cat);' }
31
+ let(:quoted_cmd) { "'#{cmd}'" }
32
+
33
+ # TODO: This is actually the base64 of `cmd` + a trailing newline. I'm
34
+ # not sure whether it should be trimmed first or not.
35
+ let(:base64) do
36
+ "ZWNobyBoaSB0aGVyZSB8IChlY2hvICJmcm9tIHN1YnNoZWxsIjsgY2F0KTsK"
37
+ end
38
+
39
+ # TODO: This is actually the checksum of `base64` + a trailing newline. I'm
40
+ # not sure whether it should be trimmed first or not.
41
+ let(:base64_checksum) { "18e75ac603d7cf76db5a841157a3871c5d53f217" }
42
+ let(:expected_output) do
43
+ <<EOS
44
+ from subshell
45
+ hi there
46
+ EOS
47
+ end
48
+
49
+ context "--print_base64" do
50
+
51
+ it "encodes commands from args" do
52
+ result = run_script :"--print_base64", quoted_cmd
53
+ expect(result).to eq base64
54
+ expect($?.exitstatus).to be 0
55
+ end
56
+
57
+ it "encodes commands from STDIN" do
58
+ result = run_script :"--print_base64", :stdin => cmd
59
+ expect(result).to eq base64
60
+ expect($?.exitstatus).to be 0
61
+ end
62
+
63
+ context "multiline strings" do
64
+ it "encodes newlines" do
65
+ cmd = <<EOS
66
+ cd /tmp
67
+ pwd
68
+ EOS
69
+ result = run_script :"--print_base64", :stdin => cmd
70
+ expect(result).to eq "Y2QgL3RtcApwd2QK"
71
+ expect(Base64.decode64 result).to eq cmd
72
+ end
73
+ end
74
+
75
+ context "trailing whitespace" do
76
+
77
+ it "ignores trailing newlines" do
78
+ cmd = "pwd"
79
+
80
+ 3.times do |i|
81
+ append_str = "\n" * i
82
+
83
+ nl_cmd_string = cmd + append_str
84
+ stdin_result = run_script :"--print_base64", :stdin => nl_cmd_string
85
+ argv_result = run_script :"--print_base64", "\"#{nl_cmd_string}\""
86
+
87
+ expect(stdin_result).to eq "cHdkCg=="
88
+ expect(argv_result).to eq "cHdkCg=="
89
+
90
+ expect(Base64.decode64 argv_result).to eq "pwd\n"
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ context "--print_checksum" do
97
+
98
+ it "fails if arg is not a base64 blob" do
99
+ result = run_script :"--print_checksum", :stdin => "this is not base64"
100
+ expect($?.exitstatus).to eq 9
101
+ end
102
+
103
+ it "prints the sha1 checksum from args" do
104
+ result = run_script :"--print_checksum", base64
105
+ expect(result).to eq base64_checksum
106
+ expect($?.exitstatus).to eq 0
107
+ end
108
+
109
+ it "prints the sha1 checksum from STDIN" do
110
+ result = run_script :"--print_checksum", :stdin => base64
111
+ expect(result).to eq base64_checksum
112
+ expect($?.exitstatus).to eq 0
113
+ end
114
+
115
+ it "is resilient to trailing whitespace" do
116
+ result1 = run_script :"--print_checksum", base64 + "\n\n"
117
+ expect(result1).to eq base64_checksum
118
+ expect($?.exitstatus).to eq 0
119
+
120
+ result2 = run_script :"--print_checksum", :stdin => base64 + "\n\n\n"
121
+ expect(result2).to eq base64_checksum
122
+ expect($?.exitstatus).to eq 0
123
+ end
124
+ end
125
+
126
+ context "--check" do
127
+
128
+ context "errors" do
129
+
130
+ it "if less than 2 args are passed" do
131
+ result = run_script :"--check", "only1arg"
132
+ expect($?.exitstatus).to eq 2
133
+
134
+ result = run_script :"--check"
135
+ expect($?.exitstatus).to eq 2
136
+ end
137
+
138
+ it "fails for a invalid base64" do
139
+ result = run_script :"--check", "not-base-64", base64_checksum
140
+ expect($?.exitstatus).to eq 9
141
+ end
142
+
143
+ it "fails for a bad checksum" do
144
+ result = run_script :"--check", base64, "not-a-checksum"
145
+ expect($?.exitstatus).to eq 8
146
+ end
147
+ end
148
+
149
+ context "from args" do
150
+
151
+ it "succeeds if the sha1 checksum matches" do
152
+ result = run_script :"--check", base64, base64_checksum
153
+ expect($?.exitstatus).to eq 0
154
+ end
155
+ end
156
+ end
157
+
158
+ context "--run" do
159
+
160
+ context "errors" do
161
+
162
+ it "if less than 2 args are passed" do
163
+ result = run_script :"--run", "only1arg"
164
+ expect($?.exitstatus).to eq 2
165
+
166
+ result = run_script :"--run"
167
+ expect($?.exitstatus).to eq 2
168
+ end
169
+
170
+ it "fails for a invalid base64" do
171
+ result = run_script :"--run", "not-base-64", base64_checksum
172
+ expect($?.exitstatus).to eq 9
173
+ end
174
+
175
+ it "fails for a bad checksum" do
176
+ result = run_script :"--run", base64, "not-a-checksum"
177
+ expect($?.exitstatus).to eq 8
178
+ end
179
+ end
180
+
181
+ context "record file" do
182
+
183
+ let(:record_file) { "/tmp/harness.cmds" }
184
+
185
+ after(:example) do
186
+ File.delete record_file
187
+ end
188
+
189
+ it "stores the executed command history" do
190
+ result = run_script :"--run", base64, base64_checksum,
191
+ "--record_file #{record_file}"
192
+ expect(result).to eq expected_output
193
+ expect(File.read(record_file).strip).to eq cmd
194
+ end
195
+ end
196
+
197
+ context "runs valid commands" do
198
+
199
+ it "from args" do
200
+ result = run_script :"--run", base64, base64_checksum
201
+ expect(result).to eq expected_output
202
+ expect($?.exitstatus).to eq 0
203
+ end
204
+
205
+ it "from STDIN" do
206
+ result = run_script :"--run", base64_checksum, :stdin => base64
207
+ expect(result).to eq expected_output
208
+ expect($?.exitstatus).to eq 0
209
+ end
210
+ end
211
+
212
+ context "runs in harness shell" do
213
+
214
+ it "bash" do
215
+ result = run_script :"--run", base64_checksum, {
216
+ :stdin => base64,
217
+ :harness_shell => "bash"
218
+ }
219
+ expect(result).to eq expected_output
220
+ expect($?.exitstatus).to eq 0
221
+ end
222
+
223
+ it "bash --posix" do
224
+ result = run_script :"--run", base64_checksum, {
225
+ :stdin => base64,
226
+ :harness_shell => "bash --posix"
227
+ }
228
+ expect(result).to eq expected_output
229
+ expect($?.exitstatus).to eq 0
230
+ end
231
+
232
+ context "dash" do
233
+
234
+ it "from args" do
235
+ result = run_script :"--run", base64, base64_checksum, {
236
+ :harness_shell => "/bin/dash"
237
+ }
238
+ expect(result).to eq expected_output
239
+ expect($?.exitstatus).to eq 0
240
+ end
241
+
242
+ it "from STDIN" do
243
+ result = run_script :"--run", base64_checksum, {
244
+ :stdin => base64,
245
+ :harness_shell => "/bin/dash"
246
+ }
247
+ expect(result).to eq expected_output
248
+ expect($?.exitstatus).to eq 0
249
+ end
250
+ end
251
+ end
252
+
253
+ context "run in command shell" do
254
+
255
+ let(:cmd) do
256
+ <<SH_EOS
257
+ # The cmd_shell Test
258
+ echo $0
259
+
260
+ # CD Test
261
+ cd /tmp
262
+ pwd
263
+
264
+ # The subshell tests
265
+ echo "piped to cat" | (
266
+ echo from a subshell
267
+ cat
268
+ )
269
+ (
270
+ exec 2>&1
271
+ echo "out to err and back to out" 1>&2
272
+ )
273
+
274
+ # The sudo test
275
+ sudo -u $(whoami) whoami
276
+
277
+ # Variable expansion
278
+ foo=bar
279
+ echo "I am FOO: ${foo}"
280
+
281
+ # Subshell variable expansion
282
+ (
283
+ echo "I am subshell FOO: ${foo}"
284
+ foo=baz
285
+ echo "I am subshell FOO2: ${foo}"
286
+ )
287
+
288
+ # The ulimit test
289
+ tmp_file=$(mktemp)
290
+ (
291
+ ulimit -f 0
292
+ echo "this will blow up w/ signal SIGXFSZ" > $tmp_file
293
+ )
294
+ echo $? # expect exit 153 (signal 25 + 128), SIGXFSZ
295
+ rm $tmp_file
296
+
297
+ SH_EOS
298
+ end
299
+
300
+ let(:whoami) { %x{whoami}.strip }
301
+ let(:expected_output) do
302
+ <<EOS
303
+ %s
304
+ /tmp
305
+ from a subshell
306
+ piped to cat
307
+ out to err and back to out
308
+ #{whoami}
309
+ I am FOO: bar
310
+ I am subshell FOO: bar
311
+ I am subshell FOO2: baz
312
+ 153
313
+ EOS
314
+ end
315
+
316
+ let(:base64) { run_script :"--print_base64", :stdin => cmd }
317
+ let(:base64_checksum) { run_script :"--print_checksum", :stdin => base64 }
318
+
319
+ context "bash" do
320
+
321
+ it "from STDIN" do
322
+ result = run_script :"--run", base64_checksum,
323
+ "--cmd_shell /bin/bash", :stdin => base64
324
+ expect($?.exitstatus).to eq 0
325
+
326
+ expected_output_for_shell = expected_output % "/bin/bash"
327
+ expect(result).to eq expected_output_for_shell
328
+ end
329
+ end
330
+
331
+ context "bash --posix" do
332
+
333
+ it "from STDIN" do
334
+ result = run_script :"--run", base64_checksum,
335
+ "--cmd_shell '/bin/bash --posix'", :stdin => base64
336
+ expect($?.exitstatus).to eq 0
337
+
338
+ expected_output_for_shell = expected_output % "/bin/bash"
339
+ expect(result).to eq expected_output_for_shell
340
+ end
341
+ end
342
+
343
+ context "dash" do
344
+
345
+ it "from STDIN" do
346
+ result = run_script :"--run", base64_checksum,
347
+ "--cmd_shell /bin/dash", :stdin => base64
348
+ expect($?.exitstatus).to eq 0
349
+
350
+ expected_output_for_shell = expected_output % "/bin/dash"
351
+ expect(result).to eq expected_output_for_shell
352
+ end
353
+ end
354
+ end
355
+
356
+ context "return codes" do
357
+
358
+ def run(cmd)
359
+ base64 = run_script :"--print_base64", :stdin => cmd
360
+ base64_checksum = run_script :"--print_checksum", :stdin => base64
361
+ run_script :"--run", base64_checksum,
362
+ "--cmd_shell /bin/dash", :stdin => base64
363
+ end
364
+
365
+ it "returns 0 on success" do
366
+ run "true"
367
+ expect($?.exitstatus).to be 0
368
+ end
369
+
370
+ it "returns 'exitcode' on failure" do
371
+ run "exit 123"
372
+ expect($?.exitstatus).to be 123
373
+ end
374
+ end
375
+ end
376
+
377
+ context "usage error" do
378
+
379
+ it "prints a usage message" do
380
+ result = run_script nil
381
+ expect(result).to match /^Usages:.*/i
382
+ expect($?.exitstatus).to be 1
383
+ end
384
+ end
385
+ end