cem_acpt 0.7.3 → 0.8.1

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