cem_acpt 0.7.3 → 0.8.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.
@@ -6,6 +6,7 @@ module CemAcpt
6
6
  module Provision
7
7
  # Class provides methods for gathering provision data for Windows nodes
8
8
  class Windows < OsData
9
+ # A name that will match with how image names are on GCP
9
10
  def self.valid_names
10
11
  %w[windows]
11
12
  end
@@ -17,6 +18,14 @@ module CemAcpt
17
18
  def puppet_bin_path
18
19
  'C:/Program Files/Puppet Labs/Puppet/bin/puppet.bat'
19
20
  end
21
+
22
+ def destination_provision_directory
23
+ 'C:/cem_acpt'
24
+ end
25
+
26
+ def provision_commands
27
+ ['placeholder']
28
+ end
20
29
  end
21
30
  end
22
31
  end
@@ -2,10 +2,10 @@
2
2
 
3
3
  require 'fileutils'
4
4
  require 'json'
5
- require 'ruby-terraform'
6
5
  require_relative '../logging'
7
6
  require_relative 'terraform/linux'
8
7
  require_relative 'terraform/windows'
8
+ require_relative 'terraform/terraform_cmd'
9
9
 
10
10
  module CemAcpt
11
11
  module Provision
@@ -27,22 +27,27 @@ module CemAcpt
27
27
  @public_key = nil
28
28
  end
29
29
 
30
+ # @return [Hash] A hash of instance names and IPs
30
31
  def provision(reuse_working_dir: false)
31
- logger.info('Terraform') { 'Provisioning nodes...' }
32
+ logger.info('CemAcpt::Provision::Terraform') { 'Provisioning nodes...' }
32
33
  @working_dir = new_working_dir unless reuse_working_dir
33
34
  validate_working_dir!
34
35
  save_vars_to_file!(formatted_vars) # Easier to reuse nodes this way
35
-
36
- terraform_configure_logging
37
36
  terraform_init
38
37
  terraform_plan(formatted_vars, DEFAULT_PLAN_NAME)
39
38
  terraform_apply(DEFAULT_PLAN_NAME)
40
- JSON.parse(terraform_output('instance_name_ip', json: true))
39
+ begin
40
+ output = terraform_output('instance_name_ip', json: true)
41
+ JSON.parse(output)
42
+ rescue JSON::ParserError => e
43
+ logger.error('CemAcpt::Provision::Terraform') { "Error parsing Terraform output: #{output}" }
44
+ raise e
45
+ end
41
46
  end
42
47
 
43
48
  def destroy
44
49
  terraform_destroy(formatted_vars)
45
- logger.verbose('Terraform') { "Deleting old working directory #{working_dir}" }
50
+ logger.verbose('CemAcpt::Provision::Terraform') { "Deleting old working directory #{working_dir}" }
46
51
  FileUtils.rm_rf(working_dir)
47
52
  @working_dir = nil
48
53
  @module_package_path = nil
@@ -57,25 +62,17 @@ module CemAcpt
57
62
  private
58
63
 
59
64
  def terraform
60
- @terraform ||= RubyTerraform
61
- end
62
-
63
- def terraform_configure_logging
64
- terraform.configure do |c|
65
- c.logger = logger
66
- c.stdout = c.logger
67
- c.stderr = c.logger
68
- end
65
+ @terraform ||= CemAcpt::Provision::TerraformCmd.new(working_dir, environment)
69
66
  end
70
67
 
71
68
  def terraform_init
72
- logger.debug('Terraform') { 'Initializing Terraform' }
69
+ logger.info('CemAcpt::Provision::Terraform') { 'Initializing Terraform' }
73
70
  terraform.init({ chdir: working_dir, input: false, no_color: true }, { environment: environment })
74
71
  end
75
72
 
76
73
  def terraform_plan(vars, plan_name = DEFAULT_PLAN_NAME)
