puppet_litmus 0.18.4 → 0.23.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 +98 -0
- data/lib/puppet_litmus/inventory_manipulation.rb +12 -12
- data/lib/puppet_litmus/puppet_helpers.rb +59 -12
- data/lib/puppet_litmus/rake_helper.rb +103 -22
- data/lib/puppet_litmus/rake_tasks.rb +96 -67
- data/lib/puppet_litmus/spec_helper_acceptance.rb +3 -0
- 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 +26 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c3c6a488e0f0aa8b61b2f1d6678fd54d093aa11693b8ab004f57205f72049c9
|
4
|
+
data.tar.gz: 65ce0b461f8c1b0683f9b608aa08ede12d44794d766d007da423472fbb856535
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2191d39436024913ac79e2e4e1ea16306f4c4ece91c6ff3782a0d67eeda849ccc1079ac8976ed1c7e1edb5d9cb5fd92b86ef1a2f9261cde1134710267e08f29c
|
7
|
+
data.tar.gz: 0661730c8fd0a5e4152840ff84edf917bd76d46691d580981d1d265d957457358aa4afa04ba6f573db9a97dfec2cb10fee74a5638249e4f31e40f7893b21e5f1
|
@@ -0,0 +1,98 @@
|
|
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-7' => 'rhel-7',
|
10
|
+
'RedHat-8' => 'rhel-8',
|
11
|
+
'SLES-12' => 'sles-12',
|
12
|
+
'SLES-15' => 'sles-15',
|
13
|
+
'Windows-2012 R2' => 'windows-2012-r2-core',
|
14
|
+
'Windows-2016' => 'windows-2016',
|
15
|
+
'Windows-2019' => 'windows-2019-core',
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
DOCKER_PLATFORMS = [
|
19
|
+
'CentOS-6',
|
20
|
+
'CentOS-7',
|
21
|
+
'CentOS-8',
|
22
|
+
'Debian-10',
|
23
|
+
'Debian-8',
|
24
|
+
'Debian-9',
|
25
|
+
'OracleLinux-6',
|
26
|
+
'OracleLinux-7',
|
27
|
+
'Scientific-6',
|
28
|
+
'Scientific-7',
|
29
|
+
'Ubuntu-14.04',
|
30
|
+
'Ubuntu-16.04',
|
31
|
+
'Ubuntu-18.04',
|
32
|
+
'Ubuntu-20.04',
|
33
|
+
].freeze
|
34
|
+
|
35
|
+
# This table uses the latest version in each collection for accurate
|
36
|
+
# comparison when evaluating puppet requirements from the metadata
|
37
|
+
COLLECTION_TABLE = {
|
38
|
+
'5.5.22' => 'puppet5',
|
39
|
+
'6.19.1' => 'puppet6-nightly',
|
40
|
+
'7.0.0' => 'puppet7-nightly',
|
41
|
+
}.freeze
|
42
|
+
|
43
|
+
matrix = {
|
44
|
+
platform: [],
|
45
|
+
collection: [],
|
46
|
+
}
|
47
|
+
|
48
|
+
metadata = JSON.parse(File.read('metadata.json'))
|
49
|
+
# Set platforms based on declared operating system support
|
50
|
+
metadata['operatingsystem_support'].sort_by { |a| a['operatingsystem'] }.each do |sup|
|
51
|
+
os = sup['operatingsystem']
|
52
|
+
sup['operatingsystemrelease'].sort_by { |a| a.to_i }.each do |ver|
|
53
|
+
image_key = "#{os}-#{ver}"
|
54
|
+
if IMAGE_TABLE.key? image_key
|
55
|
+
matrix[:platform] << IMAGE_TABLE[image_key]
|
56
|
+
elsif DOCKER_PLATFORMS.include? image_key
|
57
|
+
puts "Expecting #{image_key} test using docker on travis"
|
58
|
+
else
|
59
|
+
puts "::warning::Cannot find image for #{image_key}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Set collections based on puppet version requirements
|
65
|
+
if metadata.key?('requirements') && metadata['requirements'].length.positive?
|
66
|
+
metadata['requirements'].each do |req|
|
67
|
+
next unless req.key?('name') && req.key?('version_requirement') && req['name'] == 'puppet'
|
68
|
+
|
69
|
+
ver_regexp = %r{^([>=<]{1,2})\s*([\d.]+)\s+([>=<]{1,2})\s*([\d.]+)$}
|
70
|
+
match = ver_regexp.match(req['version_requirement'])
|
71
|
+
if match.nil?
|
72
|
+
puts "::warning::Didn't recognize version_requirement '#{req['version_requirement']}'"
|
73
|
+
break
|
74
|
+
end
|
75
|
+
|
76
|
+
cmp_one, ver_one, cmp_two, ver_two = match.captures
|
77
|
+
reqs = ["#{cmp_one} #{ver_one}", "#{cmp_two} #{ver_two}"]
|
78
|
+
|
79
|
+
COLLECTION_TABLE.each do |key, val|
|
80
|
+
if Gem::Requirement.create(reqs).satisfied_by?(Gem::Version.new(key))
|
81
|
+
matrix[:collection] << val
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Set to defaults (all collections) if no matches are found
|
88
|
+
if matrix[:collection].empty?
|
89
|
+
matrix[:collection] = COLLECTION_TABLE.values
|
90
|
+
end
|
91
|
+
|
92
|
+
# Just to make sure there aren't any duplicates
|
93
|
+
matrix[:platform] = matrix[:platform].uniq.sort
|
94
|
+
matrix[:collection] = matrix[:collection].uniq.sort
|
95
|
+
|
96
|
+
puts "::set-output name=matrix::#{JSON.generate(matrix)}"
|
97
|
+
|
98
|
+
puts "Created matrix with #{matrix[:platform].length * matrix[:collection].length} cells."
|
@@ -17,10 +17,7 @@ module PuppetLitmus::InventoryManipulation
|
|
17
17
|
end
|
18
18
|
raise "There is no inventory file at '#{inventory_full_path}'." unless File.exist?(inventory_full_path)
|
19
19
|
|
20
|
-
|
21
|
-
raise "Inventory file is incompatible (version 2 and up). Try the 'bolt project migrate' command." if inventory_hash.dig('version').nil? || (inventory_hash['version'] < 2)
|
22
|
-
|
23
|
-
inventory_hash
|
20
|
+
YAML.load_file(inventory_full_path)
|
24
21
|
end
|
25
22
|
|
26
23
|
# Provide a default hash for executing against localhost
|
@@ -28,7 +25,6 @@ module PuppetLitmus::InventoryManipulation
|
|
28
25
|
# @return [Hash] inventory.yaml hash containing only an entry for localhost
|
29
26
|
def localhost_inventory_hash
|
30
27
|
{
|
31
|
-
'version' => 2,
|
32
28
|
'groups' => [
|
33
29
|
{
|
34
30
|
'name' => 'local',
|
@@ -50,12 +46,11 @@ module PuppetLitmus::InventoryManipulation
|
|
50
46
|
# @param targets [Array]
|
51
47
|
# @return [Array] array of targets.
|
52
48
|
def find_targets(inventory_hash, targets)
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
targets
|
49
|
+
if targets.nil?
|
50
|
+
inventory_hash.to_s.scan(%r{uri"=>"(\S*)"}).flatten
|
51
|
+
else
|
52
|
+
[targets]
|
53
|
+
end
|
59
54
|
end
|
60
55
|
|
61
56
|
# Determines if a node_name exists in a group in the inventory_hash.
|
@@ -263,7 +258,12 @@ module PuppetLitmus::InventoryManipulation
|
|
263
258
|
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
264
259
|
# @param node_name [String] node of nodes to limit the search for the node_name in
|
265
260
|
def add_platform_field(inventory_hash, node_name)
|
266
|
-
facts =
|
261
|
+
facts = begin
|
262
|
+
facts_from_node(inventory_hash, node_name)
|
263
|
+
rescue StandardError => e
|
264
|
+
warn e
|
265
|
+
{}
|
266
|
+
end
|
267
267
|
Honeycomb.current_span.add_field('litmus.platform', facts&.dig('platform'))
|
268
268
|
end
|
269
269
|
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,22 @@ module PuppetLitmus::PuppetHelpers
|
|
83
83
|
command_to_run += ' --noop' if !opts[:noop].nil? && (opts[:noop] == true)
|
84
84
|
command_to_run += ' --detailed-exitcodes' if use_detailed_exit_codes == true
|
85
85
|
|
86
|
-
span.add_field('litmus.command_to_run', command_to_run)
|
87
86
|
span.add_field('litmus.target_node_name', target_node_name)
|
88
|
-
bolt_result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
|
89
|
-
span.add_field('litmus.bolt_result', bolt_result)
|
90
87
|
|
88
|
+
if os[:family] == 'windows'
|
89
|
+
# IAC-1365 - Workaround for BOLT-1535 and bolt issue #1650
|
90
|
+
command_to_run = "try { #{command_to_run}; exit $LASTEXITCODE } catch { write-error $_ ; exit 1 }"
|
91
|
+
span.add_field('litmus.command_to_run', command_to_run)
|
92
|
+
bolt_result = Tempfile.open(['temp', '.ps1']) do |script|
|
93
|
+
script.write(command_to_run)
|
94
|
+
script.close
|
95
|
+
run_script(script.path, target_node_name, [], options: {}, config: nil, inventory: inventory_hash)
|
96
|
+
end
|
97
|
+
else
|
98
|
+
span.add_field('litmus.command_to_run', command_to_run)
|
99
|
+
bolt_result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
|
100
|
+
end
|
101
|
+
span.add_field('litmus.bolt_result', bolt_result)
|
91
102
|
result = OpenStruct.new(exit_code: bolt_result.first['value']['exit_code'],
|
92
103
|
stdout: bolt_result.first['value']['stdout'],
|
93
104
|
stderr: bolt_result.first['value']['stderr'])
|
@@ -117,7 +128,7 @@ module PuppetLitmus::PuppetHelpers
|
|
117
128
|
# @return [String] The path to the location of the manifest.
|
118
129
|
def create_manifest_file(manifest)
|
119
130
|
Honeycomb.start_span(name: 'litmus.create_manifest_file') do |span|
|
120
|
-
ENV['
|
131
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
121
132
|
span.add_field('litmus.manifest', manifest)
|
122
133
|
|
123
134
|
require 'tmpdir'
|
@@ -147,6 +158,42 @@ module PuppetLitmus::PuppetHelpers
|
|
147
158
|
end
|
148
159
|
end
|
149
160
|
|
161
|
+
# Writes a string variable to a file on a target node at a specified path.
|
162
|
+
#
|
163
|
+
# @param content [String] String data to write to the file.
|
164
|
+
# @param destination [String] The path on the target node to write the file.
|
165
|
+
# @return [Bool] Success. The file was succesfully writtne on the target.
|
166
|
+
def write_file(content, destination)
|
167
|
+
Honeycomb.start_span(name: 'litmus.write_file') do |span|
|
168
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
169
|
+
span.add_field('litmus.destination', destination)
|
170
|
+
|
171
|
+
require 'tmpdir'
|
172
|
+
target_node_name = ENV['TARGET_HOST']
|
173
|
+
|
174
|
+
Tempfile.create('litmus') do |tmp_file|
|
175
|
+
tmp_file.write(content)
|
176
|
+
tmp_file.flush
|
177
|
+
if target_node_name.nil? || target_node_name == 'localhost'
|
178
|
+
require 'fileutils'
|
179
|
+
# no need to transfer
|
180
|
+
FileUtils.cp(tmp_file.path, destination)
|
181
|
+
else
|
182
|
+
# transfer to TARGET_HOST
|
183
|
+
inventory_hash = inventory_hash_from_inventory_file
|
184
|
+
span.add_field('litmus.node_name', target_node_name)
|
185
|
+
add_platform_field(inventory_hash, target_node_name)
|
186
|
+
|
187
|
+
bolt_result = upload_file(tmp_file.path, destination, target_node_name, options: {}, config: nil, inventory: inventory_hash)
|
188
|
+
span.add_field('litmus.bolt_result.file_upload', bolt_result)
|
189
|
+
raise bolt_result.first['value'].to_s unless bolt_result.first['status'] == 'success'
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
true
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
150
197
|
# Runs a command against the target system
|
151
198
|
#
|
152
199
|
# @param command_to_run [String] The command to execute.
|
@@ -155,7 +202,7 @@ module PuppetLitmus::PuppetHelpers
|
|
155
202
|
# @return [Object] A result object from the command.
|
156
203
|
def run_shell(command_to_run, opts = {})
|
157
204
|
Honeycomb.start_span(name: 'litmus.run_shell') do |span|
|
158
|
-
ENV['
|
205
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
159
206
|
span.add_field('litmus.command_to_run', command_to_run)
|
160
207
|
span.add_field('litmus.opts', opts)
|
161
208
|
|
@@ -192,7 +239,7 @@ module PuppetLitmus::PuppetHelpers
|
|
192
239
|
# @return [Object] A result object from the command.
|
193
240
|
def bolt_upload_file(source, destination, opts = {}, options = {})
|
194
241
|
Honeycomb.start_span(name: 'litmus.bolt_upload_file') do |span|
|
195
|
-
ENV['
|
242
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
196
243
|
span.add_field('litmus.source', source)
|
197
244
|
span.add_field('litmus.destination', destination)
|
198
245
|
span.add_field('litmus.opts', opts)
|
@@ -244,7 +291,7 @@ module PuppetLitmus::PuppetHelpers
|
|
244
291
|
# @return [Object] A result object from the task.The values available are stdout, stderr and result.
|
245
292
|
def run_bolt_task(task_name, params = {}, opts = {})
|
246
293
|
Honeycomb.start_span(name: 'litmus.run_task') do |span|
|
247
|
-
ENV['
|
294
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
248
295
|
span.add_field('litmus.task_name', task_name)
|
249
296
|
span.add_field('litmus.params', params)
|
250
297
|
span.add_field('litmus.opts', opts)
|
@@ -312,7 +359,7 @@ module PuppetLitmus::PuppetHelpers
|
|
312
359
|
# @return [Object] A result object from the script run.
|
313
360
|
def bolt_run_script(script, opts = {}, arguments: [])
|
314
361
|
Honeycomb.start_span(name: 'litmus.bolt_run_script') do |span|
|
315
|
-
ENV['
|
362
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
316
363
|
span.add_field('litmus.script', script)
|
317
364
|
span.add_field('litmus.opts', opts)
|
318
365
|
span.add_field('litmus.arguments', arguments)
|
@@ -382,7 +429,7 @@ module PuppetLitmus::PuppetHelpers
|
|
382
429
|
|
383
430
|
# Return the stdout of the puppet run
|
384
431
|
def puppet_output(bolt_result)
|
385
|
-
bolt_result.dig(0, 'value', 'stderr').to_s
|
432
|
+
bolt_result.dig(0, 'value', 'stderr').to_s + \
|
386
433
|
bolt_result.dig(0, 'value', 'stdout').to_s
|
387
434
|
end
|
388
435
|
|
@@ -9,8 +9,8 @@ Honeycomb.configure do |config|
|
|
9
9
|
config.client = Libhoney::NullClient.new
|
10
10
|
end
|
11
11
|
end
|
12
|
-
process_span = Honeycomb.start_span(name: "litmus: #{([$PROGRAM_NAME] + ($ARGV || [])).join(' ')}", serialized_trace: ENV['
|
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
16
|
Honeycomb.add_field_to_trace('litmus.version', PuppetLitmus::VERSION)
|
@@ -41,6 +41,13 @@ elsif ENV['GITHUB_ACTIONS'] == 'true'
|
|
41
41
|
Honeycomb.add_field_to_trace('ci.sha', ENV['GITHUB_SHA'])
|
42
42
|
end
|
43
43
|
at_exit do
|
44
|
+
if $ERROR_INFO.is_a?(SystemExit)
|
45
|
+
process_span.add_field('process.exit_code', $ERROR_INFO.status)
|
46
|
+
elsif $ERROR_INFO
|
47
|
+
process_span.add_field('process.exit_code', $ERROR_INFO.class.name)
|
48
|
+
else
|
49
|
+
process_span.add_field('process.exit_code', 'unknown')
|
50
|
+
end
|
44
51
|
process_span.send
|
45
52
|
end
|
46
53
|
|
@@ -48,7 +55,7 @@ end
|
|
48
55
|
module PuppetLitmus::RakeHelper
|
49
56
|
# DEFAULT_CONFIG_DATA should be frozen for our safety, but it needs to work around https://github.com/puppetlabs/bolt/pull/1696
|
50
57
|
DEFAULT_CONFIG_DATA ||= { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') } # .freeze # rubocop:disable Style/MutableConstant
|
51
|
-
SUPPORTED_PROVISIONERS ||= %w[abs docker docker_exp vagrant vmpooler].freeze
|
58
|
+
SUPPORTED_PROVISIONERS ||= %w[abs docker docker_exp provision_service vagrant vmpooler].freeze
|
52
59
|
|
53
60
|
# Gets a string representing the operating system and version.
|
54
61
|
#
|
@@ -96,7 +103,7 @@ module PuppetLitmus::RakeHelper
|
|
96
103
|
# @return [Object] the standard out stream.
|
97
104
|
def run_local_command(command)
|
98
105
|
Honeycomb.start_span(name: 'litmus.run_local_command') do |span|
|
99
|
-
ENV['
|
106
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
100
107
|
span.add_field('litmus.command', command)
|
101
108
|
|
102
109
|
require 'open3'
|
@@ -119,13 +126,18 @@ module PuppetLitmus::RakeHelper
|
|
119
126
|
|
120
127
|
Honeycomb.add_field_to_trace('litmus.provisioner', provisioner)
|
121
128
|
Honeycomb.start_span(name: 'litmus.provision') do |span|
|
122
|
-
ENV['
|
129
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
123
130
|
span.add_field('litmus.platform', platform)
|
124
|
-
|
131
|
+
|
132
|
+
task_name = provisioner_task(provisioner)
|
133
|
+
span.add_field('litmus.task_name', task_name)
|
134
|
+
span.add_field('litmus.params', params)
|
125
135
|
span.add_field('litmus.config', DEFAULT_CONFIG_DATA)
|
126
136
|
|
127
|
-
bolt_result = run_task(
|
137
|
+
bolt_result = run_task(task_name, 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil)
|
138
|
+
span.add_field('litmus.result', bolt_result)
|
128
139
|
span.add_field('litmus.node_name', bolt_result&.first&.dig('value', 'node_name'))
|
140
|
+
|
129
141
|
raise_bolt_errors(bolt_result, "provisioning of #{platform} failed.")
|
130
142
|
|
131
143
|
bolt_result
|
@@ -148,7 +160,7 @@ module PuppetLitmus::RakeHelper
|
|
148
160
|
|
149
161
|
def tear_down_nodes(targets, inventory_hash)
|
150
162
|
Honeycomb.start_span(name: 'litmus.tear_down_nodes') do |span|
|
151
|
-
ENV['
|
163
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
152
164
|
span.add_field('litmus.targets', targets)
|
153
165
|
|
154
166
|
include ::BoltSpec::Run
|
@@ -160,6 +172,18 @@ module PuppetLitmus::RakeHelper
|
|
160
172
|
next if node_name == 'litmus_localhost'
|
161
173
|
|
162
174
|
result = tear_down(node_name, inventory_hash)
|
175
|
+
# Some provisioners tear_down targets that were created as a batch job.
|
176
|
+
# These provisioners should return the list of additional targets
|
177
|
+
# removed so that we do not attempt to process them.
|
178
|
+
if result != [] && result[0]['value'].key?('removed')
|
179
|
+
removed_targets = result[0]['value']['removed']
|
180
|
+
result[0]['value'].delete('removed')
|
181
|
+
removed_targets.each do |removed_target|
|
182
|
+
targets.delete(removed_target)
|
183
|
+
results[removed_target] = result
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
163
187
|
results[node_name] = result unless result == []
|
164
188
|
end
|
165
189
|
results
|
@@ -168,7 +192,7 @@ module PuppetLitmus::RakeHelper
|
|
168
192
|
|
169
193
|
def tear_down(node_name, inventory_hash)
|
170
194
|
Honeycomb.start_span(name: 'litmus.tear_down') do |span|
|
171
|
-
ENV['
|
195
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
172
196
|
# how do we know what provisioner to use
|
173
197
|
|
174
198
|
span.add_field('litmus.node_name', node_name)
|
@@ -184,7 +208,7 @@ module PuppetLitmus::RakeHelper
|
|
184
208
|
|
185
209
|
def install_agent(collection, targets, inventory_hash)
|
186
210
|
Honeycomb.start_span(name: 'litmus.install_agent') do |span|
|
187
|
-
ENV['
|
211
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
188
212
|
span.add_field('litmus.collection', collection)
|
189
213
|
span.add_field('litmus.targets', targets)
|
190
214
|
|
@@ -192,7 +216,6 @@ module PuppetLitmus::RakeHelper
|
|
192
216
|
params = if collection.nil?
|
193
217
|
{}
|
194
218
|
else
|
195
|
-
Honeycomb.current_span.add_field('litmus.collection', collection)
|
196
219
|
{ 'collection' => collection }
|
197
220
|
end
|
198
221
|
raise "puppet_agent was not found in #{DEFAULT_CONFIG_DATA['modulepath']}, please amend the .fixtures.yml file" \
|
@@ -208,9 +231,13 @@ module PuppetLitmus::RakeHelper
|
|
208
231
|
def configure_path(inventory_hash)
|
209
232
|
results = []
|
210
233
|
# fix the path on ssh_nodes
|
211
|
-
unless inventory_hash['groups'].select { |group| group['name'] == 'ssh_nodes' }.size.zero?
|
212
|
-
results
|
213
|
-
|
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)
|
214
241
|
end
|
215
242
|
results
|
216
243
|
end
|
@@ -227,7 +254,7 @@ module PuppetLitmus::RakeHelper
|
|
227
254
|
module_dir ||= Dir.pwd
|
228
255
|
target_dir ||= File.join(source_dir, 'pkg')
|
229
256
|
|
230
|
-
puts "Building '#{module_dir}' into '#{target_dir}'
|
257
|
+
puts "Building '#{module_dir}' into '#{target_dir}'"
|
231
258
|
builder = Puppet::Modulebuilder::Builder.new(module_dir, target_dir, nil)
|
232
259
|
|
233
260
|
# Force the metadata to be read. Raises if metadata could not be found
|
@@ -269,10 +296,11 @@ module PuppetLitmus::RakeHelper
|
|
269
296
|
# @param target_node_name [String] the name of the target where the module should be installed
|
270
297
|
# @param module_tar [String] the filename of the module tarball to upload
|
271
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.
|
272
300
|
# @return a bolt result
|
273
|
-
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
|
274
302
|
Honeycomb.start_span(name: 'install_module') do |span|
|
275
|
-
ENV['
|
303
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
276
304
|
span.add_field('litmus.target_node_name', target_node_name)
|
277
305
|
span.add_field('litmus.module_tar', module_tar)
|
278
306
|
|
@@ -290,6 +318,7 @@ module PuppetLitmus::RakeHelper
|
|
290
318
|
|
291
319
|
module_repository_opts = "--module_repository '#{module_repository}'" unless module_repository.nil?
|
292
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'
|
293
322
|
span.add_field('litmus.install_module_command', install_module_command)
|
294
323
|
|
295
324
|
bolt_result = run_command(install_module_command, target_nodes, config: nil, inventory: inventory_hash.clone)
|
@@ -327,24 +356,30 @@ module PuppetLitmus::RakeHelper
|
|
327
356
|
|
328
357
|
def check_connectivity?(inventory_hash, target_node_name)
|
329
358
|
Honeycomb.start_span(name: 'litmus.check_connectivity') do |span|
|
330
|
-
ENV['
|
359
|
+
ENV['HONEYCOMB_TRACE'] = span.to_trace_header
|
331
360
|
# if we're only checking connectivity for a single node
|
332
361
|
if target_node_name
|
333
|
-
span.add_field('litmus.
|
362
|
+
span.add_field('litmus.target_node_name', target_node_name)
|
334
363
|
add_platform_field(inventory_hash, target_node_name)
|
335
364
|
end
|
336
365
|
|
337
366
|
include ::BoltSpec::Run
|
338
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
|
+
|
339
371
|
results = run_command('cd .', target_nodes, config: nil, inventory: inventory_hash)
|
340
372
|
span.add_field('litmus.bolt_result', results)
|
341
373
|
failed = []
|
342
|
-
results.each do |result|
|
343
|
-
|
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'])
|
344
377
|
end
|
345
|
-
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' })
|
346
380
|
raise "Connectivity has failed on: #{failed}" unless failed.length.zero?
|
347
381
|
|
382
|
+
puts 'Connectivity check PASSED.'
|
348
383
|
true
|
349
384
|
end
|
350
385
|
end
|
@@ -391,4 +426,50 @@ module PuppetLitmus::RakeHelper
|
|
391
426
|
|
392
427
|
nil
|
393
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
|
394
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?
|
@@ -67,6 +68,8 @@ module PuppetLitmus
|
|
67
68
|
set :host, options[:host_name] || host
|
68
69
|
set :ssh_options, options
|
69
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.23.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-02-01 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,7 +89,21 @@ dependencies:
|
|
89
89
|
version: '1.34'
|
90
90
|
- - "<"
|
91
91
|
- !ruby/object:Gem::Version
|
92
|
-
version:
|
92
|
+
version: 3.0.0
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: retryable
|
95
|
+
requirement: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - "~>"
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '3.0'
|
100
|
+
type: :runtime
|
101
|
+
prerelease: false
|
102
|
+
version_requirements: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - "~>"
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '3.0'
|
93
107
|
- !ruby/object:Gem::Dependency
|
94
108
|
name: parallel
|
95
109
|
requirement: !ruby/object:Gem::Requirement
|
@@ -150,12 +164,14 @@ description: " Providing a simple command line tool for puppet content creato
|
|
150
164
|
to enable simple and complex test deployments.\n"
|
151
165
|
email:
|
152
166
|
- info@puppet.com
|
153
|
-
executables:
|
167
|
+
executables:
|
168
|
+
- matrix_from_metadata
|
154
169
|
extensions: []
|
155
170
|
extra_rdoc_files: []
|
156
171
|
files:
|
157
172
|
- LICENSE
|
158
173
|
- README.md
|
174
|
+
- exe/matrix_from_metadata
|
159
175
|
- lib/puppet_litmus.rb
|
160
176
|
- lib/puppet_litmus/inventory_manipulation.rb
|
161
177
|
- lib/puppet_litmus/puppet_helpers.rb
|
@@ -169,10 +185,10 @@ files:
|
|
169
185
|
- spec/data/jim.yaml
|
170
186
|
- spec/lib/puppet_litmus/inventory_manipulation_spec.rb
|
171
187
|
- spec/lib/puppet_litmus/puppet_helpers_spec.rb
|
188
|
+
- spec/lib/puppet_litmus/puppet_litmus_version_spec.rb
|
172
189
|
- spec/lib/puppet_litmus/rake_helper_spec.rb
|
173
190
|
- spec/lib/puppet_litmus/rake_tasks_spec.rb
|
174
191
|
- spec/lib/puppet_litmus/util_spec.rb
|
175
|
-
- spec/lib/puppet_litmus/version_spec.rb
|
176
192
|
- spec/spec_helper.rb
|
177
193
|
homepage: https://github.com/puppetlabs/puppet_litmus
|
178
194
|
licenses:
|
@@ -186,14 +202,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
186
202
|
requirements:
|
187
203
|
- - ">="
|
188
204
|
- !ruby/object:Gem::Version
|
189
|
-
version:
|
205
|
+
version: 2.5.0
|
190
206
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
191
207
|
requirements:
|
192
208
|
- - ">="
|
193
209
|
- !ruby/object:Gem::Version
|
194
210
|
version: '0'
|
195
211
|
requirements: []
|
196
|
-
rubygems_version: 3.
|
212
|
+
rubygems_version: 3.0.6
|
197
213
|
signing_key:
|
198
214
|
specification_version: 4
|
199
215
|
summary: Providing a simple command line tool for puppet content creators, to enable
|
@@ -201,7 +217,7 @@ summary: Providing a simple command line tool for puppet content creators, to en
|
|
201
217
|
test_files:
|
202
218
|
- spec/spec_helper.rb
|
203
219
|
- spec/lib/puppet_litmus/rake_tasks_spec.rb
|
204
|
-
- spec/lib/puppet_litmus/
|
220
|
+
- spec/lib/puppet_litmus/puppet_litmus_version_spec.rb
|
205
221
|
- spec/lib/puppet_litmus/util_spec.rb
|
206
222
|
- spec/lib/puppet_litmus/inventory_manipulation_spec.rb
|
207
223
|
- spec/lib/puppet_litmus/rake_helper_spec.rb
|