loom-core 0.0.5 → 0.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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/bin/loom +5 -1
  4. data/lib/loom/config.rb +10 -3
  5. data/lib/loom/facts/fact_set.rb +7 -1
  6. data/lib/loom/mods/module.rb +3 -4
  7. data/lib/loom/pattern/definition_context.rb +1 -1
  8. data/lib/loom/pattern/dsl.rb +113 -32
  9. data/lib/loom/pattern/reference_set.rb +7 -7
  10. data/lib/loom/runner.rb +8 -0
  11. data/lib/loom/shell/api.rb +2 -0
  12. data/lib/loom/version.rb +1 -1
  13. data/lib/loomext/corefacts/system_info_provider.rb +2 -1
  14. data/lib/loomext/coremods/all.rb +2 -1
  15. data/lib/loomext/coremods/exec.rb +10 -0
  16. data/lib/loomext/coremods/git.rb +19 -0
  17. data/lib/loomext/coremods/package/adapter.rb +1 -1
  18. data/lib/loomext/coremods/systemd.rb +1 -0
  19. data/lib/loomext/coremods/systemd/all.rb +2 -0
  20. data/lib/loomext/coremods/{system.rb → systemd/systemd.rb} +19 -5
  21. data/lib/loomext/coremods/systemd/systemd_units.rb +69 -0
  22. data/scripts/harness.sh +1 -0
  23. data/spec/.loom/error_handling.loom +15 -0
  24. data/spec/.loom/fail.loom +20 -0
  25. data/spec/.loom/files.loom +39 -0
  26. data/spec/.loom/net.loom +21 -0
  27. data/spec/.loom/pattern_context.loom +78 -0
  28. data/spec/.loom/pkg.loom +33 -0
  29. data/spec/.loom/shell.loom +46 -0
  30. data/spec/.loom/test.loom +73 -0
  31. data/spec/.loom/user.loom +29 -0
  32. data/spec/.loom/vms.loom +22 -0
  33. data/spec/loom/facts/fact_set_spec.rb +57 -0
  34. data/spec/loom/pattern/dsl_spec.rb +58 -9
  35. data/spec/loom/shell/harness_blob_spec.rb +1 -1
  36. data/spec/loom/shell/harness_command_builder_spec.rb +1 -1
  37. data/spec/loomext/coremods/systemd_spec.rb +31 -0
  38. data/spec/runloom.sh +12 -19
  39. data/spec/scripts/harness_spec.rb +1 -1
  40. data/spec/shared/loom_internals_helper.rb +41 -0
  41. data/spec/spec_helper.rb +5 -1
  42. data/spec/systemd.loom +22 -0
  43. data/spec/test_loom_spec.rb +95 -17
  44. data/test +2 -0
  45. metadata +26 -5
  46. data/spec/test.loom +0 -370
