cem_acpt 0.2.5 → 0.6.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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/spec.yml +38 -0
  3. data/Gemfile +4 -3
  4. data/Gemfile.lock +85 -56
  5. data/README.md +144 -83
  6. data/cem_acpt.gemspec +8 -7
  7. data/exe/cem_acpt +41 -7
  8. data/lib/cem_acpt/config.rb +345 -0
  9. data/lib/cem_acpt/core_extensions.rb +17 -61
  10. data/lib/cem_acpt/goss/api/action_response.rb +175 -0
  11. data/lib/cem_acpt/goss/api.rb +83 -0
  12. data/lib/cem_acpt/goss.rb +8 -0
  13. data/lib/cem_acpt/image_name_builder.rb +0 -9
  14. data/lib/cem_acpt/logging/formatter.rb +97 -0
  15. data/lib/cem_acpt/logging.rb +168 -142
  16. data/lib/cem_acpt/platform/base.rb +26 -37
  17. data/lib/cem_acpt/platform/gcp.rb +48 -62
  18. data/lib/cem_acpt/platform.rb +30 -28
  19. data/lib/cem_acpt/provision/terraform/linux.rb +47 -0
  20. data/lib/cem_acpt/provision/terraform/os_data.rb +72 -0
  21. data/lib/cem_acpt/provision/terraform/windows.rb +22 -0
  22. data/lib/cem_acpt/provision/terraform.rb +193 -0
  23. data/lib/cem_acpt/provision.rb +20 -0
  24. data/lib/cem_acpt/puppet_helpers.rb +0 -1
  25. data/lib/cem_acpt/test_data.rb +23 -13
  26. data/lib/cem_acpt/test_runner/log_formatter/goss_action_response.rb +104 -0
  27. data/lib/cem_acpt/test_runner/log_formatter.rb +10 -0
  28. data/lib/cem_acpt/test_runner.rb +170 -3
  29. data/lib/cem_acpt/utils/puppet.rb +29 -0
  30. data/lib/cem_acpt/utils/ssh.rb +197 -0
  31. data/lib/cem_acpt/utils/terminal.rb +27 -0
  32. data/lib/cem_acpt/utils.rb +4 -138
  33. data/lib/cem_acpt/version.rb +1 -1
  34. data/lib/cem_acpt.rb +70 -20
  35. data/lib/terraform/gcp/linux/goss/puppet_idempotent.yaml +10 -0
  36. data/lib/terraform/gcp/linux/goss/puppet_noop.yaml +12 -0
  37. data/lib/terraform/gcp/linux/main.tf +191 -0
  38. data/lib/terraform/gcp/linux/systemd/goss-acpt.service +8 -0
  39. data/lib/terraform/gcp/linux/systemd/goss-idempotent.service +8 -0
  40. data/lib/terraform/gcp/linux/systemd/goss-noop.service +8 -0
  41. data/lib/terraform/gcp/windows/.keep +0 -0
  42. data/sample_config.yaml +22 -21
  43. metadata +88 -56
  44. data/lib/cem_acpt/bootstrap/bootstrapper.rb +0 -206
  45. data/lib/cem_acpt/bootstrap/operating_system/rhel_family.rb +0 -129
  46. data/lib/cem_acpt/bootstrap/operating_system.rb +0 -17
  47. data/lib/cem_acpt/bootstrap.rb +0 -12
  48. data/lib/cem_acpt/context.rb +0 -153
  49. data/lib/cem_acpt/platform/base/cmd.rb +0 -71
  50. data/lib/cem_acpt/platform/gcp/cmd.rb +0 -345
  51. data/lib/cem_acpt/platform/gcp/compute.rb +0 -332
  52. data/lib/cem_acpt/platform/vmpooler.rb +0 -24
  53. data/lib/cem_acpt/rspec_utils.rb +0 -242
  54. data/lib/cem_acpt/shared_objects.rb +0 -537
  55. data/lib/cem_acpt/spec_helper_acceptance.rb +0 -184
  56. data/lib/cem_acpt/test_runner/run_handler.rb +0 -187
  57. data/lib/cem_acpt/test_runner/runner.rb +0 -210
  58. data/lib/cem_acpt/test_runner/runner_result.rb +0 -103
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'goss/api'
4
+
5
+ module CemAcpt
6
+ # Holds all the Goss related code
7
+ module Goss; end
8
+ end
@@ -41,13 +41,8 @@ module CemAcpt
41
41
  # @param test_data [Hash] The test data to use to build the image name.
