cem_acpt 0.1.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.
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CemAcpt
4
+ # This module provides a method and class for extracting and formatting
5
+ # test data.
6
+ module TestData
7
+ require_relative 'core_extensions'
8
+ require_relative 'logging'
9
+
10
+ # Returns a hash of test data.
11
+ # @param config [CemAcpt::Config] the config object
12
+ # @return [Array<Hash>] an array of test data hashes
13
+ def self.acceptance_test_data(config)
14
+ Fetcher.new(config).acceptance_test_data
15
+ end
16
+
17
+ # Fetcher provides the methods for extracting and formatting test data.
18
+ class Fetcher
19
+ include CemAcpt::Logging
20
+ using CemAcpt::CoreExtensions::ExtendedHash
21
+
22
+ attr_reader :module_dir, :acceptance_tests
23
+
24
+ # Initializes a new Fetcher object.
25
+ # @param config [CemAcpt::Config] the config object
26
+ def initialize(config)
27
+ @config = config
28
+ @module_dir = config.get('module_dir')
29
+ @acceptance_tests = find_acceptance_tests(config.get('module_dir'))
30
+ end
31
+
32
+ # Extracts, formats, and returns a test data hash.
33
+ # @return [Array<Hash>] an array of test data hashes
34
+ def acceptance_test_data
35
+ logger.info 'Gathering acceptance test data...'
36
+ acceptance_tests.each_with_object([]) do |t, a|
37
+ logger.debug("Processing #{t}...")
38
+ test_name = File.basename(t, '_spec.rb')
39
+ test_data = {
40
+ test_name: test_name,
41
+ test_file: File.expand_path(t),
42
+ }
43
+ next unless @config.has?('tests') && @config.get('tests').include?(test_name)
44
+
45
+ process_for_each(test_data).each do |test_data_i|
46
+ process_static_vars(test_data_i)
47
+ process_name_pattern_vars(test_name, test_data_i)
48
+ vars_post_processing!(test_data_i)
49
+ test_data_i.format!
50
+ a << test_data_i
51
+ end
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ # Locates acceptance tests in the module directory.
58
+ # @return [Array<String>] the list of acceptance test paths
59
+ def find_acceptance_tests(module_dir)
60
+ tests = Dir.glob(File.join(module_dir, 'spec', 'acceptance', '*_spec.rb'))
61
+ raise 'No acceptance tests found' if tests.empty?
62
+
63
+ logger.info "Found #{tests.size} acceptance tests"
64
+ tests
65
+ end
66
+
67
+ # Processes a for_each statement in the test data config.
68
+ # @param test_data [Hash] the test data hash
69
+ # @return [Array<Hash>] the list of test data hashes
70
+ def process_for_each(test_data)
71
+ return [test_data] unless @config.has?('test_data.for_each')
72
+
73
+ @config.get('test_data.for_each').each_with_object([]) do |(k, v), a|
74
+ v.each do |v_i|
75
+ test_data_i = test_data.dup
76
+ test_data_i[k] = v_i
77
+ a << test_data_i
78
+ end
79
+ end
80
+ end
81
+
82
+ def process_static_vars(test_data)
83
+ return unless @config.has?('test_data.vars')
84
+
85
+ logger.debug('Processing static variables...')
86
+ vars = @config.get('test_data.vars').each_with_object({}) do |(k, v), h|
87
+ logger.debug("Static variable: #{k}")
88
+ h[k] = v
89
+ end
90
+ test_data.merge!(vars)
91
+ test_data.format!
92
+ end
93
+
94
+ def process_name_pattern_vars(test_name, test_data)
95
+ logger.debug('Processing test name pattern...')
96
+ name_pattern_vars = name_pattern_matches(test_name) || {}
97
+ test_data.merge!(name_pattern_vars)
98
+ test_data.format!
99
+ end
100
+
101
+ def name_pattern_matches(test_name)
102
+ return test_name unless @config.has?('test_data.name_pattern_vars')
103
+
104
+ pattern = @config.get('test_data.name_pattern_vars')
105
+ logger.debug("Test name pattern: #{pattern.inspect}")
106
+ pattern_matches = test_name.match(pattern)&.named_captures
107
+ unless pattern_matches
108
+ logger.error("Test name #{test_name} does not match pattern #{pattern.inspect}")
109
+ return
110
+ end
111
+ logger.debug("Test name matches pattern: #{pattern_matches}")
112
+ pattern_matches
113
+ end
114
+
115
+ def vars_post_processing!(test_data)
116
+ return unless @config.has?('test_data.vars_post_processing')
117
+
118
+ logger.debug("Running test name pattern post processing with test data: #{test_data}")
119
+ post_processing_new_vars!(test_data)
120
+ post_processing_delete_vars!(test_data)
121
+ end
122
+
123
+ def post_processing_new_vars!(test_data)
124
+ new_vars = @config.get('test_data.vars_post_processing.new_vars')
125
+ return unless new_vars
126
+
127
+ logger.debug("Processing new variables: #{new_vars}")
128
+ new_vars.each do |var|
129
+ if var.has?('string_split')
130
+ test_data[var.dot_dig('name')] = op_string_split(test_data, var)
131
+ else
132
+ logger.error("Unknown post processing operation for new var #{var}")
133
+ end
134
+ end
135
+ end
136
+
137
+ def op_string_split(test_data, var)
138
+ logger.debug("Processing string split for new var #{var}")
139
+ from_var = var.dot_dig('string_split.from')
140
+ logger.debug("String split from var: #{from_var}")
141
+ from = test_data.dot_dig(from_var)
142
+ logger.debug("String split from value: #{from}")
143
+ parts = from.split(var.dot_dig('string_split.using'))
144
+ logger.debug("String split parts: #{parts}")
145
+ var.has?('string_split.part') ? parts[var.dot_dig('string_split.part')] : parts
146
+ end
147
+
148
+ def post_processing_delete_vars!(test_data)
149
+ delete_vars = @config.get('test_data.vars_post_processing.delete_vars')
150
+ return unless delete_vars
151
+
152
+ logger.debug('Processing delete variables...')
153
+ test_data.reject! { |k, _| delete_vars.include?(k.to_s) }
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open3'
4
+ require 'securerandom'
5
+
6
+ module CemAcpt
7
+ # Utility methods and modules for CemAcpt.
8
+ module Utils
9
+ def self.os
10
+ case RbConfig::CONFIG['host_os']
11
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
12
+ :windows
13
+ else
14
+ :nix
15
+ end
16
+ end
17
+
18
+ module PuppetUtils
19
+ DEFAULT_PUPPET_PATH = {
20
+ nix: '/opt/puppetlabs/bin/puppet',
21
+ windows: 'C:/Program Files/Puppet Labs/Puppet/bin/puppet.bat',
22
+ }.freeze
23
+
24
+ def self.puppet_executable
25
+ this_os = CemAcpt::Utils.os
26
+ if File.file?(DEFAULT_PUPPET_PATH[this_os]) && File.executable?(DEFAULT_PUPPET_PATH[this_os])
27
+ return DEFAULT_PUPPET_PATH[this_os]
28
+ end
29
+
30
+ file_name = 'puppet'
31
+ if this_os == :windows
32
+ exts = ENV['PATHEXT'] ? ".{#{ENV['PATHEXT'].tr(';', ',').tr('.', '').downcase}}" : '.{exe,com,bat}'
33
+ file_name = "#{file_name}#{exts}"
34
+ end
35
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
36
+ if File.file?(File.join(path, file_name)) && File.executable?(File.join(path, file_name))
37
+ return File.join(path, file_name)
38
+ end
39
+ end
40
+ raise 'Could not find Puppet executable! Is Puppet installed?'
41
+ end
42
+ end
43
+
44
+ module NodeUtils
45
+ def self.random_instance_name(prefix: 'cem-acpt-', length: 24)
46
+ rand_length = length - prefix.length
47
+ "#{prefix}#{SecureRandom.hex(rand_length)}"
48
+ end
49
+
50
+ def self.ephemeral_ssh_key(type: 'ecdsa', bits: '521', comment: nil, keydir: '/tmp')
51
+ raise ArgumentError, 'keydir does not exist' unless File.directory?(keydir)
52
+
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
+ ]
62
+ keygen_cmd << "-C \"#{comment}\"" if comment
63
+ _, stderr, status = Open3.capture3(keygen_cmd.join(' '))
64
+ raise "Failed to generate ephemeral SSH key: #{stderr}" unless status.success?
65
+
66
+ [keyfile, "#{keyfile}.pub"]
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CemAcpt
4
+ VERSION = '0.1.0'
5
+ end
data/lib/cem_acpt.rb ADDED
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CemAcpt
4
+ require_relative 'cem_acpt/logging'
5
+ require_relative 'cem_acpt/version'
6
+ require_relative 'cem_acpt/spec_helper_acceptance'
7
+
8
+ class << self
9
+ include CemAcpt::Logging
10
+ end
11
+
12
+ def self.run(params)
13
+ require_relative 'cem_acpt/runner'
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])
23
+ end
24
+
25
+ CemAcpt::RunHandler.new(params).run
26
+ end
27
+ end
@@ -0,0 +1,58 @@
1
+ test_data:
2
+ for_each:
3
+ collection:
4
+ - puppet6
5
+ - puppet7
6
+ vars:
7
+ test_static_var: 'static_var_value'
8
+ name_pattern_vars: !ruby/regexp '/^(?<framework>[a-z]+)_(?<image_fam>[a-z0-9-]+)_(?<firewall>[a-z]+)_(?<framework_vars>[-_a-z0-9]+)$/'
9
+ vars_post_processing:
10
+ new_vars:
11
+ - name: 'profile'
12
+ string_split:
13
+ from: 'framework_vars'
14
+ using: '_'
15
+ part: 0
16
+ - name: 'level'
17
+ string_split:
18
+ from: 'framework_vars'
19
+ using: '_'
20
+ part: 1
21
+ delete_vars:
22
+ - 'framework_vars'
23
+
24
+ platform: gcp
25
+
26
+ node_data:
27
+ machine_type: 'e2-small'
28
+ project:
29
+ name: 'some-project'
30
+ zone: 'us-west1-b'
31
+ disk:
32
+ name: 'disk-1'
33
+ size: 20
34
+ type: 'pd-standard'
35
+ network_interface:
36
+ tier: 'STANDARD'
37
+ subnetwork: 'some-subnet'
38
+ metadata:
39
+ ephemeral_ssh_key:
40
+ lifetime: 2
41
+ keydir: '/tmp/acpt_test_keys'
42
+
43
+ image_name_builder:
44
+ character_substitutions:
45
+ - ['_', '-']
46
+ parts:
47
+ - 'cem-acpt'
48
+ - '$image_fam'
49
+ - '$collection'
50
+ - '$firewall'
51
+ join_with: '-'
52
+
53
+ tests:
54
+ - cis_rhel-7_firewalld_server_1
55
+ - cis_rhel-7_iptables_server_1
56
+ - cis_rhel-8_firewalld_server_1
57
+ - cis_rhel-8_firewalld_server_2
58
+ - cis_rhel-8_iptables_server_1
metadata ADDED
@@ -0,0 +1,195 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cem_acpt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Heston Snodgrass
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-03-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: concurrent-ruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.1.9
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.1.9
27
+ - !ruby/object:Gem::Dependency
28
+ name: deep_merge
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.2.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.2.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: net-scp
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: net-ssh
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '6.1'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '6.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: puppet-modulebuilder
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.0.1
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.0.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.8'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.8'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.1'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.1'
111
+ - !ruby/object:Gem::Dependency
112
+ name: serverspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.1'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.1'
125
+ description: Litmus-like library focusing on CEM Acceptance Tests
126
+ email:
127
+ - hsnodgrass3@gmail.com
128
+ executables:
129
+ - cem_acpt
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - ".gitignore"
134
+ - ".rspec"
135
+ - ".travis.yml"
136
+ - CODEOWNERS
137
+ - Gemfile
138
+ - Gemfile.lock
139
+ - README.md
140
+ - Rakefile
141
+ - bin/console
142
+ - bin/setup
143
+ - cem_acpt.gemspec
144
+ - exe/cem_acpt
145
+ - lib/cem_acpt.rb
146
+ - lib/cem_acpt/bootstrap.rb
147
+ - lib/cem_acpt/bootstrap/bootstrapper.rb
148
+ - lib/cem_acpt/bootstrap/operating_system.rb
149
+ - lib/cem_acpt/bootstrap/operating_system/rhel_family.rb
150
+ - lib/cem_acpt/context.rb
151
+ - lib/cem_acpt/core_extensions.rb
152
+ - lib/cem_acpt/image_name_builder.rb
153
+ - lib/cem_acpt/logging.rb
154
+ - lib/cem_acpt/platform.rb
155
+ - lib/cem_acpt/platform/base.rb
156
+ - lib/cem_acpt/platform/base/cmd.rb
157
+ - lib/cem_acpt/platform/gcp.rb
158
+ - lib/cem_acpt/platform/gcp/cmd.rb
159
+ - lib/cem_acpt/platform/gcp/compute.rb
160
+ - lib/cem_acpt/platform/vmpooler.rb
161
+ - lib/cem_acpt/puppet_helpers.rb
162
+ - lib/cem_acpt/runner.rb
163
+ - lib/cem_acpt/shared_objects.rb
164
+ - lib/cem_acpt/spec_helper_acceptance.rb
165
+ - lib/cem_acpt/test_data.rb
166
+ - lib/cem_acpt/utils.rb
167
+ - lib/cem_acpt/version.rb
168
+ - sample_config.yaml
169
+ homepage: https://github.com/puppetlabs/cem_acpt
170
+ licenses:
171
+ - MIT
172
+ metadata:
173
+ homepage_uri: https://github.com/puppetlabs/cem_acpt
174
+ source_code_uri: https://github.com/puppetlabs/cem_acpt
175
+ changelog_uri: https://github.com/puppetlabs/cem_acpt
176
+ post_install_message:
177
+ rdoc_options: []
178
+ require_paths:
179
+ - lib
180
+ required_ruby_version: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - ">="
183
+ - !ruby/object:Gem::Version
184
+ version: 2.5.0
185
+ required_rubygems_version: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ requirements: []
191
+ rubygems_version: 3.1.4
192
+ signing_key:
193
+ specification_version: 4
194
+ summary: CEM Acceptance Tests
195
+ test_files: []