puppet_litmus 0.18.0 → 0.19.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b57432738dbfa9e3c83c891565286c43703437df4d4b8f3a1c09431012dd869d
4
- data.tar.gz: 7d4213842c29f689b943dea1a89fc1ea5c4cda272a67fad70c2d55a9b74114a8
3
+ metadata.gz: ffba803cc6b53c4bdb7084bf11be4fac807f3b3cd237c5ddb297dcf6ca3a403d
4
+ data.tar.gz: 5cdb613f2be957e20a63fc1250b10113e468e162817657f0f987675f2ba7c055
5
5
  SHA512:
6
- metadata.gz: 01344ef395005540dc7ae2d109b19e6bf94064f0a27031e886c2f114cdaec8c33c6cbcd71c57a0c164b4724cc5014fb17b7b8ddf12220189f2e10bb22ead26b7
7
- data.tar.gz: 4f6a4144f1785e618d40560463de94e874d03208c1a35ba2ca3d031752e9791b133466105f45b50e4ef59f0f12a3e0736e606d2140d0e517b63c633a0dd7aae6
6
+ metadata.gz: ea1fd95a69bfac81b56f94f3ee28b91be2587c501bf24729931c128315e41d5eaf937dce013ee0d63ec7a350469ac32ab0d7711e50411a0e5fe00340dabdd803
7
+ data.tar.gz: 899ac2b0edb422ccf35c9cdb5f46bec6b2a6a97557dca12ffc994e046ed8cf1e6eec2553865a3c4439b272d1dd29b0111fc24b2e1bb93575cb77fc0bfd7606b1
data/README.md CHANGED
@@ -24,7 +24,7 @@ Install Litmus as a gem by running ```gem install puppet_litmus```.
24
24
 
25
25
  ## Documentation
26
26
 