42
42
  # @return [String] The image name.
43
43
  def build(test_data)
44
- logger.debug 'Building image name...'
45
- logger.debug "Using config: #{@config.to_h}"
46
- logger.debug "Test data: #{test_data}"
47
44
  parts = resolve_parts(test_data)
48
- logger.debug "Resolved parts: #{parts}"
49
45
  image_name = create_image_name(parts)
50
- logger.debug "Created image name: #{image_name}"
51
46
  final_image_name = character_substitutions(image_name)
52
47
  logger.debug "Final image name: #{final_image_name}"
53
48
  final_image_name
@@ -61,10 +56,8 @@ module CemAcpt
61
56
  # @return [Array] The parts array with variables resolved.
62
57
  def resolve_parts(test_data)
63
58
  @config[:parts].each_with_object([]) do |part, ary|
64
- logger.debug "Resolving part: #{part}"
65
59
  if part.start_with?('$')
66
60
  var_path = part[1..-1]
67
- logger.debug "Resolving variable path: #{var_path}"
68
61
  ary << test_data.dot_dig(var_path)
69
62
  else
70
63
  ary << part
@@ -77,10 +70,8 @@ module CemAcpt
77
70
  # @return [String] The image name.
78
71
  def create_image_name(parts)
79
72
  image_name = @config[:join_with] ? parts.join(@config[:join_with]) : parts.join
80
- logger.debug("Final image name: #{image_name}")
81
73
 
82
74
  if @config[:validation_pattern]
83
- logger.debug "Validating image name: #{image_name}..."
84
75
  return image_name if image_name.match?(@config[:validation_pattern])
85
76
 
86
77
  raise "Image name #{image_name} does not match validation pattern #{@config[:validation_pattern]}"
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CemAcpt
4
+ module Logging
5
+ module Formatter
6
+ class << self
7
+ def valid_format?(log_format)
8
+ all.any? { |f| f.log_format == log_format.downcase.to_sym }
9
+ end
10
+
11
+ def for(log_format)
12
+ formatter = all.find { |f| f.log_format == log_format.downcase.to_sym }
13
+ raise "Unknown log format: #{log_format}" unless formatter
14
+
15
+ formatter.get
16
+ end
17
+
18
+ def all
19
+ @all ||= [FileFormatter.new, JSONFormatter.new, TextFormatter.new, GithubActionFormatter.new]
20
+ end
21
+ end
22
+
23
+ class FileFormatter
24
+ attr_reader :log_format
25
+
26
+ def initialize
27
+ @log_format = :file
28
+ end
29
+
30
+ def get
31
+ proc do |severity, datetime, progname, msg|
32
+ format(severity, datetime, progname, msg)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def format(severity, datetime, progname, msg)
39
+ "[#{datetime}] #{severity}: #{progname}: #{msg}\n"
40
+ end
41
+ end
42
+
43
+ class JSONFormatter < FileFormatter
44
+ def initialize
45
+ super
46
+ @log_format = :json
47
+ end
48
+
49
+ private
50
+
51
+ def format(severity, datetime, progname, msg)
52
+ require 'json'
53
+ {
54
+ timestamp: datetime,
55
+ progname: progname,
56
+ severity: severity,
57
+ message: msg,
58
+ }.to_json + "\n"
59
+ end
60
+ end
61
+
62
+ class TextFormatter < FileFormatter
63
+ def initialize
64
+ super
65
+ @log_format = :text
66
+ end
67
+
68
+ private
69
+
70
+ def format(severity, _datetime, progname, msg)
71
+ "#{severity}: #{progname}: #{msg}\n"
72
+ end
73
+ end
74
+
75
+ class GithubActionFormatter < FileFormatter
76
+ SEV_MAP = {
77
+ 'DEBUG' => '::debug::',
78
+ 'INFO' => '::notice::',
79
+ 'WARN' => '::warning::',
80
+ 'ERROR' => '::error::',
81
+ 'FATAL' => '::error::',
82
+ }.freeze
83
+
84
+ def initialize
85
+ super
86
+ @log_format = :github_action
87
+ end
88
+
89
+ private
90
+
91
+ def format(severity, _datetime, progname, msg)
92
+ "#{SEV_MAP[severity]}[#{progname}] #{msg}\n"
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -1,27 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'logger'
4
+ require 'cem_acpt/logging/formatter'
4
5
 
