cem_acpt 0.1.0 → 0.2.0

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.
@@ -15,12 +15,21 @@ module CemAcpt
15
15
  end
16
16
  end
17
17
 
18
- module PuppetUtils
18
+ # File-related utilities
19
+ module File
20
+ def self.set_permissions(permission, *file_paths)
21
+ file_paths.map { |p| File.chmod(permission, p) }
22
+ end
23
+ end
24
+
25
+ # Puppet-related utilities
26
+ module Puppet
19
27
  DEFAULT_PUPPET_PATH = {
20
28
  nix: '/opt/puppetlabs/bin/puppet',
21
29
  windows: 'C:/Program Files/Puppet Labs/Puppet/bin/puppet.bat',
22
30
  }.freeze
23
31
 
32
+ # Finds and returns the Puppet executable
24
33
  def self.puppet_executable
25
34
  this_os = CemAcpt::Utils.os
26
35
  if File.file?(DEFAULT_PUPPET_PATH[this_os]) && File.executable?(DEFAULT_PUPPET_PATH[this_os])
@@ -39,32 +48,95 @@ module CemAcpt
39
48
  end
40
49
  raise 'Could not find Puppet executable! Is Puppet installed?'
41
50
  end
51
+
52
+ # Builds a Puppet module package.
53
+ # @param module_dir [String] Path to the module directory. If target_dir
54
+ # is specified as a relative path, it will be relative to the module dir.
55
+ # @param target_dir [String] Path to the target directory where the package
56
+ # will be built. This defaults to the relative path 'pkg/'.
57
+ # @param should_log [Boolean] Whether or not to log the build process.
58
+ # @return [String] Path to the built package.
59
+ def self.build_module_package(module_dir, target_dir = nil, should_log: false)
60
+ require 'puppet/modulebuilder'
61
+ require 'fileutils'
62
+
63
+ builder_logger = should_log ? logger : nil
64
+ builder = ::Puppet::Modulebuilder::Builder.new(::File.expand_path(module_dir), target_dir, builder_logger)
65
+
66
+ # Validates module metadata by raising exception if invalid
67
+ _metadata = builder.metadata
68
+
69
+ # Builds the module package
70
+ builder.build
71
+ end
42
72
  end
43
73
 
44
- module NodeUtils
74
+ # Node-related utilities
75
+ module Node
45
76
  def self.random_instance_name(prefix: 'cem-acpt-', length: 24)
46
77
  rand_length = length - prefix.length
47
78
  "#{prefix}#{SecureRandom.hex(rand_length)}"
48
79
  end
80
+ end
81
+
82
+ # SSH-related utilities
83
+ module SSH
84
+ def self.keygen
85
+ bin_path = `command -v ssh-keygen`.chomp
86
+ raise 'Cannot find ssh-keygen! Install it and verify PATH' unless bin_path
87
+
88
+ bin_path
89
+ end
49
90
 
50
- def self.ephemeral_ssh_key(type: 'ecdsa', bits: '521', comment: nil, keydir: '/tmp')
91
+ def self.default_keydir
92
+ ssh_dir = File.join(ENV['HOME'], '.ssh')
93
+ raise "SSH directory at #{ssh_dir} does not exist" unless File.directory?(ssh_dir)
94
+
95
+ ssh_dir
96
+ end
97
+
98
+ def self.ephemeral_ssh_key(type: 'rsa', bits: '4096', comment: nil, keydir: default_keydir)
51
99
  raise ArgumentError, 'keydir does not exist' unless File.directory?(keydir)
52
100
 
53
- keyfile = "#{keydir}/#{SecureRandom.hex(16)}"
54
- keygen_cmd = [
55
- 'ssh-keygen',
56
- "-t #{type}",
57
- "-b #{bits}",
58
- "-f #{keyfile}",
59
- '-N ""',
60
- '-q',
61
- ]
101
+ keyfile = File.join(keydir, SecureRandom.hex(16))
102
+ keygen_cmd = [ssh_keygen, "-t #{type}", "-b #{bits}", "-f #{keyfile}", '-N ""']
62
103
  keygen_cmd << "-C \"#{comment}\"" if comment
