cem_acpt 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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