5
6
  module CemAcpt
6
7
  # Logging for CemAcpt
7
8
  module Logging
8
9
  LEVEL_MAP = {
9
- 'debug' => Logger::DEBUG,
10
- 'info' => Logger::INFO,
11
- 'warn' => Logger::WARN,
12
- 'error' => Logger::ERROR,
13
- 'fatal' => Logger::FATAL,
14
- 'unknown' => Logger::UNKNOWN,
10
+ 'debug' => ::Logger::DEBUG,
11
+ 'info' => ::Logger::INFO,
12
+ 'warn' => ::Logger::WARN,
13
+ 'error' => ::Logger::ERROR,
14
+ 'fatal' => ::Logger::FATAL,
15
+ 'unknown' => ::Logger::UNKNOWN,
15
16
  }
16
17
 
17
18
  # Delegator class for when you want to log to multiple devices
18
19
  # at the same time, such as STDOUT and a file.
19
- # @param loggers [::Logger] one or more instances of Logger
20
+ # @param loggers [Logger] one or more instances of CemAcpt::Logging::Logger
20
21
  class MultiLogger
22
+ # @param configs [Array<Hash>] an array of hashes containing the
23
+ # configuration for each logger. Each hash must contain the keys
24
+ # :logdev, :shift_age, and :shift_size. The values for these keys
25
+ # are passed to the Logger.new method. Any other keys and values
26
+ # are passed to the Logger.new method as keyword arguments.
27
+ # @return [MultiLogger] a new instance of MultiLogger
28
+ def self.from_logger_configs(configs)
29
+ return if configs.nil? || configs.empty?
30
+
31
+ loggers = configs.map do |config|
32
+ CemAcpt::Logging::Logger.new(config[:logdev],
33
+ config[:shift_age],
34
+ config[:shift_size],
35
+ **config.except(:logdev, :shift_age, :shift_size))
36
+ end
37
+ new(*loggers)
38
+ end
39
+
21
40
  def initialize(*loggers)
22
41
  @loggers = loggers
23
42
  end
24
43
 
44
+ def logger_configs
45
+ @loggers.map(&:config_hash)
46
+ end
47
+
25
48
  def method_missing(m, *args, &block)
26
49
  if @loggers.all? { |l| l.respond_to?(m) }
27
50
  @loggers.map { |l| l.send(m, *args, &block) }
@@ -35,6 +58,137 @@ module CemAcpt
35
58
  end
36
59
  end
37
60
 
