simp-beaker-helpers 1.18.8

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 (56) hide show
  1. checksums.yaml +7 -0
  2. data/.fixtures.yml +8 -0
  3. data/.gitignore +8 -0
  4. data/.gitlab-ci.yml +163 -0
  5. data/.rspec +4 -0
  6. data/.rubocop.yml +546 -0
  7. data/.travis.yml +36 -0
  8. data/CHANGELOG.md +231 -0
  9. data/Gemfile +51 -0
  10. data/LICENSE +27 -0
  11. data/README.md +543 -0
  12. data/Rakefile +151 -0
  13. data/files/pki/clean.sh +1 -0
  14. data/files/pki/make.sh +101 -0
  15. data/files/pki/template_ca.cnf +259 -0
  16. data/files/pki/template_host.cnf +263 -0
  17. data/files/puppet-agent-versions.yaml +46 -0
  18. data/lib/simp/beaker_helpers.rb +1231 -0
  19. data/lib/simp/beaker_helpers/constants.rb +25 -0
  20. data/lib/simp/beaker_helpers/inspec.rb +328 -0
  21. data/lib/simp/beaker_helpers/snapshot.rb +156 -0
  22. data/lib/simp/beaker_helpers/ssg.rb +383 -0
  23. data/lib/simp/beaker_helpers/version.rb +5 -0
  24. data/lib/simp/beaker_helpers/windows.rb +16 -0
  25. data/lib/simp/rake/beaker.rb +269 -0
  26. data/simp-beaker-helpers.gemspec +38 -0
  27. data/spec/acceptance/nodesets/default.yml +32 -0
  28. data/spec/acceptance/suites/default/check_puppet_version_spec.rb +23 -0
  29. data/spec/acceptance/suites/default/enable_fips_spec.rb +23 -0
  30. data/spec/acceptance/suites/default/fixture_modules_spec.rb +22 -0
  31. data/spec/acceptance/suites/default/install_simp_deps_repo_spec.rb +43 -0
  32. data/spec/acceptance/suites/default/nodesets +1 -0
  33. data/spec/acceptance/suites/default/pki_tests_spec.rb +55 -0
  34. data/spec/acceptance/suites/default/set_hieradata_on_spec.rb +33 -0
  35. data/spec/acceptance/suites/default/write_hieradata_to_spec.rb +33 -0
  36. data/spec/acceptance/suites/fips_from_fixtures/00_default_spec.rb +63 -0
  37. data/spec/acceptance/suites/fips_from_fixtures/metadata.yml +2 -0
  38. data/spec/acceptance/suites/fips_from_fixtures/nodesets +1 -0
  39. data/spec/acceptance/suites/offline/00_default_spec.rb +165 -0
  40. data/spec/acceptance/suites/offline/README +2 -0
  41. data/spec/acceptance/suites/offline/nodesets/default.yml +26 -0
  42. data/spec/acceptance/suites/puppet_collections/00_default_spec.rb +25 -0
  43. data/spec/acceptance/suites/puppet_collections/metadata.yml +2 -0
  44. data/spec/acceptance/suites/puppet_collections/nodesets/default.yml +30 -0
  45. data/spec/acceptance/suites/snapshot/00_snapshot_test_spec.rb +82 -0
  46. data/spec/acceptance/suites/snapshot/10_general_usage_spec.rb +56 -0
  47. data/spec/acceptance/suites/snapshot/nodesets +1 -0
  48. data/spec/acceptance/suites/windows/00_default_spec.rb +119 -0
  49. data/spec/acceptance/suites/windows/metadata.yml +2 -0
  50. data/spec/acceptance/suites/windows/nodesets/default.yml +33 -0
  51. data/spec/acceptance/suites/windows/nodesets/win2016.yml +35 -0
  52. data/spec/acceptance/suites/windows/nodesets/win2019.yml +34 -0
  53. data/spec/lib/simp/beaker_helpers_spec.rb +216 -0
  54. data/spec/spec_helper.rb +100 -0
  55. data/spec/spec_helper_acceptance.rb +25 -0
  56. metadata +243 -0