@@ -0,0 +1,29 @@
1
+ # Tests for LoomExt::CoreMods::User
2
+ module User
3
+ include Loom::Pattern
4
+
5
+ desc "Adds a bunch of users and then removes them right away"
6
+ pattern :add_users do |loom, facts|
7
+ loom.sudo do
8
+ loom.user.add :nara, uid: 1001, home_dir: "/home/nara"
9
+ loom.user.add :marrian, home_dir: "/home/marrian"
10
+
11
+ loom.user.add_system_user :app, uid: 900
12
+ loom.user.add_system_user :batch
13
+
14
+ loom.user.remove :nara
15
+ loom.user.remove :marrian
16
+ loom.user.remove :app
17
+ loom.user.remove :batch
18
+ end
19
+ end
20
+
21
+ desc "Makes the postgres user a sudoer"
22
+ pattern :sudoers do |loom, facts|
23
+ loom.sudo do
24
+ loom.user.add_system_user :postgres, uid: 999
25
+ loom.user.make_sudoer :postgres
26
+ loom.user.remove :postgres
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,22 @@
1
+ # Tests for LoomExt::CoreMods::Vbox.
2
+ # Currently disabled
3
+ module VMs
4
+ include Loom::Pattern
5
+
6
+ desc "Check if some virtualbox vms exist"
7
+ pattern :vm_exists? do |loom, facts|
8
+ vm0_exists = loom.local.vbox.check_exists "vm0.local"
9
+ puts "Virtualbox VM 'vm0.local' exists: #{vm0_exists}"
10
+ end
11
+
12
+ desc "Check if some virtualbox vms exist"
13
+ pattern :list do |loom, facts|
14
+ loom.local.vbox.list
15
+ end
16
+
17
+ desc "Takes a snapshot of a VM"
18
+ pattern :snap do |loom, facts|
19
+ loom.local.vbox.snapshot "vm0.local", snapshot_name: "test:snap"
20
+ loom.local.vbox.snapshot "vm0.local", action: :delete, snapshot_name: "test:snap"
21
+ end
22
+ end
@@ -0,0 +1,57 @@
1
+ require 'loom/facts'
2
+
3
+ describe Loom::Facts::FactSet do
4
+
5
+ let(:fact_set) { Loom::Facts::FactSet.new hostspec, facts }
6
+ let(:hostspec) { Loom::HostSpec.new "dummy.host" }
7
+ let(:facts) {{
8
+ :fact_a => :a,
9
+ :fact_b => [:other, :facts]
10
+ }}
11
+
12
+ subject { fact_set }
13
+
14
+ let(:other_fact_set) { Loom::Facts::FactSet.new hostspec, other_facts }
15
+ let(:other_facts) {{
16
+ :fact_y => :y,
17
+ :fact_z => :z,
18
+ }}
19
+
20
+ context Loom::Facts::EMPTY do
21
+
22
+ subject { Loom::Facts::EMPTY }
23
+
24
+ it "should equal Facts.is_empty?" do
25
+ expect(Loom::Facts.is_empty? subject).to be true
26
+
27
+ # sanity
28
+ expect(Loom::Facts.is_empty?({})).to be false
29
+ expect(Loom::Facts.is_empty?(fact_set)).to be false
30
+ end
31
+ end
32
+
33
+ context :merge do
34
+ it "merges with other fact sets" do
35
+ new_facts = subject.merge other_fact_set
36
+ expect(new_facts[:fact_a]).to be :a
37
+ expect(new_facts[:fact_y]).to be :y
38
+ end
39
+
40
+ it "merges with other hashes" do
41
+ new_facts = subject.merge :other_stuff => :in_a_hash
42
+ expect(new_facts[:fact_a]).to be :a
43
+ expect(new_facts[:other_stuff]).to be :in_a_hash
44
+ end
45
+ end
46
+
47
+ context :[] do
48
+
49
+ it "returns EMPTY for no result" do
50
+ expect(fact_set[:invalid_key]).to be Loom::Facts::EMPTY
51
+ end
52
+
53
+ it "gets facts by name" do
54
+ expect(fact_set[:fact_a]).to be :a
55
+ end
56
+ end
57
+ end
@@ -12,7 +12,7 @@ describe Loom::Pattern::DSL do
12
12
  end
13
13
 
14
14
  before do
15
- # bit buckets the logs
15
+ # intercept logs
16
16
  @logger_io = StringIO.new
17
17
  Loom.configure do |config|
18
18
  config.log_device = @logger_io
@@ -28,7 +28,7 @@ describe Loom::Pattern::DSL do
28
28
  context "pattern basics" do
29
29
 
30
30
  let(:loom_file) do
31
- <<EOS
31
+ <<RB_LOOM
32
32
  desc "a description of pattern_under_test"
33
33
  pattern :pattern_under_test do |loom, facts|
34
34
  loom.do_outer_thing
@@ -42,7 +42,7 @@ describe Loom::Pattern::DSL do
42
42
  loom.do_inner_thing
43
43
  end
44
44
  end
45
- EOS
45
+ RB_LOOM
46
46
  end
47
47
 
48
48
  it "defines a Pattern::Reference" do
@@ -67,7 +67,7 @@ EOS
67
67
  context "#let" do
68
68
 
69
69
  let(:loom_file) do
70
- <<EOS
70
+ <<RB_LOOM
71
71
  let(:let_var_1) { "let var 1"}
72
72
  let(:let_var_2) { "let var 2"}
73
73
 
@@ -92,7 +92,7 @@ EOS
92
92
  loom.do_inner_thing(let_var_1, let_var_2, let_var_3)
93
93
  end
94
94
  end
95
- EOS
95
+ RB_LOOM
96
96
  end
97
97
 
98
98
  it "defines :let declartions at the top level" do
@@ -117,7 +117,7 @@ EOS
117
117
  context "#with_facts" do
118
118
 
119
119
  let(:loom_file) do
120
- <<EOS
120
+ <<RB_LOOM
121
121
  with_facts :outer_fact => :outer
122
122
 
123
123
  desc "a description of pattern_under_test"
@@ -135,7 +135,7 @@ EOS
135
135
  loom.do_inner_thing(facts[:outer_fact], facts[:inner_fact])
