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,101 @@
1
+ require 'loom/host_spec'
2
+
3
+ describe Loom::HostSpec do
4
+
5
+ subject { Loom::HostSpec.new host_string }
6
+
7
+ context "local strings" do
8
+
9
+ context "with no user/nor port" do
10
+ let(:host_string) { "localhost" }
11
+
12
+ it "parses localhost configurations" do
13
+ expect(subject.is_localhost?).to be true
14
+ expect(subject.is_remote?).to be false
15
+ end
16
+
17
+ it "has been parsed" do
18
+ expect(subject.hostname).to eq "localhost"
19
+ expect(subject.user).to be nil
20
+ expect(subject.port).to be nil
21
+ end
22
+ end
23
+
24
+ context "with a user" do
25
+ let(:host_string) { "ej@localhost" }
26
+
27
+ it "parses configurations as remote" do
28
+ expect(subject.is_localhost?).to be false
29
+ expect(subject.is_remote?).to be true
30
+ end
31
+
32
+ it "has been parsed" do
33
+ expect(subject.hostname).to eq "localhost"
34
+ expect(subject.user).to eq "ej"
35
+ expect(subject.port).to be nil
36
+ end
37
+ end
38
+
39
+ context "with a port" do
40
+ let(:host_string) { "localhost:22" }
41
+
42
+ it "parses configurations as remote" do
43
+ expect(subject.is_localhost?).to be false
44
+ expect(subject.is_remote?).to be true
45
+ end
46
+
47
+ it "has been parsed" do
48
+ expect(subject.hostname).to eq "localhost"
49
+ expect(subject.user).to be nil
50
+ expect(subject.port).to eq 22
51
+ end
52
+ end
53
+
54
+ end
55
+
56
+ context "remote strings" do
57
+
58
+ let(:host_string) { "erick@remote.machine:27" }
59
+
60
+ it "parses user@localhost configurations as remote" do
61
+ expect(subject.is_localhost?).to be false
62
+ expect(subject.is_remote?).to be true
63
+ end
64
+
65
+ it "has been parsed" do
66
+ expect(subject.user).to eq "erick"
67
+ expect(subject.hostname).to eq "remote.machine"
68
+ expect(subject.port).to eq 27
69
+ end
70
+ end
71
+
72
+ context "ipv4 addresses" do
73
+ let(:host_string) { "erick@192.168.1.100:27" }
74
+
75
+ it "parses user@localhost configurations as remote" do
76
+ expect(subject.is_localhost?).to be false
77
+ expect(subject.is_remote?).to be true
78
+ end
79
+
80
+ it "has been parsed" do
81
+ expect(subject.user).to eq "erick"
82
+ expect(subject.hostname).to eq "192.168.1.100"
83
+ expect(subject.port).to eq 27
84
+ end
85
+ end
86
+
87
+ context "ipv6 addresses" do
88
+ let(:host_string) { "[2a02:120b:2c28:5920:6257:18ff:febf:13c8:22]" }
89
+
90
+ it "parses user@localhost configurations as remote" do
91
+ expect(subject.is_localhost?).to be false
92
+ expect(subject.is_remote?).to be true
93
+ end
94
+
95
+ it "has been parsed" do
96
+ expect(subject.hostname).to eq "2a02:120b:2c28:5920:6257:18ff:febf:13c8"
97
+ expect(subject.user).to be nil
98
+ expect(subject.port).to eq 22
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,154 @@
1
+ require "loom/config"
2
+ require "loom/inventory"
3
+ require "tmpdir"
4
+ require "yaml"
5
+
6
+ describe Loom::Inventory do
7
+
8
+ HOSTLIST = %w[host1 host2 host3]
9
+ HOSTGROUPS = {
10
+ :group1 => %w[g1host g2host],
11
+ :duplicate_group => %w[host1]
12
+ }
13
+
14
+ INVENTORY_HOSTLIST = [
15
+ 'inv.host1',
16
+ 'inv.host2',
17
+ :inventory_group => ['inv.group.host'],
18
+ 'string_group' => ['string.group.host']
19
+ ]
20
+
21
+ class TmpInventoryFile
22
+ def self.transaction(search_paths_list, &block)
23
+ Dir.mktmpdir do |dir|
24
+ search_paths_list << dir
25
+
26
+ File.open File.join(dir, "inventory.yml"), "w" do
27
+ |f| f << INVENTORY_HOSTLIST.to_yaml
28
+ end
29
+ yield
30
+ end
31
+ end
32
+ end
33
+
34
+ CONFIG_DEFAULTS = {
35
+ :loom_search_paths => []
36
+ }
37
+
38
+ let(:config_map) { {} }
39
+ let(:config) do
40
+ Loom::Config.configure do |c|
41
+ CONFIG_DEFAULTS.merge(config_map).each { |k,v| c[k] = v }
42
+ end
43
+ end
44
+
45
+ context "methods" do
46
+
47
+ subject { Loom::Inventory::InventoryList.new hostlist, hostgroups }
48
+ let(:hostlist) { HOSTLIST }
49
+ let(:hostgroups) { {} }
50
+
51
+ it "#hosts should parse hostnames" do
52
+ expect(subject.hosts.first).to be_a Loom::HostSpec
53
+ expect(subject.hosts.size).to be 3
54
+ end
55
+
56
+ it "#disable marks hosts #disabled?" do
57
+ expect(subject.disabled? "host1").to be false
58
+ subject.disable "host1"
59
+ expect(subject.disabled? "host1").to be true
60
+ expect(subject.disabled? "host2").to be false
61
+ end
62
+
63
+ it "lists #hostnames" do
64
+ expect(subject.hostnames).to eql %w[host1 host2 host3]
65
+ end
66
+
67
+ context "with groups" do
68
+
69
+ let(:hostgroups) { HOSTGROUPS }
70
+
71
+ context "#hostnames" do
72
+ it "#hostnames includes hostnames from the group" do
73
+ expect(subject.hostnames).to include("g1host")
74
+ end
75
+
76
+ it "only lists unique hosts" do
77
+ expect(subject.hostnames.size).to be 5
78
+ end
79
+ end
80
+
81
+ it "lists #group_names" do
82
+ expect(subject.group_names).to eq [:group1, :duplicate_group]
83
+ end
84
+ end
85
+ end
86
+
87
+ context "#total_inventory" do
88
+
89
+ subject { Loom::Inventory::InventoryList.total_inventory config }
90
+
91
+ context "with explicit hosts configured" do
92
+
93
+ let(:config_map) { {:inventory_hosts => %w[confighost1 confighost2]} }
94
+
95
+ it "gets hosts from config" do
96
+ expect(subject.hostnames).to eq %w[confighost1 confighost2]
97
+ end
98
+ end
99
+
100
+ context "with inventory files" do
101
+
102
+ # To be passed into the TmpInventoryFile.transaction by :around
103
+ let(:config_map) { {:loom_search_paths => []} }
104
+
105
+ around(:example) do |example|
106
+ TmpInventoryFile.transaction config_map[:loom_search_paths], &example
107
+ end
108
+
109
+ it "finds hosts in the tmp config" do
110
+ expected = %w[inv.host1 inv.host2 inv.group.host string.group.host]
111
+ expect(subject.hostnames).to match_array expected
112
+ end
113
+
114
+ end
115
+ end
116
+
117
+ context "#active_inventory" do
118
+
119
+ subject { Loom::Inventory::InventoryList.active_inventory config }
120
+
121
+ context "with inventory file" do
122
+
123
+ around(:example) do |example|
124
+ TmpInventoryFile.transaction config_map[:loom_search_paths], &example
125
+ end
126
+
127
+ context "with explicit hosts" do
128
+
129
+ # To be passed into the TmpInventoryFile.transaction by :around
130
+ let(:config_map) do
131
+ {:inventory_hosts => HOSTLIST, :loom_search_paths => []}
132
+ end
133
+
134
+ it "gets only explicit hosts from config" do
135
+ expect(subject.hostnames).to eq HOSTLIST
136
+ end
137
+ end
138
+
139
+ context "with explicit groups" do
140
+
141
+ # To be passed into the TmpInventoryFile.transaction by :around
142
+ let(:config_map) do
143
+ {:inventory_groups => [:inventory_group, :string_group],
144
+ :loom_search_paths => []}
145
+ end
146
+
147
+ it "gets only explicit hosts from config" do
148
+ expect(subject.hostnames).to match_array %w[inv.group.host string.group.host]
149
+ end
150
+
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,275 @@
1
+ require "loom/method_signature"
2
+
3
+ describe Loom::MethodSignature do
4
+
5
+ module MethodModule
6
+ def one_req_arg(req); end
7
+ def two_req_args(req1, req2); end
8
+
9
+ def opt_arg(opt=nil); end
10
+ def two_opt_args(opt1=1, opt2=nil); end
11
+
12
+ def rest_args(*rest); end
13
+ def req_and_rest_args(req, *rest); end
14
+ def req_and_opt_and_rest_args(req, opt=nil, *rest); end
15
+
16
+ def one_keyreq_arg(keyreq:); end
17
+ def two_keyreq_args(keyreq1:, keyreq2:); end
18
+
19
+ def one_key_arg(key: 1); end
20
+ def two_key_args(key1: 1, key2: nil); end
21
+
22
+ def keyrest_arg(**keyrest); end
23
+ def key_and_keyrest_arg(keyreq:, key: 2, **keyrest); end
24
+
25
+ def block_arg(&block); end
26
+
27
+ def all_args(req, opt=1, *rest, keyreq:, key: 2, **keyrest, &block); end
28
+ def no_args; end
29
+
30
+ class << self
31
+ def [](method_name)
32
+ MethodModule.instance_method method_name
33
+ end
34
+ end
35
+ end
36
+
37
+ context Loom::MethodSignature do
38
+
39
+ context "#one_req_arg" do
40
+ subject { Loom::MethodSignature.new MethodModule[:one_req_arg] }
41
+
42
+ it "has the right signature" do
43
+ expect(subject.req_args.size).to be 1
44
+ end
45
+
46
+ it "implements has_xyz_args?" do
47
+ expect(subject.has_req_args?).to be true
48
+ expect(subject.has_opt_args?).to be false
49
+ expect(subject.has_rest_args?).to be false
50
+ end
51
+ end
52
+
53
+ context "#one_req_arg" do
54
+ subject { Loom::MethodSignature.new MethodModule[:one_keyreq_arg] }
55
+
56
+ it "has the right signature" do
57
+ expect(subject.keyreq_args.size).to be 1
58
+ end
59
+
60
+ it "implements has_xyz_args?" do
61
+ expect(subject.has_keyreq_args?).to be true
62
+ expect(subject.has_key_args?).to be false
63
+ expect(subject.has_keyrest_args?).to be false
64
+ end
65
+ end
66
+
67
+ context "#keyrest_arg" do
68
+ subject { Loom::MethodSignature.new MethodModule[:keyrest_arg] }
69
+
70
+ it "has the right signature" do
71
+ expect(subject.keyrest_args.size).to be 1
72
+ end
73
+
74
+ it "implements has_xyz_args?" do
75
+ expect(subject.has_keyreq_args?).to be false
76
+ expect(subject.has_key_args?).to be false
77
+ expect(subject.has_keyrest_args?).to be true
78
+ end
79
+ end
80
+
81
+ context "#two_key_args" do
82
+ subject { Loom::MethodSignature.new MethodModule[:two_key_args] }
83
+
84
+ it "has the right signature" do
85
+ expect(subject.req_args.size).to be 0
86
+ expect(subject.opt_args.size).to be 0
87
+ expect(subject.rest_args.size).to be 0
88
+
89
+ expect(subject.keyreq_args.size).to be 0
90
+ expect(subject.key_args.size).to be 2
91
+ expect(subject.keyrest_args.size).to be 0
92
+
93
+ expect(subject.block_args.size).to be 0
94
+ end
95
+ end
96
+
97
+ context "#all_args" do
98
+ subject { Loom::MethodSignature.new MethodModule[:all_args] }
99
+
100
+ it "has the right signature" do
101
+ expect(subject.req_args.size).to be 1
102
+ expect(subject.opt_args.size).to be 1
103
+ expect(subject.rest_args.size).to be 1
104
+
105
+ expect(subject.keyreq_args.size).to be 1
106
+ expect(subject.key_args.size).to be 1
107
+ expect(subject.keyrest_args.size).to be 1
108
+
109
+ expect(subject.block_args.size).to be 1
110
+ end
111
+
112
+ it "implements has_xyz_args?" do
113
+ expect(subject.has_req_args?).to be true
114
+ expect(subject.has_opt_args?).to be true
115
+ expect(subject.has_rest_args?).to be true
116
+
117
+ expect(subject.has_keyreq_args?).to be true
118
+ expect(subject.has_key_args?).to be true
119
+ expect(subject.has_keyrest_args?).to be true
120
+
121
+ expect(subject.has_block_args?).to be true
122
+ end
123
+ end
124
+
125
+ context "#no_args" do
126
+ subject { Loom::MethodSignature.new MethodModule[:no_args] }
127
+
128
+ it "has the right signature" do
129
+ expect(subject.req_args.size).to be 0
130
+ expect(subject.opt_args.size).to be 0
131
+ expect(subject.rest_args.size).to be 0
132
+
133
+ expect(subject.keyreq_args.size).to be 0
134
+ expect(subject.key_args.size).to be 0
135
+ expect(subject.keyrest_args.size).to be 0
136
+ expect(subject.block_args.size).to be 0
137
+ end
138
+
139
+ it "implements has_xyz_args?" do
140
+ expect(subject.has_req_args?).to be false
141
+ expect(subject.has_opt_args?).to be false
142
+ expect(subject.has_rest_args?).to be false
143
+
144
+ expect(subject.has_keyreq_args?).to be false
145
+ expect(subject.has_key_args?).to be false
146
+ expect(subject.has_keyrest_args?).to be false
147
+
148
+ expect(subject.has_block_args?).to be false
149
+ end
150
+ end
151
+ end
152
+
153
+ context Loom::MethodSignature::MatchSpec do
154
+
155
+ context 'with required argument matching' do
156
+
157
+ subject { Loom::MethodSignature::MatchSpec.builder.req_args(1).build }
158
+ let(:passing_methods) { [:one_req_arg, :rest] }
159
+
160
+ MethodModule.instance_methods.each do |m|
161
+ it "checks ##{m} for matches" do
162
+ v = passing_methods.include? m
163
+ expect(subject.match?(MethodModule[m])).to be v
164
+ end
165
+ end
166
+ end
167
+
168
+ context 'with optional argument matching' do
169
+
170
+ subject { Loom::MethodSignature::MatchSpec.builder.opt_args(1).build }
171
+ let(:passing_methods) { [:opt_arg, :rest] }
172
+
173
+ MethodModule.instance_methods.each do |m|
174
+ it "checks ##{m} for matches" do
175
+ v = passing_methods.include? m
176
+ expect(subject.match?(MethodModule[m])).to be v
177
+ end
178
+ end
179
+ end
180
+
181
+ context 'with required keyword argument matching' do
182
+
183
+ subject { Loom::MethodSignature::MatchSpec.builder.keyreq_args(1).build }
184
+ let(:passing_methods) { [:one_keyreq_arg] }
185
+
186
+ MethodModule.instance_methods.each do |m|
187
+ it "checks ##{m} for matches" do
188
+ v = passing_methods.include? m
189
+ expect(subject.match?(MethodModule[m])).to be v
190
+ end
191
+ end
192
+ end
193
+
194
+ context 'with optional keyword argument matching' do
195
+
196
+ subject { Loom::MethodSignature::MatchSpec.builder.key_args(1).build }
197
+ let(:passing_methods) { [:one_key_arg] }
198
+
199
+ MethodModule.instance_methods.each do |m|
200
+ it "checks ##{m} for matches" do
201
+ v = passing_methods.include? m
202
+ expect(subject.match?(MethodModule[m])).to be v
203
+ end
204
+ end
205
+ end
206
+
207
+ context 'with a pattern signature' do
208
+
209
+ subject do
210
+ Loom::MethodSignature::MatchSpec.builder
211
+ .req_args(2)
212
+ .has_rest_args(nil)
213
+ .has_keyrest_args(nil)
214
+ .has_block(nil)
215
+ .build
216
+ end
217
+
218
+ let(:valid) { lambda do |loom, config| end }
219
+ let(:valid2) { lambda do |loom, config, *args| end }
220
+ let(:valid3) { lambda do |*args| end }
221
+ let(:invalid) { lambda do |loom, config, other| end }
222
+
223
+ it "checks :valid passes for well formatted pattern" do
224
+ expect(subject.match?(valid)).to be true
225
+ end
226
+
227
+ it "checks :valid2 passes for well formatted pattern" do
228
+ expect(subject.match?(valid2)).to be true
229
+ end
230
+
231
+ it "checks :valid2 passes for well formatted pattern" do
232
+ expect(subject.match?(valid3)).to be true
233
+ end
234
+
235
+ it "checks :invalid fails for badly formatted pattern" do
236
+ expect(subject.match?(invalid)).to be false
237
+ end
238
+
239
+ end
240
+
241
+ context 'with a mod signature' do
242
+
243
+ subject do
244
+ Loom::MethodSignature::MatchSpec.builder
245
+ .req_args(2)
246
+ .has_rest_args(nil)
247
+ .has_keyrest_args(true)
248
+ .has_block(nil)
249
+ .build
250
+ end
251
+
252
+ let(:valid) { lambda do |loom, config, *args, **rest| end }
253
+ let(:valid2) { lambda do |loom, config, **rest| end }
254
+ let(:valid3) { lambda do |loom, config, foo:, bar: nil| end }
255
+ let(:invalid) { lambda do |loom, config, foo, bar| end }
256
+
257
+ it "checks :valid passes for well formatted pattern" do
258
+ expect(subject.match?(valid)).to be true
259
+ end
260
+
261
+ it "checks :valid2 passes for well formatted pattern" do
262
+ expect(subject.match?(valid2)).to be true
263
+ end
264
+
265
+ it "checks :valid3 passes for well formatted pattern" do
266
+ expect(subject.match?(valid3)).to be true
267
+ end
268
+
269
+ it "checks :invalid fails for badly formatted pattern" do
270
+ expect(subject.match?(invalid)).to be false
271
+ end
272
+
273
+ end
274
+ end
275
+ end