@@ -0,0 +1,383 @@
1
+ module Simp::BeakerHelpers
2
+ require 'simp/beaker_helpers/constants'
3
+
4
+ # Helpers for working with the SCAP Security Guide
5
+ class SSG
6
+
7
+ if ENV['BEAKER_ssg_repo']
8
+ GIT_REPO = ENV['BEAKER_ssg_repo']
9
+ else
10
+ fail('You are offline: Set BEAKER_ssg_repo to point to the git repo that hosts the SSG content') unless ONLINE
11
+
12
+ GIT_REPO = 'https://github.com/ComplianceAsCode/content.git'
13
+ end
14
+
15
+ # If this is not set, the closest tag to the default branch will be used
16
+ GIT_BRANCH = nil
17
+
18
+ if ENV['BEAKER_ssg_branch']
19
+ GIT_BRANCH = ENV['BEAKER_ssg_branch']
20
+ end
21
+
22
+ EL_PACKAGES = [
23
+ 'PyYAML',
24
+ 'cmake',
25
+ 'git',
26
+ 'openscap-python',
27
+ 'openscap-utils',
28
+ 'python-lxml',
29
+ 'python-jinja2'
30
+ ]
31
+
32
+ EL8_PACKAGES = [
33
+ 'python3',
34
+ 'python3-pyyaml',
35
+ 'cmake',
36
+ 'git',
37
+ 'openscap-python3',
38
+ 'openscap-utils',
39
+ 'python3-lxml',
40
+ 'python3-jinja2'
41
+ ]
42
+
43
+ OS_INFO = {
44
+ 'RedHat' => {
45
+ '6' => {
46
+ 'required_packages' => EL_PACKAGES,
47
+ 'ssg' => {
48
+ 'profile_target' => 'rhel6',
49
+ 'build_target' => 'rhel6',
50
+ 'datastream' => 'ssg-rhel6-ds.xml'
51
+ }
52
+ },
53
+ '7' => {
54
+ 'required_packages' => EL_PACKAGES,
55
+ 'ssg' => {
56
+ 'profile_target' => 'rhel7',
57
+ 'build_target' => 'rhel7',
58
+ 'datastream' => 'ssg-rhel7-ds.xml'
59
+ }
60
+ },
61
+ '8' => {
62
+ 'required_packages' => EL8_PACKAGES,
63
+ 'ssg' => {
64
+ 'profile_target' => 'rhel8',
65
+ 'build_target' => 'rhel8',
66
+ 'datastream' => 'ssg-rhel8-ds.xml'
67
+ }
68
+ }
69
+ },
70
+ 'CentOS' => {
71
+ '6' => {
72
+ 'required_packages' => EL_PACKAGES,
73
+ 'ssg' => {
74
+ 'profile_target' => 'rhel6',
75
+ 'build_target' => 'centos6',
76
+ 'datastream' => 'ssg-centos6-ds.xml'
77
+ }
78
+ },
79
+ '7' => {
80
+ 'required_packages' => EL_PACKAGES,
81
+ 'ssg' => {
82
+ 'profile_target' => 'rhel7',
83
+ 'build_target' => 'centos7',
84
+ 'datastream' => 'ssg-centos7-ds.xml'
85
+ }
86
+ },
87
+ '8' => {
88
+ 'required_packages' => EL8_PACKAGES,
89
+ 'ssg' => {
90
+ 'profile_target' => 'rhel8',
91
+ 'build_target' => 'centos8',
92
+ 'datastream' => 'ssg-centos8-ds.xml'
93
+ }
94
+ }
95
+ },
96
+ 'OracleLinux' => {
97
+ '7' => {
98
+ 'required_packages' => EL_PACKAGES,
99
+ 'ssg' => {
100
+ 'profile_target' => 'ol7',
101
+ 'build_target' => 'ol7',
102
+ 'datastream' => 'ssg-ol7-ds.xml'
103
+ },
104
+ '8' => {
105
+ 'required_packages' => EL8_PACKAGES,
106
+ 'ssg' => {
107
+ 'profile_target' => 'ol8',
108
+ 'build_target' => 'ol8',
109
+ 'datastream' => 'ssg-ol8-ds.xml'
110
+ }
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ attr_accessor :scap_working_dir
117
+
118
+ # Create a new SSG helper for the specified host
119
+ #
120
+ # @param sut
121
+ # The SUT against which to run
122
+ #
123
+ def initialize(sut)
124
+ @sut = sut
125
+
126
+ @os = fact_on(@sut, 'operatingsystem')
127
+ @os_rel = fact_on(@sut, 'operatingsystemmajrelease')
128
+
129
+ sut.mkdir_p('scap_working_dir')
130
+
131
+ @scap_working_dir = on(sut, 'cd scap_working_dir && pwd').stdout.strip
132
+
133
+ unless OS_INFO[@os]
134
+ fail("Error: The '#{@os}' Operating System is not supported")
135
+ end
136
+
137
+ OS_INFO[@os][@os_rel]['required_packages'].each do |pkg|
138
+ @sut.install_package(pkg)
139
+ end
140
+
141
+ @output_dir = File.absolute_path('sec_results/ssg')
142
+
143
+ unless File.directory?(@output_dir)
144
+ FileUtils.mkdir_p(@output_dir)
145
+ end
146
+
147
+ @result_file = "#{@sut.hostname}-ssg-#{Time.now.to_i}"
148
+
149
+
150
+ get_ssg_datastream
151
+ end
152
+
153
+ def profile_target
154
+ OS_INFO[@os][@os_rel]['ssg']['profile_target']
155
+ end
156
+
157
+ def remediate(profile)
158
+ evaluate(profile, true)
159
+ end
160
+
161
+ def evaluate(profile, remediate=false)
162
+ cmd = "cd #{@scap_working_dir}; oscap xccdf eval"
163
+
164
+ if remediate
165
+ cmd += ' --remediate'
166
+ end
167
+
168
+ cmd += %( --fetch-remote-resources --profile #{profile} --results #{@result_file}.xml --report #{@result_file}.html #{OS_INFO[@os][@os_rel]['ssg']['datastream']})
169
+
170
+ # We accept all exit codes here because there have occasionally been
171
+ # failures in the SSG content and we're not testing that.
172
+
173
+ on(@sut, cmd, :accept_all_exit_codes => true)
174
+
175
+ ['xml', 'html'].each do |ext|
176
+ path = "#{@scap_working_dir}/#{@result_file}.#{ext}"
177
+ scp_from(@sut, path, @output_dir)
178
+
179
+ fail("Could not retrieve #{path} from #{@sut}") unless File.exist?(File.join(@output_dir, "#{@result_file}.#{ext}"))
180
+ end
181
+ end
182
+
183
+ # Output the report
184
+ #
185
+ # @param report
186
+ # The results Hash
187
+ #
188
+ def write_report(report)
189
+ File.open(File.join(@output_dir, @result_file) + '.report', 'w') do |fh|
190
+ fh.puts(report[:report].uncolor)
191
+ end
192
+ end
193
+
194
+ # Retrieve a subset of test results based on a match to filter
195
+ #
196
+ # @param filter [String, Array[String]]
197
+ # A 'short name' filter that will be matched against the rule ID name
198
+ #
199
+ # @param exclusions [String, Array[String]]
200
+ # A 'short name' filter of items that will be removed from the `filter`
201
+ # matches
202
+ #
203
+ # @return [Hash] A Hash of statistics and a formatted report
204
+ #
205
+ # FIXME:
206
+ # - This is a hack! Should be searching for rules based on a set
207
+ # set of STIG ids, but don't see those ids in the oscap results xml.
208
+ # Further mapping is required...
209
+ # - Create the same report structure as inspec
210
+ def process_ssg_results(filter=nil, exclusions=nil)
211
+ self.class.process_ssg_results(
212
+ File.join(@output_dir, @result_file) + '.xml',
213
+ filter,
214
+ exclusions
215
+ )
216
+ end
217
+
218
+ # Process the results of an SSG run
219
+ #
220
+ # @param result_file [String]
221
+ # The oscap result XML file to process
222
+ #
223
+ # @param filter [String, Array[String]]
224
+ # A 'short name' filter that will be matched against the rule ID name
225
+ #
226
+ # @param exclusions [String, Array[String]]
227
+ # A 'short name' filter of items that will be removed from the `filter`
228
+ # matches
229
+ #
230
+ # @return [Hash] A Hash of statistics and a formatted report
231
+ #
232
+ def self.process_ssg_results(result_file, filter=nil, exclusions=nil)
233
+ require 'highline'
234
+ require 'nokogiri'
235
+
236
+ HighLine.colorize_strings
237
+
238
+ fail("Could not find results XML file '#{result_file}'") unless File.exist?(result_file)
239
+
240
+ puts "Processing #{result_file}"
241
+ doc = Nokogiri::XML(File.open(result_file))
242
+
243
+ # because I'm lazy
244
+ doc.remove_namespaces!
245
+
246
+ if filter
247
+ filter = Array(filter)
248
+
249
+ xpath_query = [
250
+ '//rule-result[(',
251
+ ]
252
+
253
+ xpath_query << filter.map do |flt|
254
+ "contains(@idref,'#{flt}')"
255
+ end.join(' or ')
256
+
257
+ xpath_query << ')' if filter.size > 1
258
+
259
+ if exclusions
260
+ exclusions = Array(exclusions)
261
+
262
+ xpath_query << 'and not('
263
+
264
+ xpath_query << exclusions.map do |exl|
265
+ "contains(@idref,'#{exl}')"
266
+ end.join(' or ')
267
+
268
+ xpath_query << ')' if exclusions.size > 1
269
+ end
270
+
271
+ xpath_query << ')]'
272
+
273
+ xpath_query = xpath_query.join(' ')
274
+
275
+ # XPATH to get the pertinent test results:
276
+ # Any node named 'rule-result' for which the attribute 'idref'
277
+ # contains any of the `filter` Strings and does not contain any of the
278
+ # `exclusions` Strings
279
+ result_nodes = doc.xpath(xpath_query)
280
+ else
281
+ result_nodes = doc.xpath('//rule-result')
282
+ end
283
+
284
+ stats = {
285
+ :failed => [],
286
+ :passed => [],
287
+ :skipped => [],
288
+ :filter => filter.nil? ? 'No Filter' : filter,
289
+ :report => nil,
290
+ :score => 0
291
+ }
292
+
293
+ result_nodes.each do |rule_result|
294
+ # Results are recorded in a child node named 'result'.
295
+ # Within the 'result' node, the actual result string is
296
+ # the content of that node's (only) child node.
297
+
298
+ result = rule_result.element_children.at('result')
299
+ result_id = rule_result.attributes['idref'].value.to_s
300
+ result_value = [
301
+ 'Title: ' + doc.xpath("//Rule[@id='#{result_id}']/title/text()").first.to_s,
302
+ ' ID: ' + result_id
303
+ ].join("\n")
304
+
305
+ if result.child.content == 'fail'
306
+ stats[:failed] << result_value.red
307
+ elsif result.child.content == 'pass'
308
+ stats[:passed] << result_value.green
309
+ else
310
+ stats[:skipped] << result_value.yellow
311
+ end
312
+ end
313
+
314
+ report = []
315
+
316
+ report << '== Skipped =='
317
+ report << stats[:skipped].join("\n")
318
+
319
+ report << '== Passed =='
320
+ report << stats[:passed].join("\n")
321
+
322
+ report << '== Failed =='
323
+ report << stats[:failed].join("\n")
324
+
325
+
326
+ report << 'OSCAP Statistics:'
327
+
328
+ if filter
329
+ report << " * Used Filter: 'idref' ~= '#{stats[:filter]}'"
330
+ end
331
+
332
+ report << " * Passed: #{stats[:passed].count.to_s.green}"
333
+ report << " * Failed: #{stats[:failed].count.to_s.red}"
334
+ report << " * Skipped: #{stats[:skipped].count.to_s.yellow}"
335
+
336
+ score = 0
337
+
338
+ if (stats[:passed].count + stats[:failed].count) > 0
339
+ score = ((stats[:passed].count.to_f/(stats[:passed].count + stats[:failed].count)) * 100.0).round(0)
340
+ end
341
+
342
+ report << "\n Score: #{score}%"
343
+
344
+ stats[:score] = score
345
+ stats[:report] = report.join("\n")
346
+
347
+ return stats
348
+ end
349
+
350
+ private
351
+
352
+ def get_ssg_datastream
353
+ # Allow users to point at a specific SSG release 'tar.bz2' file
354
+ ssg_release = ENV['BEAKER_ssg_release']
355
+
356
+ # Grab the latest SSG release in fixtures if it exists
357
+ ssg_release ||= Dir.glob('spec/fixtures/ssg_releases/*.bz2').last
358
+
359
+ if ssg_release
360
+ copy_to(@sut, ssg_release, @scap_working_dir)
361
+
362
+ on(@sut, %(mkdir -p scap-content && tar -xj -C scap-content --strip-components 1 -f #{ssg_release} && cp scap-content/*ds.xml #{@scap_working_dir}))
363
+ else
364
+ on(@sut, %(git clone #{GIT_REPO} scap-content))
365
+ if GIT_BRANCH
366
+ on(@sut, %(cd scap-content; git checkout #{GIT_BRANCH}))
367
+ else
368
+ on(@sut, %(cd scap-content; git checkout $(git describe --abbrev=0 --tags)))
369
+ end
370
+
371
+ # Work around the issue where the profiles now strip out derivative
372
+ # content that isn't explicitlly approved for that OS. This means that
373
+ # we are unable to test CentOS builds against the STIG, etc...
374
+ #
375
+ # This isn't 100% correct but it's "good enough" for an automated CI
376
+ # environment to tell us if something is critically out of alignment.
377
+ on(@sut, %(cd scap-content/build-scripts; sed -i 's/ssg.build_derivatives.profile_handling/#ssg.build_derivatives.profile_handling/g' enable_derivatives.py))
378
+
379
+ on(@sut, %(cd scap-content/build; cmake ../; make -j4 #{OS_INFO[@os][@os_rel]['ssg']['build_target']}-content && cp *ds.xml #{@scap_working_dir}))
380
+ end
381
+ end
382
+ end
383
+ end
@@ -0,0 +1,5 @@
1
+ module Simp; end
2
+
3
+ module Simp::BeakerHelpers
4
+ VERSION = '1.18.8'
5
+ end
@@ -0,0 +1,16 @@
1
+ module Simp; end
2
+ module Simp::BeakerHelpers; end
3
+
4
+ module Simp::BeakerHelpers::Windows
5
+ begin
6
+ require 'beaker-windows'
7
+ rescue LoadError
8
+ logger.error(%{You must include 'beaker-windows' in your Gemfile for windows support})
9
+ exit 1
10
+ end
11
+
12
+ include BeakerWindows::Path
13
+ include BeakerWindows::Powershell
14
+ include BeakerWindows::Registry
15
+ include BeakerWindows::WindowsFeature
16
+ end
@@ -0,0 +1,269 @@
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/tasklib'
4
+ require 'fileutils'
5
+ require 'puppetlabs_spec_helper/tasks/beaker'
6
+ require 'puppetlabs_spec_helper/tasks/fixtures'
7
+
8
+ module Simp; end
9
+ module Simp::Rake
10
+ class Beaker < ::Rake::TaskLib
11
+ def initialize(base_dir)
12
+
13
+ @base_dir = base_dir
14
+ @clean_list = []
15
+
16
+ ::CLEAN.include( %{#{@base_dir}/log} )
17
+ ::CLEAN.include( %{#{@base_dir}/junit} )
18
+ ::CLEAN.include( %{#{@base_dir}/sec_results} )
19
+ ::CLEAN.include( %{#{@base_dir}/spec/fixtures/inspec_deps} )
20
+
21
+ yield self if block_given?
22
+
23
+ ::CLEAN.include( @clean_list )
24
+
25
+ namespace :beaker do
26
+ desc <<-EOM
27
+ Run a Beaker test against a specific Nodeset
28
+ * :nodeset - The nodeset against which you wish to run
29
+ EOM
30
+ task :run, [:nodeset] do |t,args|
31
+ fail "You must pass :nodeset to #{t}" unless args[:nodeset]
32
+ nodeset = args[:nodeset].strip
33
+
34
+ old_stdout = $stdout
35
+ nodesets = StringIO.new
36
+ $stdout = nodesets
37
+
38
+ Rake::Task['beaker_nodes'].invoke
39
+
40
+ $stdout = old_stdout
41
+
42
+ nodesets = nodesets.string.split("\n")
43
+
44
+ fail "Nodeset '#{nodeset}' not found. Valid Nodesets:\n#{nodesets.map{|x| x = %( * #{x})}.join(%(\n))}" unless nodesets.include?(nodeset)
45
+
46
+ ENV['BEAKER_set'] = nodeset
47
+
48
+ Rake::Task['beaker'].invoke
49
+ end
50
+
51
+ desc <<-EOM
52
+ Run Beaker test suites.
53
+ * :suite - A specific suite to run
54
+ * If you set this to `ALL`, all suites will be run
55
+
56
+ * :nodeset - A specific nodeset to run on within a specific suite
57
+ * If you set this to `ALL`, all nodesets will be looped through, starting with 'default'
58
+ * You may also set this to a colon delimited list of short
59
+ nodeset names that will be run in that order
60
+
61
+ ## Suite Execution
62
+
63
+ By default the only suite that will be executed is `default`.
64
+ Since each suite is executed in a new environment, spin up can
65
+ take a lot of time. Therefore, the default is to only run the
66
+ default suite.
67
+
68
+ If there is a suite where the metadata contains `default_run` set
69
+ to the Boolean `true`, then that suite will be part of the
70
+ default suite execution.
71
+
72
+ You can run all suites by setting the passed suite name to `ALL`
73
+ (case sensitive).
74
+
75
+ ## Environment Variables
76
+
77
+ * BEAKER_suite_runall
78
+ * Run all Suites
79
+
80
+ * BEAKER_suite_basedir
81
+ * The base directory where suites will be defined
82
+ * Default: spec/acceptance
83
+
84
+ ## Global Suite Configuration
85
+ A file `config.yml` can be placed in the `suites` directory to
86
+ control certain aspects of the suite run.
87
+
88
+ ### Supported Config:
89
+
90
+ ```yaml
91
+ ---
92
+ # Fail the entire suite at the first failure
93
+ 'fail_fast' : <true|false> => Default: true
94
+ ```
95
+ ## Individual Suite Configuration
96
+
97
+ Each suite may contain a YAML file, metadata.yml, which will be
98
+ used to provide information to the suite of tests.
99
+
100
+ ### Supported Config:
101
+
102
+ ```yaml
103
+ ---
104
+ 'name' : '<User friendly name for the suite>'
105
+
106
+ # Run this suite by default
107
+ 'default_run' : <true|false> => Default: false
108
+ ```
109
+ EOM
110
+ task :suites, [:suite, :nodeset] => ['spec_prep'] do |t,args|
111
+ suite = args[:suite]
112
+ nodeset = args[:nodeset]
113
+
114
+ # Record Tasks That Fail
115
+ # Need to figure out how to capture the errors
116
+ failures = Hash.new
117
+
118
+ suite_basedir = File.join(@base_dir, 'spec/acceptance/suites')
119
+
120
+ if ENV['BEAKER_suite_basedir']
121
+ suite_basedir = ENV['BEAKER_suite_basedir']
122
+ end
123
+
124
+ raise("Error: Suites Directory at '#{suite_basedir}'!") unless File.directory?(suite_basedir)
125
+
126
+ if suite && (suite != 'ALL')
127
+ unless File.directory?(File.join(suite_basedir, suite))
128
+ STDERR.puts("Error: Could not find suite '#{suite}'")
129
+ STDERR.puts("Available Suites:")
130
+ STDERR.puts(' * ' + Dir.glob(
131
+ File.join(suite_basedir, '*')).sort.map{ |x|
132
+ File.basename(x)
133
+ }.join("\n * ")
134
+ )
135
+ exit(1)
136
+ end
137
+ end
138
+
139
+ suite_config = {
140
+ 'fail_fast' => true
141
+ }
142
+ suite_config_metadata_path = File.join(suite_basedir, 'config.yml')
143
+ if File.file?(suite_config_metadata_path)
144
+ suite_config.merge!(YAML.load_file(suite_config_metadata_path))
145
+ end
146
+
147
+ suites = Hash.new
148
+
149
+ if suite && (suite != 'ALL')
150
+ suites[suite] = Hash.new
151
+ # If a suite was set, make sure it runs.
152
+ suites[suite]['default_run'] = true
153
+ else
154
+ Dir.glob(File.join(suite_basedir,'*')) do |file|
155
+ if File.directory?(file)
156
+ suites[File.basename(file)] = Hash.new
157
+
158
+ if suite == 'ALL'
159
+ suites[File.basename(file)]['default_run'] = true
160
+ end
161
+ end
162
+ end
163
+ end
164
+
165
+ suites.keys.each do |ste|
166
+ suites[ste]['name'] = ste
167
+ suites[ste]['path'] = File.join(suite_basedir, ste)
168
+
169
+ metadata_path = File.join(suites[ste]['path'], 'metadata.yml')
170
+ if File.file?(metadata_path)
171
+ suites[ste]['metadata'] = YAML.load_file(metadata_path)
172
+ end
173
+
174
+ unless File.directory?(File.join(suites[ste]['path'],'nodesets'))
175
+ Dir.chdir(suites[ste]['path']) do
176
+ if File.directory?('../../nodesets')
177
+ FileUtils.ln_s('../../nodesets', 'nodesets')
178
+ end
179
+ end
180
+ end
181
+
182
+ suites[ste].merge!(suites[ste]['metadata']) if suites[ste]['metadata']
183
+
184
+ # Ensure that the 'default' suite runs unless explicitly disabled.
185
+ if suites['default']
186
+ if ( suites['default']['default_run'].nil? ) || ( suites['default']['default_run'] == true )
187
+ suites['default']['default_run'] = true
188
+ end
189
+ end
190
+ end
191
+
192
+ raise("Error: No Suites Found in '#{suite_basedir}'!") if suites.empty?
193
+
194
+ # Need to ensure that 'default' is first
195
+ ordered_suites = suites.keys.sort
196
+ default_suite = ordered_suites.delete('default')
197
+ ordered_suites.unshift(default_suite) if default_suite
198
+
199
+ ordered_suites.each do |ste|
200
+
201
+ next unless (suites[ste]['default_run'] == true)
202
+
203
+ name = suites[ste]['name']
204
+
205
+ $stdout.puts("\n\n=== Suite '#{name}' Starting ===\n\n")
206
+
207
+ nodesets = Array.new
208
+ nodeset_path = File.join(suites[ste]['path'],'nodesets')
209
+
210
+ if nodeset
211
+ if nodeset == 'ALL'
212
+ nodesets = Dir.glob(File.join(nodeset_path, '*.yml'))
213
+
214
+ # Make sure we run the default set first
215
+ default_set = nodesets.delete(File.join(nodeset_path, 'default.yml'))
216
+ nodesets.unshift(default_set) if default_set
217
+ else
218
+ nodeset.split(':').each do |tgt_nodeset|
219
+ nodesets << File.join(nodeset_path, "#{tgt_nodeset.strip}.yml")
220
+ end
221
+ end
222
+ else
223
+ nodesets << File.join(nodeset_path, 'default.yml')
224
+ end
225
+
226
+ nodesets.each do |nodeset_yml|
227
+ unless File.file?(nodeset_yml)
228
+ $stdout.puts("=== Suite #{name} Nodeset '#{File.basename(nodeset_yml, '.yml')}' Not Found, Skipping ===")
229
+ next
230
+ end
231
+
232
+ ENV['BEAKER_setfile'] = nodeset_yml
233
+
234
+ Rake::Task[:beaker].clear
235
+ RSpec::Core::RakeTask.new(:beaker) do |tsk|
236
+ tsk.rspec_opts = ['--color']
237
+ tsk.pattern = File.join(suites[ste]['path'])
238
+ end
239
+
240
+ current_suite_task = Rake::Task[:beaker]
241
+
242
+ if suite_config['fail_fast'] == true
243
+ current_suite_task.execute
244
+ else
245
+ begin
246
+ current_suite_task.execute
247
+ rescue SystemExit
248
+ failures[suites[ste]['name']] = {
249
+ 'path' => suites[ste]['path'],
250
+ 'nodeset' => File.basename(nodeset_yml, '.yml')
251
+ }
252
+ end
253
+ end
254
+
255
+ $stdout.puts("\n\n=== Suite '#{name}' Complete ===\n\n")
256
+ end
257
+ end
258
+
259
+ unless failures.keys.empty?
260
+ $stdout.puts("The following tests had failures:")
261
+ failures.keys.sort.each do |ste|
262
+ $stdout.puts(" * #{ste} => #{failures[ste]['path']} on #{failures[ste]['nodeset']}")
263
+ end
264
+ end
265
+ end
266
+ end
267
+ end
268
+ end
269
+ end