136
136
  end
137
137
  end
138
- EOS
138
+ RB_LOOM
139
139
  end
140
140
 
141
141
  it "defines fact sets at the top level" do
@@ -151,10 +151,59 @@ EOS
151
151
  end
152
152
  end
153
153
 
154
+ context "#report" do
155
+
156
+ let(:report_fact_set) { a_fact_set.merge :the_report_facts => [1,2,3] }
157
+ let(:loom_file) do
158
+ <<RB_LOOM
159
+ let(:the_let_facts) { [:a, :b] }
160
+
161
+ report :the_report_facts
162
+ report :the_let_facts
163
+
164
+ report :the_other_facts do
165
+ { :some => :other, :stuff => :ok }
166
+ end
167
+
168
+ report :by_another_form, format: :json do
169
+ { :other => :data }
170
+ end
171
+ RB_LOOM
172
+ end
173
+
174
+ let(:new_stdio) { StringIO.new }
175
+ before { $stdout = new_stdio }
176
+ after { $stdout = STDOUT }
177
+
178
+ it 'prints the called report by fact value' do
179
+ @reference_set['the_report_facts'].call fake_shell, report_fact_set
180
+ report_output = new_stdio.string
181
+ expect(report_output).to eql [1,2,3].to_yaml
182
+ end
183
+
184
+ it 'prints the called report by let value' do
185
+ @reference_set['the_let_facts'].call fake_shell, report_fact_set
186
+ report_output = new_stdio.string
187
+ expect(report_output).to eql [:a, :b].to_yaml
188
+ end
189
+
190
+ it 'prints the given report block' do
191
+ @reference_set['the_other_facts'].call fake_shell, report_fact_set
192
+ report_output = new_stdio.string
193
+ expect(report_output).to eql({:some => :other, :stuff => :ok}.to_yaml)
194
+ end
195
+
196
+ it 'prints data in different formats' do
197
+ @reference_set['by_another_form'].call fake_shell, report_fact_set
198
+ report_output = new_stdio.string
199
+ expect(report_output.strip).to eql('{"other":"data"}')
200
+ end
201
+ end
202
+
154
203
  context "hooks" do
155
204
 
156
205
  let(:loom_file) do
157
- <<EOS
206
+ <<RB_LOOM
158
207
 
159
208
  let(:hook_order) { [] }
160
209
 
@@ -179,7 +228,7 @@ EOS
179
228
  loom.do_inner_thing(hook_order)
180
229
  end
181
230
  end
182
- EOS
231
+ RB_LOOM
183
232
  end
184
233
 
185
234
  it "executes outer before hooks first and after hooks last" do
@@ -1,4 +1,4 @@
1
- describe Loom::Shell::HarnessBlob do
1
+ xdescribe Loom::Shell::HarnessBlob do
2
2
 
3
3
  HARNESS = "./scripts/harness.sh"
4
4
 
@@ -1,4 +1,4 @@
1
- describe Loom::Shell::HarnessCommandBuilder do
1
+ xdescribe Loom::Shell::HarnessCommandBuilder do
2
2
 
3
3
  let(:cmd) do
4
4
  <<CMD
@@ -0,0 +1,31 @@
1
+ describe LoomExt::CoreMods::Systemd do
2
+ include LoomSpec::LoomInternalsHelper
3
+
4
+ let(:fake_loom) { create_fake_shell_api }
5
+ let(:a_fact_set) { create_fact_set }
6
+ let(:pattern_list_units) { @reference_set['list_units'] }
7
+
8
+ before do
9
+ # intercept logs
10
+ @logger_io = capture_logs_to_io
11
+ @reference_set = create_reference_set loom_file
12
+ end
13
+
14
+ context "actions" do
15
+
16
+ let(:loom_file) do
17
+ <<RB
18
+ require "loomext/all"
19
+ desc "lists systemd-units"
20
+ pattern :list_units do |l,_|
21
+ l.systemd.is_loaded?(:my_service)
22
+ end
23
+ RB
24
+ end
25
+
26
+ it "should execute this" do
27
+ pattern_list_units.call fake_loom, a_fact_set
28
+ expect(fake_loom.cmd_execution_args.join(' '.strip)).to eql 'my_service'
29
+ end
30
+ end
31
+ end
@@ -1,35 +1,28 @@
1
1
  #!/bin/sh
2
+ pattern_file="${1}"
3
+ if [ "${pattern_file}" = "" ]; then
4
+ echo "Usage: $0 <spec/path/to/.loom/file>"
5
+ exit 1
6
+ fi
2
7
 