77
- logger.debug('Terraform') { "Creating Terraform plan '#{plan_name}'" }
78
- logger.verbose('Terraform') { "Using vars:\n#{JSON.pretty_generate(vars)}" }
74
+ logger.info('CemAcpt::Provision::Terraform') { "Creating Terraform plan '#{plan_name}'" }
75
+ logger.verbose('CemAcpt::Provision::Terraform') { "Using vars:\n#{JSON.pretty_generate(vars)}" }
79
76
  terraform.plan(
80
77
  {
81
78
  chdir: working_dir,
@@ -91,23 +88,24 @@ module CemAcpt
91
88
  end
92
89
 
93
90
  def terraform_apply(plan_name = DEFAULT_PLAN_NAME)
94
- logger.debug('Terraform') { "Running Terraform apply with the plan #{plan_name}" }
91
+ logger.info('CemAcpt::Provision::Terraform') { "Running Terraform apply with the plan #{plan_name}" }
95
92
  terraform.apply({ chdir: working_dir, input: false, no_color: true, plan: plan_name }, { environment: environment })
96
93
  end
97
94
 
98
95
  def terraform_output(name, json: true)
99
- logger.debug('Terraform') { "Getting Terraform output #{name}" }
96
+ logger.info('CemAcpt::Provision::Terraform') { "Getting Terraform output #{name}" }
100
97
  terraform.output({ chdir: working_dir, no_color: true, json: json, name: name }, { environment: environment })
101
98
  end
102
99
 
103
100
  def terraform_destroy(vars)
104
- logger.debug('Terraform') { 'Destroying Terraform resources' }
101
+ logger.info('CemAcpt::Provision::Terraform') { 'Destroying Terraform resources...' }
102
+ logger.verbose('CemAcpt::Provision::Terraform') { "Using vars: #{vars}" }
105
103
  terraform.destroy(
106
104
  {
107
105
  chdir: working_dir,
108
106
  auto_approve: true,
109
107
  input: false,
110
- no_color: true,
108
+ no_color: nil,
111
109
  vars: vars,
112
110
  },
113
111
  {
@@ -117,18 +115,18 @@ module CemAcpt
117
115
  end
118
116
 
119
117
  def terraform_show
120
- logger.debug('Terraform') { 'Showing Terraform state' }
118
+ logger.info('CemAcpt::Provision::Terraform') { 'Showing Terraform state' }
121
119
  terraform.show({ chdir: working_dir, no_color: true }, { environment: environment })
122
120
  end
123
121
 
124
122
  def new_backend(test_name)
125
123
  if CemAcpt::Provision::Linux.use_for?(test_name)
126
- logger.info('Terraform') { 'Using Linux backend' }
127
- logger.verbose('Terraform') { "Creating backend with provision_data:\n#{JSON.pretty_generate(@provision_data)}" }
124
+ logger.info('CemAcpt::Provision::Terraform') { 'Using Linux backend' }
125
+ logger.verbose('CemAcpt::Provision::Terraform') { "Creating backend with provision_data:\n#{JSON.pretty_generate(@provision_data)}" }
128
126
  CemAcpt::Provision::Linux.new(@config, @provision_data)
129
127
  elsif CemAcpt::Provision::Windows.use_for?(test_name)
130
- logger.info('Terraform') { 'Using Windows backend' }
131
- logger.verbose('Terraform') { "Creating backend with provision_data:\n#{JSON.pretty_generate(@provision_data)}" }
128
+ logger.info('CemAcpt::Provision::Terraform') { 'Using Windows backend' }
129
+ logger.verbose('CemAcpt::Provision::Terraform') { "Creating backend with provision_data:\n#{JSON.pretty_generate(@provision_data)}" }
132
130
  CemAcpt::Provision::Windows.new(@config, @provision_data)
133
131
  else
134
132
  err_msg = [
@@ -136,9 +134,8 @@ module CemAcpt
136
134
  "Known OSes are: #{CemAcpt::Provision::Linux.valid_names.join(', ')}",
137
135
  "and #{CemAcpt::Provision::Windows.valid_names.join(', ')}.",
138
136
  "Known versions are: #{CemAcpt::Provision::Linux.valid_versions.join(', ')}",
139
- ", and #{CemAcpt::Provision::Windows.valid_versions.join(', ')}."
137
+ ", and #{CemAcpt::Provision::Windows.valid_versions.join(', ')}.",
140
138
  ].join(' ')
141
- logger.error('Terraform') { err_msg }
142
139
  raise ArgumentError, err_msg
143
140
  end
144
141
  end
@@ -146,57 +143,55 @@ module CemAcpt
146
143
  def new_environment(config)
147
144
  env = (config.get('terraform.environment') || {})
148
145
  env['CLOUDSDK_PYTHON_SITEPACKAGES'] = '1' # This is needed for gcloud to use numpy
149
- logger.verbose('Terraform') { "Using environment:\n#{JSON.pretty_generate(env)}" }
146
+ logger.verbose('CemAcpt::Provision::Terraform') { "Using environment:\n#{JSON.pretty_generate(env)}" }
150
147
  env
151
148
  end
152
149
 
153
150
  def new_working_dir
154
- logger.debug('Terraform') { 'Creating new working directory' }
151
+ logger.debug('CemAcpt::Provision::Terraform') { 'Creating new working directory' }
155
152
  base_dir = File.join(@config.get('terraform.dir'), @config.get('platform.name'))
156
- logger.verbose('Terraform') { "Base directory defined as #{base_dir}" }
153
+ logger.verbose('CemAcpt::Provision::Terraform') { "Base directory defined as #{base_dir}" }
157
154
  @backend.base_provision_directory = base_dir
158
- logger.verbose('Terraform') { 'Base directory set in backend' }
155
+ logger.verbose('CemAcpt::Provision::Terraform') { 'Base directory set in backend' }
159
156
  work_dir = File.join(@config.get('terraform.dir'), "test_#{Time.now.to_i}")
160
- logger.verbose('Terraform') { "Working directory defined as #{work_dir}" }
161
- logger.verbose('Terraform') { "Copying backend provision directory #{@backend.provision_directory} to working directory" }
157
+ logger.verbose('CemAcpt::Provision::Terraform') { "Working directory defined as #{work_dir}" }
158
+ logger.verbose('CemAcpt::Provision::Terraform') { "Copying backend provision directory #{@backend.provision_directory} to working directory" }
162
159
  FileUtils.cp_r(@backend.provision_directory, work_dir)
163
- logger.verbose('Terraform') { "Copied provision directory #{@backend.provision_directory} to #{work_dir}" }
160
+ logger.verbose('CemAcpt::Provision::Terraform') { "Copied provision directory #{@backend.provision_directory} to #{work_dir}" }
164
161
  FileUtils.cp(@provision_data[:module_package_path], work_dir)
165
162
  @module_package_path = File.join(work_dir, File.basename(@provision_data[:module_package_path]))
166
- logger.verbose('Terraform') { "Copied module package #{@provision_data[:module_package_path]} to #{work_dir}" }
163
+ logger.verbose('CemAcpt::Provision::Terraform') { "Copied module package #{@provision_data[:module_package_path]} to #{work_dir}" }
167
164
  if File.exist?(@provision_data[:private_key])
168
165
  FileUtils.cp(@provision_data[:private_key], work_dir)
169
166
  @private_key = File.join(work_dir, File.basename(@provision_data[:private_key]))
170
- logger.verbose('Terraform') { "Copied private key #{@provision_data[:private_key]} to #{work_dir}" }
167
+ logger.verbose('CemAcpt::Provision::Terraform') { "Copied private key #{@provision_data[:private_key]} to #{work_dir}" }
171
168
  end
172
169
  if File.exist?(@provision_data[:public_key])
173
170
  FileUtils.cp(@provision_data[:public_key], work_dir)
174
171
  @public_key = File.join(work_dir, File.basename(@provision_data[:public_key]))
175
- logger.verbose('Terraform') { "Copied public key #{@provision_data[:public_key]} to #{work_dir}" }
172
+ logger.verbose('CemAcpt::Provision::Terraform') { "Copied public key #{@provision_data[:public_key]} to #{work_dir}" }
176
173
  end
177
174
  work_dir
178
175
  rescue StandardError => e
179
- logger.error('Terraform') { 'Error creating working directory' }
176
+ logger.error('CemAcpt::Provision::Terraform') { 'Error creating working directory' }
180
177
  raise e
181
178
  end
182
179
 
183
180
  def validate_working_dir!
184
- logger.debug('Terraform') { "Validating working directory #{working_dir}" }
185
- logger.verbose('Terraform') { "Content of #{working_dir}:\n#{Dir.glob(File.join(working_dir, '*')).join("\n")}" }
181
+ logger.debug('CemAcpt::Provision::Terraform') { "Validating working directory #{working_dir}" }
182
+ logger.verbose('CemAcpt::Provision::Terraform') { "Content of #{working_dir}:\n#{Dir.glob(File.join(working_dir, '*')).join("\n")}" }
186
183
  raise "Terraform working directory #{working_dir} does not exist" unless File.directory?(working_dir)
187
184
  raise "Terraform working directory #{working_dir} does not contain a Terraform file" unless Dir.glob(File.join(working_dir, '*.tf')).any?
188
185
 
189
- logger.info('Terraform') { "Using working directory: #{working_dir}" }
186
+ logger.info('CemAcpt::Provision::Terraform') { "Using working directory: #{working_dir}" }
190
187
  rescue StandardError => e
191
- logger.error('Terraform') { 'Error validating working directory' }
192
188
  raise e
193
189
  end
194
190
 
195
191
  def save_vars_to_file!(vars)
196
- logger.debug('Terraform') { "Saving vars to file #{File.join(working_dir, DEFAULT_VARS_FILE)}" }
192
+ logger.debug('CemAcpt::Provision::Terraform') { "Saving vars to file #{File.join(working_dir, DEFAULT_VARS_FILE)}" }
197
193
  File.write(File.join(working_dir, DEFAULT_VARS_FILE), vars.to_json)
198
194
  rescue StandardError => e
199
- logger.error('Terraform') { 'Error saving vars to file' }
200
195
  raise e
201
196
  end
202
197
 
@@ -214,7 +209,6 @@ module CemAcpt
214
209
  end
215
210
  node_data.to_json
216
211
  rescue StandardError => e
217
- logger.error('Terraform') { 'Error creating node data' }
218
212
  raise e
219
213
  end
220
214
 
@@ -228,7 +222,6 @@ module CemAcpt
228
222
  }
229
223
  )
230
224
  rescue StandardError => e
231
- logger.error('Terraform') { 'Error creating formatted vars' }
232
225
  raise e
233
226
  end
234
227
  end
@@ -10,7 +10,7 @@ module CemAcpt
10
10
  def self.new_provisioner(config, provision_data)
11
11
  case config.get('provisioner')
12
12
  when 'terraform'
13
- logger.debug('Provision') { 'Using Terraform provisioner' }
13
+ logger.debug('CemAcpt::Provision') { 'Using Terraform provisioner' }
14
14
  CemAcpt::Provision::Terraform.new(config, provision_data)
15
15
  else
16
16
  raise ArgumentError, "Unknown provisioner #{config.get('provisioner')}"
@@ -30,7 +30,7 @@ module CemAcpt
30
30
  _metadata = builder.metadata
31
31
 
32
32
  # Builds the module package
33
- logger.info("Building module package for #{builder.release_name}")
33
+ logger.info('CemAcpt::PuppetHelpers') { "Building module package for #{builder.release_name}" }
34
34
  builder.build
35
35
  end
36
36
  end
@@ -33,7 +33,7 @@ module CemAcpt
33
33
  # Extracts, formats, and returns a test data hash.
34
34
  # @return [Array<Hash>] an array of test data hashes
35
35
  def acceptance_test_data
36
- logger.info 'Gathering acceptance test data...'
36
+ logger.info('CemAcpt::TestData') { 'Gathering acceptance test data...' }
37
37
  raise "No 'tests' entry found in config" unless @config.has?('tests')
38
38
 
39
39
  @config.get('tests').each_with_object([]) do |test_name, a|
@@ -45,7 +45,7 @@ module CemAcpt
45
45
  raise "Goss file not found for test #{test_name}" unless File.exist?(goss_file)
46
46
  raise "Puppet manifest not found for test #{test_name}" unless File.exist?(puppet_manifest)
47
47
 
48
- logger.debug("Complete test directory found for test #{test_name}: #{test_dir}")
48
+ logger.debug('CemAcpt::TestData') { "Complete test directory found for test #{test_name}: #{test_dir}" }
49
49
  test_data = {
50
50
  test_name: test_name,
51
51
  test_dir: File.expand_path(test_dir),
@@ -71,7 +71,7 @@ module CemAcpt
71
71
  @acceptance_tests = acpt_test_dir.children.select { |f| f.directory? && File.exist?(File.join(f, 'goss.yaml')) }.map(&:to_s)
72
72
  raise 'No acceptance tests found' if @acceptance_tests.empty?
73
73
 
74
- logger.info "Found #{@acceptance_tests.size} acceptance tests"
74
+ logger.info('CemAcpt') { "Found #{@acceptance_tests.size} acceptance tests" }
75
75
  end
76
76
 
77
77
  # Processes a for_each statement in the test data config.
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CemAcpt
4
+ module TestRunner
5
+ module LogFormatter
6
+ class ErrorFormatter
7
+ def inspect
8
+ to_s
9
+ end
10
+
11
+ def to_s
12
+ "#<#{self.class.name}:0x#{object_id.to_s(16)}>"
13
+ end
14
+
15
+ def summary(response)
16
+ "Error: #{response.summary}"
17
+ end
18
+
19
+ def results(response)
20
+ [response.summary, response.results.join("\n")]
21
+ end
22
+
23
+ def host_name(response)
24
+ "Error: #{response.error.class.name}"
25
+ end
26
+
27
+ def test_name(response)
28
+ "Error: #{response.error.class.name}"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,10 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'log_formatter/goss_action_response'
4
+ require_relative 'log_formatter/error_formatter'
4
5
 
5
6
  module CemAcpt
6
7
  module TestRunner
7
8
  # Holds classes for formatting test runner results
8
- module LogFormatter; end
9
+ module LogFormatter
10
+ def self.new_formatter(result, *args, **kwargs)
11
+ if result.error?
12
+ ErrorFormatter.new
13
+ else
14
+ GossActionResponse.new(*args)
15
+ end
16
+ end
17
+ end
9
18
  end
10
19
  end