puppet_litmus 0.19.0 → 0.23.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ffba803cc6b53c4bdb7084bf11be4fac807f3b3cd237c5ddb297dcf6ca3a403d
4
- data.tar.gz: 5cdb613f2be957e20a63fc1250b10113e468e162817657f0f987675f2ba7c055
3
+ metadata.gz: 11d83a8c9bc28bddfab1549c27e41514cbab10e4e942b1bceadf474bb7ab2866
4
+ data.tar.gz: e699d388947cb532e2f52a64f3f393da291140cc68efe0f8149b5dea3d8df29b
5
5
  SHA512:
6
- metadata.gz: ea1fd95a69bfac81b56f94f3ee28b91be2587c501bf24729931c128315e41d5eaf937dce013ee0d63ec7a350469ac32ab0d7711e50411a0e5fe00340dabdd803
7
- data.tar.gz: 899ac2b0edb422ccf35c9cdb5f46bec6b2a6a97557dca12ffc994e046ed8cf1e6eec2553865a3c4439b272d1dd29b0111fc24b2e1bb93575cb77fc0bfd7606b1
6
+ metadata.gz: a33d9d98831af21eeadc21971fc48c1d27f50270e20c5daf267340b1d811bfcb1f3860e8fcd5181a52e58f3f28d068bb100ae18f425a60be98f54ac61b4fb063
7
+ data.tar.gz: ac37e3be71bf832cc286cad40299eab785c6c52ceb14a3a1bdb7952b51f775bf78b7c7b66298320e3bac081835f3e51083230c7d1cc773e3ee2ede5c7f8ceb96
@@ -1,12 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- # this script creates a build matrix for github actions from the claimed supported platforms in metadata.json
4
+ # this script creates a build matrix for github actions from the claimed supported platforms and puppet versions in metadata.json
5
5
 
6
6
  require 'json'
7
7
 