3
- spec_file="spec/test.loom"
8
+ spec_file="spec/${pattern_file}"
4
9
  all_patterns=$(bin/loom patterns --print \
5
10
  -t \
6
11
  -l ${spec_file})
7
12
 
13
+ shift
8
14
  addl_args="${@}"
9
15
 
16
+ # TODO: Fix this to bring up a local container via
17
+ # systemd-nspawn. Either an alpine host, or something else really
18
+ # cheap and fast. NO DOCKER. Will rkt ever be a reasonable option?
10
19
  bin/loom weave ${all_patterns} \
11
20
  -t \
12
21
  -l ${spec_file} \
13
22
  -X log_level=info \
14
- -H vm0.local \
23
+ -H rp0 \
24
+ -V \
15
25
  -X sshkit_log_level=warn \
16
26
  -X log_device=stderr \
17
27
  -X run_failure_strategy=cowboy \
18
28
  ${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
@@ -1,7 +1,7 @@
1
1
  require 'base64'
2
2
  require 'tmpdir'
3
3
 
4
- describe 'loom harness script' do
4
+ xdescribe 'loom harness script' do
5
5
 
6
6
  HARNESS = "./scripts/harness.sh"
7
7
 
@@ -0,0 +1,41 @@
1
+ require './lib/loom'
2
+ require './lib/loomext/all'
3
+
4
+ module LoomSpec
5
+ module LoomInternalsHelper
6
+ FAKE_FACTS = { :fact_one => 1, :fact_two => :two }
7
+
8
+ def create_fact_set(fake_host: 'fake.host', fake_facts: FAKE_FACTS)
9
+ Loom::Facts::FactSet.new fake_host, fake_facts
10
+ end
11
+
12
+ def create_fake_shell_api
13
+ Loom::Shell::FakeApi.new
14
+ end
15
+
16
+ def create_reference_set(loom_file_src=nil, path: 'loom/file/path')
17
+ loom_file_src ||= File.read(path)
18
+ Loom::Pattern::ReferenceSet::Builder.create(loom_file_src, path)
19
+ end
20
+
21
+ def capture_logs_to_io
22
+ logger_io = StringIO.new
23
+ Loom.configure do |config|
24
+ config.log_device = logger_io
25
+ end
26
+ logger_io
27
+ end
28
+
29
+ def create_dry_run_shell
30
+ end
31
+
32
+ def create_config(**config_map)
33
+ Loom::Config.new config_map
34
+ end
35
+
36
+ def create_mod_loader(config: nil)
37
+ config ||= create_config
38
+ Loom::Mod::ModLoader.new config
39
+ end
40
+ end
41
+ end
@@ -1,6 +1,10 @@
1
- Dir.glob('./spec/shared/**/*.rb').each { |f| require f }
2
1
  require './lib/loom'
3
2
 
3
+ Dir.glob('./spec/shared/**/*.rb').each do |f|
4
+ $stderr.puts "requiring spec helper: #{f}"
5
+ require f
6
+ end
7
+
4
8
  # This file was generated by the `rspec --init` command. Conventionally, all
5
9
  # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
6
10
  # The generated `.rspec` file contains `--require spec_helper` which will cause
@@ -0,0 +1,22 @@
1
+ module Systemd
2
+ include Loom::Pattern
3
+
4
+ report :sshd_status, format: :json do |l, _|
5
+ l.systemd_units("sshd.service").status
6
+ end
7
+
8
+ report :units, format: :raw do |l, _|
9
+ l.systemd_units.list
10
+ end
11
+
12
+ report :sockets, format: :raw do |l, _|
13
+ l.systemd_sockets.list
14
+ end
15
+
16
+ report :timers, format: :raw do |l, _|
17
+ l.systemd_timers.list
18
+ end
19
+ end
20
+
21
+
22
+
@@ -1,5 +1,85 @@
1
1
  require 'open3'
2
2
 
3
+ describe "spec .loom files" do
4
+ include LoomSpec::LoomInternalsHelper
5
+
6
+ LOOM_FILES = Dir.glob "spec/.loom/**/*.loom"
7
+
8
+ # Tests disabled until this runs on an overlayfs container
9
+ XFILE_SET = {
10
+ "error_handling.loom" => true,
11
+ "files.loom" => true,
12
+ "pkg.loom" => true,
13
+ "user.loom" => true,
14
+ "vms.loom" => true,
15
+ }
16
+
17
+ EXPECTED_EXIT_CODE = {
18
+ "fail.loom" => 103
19
+ }
20
+
21
+ SPEC_TAGS = {
22
+ :integration => true,
23
+ :long => true
24
+ }
25
+
26
+ let(:host) { "rp0" }
27
+ let(:patterns) { ref_set.slugs }
28
+ let(:command) {[
29
+ "bin/loom weave #{patterns.join " "}",
30
+ "-t",
31
+ "-l #{loom_file}",
32
+ "-X log_level=info",
33
+ "-H #{host}",
34
+ "-V",
35
+ "-X sshkit_log_level=warn",
36
+ "-X log_device=stderr",
37
+ "-X run_failure_strategy=cowboy",
38
+ ]}
39
+
40
+ # bundle exec rspec --tag smoke
41
+ context "test.loom" do
42
+ let(:loom_file) { LOOM_FILES.select { |p| p.match?(/test.loom/) }.first }
43
+ let(:ref_set) { create_reference_set(path: loom_file) }
44
+ let(:patterns) { ref_set.slugs.select { |s| s.match?(/^smoke:/) } }
45
+ let(:host) { "localhost" }
46
+
47
+ it "should pass a few tests quickly", :smoke => true do
48
+ exec = command.join(' ')
49
+ output = `#{exec}`
50
+ expect($?.exitstatus).to eq 0
51
+ end
52
+ end
53
+
54
+ # bundle exec rspec --tag integration
55
+ LOOM_FILES.each do |loom_file|
56
+ context File.basename loom_file do
57
+ let(:loom_file) { loom_file }
58
+ let(:ref_set) { create_reference_set(path: loom_file) }
59
+
60
+ if XFILE_SET[File.basename(loom_file)]
61
+ xit "should pass all the tests",
62
+ SPEC_TAGS.merge(:file => File.basename(loom_file)) {}
63
+ else
64
+ it "should pass all tests",
65
+ SPEC_TAGS.merge(:file => File.basename(loom_file)) do
66
+ exec = command.join(' ')
67
+ puts exec
68
+ # TODO pattern match the commands on STDOUT (see comment in
69
+ # .loom/test.loom)
70
+ output = `#{exec}`
71
+
72
+ basename = File.basename(loom_file)
73
+ expected_exit_code = EXPECTED_EXIT_CODE[basename] || 0
74
+ expect($?.exitstatus).to eq expected_exit_code
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ # Archived 10/26/2018
82
+
3
83
  # TODO: fix this test to run the runloom.sh script. Currently running
4
84
  # the loom script in a child process from ruby runs without a
5
85
  # TTY. This causes the sshkit/net:ssh connection to connect abnormally
@@ -33,25 +113,23 @@ require 'open3'
33
113
  # /home/erick/.gem/ruby/gems/sshkit-1.11.3/lib/sshkit/backends/abstract.rb:141:in `tap'
34
114
  # /home/erick/.gem/ruby/gems/sshkit-1.11.3/lib/sshkit/backends/abstract.rb:141:in `create_command_and_execute'
35
115
  # /home/erick/workspace/src/loom/lib/loom/shell.rb:91:in `execute'
116
+ # xit "executes successfully" do
36
117
 
37
- describe "spec/test.loom file" do
118
+ # exit_status = nil
38
119
 
39
- let(:runloom) { File.join File.dirname(__FILE__), 'runloom.sh' }
120
+ # # https://stackoverflow.com/questions/6338908/ruby-difference-between-exec-system-and-x-or-backticks
121
+ # output = `#{runloom} -F fact_1=1,fact_2=2,fact_3=3`
40
122
 
41
- it "executes successfully" do
42
- exit_status = nil
43
- `#{runloom}`
44
- # Open3.popen3(runloom) do |stdin,stdout,stderr,wait_thred|
45
- # puts "running #{runloom}"
46
- # while line=stderr.gets do
47
- # puts line
48
- # end
49
- #
50
- # exit_status = wait_thred.value
51
- # end
123
+ # # Open3.popen3(runloom) do |stdin,stdout,stderr,wait_thred|
124
+ # # puts "running #{runloom}"
125
+ # # while line=stderr.gets do
126
+ # # puts line
127
+ # # end
128
+ # #
129
+ # # exit_status = wait_thred.value
130
+ # # end
52
131
 
53
- puts "exit status: #{$?.exitstatus}"
132
+ # puts "exit status: #{$?.exitstatus}"
54
133
 
55
- expect($?.exitstatus).to eq 0
56
- end
57
- end
134
+ # expect($?.exitstatus).to eq 0
135
+ # end