63
104
  _, stderr, status = Open3.capture3(keygen_cmd.join(' '))
64
105
  raise "Failed to generate ephemeral SSH key: #{stderr}" unless status.success?
65
106
 
66
107
  [keyfile, "#{keyfile}.pub"]
67
108
  end
109
+
110
+ def self.acpt_known_hosts(keydir: default_keydir, file_name: 'acpt_known_hosts', overwrite: true)
111
+ kh_file = File.join(keydir, file_name)
112
+ File.open(kh_file, 'w') { |f| f.write("\n") } unless File.exist?(kh_file) && !overwrite
113
+ kh_file
114
+ end
115
+
116
+ def self.set_ssh_file_permissions(priv_key, pub_key, known_hosts)
117
+ CemAcpt::Utils::File.set_permissions(0o600, priv_key, pub_key, known_hosts)
118
+ end
119
+ end
120
+
121
+ # Terminal-related utilities
122
+ module Terminal
123
+ def self.keep_terminal_alive
124
+ require 'concurrent-ruby'
125
+ executor = Concurrent::SingleThreadExecutor.new
126
+ executor.post do
127
+ loop do
128
+ $stdout.print(".\r")
129
+ sleep(1)
130
+ $stdout.print("..\r")
131
+ sleep(1)
132
+ $stdout.print("...\r")
133
+ sleep(1)
134
+ $stdout.print(" \r")
135
+ sleep(1)
136
+ end
137
+ end
138
+ executor
139
+ end
68
140
  end
69
141
  end
70
142
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CemAcpt
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/cem_acpt.rb CHANGED
@@ -10,18 +10,25 @@ module CemAcpt
10
10
  end
11
11
 
12
12
  def self.run(params)
13
- require_relative 'cem_acpt/runner'
13
+ require_relative 'cem_acpt/context'
14
14
 
15
- logger.level = params[:log_level] if params[:log_level]
16
-
17
- if params[:log_file]
18
- log_fmt = params[:log_format] ? new_log_formatter(params[:log_format]) : new_log_formatter(:file)
19
- logger.formatter = log_fmt
20
- logger.reopen(params[:log_file])
21
- elsif params[:log_format]
22
- logger.formatter = new_log_formatter(params[:log_format])
15
+ log_level = params[:log_level]
16
+ log_formatter = params[:log_format] ? new_log_formatter(params[:log_format]) : nil
17
+ logdevs = [$stdout]
18
+ if params[:log_file] && params[:quiet]
19
+ logdevs = [params[:log_file]]
20
+ elsif params[:log_file] && !params[:quiet]
21
+ logdevs << params[:log_file]
23
22
  end
24
-
25
- CemAcpt::RunHandler.new(params).run
23
+ if (params[:CI] || ENV['CI'] || ENV['GITHUB_ACTION']) && !logdevs.include?($stdout)
24
+ logdevs << $stdout
25
+ end
26
+ new_logger(
27
+ *logdevs,
28
+ level: log_level,
29
+ formatter: log_formatter,
30
+ )
31
+ exit_code = CemAcpt::Context.with(params, &:run)
32
+ exit exit_code
26
33
  end
27
34
  end
metadata CHANGED
@@ -1,127 +1,127 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cem_acpt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Heston Snodgrass
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-04 00:00:00.000000000 Z
11
+ date: 2022-06-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: concurrent-ruby
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
18
  version: 1.1.9
20
- type: :runtime
19
+ name: concurrent-ruby
21
20
  prerelease: false
21
+ type: :runtime
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 1.1.9
27
27
  - !ruby/object:Gem::Dependency
28
- name: deep_merge
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - "~>"
32
31
  - !ruby/object:Gem::Version
33
32
  version: 1.2.2
34
- type: :runtime
33
+ name: deep_merge
35
34
  prerelease: false
35
+ type: :runtime
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: 1.2.2
41
41
  - !ruby/object:Gem::Dependency
42
- name: net-scp
43
42
  requirement: !ruby/object:Gem::Requirement
44
43
  requirements:
45
44
  - - "~>"
46
45
  - !ruby/object:Gem::Version
47
- version: '3.0'
48
- type: :runtime
46
+ version: 7.0.0.beta1
47
+ name: net-ssh
49
48
  prerelease: false