8
8
  IMAGE_TABLE = {
9
- 'RedHat-6' => 'rhel-6',
10
9
  'RedHat-7' => 'rhel-7',
11
10
  'RedHat-8' => 'rhel-8',
12
11
  'SLES-12' => 'sles-12',
@@ -33,16 +32,21 @@ DOCKER_PLATFORMS = [
33
32
  'Ubuntu-20.04',
34
33
  ].freeze
35
34
 
35
+ # This table uses the latest version in each collection for accurate
36
+ # comparison when evaluating puppet requirements from the metadata
37
+ COLLECTION_TABLE = {
38
+ '5.5.22' => 'puppet5',
39
+ '6.19.1' => 'puppet6-nightly',
40
+ '7.0.0' => 'puppet7-nightly',
41
+ }.freeze
42
+
36
43
  matrix = {
37
44
  platform: [],
38
- collection: %w[
39
- puppet5
40
- puppet6
41
- puppet7-nightly
42
- ],
45
+ collection: [],
43
46
  }
44
47
 
45
48
  metadata = JSON.parse(File.read('metadata.json'))
49
+ # Set platforms based on declared operating system support
46
50
  metadata['operatingsystem_support'].sort_by { |a| a['operatingsystem'] }.each do |sup|
47
51
  os = sup['operatingsystem']
48
52
  sup['operatingsystemrelease'].sort_by { |a| a.to_i }.each do |ver|
@@ -57,6 +61,38 @@ metadata['operatingsystem_support'].sort_by { |a| a['operatingsystem'] }.each do
57
61
  end
58
62
  end
59
63
 
64
+ # Set collections based on puppet version requirements
65
+ if metadata.key?('requirements') && metadata['requirements'].length.positive?
66
+ metadata['requirements'].each do |req|
67
+ next unless req.key?('name') && req.key?('version_requirement') && req['name'] == 'puppet'
68
+
69
+ ver_regexp = %r{^([>=<]{1,2})\s*([\d.]+)\s+([>=<]{1,2})\s*([\d.]+)$}
70
+ match = ver_regexp.match(req['version_requirement'])
71
+ if match.nil?
72
+ puts "::warning::Didn't recognize version_requirement '#{req['version_requirement']}'"
73
+ break
74
+ end
75
+
76
+ cmp_one, ver_one, cmp_two, ver_two = match.captures
77
+ reqs = ["#{cmp_one} #{ver_one}", "#{cmp_two} #{ver_two}"]
78
+
79
+ COLLECTION_TABLE.each do |key, val|
80
+ if Gem::Requirement.create(reqs).satisfied_by?(Gem::Version.new(key))
81
+ matrix[:collection] << val
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ # Set to defaults (all collections) if no matches are found
88
+ if matrix[:collection].empty?
89
+ matrix[:collection] = COLLECTION_TABLE.values
90
+ end
91
+
92
+ # Just to make sure there aren't any duplicates
93
+ matrix[:platform] = matrix[:platform].uniq.sort
94
+ matrix[:collection] = matrix[:collection].uniq.sort
95
+
60
96
  puts "::set-output name=matrix::#{JSON.generate(matrix)}"
61
97
 
62
98
  puts "Created matrix with #{matrix[:platform].length * matrix[:collection].length} cells."
@@ -17,10 +17,7 @@ module PuppetLitmus::InventoryManipulation
17
17
  end
18
18
  raise "There is no inventory file at '#{inventory_full_path}'." unless File.exist?(inventory_full_path)
19
19
 
20
- inventory_hash = YAML.load_file(inventory_full_path)
21
- raise "Inventory file is incompatible (version 2 and up). Try the 'bolt project migrate' command." if inventory_hash['version'].nil? || (inventory_hash['version'] < 2)
22
-
23
- inventory_hash
20
+ YAML.load_file(inventory_full_path)
24
21
  end
25
22
 
26
23
  # Provide a default hash for executing against localhost
@@ -28,7 +25,6 @@ module PuppetLitmus::InventoryManipulation
28
25
  # @return [Hash] inventory.yaml hash containing only an entry for localhost
29
26
  def localhost_inventory_hash
30
27
  {
31
- 'version' => 2,
32
28
  'groups' => [
33
29
  {
34
30
  'name' => 'local',
@@ -262,7 +258,12 @@ module PuppetLitmus::InventoryManipulation
262
258
  # @param inventory_hash [Hash] hash of the inventory.yaml file
263
259
  # @param node_name [String] node of nodes to limit the search for the node_name in
264
260
  def add_platform_field(inventory_hash, node_name)
265
- facts = facts_from_node(inventory_hash, node_name)
261
+ facts = begin
262
+ facts_from_node(inventory_hash, node_name)
263
+ rescue StandardError => e
264
+ warn e
265
+ {}
266
+ end
266
267
  Honeycomb.current_span.add_field('litmus.platform', facts&.dig('platform'))
267
268
  end
268
269
  end
@@ -9,9 +9,9 @@ module PuppetLitmus::PuppetHelpers
9
9
  # @return [Boolean] The result of the 2 apply manifests.
10
10
  def idempotent_apply(manifest)
11
11
  Honeycomb.start_span(name: 'litmus.idempotent_apply') do |span|
12
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
12
+ ENV['HONEYCOMB_TRACE'] = span.to_trace_header
13
13
  manifest_file_location = create_manifest_file(manifest)
14
- apply_manifest(nil, expect_failures: false, manifest_file_location: manifest_file_location)
14
+ apply_manifest(nil, catch_failures: true, manifest_file_location: manifest_file_location)
15
15
  apply_manifest(nil, catch_changes: true, manifest_file_location: manifest_file_location)
16
16
  end
17
17
  end
@@ -30,7 +30,7 @@ module PuppetLitmus::PuppetHelpers
30
30
  # :catch_failures [Boolean] (false) We're after only complete success so allow exit codes 0 and 2 only.
31
31
  # :expect_failures [Boolean] (false) We're after failures specifically so allow exit codes 1, 4, and 6 only.
32
32
  # :manifest_file_location [Path] The place on the target system.
33
- # :hiera_config [Path] The path to the hiera.yaml configuration on the runner.
33
+ # :hiera_config [Path] The path to the hiera.yaml configuration on the target.
34
34
  # :prefix_command [String] prefixes the puppet apply command; eg "export LANGUAGE='ja'".
35
35
  # :trace [Boolean] run puppet apply with the trace flag (defaults to `true`).
36
36
  # :debug [Boolean] run puppet apply with the debug flag.
@@ -39,7 +39,7 @@ module PuppetLitmus::PuppetHelpers
39
39
  # @return [Object] A result object from the apply.
40
40
  def apply_manifest(manifest, opts = {})
41
41
  Honeycomb.start_span(name: 'litmus.apply_manifest') do |span|
42
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
42
+ ENV['HONEYCOMB_TRACE'] = span.to_trace_header
43
43
  span.add_field('litmus.manifest', manifest)
44
44
  span.add_field('litmus.opts', opts)
45
45
 
@@ -83,11 +83,22 @@ module PuppetLitmus::PuppetHelpers
83
83
  command_to_run += ' --noop' if !opts[:noop].nil? && (opts[:noop] == true)
84
84
  command_to_run += ' --detailed-exitcodes' if use_detailed_exit_codes == true
85
85
 
86
- span.add_field('litmus.command_to_run', command_to_run)
87
86
  span.add_field('litmus.target_node_name', target_node_name)
88
- bolt_result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
89
- span.add_field('litmus.bolt_result', bolt_result)
90
87
 
88
+ if os[:family] == 'windows'
89
+ # IAC-1365 - Workaround for BOLT-1535 and bolt issue #1650
90
+ command_to_run = "try { #{command_to_run}; exit $LASTEXITCODE } catch { write-error $_ ; exit 1 }"
91
+ span.add_field('litmus.command_to_run', command_to_run)
92
+ bolt_result = Tempfile.open(['temp', '.ps1']) do |script|
93
+ script.write(command_to_run)
94
+ script.close
95
+ run_script(script.path, target_node_name, [], options: {}, config: nil, inventory: inventory_hash)
96
+ end
97
+ else
98
+ span.add_field('litmus.command_to_run', command_to_run)
99
+ bolt_result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
100
+ end
101
+ span.add_field('litmus.bolt_result', bolt_result)
91
102
  result = OpenStruct.new(exit_code: bolt_result.first['value']['exit_code'],
92
103
  stdout: bolt_result.first['value']['stdout'],
93
104
  stderr: bolt_result.first['value']['stderr'])
@@ -117,7 +128,7 @@ module PuppetLitmus::PuppetHelpers
117
128
  # @return [String] The path to the location of the manifest.
118
129
  def create_manifest_file(manifest)
119
130
  Honeycomb.start_span(name: 'litmus.create_manifest_file') do |span|
120
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
131
+ ENV['HONEYCOMB_TRACE'] = span.to_trace_header
121
132
  span.add_field('litmus.manifest', manifest)
122
133
 
123
134
  require 'tmpdir'
@@ -154,7 +165,7 @@ module PuppetLitmus::PuppetHelpers
154
165
  # @return [Bool] Success. The file was succesfully writtne on the target.
155
166
  def write_file(content, destination)
156
167
  Honeycomb.start_span(name: 'litmus.write_file') do |span|
157
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
168
+ ENV['HONEYCOMB_TRACE'] = span.to_trace_header
158
169
  span.add_field('litmus.destination', destination)
159
170
 
160
171
  require 'tmpdir'
@@ -191,7 +202,7 @@ module PuppetLitmus::PuppetHelpers
191
202
  # @return [Object] A result object from the command.
192
203
  def run_shell(command_to_run, opts = {})
193
204
  Honeycomb.start_span(name: 'litmus.run_shell') do |span|
194
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
205
+ ENV['HONEYCOMB_TRACE'] = span.to_trace_header
195
206
  span.add_field('litmus.command_to_run', command_to_run)
196
207
  span.add_field('litmus.opts', opts)
197
208
 
@@ -228,7 +239,7 @@ module PuppetLitmus::PuppetHelpers
228
239
  # @return [Object] A result object from the command.
229
240
  def bolt_upload_file(source, destination, opts = {}, options = {})
230
241
  Honeycomb.start_span(name: 'litmus.bolt_upload_file') do |span|
231
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
242
+ ENV['HONEYCOMB_TRACE'] = span.to_trace_header
232
243
  span.add_field('litmus.source', source)
233
244
  span.add_field('litmus.destination', destination)
234
245
  span.add_field('litmus.opts', opts)
@@ -280,7 +291,7 @@ module PuppetLitmus::PuppetHelpers
280
291
  # @return [Object] A result object from the task.The values available are stdout, stderr and result.
281
292
  def run_bolt_task(task_name, params = {}, opts = {})
282
293
  Honeycomb.start_span(name: 'litmus.run_task') do |span|
283
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
294
+ ENV['HONEYCOMB_TRACE'] = span.to_trace_header
284
295
  span.add_field('litmus.task_name', task_name)
285
296
  span.add_field('litmus.params', params)
286
297
  span.add_field('litmus.opts', opts)
@@ -348,7 +359,7 @@ module PuppetLitmus::PuppetHelpers
348
359
  # @return [Object] A result object from the script run.
349
360
  def bolt_run_script(script, opts = {}, arguments: [])
350
361
  Honeycomb.start_span(name: 'litmus.bolt_run_script') do |span|
351
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
362
+ ENV['HONEYCOMB_TRACE'] = span.to_trace_header
352
363
  span.add_field('litmus.script', script)
353
364
  span.add_field('litmus.opts', opts)
354
365
  span.add_field('litmus.arguments', arguments)
@@ -9,8 +9,8 @@ Honeycomb.configure do |config|
9
9
  config.client = Libhoney::NullClient.new
10
10
  end
11
11
  end
12
- process_span = Honeycomb.start_span(name: "litmus: #{([$PROGRAM_NAME] + ($ARGV || [])).join(' ')}", serialized_trace: ENV['HTTP_X_HONEYCOMB_TRACE'])
13
- ENV['HTTP_X_HONEYCOMB_TRACE'] = process_span.to_trace_header
12
+ process_span = Honeycomb.start_span(name: "litmus: #{([$PROGRAM_NAME] + ($ARGV || [])).join(' ')}", serialized_trace: ENV['HONEYCOMB_TRACE'])
13
+ ENV['HONEYCOMB_TRACE'] = process_span.to_trace_header
14
14
  Honeycomb.add_field_to_trace('litmus.pid', Process.pid)
15
15
  if defined? PuppetLitmus::VERSION
16
16
  Honeycomb.add_field_to_trace('litmus.version', PuppetLitmus::VERSION)
@@ -41,6 +41,13 @@ elsif ENV['GITHUB_ACTIONS'] == 'true'
41
41
  Honeycomb.add_field_to_trace('ci.sha', ENV['GITHUB_SHA'])
42
42
  end
43
43
  at_exit do
44
+ if $ERROR_INFO.is_a?(SystemExit)
45
+ process_span.add_field('process.exit_code', $ERROR_INFO.status)
46
+ elsif $ERROR_INFO
47
+ process_span.add_field('process.exit_code', $ERROR_INFO.class.name)
48
+ else
49
+ process_span.add_field('process.exit_code', 'unknown')
50
+ end
44
51
  process_span.send
45
52
  end
46
53
 
@@ -96,7 +103,7 @@ module PuppetLitmus::RakeHelper
96
103
  # @return [Object] the standard out stream.
97
104
  def run_local_command(command)
98
105
  Honeycomb.start_span(name: 'litmus.run_local_command') do |span|
99
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
106
+ ENV['HONEYCOMB_TRACE'] = span.to_trace_header
100
107
  span.add_field('litmus.command', command)
101
108
 
102
109
  require 'open3'
@@ -119,13 +126,18 @@ module PuppetLitmus::RakeHelper
119
126
 
120
127
  Honeycomb.add_field_to_trace('litmus.provisioner', provisioner)
121
128
  Honeycomb.start_span(name: 'litmus.provision') do |span|
122
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
129
+ ENV['HONEYCOMB_TRACE'] = span.to_trace_header
123
130
  span.add_field('litmus.platform', platform)
124
- span.add_field('litmus.inventory', params['inventory'])
131
+
132
+ task_name = provisioner_task(provisioner)
133
+ span.add_field('litmus.task_name', task_name)
134
+ span.add_field('litmus.params', params)
125
135
  span.add_field('litmus.config', DEFAULT_CONFIG_DATA)
126
136
 
127
- bolt_result = run_task(provisioner_task(provisioner), 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil)
137
+ bolt_result = run_task(task_name, 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil)
138
+ span.add_field('litmus.result', bolt_result)
128
139
  span.add_field('litmus.node_name', bolt_result&.first&.dig('value', 'node_name'))
140
+
129
141
  raise_bolt_errors(bolt_result, "provisioning of #{platform} failed.")
130
142
 
131
143
  bolt_result
@@ -148,7 +160,7 @@ module PuppetLitmus::RakeHelper
148
160
 
149
161
  def tear_down_nodes(targets, inventory_hash)
150
162
  Honeycomb.start_span(name: 'litmus.tear_down_nodes') do |span|
151
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
163
+ ENV['HONEYCOMB_TRACE'] = span.to_trace_header
152
164
  span.add_field('litmus.targets', targets)
153
165
 
154
166
  include ::BoltSpec::Run
@@ -160,6 +172,18 @@ module PuppetLitmus::RakeHelper
160
172
  next if node_name == 'litmus_localhost'
161
173
 
162
174
  result = tear_down(node_name, inventory_hash)
175
+ # Some provisioners tear_down targets that were created as a batch job.
176
+ # These provisioners should return the list of additional targets
177
+ # removed so that we do not attempt to process them.
178
+ if result != [] && result[0]['value'].key?('removed')
179
+ removed_targets = result[0]['value']['removed']
180
+ result[0]['value'].delete('removed')
181
+ removed_targets.each do |removed_target|
182
+ targets.delete(removed_target)
183
+ results[removed_target] = result
184
+ end
185
+ end
186
+
163
187
  results[node_name] = result unless result == []
164
188
  end
165
189
  results
@@ -168,7 +192,7 @@ module PuppetLitmus::RakeHelper
168
192
 
169
193
  def tear_down(node_name, inventory_hash)
170
194
  Honeycomb.start_span(name: 'litmus.tear_down') do |span|
171
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
195
+ ENV['HONEYCOMB_TRACE'] = span.to_trace_header
172
196
  # how do we know what provisioner to use
173
197
 
174
198
  span.add_field('litmus.node_name', node_name)
@@ -184,7 +208,7 @@ module PuppetLitmus::RakeHelper
184
208
 
185
209
  def install_agent(collection, targets, inventory_hash)
186
210
  Honeycomb.start_span(name: 'litmus.install_agent') do |span|
187
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
211
+ ENV['HONEYCOMB_TRACE'] = span.to_trace_header
188
212
  span.add_field('litmus.collection', collection)
189
213
  span.add_field('litmus.targets', targets)
190
214
 
@@ -192,7 +216,6 @@ module PuppetLitmus::RakeHelper
192
216
  params = if collection.nil?
193
217
  {}
194
218
  else
195
- Honeycomb.current_span.add_field('litmus.collection', collection)
196
219
  { 'collection' => collection }
197
220
  end
198
221
  raise "puppet_agent was not found in #{DEFAULT_CONFIG_DATA['modulepath']}, please amend the .fixtures.yml file" \
@@ -277,7 +300,7 @@ module PuppetLitmus::RakeHelper
277
300
  # @return a bolt result
278
301
  def install_module(inventory_hash, target_node_name, module_tar, module_repository = nil, ignore_dependencies = false) # rubocop:disable Style/OptionalBooleanParameter
279
302
  Honeycomb.start_span(name: 'install_module') do |span|
280
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
303
+ ENV['HONEYCOMB_TRACE'] = span.to_trace_header
281
304
  span.add_field('litmus.target_node_name', target_node_name)
282
305
  span.add_field('litmus.module_tar', module_tar)
283
306
 
@@ -333,24 +356,30 @@ module PuppetLitmus::RakeHelper
333
356
 
334
357
  def check_connectivity?(inventory_hash, target_node_name)
335
358
  Honeycomb.start_span(name: 'litmus.check_connectivity') do |span|
336
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
359
+ ENV['HONEYCOMB_TRACE'] = span.to_trace_header
337
360
  # if we're only checking connectivity for a single node
338
361
  if target_node_name
339
- span.add_field('litmus.node_name', target_node_name)
362
+ span.add_field('litmus.target_node_name', target_node_name)
340
363
  add_platform_field(inventory_hash, target_node_name)
341
364
  end
342
365
 
343
366
  include ::BoltSpec::Run
344
367
  target_nodes = find_targets(inventory_hash, target_node_name)
368
+ puts "Checking connectivity for #{target_nodes.inspect}"
369
+ span.add_field('litmus.target_nodes', target_nodes)
370
+
345
371
  results = run_command('cd .', target_nodes, config: nil, inventory: inventory_hash)
346
372
  span.add_field('litmus.bolt_result', results)
347
373
  failed = []
348
- results.each do |result|
349
- failed.push(result['target']) if result['status'] == 'failure'
374
+ results.reject { |r| r['status'] == 'success' }.each do |result|
375
+ puts "Failure connecting to #{result['target']}:\n#{result.inspect}"
376
+ failed.push(result['target'])
350
377
  end
351
- span.add_field('litmus.connectivity_failed', failed)
378
+ span.add_field('litmus.connectivity_success', results.select { |r| r['status'] == 'success' })
379
+ span.add_field('litmus.connectivity_failure', results.reject { |r| r['status'] == 'success' })
352
380
  raise "Connectivity has failed on: #{failed}" unless failed.length.zero?
353
381
 
382
+ puts 'Connectivity check PASSED.'
354
383
  true
355
384
  end
356
385
  end
@@ -397,4 +426,50 @@ module PuppetLitmus::RakeHelper
397
426
 
398
427
  nil
399
428
  end
429
+
430
+ def start_spinner(message)
431
+ if (ENV['CI'] || '').downcase == 'true'
432
+ puts message
433
+ spinner = Thread.new do
434
+ # CI systems are strange beasts, we only output a '.' every wee while to keep the terminal alive.
435
+ loop do
436
+ printf '.'
437
+ sleep(10)
438
+ end
439
+ end
440
+ else
441
+ require 'tty-spinner'
442
+ spinner = TTY::Spinner.new("[:spinner] #{message}")
443
+ spinner.auto_spin
444
+ end
445
+ spinner
446
+ end
447
+
448
+ def stop_spinner(spinner)
449
+ if (ENV['CI'] || '').downcase == 'true'
450
+ Thread.kill(spinner)
451
+ else
452
+ spinner.success
453
+ end
454
+ end
455
+
456
+ require 'retryable'
457
+
458
+ Retryable.configure do |config|
459
+ config.sleep = ->(n) { (1.5**n) + Random.rand(0.5) }
460
+ # config.log_method = ->(retries, exception) do
461
+ # Logger.new($stdout).debug("[Attempt ##{retries}] Retrying because [#{exception.class} - #{exception.message}]: #{exception.backtrace.first(5).join(' | ')}")
462
+ # end
463
+ end
464
+
465
+ class LitmusTimeoutError < StandardError; end
466
+
467
+ def with_retries(options: { tries: Float::INFINITY }, max_wait_minutes: 8)
468
+ stop = Time.now + (max_wait_minutes * 60)
469
+ Retryable.retryable(options.merge(not: [LitmusTimeoutError])) do
470
+ raise LitmusTimeoutError if Time.now > stop
471
+
472
+ yield
473
+ end
474
+ end
400
475
  end
@@ -33,35 +33,35 @@ namespace :litmus do
33
33
  inventory_vars = provision_hash[args[:key]]['vars']
34
34
  # Splat the params into environment variables to pass to the provision task but only in this runspace
35
35
  provision_hash[args[:key]]['params']&.each { |k, value| ENV[k.upcase] = value.to_s }
36
- results = []
37
36
  failed_image_message = ''
38
- provision_hash[args[:key]]['images'].each do |image|
39
- if (ENV['CI'] == 'true') || !ENV['DISTELLI_BUILDNUM'].nil?
40
- progress = Thread.new do
41
- loop do
42
- printf '.'
43
- sleep(10)
44
- end
45
- end
46
- else
47
- require 'tty-spinner'
48
- spinner = TTY::Spinner.new("Provisioning #{image} using #{provisioner} provisioner.[:spinner]")
49
- spinner.auto_spin
50
- end
51
- result = provision(provisioner, image, inventory_vars)
52
-
53
- if (ENV['CI'] == 'true') || !ENV['DISTELLI_BUILDNUM'].nil?
54
- Thread.kill(progress)
55
- else
56
- spinner.success
37
+ if provision_hash[args[:key]]['images'].instance_of?(Hash)
38
+ begin
39
+ spinner = start_spinner("Provisioning multiple images using #{provisioner} provisioner.")
40
+ result = provision(provisioner, provision_hash[args[:key]]['images'], inventory_vars)
41
+ ensure
42
+ stop_spinner(spinner)
57
43
  end
58
44
 
59
45
  if result.first['status'] != 'success'
60
46
  failed_image_message += "=====\n#{result.first['target']}\n#{result.first['value']['_output']}\n#{result.inspect}"
61
47
  else
62
- $stdout.puts "#{result.first['value']['node_name']}, #{image}"
48
+ $stdout.puts 'Success'
49
+ end
50
+ else
51
+ provision_hash[args[:key]]['images'].each do |image|
52
+ begin
53
+ spinner = start_spinner("Provisioning #{image} using #{provisioner} provisioner.")
54
+ result = provision(provisioner, image, inventory_vars)
55
+ ensure
56
+ stop_spinner(spinner)
57
+ end
58
+
59
+ if result.first['status'] != 'success'
60
+ failed_image_message += "=====\n#{result.first['target']}\n#{result.first['value']['_output']}\n#{result.inspect}"
61
+ else
62
+ $stdout.puts "#{result.first['value']['node_name']}, #{image}"
63
+ end
63
64
  end
64
- results << result
65
65
  end
66
66
 
67
67
  raise "Failed to provision with '#{provisioner}'\n #{failed_image_message}" unless failed_image_message.empty?
@@ -74,28 +74,35 @@ namespace :litmus do
74
74
  desc 'provision a test system using the given provisioner and platform name. See the puppetlabs-provision module tasks for more documentation'
75
75
  task :provision, [:provisioner, :platform, :inventory_vars] do |_task, args|
76
76
  Rake::Task['spec_prep'].invoke
77
- if (ENV['CI'] == 'true') || !ENV['DISTELLI_BUILDNUM'].nil?
78
- progress = Thread.new do
79
- loop do
80
- printf '.'
81
- sleep(10)
77
+
78
+ begin
79
+ spinner = start_spinner("Provisioning #{args[:platform]} using #{args[:provisioner]} provisioner.")
80
+
81
+ results = provision(args[:provisioner], args[:platform], args[:inventory_vars])
82
+
83
+ unless results.first['status'] == 'success'
84
+ raise "Failed provisioning #{args[:platform]} using #{args[:provisioner]}\n#{results.first}"
85
+ end
86
+
87
+ puts "Successfully provisioned #{args[:platform]} using #{args[:provisioner]}\n"
88
+
89
+ target_names = if results.first['value']['node']
90
+ [results.first['value']['node']['uri']]
91
+ else
92
+ results.first['value']['target_names'] || [] # provision_service multi-node provisioning
93
+ end
94
+ target_names.each do |target|
95
+ Honeycomb.start_span(name: 'litmus.provision.check_connectivity') do |span|
96
+ span.add_field('target_name', target)
97
+ with_retries do
98
+ check_connectivity?(inventory_hash_from_inventory_file, target)
99
+ end
82
100
  end
83
101
  end
84
- else
85
- require 'tty-spinner'
86
- spinner = TTY::Spinner.new("Provisioning #{args[:platform]} using #{args[:provisioner]} provisioner.[:spinner]")
87
- spinner.auto_spin
88
- end
89
- results = provision(args[:provisioner], args[:platform], args[:inventory_vars])
90
- if results.first['status'] != 'success'
91
- raise "Failed provisioning #{args[:platform]} using #{args[:provisioner]}\n#{results.first}"
102
+ ensure
103
+ stop_spinner(spinner)
92
104
  end
93
105
 
94
- if (ENV['CI'] == 'true') || !ENV['DISTELLI_BUILDNUM'].nil?
95
- Thread.kill(progress)
96
- else
97
- spinner.success
98
- end
99
106
  puts "#{results.first['value']['node_name']}, #{args[:platform]}"
100
107
  end
101
108
 
@@ -118,36 +125,37 @@ namespace :litmus do
118
125
 
119
126
  results = install_agent(args[:collection], targets, inventory_hash)
120
127
  results.each do |result|
121
- if result['status'] != 'success'
122
- command_to_run = "bolt task run puppet_agent::install --targets #{result['target']} --inventoryfile inventory.yaml --modulepath #{DEFAULT_CONFIG_DATA['modulepath']}"
123
- raise "Failed on #{result['target']}\n#{result}\ntry running '#{command_to_run}'"
124
- else
125
- # validate successful install
126
- puts "Successfull install result: #{result.inspect}" if ENV['DEBUG'] == true
127
- retries = 0
128
- begin
129
- responses = run_command('puppet --version', targets, options: {}, config: DEFAULT_CONFIG_DATA, inventory: inventory_hash.clone)
130
- responses.each do |response|
131
- raise "Error checking puppet version on #{response.to_json}" if response['status'] != 'success'
132
- end
133
- rescue StandardError => e
134
- puts "ERROR:#{e}"
135
- # fix the path
136
- path_changes = configure_path(inventory_hash)
128
+ command_to_run = "bolt task run puppet_agent::install --targets #{result['target']} --inventoryfile inventory.yaml --modulepath #{DEFAULT_CONFIG_DATA['modulepath']}"
129
+ raise "Failed on #{result['target']}\n#{result}\ntry running '#{command_to_run}'" if result['status'] != 'success'
130
+
131
+ # validate successful install
132
+ puts "Successfull install result: #{result.inspect}" if ENV['DEBUG'] == 'true'
133
+ retries = 0
134
+ begin
135
+ responses = run_command('puppet --version', targets, options: {}, config: DEFAULT_CONFIG_DATA, inventory: inventory_hash.clone)
136
+ responses.each do |response|
137
+ raise "Error checking puppet version on #{response.to_json}" if response['status'] != 'success'
138
+ end
139
+ rescue StandardError => e
140
+ puts "ERROR:#{e}" if ENV['DEBUG'] == 'true'
141
+ # fix the path
142
+ path_changes = configure_path(inventory_hash)
143
+ if ENV['DEBUG'] == 'true'
137
144
  path_changes.each do |change|
138
145
  puts "Configuring puppet path result: #{change.inspect}"
139
146
  end
140
-
141
- retries += 1
142
- sleep 3
143
- retry if retries <= 300
144
- raise 'Failed to detect installed puppet version after 5 minutes'
145
147
  end
146
148
 
147
- # add puppet-agent feature to successful nodes
148
- inventory_hash = add_feature_to_node(inventory_hash, 'puppet-agent', result['target'])
149
+ retries += 1
150
+ sleep 3
151
+ retry if retries <= 300
152
+ raise 'Failed to detect installed puppet version after 5 minutes'
149
153
  end
154
+
155
+ # add puppet-agent feature to successful nodes
156
+ inventory_hash = add_feature_to_node(inventory_hash, 'puppet-agent', result['target'])
150
157
  end
158
+
151
159
  # update the inventory with the puppet-agent feature set per node
152
160
  write_to_inventory_file(inventory_hash, 'inventory.yaml')
153
161
  end
@@ -362,7 +370,7 @@ namespace :litmus do
362
370
  success_list = []
363
371
  failure_list = []
364
372
  # Provision targets depending on what environment we're in
365
- if (ENV['CI'] == 'true') || !ENV['DISTELLI_BUILDNUM'].nil?
373
+ if ENV['CI'] == 'true'
366
374
  # CI systems are strange beasts, we only output a '.' every wee while to keep the terminal alive.
367
375
  puts "Running against #{targets.size} targets.\n"
368
376
  progress = Thread.new do
@@ -379,7 +387,7 @@ namespace :litmus do
379
387
  at_exit { exit! }
380
388
 
381
389
  env = options[:env].nil? ? {} : options[:env]
382
- env['HTTP_X_HONEYCOMB_TRACE'] = Honeycomb.current_span.to_trace_header
390
+ env['HONEYCOMB_TRACE'] = Honeycomb.current_span.to_trace_header
383
391
  stdout, stderr, status = Open3.capture3(env, test)
384
392
  ["\n================\n#{title}\n", stdout, stderr, status]
385
393
  end
@@ -397,7 +405,7 @@ namespace :litmus do
397
405
  spinners = TTY::Spinner::Multi.new("[:spinner] Running against #{targets.size} targets.")
398
406
  payloads.each do |title, test, options|
399
407
  env = options[:env].nil? ? {} : options[:env]
400
- env['HTTP_X_HONEYCOMB_TRACE'] = Honeycomb.current_span.to_trace_header
408
+ env['HONEYCOMB_TRACE'] = Honeycomb.current_span.to_trace_header
401
409
  spinners.register("[:spinner] #{title}") do |sp|
402
410
  stdout, stderr, status = Open3.capture3(env, test)
403
411
  if status.to_i.zero?
@@ -2,5 +2,5 @@
2
2
 
3
3
  # version of this gem
4
4
  module PuppetLitmus
5
- VERSION ||= '0.19.0'
5
+ VERSION ||= '0.23.1'
6
6
  end
@@ -77,6 +77,42 @@ RSpec.describe PuppetLitmus::RakeHelper do
77
77
  end
78
78
  end
79
79
 
80
+ context 'with bulk tear_down' do
81
+ let(:inventory_hash) do
82
+ { 'groups' =>
83
+ [{ 'name' => 'ssh_nodes', 'targets' =>
84
+ [
85
+ { 'uri' => 'one.host', 'facts' => { 'provisioner' => 'abs', 'platform' => 'ubuntu-1604-x86_64', 'job_id' => 'iac-task-pid-21648' } },
86
+ { 'uri' => 'two.host', 'facts' => { 'provisioner' => 'abs', 'platform' => 'ubuntu-1804-x86_64', 'job_id' => 'iac-task-pid-21648' } },
87
+ { 'uri' => 'three.host', 'facts' => { 'provisioner' => 'abs', 'platform' => 'ubuntu-2004-x86_64', 'job_id' => 'iac-task-pid-21648' } },
88
+ { 'uri' => 'four.host', 'facts' => { 'provisioner' => 'abs', 'platform' => 'ubuntu-2004-x86_64', 'job_id' => 'iac-task-pid-21649' } },
89
+ ] }] }
90
+ end
91
+ let(:targets) { ['one.host'] }
92
+ let(:params) { { 'action' => 'tear_down', 'node_name' => 'one.host', 'inventory' => Dir.pwd } }
93
+
94
+ it 'calls function' do
95
+ allow(File).to receive(:directory?).with(File.join(described_class::DEFAULT_CONFIG_DATA['modulepath'], 'provision')).and_return(true)
96
+ allow_any_instance_of(BoltSpec::Run).to receive(:run_task).with('provision::abs', 'localhost', params, config: described_class::DEFAULT_CONFIG_DATA, inventory: nil).and_return(
97
+ [{ 'target' => 'localhost',
98
+ 'action' => 'task',
99
+ 'object' => 'provision::abs',
100
+ 'status' => 'success',
101
+ 'value' =>
102
+ { 'status' => 'ok',
103
+ 'removed' =>
104
+ ['one.host',
105
+ 'two.host',
106
+ 'three.host'] } }],
107
+ )
108
+ results = tear_down_nodes(targets, inventory_hash)
109
+ expect(results.keys).to eq(['one.host', 'two.host', 'three.host'])
110
+ results.each_value do |value|
111
+ expect(value[0]['value']).to eq({ 'status' => 'ok' })
112
+ end
113
+ end
114
+ end
115
+
80
116
  context 'with install_agent' do
81
117
  let(:inventory_hash) do
82
118
  { 'groups' =>
@@ -68,7 +68,13 @@ describe 'litmus rake tasks' do
68
68
 
69
69
  allow(File).to receive(:directory?).with(any_args).and_return(true)
70
70
  allow_any_instance_of(BoltSpec::Run).to receive(:run_task).with(any_args).and_return(results)
71
+ allow_any_instance_of(PuppetLitmus::InventoryManipulation).to receive(:inventory_hash_from_inventory_file).with(any_args).and_return({})
72
+ allow_any_instance_of(PuppetLitmus::RakeHelper).to receive(:check_connectivity?).with(any_args).and_return(true)
73
+
74
+ expect($stdout).to receive(:puts).with('Provisioning centos:7 using docker provisioner.')
75
+ expect($stdout).to receive(:puts).with("Successfully provisioned centos:7 using docker\n")
71
76
  expect($stdout).to receive(:puts).with('localhost:2222, centos:7')
77
+
72
78
  Rake::Task['litmus:provision'].invoke('docker', 'centos:7')
73
79
  end
74
80
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppet_litmus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.0
4
+ version: 0.23.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet, Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-23 00:00:00.000000000 Z
11
+ date: 2021-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bolt
@@ -90,6 +90,20 @@ dependencies:
90
90
  - - "<"
91
91
  - !ruby/object:Gem::Version
92
92
  version: 3.0.0
93
+ - !ruby/object:Gem::Dependency
94
+ name: retryable
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '3.0'
100
+ type: :runtime
101
+ prerelease: false
102
+ version_requirements: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - "~>"
105
+ - !ruby/object:Gem::Version
106
+ version: '3.0'
93
107
  - !ruby/object:Gem::Dependency
94
108
  name: parallel
95
109
  requirement: !ruby/object:Gem::Requirement
@@ -171,10 +185,10 @@ files:
171
185
  - spec/data/jim.yaml
172
186
  - spec/lib/puppet_litmus/inventory_manipulation_spec.rb
173
187
  - spec/lib/puppet_litmus/puppet_helpers_spec.rb
188
+ - spec/lib/puppet_litmus/puppet_litmus_version_spec.rb
174
189
  - spec/lib/puppet_litmus/rake_helper_spec.rb
175
190
  - spec/lib/puppet_litmus/rake_tasks_spec.rb
176
191
  - spec/lib/puppet_litmus/util_spec.rb
177
- - spec/lib/puppet_litmus/version_spec.rb
178
192
  - spec/spec_helper.rb
179
193
  homepage: https://github.com/puppetlabs/puppet_litmus
180
194
  licenses:
@@ -188,14 +202,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
188
202
  requirements:
189
203
  - - ">="
190
204
  - !ruby/object:Gem::Version
191
- version: '0'
205
+ version: 2.5.0
192
206
  required_rubygems_version: !ruby/object:Gem::Requirement
193
207
  requirements:
194
208
  - - ">="
195
209
  - !ruby/object:Gem::Version
196
210
  version: '0'
197
211
  requirements: []
198
- rubygems_version: 3.1.4
212
+ rubygems_version: 3.1.2
199
213
  signing_key:
200
214
  specification_version: 4
201
215
  summary: Providing a simple command line tool for puppet content creators, to enable
@@ -203,7 +217,7 @@ summary: Providing a simple command line tool for puppet content creators, to en
203
217
  test_files:
204
218
  - spec/spec_helper.rb
205
219
  - spec/lib/puppet_litmus/rake_tasks_spec.rb
206
- - spec/lib/puppet_litmus/version_spec.rb
220
+ - spec/lib/puppet_litmus/puppet_litmus_version_spec.rb
207
221
  - spec/lib/puppet_litmus/util_spec.rb
208
222
  - spec/lib/puppet_litmus/inventory_manipulation_spec.rb
209
223
  - spec/lib/puppet_litmus/rake_helper_spec.rb