puppet_litmus 0.16.0 → 0.18.3

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.
@@ -1,11 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module PuppetLitmus; end # rubocop:disable Style/Documentation
3
+ require 'bolt_spec/run'
4
+ require 'honeycomb-beeline'
5
+ require 'puppet_litmus/version'
6
+ Honeycomb.configure do |config|
7
+ # override client if no configuration is provided, so that the pesky libhoney warning about lack of configuration is not shown
8
+ unless ENV['HONEYCOMB_WRITEKEY'] && ENV['HONEYCOMB_DATASET']
9
+ config.client = Libhoney::NullClient.new
10
+ end
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
14
+ Honeycomb.add_field_to_trace('litmus.pid', Process.pid)
15
+ Honeycomb.add_field_to_trace('litmus.version', PuppetLitmus::VERSION)
16
+ if ENV['CI'] == 'true' && ENV['TRAVIS'] == 'true'
17
+ Honeycomb.add_field_to_trace('module_name', ENV['TRAVIS_REPO_SLUG'])
18
+ Honeycomb.add_field_to_trace('ci.provider', 'travis')
19
+ Honeycomb.add_field_to_trace('ci.build_id', ENV['TRAVIS_BUILD_ID'])
20
+ Honeycomb.add_field_to_trace('ci.build_url', ENV['TRAVIS_BUILD_WEB_URL'])
21
+ Honeycomb.add_field_to_trace('ci.job_url', ENV['TRAVIS_JOB_WEB_URL'])
22
+ Honeycomb.add_field_to_trace('ci.commit_message', ENV['TRAVIS_COMMIT_MESSAGE'])
23
+ Honeycomb.add_field_to_trace('ci.sha', ENV['TRAVIS_PULL_REQUEST_SHA'] || ENV['TRAVIS_COMMIT'])
24
+ elsif ENV['CI'] == 'True' && ENV['APPVEYOR'] == 'True'
25
+ Honeycomb.add_field_to_trace('module_name', ENV['APPVEYOR_PROJECT_SLUG'])
26
+ Honeycomb.add_field_to_trace('ci.provider', 'appveyor')
27
+ Honeycomb.add_field_to_trace('ci.build_id', ENV['APPVEYOR_BUILD_ID'])
28
+ Honeycomb.add_field_to_trace('ci.build_url', "https://ci.appveyor.com/project/#{ENV['APPVEYOR_REPO_NAME']}/builds/#{ENV['APPVEYOR_BUILD_ID']}")
29
+ Honeycomb.add_field_to_trace('ci.job_url', "https://ci.appveyor.com/project/#{ENV['APPVEYOR_REPO_NAME']}/build/job/#{ENV['APPVEYOR_JOB_ID']}")
30
+ Honeycomb.add_field_to_trace('ci.commit_message', ENV['APPVEYOR_REPO_COMMIT_MESSAGE'])
31
+ Honeycomb.add_field_to_trace('ci.sha', ENV['APPVEYOR_PULL_REQUEST_HEAD_COMMIT'] || ENV['APPVEYOR_REPO_COMMIT'])
32
+ elsif ENV['GITHUB_ACTIONS'] == 'true'
33
+ Honeycomb.add_field_to_trace('module_name', ENV['GITHUB_REPOSITORY'])
34
+ Honeycomb.add_field_to_trace('ci.provider', 'github')
35
+ Honeycomb.add_field_to_trace('ci.build_id', ENV['GITHUB_RUN_ID'])
36
+ Honeycomb.add_field_to_trace('ci.build_url', "https://github.com/#{ENV['GITHUB_REPOSITORY']}/actions/runs/#{ENV['GITHUB_RUN_ID']}")
37
+ Honeycomb.add_field_to_trace('ci.sha', ENV['GITHUB_SHA'])
38
+ end
39
+ at_exit do
40
+ process_span.send
41
+ end
4
42
 
5
43
  # helper methods for the litmus rake tasks
6
44
  module PuppetLitmus::RakeHelper