27
- For documentation, see our [Litmus Wiki](https://github.com/puppetlabs/puppet_litmus/wiki).
27
+ For documentation, see our [Litmus Docs Site](https://puppetlabs.github.io/litmus/).
28
28
 
29
29
  ## Other Resources
30
30
 
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # this script creates a build matrix for github actions from the claimed supported platforms in metadata.json
5
+
6
+ require 'json'
7
+
8
+ IMAGE_TABLE = {
9
+ 'RedHat-6' => 'rhel-6',
10
+ 'RedHat-7' => 'rhel-7',
11
+ 'RedHat-8' => 'rhel-8',
12
+ 'SLES-12' => 'sles-12',
13
+ 'SLES-15' => 'sles-15',
14
+ 'Windows-2012 R2' => 'windows-2012-r2-core',
15
+ 'Windows-2016' => 'windows-2016',
16
+ 'Windows-2019' => 'windows-2019-core',
17
+ }.freeze
18
+
19
+ DOCKER_PLATFORMS = [
20
+ 'CentOS-6',
21
+ 'CentOS-7',
22
+ 'CentOS-8',
23
+ 'Debian-10',
24
+ 'Debian-8',
25
+ 'Debian-9',
26
+ 'OracleLinux-6',
27
+ 'OracleLinux-7',
28
+ 'Scientific-6',
29
+ 'Scientific-7',
30
+ 'Ubuntu-14.04',
31
+ 'Ubuntu-16.04',
32
+ 'Ubuntu-18.04',
33
+ 'Ubuntu-20.04',
34
+ ].freeze
35
+
36
+ matrix = {
37
+ platform: [],
38
+ collection: %w[
39
+ puppet5
40
+ puppet6
41
+ puppet7-nightly
42
+ ],
43
+ }
44
+
45
+ metadata = JSON.parse(File.read('metadata.json'))
46
+ metadata['operatingsystem_support'].sort_by { |a| a['operatingsystem'] }.each do |sup|
47
+ os = sup['operatingsystem']
48
+ sup['operatingsystemrelease'].sort_by { |a| a.to_i }.each do |ver|
49
+ image_key = "#{os}-#{ver}"
50
+ if IMAGE_TABLE.key? image_key
51
+ matrix[:platform] << IMAGE_TABLE[image_key]
52
+ elsif DOCKER_PLATFORMS.include? image_key
53
+ puts "Expecting #{image_key} test using docker on travis"
54
+ else
55
+ puts "::warning::Cannot find image for #{image_key}"
56
+ end
57
+ end
58
+ end
59
+
60
+ puts "::set-output name=matrix::#{JSON.generate(matrix)}"
61
+
62
+ puts "Created matrix with #{matrix[:platform].length * matrix[:collection].length} cells."
@@ -4,7 +4,6 @@
4
4
  module PuppetLitmus; end
5
5
 
6
6
  require 'bolt_spec/run'
7
- require 'puppet_litmus/honeycomb_utils'
8
7
  require 'puppet_litmus/inventory_manipulation'
9
8
  require 'puppet_litmus/puppet_helpers'
10
9
  require 'puppet_litmus/rake_helper'
@@ -18,7 +18,7 @@ module PuppetLitmus::InventoryManipulation
18
18
  raise "There is no inventory file at '#{inventory_full_path}'." unless File.exist?(inventory_full_path)
19
19
 
20
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.dig('version').nil? || (inventory_hash['version'] < 2)
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
22
 
23
23
  inventory_hash
24
24
  end
@@ -50,12 +50,11 @@ module PuppetLitmus::InventoryManipulation
50
50
  # @param targets [Array]
51
51
  # @return [Array] array of targets.
52
52
  def find_targets(inventory_hash, targets)
53
- targets = if targets.nil?
54
- inventory_hash.to_s.scan(%r{uri"=>"(\S*)"}).flatten
55
- else
56
- [targets]
57
- end
58
- targets
53
+ if targets.nil?
54
+ inventory_hash.to_s.scan(%r{uri"=>"(\S*)"}).flatten
55
+ else
56
+ [targets]
57
+ end
59
58
  end
60
59
 
61
60
  # Determines if a node_name exists in a group in the inventory_hash.
@@ -235,7 +234,7 @@ module PuppetLitmus::InventoryManipulation
235
234
  #
236
235
  # @param inventory_hash [Hash] hash of the inventory.yaml file
237
236
  # @param feature_name [String] feature to locate in the node
238
- # node_name [String] node of nodes to limit the search for the node_name in
237
+ # @param node_name [String] node of nodes to limit the search for the node_name in
239
238
  # @return inventory.yaml file with feature removed from the node.
240
239
  # @return [Hash] inventory_hash with feature added to node if node_name exists in inventory hash.
241
240
  def remove_feature_from_node(inventory_hash, feature_name, node_name)
@@ -257,4 +256,13 @@ module PuppetLitmus::InventoryManipulation
257
256
  def write_to_inventory_file(inventory_hash, inventory_full_path)
258
257
  File.open(inventory_full_path, 'wb+') { |f| f.write(inventory_hash.to_yaml) }
259
258
  end
259
+
260
+ # Add the `litmus.platform` with platform information for the target
261
+ #
262
+ # @param inventory_hash [Hash] hash of the inventory.yaml file
263
+ # @param node_name [String] node of nodes to limit the search for the node_name in
264
+ def add_platform_field(inventory_hash, node_name)
265
+ facts = facts_from_node(inventory_hash, node_name)
266
+ Honeycomb.current_span.add_field('litmus.platform', facts&.dig('platform'))
267
+ end
260
268
  end
@@ -9,7 +9,7 @@ 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 unless ENV['HTTP_X_HONEYCOMB_TRACE']
12
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
13
13
  manifest_file_location = create_manifest_file(manifest)
14
14
  apply_manifest(nil, expect_failures: false, manifest_file_location: manifest_file_location)
15
15
  apply_manifest(nil, catch_changes: true, manifest_file_location: manifest_file_location)
@@ -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 unless ENV['HTTP_X_HONEYCOMB_TRACE']
42
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
43
43
  span.add_field('litmus.manifest', manifest)
44
44
  span.add_field('litmus.opts', opts)
45
45
 
@@ -73,7 +73,7 @@ module PuppetLitmus::PuppetHelpers
73
73
  raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
74
74
 
75
75
  span.add_field('litmus.node_name', target_node_name)
76
- PuppetLitmus::HoneycombUtils.add_platform_field(inventory_hash, target_node_name)
76
+ add_platform_field(inventory_hash, target_node_name)
77
77
 
78
78
  command_to_run = "#{opts[:prefix_command]} puppet apply #{manifest_file_location}"
79
79
  command_to_run += ' --trace' if !opts[:trace].nil? && (opts[:trace] == true)
@@ -117,7 +117,7 @@ module PuppetLitmus::PuppetHelpers
117
117
  # @return [String] The path to the location of the manifest.
118
118
  def create_manifest_file(manifest)
119
119
  Honeycomb.start_span(name: 'litmus.create_manifest_file') do |span|
120
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
120
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
121
121
  span.add_field('litmus.manifest', manifest)
122
122
 
123
123
  require 'tmpdir'
@@ -133,9 +133,9 @@ module PuppetLitmus::PuppetHelpers
133
133
  # transfer to TARGET_HOST
134
134
  inventory_hash = inventory_hash_from_inventory_file
135
135
  span.add_field('litmus.node_name', target_node_name)
136
- PuppetLitmus::HoneycombUtils.add_platform_field(inventory_hash, target_node_name)
136
+ add_platform_field(inventory_hash, target_node_name)
137
137
 
138
- manifest_file_location = "/tmp/#{File.basename(manifest_file)}"
138
+ manifest_file_location = File.basename(manifest_file)
139
139
  bolt_result = upload_file(manifest_file.path, manifest_file_location, target_node_name, options: {}, config: nil, inventory: inventory_hash)
140
140
  span.add_field('litmus.bolt_result', bolt_result)
141
141
  raise bolt_result.first['value'].to_s unless bolt_result.first['status'] == 'success'
@@ -147,6 +147,42 @@ module PuppetLitmus::PuppetHelpers
147
147
  end
148
148
  end
149
149
 
150
+ # Writes a string variable to a file on a target node at a specified path.
151
+ #
152
+ # @param content [String] String data to write to the file.
153
+ # @param destination [String] The path on the target node to write the file.
154
+ # @return [Bool] Success. The file was succesfully writtne on the target.
155
+ def write_file(content, destination)
156
+ Honeycomb.start_span(name: 'litmus.write_file') do |span|
157
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
158
+ span.add_field('litmus.destination', destination)
159
+
160
+ require 'tmpdir'
161
+ target_node_name = ENV['TARGET_HOST']
162
+
163
+ Tempfile.create('litmus') do |tmp_file|
164
+ tmp_file.write(content)
165
+ tmp_file.flush
166
+ if target_node_name.nil? || target_node_name == 'localhost'
167
+ require 'fileutils'
168
+ # no need to transfer
169
+ FileUtils.cp(tmp_file.path, destination)
170
+ else
171
+ # transfer to TARGET_HOST
172
+ inventory_hash = inventory_hash_from_inventory_file
173
+ span.add_field('litmus.node_name', target_node_name)
174
+ add_platform_field(inventory_hash, target_node_name)
175
+
176
+ bolt_result = upload_file(tmp_file.path, destination, target_node_name, options: {}, config: nil, inventory: inventory_hash)
177
+ span.add_field('litmus.bolt_result.file_upload', bolt_result)
178
+ raise bolt_result.first['value'].to_s unless bolt_result.first['status'] == 'success'
179
+ end
180
+ end
181
+
182
+ true
183
+ end
184
+ end
185
+
150
186
  # Runs a command against the target system
151
187
  #
152
188
  # @param command_to_run [String] The command to execute.
@@ -155,7 +191,7 @@ module PuppetLitmus::PuppetHelpers
155
191
  # @return [Object] A result object from the command.
156
192
  def run_shell(command_to_run, opts = {})
157
193
  Honeycomb.start_span(name: 'litmus.run_shell') do |span|
158
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
194
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
159
195
  span.add_field('litmus.command_to_run', command_to_run)
160
196
  span.add_field('litmus.opts', opts)
161
197
 
@@ -164,7 +200,7 @@ module PuppetLitmus::PuppetHelpers
164
200
  raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
165
201
 
166
202
  span.add_field('litmus.node_name', target_node_name)
167
- PuppetLitmus::HoneycombUtils.add_platform_field(inventory_hash, target_node_name)
203
+ add_platform_field(inventory_hash, target_node_name)
168
204
 
169
205
  bolt_result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
170
206
  span.add_field('litmus.bolt_result', bolt_result)
@@ -192,7 +228,7 @@ module PuppetLitmus::PuppetHelpers
192
228
  # @return [Object] A result object from the command.
193
229
  def bolt_upload_file(source, destination, opts = {}, options = {})
194
230
  Honeycomb.start_span(name: 'litmus.bolt_upload_file') do |span|
195
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
231
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
196
232
  span.add_field('litmus.source', source)
197
233
  span.add_field('litmus.destination', destination)
198
234
  span.add_field('litmus.opts', opts)
@@ -203,7 +239,7 @@ module PuppetLitmus::PuppetHelpers
203
239
  raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
204
240
 
205
241
  span.add_field('litmus.node_name', target_node_name)
206
- PuppetLitmus::HoneycombUtils.add_platform_field(inventory_hash, target_node_name)
242
+ add_platform_field(inventory_hash, target_node_name)
207
243
 
208
244
  bolt_result = upload_file(source, destination, target_node_name, options: options, config: nil, inventory: inventory_hash)
209
245
  span.add_field('litmus.bolt_result', bolt_result)
@@ -244,7 +280,7 @@ module PuppetLitmus::PuppetHelpers
244
280
  # @return [Object] A result object from the task.The values available are stdout, stderr and result.
245
281
  def run_bolt_task(task_name, params = {}, opts = {})
246
282
  Honeycomb.start_span(name: 'litmus.run_task') do |span|
247
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
283
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
248
284
  span.add_field('litmus.task_name', task_name)
249
285
  span.add_field('litmus.params', params)
250
286
  span.add_field('litmus.opts', opts)
@@ -261,7 +297,7 @@ module PuppetLitmus::PuppetHelpers
261
297
  raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
262
298
 
263
299
  span.add_field('litmus.node_name', target_node_name)
264
- PuppetLitmus::HoneycombUtils.add_platform_field(inventory_hash, target_node_name)
300
+ add_platform_field(inventory_hash, target_node_name)
265
301
 
266
302
  bolt_result = run_task(task_name, target_node_name, params, config: config_data, inventory: inventory_hash)
267
303
  result_obj = {
@@ -312,7 +348,7 @@ module PuppetLitmus::PuppetHelpers
312
348
  # @return [Object] A result object from the script run.
313
349
  def bolt_run_script(script, opts = {}, arguments: [])
314
350
  Honeycomb.start_span(name: 'litmus.bolt_run_script') do |span|
315
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
351
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
316
352
  span.add_field('litmus.script', script)
317
353
  span.add_field('litmus.opts', opts)
318
354
  span.add_field('litmus.arguments', arguments)
@@ -322,7 +358,7 @@ module PuppetLitmus::PuppetHelpers
322
358
  raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
323
359
 
324
360
  span.add_field('litmus.node_name', target_node_name)
325
- PuppetLitmus::HoneycombUtils.add_platform_field(inventory_hash, target_node_name)
361
+ add_platform_field(inventory_hash, target_node_name)
326
362
 
327
363
  bolt_result = run_script(script, target_node_name, arguments, options: opts, config: nil, inventory: inventory_hash)
328
364
 
@@ -382,7 +418,7 @@ module PuppetLitmus::PuppetHelpers
382
418
 
383
419
  # Return the stdout of the puppet run
384
420
  def puppet_output(bolt_result)
385
- bolt_result.dig(0, 'value', 'stderr').to_s << \
421
+ bolt_result.dig(0, 'value', 'stderr').to_s + \
386
422
  bolt_result.dig(0, 'value', 'stdout').to_s
387
423
  end
388
424
 
@@ -2,16 +2,21 @@
2
2
 
3
3
  require 'bolt_spec/run'
4
4
  require 'honeycomb-beeline'
5
- require 'puppet_litmus/honeycomb_utils'
5
+ require 'puppet_litmus/version'
6
6
  Honeycomb.configure do |config|
7
7
  # override client if no configuration is provided, so that the pesky libhoney warning about lack of configuration is not shown
8
8
  unless ENV['HONEYCOMB_WRITEKEY'] && ENV['HONEYCOMB_DATASET']
9
9
  config.client = Libhoney::NullClient.new
10
10
  end
11
11
  end
12
- process_span = Honeycomb.start_span(name: 'Litmus Testing', serialized_trace: ENV['HTTP_X_HONEYCOMB_TRACE'])
13
- ENV['HTTP_X_HONEYCOMB_TRACE'] = process_span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
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
14
14
  Honeycomb.add_field_to_trace('litmus.pid', Process.pid)
15
+ if defined? PuppetLitmus::VERSION
16
+ Honeycomb.add_field_to_trace('litmus.version', PuppetLitmus::VERSION)
17
+ else
18
+ Honeycomb.add_field_to_trace('litmus.version', 'undefined')
19
+ end
15
20
  if ENV['CI'] == 'true' && ENV['TRAVIS'] == 'true'
16
21
  Honeycomb.add_field_to_trace('module_name', ENV['TRAVIS_REPO_SLUG'])
17
22
  Honeycomb.add_field_to_trace('ci.provider', 'travis')
@@ -43,7 +48,7 @@ end
43
48
  module PuppetLitmus::RakeHelper
44
49
  # DEFAULT_CONFIG_DATA should be frozen for our safety, but it needs to work around https://github.com/puppetlabs/bolt/pull/1696
45
50
  DEFAULT_CONFIG_DATA ||= { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') } # .freeze # rubocop:disable Style/MutableConstant
46
- SUPPORTED_PROVISIONERS ||= %w[abs docker docker_exp vagrant vmpooler].freeze
51
+ SUPPORTED_PROVISIONERS ||= %w[abs docker docker_exp provision_service vagrant vmpooler].freeze
47
52
 
48
53
  # Gets a string representing the operating system and version.
49
54
  #
@@ -91,7 +96,7 @@ module PuppetLitmus::RakeHelper
91
96
  # @return [Object] the standard out stream.
92
97
  def run_local_command(command)
93
98
  Honeycomb.start_span(name: 'litmus.run_local_command') do |span|
94
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
99
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
95
100
  span.add_field('litmus.command', command)
96
101
 
97
102
  require 'open3'
@@ -114,14 +119,14 @@ module PuppetLitmus::RakeHelper
114
119
 
115
120
  Honeycomb.add_field_to_trace('litmus.provisioner', provisioner)
116
121
  Honeycomb.start_span(name: 'litmus.provision') do |span|
117
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
122
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
118
123
  span.add_field('litmus.platform', platform)
119
124
  span.add_field('litmus.inventory', params['inventory'])
120
125
  span.add_field('litmus.config', DEFAULT_CONFIG_DATA)
121
126
 
122
127
  bolt_result = run_task(provisioner_task(provisioner), 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil)
123
-
124
128
  span.add_field('litmus.node_name', bolt_result&.first&.dig('value', 'node_name'))
129
+ raise_bolt_errors(bolt_result, "provisioning of #{platform} failed.")
125
130
 
126
131
  bolt_result
127
132
  end
@@ -143,7 +148,7 @@ module PuppetLitmus::RakeHelper
143
148
 
144
149
  def tear_down_nodes(targets, inventory_hash)
145
150
  Honeycomb.start_span(name: 'litmus.tear_down_nodes') do |span|
146
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
151
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
147
152
  span.add_field('litmus.targets', targets)
148
153
 
149
154
  include ::BoltSpec::Run
@@ -163,21 +168,23 @@ module PuppetLitmus::RakeHelper
163
168
 
164
169
  def tear_down(node_name, inventory_hash)
165
170
  Honeycomb.start_span(name: 'litmus.tear_down') do |span|
166
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
171
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
167
172
  # how do we know what provisioner to use
168
173
 
169
174
  span.add_field('litmus.node_name', node_name)
170
- PuppetLitmus::HoneycombUtils.add_platform_field(inventory_hash, node_name)
175
+ add_platform_field(inventory_hash, node_name)
171
176
 
172
177
  params = { 'action' => 'tear_down', 'node_name' => node_name, 'inventory' => Dir.pwd }
173
178
  node_facts = facts_from_node(inventory_hash, node_name)
174
- run_task(provisioner_task(node_facts['provisioner']), 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil)
179
+ bolt_result = run_task(provisioner_task(node_facts['provisioner']), 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil)
180
+ raise_bolt_errors(bolt_result, "tear_down of #{node_name} failed.")
181
+ bolt_result
175
182
  end
176
183
  end
177
184
 
178
185
  def install_agent(collection, targets, inventory_hash)
179
186
  Honeycomb.start_span(name: 'litmus.install_agent') do |span|
180
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
187
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
181
188
  span.add_field('litmus.collection', collection)
182
189
  span.add_field('litmus.targets', targets)
183
190
 
@@ -201,81 +208,102 @@ module PuppetLitmus::RakeHelper
201
208
  def configure_path(inventory_hash)
202
209
  results = []
203
210
  # fix the path on ssh_nodes
204
- unless inventory_hash['groups'].select { |group| group['name'] == 'ssh_nodes' }.size.zero?
205
- results = run_command('echo PATH="$PATH:/opt/puppetlabs/puppet/bin" > /etc/environment',
206
- 'ssh_nodes', config: nil, inventory: inventory_hash)
211
+ unless inventory_hash['groups'].select { |group| group['name'] == 'ssh_nodes' && !group['targets'].empty? }.size.zero?
212
+ results << run_command('echo PATH="$PATH:/opt/puppetlabs/puppet/bin" > /etc/environment',
213
+ 'ssh_nodes', config: nil, inventory: inventory_hash)
214
+ end
215
+ unless inventory_hash['groups'].select { |group| group['name'] == 'winrm_nodes' && !group['targets'].empty? }.size.zero?
216
+ results << run_command('[Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\Program Files\Puppet Labs\Puppet\bin;C:\Program Files (x86)\Puppet Labs\Puppet\bin", "Machine")',
217
+ 'winrm_nodes', config: nil, inventory: inventory_hash)
207
218
  end
208
219
  results
209
220
  end
210
221
 
222
+ # Build the module in `module_dir` and put the resulting compressed tarball into `target_dir`.
223
+ #
211
224
  # @param opts Hash of options to build the module
212
225
  # @param module_dir [String] The path of the module to build. If missing defaults to Dir.pwd
213
- # @param target_dir [String] The path the module will be built into. The default is <source_dir>/pkg
226
+ # @param target_dir [String] The path the module will be built into. The default is <module_dir>/pkg
214
227
  # @return [String] The path to the built module
215
228
  def build_module(module_dir = nil, target_dir = nil)
216
229
  require 'puppet/modulebuilder'
217
230
 
218
- source_dir = module_dir || Dir.pwd
219
- dest_dir = target_dir || File.join(source_dir, 'pkg')
231
+ module_dir ||= Dir.pwd
232
+ target_dir ||= File.join(source_dir, 'pkg')
233
+
234
+ puts "Building '#{module_dir}' into '#{target_dir}'"
235
+ builder = Puppet::Modulebuilder::Builder.new(module_dir, target_dir, nil)
220
236
 
221
- builder = Puppet::Modulebuilder::Builder.new(source_dir, dest_dir, nil)
222
237
  # Force the metadata to be read. Raises if metadata could not be found
223
238
  _metadata = builder.metadata
224
239
 
225
240
  builder.build
226
241
  end
227
242
 
228
- def install_module(inventory_hash, target_node_name, module_tar, module_repository = 'https://forgeapi.puppetlabs.com')
243
+ # Builds all the modules in a specified directory
244
+ #
245
+ # @param source_dir [String] the directory to get the modules from
246
+ # @param target_dir [String] temporary location to store tarballs before uploading. This directory will be cleaned before use. The default is <source_dir>/pkg
247
+ # @return [Array] an array of module tars' filenames
248
+ def build_modules_in_dir(source_dir, target_dir = nil)
249
+ target_dir ||= File.join(Dir.pwd, 'pkg')
250
+ # remove old build dir if exists, before we build afresh
251
+ FileUtils.rm_rf(target_dir) if File.directory?(target_dir)
252
+
253
+ module_tars = Dir.entries(source_dir).map do |entry|
254
+ next if ['.', '..'].include? entry
255
+
256
+ module_dir = File.join(source_dir, entry)
257
+ next unless File.directory? module_dir
258
+
259
+ build_module(module_dir, target_dir)
260
+ end
261
+ module_tars.compact
262
+ end
263
+
264
+ # @deprecated Use `build_modules_in_dir` instead
265
+ def build_modules_in_folder(source_folder)
266
+ build_modules_in_dir(source_folder)
267
+ end
268
+
269
+ # Install a specific module tarball to the specified target.
270
+ # This method installs dependencies using a forge repository.
271
+ #
272
+ # @param inventory_hash [Hash] the pre-loaded inventory
273
+ # @param target_node_name [String] the name of the target where the module should be installed
274
+ # @param module_tar [String] the filename of the module tarball to upload
275
+ # @param module_repository [String] the URL for the forge to use for downloading modules. Defaults to the public Forge API.
276
+ # @param ignore_dependencies [Boolean] flag used to ignore module dependencies defaults to false.
277
+ # @return a bolt result
278
+ def install_module(inventory_hash, target_node_name, module_tar, module_repository = nil, ignore_dependencies = false) # rubocop:disable Style/OptionalBooleanParameter
229
279
  Honeycomb.start_span(name: 'install_module') do |span|
230
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
280
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
231
281
  span.add_field('litmus.target_node_name', target_node_name)
232
282
  span.add_field('litmus.module_tar', module_tar)
233
283
 
234
- # make sure the target module is not installed
284
+ # make sure the module to install is not installed
235
285
  # otherwise `puppet module install` might silently skip it
236
- uninstall_module(inventory_hash.clone, target_node_name, force: true)
286
+ module_name = File.basename(module_tar, '.tar.gz').split('-', 3)[0..1].join('-')
287
+ uninstall_module(inventory_hash.clone, target_node_name, module_name, force: true)
237
288
 
238
289
  include ::BoltSpec::Run
239
290
 
240
291
  target_nodes = find_targets(inventory_hash, target_node_name)
241
292
  span.add_field('litmus.target_nodes', target_nodes)
242
- bolt_result = upload_file(module_tar, "/tmp/#{File.basename(module_tar)}", target_nodes, options: {}, config: nil, inventory: inventory_hash.clone)
293
+ bolt_result = upload_file(module_tar, File.basename(module_tar), target_nodes, options: {}, config: nil, inventory: inventory_hash.clone)
243
294
  raise_bolt_errors(bolt_result, 'Failed to upload module.')
244
295
 
245
- install_module_command = "puppet module install --module_repository '#{module_repository}' /tmp/#{File.basename(module_tar)}"
296
+ module_repository_opts = "--module_repository '#{module_repository}'" unless module_repository.nil?
297
+ install_module_command = "puppet module install #{module_repository_opts} #{File.basename(module_tar)}"
298
+ install_module_command += ' --ignore-dependencies --force' if ignore_dependencies.to_s.downcase == 'true'
246
299
  span.add_field('litmus.install_module_command', install_module_command)
247
300
 
248
301
  bolt_result = run_command(install_module_command, target_nodes, config: nil, inventory: inventory_hash.clone)
249
- raise_bolt_errors(bolt_result, "Installation of package #{module_tar} failed.")
302
+ raise_bolt_errors(bolt_result, "Installation of package #{File.basename(module_tar)} failed.")
250
303
  bolt_result
251
304
  end
252
305
  end
253
306
 
254
- # Builds all the modules in a specified module
255
- #
256
- # @param source_folder [String] the folder to get the modules from
257
- # @return [Array] an array of module tar's
258
- def build_modules_in_folder(source_folder)
259
- folder_list = Dir.entries(source_folder).reject { |f| File.directory? f }
260
- module_tars = []
261
-
262
- target_dir = File.join(Dir.pwd, 'pkg')
263
- # remove old build folder if exists, before we build afresh
264
- FileUtils.rm_rf(target_dir) if File.directory?(target_dir)
265
-
266
- folder_list.each do |folder|
267
- folder_handle = Dir.open(File.join(source_folder, folder))
268
- next if File.symlink?(folder_handle)
269
-
270
- module_dir = folder_handle.path
271
-
272
- # build_module
273
- module_tar = build_module(module_dir, target_dir)
274
- module_tars.push(File.new(module_tar))
275
- end
276
- module_tars
277
- end
278
-
279
307
  def metadata_module_name
280
308
  require 'json'
281
309
  raise 'Could not find metadata.json' unless File.exist?(File.join(Dir.pwd, 'metadata.json'))
@@ -286,22 +314,30 @@ module PuppetLitmus::RakeHelper
286
314
  metadata['name']
287
315
  end
288
316
 
317
+ # Uninstall a module from a specified target
318
+ # @param inventory_hash [Hash] the pre-loaded inventory
319
+ # @param target_node_name [String] the name of the target where the module should be uninstalled
320
+ # @param module_to_remove [String] the name of the module to remove. Defaults to the module under test.
321
+ # @param opts [Hash] additional options to pass on to `puppet module uninstall`
289
322
  def uninstall_module(inventory_hash, target_node_name, module_to_remove = nil, **opts)
290
323
  include ::BoltSpec::Run
291
324
  module_name = module_to_remove || metadata_module_name
292
325
  target_nodes = find_targets(inventory_hash, target_node_name)
293
326
  install_module_command = "puppet module uninstall #{module_name}"
294
327
  install_module_command += ' --force' if opts[:force]
295
- run_command(install_module_command, target_nodes, config: nil, inventory: inventory_hash)
328
+ bolt_result = run_command(install_module_command, target_nodes, config: nil, inventory: inventory_hash)
329
+ # `puppet module uninstall --force` fails if the module is not installed. Ignore errors when force is set
330
+ raise_bolt_errors(bolt_result, "uninstalling #{module_name} failed.") unless opts[:force]
331
+ bolt_result
296
332
  end
297
333
 
298
334
  def check_connectivity?(inventory_hash, target_node_name)
299
335
  Honeycomb.start_span(name: 'litmus.check_connectivity') do |span|
300
- ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
336
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
301
337
  # if we're only checking connectivity for a single node
302
338
  if target_node_name
303
339
  span.add_field('litmus.node_name', target_node_name)
304
- PuppetLitmus::HoneycombUtils.add_platform_field(inventory_hash, target_node_name)
340
+ add_platform_field(inventory_hash, target_node_name)
305
341
  end
306
342
 
307
343
  include ::BoltSpec::Run
@@ -331,7 +367,7 @@ module PuppetLitmus::RakeHelper
331
367
  # Parse out errors messages in result set returned by Bolt command.
332
368
  #
333
369
  # @param result_set [Array] result set returned by Bolt command.
334
- # @return [Hash] Error messages grouped by target.
370
+ # @return [Hash] Errors grouped by target.
335
371
  def check_bolt_errors(result_set)
336
372
  errors = {}
337
373
  # iterate through each error
@@ -342,8 +378,7 @@ module PuppetLitmus::RakeHelper
342
378
 
343
379
  target = target_result['target']
344
380
  # get some info from error
345
- error_msg = target_result['value']['_error']['msg']
346
- errors[target] = error_msg
381
+ errors[target] = target_result['value']
347
382
  end
348
383
  errors
349
384
  end
@@ -356,7 +391,8 @@ module PuppetLitmus::RakeHelper
356
391
  errors = check_bolt_errors(result_set)
357
392
 
358
393
  unless errors.empty?
359
- raise "#{error_msg}\nErrors: #{errors}"
394
+ formatted_results = errors.map { |k, v| " #{k}: #{v.inspect}" }.join("\n")
395
+ raise "#{error_msg}\nResults:\n#{formatted_results}}"
360
396
  end
361
397
 
362
398
  nil