cem_acpt 0.1.0

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