61
+ # Delagator class for the standard Ruby Logger class.
62
+ # This class is used to ensure the Github Actions formatter
63
+ # correctly formats the log output when using the standard
64
+ # Ruby Logger class methods such as #info, #debug, etc.
65
+ class Logger < ::Logger
66
+ attr_reader :config_hash
67
+ attr_accessor :trap_context
68
+
69
+ def initialize(logdev, shift_age = 0, shift_size = 1_048_576, **kwargs)
70
+ @config_hash = { logdev: logdev, shift_age: shift_age, shift_size: shift_size, **kwargs }
71
+ @ci_mode = (kwargs[:formatter].downcase.to_sym == :github_action || !ENV['GITHUB_ACTIONS'].nil? || !ENV['CI'].nil?)
72
+ kwargs[:formatter] = CemAcpt::Logging::Formatter.for(kwargs[:formatter]) if kwargs[:formatter]
73
+ super(logdev, shift_age, shift_size, **kwargs)
74
+ @raw_logdev = logdev # Used for logging from trap context
75
+ @trap_context = false # If true, will not use the standard Logger methods and will write directly to the logdev
76
+ @verbose = false
77
+ end
78
+
79
+ def set_verbose(value)
80
+ raise ArgumentError, 'value must be a boolean' unless [true, false].include?(value)
81
+
82
+ @verbose = value
83
+ end
84
+
85
+ def verbose(progname = nil, &block)
86
+ return unless @verbose
87
+
88
+ debug(progname, &block)
89
+ end
90
+
91
+ def debug(progname = nil, &block)
92
+ return if log_trap_context('debug', progname, &block)
93
+ return if log_ci_mode('debug', progname, &block)
94
+
95
+ super
96
+ end
97
+
98
+ def info(progname = nil, &block)
99
+ return if log_trap_context('info', progname, &block)
100
+ return if log_ci_mode('info', progname, &block)
101
+
102
+ super
103
+ end
104
+
105
+ def warn(progname = nil, &block)
106
+ return if log_trap_context('warn', progname, &block)
107
+ return if log_ci_mode('warn', progname, &block)
108
+
109
+ super
110
+ end
111
+
112
+ def error(progname = nil, &block)
113
+ return if log_trap_context('error', progname, &block)
114
+ return if log_ci_mode('error', progname, &block)
115
+
116
+ super
117
+ end
118
+
119
+ def fatal(progname = nil, &block)
120
+ return if log_trap_context('fatal', progname, &block)
121
+ return if log_ci_mode('fatal', progname, &block)
122
+
123
+ super
124
+ end
125
+
126
+ # Wraps the given block in a Github Actions group if in CI mode
127
+ # @param name [String] the name of the group
128
+ def with_ci_group(name)
129
+ return yield unless @ci_mode
130
+
131
+ self.<< "::group::#{name}\n"
132
+ yield
133
+ ensure
134
+ self.<< "::endgroup::\n" if @ci_mode
135
+ end
136
+
137
+ private
138
+
139
+ # Can't use the standard Logger methods when in trap context
140
+ # because they don't work. So we have to write directly to the
141
+ # raw logdev. Unlike the standard Logger methods, there is no
142
+ # Mutex lock, so messages are not guaranteed to be in order.
143
+ # This is necessary in the trap context because using a Mutex
144
+ # lock in a trap context will cause a deadlock.
145
+ def log_trap_context(severity, progname = nil, &block)
146
+ return false unless @trap_context && level <= LEVEL_MAP[severity]
147
+
148
+ if @raw_logdev.respond_to?(:puts)
149
+ @raw_logdev.puts(ci_format(severity, false, progname, &block))
150
+ elsif @raw_logdev.respond_to?(:write)
151
+ @raw_logdev.write(ci_format(severity, true, progname, &block))
152
+ else
153
+ # Default to logging to STDERR
154
+ $stderr.puts(ci_format(severity, false, progname, &block))
155
+ end
156
+ true
157
+ end
158
+
159
+ # CI mode uses << instead of the standard Logger methods
160
+ # because Github Actions wants messages to be echoed. This
161
+ # is a way to ensure that, in the event of using a multi-logger
162
+ # with a standard Logger, the CI mode messages are still
163
+ # formatted correctly.
164
+ def log_ci_mode(severity, progname = nil, &block)
165
+ return false unless @ci_mode && level <= LEVEL_MAP[severity]
166
+
167
+ self.<<(ci_format(severity, true, progname, &block))
168
+ true
169
+ end
170
+
171
+ # Formats the log message for CI mode, translating the severity
172
+ # level to the correct format for Github Actions and adding
173
+ # the colon separators.
174
+ def ci_format(severity, newline = true, progname = nil, &block)
175
+ sev = if severity == 'fatal'
176
+ 'error'
177
+ elsif severity == 'warn'
178
+ 'warning'
179
+ elsif severity == 'info'
180
+ 'notice'
181
+ else
182
+ severity
183
+ end
184
+ block_message = block_given? ? block.call : nil
185
+ base = [progname, block_message].compact.join(': ')
186
+ return if base.empty?
187
+
188
+ newline ? "::#{sev}::#{base}\n" : "::#{sev}::#{base}"
189
+ end
190
+ end
191
+
38
192
  class << self