7
- DEFAULT_CONFIG_DATA ||= { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }.freeze
8
- VALID_PROVISIONERS ||= %w[abs docker docker_exp vagrant vmpooler].freeze
45
+ # DEFAULT_CONFIG_DATA should be frozen for our safety, but it needs to work around https://github.com/puppetlabs/bolt/pull/1696
46
+ DEFAULT_CONFIG_DATA ||= { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') } # .freeze # rubocop:disable Style/MutableConstant
47
+ SUPPORTED_PROVISIONERS ||= %w[abs docker docker_exp vagrant vmpooler].freeze
9
48
 
10
49
  # Gets a string representing the operating system and version.
11
50
  #
@@ -52,53 +91,41 @@ module PuppetLitmus::RakeHelper
52
91
  # @param command [String] command to execute.
53
92
  # @return [Object] the standard out stream.
54
93
  def run_local_command(command)
55
- require 'open3'
56
- stdout, stderr, status = Open3.capture3(command)
57
- error_message = "Attempted to run\ncommand:'#{command}'\nstdout:#{stdout}\nstderr:#{stderr}"
58
- raise error_message unless status.to_i.zero?
94
+ Honeycomb.start_span(name: 'litmus.run_local_command') do |span|
95
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
96
+ span.add_field('litmus.command', command)
59
97
 
60
- stdout
61
- end
98
+ require 'open3'
99
+ stdout, stderr, status = Open3.capture3(command)
100
+ error_message = "Attempted to run\ncommand:'#{command}'\nstdout:#{stdout}\nstderr:#{stderr}"
62
101
 
63
- # Builds all the modules in a specified module
64
- #
65
- # @param source_folder [String] the folder to get the modules from
66
- # @return [Array] an array of module tar's
67
- def build_modules_in_folder(source_folder)
68
- folder_list = Dir.entries(source_folder).reject { |f| File.directory? f }
69
- module_tars = []
70
- folder_list.each do |folder|
71
- folder_handle = Dir.open(File.join(source_folder, folder))
72
- next if File.symlink?(folder_handle)
73
-
74
- module_dir = folder_handle.path
75
- target_dir = File.join(Dir.pwd, 'pkg')
76
- # remove old build folder if exists, before we build afresh
77
- FileUtils.rm_rf(target_dir) if File.directory?(target_dir)
78
-
79
- # build_module
80
- module_tar = build_module(module_dir, target_dir)
81
- module_tars.push(File.new(module_tar))
102
+ raise error_message unless status.to_i.zero?
103
+
104
+ stdout
82
105
  end
83
- module_tars
84
106
  end
85
107
 
86
108
  def provision(provisioner, platform, inventory_vars)
87
- require 'bolt_spec/run'
88
- include BoltSpec::Run
109
+ include ::BoltSpec::Run
89
110
  raise "the provision module was not found in #{DEFAULT_CONFIG_DATA['modulepath']}, please amend the .fixtures.yml file" unless
90
111
  File.directory?(File.join(DEFAULT_CONFIG_DATA['modulepath'], 'provision'))
91
112
 
92
- unless VALID_PROVISIONERS.include?(provisioner)
93
- raise "Unknown provisioner '#{provisioner}', try #{VALID_PROVISIONERS.join('/')}"
94
- end
113
+ params = { 'action' => 'provision', 'platform' => platform, 'inventory' => Dir.pwd }
114
+ params['vars'] = inventory_vars unless inventory_vars.nil?
115
+
116
+ Honeycomb.add_field_to_trace('litmus.provisioner', provisioner)
117
+ Honeycomb.start_span(name: 'litmus.provision') do |span|
118
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
119
+ span.add_field('litmus.platform', platform)
120
+ span.add_field('litmus.inventory', params['inventory'])
121
+ span.add_field('litmus.config', DEFAULT_CONFIG_DATA)
95
122
 
96
- params = if inventory_vars.nil?
97
- { 'action' => 'provision', 'platform' => platform, 'inventory' => Dir.pwd }
98
- else
99
- { 'action' => 'provision', 'platform' => platform, 'inventory' => Dir.pwd, 'vars' => inventory_vars }
100
- end
101
- run_task("provision::#{provisioner}", 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil)
123
+ bolt_result = run_task(provisioner_task(provisioner), 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil)
124
+ span.add_field('litmus.node_name', bolt_result&.first&.dig('value', 'node_name'))
125
+ raise_bolt_errors(bolt_result, "provisioning of #{platform} failed.")
126
+
127
+ bolt_result
128
+ end
102
129
  end
103
130
 
104
131
  def provision_list(provision_hash, key)
@@ -107,6 +134,8 @@ module PuppetLitmus::RakeHelper
107
134
  # Splat the params into environment variables to pass to the provision task but only in this runspace
108
135
  provision_hash[key]['params']&.each { |k, value| ENV[k.upcase] = value.to_s }
109
136
  results = []
137
+
138
+ Honeycomb.current_span.add_field('litmus.images', provision_hash[key]['images'])
110
139
  provision_hash[key]['images'].each do |image|
111
140
  results << provision(provisioner, image, inventory_vars)
112
141
  end
@@ -114,42 +143,62 @@ module PuppetLitmus::RakeHelper
114
143
  end
115
144
 
116
145
  def tear_down_nodes(targets, inventory_hash)
117
- require 'bolt_spec/run'
118
- include BoltSpec::Run
119
- config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
120
- raise "the provision module was not found in #{config_data['modulepath']}, please amend the .fixtures.yml file" unless File.directory?(File.join(config_data['modulepath'], 'provision'))
146
+ Honeycomb.start_span(name: 'litmus.tear_down_nodes') do |span|
147
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
148
+ span.add_field('litmus.targets', targets)
121
149
 
122
- results = {}
123
- targets.each do |node_name|
124
- next if node_name == 'litmus_localhost'
150
+ include ::BoltSpec::Run
151
+ config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
152
+ raise "the provision module was not found in #{config_data['modulepath']}, please amend the .fixtures.yml file" unless File.directory?(File.join(config_data['modulepath'], 'provision'))
125
153
 
126
- result = tear_down(node_name, inventory_hash)
127
- results[node_name] = result unless result == []
154
+ results = {}
155
+ targets.each do |node_name|
156
+ next if node_name == 'litmus_localhost'
157
+
158
+ result = tear_down(node_name, inventory_hash)
159
+ results[node_name] = result unless result == []
160
+ end
161
+ results
128
162
  end
129
- results
130
163
  end
131
164
 
132
165
  def tear_down(node_name, inventory_hash)
133
- # how do we know what provisioner to use
134
- node_facts = facts_from_node(inventory_hash, node_name)
135
- return [] unless VALID_PROVISIONERS.include?(node_facts['provisioner'])
136
-
137
- params = { 'action' => 'tear_down', 'node_name' => node_name, 'inventory' => Dir.pwd }
138
- run_task("provision::#{node_facts['provisioner']}", 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil)
166
+ Honeycomb.start_span(name: 'litmus.tear_down') do |span|
167
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
168
+ # how do we know what provisioner to use
169
+
170
+ span.add_field('litmus.node_name', node_name)
171
+ add_platform_field(inventory_hash, node_name)
172
+
173
+ params = { 'action' => 'tear_down', 'node_name' => node_name, 'inventory' => Dir.pwd }
174
+ node_facts = facts_from_node(inventory_hash, node_name)
175
+ bolt_result = run_task(provisioner_task(node_facts['provisioner']), 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil)
176
+ raise_bolt_errors(bolt_result, "tear_down of #{node_name} failed.")
177
+ bolt_result
178
+ end
139
179
  end
140
180
 
141
181
  def install_agent(collection, targets, inventory_hash)
142
- require 'bolt_spec/run'
143
- include BoltSpec::Run
144
- params = if collection.nil?
145
- {}
146
- else
147
- { 'collection' => collection }
148
- end
149
- raise "puppet_agent was not found in #{DEFAULT_CONFIG_DATA['modulepath']}, please amend the .fixtures.yml file" unless File.directory?(File.join(DEFAULT_CONFIG_DATA['modulepath'], 'puppet_agent'))
150
-
151
- # using boltspec, when the runner is called it changes the inventory_hash dropping the version field. The clone works around this
152
- run_task('puppet_agent::install', targets, params, config: DEFAULT_CONFIG_DATA, inventory: inventory_hash.clone)
182
+ Honeycomb.start_span(name: 'litmus.install_agent') do |span|
183
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
184
+ span.add_field('litmus.collection', collection)
185
+ span.add_field('litmus.targets', targets)
186
+
187
+ include ::BoltSpec::Run
188
+ params = if collection.nil?
189
+ {}
190
+ else
191
+ Honeycomb.current_span.add_field('litmus.collection', collection)
192
+ { 'collection' => collection }
193
+ end
194
+ raise "puppet_agent was not found in #{DEFAULT_CONFIG_DATA['modulepath']}, please amend the .fixtures.yml file" \
195
+ unless File.directory?(File.join(DEFAULT_CONFIG_DATA['modulepath'], 'puppet_agent'))
196
+
197
+ # using boltspec, when the runner is called it changes the inventory_hash dropping the version field. The clone works around this
198
+ bolt_result = run_task('puppet_agent::install', targets, params, config: DEFAULT_CONFIG_DATA, inventory: inventory_hash.clone)
199
+ raise_bolt_errors(bolt_result, 'Installation of agent failed.')
200
+ bolt_result
201
+ end
153
202
  end
154
203
 
155
204
  def configure_path(inventory_hash)
@@ -162,35 +211,87 @@ module PuppetLitmus::RakeHelper
162
211
  results
163
212
  end
164
213
 
214
+ # Build the module in `module_dir` and put the resulting compressed tarball into `target_dir`.
215
+ #
165
216
  # @param opts Hash of options to build the module
166
217
  # @param module_dir [String] The path of the module to build. If missing defaults to Dir.pwd
167
- # @param target_dir [String] The path the module will be built into. The default is <source_dir>/pkg
218
+ # @param target_dir [String] The path the module will be built into. The default is <module_dir>/pkg
168
219
  # @return [String] The path to the built module
169
220
  def build_module(module_dir = nil, target_dir = nil)
170
221
  require 'puppet/modulebuilder'
171
222
 
172
- source_dir = module_dir || Dir.pwd
173
- dest_dir = target_dir || File.join(source_dir, 'pkg')
223
+ module_dir ||= Dir.pwd
224
+ target_dir ||= File.join(source_dir, 'pkg')
225
+
226
+ puts "Building '#{module_dir}' into '#{target_dir}''"
227
+ builder = Puppet::Modulebuilder::Builder.new(module_dir, target_dir, nil)
174
228
 
175
- builder = Puppet::Modulebuilder::Builder.new(source_dir, dest_dir, nil)
176
229
  # Force the metadata to be read. Raises if metadata could not be found
177
230
  _metadata = builder.metadata
178
231
 
179
232
  builder.build
180
233
  end
181
234
 
182
- def install_module(inventory_hash, target_node_name, module_tar)
183
- require 'bolt_spec/run'
184
- include BoltSpec::Run
185
- target_nodes = find_targets(inventory_hash, target_node_name)
186
- target_string = if target_node_name.nil?
187
- 'all'
188
- else
189
- target_node_name
190
- end
191
- run_local_command("bundle exec bolt file upload \"#{module_tar}\" /tmp/#{File.basename(module_tar)} --nodes #{target_string} --inventoryfile inventory.yaml")
192
- install_module_command = "puppet module install /tmp/#{File.basename(module_tar)}"
193
- run_command(install_module_command, target_nodes, config: nil, inventory: inventory_hash)
235
+ # Builds all the modules in a specified directory
236
+ #
237
+ # @param source_dir [String] the directory to get the modules from
238
+ # @param target_dir [String] temporary location to store tarballs before uploading. This directory will be cleaned before use. The default is <source_dir>/pkg
239
+ # @return [Array] an array of module tars' filenames
240
+ def build_modules_in_dir(source_dir, target_dir = nil)
241
+ target_dir ||= File.join(Dir.pwd, 'pkg')
242
+ # remove old build dir if exists, before we build afresh
243
+ FileUtils.rm_rf(target_dir) if File.directory?(target_dir)
244
+
245
+ module_tars = Dir.entries(source_dir).map do |entry|
246
+ next if ['.', '..'].include? entry
247
+
248
+ module_dir = File.join(source_dir, entry)
249
+ next unless File.directory? module_dir
250
+
251
+ build_module(module_dir, target_dir)
252
+ end
253
+ module_tars.compact
254
+ end
255
+
256
+ # @deprecated Use `build_modules_in_dir` instead
257
+ def build_modules_in_folder(source_folder)
258
+ build_modules_in_dir(source_folder)
259
+ end
260
+
261
+ # Install a specific module tarball to the specified target.
262
+ # This method installs dependencies using a forge repository.
263
+ #
264
+ # @param inventory_hash [Hash] the pre-loaded inventory
265
+ # @param target_node_name [String] the name of the target where the module should be installed
266
+ # @param module_tar [String] the filename of the module tarball to upload
267
+ # @param module_repository [String] the URL for the forge to use for downloading modules. Defaults to the public Forge API.
268
+ # @return a bolt result
269
+ def install_module(inventory_hash, target_node_name, module_tar, module_repository = nil)
270
+ Honeycomb.start_span(name: 'install_module') do |span|
271
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
272
+ span.add_field('litmus.target_node_name', target_node_name)
273
+ span.add_field('litmus.module_tar', module_tar)
274
+
275
+ # make sure the module to install is not installed
276
+ # otherwise `puppet module install` might silently skip it
277
+ module_name = File.basename(module_tar, '.tar.gz').split('-', 3)[0..1].join('-')
278
+ uninstall_module(inventory_hash.clone, target_node_name, module_name, force: true)
279
+
280
+ include ::BoltSpec::Run
281
+
282
+ target_nodes = find_targets(inventory_hash, target_node_name)
283
+ span.add_field('litmus.target_nodes', target_nodes)
284
+ bolt_result = upload_file(module_tar, File.basename(module_tar), target_nodes, options: {}, config: nil, inventory: inventory_hash.clone)
285
+ raise_bolt_errors(bolt_result, 'Failed to upload module.')
286
+
287
+ module_repository_opts = "--module_repository '#{module_repository}'" unless module_repository.nil?
288
+ install_module_command = "puppet module install #{module_repository_opts} #{File.basename(module_tar)}"
289
+ span.add_field('litmus.install_module_command', install_module_command)
290
+
291
+ bolt_result = run_command(install_module_command, target_nodes, config: nil, inventory: inventory_hash.clone)
292
+ raise_bolt_errors(bolt_result, "Installation of package #{File.basename(module_tar)} failed.")
293
+ bolt_result
294
+ end
194
295
  end
195
296
 
196
297
  def metadata_module_name
@@ -203,26 +304,87 @@ module PuppetLitmus::RakeHelper
203
304
  metadata['name']
204
305
  end
205
306
 
206
- def uninstall_module(inventory_hash, target_node_name, module_to_remove = nil)
207
- require 'bolt_spec/run'
208
- include BoltSpec::Run
307
+ # Uninstall a module from a specified target
308
+ # @param inventory_hash [Hash] the pre-loaded inventory
309
+ # @param target_node_name [String] the name of the target where the module should be uninstalled
310
+ # @param module_to_remove [String] the name of the module to remove. Defaults to the module under test.
311
+ # @param opts [Hash] additional options to pass on to `puppet module uninstall`
312
+ def uninstall_module(inventory_hash, target_node_name, module_to_remove = nil, **opts)
313
+ include ::BoltSpec::Run
209
314
  module_name = module_to_remove || metadata_module_name
210
315
  target_nodes = find_targets(inventory_hash, target_node_name)
211
316
  install_module_command = "puppet module uninstall #{module_name}"
212
- run_command(install_module_command, target_nodes, config: nil, inventory: inventory_hash)
317
+ install_module_command += ' --force' if opts[:force]
318
+ bolt_result = run_command(install_module_command, target_nodes, config: nil, inventory: inventory_hash)
319
+ # `puppet module uninstall --force` fails if the module is not installed. Ignore errors when force is set
320
+ raise_bolt_errors(bolt_result, "uninstalling #{module_name} failed.") unless opts[:force]
321
+ bolt_result
213
322
  end
214
323
 
215
324
  def check_connectivity?(inventory_hash, target_node_name)
216
- require 'bolt_spec/run'
217
- include BoltSpec::Run
218
- target_nodes = find_targets(inventory_hash, target_node_name)
219
- results = run_command('cd .', target_nodes, config: nil, inventory: inventory_hash)
220
- failed = []
221
- results.each do |result|
222
- failed.push(result['target']) if result['status'] == 'failure'
325
+ Honeycomb.start_span(name: 'litmus.check_connectivity') do |span|
326
+ ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
327
+ # if we're only checking connectivity for a single node
328
+ if target_node_name
329
+ span.add_field('litmus.node_name', target_node_name)
330
+ add_platform_field(inventory_hash, target_node_name)
331
+ end
332
+
333
+ include ::BoltSpec::Run
334
+ target_nodes = find_targets(inventory_hash, target_node_name)
335
+ results = run_command('cd .', target_nodes, config: nil, inventory: inventory_hash)
336
+ span.add_field('litmus.bolt_result', results)
337
+ failed = []
338
+ results.each do |result|
339
+ failed.push(result['target']) if result['status'] == 'failure'
340
+ end
341
+ span.add_field('litmus.connectivity_failed', failed)
342
+ raise "Connectivity has failed on: #{failed}" unless failed.length.zero?
343
+
344
+ true
345
+ end
346
+ end
347
+
348
+ def provisioner_task(provisioner)
349
+ if SUPPORTED_PROVISIONERS.include?(provisioner)
350
+ "provision::#{provisioner}"
351
+ else
352
+ warn "WARNING: Unsuported provisioner '#{provisioner}', try #{SUPPORTED_PROVISIONERS.join('/')}"
353
+ provisioner.to_s
354
+ end
355
+ end
356
+
357
+ # Parse out errors messages in result set returned by Bolt command.
358
+ #
359
+ # @param result_set [Array] result set returned by Bolt command.
360
+ # @return [Hash] Errors grouped by target.
361
+ def check_bolt_errors(result_set)
362
+ errors = {}
363
+ # iterate through each error
364
+ result_set.each do |target_result|
365
+ status = target_result['status']
366
+ # jump to the next one when there is not fail
367
+ next if status != 'failure'
368
+
369
+ target = target_result['target']
370
+ # get some info from error
371
+ errors[target] = target_result['value']
372
+ end
373
+ errors
374
+ end
375
+
376
+ # Parse out errors messages in result set returned by Bolt command. If there are errors, raise them.
377
+ #
378
+ # @param result_set [Array] result set returned by Bolt command.
379
+ # @param error_msg [String] error message to raise when errors are detected. The actual errors will be appended.
380
+ def raise_bolt_errors(result_set, error_msg)
381
+ errors = check_bolt_errors(result_set)
382
+
383
+ unless errors.empty?
384
+ formatted_results = errors.map { |k, v| " #{k}: #{v.inspect}" }.join("\n")
385
+ raise "#{error_msg}\nResults:\n#{formatted_results}}"
223
386
  end
224
- raise "Connectivity has failed on: #{failed}" unless failed.length.zero?
225
387
 
226
- true
388
+ nil
227
389
  end
228
390
  end
@@ -17,15 +17,15 @@ namespace :litmus do
17
17
  end
18
18
 
19
19
  # Provisions a list of OSes from provision.yaml file e.g. 'bundle exec rake litmus:provision_list[default]'.
20
- # @See https://github.com/puppetlabs/puppet_litmus/wiki/Overview-of-Litmus#provisioning-via-yaml
20
+ # @See https://puppetlabs.github.io/litmus/Litmus-core-commands.html#provisioning-via-yaml
21
21
  #
22
22
  # @param :key [String] key that maps to a value for a provisioner and an image to be used for each OS provisioned.
23
- desc "provision list of machines from provision.yaml file. 'bundle exec rake 'litmus:provision_list[default]'"
23
+ desc 'provision list of machines from provision.yaml file'
24
24
  task :provision_list, [:key] do |_task, args|
25
25
  raise 'Cannot find provision.yaml file' unless File.file?('./provision.yaml')
26
26
 
27
27
  provision_hash = YAML.load_file('./provision.yaml')
28
- raise "No key #{args[:key]} in ./provision.yaml, see https://github.com/puppetlabs/puppet_litmus/wiki/Overview-of-Litmus#provisioning-via-yaml for examples" if provision_hash[args[:key]].nil?
28
+ raise "No key #{args[:key]} in ./provision.yaml, see https://puppetlabs.github.io/litmus/Litmus-core-commands.html#provisioning-via-yaml for examples" if provision_hash[args[:key]].nil?
29
29
 
30
30
  Rake::Task['spec_prep'].invoke
31
31
 
@@ -57,9 +57,9 @@ namespace :litmus do
57
57
  end
58
58
 
59
59
  if result.first['status'] != 'success'
60
- failed_image_message += "=====\n#{result.first['node']}\n#{result.first['result']['_output']}\n#{result.inspect}"
60
+ failed_image_message += "=====\n#{result.first['target']}\n#{result.first['value']['_output']}\n#{result.inspect}"
61
61
  else
62
- STDOUT.puts "#{result.first['result']['node_name']}, #{image}"
62
+ STDOUT.puts "#{result.first['value']['node_name']}, #{image}"
63
63
  end
64
64
  results << result
65
65
  end
@@ -71,7 +71,7 @@ namespace :litmus do
71
71
  #
72
72
  # @param :provisioner [String] provisioner to use in provisioning given platform.
73
73
  # @param :platform [String] OS platform for container or VM to use.
74
- desc "provision container/VM - abs/docker/vagrant/vmpooler eg 'bundle exec rake 'litmus:provision[vmpooler, ubuntu-1604-x86_64]'"
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
77
  if (ENV['CI'] == 'true') || !ENV['DISTELLI_BUILDNUM'].nil?
@@ -96,14 +96,14 @@ namespace :litmus do
96
96
  else
97
97
  spinner.success
98
98
  end
99
- puts "#{results.first['result']['node_name']}, #{args[:platform]}"
99
+ puts "#{results.first['value']['node_name']}, #{args[:platform]}"
100
100
  end
101
101
 
102
102
  # Install puppet agent on a collection of nodes
103
103
  #
104
104
  # @param :collection [String] parameters to pass to the puppet agent install command.
105
105
  # @param :target_node_name [Array] nodes on which to install puppet agent.
106
- desc 'install puppet agent, [:collection, :target_node_name]'
106
+ desc 'install a puppet agent to all or a specified set of targets'
107
107
  task :install_agent, [:collection, :target_node_name] do |_task, args|
108
108
  inventory_hash = inventory_hash_from_inventory_file
109
109
  targets = find_targets(inventory_hash, args[:target_node_name])
@@ -119,11 +119,11 @@ namespace :litmus do
119
119
  results = install_agent(args[:collection], targets, inventory_hash)
120
120
  results.each do |result|
121
121
  if result['status'] != 'success'
122
- command_to_run = "bolt task run puppet_agent::install --targets #{result['node']} --inventoryfile inventory.yaml --modulepath #{DEFAULT_CONFIG_DATA['modulepath']}"
123
- raise "Failed on #{result['node']}\n#{result}\ntry running '#{command_to_run}'"
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
124
  else
125
125
  # add puppet-agent feature to successful nodes
126
- inventory_hash = add_feature_to_node(inventory_hash, 'puppet-agent', result['node'])
126
+ inventory_hash = add_feature_to_node(inventory_hash, 'puppet-agent', result['target'])
127
127
  end
128
128
  end
129
129
  # update the inventory with the puppet-agent feature set per node
@@ -134,7 +134,7 @@ namespace :litmus do
134
134
 
135
135
  results.each do |result|
136
136
  if result['status'] != 'success'
137
- puts "Failed on #{result['node']}\n#{result}"
137
+ puts "Failed on #{result['target']}\n#{result}"
138
138
  end
139
139
  end
140
140
  end
@@ -143,7 +143,7 @@ namespace :litmus do
143
143
  #
144
144
  # @param :target_node_name [Array] nodes on which to add the feature.
145
145
  # @param :added_feature [String] the feature which you wish to add.
146
- desc 'add_feature, [:added_feature, :target_node_name]'
146
+ desc 'add a feature tag to a node'
147
147
  task :add_feature, [:added_feature, :target_node_name] do |_task, args|
148
148
  inventory_hash = inventory_hash_from_inventory_file
149
149
  targets = find_targets(inventory_hash, args[:target_node_name])
@@ -166,65 +166,69 @@ namespace :litmus do
166
166
  puts 'Feature added'
167
167
  end
168
168
 
169
- # Install the puppet modules from a source directory to nodes. It does not install dependencies.
169
+ # Install the puppet module under test on a collection of nodes
170
170
  #
171
- # @param :source [String] source directory to look in (ignores symlinks) defaults do './spec/fixtures/modules'.
172
171
  # @param :target_node_name [Array] nodes on which to install a puppet module for testing.
173
- desc 'install_module - build and install module'
174
- task :install_modules_from_directory, [:source, :target_node_name] do |_task, args|
172
+ desc 'build the module under test and install it onto targets'
173
+ task :install_module, [:target_node_name, :module_repository] do |_task, args|
174
+ args.with_defaults(target_node_name: nil, module_repository: nil)
175
175
  inventory_hash = inventory_hash_from_inventory_file
176
176
  target_nodes = find_targets(inventory_hash, args[:target_node_name])
177
177
  if target_nodes.empty?
178
178
  puts 'No targets found'
179
179
  exit 0
180
180
  end
181
- source_folder = if args[:source].nil?
182
- './spec/fixtures/modules'
183
- else
184
- File.expand_path(args[:source])
185
- end
186
- raise "Source folder doesnt exist #{source_folder}" unless File.directory?(source_folder)
187
-
188
- module_tars = build_modules_in_folder(source_folder)
189
- puts 'Building'
190
- module_tars.each do |module_tar|
191
- print "#{File.basename(module_tar)} "
192
- end
193
- require 'bolt_spec/run'
194
- include BoltSpec::Run
195
- puts "\nSending"
196
- module_tars.each do |module_tar|
197
- upload_file(module_tar.path, "/tmp/#{File.basename(module_tar)}", target_nodes, options: {}, config: nil, inventory: inventory_hash)
198
- print "#{File.basename(module_tar)} "
199
- end
200
- puts "\nInstalling"
201
- module_tars.each do |module_tar|
202
- # install_module
203
- install_module_command = "puppet module install --force /tmp/#{File.basename(module_tar)}"
204
- run_command(install_module_command, target_nodes, config: nil, inventory: inventory_hash)
205
- print "#{File.basename(module_tar)} "
206
- end
181
+
182
+ module_tar = build_module
183
+ puts "Built '#{module_tar}'"
184
+
185
+ # module_tar = Dir.glob('pkg/*.tar.gz').max_by { |f| File.mtime(f) }
186
+ raise "Unable to find package in 'pkg/*.tar.gz'" if module_tar.nil?
187
+
188
+ install_module(inventory_hash, args[:target_node_name], module_tar, args[:module_repository])
189
+
190
+ puts "Installed '#{module_tar}' on #{args[:target_node_name]}"
207
191
  end
208
192
 
209
- # Check that the nodes in the inventory are still contactable
193
+ # Install the puppet modules from a source directory to nodes. It does not install dependencies.
210
194
  #
211
- # @param :target_node_name [Array] nodes on which to check connnectivity
212
- desc 'check_connectivity - build and install module'
213
- task :check_connectivity, [:target_node_name] do |_task, args|
195
+ # @param :source [String] source directory to look in (ignores symlinks) defaults do './spec/fixtures/modules'.
196
+ # @param :target_node_name [Array] nodes on which to install a puppet module for testing.
197
+ desc 'build and install all modules from a directory'
198
+ task :install_modules_from_directory, [:source, :target_node_name, :module_repository] do |_task, args|
199
+ args.with_defaults(source: nil, target_node_name: nil, module_repository: nil)
214
200
  inventory_hash = inventory_hash_from_inventory_file
215
201
  target_nodes = find_targets(inventory_hash, args[:target_node_name])
216
202
  if target_nodes.empty?
217
203
  puts 'No targets found'
218
204
  exit 0
219
205
  end
220
- check_connectivity?(inventory_hash, args[:target_node_name])
206
+ source_dir = if args[:source].nil?
207
+ './spec/fixtures/modules'
208
+ else
209
+ File.expand_path(args[:source])
210
+ end
211
+ raise "Source directory doesn't exist #{source_dir}" unless File.directory?(source_dir)
212
+
213
+ puts "Building all modules in #{source_dir.inspect}"
214
+ module_tars = build_modules_in_dir(source_dir)
215
+ require 'bolt_spec/run'
216
+ include BoltSpec::Run
217
+ module_tars.each do |module_tar|
218
+ puts "Installing '#{module_tar}'"
219
+ target_nodes.each do |target_node_name|
220
+ install_module(inventory_hash, target_node_name, module_tar, args[:module_repository])
221
+ puts "Installed '#{module_tar}' on #{target_node_name}"
222
+ end
223
+ end
221
224
  end
222
225
 
223
- # Install the puppet module under test on a collection of nodes
226
+ # Uninstall the puppet module under test on a collection of nodes
224
227
  #
225
228
  # @param :target_node_name [Array] nodes on which to install a puppet module for testing.
226
- desc 'install_module - build and install module'
227
- task :install_module, [:target_node_name] do |_task, args|
229
+ # @param :module_name [String] module name to be uninstalled
230
+ desc 'uninstall a specific module'
231
+ task :uninstall_module, [:target_node_name, :module_name] do |_task, args|
228
232
  inventory_hash = inventory_hash_from_inventory_file
229
233
  target_nodes = find_targets(inventory_hash, args[:target_node_name])
230
234
  if target_nodes.empty?
@@ -232,39 +236,58 @@ namespace :litmus do
232
236
  exit 0
233
237
  end
234
238
 
235
- module_tar = build_module
236
- puts 'Built'
237
-
238
- # module_tar = Dir.glob('pkg/*.tar.gz').max_by { |f| File.mtime(f) }
239
- raise "Unable to find package in 'pkg/*.tar.gz'" if module_tar.nil?
240
-
241
- result = install_module(inventory_hash, args[:target_node_name], module_tar)
239
+ result = uninstall_module(inventory_hash, args[:target_node_name], args[:module_name])
242
240
 
243
- raise "Failed trying to run 'puppet module install /tmp/#{File.basename(module_tar)}' against inventory." unless result.is_a?(Array)
241
+ raise "Failed trying to run 'puppet module uninstall #{module_name}' against inventory." unless result.is_a?(Array)
244
242
 
245
243
  result.each do |node|
246
- puts "#{node['node']} failed #{node['result']}" if node['status'] != 'success'
244
+ puts "#{node['target']} failed #{node['value']}" if node['status'] != 'success'
247
245
  end
248
246
 
249
- puts 'Installed'
247
+ puts 'Uninstalled'
248
+ end
249
+
250
+ # Reinstall the puppet module under test on a collection of nodes
251
+ #
252
+ # @param :target_node_name [Array] nodes on which to install a puppet module for testing.
253
+ desc 'reinstall the module under test'
254
+ task :reinstall_module, [:target_node_name, :module_repository] do |_task, args|
255
+ args.with_defaults(target_node_name: nil, module_repository: nil)
256
+ Rake::Task['litmus:uninstall_module'].invoke(args[:target_node_name])
257
+ Rake::Task['litmus:install_module'].invoke(args[:target_node_name], args[:module_repository])
258
+ end
259
+
260
+ # Check that the nodes in the inventory are still contactable
261
+ #
262
+ # @param :target_node_name [Array] nodes on which to check connnectivity
263
+ desc 'check the connectivity to all provisioned targets'
264
+ task :check_connectivity, [:target_node_name] do |_task, args|
265
+ inventory_hash = inventory_hash_from_inventory_file
266
+ target_nodes = find_targets(inventory_hash, args[:target_node_name])
267
+ if target_nodes.empty?
268
+ puts 'No targets found'
269
+ exit 0
270
+ end
271
+ check_connectivity?(inventory_hash, args[:target_node_name])
250
272
  end
251
273
 
252
274
  # Provision a list of machines, install a puppet agent, and install the puppet module under test on a collection of nodes
253
275
  #
254
276
  # @param :key [String] key that maps to a value for a provisioner and an image to be used for each OS provisioned.
255
277
  # @param :collection [String] parameters to pass to the puppet agent install command.
256
- desc 'provision_install - provision a list of machines, install an agent, and the module.'
257
- task :provision_install, [:key, :collection] do |_task, args|
278
+ desc 'provision a list of machines, install an agent, and the module.'
279
+ task :provision_install, [:key, :collection, :module_repository] do |_task, args|
280
+ args.with_defaults(module_repository: nil)
258
281
  Rake::Task['spec_prep'].invoke
259
282
  Rake::Task['litmus:provision_list'].invoke(args[:key])
260
283
  Rake::Task['litmus:install_agent'].invoke(args[:collection])
261
- Rake::Task['litmus:install_module'].invoke
284
+ Rake::Task['litmus:install_module'].invoke(nil, args[:module_repository])
262
285
  end
263
286
 
264
287
  # Decommissions test machines.
265
288
  #
266
289
  # @param :target [Array] nodes to remove from test environemnt and decommission.
267
- desc 'tear-down - decommission machines'
290
+ desc 'destroy provisioned targets'
268
291
  task :tear_down, [:target] do |_task, args|
269
292
  inventory_hash = inventory_hash_from_inventory_file
270
293
  targets = find_targets(inventory_hash, args[:target])
@@ -277,7 +300,7 @@ namespace :litmus do
277
300
  results = tear_down_nodes(targets, inventory_hash)
278
301
  results.each do |node, result|
279
302
  if result.first['status'] != 'success'
280
- bad_results << "#{node}, #{result.first['result']['_error']['msg']}"
303
+ bad_results << "#{node}, #{result.first['value']['_error']['msg']}"
281
304
  else
282
305
  puts "#{node}: #{result.first['status']}"
283
306
  end
@@ -290,39 +313,6 @@ namespace :litmus do
290
313
  end
291
314
  end
292
315
 
293
- # Uninstall the puppet module under test on a collection of nodes
294
- #
295
- # @param :target_node_name [Array] nodes on which to install a puppet module for testing.
296
- # @param :module_name [String] module name to be uninstalled
297
- desc 'uninstall_module - uninstall module'
298
- task :uninstall_module, [:target_node_name, :module_name] do |_task, args|
299
- inventory_hash = inventory_hash_from_inventory_file
300
- target_nodes = find_targets(inventory_hash, args[:target_node_name])
301
- if target_nodes.empty?
302
- puts 'No targets found'
303
- exit 0
304
- end
305
-
306
- result = uninstall_module(inventory_hash, args[:target_node_name], args[:module_name])
307
-
308
- raise "Failed trying to run 'puppet module uninstall #{module_name}' against inventory." unless result.is_a?(Array)
309
-
310
- result.each do |node|
311
- puts "#{node['node']} failed #{node['result']}" if node['status'] != 'success'
312
- end
313
-
314
- puts 'Uninstalled'
315
- end
316
-
317
- # Reinstall the puppet module under test on a collection of nodes
318
- #
319
- # @param :target_node_name [Array] nodes on which to install a puppet module for testing.
320
- desc 'reinstall_module - reinstall module'
321
- task :reinstall_module, [:target_node_name] do |_task, args|
322
- Rake::Task['litmus:uninstall_module'].invoke(args[:target_node_name])
323
- Rake::Task['litmus:install_module'].invoke(args[:target_node_name])
324
- end
325
-
326
316
  namespace :acceptance do
327
317
  require 'rspec/core/rake_task'
328
318
  if File.file?('inventory.yaml')
@@ -339,7 +329,7 @@ namespace :litmus do
339
329
  payloads = []
340
330
  # Generate list of targets to provision
341
331
  targets.each do |target|
342
- test = 'bundle exec rspec ./spec/acceptance --format progress'
332
+ test = 'bundle exec rspec ./spec/acceptance --format progress --require rspec_honeycomb_formatter --format RSpecHoneycombFormatter'
343
333
  title = "#{target}, #{facts_from_node(inventory_hash, target)['platform']}"
344
334
  options = {
345
335
  env: {
@@ -365,7 +355,12 @@ namespace :litmus do
365
355
 
366
356
  require 'parallel'
367
357
  results = Parallel.map(payloads) do |title, test, options|
358
+ # avoid sending the parent process' main span in the sub-processes
359
+ # https://www.ruby-forum.com/t/at-exit-inherited-across-fork/122473/2
360
+ at_exit { exit! }
361
+
368
362
  env = options[:env].nil? ? {} : options[:env]
363
+ env['HTTP_X_HONEYCOMB_TRACE'] = Honeycomb.current_span.to_trace_header
369
364
  stdout, stderr, status = Open3.capture3(env, test)
370
365
  ["\n================\n#{title}\n", stdout, stderr, status]
371
366
  end
@@ -383,6 +378,7 @@ namespace :litmus do
383
378
  spinners = TTY::Spinner::Multi.new("[:spinner] Running against #{targets.size} targets.")
384
379
  payloads.each do |title, test, options|
385
380
  env = options[:env].nil? ? {} : options[:env]
381
+ env['HTTP_X_HONEYCOMB_TRACE'] = Honeycomb.current_span.to_trace_header
386
382
  spinners.register("[:spinner] #{title}") do |sp|
387
383
  stdout, stderr, status = Open3.capture3(env, test)
388
384
  if status.to_i.zero?