puppet_litmus 0.18.3 → 0.22.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 +4 -4
- data/exe/matrix_from_metadata +99 -0
- data/lib/puppet_litmus/inventory_manipulation.rb +12 -8
- data/lib/puppet_litmus/puppet_helpers.rb +55 -11
- data/lib/puppet_litmus/rake_helper.rb +108 -23
- data/lib/puppet_litmus/rake_tasks.rb +96 -67
- data/lib/puppet_litmus/spec_helper_acceptance.rb +4 -1
- data/lib/puppet_litmus/version.rb +1 -1
- data/spec/lib/puppet_litmus/puppet_helpers_spec.rb +47 -9
- data/spec/lib/puppet_litmus/{version_spec.rb → puppet_litmus_version_spec.rb} +0 -0
- data/spec/lib/puppet_litmus/rake_helper_spec.rb +36 -0
- data/spec/lib/puppet_litmus/rake_tasks_spec.rb +13 -7
- metadata +29 -53
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '09a793d09382a0f461b0390ebb71f94ae4a14df41873d44a77f2006ee15934bf'
|
4
|
+
data.tar.gz: eced3b1aceb10d8fc85f90dd15ab3cfc300f793b85e2f516548510d59bd9ab4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2226c90e2928e78e44ab3faf2ff837da356934ea89768637b7943c32d615b3c04ff605a00063dff6ad24126ac5e9a43f2e86f16c6385132bbc9c4ba7a27b82e
|
7
|
+
data.tar.gz: 5651ff30427d4f96b72440c28458251e00c4c595df98a602130fa95aec6f18c1f5d378d2a737ece3c918367cd38933f7aa615733ffc0c5ee77e6240f4fd82db8
|
@@ -0,0 +1,99 @@
|
|
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 and puppet versions 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
|
+
# This table uses the latest version in each collection for accurate
|
37
|
+
# comparison when evaluating puppet requirements from the metadata
|
38
|
+
COLLECTION_TABLE = {
|
39
|
+
'5.5.22' => 'puppet5',
|
40
|
+
'6.19.1' => 'puppet6-nightly',
|
41
|
+
'7.0.0' => 'puppet7-nightly',
|
42
|
+
}.freeze
|
43
|
+
|
44
|
+
matrix = {
|
45
|
+
platform: [],
|
46
|
+
collection: [],
|
47
|
+
}
|
48
|
+
|
49
|
+
metadata = JSON.parse(File.read('metadata.json'))
|
50
|
+
# Set platforms based on declared operating system support
|
51
|
+
metadata['operatingsystem_support'].sort_by { |a| a['operatingsystem'] }.each do |sup|
|
52
|
+
os = sup['operatingsystem']
|
53
|
+
sup['operatingsystemrelease'].sort_by { |a| a.to_i }.each do |ver|
|
54
|
+
image_key = "#{os}-#{ver}"
|
55
|
+
if IMAGE_TABLE.key? image_key
|
56
|
+
matrix[:platform] << IMAGE_TABLE[image_key]
|
57
|
+
elsif DOCKER_PLATFORMS.include? image_key
|
58
|
+
puts "Expecting #{image_key} test using docker on travis"
|
59
|
+
else
|
60
|
+
puts "::warning::Cannot find image for #{image_key}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Set collections based on puppet version requirements
|
66
|
+
if metadata.key?('requirements') && metadata['requirements'].length.positive?
|
67
|
+
metadata['requirements'].each do |req|
|
68
|
+
next unless req.key?('name') && req.key?('version_requirement') && req['name'] == 'puppet'
|
69
|
+
|
70
|
+
ver_regexp = %r{^([>=<]{1,2})\s*([\d.]+)\s+([>=<]{1,2})\s*([\d.]+)$}
|
71
|
+
match = ver_regexp.match(req['version_requirement'])
|
72
|
+
if match.nil?
|
73
|
+
puts "::warning::Didn't recognize version_requirement '#{req['version_requirement']}'"
|
74
|
+
break
|
75
|
+
end
|
76
|
+
|
77
|
+
cmp_one, ver_one, cmp_two, ver_two = match.captures
|
78
|
+
reqs = ["#{cmp_one} #{ver_one}", "#{cmp_two} #{ver_two}"]
|
79
|
+
|
80
|
+
COLLECTION_TABLE.each do |key, val|
|
81
|
+
if Gem::Requirement.create(reqs).satisfied_by?(Gem::Version.new(key))
|
82
|
+
matrix[:collection] << val
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Set to defaults (all collections) if no matches are found
|
89
|
+
if matrix[:collection].empty?
|
90
|
+
matrix[:collection] = COLLECTION_TABLE.values
|
91
|
+
end
|
92
|
+
|
93
|
+
# Just to make sure there aren't any duplicates
|
94
|
+
matrix[:platform] = matrix[:platform].uniq.sort
|
95
|
+
matrix[:collection] = matrix[:collection].uniq.sort
|
96
|
+
|
97
|
+
puts "::set-output name=matrix::#{JSON.generate(matrix)}"
|
98
|
+
|
99
|
+
puts "Created matrix with #{matrix[:platform].length * matrix[:collection].length} cells."
|
@@ -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
|
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
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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.
|
@@ -263,7 +262,12 @@ module PuppetLitmus::InventoryManipulation
|
|
263
262
|
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
264
263
|
# @param node_name [String] node of nodes to limit the search for the node_name in
|
265
264
|
def add_platform_field(inventory_hash, node_name)
|
266
|
-
facts =
|
265
|
+
facts = begin
|
266
|
+
facts_from_node(inventory_hash, node_name)
|
267
|
+
rescue StandardError => e
|
268
|
+
warn e
|
269
|
+
{}
|
270
|
+
end
|
267
271
|
Honeycomb.current_span.add_field('litmus.platform', facts&.dig('platform'))
|
268
272
|
end
|
269
273
|
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['
|
12
|
+
ENV['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)
|
@@ -30,7 +30,7 @@ module PuppetLitmus::PuppetHelpers
|
|
30
30
|
# :catch_failures [Boolean] (false) We're after only complete success so allow exit codes 0 and 2 only.
|
31
31
|
# :expect_failures [Boolean] (false) We're after failures specifically so allow exit codes 1, 4, and 6 only.
|
32
32
|
# :manifest_file_location [Path] The place on the target system.
|
33
|
-
# :hiera_config [Path] The path to the hiera.yaml configuration on the
|
33
|
+
# :hiera_config [Path] The path to the hiera.yaml configuration on the target.
|
34
34
|
# :prefix_command [String] prefixes the puppet apply command; eg "export LANGUAGE='ja'".
|
35
35
|
# :trace [Boolean] run puppet apply with the trace flag (defaults to `true`).
|
36
36
|
# :debug [Boolean] run puppet apply with the debug flag.
|
@@ -39,7 +39,7 @@ module PuppetLitmus::PuppetHelpers
|
|
39
39
|
# @return [Object] A result object from the apply.
|
40
40
|
def apply_manifest(manifest, opts = {})
|
41
41
|
Honeycomb.start_span(name: 'litmus.apply_manifest') do |span|
|
42
|
-
ENV['
|
42
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
43
43
|
span.add_field('litmus.manifest', manifest)
|
44
44
|
span.add_field('litmus.opts', opts)
|
45
45
|
|
@@ -83,11 +83,19 @@ module PuppetLitmus::PuppetHelpers
|
|
83
83
|
command_to_run += ' --noop' if !opts[:noop].nil? && (opts[:noop] == true)
|
84
84
|
command_to_run += ' --detailed-exitcodes' if use_detailed_exit_codes == true
|
85
85
|
|
86
|
+
command_to_run = "try { #{command_to_run}; exit $LASTEXITCODE } catch { write-error $_ ; exit 1 }" if os[:family] == 'windows'
|
87
|
+
|
86
88
|
span.add_field('litmus.command_to_run', command_to_run)
|
87
89
|
span.add_field('litmus.target_node_name', target_node_name)
|
88
|
-
|
89
|
-
|
90
|
+
# IAC-1365 - Workaround for BOLT-1535 and bolt issue #1650
|
91
|
+
# bolt_result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
|
92
|
+
bolt_result = Tempfile.open(['temp', '.ps1']) do |script|
|
93
|
+
script.write(command_to_run)
|
94
|
+
script.close
|
95
|
+
run_script(script.path, target_node_name, [], options: {}, config: nil, inventory: inventory_hash)
|
96
|
+
end
|
90
97
|
|
98
|
+
span.add_field('litmus.bolt_result', bolt_result)
|
91
99
|
result = OpenStruct.new(exit_code: bolt_result.first['value']['exit_code'],
|
92
100
|
stdout: bolt_result.first['value']['stdout'],
|
93
101
|
stderr: bolt_result.first['value']['stderr'])
|
@@ -117,7 +125,7 @@ module PuppetLitmus::PuppetHelpers
|
|
117
125
|
# @return [String] The path to the location of the manifest.
|
118
126
|
def create_manifest_file(manifest)
|
119
127
|
Honeycomb.start_span(name: 'litmus.create_manifest_file') do |span|
|
120
|
-
ENV['
|
128
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
121
129
|
span.add_field('litmus.manifest', manifest)
|
122
130
|
|
123
131
|
require 'tmpdir'
|
@@ -147,6 +155,42 @@ module PuppetLitmus::PuppetHelpers
|
|
147
155
|
end
|
148
156
|
end
|
149
157
|
|
158
|
+
# Writes a string variable to a file on a target node at a specified path.
|
159
|
+
#
|
160
|
+
# @param content [String] String data to write to the file.
|
161
|
+
# @param destination [String] The path on the target node to write the file.
|
162
|
+
# @return [Bool] Success. The file was succesfully writtne on the target.
|
163
|
+
def write_file(content, destination)
|
164
|
+
Honeycomb.start_span(name: 'litmus.write_file') do |span|
|
165
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
166
|
+
span.add_field('litmus.destination', destination)
|
167
|
+
|
168
|
+
require 'tmpdir'
|
169
|
+
target_node_name = ENV['TARGET_HOST']
|
170
|
+
|
171
|
+
Tempfile.create('litmus') do |tmp_file|
|
172
|
+
tmp_file.write(content)
|
173
|
+
tmp_file.flush
|
174
|
+
if target_node_name.nil? || target_node_name == 'localhost'
|
175
|
+
require 'fileutils'
|
176
|
+
# no need to transfer
|
177
|
+
FileUtils.cp(tmp_file.path, destination)
|
178
|
+
else
|
179
|
+
# transfer to TARGET_HOST
|
180
|
+
inventory_hash = inventory_hash_from_inventory_file
|
181
|
+
span.add_field('litmus.node_name', target_node_name)
|
182
|
+
add_platform_field(inventory_hash, target_node_name)
|
183
|
+
|
184
|
+
bolt_result = upload_file(tmp_file.path, destination, target_node_name, options: {}, config: nil, inventory: inventory_hash)
|
185
|
+
span.add_field('litmus.bolt_result.file_upload', bolt_result)
|
186
|
+
raise bolt_result.first['value'].to_s unless bolt_result.first['status'] == 'success'
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
true
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
150
194
|
# Runs a command against the target system
|
151
195
|
#
|
152
196
|
# @param command_to_run [String] The command to execute.
|
@@ -155,7 +199,7 @@ module PuppetLitmus::PuppetHelpers
|
|
155
199
|
# @return [Object] A result object from the command.
|
156
200
|
def run_shell(command_to_run, opts = {})
|
157
201
|
Honeycomb.start_span(name: 'litmus.run_shell') do |span|
|
158
|
-
ENV['
|
202
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
159
203
|
span.add_field('litmus.command_to_run', command_to_run)
|
160
204
|
span.add_field('litmus.opts', opts)
|
161
205
|
|
@@ -192,7 +236,7 @@ module PuppetLitmus::PuppetHelpers
|
|
192
236
|
# @return [Object] A result object from the command.
|
193
237
|
def bolt_upload_file(source, destination, opts = {}, options = {})
|
194
238
|
Honeycomb.start_span(name: 'litmus.bolt_upload_file') do |span|
|
195
|
-
ENV['
|
239
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
196
240
|
span.add_field('litmus.source', source)
|
197
241
|
span.add_field('litmus.destination', destination)
|
198
242
|
span.add_field('litmus.opts', opts)
|
@@ -244,7 +288,7 @@ module PuppetLitmus::PuppetHelpers
|
|
244
288
|
# @return [Object] A result object from the task.The values available are stdout, stderr and result.
|
245
289
|
def run_bolt_task(task_name, params = {}, opts = {})
|
246
290
|
Honeycomb.start_span(name: 'litmus.run_task') do |span|
|
247
|
-
ENV['
|
291
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
248
292
|
span.add_field('litmus.task_name', task_name)
|
249
293
|
span.add_field('litmus.params', params)
|
250
294
|
span.add_field('litmus.opts', opts)
|
@@ -312,7 +356,7 @@ module PuppetLitmus::PuppetHelpers
|
|
312
356
|
# @return [Object] A result object from the script run.
|
313
357
|
def bolt_run_script(script, opts = {}, arguments: [])
|
314
358
|
Honeycomb.start_span(name: 'litmus.bolt_run_script') do |span|
|
315
|
-
ENV['
|
359
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
316
360
|
span.add_field('litmus.script', script)
|
317
361
|
span.add_field('litmus.opts', opts)
|
318
362
|
span.add_field('litmus.arguments', arguments)
|
@@ -382,7 +426,7 @@ module PuppetLitmus::PuppetHelpers
|
|
382
426
|
|
383
427
|
# Return the stdout of the puppet run
|
384
428
|
def puppet_output(bolt_result)
|
385
|
-
bolt_result.dig(0, 'value', 'stderr').to_s
|
429
|
+
bolt_result.dig(0, 'value', 'stderr').to_s + \
|
386
430
|
bolt_result.dig(0, 'value', 'stdout').to_s
|
387
431
|
end
|
388
432
|
|
@@ -9,10 +9,14 @@ Honeycomb.configure do |config|
|
|
9
9
|
config.client = Libhoney::NullClient.new
|
10
10
|
end
|
11
11
|
end
|
12
|
-
process_span = Honeycomb.start_span(name: "litmus: #{([$PROGRAM_NAME] + ($ARGV || [])).join(' ')}", serialized_trace: ENV['
|
13
|
-
ENV['
|
12
|
+
process_span = Honeycomb.start_span(name: "litmus: #{([$PROGRAM_NAME] + ($ARGV || [])).join(' ')}", serialized_trace: ENV['HONEYCOMB_TRACE'])
|
13
|
+
ENV['HONEYCOMB_TRACE'] = process_span.to_trace_header
|
14
14
|
Honeycomb.add_field_to_trace('litmus.pid', Process.pid)
|
15
|
-
|
15
|
+
if defined? PuppetLitmus::VERSION
|
16
|
+
Honeycomb.add_field_to_trace('litmus.version', PuppetLitmus::VERSION)
|
17
|
+
else
|
18
|
+
Honeycomb.add_field_to_trace('litmus.version', 'undefined')
|
19
|
+
end
|
16
20
|
if ENV['CI'] == 'true' && ENV['TRAVIS'] == 'true'
|
17
21
|
Honeycomb.add_field_to_trace('module_name', ENV['TRAVIS_REPO_SLUG'])
|
18
22
|
Honeycomb.add_field_to_trace('ci.provider', 'travis')
|
@@ -37,6 +41,13 @@ elsif ENV['GITHUB_ACTIONS'] == 'true'
|
|
37
41
|
Honeycomb.add_field_to_trace('ci.sha', ENV['GITHUB_SHA'])
|
38
42
|
end
|
39
43
|
at_exit do
|
44
|
+
if $ERROR_INFO.is_a?(SystemExit)
|
45
|
+
process_span.add_field('process.exit_code', $ERROR_INFO.status)
|
46
|
+
elsif $ERROR_INFO
|
47
|
+
process_span.add_field('process.exit_code', $ERROR_INFO.class.name)
|
48
|
+
else
|
49
|
+
process_span.add_field('process.exit_code', 'unknown')
|
50
|
+
end
|
40
51
|
process_span.send
|
41
52
|
end
|
42
53
|
|
@@ -44,7 +55,7 @@ end
|
|
44
55
|
module PuppetLitmus::RakeHelper
|
45
56
|
# DEFAULT_CONFIG_DATA should be frozen for our safety, but it needs to work around https://github.com/puppetlabs/bolt/pull/1696
|
46
57
|
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
|
58
|
+
SUPPORTED_PROVISIONERS ||= %w[abs docker docker_exp provision_service vagrant vmpooler].freeze
|
48
59
|
|
49
60
|
# Gets a string representing the operating system and version.
|
50
61
|
#
|
@@ -92,7 +103,7 @@ module PuppetLitmus::RakeHelper
|
|
92
103
|
# @return [Object] the standard out stream.
|
93
104
|
def run_local_command(command)
|
94
105
|
Honeycomb.start_span(name: 'litmus.run_local_command') do |span|
|
95
|
-
ENV['
|
106
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
96
107
|
span.add_field('litmus.command', command)
|
97
108
|
|
98
109
|
require 'open3'
|
@@ -115,13 +126,18 @@ module PuppetLitmus::RakeHelper
|
|
115
126
|
|
116
127
|
Honeycomb.add_field_to_trace('litmus.provisioner', provisioner)
|
117
128
|
Honeycomb.start_span(name: 'litmus.provision') do |span|
|
118
|
-
ENV['
|
129
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
119
130
|
span.add_field('litmus.platform', platform)
|
120
|
-
|
131
|
+
|
132
|
+
task_name = provisioner_task(provisioner)
|
133
|
+
span.add_field('litmus.task_name', task_name)
|
134
|
+
span.add_field('litmus.params', params)
|
121
135
|
span.add_field('litmus.config', DEFAULT_CONFIG_DATA)
|
122
136
|
|
123
|
-
bolt_result = run_task(
|
137
|
+
bolt_result = run_task(task_name, 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil)
|
138
|
+
span.add_field('litmus.result', bolt_result)
|
124
139
|
span.add_field('litmus.node_name', bolt_result&.first&.dig('value', 'node_name'))
|
140
|
+
|
125
141
|
raise_bolt_errors(bolt_result, "provisioning of #{platform} failed.")
|
126
142
|
|
127
143
|
bolt_result
|
@@ -144,7 +160,7 @@ module PuppetLitmus::RakeHelper
|
|
144
160
|
|
145
161
|
def tear_down_nodes(targets, inventory_hash)
|
146
162
|
Honeycomb.start_span(name: 'litmus.tear_down_nodes') do |span|
|
147
|
-
ENV['
|
163
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
148
164
|
span.add_field('litmus.targets', targets)
|
149
165
|
|
150
166
|
include ::BoltSpec::Run
|
@@ -156,6 +172,18 @@ module PuppetLitmus::RakeHelper
|
|
156
172
|
next if node_name == 'litmus_localhost'
|
157
173
|
|
158
174
|
result = tear_down(node_name, inventory_hash)
|
175
|
+
# Some provisioners tear_down targets that were created as a batch job.
|
176
|
+
# These provisioners should return the list of additional targets
|
177
|
+
# removed so that we do not attempt to process them.
|
178
|
+
if result != [] && result[0]['value'].key?('removed')
|
179
|
+
removed_targets = result[0]['value']['removed']
|
180
|
+
result[0]['value'].delete('removed')
|
181
|
+
removed_targets.each do |removed_target|
|
182
|
+
targets.delete(removed_target)
|
183
|
+
results[removed_target] = result
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
159
187
|
results[node_name] = result unless result == []
|
160
188
|
end
|
161
189
|
results
|
@@ -164,7 +192,7 @@ module PuppetLitmus::RakeHelper
|
|
164
192
|
|
165
193
|
def tear_down(node_name, inventory_hash)
|
166
194
|
Honeycomb.start_span(name: 'litmus.tear_down') do |span|
|
167
|
-
ENV['
|
195
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
168
196
|
# how do we know what provisioner to use
|
169
197
|
|
170
198
|
span.add_field('litmus.node_name', node_name)
|
@@ -180,7 +208,7 @@ module PuppetLitmus::RakeHelper
|
|
180
208
|
|
181
209
|
def install_agent(collection, targets, inventory_hash)
|
182
210
|
Honeycomb.start_span(name: 'litmus.install_agent') do |span|
|
183
|
-
ENV['
|
211
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
184
212
|
span.add_field('litmus.collection', collection)
|
185
213
|
span.add_field('litmus.targets', targets)
|
186
214
|
|
@@ -188,7 +216,6 @@ module PuppetLitmus::RakeHelper
|
|
188
216
|
params = if collection.nil?
|
189
217
|
{}
|
190
218
|
else
|
191
|
-
Honeycomb.current_span.add_field('litmus.collection', collection)
|
192
219
|
{ 'collection' => collection }
|
193
220
|
end
|
194
221
|
raise "puppet_agent was not found in #{DEFAULT_CONFIG_DATA['modulepath']}, please amend the .fixtures.yml file" \
|
@@ -204,9 +231,13 @@ module PuppetLitmus::RakeHelper
|
|
204
231
|
def configure_path(inventory_hash)
|
205
232
|
results = []
|
206
233
|
# fix the path on ssh_nodes
|
207
|
-
unless inventory_hash['groups'].select { |group| group['name'] == 'ssh_nodes' }.size.zero?
|
208
|
-
results
|
209
|
-
|
234
|
+
unless inventory_hash['groups'].select { |group| group['name'] == 'ssh_nodes' && !group['targets'].empty? }.size.zero?
|
235
|
+
results << run_command('echo PATH="$PATH:/opt/puppetlabs/puppet/bin" > /etc/environment',
|
236
|
+
'ssh_nodes', config: nil, inventory: inventory_hash)
|
237
|
+
end
|
238
|
+
unless inventory_hash['groups'].select { |group| group['name'] == 'winrm_nodes' && !group['targets'].empty? }.size.zero?
|
239
|
+
results << run_command('[Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\Program Files\Puppet Labs\Puppet\bin;C:\Program Files (x86)\Puppet Labs\Puppet\bin", "Machine")',
|
240
|
+
'winrm_nodes', config: nil, inventory: inventory_hash)
|
210
241
|
end
|
211
242
|
results
|
212
243
|
end
|
@@ -223,7 +254,7 @@ module PuppetLitmus::RakeHelper
|
|
223
254
|
module_dir ||= Dir.pwd
|
224
255
|
target_dir ||= File.join(source_dir, 'pkg')
|
225
256
|
|
226
|
-
puts "Building '#{module_dir}' into '#{target_dir}'
|
257
|
+
puts "Building '#{module_dir}' into '#{target_dir}'"
|
227
258
|
builder = Puppet::Modulebuilder::Builder.new(module_dir, target_dir, nil)
|
228
259
|
|
229
260
|
# Force the metadata to be read. Raises if metadata could not be found
|
@@ -265,10 +296,11 @@ module PuppetLitmus::RakeHelper
|
|
265
296
|
# @param target_node_name [String] the name of the target where the module should be installed
|
266
297
|
# @param module_tar [String] the filename of the module tarball to upload
|
267
298
|
# @param module_repository [String] the URL for the forge to use for downloading modules. Defaults to the public Forge API.
|
299
|
+
# @param ignore_dependencies [Boolean] flag used to ignore module dependencies defaults to false.
|
268
300
|
# @return a bolt result
|
269
|
-
def install_module(inventory_hash, target_node_name, module_tar, module_repository = nil)
|
301
|
+
def install_module(inventory_hash, target_node_name, module_tar, module_repository = nil, ignore_dependencies = false) # rubocop:disable Style/OptionalBooleanParameter
|
270
302
|
Honeycomb.start_span(name: 'install_module') do |span|
|
271
|
-
ENV['
|
303
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
272
304
|
span.add_field('litmus.target_node_name', target_node_name)
|
273
305
|
span.add_field('litmus.module_tar', module_tar)
|
274
306
|
|
@@ -286,6 +318,7 @@ module PuppetLitmus::RakeHelper
|
|
286
318
|
|
287
319
|
module_repository_opts = "--module_repository '#{module_repository}'" unless module_repository.nil?
|
288
320
|
install_module_command = "puppet module install #{module_repository_opts} #{File.basename(module_tar)}"
|
321
|
+
install_module_command += ' --ignore-dependencies --force' if ignore_dependencies.to_s.downcase == 'true'
|
289
322
|
span.add_field('litmus.install_module_command', install_module_command)
|
290
323
|
|
291
324
|
bolt_result = run_command(install_module_command, target_nodes, config: nil, inventory: inventory_hash.clone)
|
@@ -323,24 +356,30 @@ module PuppetLitmus::RakeHelper
|
|
323
356
|
|
324
357
|
def check_connectivity?(inventory_hash, target_node_name)
|
325
358
|
Honeycomb.start_span(name: 'litmus.check_connectivity') do |span|
|
326
|
-
ENV['
|
359
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
327
360
|
# if we're only checking connectivity for a single node
|
328
361
|
if target_node_name
|
329
|
-
span.add_field('litmus.
|
362
|
+
span.add_field('litmus.target_node_name', target_node_name)
|
330
363
|
add_platform_field(inventory_hash, target_node_name)
|
331
364
|
end
|
332
365
|
|
333
366
|
include ::BoltSpec::Run
|
334
367
|
target_nodes = find_targets(inventory_hash, target_node_name)
|
368
|
+
puts "Checking connectivity for #{target_nodes.inspect}"
|
369
|
+
span.add_field('litmus.target_nodes', target_nodes)
|
370
|
+
|
335
371
|
results = run_command('cd .', target_nodes, config: nil, inventory: inventory_hash)
|
336
372
|
span.add_field('litmus.bolt_result', results)
|
337
373
|
failed = []
|
338
|
-
results.each do |result|
|
339
|
-
|
374
|
+
results.reject { |r| r['status'] == 'success' }.each do |result|
|
375
|
+
puts "Failure connecting to #{result['target']}:\n#{result.inspect}"
|
376
|
+
failed.push(result['target'])
|
340
377
|
end
|
341
|
-
span.add_field('litmus.
|
378
|
+
span.add_field('litmus.connectivity_success', results.select { |r| r['status'] == 'success' })
|
379
|
+
span.add_field('litmus.connectivity_failure', results.reject { |r| r['status'] == 'success' })
|
342
380
|
raise "Connectivity has failed on: #{failed}" unless failed.length.zero?
|
343
381
|
|
382
|
+
puts 'Connectivity check PASSED.'
|
344
383
|
true
|
345
384
|
end
|
346
385
|
end
|
@@ -387,4 +426,50 @@ module PuppetLitmus::RakeHelper
|
|
387
426
|
|
388
427
|
nil
|
389
428
|
end
|
429
|
+
|
430
|
+
def start_spinner(message)
|
431
|
+
if (ENV['CI'] || '').downcase == 'true'
|
432
|
+
puts message
|
433
|
+
spinner = Thread.new do
|
434
|
+
# CI systems are strange beasts, we only output a '.' every wee while to keep the terminal alive.
|
435
|
+
loop do
|
436
|
+
printf '.'
|
437
|
+
sleep(10)
|
438
|
+
end
|
439
|
+
end
|
440
|
+
else
|
441
|
+
require 'tty-spinner'
|
442
|
+
spinner = TTY::Spinner.new("[:spinner] #{message}")
|
443
|
+
spinner.auto_spin
|
444
|
+
end
|
445
|
+
spinner
|
446
|
+
end
|
447
|
+
|
448
|
+
def stop_spinner(spinner)
|
449
|
+
if (ENV['CI'] || '').downcase == 'true'
|
450
|
+
Thread.kill(spinner)
|
451
|
+
else
|
452
|
+
spinner.success
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
require 'retryable'
|
457
|
+
|
458
|
+
Retryable.configure do |config|
|
459
|
+
config.sleep = ->(n) { (1.5**n) + Random.rand(0.5) }
|
460
|
+
# config.log_method = ->(retries, exception) do
|
461
|
+
# Logger.new($stdout).debug("[Attempt ##{retries}] Retrying because [#{exception.class} - #{exception.message}]: #{exception.backtrace.first(5).join(' | ')}")
|
462
|
+
# end
|
463
|
+
end
|
464
|
+
|
465
|
+
class LitmusTimeoutError < StandardError; end
|
466
|
+
|
467
|
+
def with_retries(options: { tries: Float::INFINITY }, max_wait_minutes: 8)
|
468
|
+
stop = Time.now + (max_wait_minutes * 60)
|
469
|
+
Retryable.retryable(options.merge(not: [LitmusTimeoutError])) do
|
470
|
+
raise LitmusTimeoutError if Time.now > stop
|
471
|
+
|
472
|
+
yield
|
473
|
+
end
|
474
|
+
end
|
390
475
|
end
|
@@ -33,35 +33,35 @@ namespace :litmus do
|
|
33
33
|
inventory_vars = provision_hash[args[:key]]['vars']
|
34
34
|
# Splat the params into environment variables to pass to the provision task but only in this runspace
|
35
35
|
provision_hash[args[:key]]['params']&.each { |k, value| ENV[k.upcase] = value.to_s }
|
36
|
-
results = []
|
37
36
|
failed_image_message = ''
|
38
|
-
provision_hash[args[:key]]['images'].
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
end
|
45
|
-
end
|
46
|
-
else
|
47
|
-
require 'tty-spinner'
|
48
|
-
spinner = TTY::Spinner.new("Provisioning #{image} using #{provisioner} provisioner.[:spinner]")
|
49
|
-
spinner.auto_spin
|
50
|
-
end
|
51
|
-
result = provision(provisioner, image, inventory_vars)
|
52
|
-
|
53
|
-
if (ENV['CI'] == 'true') || !ENV['DISTELLI_BUILDNUM'].nil?
|
54
|
-
Thread.kill(progress)
|
55
|
-
else
|
56
|
-
spinner.success
|
37
|
+
if provision_hash[args[:key]]['images'].instance_of?(Hash)
|
38
|
+
begin
|
39
|
+
spinner = start_spinner("Provisioning multiple images using #{provisioner} provisioner.")
|
40
|
+
result = provision(provisioner, provision_hash[args[:key]]['images'], inventory_vars)
|
41
|
+
ensure
|
42
|
+
stop_spinner(spinner)
|
57
43
|
end
|
58
44
|
|
59
45
|
if result.first['status'] != 'success'
|
60
46
|
failed_image_message += "=====\n#{result.first['target']}\n#{result.first['value']['_output']}\n#{result.inspect}"
|
61
47
|
else
|
62
|
-
|
48
|
+
$stdout.puts 'Success'
|
49
|
+
end
|
50
|
+
else
|
51
|
+
provision_hash[args[:key]]['images'].each do |image|
|
52
|
+
begin
|
53
|
+
spinner = start_spinner("Provisioning #{image} using #{provisioner} provisioner.")
|
54
|
+
result = provision(provisioner, image, inventory_vars)
|
55
|
+
ensure
|
56
|
+
stop_spinner(spinner)
|
57
|
+
end
|
58
|
+
|
59
|
+
if result.first['status'] != 'success'
|
60
|
+
failed_image_message += "=====\n#{result.first['target']}\n#{result.first['value']['_output']}\n#{result.inspect}"
|
61
|
+
else
|
62
|
+
$stdout.puts "#{result.first['value']['node_name']}, #{image}"
|
63
|
+
end
|
63
64
|
end
|
64
|
-
results << result
|
65
65
|
end
|
66
66
|
|
67
67
|
raise "Failed to provision with '#{provisioner}'\n #{failed_image_message}" unless failed_image_message.empty?
|
@@ -74,28 +74,35 @@ namespace :litmus do
|
|
74
74
|
desc 'provision a test system using the given provisioner and platform name. See the puppetlabs-provision module tasks for more documentation'
|
75
75
|
task :provision, [:provisioner, :platform, :inventory_vars] do |_task, args|
|
76
76
|
Rake::Task['spec_prep'].invoke
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
77
|
+
|
78
|
+
begin
|
79
|
+
spinner = start_spinner("Provisioning #{args[:platform]} using #{args[:provisioner]} provisioner.")
|
80
|
+
|
81
|
+
results = provision(args[:provisioner], args[:platform], args[:inventory_vars])
|
82
|
+
|
83
|
+
unless results.first['status'] == 'success'
|
84
|
+
raise "Failed provisioning #{args[:platform]} using #{args[:provisioner]}\n#{results.first}"
|
85
|
+
end
|
86
|
+
|
87
|
+
puts "Successfully provisioned #{args[:platform]} using #{args[:provisioner]}\n"
|
88
|
+
|
89
|
+
target_names = if results.first['value']['node']
|
90
|
+
[results.first['value']['node']['uri']]
|
91
|
+
else
|
92
|
+
results.first['value']['target_names'] || [] # provision_service multi-node provisioning
|
93
|
+
end
|
94
|
+
target_names.each do |target|
|
95
|
+
Honeycomb.start_span(name: 'litmus.provision.check_connectivity') do |span|
|
96
|
+
span.add_field('target_name', target)
|
97
|
+
with_retries do
|
98
|
+
check_connectivity?(inventory_hash_from_inventory_file, target)
|
99
|
+
end
|
82
100
|
end
|
83
101
|
end
|
84
|
-
|
85
|
-
|
86
|
-
spinner = TTY::Spinner.new("Provisioning #{args[:platform]} using #{args[:provisioner]} provisioner.[:spinner]")
|
87
|
-
spinner.auto_spin
|
88
|
-
end
|
89
|
-
results = provision(args[:provisioner], args[:platform], args[:inventory_vars])
|
90
|
-
if results.first['status'] != 'success'
|
91
|
-
raise "Failed provisioning #{args[:platform]} using #{args[:provisioner]}\n#{results.first}"
|
102
|
+
ensure
|
103
|
+
stop_spinner(spinner)
|
92
104
|
end
|
93
105
|
|
94
|
-
if (ENV['CI'] == 'true') || !ENV['DISTELLI_BUILDNUM'].nil?
|
95
|
-
Thread.kill(progress)
|
96
|
-
else
|
97
|
-
spinner.success
|
98
|
-
end
|
99
106
|
puts "#{results.first['value']['node_name']}, #{args[:platform]}"
|
100
107
|
end
|
101
108
|
|
@@ -118,25 +125,39 @@ namespace :litmus do
|
|
118
125
|
|
119
126
|
results = install_agent(args[:collection], targets, inventory_hash)
|
120
127
|
results.each do |result|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
128
|
+
command_to_run = "bolt task run puppet_agent::install --targets #{result['target']} --inventoryfile inventory.yaml --modulepath #{DEFAULT_CONFIG_DATA['modulepath']}"
|
129
|
+
raise "Failed on #{result['target']}\n#{result}\ntry running '#{command_to_run}'" if result['status'] != 'success'
|
130
|
+
|
131
|
+
# validate successful install
|
132
|
+
puts "Successfull install result: #{result.inspect}" if ENV['DEBUG'] == 'true'
|
133
|
+
retries = 0
|
134
|
+
begin
|
135
|
+
responses = run_command('puppet --version', targets, options: {}, config: DEFAULT_CONFIG_DATA, inventory: inventory_hash.clone)
|
136
|
+
responses.each do |response|
|
137
|
+
raise "Error checking puppet version on #{response.to_json}" if response['status'] != 'success'
|
138
|
+
end
|
139
|
+
rescue StandardError => e
|
140
|
+
puts "ERROR:#{e}" if ENV['DEBUG'] == 'true'
|
141
|
+
# fix the path
|
142
|
+
path_changes = configure_path(inventory_hash)
|
143
|
+
if ENV['DEBUG'] == 'true'
|
144
|
+
path_changes.each do |change|
|
145
|
+
puts "Configuring puppet path result: #{change.inspect}"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
retries += 1
|
150
|
+
sleep 3
|
151
|
+
retry if retries <= 300
|
152
|
+
raise 'Failed to detect installed puppet version after 5 minutes'
|
127
153
|
end
|
154
|
+
|
155
|
+
# add puppet-agent feature to successful nodes
|
156
|
+
inventory_hash = add_feature_to_node(inventory_hash, 'puppet-agent', result['target'])
|
128
157
|
end
|
158
|
+
|
129
159
|
# update the inventory with the puppet-agent feature set per node
|
130
160
|
write_to_inventory_file(inventory_hash, 'inventory.yaml')
|
131
|
-
|
132
|
-
# fix the path on ssh_nodes
|
133
|
-
results = configure_path(inventory_hash)
|
134
|
-
|
135
|
-
results.each do |result|
|
136
|
-
if result['status'] != 'success'
|
137
|
-
puts "Failed on #{result['target']}\n#{result}"
|
138
|
-
end
|
139
|
-
end
|
140
161
|
end
|
141
162
|
|
142
163
|
# Add a given feature to a selection of nodes
|
@@ -195,8 +216,8 @@ namespace :litmus do
|
|
195
216
|
# @param :source [String] source directory to look in (ignores symlinks) defaults do './spec/fixtures/modules'.
|
196
217
|
# @param :target_node_name [Array] nodes on which to install a puppet module for testing.
|
197
218
|
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)
|
219
|
+
task :install_modules_from_directory, [:source, :target_node_name, :module_repository, :ignore_dependencies] do |_task, args|
|
220
|
+
args.with_defaults(source: nil, target_node_name: nil, module_repository: nil, ignore_dependencies: false)
|
200
221
|
inventory_hash = inventory_hash_from_inventory_file
|
201
222
|
target_nodes = find_targets(inventory_hash, args[:target_node_name])
|
202
223
|
if target_nodes.empty?
|
@@ -217,7 +238,7 @@ namespace :litmus do
|
|
217
238
|
module_tars.each do |module_tar|
|
218
239
|
puts "Installing '#{module_tar}'"
|
219
240
|
target_nodes.each do |target_node_name|
|
220
|
-
install_module(inventory_hash, target_node_name, module_tar, args[:module_repository])
|
241
|
+
install_module(inventory_hash, target_node_name, module_tar, args[:module_repository], args[:ignore_dependencies])
|
221
242
|
puts "Installed '#{module_tar}' on #{target_node_name}"
|
222
243
|
end
|
223
244
|
end
|
@@ -321,15 +342,21 @@ namespace :litmus do
|
|
321
342
|
|
322
343
|
# Run acceptance tests against all machines in the inventory file in parallel.
|
323
344
|
desc 'Run tests in parallel against all machines in the inventory file'
|
324
|
-
task :parallel do
|
345
|
+
task :parallel, [:tag] do |_task, args|
|
346
|
+
args.with_defaults(tag: nil)
|
325
347
|
if targets.empty?
|
326
348
|
puts 'No targets found'
|
327
349
|
exit 0
|
328
350
|
end
|
351
|
+
tag_value = if args[:tag].nil?
|
352
|
+
nil
|
353
|
+
else
|
354
|
+
"--tag #{args[:tag]}"
|
355
|
+
end
|
329
356
|
payloads = []
|
330
357
|
# Generate list of targets to provision
|
331
358
|
targets.each do |target|
|
332
|
-
test =
|
359
|
+
test = "bundle exec rspec ./spec/acceptance #{tag_value} --format progress --require rspec_honeycomb_formatter --format RSpecHoneycombFormatter"
|
333
360
|
title = "#{target}, #{facts_from_node(inventory_hash, target)['platform']}"
|
334
361
|
options = {
|
335
362
|
env: {
|
@@ -343,7 +370,7 @@ namespace :litmus do
|
|
343
370
|
success_list = []
|
344
371
|
failure_list = []
|
345
372
|
# Provision targets depending on what environment we're in
|
346
|
-
if
|
373
|
+
if ENV['CI'] == 'true'
|
347
374
|
# CI systems are strange beasts, we only output a '.' every wee while to keep the terminal alive.
|
348
375
|
puts "Running against #{targets.size} targets.\n"
|
349
376
|
progress = Thread.new do
|
@@ -360,16 +387,16 @@ namespace :litmus do
|
|
360
387
|
at_exit { exit! }
|
361
388
|
|
362
389
|
env = options[:env].nil? ? {} : options[:env]
|
363
|
-
env['
|
390
|
+
env['HONEYCOMB_TRACE'] = Honeycomb.current_span.to_trace_header
|
364
391
|
stdout, stderr, status = Open3.capture3(env, test)
|
365
392
|
["\n================\n#{title}\n", stdout, stderr, status]
|
366
393
|
end
|
367
394
|
# because we cannot modify variables inside of Parallel
|
368
395
|
results.each do |result|
|
369
396
|
if result.last.to_i.zero?
|
370
|
-
success_list.push(result.first.scan(%r{.*})[
|
397
|
+
success_list.push(result.first.scan(%r{.*})[3])
|
371
398
|
else
|
372
|
-
failure_list.push(result.first.scan(%r{.*})[
|
399
|
+
failure_list.push(result.first.scan(%r{.*})[3])
|
373
400
|
end
|
374
401
|
end
|
375
402
|
Thread.kill(progress)
|
@@ -378,7 +405,7 @@ namespace :litmus do
|
|
378
405
|
spinners = TTY::Spinner::Multi.new("[:spinner] Running against #{targets.size} targets.")
|
379
406
|
payloads.each do |title, test, options|
|
380
407
|
env = options[:env].nil? ? {} : options[:env]
|
381
|
-
env['
|
408
|
+
env['HONEYCOMB_TRACE'] = Honeycomb.current_span.to_trace_header
|
382
409
|
spinners.register("[:spinner] #{title}") do |sp|
|
383
410
|
stdout, stderr, status = Open3.capture3(env, test)
|
384
411
|
if status.to_i.zero?
|
@@ -411,8 +438,9 @@ namespace :litmus do
|
|
411
438
|
desc "Run serverspec against #{target}"
|
412
439
|
next if target == 'litmus_localhost'
|
413
440
|
|
414
|
-
RSpec::Core::RakeTask.new(target.to_sym) do |t|
|
441
|
+
RSpec::Core::RakeTask.new(target.to_sym, :tag) do |t, args|
|
415
442
|
t.pattern = 'spec/acceptance/**{,/*/**}/*_spec.rb'
|
443
|
+
t.rspec_opts = "--tag #{args[:tag]}" unless args[:tag].nil?
|
416
444
|
ENV['TARGET_HOST'] = target
|
417
445
|
end
|
418
446
|
end
|
@@ -421,8 +449,9 @@ namespace :litmus do
|
|
421
449
|
# add localhost separately
|
422
450
|
desc 'Run serverspec against localhost, USE WITH CAUTION, this action can be potentially dangerous.'
|
423
451
|
host = 'localhost'
|
424
|
-
RSpec::Core::RakeTask.new(host.to_sym) do |t|
|
452
|
+
RSpec::Core::RakeTask.new(host.to_sym, :tag) do |t, args|
|
425
453
|
t.pattern = 'spec/acceptance/**{,/*/**}/*_spec.rb'
|
454
|
+
t.rspec_opts = "--tag #{args[:tag]}" unless args[:tag].nil?
|
426
455
|
Rake::Task['spec_prep'].invoke
|
427
456
|
ENV['TARGET_HOST'] = host
|
428
457
|
end
|
@@ -35,6 +35,7 @@ module PuppetLitmus
|
|
35
35
|
options[:port] = node_config.dig('ssh', 'port') unless node_config.dig('ssh', 'port').nil?
|
36
36
|
options[:keys] = node_config.dig('ssh', 'private-key') unless node_config.dig('ssh', 'private-key').nil?
|
37
37
|
options[:password] = node_config.dig('ssh', 'password') unless node_config.dig('ssh', 'password').nil?
|
38
|
+
run_as = node_config.dig('ssh', 'run-as') unless node_config.dig('ssh', 'run-as').nil?
|
38
39
|
# Support both net-ssh 4 and 5.
|
39
40
|
# rubocop:disable Metrics/BlockNesting
|
40
41
|
options[:verify_host_key] = if node_config.dig('ssh', 'host-key-check').nil?
|
@@ -66,7 +67,9 @@ module PuppetLitmus
|
|
66
67
|
end
|
67
68
|
set :host, options[:host_name] || host
|
68
69
|
set :ssh_options, options
|
69
|
-
set :request_pty,
|
70
|
+
set :request_pty, false
|
71
|
+
set :sudo_password, options[:password] if run_as
|
72
|
+
puts "Running tests as #{run_as}" if run_as
|
70
73
|
elsif target_in_group(inventory_hash, ENV['TARGET_HOST'], 'winrm_nodes')
|
71
74
|
require 'winrm'
|
72
75
|
|
@@ -113,10 +113,10 @@ RSpec.describe PuppetLitmus::PuppetHelpers do
|
|
113
113
|
let(:local) { '/tmp' }
|
114
114
|
let(:remote) { '/remote_tmp' }
|
115
115
|
# Ignore rubocop because these hashes are representative of output from an external method and editing them leads to test failures.
|
116
|
-
# rubocop:disable Layout/
|
117
|
-
let(:result_success) {[{'target'=>'some.host','action'=>'upload','object'=>'C:\foo\bar.ps1','status'=>'success','value'=>{'_output'=>'Uploaded \'C:\foo\bar.ps1\' to \'some.host:C:\bar\''}}]}
|
118
|
-
let(:result_failure) {[{'target'=>'some.host','action'=>nil,'object'=>nil,'status'=>'failure','value'=>{'_error'=>{'kind'=>'puppetlabs.tasks/task_file_error','msg'=>'No such file or directory @ rb_sysopen - /nonexistant/file/path','details'=>{},'issue_code'=>'WRITE_ERROR'}}}]}
|
119
|
-
# rubocop:enable
|
116
|
+
# rubocop:disable Layout/SpaceAroundOperators, Layout/LineLength, Layout/SpaceAfterComma
|
117
|
+
let(:result_success) { [{ 'target'=>'some.host','action'=>'upload','object'=>'C:\foo\bar.ps1','status'=>'success','value'=>{ '_output'=>'Uploaded \'C:\foo\bar.ps1\' to \'some.host:C:\bar\'' } }] }
|
118
|
+
let(:result_failure) { [{ 'target'=>'some.host','action'=>nil,'object'=>nil,'status'=>'failure','value'=>{ '_error'=>{ 'kind'=>'puppetlabs.tasks/task_file_error','msg'=>'No such file or directory @ rb_sysopen - /nonexistant/file/path','details'=>{},'issue_code'=>'WRITE_ERROR' } } }] }
|
119
|
+
# rubocop:enable, Layout/SpaceAroundOperators, Layout/LineLength, Layout/SpaceAfterComma
|
120
120
|
|
121
121
|
it 'responds to run_shell' do
|
122
122
|
expect(self).to respond_to(:bolt_upload_file).with(2..3).arguments
|
@@ -212,11 +212,11 @@ RSpec.describe PuppetLitmus::PuppetHelpers do
|
|
212
212
|
let(:params) { { 'action' => 'install', 'name' => 'foo' } }
|
213
213
|
let(:config_data) { { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') } }
|
214
214
|
# Ignore rubocop because these hashes are representative of output from an external method and editing them leads to test failures.
|
215
|
-
# rubocop:disable Layout/
|
216
|
-
let(:result_unstructured_task_success){ [{'target'=>'some.host','action'=>'task','object'=>'testtask::unstructured','status'=>'success','value'=>{'_output'=>'SUCCESS!'}}]}
|
217
|
-
let(:result_structured_task_success){ [{'target'=>'some.host','action'=>'task','object'=>'testtask::structured','status'=>'success','value'=>{'key1'=>'foo','key2'=>'bar'}}]}
|
218
|
-
let(:result_failure) {[{'target'=>'some.host','action'=>'task','object'=>'testtask::unstructured','status'=>'failure','value'=>{'_error'=>{'msg'=>'FAILURE!','kind'=>'puppetlabs.tasks/task-error','details'=>{'exitcode'=>123}}}}]}
|
219
|
-
# rubocop:enable Layout/
|
215
|
+
# rubocop:disable Layout/SpaceBeforeBlockBraces
|
216
|
+
let(:result_unstructured_task_success){ [{ 'target'=>'some.host','action'=>'task','object'=>'testtask::unstructured','status'=>'success','value'=>{ '_output'=>'SUCCESS!' } }] }
|
217
|
+
let(:result_structured_task_success){ [{ 'target'=>'some.host','action'=>'task','object'=>'testtask::structured','status'=>'success','value'=>{ 'key1'=>'foo','key2'=>'bar' } }] }
|
218
|
+
let(:result_failure) { [{ 'target'=>'some.host','action'=>'task','object'=>'testtask::unstructured','status'=>'failure','value'=>{ '_error'=>{ 'msg'=>'FAILURE!','kind'=>'puppetlabs.tasks/task-error','details'=>{ 'exitcode'=>123 } } } }] }
|
219
|
+
# rubocop:enable Layout/SpaceBeforeBlockBraces, Layout/SpaceAroundOperators, Layout/LineLength, Layout/SpaceAfterComma
|
220
220
|
|
221
221
|
it 'responds to bolt_run_task' do
|
222
222
|
expect(self).to respond_to(:run_bolt_task).with(2..3).arguments
|
@@ -286,4 +286,42 @@ RSpec.describe PuppetLitmus::PuppetHelpers do
|
|
286
286
|
end
|
287
287
|
end
|
288
288
|
end
|
289
|
+
|
290
|
+
describe '.write_file' do
|
291
|
+
let(:content) { 'foo' }
|
292
|
+
let(:destination) { '/tmp/foo' }
|
293
|
+
let(:owner) { 'foo:foo' }
|
294
|
+
let(:local_path) { '/tmp/local_foo' }
|
295
|
+
|
296
|
+
before(:each) do
|
297
|
+
allow_any_instance_of(File).to receive(:path).and_return(local_path)
|
298
|
+
end
|
299
|
+
|
300
|
+
it 'responds to write_file' do
|
301
|
+
expect(self).to respond_to(:write_file).with(2).arguments
|
302
|
+
end
|
303
|
+
|
304
|
+
context 'without setting owner' do
|
305
|
+
it 'call upload file with the correct params' do
|
306
|
+
stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'some.host'))
|
307
|
+
expect(self).to receive(:inventory_hash_from_inventory_file).and_return(inventory_hash)
|
308
|
+
result = instance_double('result')
|
309
|
+
allow(result).to receive(:first).and_return({ 'status' => 'success' })
|
310
|
+
expect(self).to receive(:upload_file).with(local_path, destination, 'some.host', options: {}, config: nil, inventory: inventory_hash).and_return(result)
|
311
|
+
result = write_file(content, destination)
|
312
|
+
expect(result).to be true
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
context 'when upload encounters an error' do
|
317
|
+
it 'call upload file with the correct params' do
|
318
|
+
stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'some.host'))
|
319
|
+
expect(self).to receive(:inventory_hash_from_inventory_file).and_return(inventory_hash)
|
320
|
+
result = instance_double('result')
|
321
|
+
allow(result).to receive(:first).and_return({ 'status' => 'failure', 'value' => 'foo error' })
|
322
|
+
expect(self).to receive(:upload_file).with(local_path, destination, 'some.host', options: {}, config: nil, inventory: inventory_hash).and_return(result)
|
323
|
+
expect { write_file(content, destination) }.to raise_error 'foo error'
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
289
327
|
end
|
File without changes
|
@@ -77,6 +77,42 @@ RSpec.describe PuppetLitmus::RakeHelper do
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
+
context 'with bulk tear_down' do
|
81
|
+
let(:inventory_hash) do
|
82
|
+
{ 'groups' =>
|
83
|
+
[{ 'name' => 'ssh_nodes', 'targets' =>
|
84
|
+
[
|
85
|
+
{ 'uri' => 'one.host', 'facts' => { 'provisioner' => 'abs', 'platform' => 'ubuntu-1604-x86_64', 'job_id' => 'iac-task-pid-21648' } },
|
86
|
+
{ 'uri' => 'two.host', 'facts' => { 'provisioner' => 'abs', 'platform' => 'ubuntu-1804-x86_64', 'job_id' => 'iac-task-pid-21648' } },
|
87
|
+
{ 'uri' => 'three.host', 'facts' => { 'provisioner' => 'abs', 'platform' => 'ubuntu-2004-x86_64', 'job_id' => 'iac-task-pid-21648' } },
|
88
|
+
{ 'uri' => 'four.host', 'facts' => { 'provisioner' => 'abs', 'platform' => 'ubuntu-2004-x86_64', 'job_id' => 'iac-task-pid-21649' } },
|
89
|
+
] }] }
|
90
|
+
end
|
91
|
+
let(:targets) { ['one.host'] }
|
92
|
+
let(:params) { { 'action' => 'tear_down', 'node_name' => 'one.host', 'inventory' => Dir.pwd } }
|
93
|
+
|
94
|
+
it 'calls function' do
|
95
|
+
allow(File).to receive(:directory?).with(File.join(described_class::DEFAULT_CONFIG_DATA['modulepath'], 'provision')).and_return(true)
|
96
|
+
allow_any_instance_of(BoltSpec::Run).to receive(:run_task).with('provision::abs', 'localhost', params, config: described_class::DEFAULT_CONFIG_DATA, inventory: nil).and_return(
|
97
|
+
[{ 'target' => 'localhost',
|
98
|
+
'action' => 'task',
|
99
|
+
'object' => 'provision::abs',
|
100
|
+
'status' => 'success',
|
101
|
+
'value' =>
|
102
|
+
{ 'status' => 'ok',
|
103
|
+
'removed' =>
|
104
|
+
['one.host',
|
105
|
+
'two.host',
|
106
|
+
'three.host'] } }],
|
107
|
+
)
|
108
|
+
results = tear_down_nodes(targets, inventory_hash)
|
109
|
+
expect(results.keys).to eq(['one.host', 'two.host', 'three.host'])
|
110
|
+
results.each_value do |value|
|
111
|
+
expect(value[0]['value']).to eq({ 'status' => 'ok' })
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
80
116
|
context 'with install_agent' do
|
81
117
|
let(:inventory_hash) do
|
82
118
|
{ 'groups' =>
|
@@ -19,9 +19,9 @@ describe 'litmus rake tasks' do
|
|
19
19
|
'template-ref' => 'heads/master-0-g7827fc2' }
|
20
20
|
expect(File).to receive(:read).with(any_args).once
|
21
21
|
expect(JSON).to receive(:parse).with(any_args).and_return(metadata)
|
22
|
-
expect(
|
23
|
-
expect(
|
24
|
-
expect(
|
22
|
+
expect($stdout).to receive(:puts).with('redhat-5-x86_64')
|
23
|
+
expect($stdout).to receive(:puts).with('ubuntu-1404-x86_64')
|
24
|
+
expect($stdout).to receive(:puts).with('ubuntu-1804-x86_64')
|
25
25
|
Rake::Task['litmus:metadata'].invoke
|
26
26
|
end
|
27
27
|
end
|
@@ -39,11 +39,11 @@ describe 'litmus rake tasks' do
|
|
39
39
|
expect_any_instance_of(PuppetLitmus::InventoryManipulation).to receive(:inventory_hash_from_inventory_file).and_return(inventory_hash)
|
40
40
|
expect(File).to receive(:directory?).with(target_dir).and_return(true)
|
41
41
|
expect_any_instance_of(Object).to receive(:build_modules_in_dir).with(target_dir).and_return([dummy_tar])
|
42
|
-
expect(
|
42
|
+
expect($stdout).to receive(:puts).with(start_with('Building all modules in'))
|
43
43
|
expect_any_instance_of(Object).to receive(:upload_file).once.and_return([])
|
44
|
-
expect(
|
44
|
+
expect($stdout).to receive(:puts).with(start_with('Installing \'spec/data/doot.tar.gz\''))
|
45
45
|
expect_any_instance_of(Object).to receive(:run_command).twice.and_return([])
|
46
|
-
expect(
|
46
|
+
expect($stdout).to receive(:puts).with(start_with('Installed \'spec/data/doot.tar.gz\''))
|
47
47
|
Rake::Task['litmus:install_modules_from_directory'].invoke('./spec/fixtures/modules')
|
48
48
|
end
|
49
49
|
end
|
@@ -68,7 +68,13 @@ describe 'litmus rake tasks' do
|
|
68
68
|
|
69
69
|
allow(File).to receive(:directory?).with(any_args).and_return(true)
|
70
70
|
allow_any_instance_of(BoltSpec::Run).to receive(:run_task).with(any_args).and_return(results)
|
71
|
-
|
71
|
+
allow_any_instance_of(PuppetLitmus::InventoryManipulation).to receive(:inventory_hash_from_inventory_file).with(any_args).and_return({})
|
72
|
+
allow_any_instance_of(PuppetLitmus::RakeHelper).to receive(:check_connectivity?).with(any_args).and_return(true)
|
73
|
+
|
74
|
+
expect($stdout).to receive(:puts).with('Provisioning centos:7 using docker provisioner.')
|
75
|
+
expect($stdout).to receive(:puts).with("Successfully provisioned centos:7 using docker\n")
|
76
|
+
expect($stdout).to receive(:puts).with('localhost:2222, centos:7')
|
77
|
+
|
72
78
|
Rake::Task['litmus:provision'].invoke('docker', 'centos:7')
|
73
79
|
end
|
74
80
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puppet_litmus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.22.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet, Inc.
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bolt
|
@@ -79,7 +79,7 @@ dependencies:
|
|
79
79
|
version: '1.34'
|
80
80
|
- - "<"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: 3.0.0
|
83
83
|
type: :runtime
|
84
84
|
prerelease: false
|
85
85
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -89,23 +89,23 @@ dependencies:
|
|
89
89
|
version: '1.34'
|
90
90
|
- - "<"
|
91
91
|
- !ruby/object:Gem::Version
|
92
|
-
version:
|
92
|
+
version: 3.0.0
|
93
93
|
- !ruby/object:Gem::Dependency
|
94
|
-
name:
|
94
|
+
name: retryable
|
95
95
|
requirement: !ruby/object:Gem::Requirement
|
96
96
|
requirements:
|
97
|
-
- - "
|
97
|
+
- - "~>"
|
98
98
|
- !ruby/object:Gem::Version
|
99
|
-
version: '0'
|
99
|
+
version: '3.0'
|
100
100
|
type: :runtime
|
101
101
|
prerelease: false
|
102
102
|
version_requirements: !ruby/object:Gem::Requirement
|
103
103
|
requirements:
|
104
|
-
- - "
|
104
|
+
- - "~>"
|
105
105
|
- !ruby/object:Gem::Version
|
106
|
-
version: '0'
|
106
|
+
version: '3.0'
|
107
107
|
- !ruby/object:Gem::Dependency
|
108
|
-
name:
|
108
|
+
name: parallel
|
109
109
|
requirement: !ruby/object:Gem::Requirement
|
110
110
|
requirements:
|
111
111
|
- - ">="
|
@@ -119,7 +119,7 @@ dependencies:
|
|
119
119
|
- !ruby/object:Gem::Version
|
120
120
|
version: '0'
|
121
121
|
- !ruby/object:Gem::Dependency
|
122
|
-
name:
|
122
|
+
name: rspec
|
123
123
|
requirement: !ruby/object:Gem::Requirement
|
124
124
|
requirements:
|
125
125
|
- - ">="
|
@@ -133,7 +133,7 @@ dependencies:
|
|
133
133
|
- !ruby/object:Gem::Version
|
134
134
|
version: '0'
|
135
135
|
- !ruby/object:Gem::Dependency
|
136
|
-
name:
|
136
|
+
name: honeycomb-beeline
|
137
137
|
requirement: !ruby/object:Gem::Requirement
|
138
138
|
requirements:
|
139
139
|
- - ">="
|
@@ -147,55 +147,31 @@ dependencies:
|
|
147
147
|
- !ruby/object:Gem::Version
|
148
148
|
version: '0'
|
149
149
|
- !ruby/object:Gem::Dependency
|
150
|
-
name:
|
151
|
-
requirement: !ruby/object:Gem::Requirement
|
152
|
-
requirements:
|
153
|
-
- - ">="
|
154
|
-
- !ruby/object:Gem::Version
|
155
|
-
version: '1.2'
|
156
|
-
- - "<"
|
157
|
-
- !ruby/object:Gem::Version
|
158
|
-
version: '2.0'
|
159
|
-
type: :runtime
|
160
|
-
prerelease: false
|
161
|
-
version_requirements: !ruby/object:Gem::Requirement
|
162
|
-
requirements:
|
163
|
-
- - ">="
|
164
|
-
- !ruby/object:Gem::Version
|
165
|
-
version: '1.2'
|
166
|
-
- - "<"
|
167
|
-
- !ruby/object:Gem::Version
|
168
|
-
version: '2.0'
|
169
|
-
- !ruby/object:Gem::Dependency
|
170
|
-
name: bcrypt_pbkdf
|
150
|
+
name: rspec_honeycomb_formatter
|
171
151
|
requirement: !ruby/object:Gem::Requirement
|
172
152
|
requirements:
|
173
153
|
- - ">="
|
174
154
|
- !ruby/object:Gem::Version
|
175
|
-
version: '
|
176
|
-
- - "<"
|
177
|
-
- !ruby/object:Gem::Version
|
178
|
-
version: '2.0'
|
155
|
+
version: '0'
|
179
156
|
type: :runtime
|
180
157
|
prerelease: false
|
181
158
|
version_requirements: !ruby/object:Gem::Requirement
|
182
159
|
requirements:
|
183
160
|
- - ">="
|
184
161
|
- !ruby/object:Gem::Version
|
185
|
-
version: '
|
186
|
-
- - "<"
|
187
|
-
- !ruby/object:Gem::Version
|
188
|
-
version: '2.0'
|
162
|
+
version: '0'
|
189
163
|
description: " Providing a simple command line tool for puppet content creators,
|
190
164
|
to enable simple and complex test deployments.\n"
|
191
165
|
email:
|
192
166
|
- info@puppet.com
|
193
|
-
executables:
|
167
|
+
executables:
|
168
|
+
- matrix_from_metadata
|
194
169
|
extensions: []
|
195
170
|
extra_rdoc_files: []
|
196
171
|
files:
|
197
172
|
- LICENSE
|
198
173
|
- README.md
|
174
|
+
- exe/matrix_from_metadata
|
199
175
|
- lib/puppet_litmus.rb
|
200
176
|
- lib/puppet_litmus/inventory_manipulation.rb
|
201
177
|
- lib/puppet_litmus/puppet_helpers.rb
|
@@ -209,10 +185,10 @@ files:
|
|
209
185
|
- spec/data/jim.yaml
|
210
186
|
- spec/lib/puppet_litmus/inventory_manipulation_spec.rb
|
211
187
|
- spec/lib/puppet_litmus/puppet_helpers_spec.rb
|
188
|
+
- spec/lib/puppet_litmus/puppet_litmus_version_spec.rb
|
212
189
|
- spec/lib/puppet_litmus/rake_helper_spec.rb
|
213
190
|
- spec/lib/puppet_litmus/rake_tasks_spec.rb
|
214
191
|
- spec/lib/puppet_litmus/util_spec.rb
|
215
|
-
- spec/lib/puppet_litmus/version_spec.rb
|
216
192
|
- spec/spec_helper.rb
|
217
193
|
homepage: https://github.com/puppetlabs/puppet_litmus
|
218
194
|
licenses:
|
@@ -226,26 +202,26 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
226
202
|
requirements:
|
227
203
|
- - ">="
|
228
204
|
- !ruby/object:Gem::Version
|
229
|
-
version:
|
205
|
+
version: 2.5.0
|
230
206
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
231
207
|
requirements:
|
232
208
|
- - ">="
|
233
209
|
- !ruby/object:Gem::Version
|
234
210
|
version: '0'
|
235
211
|
requirements: []
|
236
|
-
rubygems_version: 3.
|
212
|
+
rubygems_version: 3.0.3
|
237
213
|
signing_key:
|
238
214
|
specification_version: 4
|
239
215
|
summary: Providing a simple command line tool for puppet content creators, to enable
|
240
216
|
simple and complex test deployments.
|
241
217
|
test_files:
|
242
|
-
- spec/
|
243
|
-
- spec/
|
244
|
-
- spec/
|
218
|
+
- spec/spec_helper.rb
|
219
|
+
- spec/lib/puppet_litmus/rake_tasks_spec.rb
|
220
|
+
- spec/lib/puppet_litmus/puppet_litmus_version_spec.rb
|
245
221
|
- spec/lib/puppet_litmus/util_spec.rb
|
246
|
-
- spec/lib/puppet_litmus/version_spec.rb
|
247
222
|
- spec/lib/puppet_litmus/inventory_manipulation_spec.rb
|
248
|
-
- spec/lib/puppet_litmus/puppet_helpers_spec.rb
|
249
223
|
- spec/lib/puppet_litmus/rake_helper_spec.rb
|
250
|
-
- spec/lib/puppet_litmus/
|
251
|
-
- spec/
|
224
|
+
- spec/lib/puppet_litmus/puppet_helpers_spec.rb
|
225
|
+
- spec/data/doot.tar.gz
|
226
|
+
- spec/data/jim.yaml
|
227
|
+
- spec/data/inventory.yaml
|