39
193
  def new_logger(*logdevs, **configs)
40
194
  new_configs = current_log_config.merge(configs.reject { |_, v| v.nil? })
@@ -57,14 +211,9 @@ module CemAcpt
57
211
 
58
212
  # Exposes a logger instance. Will either use the currently set logger or
59
213
  # create a new one.
60
- # @return [Logger]
214
+ # @return [MultiLogger]
61
215
  def logger
62
- @logger ||= Logger.new(
63
- current_log_config[:logdev],
64
- current_log_config[:shift_age],
65
- current_log_config[:shift_size],
66
- **current_log_config.reject { |k, _| %i[logdev shift_age shift_size].include?(k) }
67
- )
216
+ @logger ||= new_logger($stdout)
68
217
  end
69
218
 
70
219
  # Shortcut method for logger.level
@@ -92,41 +241,7 @@ module CemAcpt
92
241
  # @param f [Symbol] the log format style to set
93
242
  # @return [Proc] the proc to be passed to Logger#formatter=
94
243
  def new_log_formatter(f)
95
- case f.downcase.to_sym
96
- when :json
97
- require 'json'
98
- @current_log_format = :json
99
- proc do |severity, datetime, progname, msg|
100
- {
101
- timestamp: datetime,
102
- progname: progname,
103
- severity: severity,
104
- message: msg,
105
- }.to_json + "\n"
106
- end
107
- when :text
108
- @current_log_format = :text
109
- proc do |severity, _datetime, _progname, msg|
110
- "#{severity} - #{msg}\n"
111
- end
112
- when :github_action
113
- @current_log_format = :github_action
114
- sev_map = {
115
- 'DEBUG' => '::debug::',
116
- 'INFO' => '::notice::',
117
- 'WARN' => '::warning::',
118
- 'ERROR' => '::error::',
119
- 'FATAL' => '::error::',
120
- }
121
- proc do |severity, _datetime, _progname, msg|
122
- "#{sev_map[severity]}{#{msg}}\n"
123
- end
124
- else
125
- @current_log_format = :file
126
- proc do |severity, datetime, _progname, msg|
127
- "[#{datetime}] #{severity}: #{msg}\n"
128
- end
129
- end
244
+ CemAcpt::Logging::Formatter.for(f)
130
245
  end
131
246
 
132
247
  # Returns the current log config if set, or the default if not.
@@ -134,15 +249,14 @@ module CemAcpt
134
249
  def current_log_config
135
250
  return @log_config if @log_config
136
251
 
137
- formatter = new_log_formatter(current_log_format)
138
252
  @log_config = {
139
253
  logdev: $stdout,
140
254
  shift_age: 'o',
141
255
  shift_size: 1_048_576,
142
- level: Logger::INFO,
256
+ level: ::Logger::INFO,
143
257
  progname: 'CemAcpt',
144
258
  datetime_format: '%Y%m%dT%H%M%S%z',
145
- formatter: formatter,
259
+ formatter: current_log_format,
146
260
  }
147
261
  @log_config
148
262
  end
@@ -177,12 +291,12 @@ module CemAcpt
177
291
  @log_config[:logdev] = $stdout
178
292
  logger.warn("Unknown log target: #{logdev.inspect}, using STDOUT")
179
293
  end