49
+ type: :runtime
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '3.0'
54
+ version: 7.0.0.beta1
55
55
  - !ruby/object:Gem::Dependency
56
- name: net-ssh
57
56
  requirement: !ruby/object:Gem::Requirement
58
57
  requirements:
59
- - - "~>"
58
+ - - ">"
60
59
  - !ruby/object:Gem::Version
61
- version: '6.1'
62
- type: :runtime
60
+ version: 0.0.1
61
+ name: puppet-modulebuilder
63
62
  prerelease: false
63
+ type: :runtime
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">"
67
67
  - !ruby/object:Gem::Version
68
- version: '6.1'
68
+ version: 0.0.1
69
69
  - !ruby/object:Gem::Dependency
70
- name: puppet-modulebuilder
71
70
  requirement: !ruby/object:Gem::Requirement
72
71
  requirements:
73
- - - ">"
72
+ - - ">="
74
73
  - !ruby/object:Gem::Version
75
- version: 0.0.1
76
- type: :runtime
74
+ version: '0'
75
+ name: rake
77
76
  prerelease: false
77
+ type: :runtime
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">"
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: 0.0.1
82
+ version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: rake
85
84
  requirement: !ruby/object:Gem::Requirement
86
85
  requirements:
87
- - - ">"
86
+ - - ">="
88
87
  - !ruby/object:Gem::Version
89
- version: '0.8'
90
- type: :runtime
88
+ version: '0'
89
+ name: rspec
91
90
  prerelease: false
91
+ type: :runtime
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ">"
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
- version: '0.8'
96
+ version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: rspec
99
98
  requirement: !ruby/object:Gem::Requirement
100
99
  requirements:
101
- - - ">"
100
+ - - ">="
102
101
  - !ruby/object:Gem::Version
103
- version: '0.1'
104
- type: :runtime
102
+ version: '0'
103
+ name: serverspec-cem-acpt
105
104
  prerelease: false
105
+ type: :runtime
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ">"
108
+ - - ">="
109
109
  - !ruby/object:Gem::Version
110
- version: '0.1'
110
+ version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
- name: serverspec
113
112
  requirement: !ruby/object:Gem::Requirement
114
113
  requirements:
115
- - - ">"
114
+ - - ">="
116
115
  - !ruby/object:Gem::Version
117
- version: '0.1'
118
- type: :runtime
116
+ version: '0'
117
+ name: rubocop
119
118
  prerelease: false
119
+ type: :development
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - ">"
122
+ - - ">="
123
123
  - !ruby/object:Gem::Version
124
- version: '0.1'
124
+ version: '0'
125
125
  description: Litmus-like library focusing on CEM Acceptance Tests
126
126
  email:
127
127
  - hsnodgrass3@gmail.com
@@ -132,7 +132,6 @@ extra_rdoc_files: []
132
132
  files:
133
133
  - ".gitignore"
134
134
  - ".rspec"
135
- - ".travis.yml"
136
135
  - CODEOWNERS
137
136
  - Gemfile
138
137
  - Gemfile.lock
@@ -159,16 +158,20 @@ files:
159
158
  - lib/cem_acpt/platform/gcp/compute.rb
160
159
  - lib/cem_acpt/platform/vmpooler.rb
161
160
  - lib/cem_acpt/puppet_helpers.rb
162
- - lib/cem_acpt/runner.rb
161
+ - lib/cem_acpt/rspec_utils.rb
163
162
  - lib/cem_acpt/shared_objects.rb
164
163
  - lib/cem_acpt/spec_helper_acceptance.rb
165
164
  - lib/cem_acpt/test_data.rb
165
+ - lib/cem_acpt/test_runner.rb
166
+ - lib/cem_acpt/test_runner/run_handler.rb
167
+ - lib/cem_acpt/test_runner/runner.rb
168
+ - lib/cem_acpt/test_runner/runner_result.rb
166
169
  - lib/cem_acpt/utils.rb
167
170
  - lib/cem_acpt/version.rb
168
171
  - sample_config.yaml
169
172
  homepage: https://github.com/puppetlabs/cem_acpt
170
173
  licenses:
