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