180
- @logger = Logger.new(
294
+ @logger = MultiLogger.new(Logger.new(
181
295
  @log_config[:logdev],
182
296
  @log_config[:shift_age],
183
297
  @log_config[:shift_size],
184
298
  **@log_config.reject { |k, _| %i[logdev shift_age shift_size].include?(k) },
185
- )
299
+ ))
186
300
  end
187
301
  end
188
302
 
@@ -260,92 +374,4 @@ module CemAcpt
260
374
  CemAcpt::Logging.new_log_config(logdev: logdev, shift_age: shift_age, shift_size: shift_size, level: level, formatter: formatter, datetime_format: datetime_format)
261
375
  end
262
376
  end
263
-
264
- module LoggingAsync
265
- require 'concurrent-ruby'
266
-
267
- class << self
268
- def log_write_thread
269
- @log_write_thread ||= Concurrent::SingleThreadExecutor.new
270
- end
271
-
272
- def async_debug(message, prefix = nil)
273
- msg = prefix.nil? || prefix.empty? ? message : "#{prefix} #{message}"
274
- CemAcpt::Logging.logger.debug(msg)
275
- end
276
-
277
- def async_info(message, prefix = nil)
278
- msg = prefix.nil? || prefix.empty? ? message : "#{prefix} #{message}"
279
- CemAcpt::Logging.logger.info(msg)
280
- end
281
-
282
- def async_warn(message, prefix = nil)
283
- msg = prefix.nil? || prefix.empty? ? message : "#{prefix} #{message}"
284
- CemAcpt::Logging.logger.warn(msg)
285
- end
286
-
287
- def async_error(message, prefix = nil)
288
- msg = prefix.nil? || prefix.empty? ? message : "#{prefix} #{message}"
289
- CemAcpt::Logging.logger.error(msg)
290
- end
291
-
292
- def async_fatal(message, prefix = nil)
293
- msg = prefix.nil? || prefix.empty? ? message : "#{prefix} #{message}"
294
- CemAcpt::Logging.logger.fatal(msg)
295
- end
296
- end
297
-
298
- # Provides class method wrappers for logging methods
299
- def self.included(base)
300
- class << base
301
- def log_write_thread
302
- CemAcpt::LoggingAsync.log_write_thread
303
- end
304
-
305
- def async_debug(message, prefix = nil)
306
- CemAcpt::LoggingAsync.async_debug(message, prefix)
307
- end
308
-
309
- def async_info(message, prefix = nil)
310
- CemAcpt::LoggingAsync.async_info(message, prefix)
311
- end
312
-
313
- def async_warn(message, prefix = nil)
314
- CemAcpt::LoggingAsync.async_warn(message, prefix)
315
- end
316
-
317
- def async_error(message, prefix = nil)
318
- CemAcpt::LoggingAsync.async_error(message, prefix)
319
- end
320
-
321
- def async_fatal(message, prefix = nil)
322
- CemAcpt::LoggingAsync.async_fatal(message, prefix)
323
- end
324
- end
325
- end
326
-
327
- def log_write_thread
328
- CemAcpt::LoggingAsync.log_write_thread
329
- end
330
-
331
- def async_debug(message, prefix = nil)
332
- CemAcpt::LoggingAsync.async_debug(message, prefix)
333
- end
334
-
335
- def async_info(message, prefix = nil)
336
- CemAcpt::LoggingAsync.async_info(message, prefix)
337
- end
338
-
339
- def async_warn(message, prefix = nil)
340
- CemAcpt::LoggingAsync.async_warn(message, prefix)
341
- end
342
-
343
- def async_error(message, prefix = nil)
344
- CemAcpt::LoggingAsync.async_error(message, prefix)
345
- end
346
-
347
- def async_fatal(message, prefix = nil)
348
- CemAcpt::LoggingAsync.async_fatal(message, prefix)
349
- end
350
- end
351
377
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
3
4
  require 'securerandom'
4
5
  require_relative File.join(__dir__, '..', 'image_name_builder')
5
6
  require_relative File.join(__dir__, '..', 'logging')
@@ -10,69 +11,57 @@ module CemAcpt::Platform
10
11
  class Base