171
- - MIT
174
+ - proprietary
172
175
  metadata:
173
176
  homepage_uri: https://github.com/puppetlabs/cem_acpt
174
177
  source_code_uri: https://github.com/puppetlabs/cem_acpt
@@ -188,7 +191,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
188
191
  - !ruby/object:Gem::Version
189
192
  version: '0'
190
193
  requirements: []
191
- rubygems_version: 3.1.4
194
+ rubygems_version: 3.2.29
192
195
  signing_key:
193
196
  specification_version: 4
194
197
  summary: CEM Acceptance Tests
data/.travis.yml DELETED
@@ -1,6 +0,0 @@
1
- ---
2
- language: ruby
3
- cache: bundler
4
- rvm:
5
- - 2.7.2
6
- before_install: gem install bundler -v 2.1.4
@@ -1,304 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'concurrent-ruby'
4
- require 'json'
5
- require 'open3'
6
-
7
- module CemAcpt
8
- require_relative 'bootstrap'
9
- require_relative 'context'
10
- require_relative 'logging'
11
- require_relative 'puppet_helpers'
12
- require_relative 'test_data'
13
- require_relative 'utils'
14
-
15
- # RunHandler orchestrates the acceptance test suites, including
16
- # creating Runner objects, handling input and output, and exception
17
- # handling.
18
- class RunHandler
19
- include CemAcpt::Logging
20
-
21
- attr_accessor :params, :config_file
22
-
23
- # @param params [Hash] the parameters passed from the command line
24
- def initialize(params)
25
- @params = params
26
- @config_file = params[:config_file] || File.expand_path('./cem_acpt_config.yaml')
27
- @module_pkg_path = Concurrent::IVar.new
28
- @start_time = nil
29
- @runners = Concurrent::Array.new
30
- @results = Concurrent::Map.new
31
- end
32
-
33
- # Runs the acceptance test suites.
34
- def run
35
- @start_time = Time.now
36
- logger.info("Running acceptance test suite at #{@start_time}")
37
- logger.debug("Params: #{@params}")
38
- logger.debug("Config file: #{@config_file}")
39
- logger.info("Using module directory: #{@params[:module_dir]}")
40
- context_opts = {
41
- config_opts: @params,
42
- config_file: @config_file,
43
- }
44
- build_module_package
45
- begin
46
- keep_terminal_alive if params[:CI] || ENV['CI'] || ENV['GITHUB_ACTION']
47
- create_and_start_runners(context_opts)
48
- rescue StandardError, SignalException, SystemExit => e
49
- handle_fatal_error(e)
50
- ensure
51
- @keep_terminal_alive&.exit
52
- end
53
- handle_test_results
54
- exit_code = @runners.map(&:spec_exit_code).any? { |rc| rc != 0 } ? 1 : 0
55
- exit exit_code
56
- end
57
-
58
- private
59
-
60
- # Prints periods to the terminal in a single line to keep the terminal
61
- # alive. This is used when running in CI mode.
62
- def keep_terminal_alive
63
- @keep_terminal_alive = Thread.new do
64
- loop do
65
- $stdout.print("|\r")
66
- sleep(1)
67
- $stdout.print("/\r")
68
- sleep(1)
69
- $stdout.print("-\r")
70
- sleep(1)
71
- $stdout.print("\\\r")
72
- sleep(1)
73
- end
74
- end
75
- end
76
-
77
- # @return [String] the module directory that contains the acceptance tests
78
- def module_dir
79
- if @params.key?(:working_dir)
80
- @params[:working_dir]
81
- else
82
- Dir.pwd
83
- end
84
- end
85
-
86
- # Builds the module package in a separate thread.
87
- # This thread is set to abort_on_exception so that any
88
- # exceptions raised in the thread will be caught and
89
- # handled by the main thread.
90
- def build_module_package
91
- pkg_thread = Thread.new do
92
- pkg_path = CemAcpt::PuppetHelpers::Module.build_module_package(@params[:module_dir])
93
- @module_pkg_path.set(pkg_path)
94
- end
95
- pkg_thread.abort_on_exception = true
96
- end
97
-
98
- # Creates and starts Runner objects for each node in the acceptance test suites.
99
- # @param context_opts [Hash] the options to pass to the Context object
100
- # @param timeout [Integer] the timeout to use for the Runner object threads in seconds
101
- def create_and_start_runners(context_opts, timeout = 600)
102
- CemAcpt::Context.with(**context_opts) do |nodes, conf, tdata, node_inv|
103
- nodes.each do |node|
104
- runner = CemAcpt::Runner.new(node, conf, tdata, node_inv, @module_pkg_path, @results)
105
- runner.start
106
- @runners << runner
107
- end
108
- @runners.each { |r| r.join(timeout) }
109
- end
110
- end
111
-
112
- # Handles how test results are logged.
113
- def handle_test_results
114
- @results.each_pair do |node, result|
115
- logger.info("SUMMARY: #{result['summary_line']} on node #{node}")
116
- next unless test_failures?(result)
117
-
118
- failed = result['examples'].reject { |e| e['status'] == 'passed' }
119
- failed.each do |e|
120
- logger.error(test_error_msg(node, e))
121
- end
122
- end
123
- debug_test_results if logger.debug?
124
- end
125
-
126
- # Formats a test result for tests that have failed. Is used for logging.
127
- # @param node [String] the name of the node the test ran on
128
- # @param result [Hash] the test result to format
129
- # @return [String] the formatted test result
130
- def test_error_msg(node, result)
131
- [
132
- "TEST FAILED: #{result['id']}",
133
- "DESCRIPTION: #{result['full_description']}",
134
- "STATUS: #{result['status']}",
135
- "LOCATION: #{result['file_path']}:#{result['line_number']}",
136
- "NODE: #{node}",
137
- result['exception']['message'],
138
- "\n",
139
- ].join("\n")
140
- end
141
-
142
- # Logs performance data for the acceptance test suites.
143
- # This is only logged if the log level is set to debug.
144
- def debug_test_results
145
- examples_by_time = []
146
- @results.each_pair do |node, result|
147
- result['examples'].each do |e|
148
- examples_by_time << [e['run_time'], e['id'], e['status'], e['line_number'], node]
149
- end
150
- end
151
- logger.debug('Showing test results in order of execution time...')
152
- examples_by_time.sort_by(&:first).reverse.each do |e|
153
- logger.debug("RUNTIME: #{e[0]}; ID: #{e[1]}; STATUS: #{e[2]}; LINE: #{e[3]}; HOST: #{e[4]};")
154
- end
155
- end
156
-
157
- # Checks for failures in the test results.
158
- # @param result [Hash] the test result to check
159
- # @return [Boolean] whether or not there are test failures in result
160
- def test_failures?(result)
161
- result['summary']['failure_count'].positive? || result['summary']['errors_outside_of_examples_count'].positive?
162
- end
163
-
164
- # Gracefully handles a fatal error and exits the program.
165
- # @param err [StandardError, Exception] the error that caused the fatal error
166
- def handle_fatal_error(err)
167
- @keep_terminal_alive&.exit
168
- logger.fatal("Fatal error: #{err.message}")
169
- logger.debug(err.backtrace.join('; '))
170
- kill_runners
171
- logger.fatal("Exiting with status 1 after #{Time.now - @start_time} seconds")
172
- exit(1)
173
- end
174
-
175
- # Kills all running Runner objects.
176
- def kill_runners
177
- @runners.each(&:kill) unless @runners.empty?
178
- end
179
- end
180
-
181
- # Runner is a class that runs a single acceptance test suite on a single node.
182
- # It is responsible for managing the lifecycle of the test suite and
183
- # reporting the results back to the main thread. Runner objects are created
184
- # by the RunHandler and then, when started, execute their logic in a thread.
185
- class Runner
186
- include CemAcpt::Logging
187
-
188
- attr_reader :spec_exit_code
189
-
190
- # @param node [String] the name of the node to run the acceptance test suite on
191
- # @param conf [CemAcpt::Config] the acceptance test suite configuration
192
- # @param tdata [Hash] the test data to use for the acceptance test suite
193
- # @param node_inv [CemAcpt::NodeInventory] the node inventory to use for the acceptance test suite
194
- # @param module_pkg_path [Concurrent::IVar] the path to the module package
195
- # @param results [Concurrent::Map] the results map to use for reporting test results
196
- def initialize(node, conf, tdata, node_inv, module_pkg_path, results)
197
- @node = node
198
- @conf = conf
199
- @tdata = tdata
200
- @node_inv = node_inv
201
- @module_pkg_path = module_pkg_path
202
- @results = results
203
- @spec_exit_code = 0
204
- validate!
205
- end
206
-
207
- # Starts the runner thread and runs the lifecycle stages of the
208
- # acceptance test suite.
209
- def start
210
- logger.info("Starting test suite for #{@node.node_name}")
211
- @thread = Thread.new do
212
- provision
213
- bootstrap
214
- run_tests
215
- destroy
216
- end
217
- end
218
-
219
- # Joins the runner thread.
220
- # @param timeout [Integer] thread timeout in seconds
221
- def join(timeout = 600)
222
- @thread.join(timeout)
223
- end
224
-
225
- # Runner thread exits and runner node is destroyed, if it exists.
226
- def exit
227
- @thread.exit
228
- @node&.destroy
229
- end
230
-
231
- # Runner thread is killed and runner node is destroyed, if it exists.
232
- def kill
233
- @thread.kill
234
- @node&.destroy
235
- end
236
-
237
- private
238
-
239
- # Provisions the node for the acceptance test suite.
240
- def provision
241
- logger.info("Provisioning #{@node.node_name}...")
242
- start_time = Time.now
243
- @node.provision
244
- sleep(1) until @node.ready?
245
- logger.info("Node #{@node.node_name} is ready...")
246
- node_desc = {
247
- test_data: @node.test_data,
248
- platform: @conf.get('platform'),
249
- local_port: @node.local_port,
250
- }.merge(@node.node)
251
- @node_inv.add(@node.node_name, node_desc)
252
- logger.info("Node #{@node.node_name} provisioned in #{Time.now - start_time} seconds")
253
- @node_inv.save(File.join(@conf.get('module_dir'), 'spec', 'fixtures', 'node_inventory.yaml'))
254
- end
255
-
256
- # Bootstraps the node for the acceptance test suite. Currently, this
257
- # just uploads and installs the module package.
258
- def bootstrap
259
- logger.info("Bootstrapping #{@node.node_name}...")
260
- until File.exist?(@module_pkg_path.value)
261
- logger.debug("Waiting for module package #{@module_pkg_path.value} to exist...")
262
- sleep(1)
263
- end
264
- logger.info("Installing module package #{@module_pkg_path.value}...")
265
- @node.install_puppet_module_package(@module_pkg_path.value)
266
- end
267
-
268
- # Runs the acceptance test suite via rspec.
269
- def run_tests
270
- require 'json'
271
-
272
- logger.info("Running tests for #{@node.node_name}...")
273
- stdout = nil
274
- stderr = nil
275
- # ENV['RSPEC_DEBUG'] = 'true' if @conf.get('log_level') == 'debug'
276
- test_command = "cd #{@conf.get('module_dir')} && bundle exec rspec #{@node.test_data[:test_file]} --format json"
277
- @node.run_tests do
278
- stdout, stderr, status = Open3.capture3(test_command)
279
- @spec_exit_code = status.exitstatus
280
- end
281
- logger.info("Tests completed with exit code: #{@spec_exit_code}")
282
- @results.put_if_absent(@node.node_name, JSON.parse(stdout))
283
- end
284
-
285
- # Destroys the node for the acceptance test suite.
286
- def destroy
287
- if @conf.get('no_destroy_nodes')
288
- logger.info("Not destroying node #{@node.node_name} because 'no_destroy_nodes' is set to true")
289
- return
290
- end
291
- logger.info("Destroying #{@node.node_name}...")
292
- @node.destroy
293
- logger.info("Node #{@node.node_name} destroyed successfully")
294
- end
295
-
296
- # Validates the runner configuration.
297
- def validate!
298
- raise 'No node provided' unless @node
299
- raise 'No config provided' unless @conf
300
- raise 'No test data provided' unless @tdata
301
- raise 'No node inventory provided' unless @node_inv
302
- end
303
- end
304
- end