11
12
  include CemAcpt::Logging
12
13
 
13
- attr_reader :config, :test_data, :local_port, :node_name, :image_name
14
+ attr_reader :test_data, :node_name
14
15
 
15
16
  # @param conf [CemAcpt::Config] the config object.
16
17
  # @param single_test_data [Hash] the test data for the current test.
17
- # @param local_port [Integer] the local port to use for the test.
18
- def initialize(conf, single_test_data, local_port)
18
+ def initialize(config, single_test_data)
19
19
  raise ArgumentError, 'single_test_data must be a Hash' unless single_test_data.is_a?(Hash)
20
20
 
21
- @config = conf.get('node_data')
21
+ @config = config
22
22
  @test_data = single_test_data
23
- @local_port = local_port
24
23
  @node_name = @test_data[:node_name] || random_node_name
25
- @image_name = conf.has?('image_name_builder') ? image_name_builder(conf, single_test_data) : @test_data[:image_name]
26
24
  end
27
25
 
28
- # Node should return a hash of all data about a created node.
29
- def node
30
- raise NotImplementedError, '#node must be implemented by subclass'
26
+ def to_h
27
+ {
28
+ node_name: node_name,
29
+ image_name: image_name,
30
+ test_data: test_data,
31
+ platform_data: platform_data,
32
+ node_data: node_data,
33
+ }
31
34
  end
32
35
 
33
- # Provision a node. Will be called asynchronously.
34
- def provision
35
- raise NotImplementedError, '#provision must be implemented by subclass'
36
+ def to_json(*args)
37
+ to_h.to_json(*args)
36
38
  end
37
39
 
38
- # Destroy a node. Will be called asynchronously.
39
- def destroy
40
- raise NotImplementedError, '#destroy must be implemented by subclass'
40
+ # Data common to all nodes of the same platform.
41
+ def platform_data
42
+ raise NotImplementedError, 'common_data must be implemented by the specific platform module'
41
43
  end
42
44
 
43
- # Tests to see if a node is ready to accept connections from the test suite.
44
- def ready?
45
- raise NotImplementedError, '#ready? must be implemented by subclass'
45
+ # Data specific to the current node.
46
+ def node_data
47
+ raise NotImplementedError, 'node_data must be implemented by the specific platform module'
46
48
  end
47
49
 
48
- # Upload and install a Puppet module package on the node. Blocking call.
49
- def install_puppet_module_package(_module_pkg_path, _remote_path)
50
- raise NotImplementedError, '#install_puppet_module_package must be implemented by subclass'
50
+ # Generates or retrieves an image name from the test data.
51
+ def image_name
52
+ @image_name ||= (@config.has?('image_name_builder') ? image_name_builder(@config, test_data) : test_data[:image_name])
51
53
  end
52
54
 
55
+ private
56
+
53
57
  # Generates a random node name.
54
58
  def random_node_name
55
- "acpt-test-#{SecureRandom.hex(10)}"
59
+ "cem-acpt-#{SecureRandom.hex(12)}"
56
60
  end
57
61
 
58
62
  # Builds an image name if the config specifies to use the image name builder.
59
63
  def image_name_builder(conf, tdata)
60
64
  @image_name_builder ||= CemAcpt::ImageNameBuilder.new(conf).build(tdata)
61
65
  end
62
-
63
- # Returns a command provider specified by the Platform module of the specific platform.
64
- def self.command_provider
65
- raise NotImplementedError, '#command_provider must be implemented by subclass'
66
- end
67
-
68
- # Applies a Puppet manifest on the given node.
69
- def self.apply_manifest(_instance_name, _manifest, _opts = {})
70
- raise NotImplementedError, '#apply_manifest must be implemented by subclass'
71
- end
72
-
73
- # Runs a shell command on the given node.
74
- def self.run_shell(_instance_name, _cmd, _opts = {})
75
- raise NotImplementedError, '#run_shell must be implemented by subclass'
76
- end
77
66
  end
78
67
  end