puppet_litmus 0.16.0 → 0.17.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/lib/puppet_litmus.rb +0 -30
- data/lib/puppet_litmus/inventory_manipulation.rb +1 -0
- data/lib/puppet_litmus/puppet_helpers.rb +196 -137
- data/lib/puppet_litmus/rake_helper.rb +142 -51
- data/lib/puppet_litmus/rake_tasks.rb +3 -1
- data/lib/puppet_litmus/version.rb +1 -1
- data/spec/lib/puppet_litmus/puppet_helpers_spec.rb +6 -11
- data/spec/lib/puppet_litmus/rake_helper_spec.rb +58 -11
- metadata +25 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c81ccd0186b54db2eb71534a0cc10e5191e1b6103201e8ef44231cc017cf5faa
|
4
|
+
data.tar.gz: dd027dff77884a4b09b66447c966c3636a2cf237e21c1b4f0ec1d28182188937
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38db1fa85c7ca655540aad80563bd095c8de46b492e618541a9dbd42b4a9305447ce2538c4ed30882f87582f4a21ebac7d0a1352288f3920d33b42af4d53fa4d
|
7
|
+
data.tar.gz: 849c989f388d9b1b4dde8a44a2788657c015c50bbfb8ff399255d64c3390557bca7e319939134a2bb2918f3ffc48b0c59a27c29ab7d0779d480c07f9a392a3dc
|
data/lib/puppet_litmus.rb
CHANGED
@@ -8,7 +8,6 @@ require 'puppet_litmus/inventory_manipulation'
|
|
8
8
|
require 'puppet_litmus/puppet_helpers'
|
9
9
|
require 'puppet_litmus/rake_helper'
|
10
10
|
require 'puppet_litmus/spec_helper_acceptance'
|
11
|
-
require 'honeycomb-beeline'
|
12
11
|
|
13
12
|
# Helper methods for testing puppet content
|
14
13
|
module PuppetLitmus
|
@@ -16,33 +15,4 @@ module PuppetLitmus
|
|
16
15
|
include PuppetLitmus::InventoryManipulation
|
17
16
|
include PuppetLitmus::PuppetHelpers
|
18
17
|
include PuppetLitmus::RakeHelper
|
19
|
-
Honeycomb.configure do |config|
|
20
|
-
end
|
21
|
-
process_span = Honeycomb.start_span(name: 'Litmus Testing')
|
22
|
-
if ENV['CI'] == 'true' && ENV['TRAVIS'] == 'true'
|
23
|
-
process_span.add_field('module_name', ENV['TRAVIS_REPO_SLUG'])
|
24
|
-
process_span.add_field('ci.provider', 'travis')
|
25
|
-
process_span.add_field('ci.build_id', ENV['TRAVIS_BUILD_ID'])
|
26
|
-
process_span.add_field('ci.build_url', ENV['TRAVIS_BUILD_WEB_URL'])
|
27
|
-
process_span.add_field('ci.job_url', ENV['TRAVIS_JOB_WEB_URL'])
|
28
|
-
process_span.add_field('ci.commit_message', ENV['TRAVIS_COMMIT_MESSAGE'])
|
29
|
-
process_span.add_field('ci.sha', ENV['TRAVIS_PULL_REQUEST_SHA'] || ENV['TRAVIS_COMMIT'])
|
30
|
-
elsif ENV['CI'] == 'True' && ENV['APPVEYOR'] == 'True'
|
31
|
-
process_span.add_field('module_name', ENV['APPVEYOR_PROJECT_SLUG'])
|
32
|
-
process_span.add_field('ci.provider', 'appveyor')
|
33
|
-
process_span.add_field('ci.build_id', ENV['APPVEYOR_BUILD_ID'])
|
34
|
-
process_span.add_field('ci.build_url', "https://ci.appveyor.com/project/#{ENV['APPVEYOR_REPO_NAME']}/builds/#{ENV['APPVEYOR_BUILD_ID']}")
|
35
|
-
process_span.add_field('ci.job_url', "https://ci.appveyor.com/project/#{ENV['APPVEYOR_REPO_NAME']}/build/job/#{ENV['APPVEYOR_JOB_ID']}")
|
36
|
-
process_span.add_field('ci.commit_message', ENV['APPVEYOR_REPO_COMMIT_MESSAGE'])
|
37
|
-
process_span.add_field('ci.sha', ENV['APPVEYOR_PULL_REQUEST_HEAD_COMMIT'] || ENV['APPVEYOR_REPO_COMMIT'])
|
38
|
-
elsif ENV['GITHUB_ACTIONS'] == 'true'
|
39
|
-
process_span.add_field('module_name', ENV['GITHUB_REPOSITORY'])
|
40
|
-
process_span.add_field('ci.provider', 'github')
|
41
|
-
process_span.add_field('ci.build_id', ENV['GITHUB_RUN_ID'])
|
42
|
-
process_span.add_field('ci.build_url', "https://github.com/#{ENV['GITHUB_REPOSITORY']}/actions/runs/#{ENV['GITHUB_RUN_ID']}")
|
43
|
-
process_span.add_field('ci.sha', ENV['GITHUB_SHA'])
|
44
|
-
end
|
45
|
-
at_exit do
|
46
|
-
process_span.send
|
47
|
-
end
|
48
18
|
end
|
@@ -8,87 +8,103 @@ module PuppetLitmus::PuppetHelpers
|
|
8
8
|
# @param manifest [String] puppet manifest code to be applied.
|
9
9
|
# @return [Boolean] The result of the 2 apply manifests.
|
10
10
|
def idempotent_apply(manifest)
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
Honeycomb.start_span(name: 'litmus.idempotent_apply') do |span|
|
12
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
|
13
|
+
manifest_file_location = create_manifest_file(manifest)
|
14
|
+
apply_manifest(nil, expect_failures: false, manifest_file_location: manifest_file_location)
|
15
|
+
apply_manifest(nil, catch_changes: true, manifest_file_location: manifest_file_location)
|
16
|
+
end
|
14
17
|
end
|
15
18
|
|
16
|
-
# rubocop:disable Layout/TrailingWhitespace
|
17
|
-
|
18
19
|
# Applies a manifest. returning the result of that apply. Mimics the apply_manifest from beaker
|
19
|
-
#
|
20
|
+
#
|
20
21
|
# When you set the environment variable RSPEC_DEBUG, the output of your
|
21
22
|
# puppet run will be displayed. If you have set the :debug flag, you will see the
|
22
23
|
# full debug log. If you have **not** set the :debug flag, it will display the regular
|
23
24
|
# output.
|
24
25
|
#
|
25
26
|
# @param manifest [String] puppet manifest code to be applied.
|
26
|
-
# @param opts [Hash] Alters the behaviour of the command. Valid options are:
|
27
|
-
# :catch_changes [Boolean] (false) We're after idempotency so allow exit code 0 only.
|
28
|
-
# :expect_changes [Boolean] (false) We're after changes specifically so allow exit code 2 only.
|
29
|
-
# :catch_failures [Boolean] (false) We're after only complete success so allow exit codes 0 and 2 only.
|
30
|
-
# :expect_failures [Boolean] (false) We're after failures specifically so allow exit codes 1, 4, and 6 only.
|
31
|
-
# :manifest_file_location [Path] The place on the target system.
|
27
|
+
# @param opts [Hash] Alters the behaviour of the command. Valid options are:
|
28
|
+
# :catch_changes [Boolean] (false) We're after idempotency so allow exit code 0 only.
|
29
|
+
# :expect_changes [Boolean] (false) We're after changes specifically so allow exit code 2 only.
|
30
|
+
# :catch_failures [Boolean] (false) We're after only complete success so allow exit codes 0 and 2 only.
|
31
|
+
# :expect_failures [Boolean] (false) We're after failures specifically so allow exit codes 1, 4, and 6 only.
|
32
|
+
# :manifest_file_location [Path] The place on the target system.
|
32
33
|
# :hiera_config [Path] The path to the hiera.yaml configuration on the runner.
|
33
|
-
# :prefix_command [String] prefixes the puppet apply command; eg "export LANGUAGE='ja'".
|
34
|
-
# :debug [Boolean] run puppet apply with the debug flag.
|
35
|
-
# :noop [Boolean] run puppet apply with the noop flag.
|
34
|
+
# :prefix_command [String] prefixes the puppet apply command; eg "export LANGUAGE='ja'".
|
35
|
+
# :debug [Boolean] run puppet apply with the debug flag.
|
36
|
+
# :noop [Boolean] run puppet apply with the noop flag.
|
36
37
|
# @yieldreturn [Block] this method will yield to a block of code passed by the caller; this can be used for additional validation, etc.
|
37
38
|
# @return [Object] A result object from the apply.
|
38
39
|
def apply_manifest(manifest, opts = {})
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
raise 'please specify only one of `catch_changes`, `expect_changes`, `catch_failures` or `expect_failures`' if
|
44
|
-
[opts[:catch_changes], opts[:expect_changes], opts[:catch_failures], opts[:expect_failures]].compact.length > 1
|
45
|
-
|
46
|
-
if opts[:catch_changes]
|
47
|
-
use_detailed_exit_codes = true
|
48
|
-
acceptable_exit_codes = [0]
|
49
|
-
elsif opts[:catch_failures]
|
50
|
-
use_detailed_exit_codes = true
|
51
|
-
acceptable_exit_codes = [0, 2]
|
52
|
-
elsif opts[:expect_failures]
|
53
|
-
use_detailed_exit_codes = true
|
54
|
-
acceptable_exit_codes = [1, 4, 6]
|
55
|
-
elsif opts[:expect_changes]
|
56
|
-
use_detailed_exit_codes = true
|
57
|
-
acceptable_exit_codes = [2]
|
58
|
-
else
|
59
|
-
use_detailed_exit_codes = false
|
60
|
-
acceptable_exit_codes = [0]
|
61
|
-
end
|
40
|
+
Honeycomb.start_span(name: 'litmus.apply_manifest') do |span|
|
41
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
|
42
|
+
span.add_field('litmus.manifest', manifest)
|
43
|
+
span.add_field('litmus.opts', opts)
|
62
44
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
45
|
+
target_node_name = targeting_localhost? ? 'litmus_localhost' : ENV['TARGET_HOST']
|
46
|
+
raise 'manifest and manifest_file_location in the opts hash are mutually exclusive arguments, pick one' if !manifest.nil? && !opts[:manifest_file_location].nil?
|
47
|
+
raise 'please pass a manifest or the manifest_file_location in the opts hash' if (manifest.nil? || manifest == '') && opts[:manifest_file_location].nil?
|
48
|
+
raise 'please specify only one of `catch_changes`, `expect_changes`, `catch_failures` or `expect_failures`' if
|
49
|
+
[opts[:catch_changes], opts[:expect_changes], opts[:catch_failures], opts[:expect_failures]].compact.length > 1
|
50
|
+
|
51
|
+
if opts[:catch_changes]
|
52
|
+
use_detailed_exit_codes = true
|
53
|
+
acceptable_exit_codes = [0]
|
54
|
+
elsif opts[:catch_failures]
|
55
|
+
use_detailed_exit_codes = true
|
56
|
+
acceptable_exit_codes = [0, 2]
|
57
|
+
elsif opts[:expect_failures]
|
58
|
+
use_detailed_exit_codes = true
|
59
|
+
acceptable_exit_codes = [1, 4, 6]
|
60
|
+
elsif opts[:expect_changes]
|
61
|
+
use_detailed_exit_codes = true
|
62
|
+
acceptable_exit_codes = [2]
|
63
|
+
else
|
64
|
+
use_detailed_exit_codes = false
|
65
|
+
acceptable_exit_codes = [0]
|
66
|
+
end
|
81
67
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
68
|
+
manifest_file_location = opts[:manifest_file_location] || create_manifest_file(manifest)
|
69
|
+
inventory_hash = File.exist?('inventory.yaml') ? inventory_hash_from_inventory_file : localhost_inventory_hash
|
70
|
+
raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
|
71
|
+
|
72
|
+
span.add_field('litmus.node_name', target_node_name)
|
73
|
+
span.add_field('litmus.platform', facts_from_node(inventory_hash, target_node_name)['platform'])
|
74
|
+
|
75
|
+
command_to_run = "#{opts[:prefix_command]} puppet apply #{manifest_file_location}"
|
76
|
+
command_to_run += " --modulepath #{Dir.pwd}/spec/fixtures/modules" if target_node_name == 'litmus_localhost'
|
77
|
+
command_to_run += " --hiera_config='#{opts[:hiera_config]}'" unless opts[:hiera_config].nil?
|
78
|
+
command_to_run += ' --debug' if !opts[:debug].nil? && (opts[:debug] == true)
|
79
|
+
command_to_run += ' --noop' if !opts[:noop].nil? && (opts[:noop] == true)
|
80
|
+
command_to_run += ' --detailed-exitcodes' if use_detailed_exit_codes == true
|
81
|
+
|
82
|
+
span.add_field('litmus.command_to_run', command_to_run)
|
83
|
+
span.add_field('litmus.target_node_name', target_node_name)
|
84
|
+
bolt_result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
|
85
|
+
span.add_field('litmus.bolt_result', bolt_result)
|
86
|
+
|
87
|
+
result = OpenStruct.new(exit_code: bolt_result.first['result']['exit_code'],
|
88
|
+
stdout: bolt_result.first['result']['stdout'],
|
89
|
+
stderr: bolt_result.first['result']['stderr'])
|
90
|
+
span.add_field('litmus.result', result.to_h)
|
91
|
+
|
92
|
+
status = result.exit_code
|
93
|
+
if opts[:catch_changes] && !acceptable_exit_codes.include?(status)
|
94
|
+
report_puppet_apply_change(command_to_run, bolt_result)
|
95
|
+
elsif !acceptable_exit_codes.include?(status)
|
96
|
+
report_puppet_apply_error(command_to_run, bolt_result, acceptable_exit_codes)
|
97
|
+
end
|
98
|
+
|
99
|
+
yield result if block_given?
|
100
|
+
|
101
|
+
if ENV['RSPEC_DEBUG']
|
102
|
+
puts "apply manifest succeded\n #{command_to_run}\n======\nwith status #{result.exit_code}"
|
103
|
+
puts result.stderr
|
104
|
+
puts result.stdout
|
105
|
+
end
|
106
|
+
result
|
90
107
|
end
|
91
|
-
result
|
92
108
|
end
|
93
109
|
|
94
110
|
# Creates a manifest file locally in a temp location, if its a remote target copy it to there.
|
@@ -96,23 +112,35 @@ module PuppetLitmus::PuppetHelpers
|
|
96
112
|
# @param manifest [String] puppet manifest code.
|
97
113
|
# @return [String] The path to the location of the manifest.
|
98
114
|
def create_manifest_file(manifest)
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
115
|
+
Honeycomb.start_span(name: 'litmus.create_manifest_file') do |span|
|
116
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
|
117
|
+
span.add_field('litmus.manifest', manifest)
|
118
|
+
|
119
|
+
require 'tmpdir'
|
120
|
+
target_node_name = ENV['TARGET_HOST']
|
121
|
+
tmp_filename = File.join(Dir.tmpdir, "manifest_#{Time.now.strftime('%Y%m%d')}_#{Process.pid}_#{rand(0x100000000).to_s(36)}.pp")
|
122
|
+
manifest_file = File.open(tmp_filename, 'w')
|
123
|
+
manifest_file.write(manifest)
|
124
|
+
manifest_file.close
|
125
|
+
if target_node_name.nil? || target_node_name == 'localhost'
|
126
|
+
# no need to transfer
|
127
|
+
manifest_file_location = manifest_file.path
|
128
|
+
else
|
129
|
+
# transfer to TARGET_HOST
|
130
|
+
inventory_hash = inventory_hash_from_inventory_file
|
131
|
+
span.add_field('litmus.node_name', target_node_name)
|
132
|
+
span.add_field('litmus.platform', facts_from_node(inventory_hash, target_node_name)['platform'])
|
133
|
+
|
134
|
+
manifest_file_location = "/tmp/#{File.basename(manifest_file)}"
|
135
|
+
bolt_result = upload_file(manifest_file.path, manifest_file_location, target_node_name, options: {}, config: nil, inventory: inventory_hash)
|
136
|
+
span.add_field('litmus.bolt_result', bolt_result)
|
137
|
+
raise bolt_result.first['result'].to_s unless bolt_result.first['status'] == 'success'
|
138
|
+
end
|
139
|
+
|
140
|
+
span.add_field('litmus.manifest_file_location', manifest_file_location)
|
141
|
+
|
142
|
+
manifest_file_location
|
114
143
|
end
|
115
|
-
manifest_file_location
|
116
144
|
end
|
117
145
|
|
118
146
|
# Runs a command against the target system
|
@@ -122,23 +150,31 @@ module PuppetLitmus::PuppetHelpers
|
|
122
150
|
# @yieldreturn [Block] this method will yield to a block of code passed by the caller; this can be used for additional validation, etc.
|
123
151
|
# @return [Object] A result object from the command.
|
124
152
|
def run_shell(command_to_run, opts = {})
|
125
|
-
Honeycomb.start_span(name: '
|
153
|
+
Honeycomb.start_span(name: 'litmus.run_shell') do |span|
|
154
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
|
155
|
+
span.add_field('litmus.command_to_run', command_to_run)
|
156
|
+
span.add_field('litmus.opts', opts)
|
157
|
+
|
126
158
|
target_node_name = targeting_localhost? ? 'litmus_localhost' : ENV['TARGET_HOST']
|
127
159
|
inventory_hash = File.exist?('inventory.yaml') ? inventory_hash_from_inventory_file : localhost_inventory_hash
|
128
160
|
raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
|
129
161
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
162
|
+
span.add_field('litmus.node_name', target_node_name)
|
163
|
+
span.add_field('litmus.platform', facts_from_node(inventory_hash, target_node_name)['platform'])
|
164
|
+
|
165
|
+
bolt_result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
|
166
|
+
span.add_field('litmus.bolt_result', bolt_result)
|
167
|
+
|
168
|
+
if bolt_result.first['result']['exit_code'] != 0 && opts[:expect_failures] != true
|
169
|
+
raise "shell failed\n`#{command_to_run}`\n======\n#{bolt_result}"
|
134
170
|
end
|
135
171
|
|
136
|
-
result = OpenStruct.new(exit_code:
|
137
|
-
exit_status:
|
138
|
-
stdout:
|
139
|
-
stderr:
|
172
|
+
result = OpenStruct.new(exit_code: bolt_result.first['result']['exit_code'],
|
173
|
+
exit_status: bolt_result.first['result']['exit_code'],
|
174
|
+
stdout: bolt_result.first['result']['stdout'],
|
175
|
+
stderr: bolt_result.first['result']['stderr'])
|
176
|
+
span.add_field('litmus.result', result.to_h)
|
140
177
|
yield result if block_given?
|
141
|
-
span.add_field('litmus_runshellsuccess', result)
|
142
178
|
result
|
143
179
|
end
|
144
180
|
end
|
@@ -151,52 +187,64 @@ module PuppetLitmus::PuppetHelpers
|
|
151
187
|
# @yieldreturn [Block] this method will yield to a block of code passed by the caller; this can be used for additional validation, etc.
|
152
188
|
# @return [Object] A result object from the command.
|
153
189
|
def bolt_upload_file(source, destination, opts = {}, options = {})
|
154
|
-
Honeycomb.start_span(name: '
|
190
|
+
Honeycomb.start_span(name: 'litmus.bolt_upload_file') do |span|
|
191
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
|
192
|
+
span.add_field('litmus.source', source)
|
193
|
+
span.add_field('litmus.destination', destination)
|
194
|
+
span.add_field('litmus.opts', opts)
|
195
|
+
span.add_field('litmus.options', options)
|
196
|
+
|
155
197
|
target_node_name = targeting_localhost? ? 'litmus_localhost' : ENV['TARGET_HOST']
|
156
198
|
inventory_hash = File.exist?('inventory.yaml') ? inventory_hash_from_inventory_file : localhost_inventory_hash
|
157
199
|
raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
|
158
200
|
|
159
|
-
|
201
|
+
span.add_field('litmus.node_name', target_node_name)
|
202
|
+
span.add_field('litmus.platform', facts_from_node(inventory_hash, target_node_name)['platform'])
|
203
|
+
|
204
|
+
bolt_result = upload_file(source, destination, target_node_name, options: options, config: nil, inventory: inventory_hash)
|
205
|
+
span.add_field('litmus.bolt_result', bolt_result)
|
160
206
|
|
161
207
|
result_obj = {
|
162
208
|
exit_code: 0,
|
163
|
-
stdout:
|
209
|
+
stdout: bolt_result.first['result']['_output'],
|
164
210
|
stderr: nil,
|
165
|
-
result:
|
211
|
+
result: bolt_result.first['result'],
|
166
212
|
}
|
167
213
|
|
168
|
-
if
|
214
|
+
if bolt_result.first['status'] != 'success'
|
169
215
|
if opts[:expect_failures] != true
|
170
|
-
span.add_field('litmus_uploadfilefailure',
|
171
|
-
raise "upload file failed\n======\n#{
|
216
|
+
span.add_field('litmus_uploadfilefailure', bolt_result)
|
217
|
+
raise "upload file failed\n======\n#{bolt_result}"
|
172
218
|
end
|
173
219
|
|
174
220
|
result_obj[:exit_code] = 255
|
175
|
-
result_obj[:stderr] =
|
221
|
+
result_obj[:stderr] = bolt_result.first['result']['_error']['msg']
|
176
222
|
end
|
177
223
|
|
178
224
|
result = OpenStruct.new(exit_code: result_obj[:exit_code],
|
179
225
|
stdout: result_obj[:stdout],
|
180
226
|
stderr: result_obj[:stderr])
|
227
|
+
span.add_field('litmus.result', result.to_h)
|
181
228
|
yield result if block_given?
|
182
|
-
span.add_field('litmus_uploadfilesucess', result)
|
183
229
|
result
|
184
230
|
end
|
185
231
|
end
|
186
232
|
|
187
|
-
# rubocop:disable Layout/TrailingWhitespace
|
188
|
-
|
189
233
|
# Runs a task against the target system.
|
190
234
|
#
|
191
235
|
# @param task_name [String] The name of the task to run.
|
192
236
|
# @param params [Hash] key : value pairs to be passed to the task.
|
193
|
-
# @param opts [Hash] Alters the behaviour of the command. Valid options are
|
194
|
-
# :expect_failures [Boolean] doesnt return an exit code of non-zero if the command failed.
|
237
|
+
# @param opts [Hash] Alters the behaviour of the command. Valid options are
|
238
|
+
# :expect_failures [Boolean] doesnt return an exit code of non-zero if the command failed.
|
195
239
|
# :inventory_file [String] path to the inventory file to use with the task.
|
196
240
|
# @return [Object] A result object from the task.The values available are stdout, stderr and result.
|
197
|
-
# rubocop:enable Layout/TrailingWhitespace
|
198
241
|
def run_bolt_task(task_name, params = {}, opts = {})
|
199
|
-
Honeycomb.start_span(name: '
|
242
|
+
Honeycomb.start_span(name: 'litmus.run_task') do |span|
|
243
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
|
244
|
+
span.add_field('litmus.task_name', task_name)
|
245
|
+
span.add_field('litmus.params', params)
|
246
|
+
span.add_field('litmus.opts', opts)
|
247
|
+
|
200
248
|
config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
|
201
249
|
target_node_name = targeting_localhost? ? 'litmus_localhost' : ENV['TARGET_HOST']
|
202
250
|
inventory_hash = if !opts[:inventory_file].nil? && File.exist?(opts[:inventory_file])
|
@@ -208,34 +256,37 @@ module PuppetLitmus::PuppetHelpers
|
|
208
256
|
end
|
209
257
|
raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
|
210
258
|
|
211
|
-
|
259
|
+
span.add_field('litmus.node_name', target_node_name)
|
260
|
+
span.add_field('litmus.platform', facts_from_node(inventory_hash, target_node_name)['platform'])
|
261
|
+
|
262
|
+
bolt_result = run_task(task_name, target_node_name, params, config: config_data, inventory: inventory_hash)
|
212
263
|
result_obj = {
|
213
264
|
exit_code: 0,
|
214
265
|
stdout: nil,
|
215
266
|
stderr: nil,
|
216
|
-
result:
|
267
|
+
result: bolt_result.first['result'],
|
217
268
|
}
|
218
269
|
|
219
|
-
if
|
270
|
+
if bolt_result.first['status'] == 'success'
|
220
271
|
# stdout returns unstructured data if structured data is not available
|
221
|
-
result_obj[:stdout] = if
|
222
|
-
|
272
|
+
result_obj[:stdout] = if bolt_result.first['result']['_output'].nil?
|
273
|
+
bolt_result.first['result'].to_s
|
223
274
|
else
|
224
|
-
|
275
|
+
bolt_result.first['result']['_output']
|
225
276
|
end
|
226
277
|
|
227
278
|
else
|
228
279
|
if opts[:expect_failures] != true
|
229
|
-
span.add_field('litmus_runtaskfailure',
|
230
|
-
raise "task failed\n`#{task_name}`\n======\n#{
|
280
|
+
span.add_field('litmus_runtaskfailure', bolt_result)
|
281
|
+
raise "task failed\n`#{task_name}`\n======\n#{bolt_result}"
|
231
282
|
end
|
232
283
|
|
233
|
-
result_obj[:exit_code] = if
|
284
|
+
result_obj[:exit_code] = if bolt_result.first['result']['_error']['details'].nil?
|
234
285
|
255
|
235
286
|
else
|
236
|
-
|
287
|
+
bolt_result.first['result']['_error']['details'].fetch('exitcode', 255)
|
237
288
|
end
|
238
|
-
result_obj[:stderr] =
|
289
|
+
result_obj[:stderr] = bolt_result.first['result']['_error']['msg']
|
239
290
|
end
|
240
291
|
|
241
292
|
result = OpenStruct.new(exit_code: result_obj[:exit_code],
|
@@ -243,7 +294,7 @@ module PuppetLitmus::PuppetHelpers
|
|
243
294
|
stderr: result_obj[:stderr],
|
244
295
|
result: result_obj[:result])
|
245
296
|
yield result if block_given?
|
246
|
-
span.add_field('
|
297
|
+
span.add_field('litmus.result', result.to_h)
|
247
298
|
result
|
248
299
|
end
|
249
300
|
end
|
@@ -256,23 +307,31 @@ module PuppetLitmus::PuppetHelpers
|
|
256
307
|
# @yieldreturn [Block] this method will yield to a block of code passed by the caller; this can be used for additional validation, etc.
|
257
308
|
# @return [Object] A result object from the script run.
|
258
309
|
def bolt_run_script(script, opts = {}, arguments: [])
|
259
|
-
Honeycomb.start_span(name: '
|
310
|
+
Honeycomb.start_span(name: 'litmus.bolt_run_script') do |span|
|
311
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
|
312
|
+
span.add_field('litmus.script', script)
|
313
|
+
span.add_field('litmus.opts', opts)
|
314
|
+
span.add_field('litmus.arguments', arguments)
|
315
|
+
|
260
316
|
target_node_name = targeting_localhost? ? 'litmus_localhost' : ENV['TARGET_HOST']
|
261
317
|
inventory_hash = File.exist?('inventory.yaml') ? inventory_hash_from_inventory_file : localhost_inventory_hash
|
262
318
|
raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
|
263
319
|
|
264
|
-
|
320
|
+
span.add_field('litmus.node_name', target_node_name)
|
321
|
+
span.add_field('litmus.platform', facts_from_node(inventory_hash, target_node_name)['platform'])
|
322
|
+
|
323
|
+
bolt_result = run_script(script, target_node_name, arguments, options: opts, config: nil, inventory: inventory_hash)
|
265
324
|
|
266
|
-
if
|
267
|
-
span.add_field('litmus_runscriptfailure',
|
268
|
-
raise "script run failed\n`#{script}`\n======\n#{
|
325
|
+
if bolt_result.first['result']['exit_code'] != 0 && opts[:expect_failures] != true
|
326
|
+
span.add_field('litmus_runscriptfailure', bolt_result)
|
327
|
+
raise "script run failed\n`#{script}`\n======\n#{bolt_result}"
|
269
328
|
end
|
270
329
|
|
271
|
-
result = OpenStruct.new(exit_code:
|
272
|
-
stdout:
|
273
|
-
stderr:
|
330
|
+
result = OpenStruct.new(exit_code: bolt_result.first['result']['exit_code'],
|
331
|
+
stdout: bolt_result.first['result']['stdout'],
|
332
|
+
stderr: bolt_result.first['result']['stderr'])
|
274
333
|
yield result if block_given?
|
275
|
-
span.add_field('
|
334
|
+
span.add_field('litmus.result', result.to_h)
|
276
335
|
result
|
277
336
|
end
|
278
337
|
end
|
@@ -289,14 +348,14 @@ module PuppetLitmus::PuppetHelpers
|
|
289
348
|
# Report an error in the puppet run
|
290
349
|
#
|
291
350
|
# @param command [String] The puppet command causing the error.
|
292
|
-
# @param
|
293
|
-
def report_puppet_apply_error(command,
|
351
|
+
# @param bolt_result [Array] The result object from bolt
|
352
|
+
def report_puppet_apply_error(command, bolt_result, acceptable_exit_codes)
|
294
353
|
puppet_apply_error = <<~ERROR
|
295
354
|
apply manifest failed
|
296
355
|
`#{command}`
|
297
|
-
with exit code #{
|
356
|
+
with exit code #{bolt_result.first['result']['exit_code']} (expected: #{acceptable_exit_codes})
|
298
357
|
====== Start output of failed Puppet apply ======
|
299
|
-
#{puppet_output(
|
358
|
+
#{puppet_output(bolt_result)}
|
300
359
|
====== End output of failed Puppet apply ======
|
301
360
|
ERROR
|
302
361
|
raise puppet_apply_error
|
@@ -305,22 +364,22 @@ module PuppetLitmus::PuppetHelpers
|
|
305
364
|
# Report an unexpected change in the puppet run
|
306
365
|
#
|
307
366
|
# @param command [String] The puppet command causing the error.
|
308
|
-
# @param
|
309
|
-
def report_puppet_apply_change(command,
|
367
|
+
# @param bolt_result [Array] The result object from bolt
|
368
|
+
def report_puppet_apply_change(command, bolt_result)
|
310
369
|
puppet_apply_changes = <<~ERROR
|
311
370
|
apply manifest expected no changes
|
312
371
|
`#{command}`
|
313
372
|
====== Start output of Puppet apply with unexpected changes ======
|
314
|
-
#{puppet_output(
|
373
|
+
#{puppet_output(bolt_result)}
|
315
374
|
====== End output of Puppet apply with unexpected changes ======
|
316
375
|
ERROR
|
317
376
|
raise puppet_apply_changes
|
318
377
|
end
|
319
378
|
|
320
379
|
# Return the stdout of the puppet run
|
321
|
-
def puppet_output(
|
322
|
-
|
323
|
-
|
380
|
+
def puppet_output(bolt_result)
|
381
|
+
bolt_result.dig(0, 'result', 'stderr').to_s << \
|
382
|
+
bolt_result.dig(0, 'result', 'stdout').to_s
|
324
383
|
end
|
325
384
|
|
326
385
|
# Checks a puppet return status and returns true if it both
|
@@ -2,10 +2,43 @@
|
|
2
2
|
|
3
3
|
module PuppetLitmus; end # rubocop:disable Style/Documentation
|
4
4
|
|
5
|
+
require 'honeycomb-beeline'
|
6
|
+
Honeycomb.configure do |config|
|
7
|
+
end
|
8
|
+
process_span = Honeycomb.start_span(name: 'Litmus Testing', serialized_trace: ENV['HTTP_X_HONEYCOMB_TRACE'])
|
9
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = process_span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
|
10
|
+
Honeycomb.add_field_to_trace('litmus.pid', Process.pid)
|
11
|
+
if ENV['CI'] == 'true' && ENV['TRAVIS'] == 'true'
|
12
|
+
Honeycomb.add_field_to_trace('module_name', ENV['TRAVIS_REPO_SLUG'])
|
13
|
+
Honeycomb.add_field_to_trace('ci.provider', 'travis')
|
14
|
+
Honeycomb.add_field_to_trace('ci.build_id', ENV['TRAVIS_BUILD_ID'])
|
15
|
+
Honeycomb.add_field_to_trace('ci.build_url', ENV['TRAVIS_BUILD_WEB_URL'])
|
16
|
+
Honeycomb.add_field_to_trace('ci.job_url', ENV['TRAVIS_JOB_WEB_URL'])
|
17
|
+
Honeycomb.add_field_to_trace('ci.commit_message', ENV['TRAVIS_COMMIT_MESSAGE'])
|
18
|
+
Honeycomb.add_field_to_trace('ci.sha', ENV['TRAVIS_PULL_REQUEST_SHA'] || ENV['TRAVIS_COMMIT'])
|
19
|
+
elsif ENV['CI'] == 'True' && ENV['APPVEYOR'] == 'True'
|
20
|
+
Honeycomb.add_field_to_trace('module_name', ENV['APPVEYOR_PROJECT_SLUG'])
|
21
|
+
Honeycomb.add_field_to_trace('ci.provider', 'appveyor')
|
22
|
+
Honeycomb.add_field_to_trace('ci.build_id', ENV['APPVEYOR_BUILD_ID'])
|
23
|
+
Honeycomb.add_field_to_trace('ci.build_url', "https://ci.appveyor.com/project/#{ENV['APPVEYOR_REPO_NAME']}/builds/#{ENV['APPVEYOR_BUILD_ID']}")
|
24
|
+
Honeycomb.add_field_to_trace('ci.job_url', "https://ci.appveyor.com/project/#{ENV['APPVEYOR_REPO_NAME']}/build/job/#{ENV['APPVEYOR_JOB_ID']}")
|
25
|
+
Honeycomb.add_field_to_trace('ci.commit_message', ENV['APPVEYOR_REPO_COMMIT_MESSAGE'])
|
26
|
+
Honeycomb.add_field_to_trace('ci.sha', ENV['APPVEYOR_PULL_REQUEST_HEAD_COMMIT'] || ENV['APPVEYOR_REPO_COMMIT'])
|
27
|
+
elsif ENV['GITHUB_ACTIONS'] == 'true'
|
28
|
+
Honeycomb.add_field_to_trace('module_name', ENV['GITHUB_REPOSITORY'])
|
29
|
+
Honeycomb.add_field_to_trace('ci.provider', 'github')
|
30
|
+
Honeycomb.add_field_to_trace('ci.build_id', ENV['GITHUB_RUN_ID'])
|
31
|
+
Honeycomb.add_field_to_trace('ci.build_url', "https://github.com/#{ENV['GITHUB_REPOSITORY']}/actions/runs/#{ENV['GITHUB_RUN_ID']}")
|
32
|
+
Honeycomb.add_field_to_trace('ci.sha', ENV['GITHUB_SHA'])
|
33
|
+
end
|
34
|
+
at_exit do
|
35
|
+
process_span.send
|
36
|
+
end
|
37
|
+
|
5
38
|
# helper methods for the litmus rake tasks
|
6
39
|
module PuppetLitmus::RakeHelper
|
7
40
|
DEFAULT_CONFIG_DATA ||= { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }.freeze
|
8
|
-
|
41
|
+
SUPPORTED_PROVISIONERS ||= %w[abs docker docker_exp vagrant vmpooler].freeze
|
9
42
|
|
10
43
|
# Gets a string representing the operating system and version.
|
11
44
|
#
|
@@ -52,12 +85,18 @@ module PuppetLitmus::RakeHelper
|
|
52
85
|
# @param command [String] command to execute.
|
53
86
|
# @return [Object] the standard out stream.
|
54
87
|
def run_local_command(command)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
88
|
+
Honeycomb.start_span(name: 'litmus.run_local_command') do |span|
|
89
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
|
90
|
+
span.add_field('litmus.command', command)
|
91
|
+
|
92
|
+
require 'open3'
|
93
|
+
stdout, stderr, status = Open3.capture3(command)
|
94
|
+
error_message = "Attempted to run\ncommand:'#{command}'\nstdout:#{stdout}\nstderr:#{stderr}"
|
59
95
|
|
60
|
-
|
96
|
+
raise error_message unless status.to_i.zero?
|
97
|
+
|
98
|
+
stdout
|
99
|
+
end
|
61
100
|
end
|
62
101
|
|
63
102
|
# Builds all the modules in a specified module
|
@@ -89,16 +128,22 @@ module PuppetLitmus::RakeHelper
|
|
89
128
|
raise "the provision module was not found in #{DEFAULT_CONFIG_DATA['modulepath']}, please amend the .fixtures.yml file" unless
|
90
129
|
File.directory?(File.join(DEFAULT_CONFIG_DATA['modulepath'], 'provision'))
|
91
130
|
|
92
|
-
|
93
|
-
|
94
|
-
|
131
|
+
params = { 'action' => 'provision', 'platform' => platform, 'inventory' => Dir.pwd }
|
132
|
+
params['vars'] = inventory_vars unless inventory_vars.nil?
|
133
|
+
|
134
|
+
Honeycomb.add_field_to_trace('litmus.provisioner', provisioner)
|
135
|
+
Honeycomb.start_span(name: 'litmus.provision') do |span|
|
136
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
|
137
|
+
span.add_field('litmus.platform', platform)
|
138
|
+
span.add_field('litmus.inventory', params['inventory'])
|
139
|
+
span.add_field('litmus.config', DEFAULT_CONFIG_DATA)
|
95
140
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
141
|
+
bolt_result = run_task(provisioner_task(provisioner), 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil)
|
142
|
+
|
143
|
+
span.add_field('litmus.node_name', bolt_result&.first&.dig('result', 'node_name'))
|
144
|
+
|
145
|
+
bolt_result
|
146
|
+
end
|
102
147
|
end
|
103
148
|
|
104
149
|
def provision_list(provision_hash, key)
|
@@ -107,6 +152,8 @@ module PuppetLitmus::RakeHelper
|
|
107
152
|
# Splat the params into environment variables to pass to the provision task but only in this runspace
|
108
153
|
provision_hash[key]['params']&.each { |k, value| ENV[k.upcase] = value.to_s }
|
109
154
|
results = []
|
155
|
+
|
156
|
+
Honeycomb.current_span.add_field('litmus.images', provision_hash[key]['images'])
|
110
157
|
provision_hash[key]['images'].each do |image|
|
111
158
|
results << provision(provisioner, image, inventory_vars)
|
112
159
|
end
|
@@ -114,42 +161,60 @@ module PuppetLitmus::RakeHelper
|
|
114
161
|
end
|
115
162
|
|
116
163
|
def tear_down_nodes(targets, inventory_hash)
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
164
|
+
Honeycomb.start_span(name: 'litmus.tear_down_nodes') do |span|
|
165
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
|
166
|
+
span.add_field('litmus.targets', targets)
|
167
|
+
|
168
|
+
require 'bolt_spec/run'
|
169
|
+
include BoltSpec::Run
|
170
|
+
config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
|
171
|
+
raise "the provision module was not found in #{config_data['modulepath']}, please amend the .fixtures.yml file" unless File.directory?(File.join(config_data['modulepath'], 'provision'))
|
121
172
|
|
122
|
-
|
123
|
-
|
124
|
-
|
173
|
+
results = {}
|
174
|
+
targets.each do |node_name|
|
175
|
+
next if node_name == 'litmus_localhost'
|
125
176
|
|
126
|
-
|
127
|
-
|
177
|
+
result = tear_down(node_name, inventory_hash)
|
178
|
+
results[node_name] = result unless result == []
|
179
|
+
end
|
180
|
+
results
|
128
181
|
end
|
129
|
-
results
|
130
182
|
end
|
131
183
|
|
132
184
|
def tear_down(node_name, inventory_hash)
|
133
|
-
|
134
|
-
|
135
|
-
|
185
|
+
Honeycomb.start_span(name: 'litmus.tear_down') do |span|
|
186
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
|
187
|
+
# how do we know what provisioner to use
|
188
|
+
node_facts = facts_from_node(inventory_hash, node_name)
|
136
189
|
|
137
|
-
|
138
|
-
|
190
|
+
span.add_field('litmus.node_name', node_name)
|
191
|
+
span.add_field('litmus.platform', node_facts['platform'])
|
192
|
+
|
193
|
+
params = { 'action' => 'tear_down', 'node_name' => node_name, 'inventory' => Dir.pwd }
|
194
|
+
run_task(provisioner_task(node_facts['provisioner']), 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil)
|
195
|
+
end
|
139
196
|
end
|
140
197
|
|
141
198
|
def install_agent(collection, targets, inventory_hash)
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
199
|
+
Honeycomb.start_span(name: 'litmus.install_agent') do |span|
|
200
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
|
201
|
+
span.add_field('litmus.collection', collection)
|
202
|
+
span.add_field('litmus.targets', targets)
|
203
|
+
|
204
|
+
require 'bolt_spec/run'
|
205
|
+
include BoltSpec::Run
|
206
|
+
params = if collection.nil?
|
207
|
+
{}
|
208
|
+
else
|
209
|
+
Honeycomb.current_span.add_field('litmus.collection', collection)
|
210
|
+
{ 'collection' => collection }
|
211
|
+
end
|
212
|
+
raise "puppet_agent was not found in #{DEFAULT_CONFIG_DATA['modulepath']}, please amend the .fixtures.yml file" \
|
213
|
+
unless File.directory?(File.join(DEFAULT_CONFIG_DATA['modulepath'], 'puppet_agent'))
|
214
|
+
|
215
|
+
# using boltspec, when the runner is called it changes the inventory_hash dropping the version field. The clone works around this
|
216
|
+
run_task('puppet_agent::install', targets, params, config: DEFAULT_CONFIG_DATA, inventory: inventory_hash.clone)
|
217
|
+
end
|
153
218
|
end
|
154
219
|
|
155
220
|
def configure_path(inventory_hash)
|
@@ -190,7 +255,13 @@ module PuppetLitmus::RakeHelper
|
|
190
255
|
end
|
191
256
|
run_local_command("bundle exec bolt file upload \"#{module_tar}\" /tmp/#{File.basename(module_tar)} --nodes #{target_string} --inventoryfile inventory.yaml")
|
192
257
|
install_module_command = "puppet module install /tmp/#{File.basename(module_tar)}"
|
193
|
-
|
258
|
+
Honeycomb.start_span(name: 'install_module') do |span|
|
259
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
|
260
|
+
span.add_field('litmus.install_module_command', install_module_command)
|
261
|
+
span.add_field('litmus.target_nodes', target_nodes)
|
262
|
+
|
263
|
+
run_command(install_module_command, target_nodes, config: nil, inventory: inventory_hash)
|
264
|
+
end
|
194
265
|
end
|
195
266
|
|
196
267
|
def metadata_module_name
|
@@ -213,16 +284,36 @@ module PuppetLitmus::RakeHelper
|
|
213
284
|
end
|
214
285
|
|
215
286
|
def check_connectivity?(inventory_hash, target_node_name)
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
287
|
+
Honeycomb.start_span(name: 'litmus.check_connectivity') do |span|
|
288
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
|
289
|
+
# if we're only checking connectivity for a single node
|
290
|
+
if target_node_name
|
291
|
+
span.add_field('litmus.node_name', target_node_name)
|
292
|
+
span.add_field('litmus.platform', facts_from_node(inventory_hash, target_node_name)['platform'])
|
293
|
+
end
|
294
|
+
|
295
|
+
require 'bolt_spec/run'
|
296
|
+
include BoltSpec::Run
|
297
|
+
target_nodes = find_targets(inventory_hash, target_node_name)
|
298
|
+
results = run_command('cd .', target_nodes, config: nil, inventory: inventory_hash)
|
299
|
+
span.add_field('litmus.bolt_result', results)
|
300
|
+
failed = []
|
301
|
+
results.each do |result|
|
302
|
+
failed.push(result['target']) if result['status'] == 'failure'
|
303
|
+
end
|
304
|
+
span.add_field('litmus.connectivity_failed', failed)
|
305
|
+
raise "Connectivity has failed on: #{failed}" unless failed.length.zero?
|
306
|
+
|
307
|
+
true
|
223
308
|
end
|
224
|
-
|
309
|
+
end
|
225
310
|
|
226
|
-
|
311
|
+
def provisioner_task(provisioner)
|
312
|
+
if SUPPORTED_PROVISIONERS.include?(provisioner)
|
313
|
+
"provision::#{provisioner}"
|
314
|
+
else
|
315
|
+
warn "WARNING: Unsuported provisioner '#{provisioner}', try #{SUPPORTED_PROVISIONERS.join('/')}"
|
316
|
+
provisioner.to_s
|
317
|
+
end
|
227
318
|
end
|
228
319
|
end
|
@@ -339,7 +339,7 @@ namespace :litmus do
|
|
339
339
|
payloads = []
|
340
340
|
# Generate list of targets to provision
|
341
341
|
targets.each do |target|
|
342
|
-
test = 'bundle exec rspec ./spec/acceptance --format progress'
|
342
|
+
test = 'bundle exec rspec ./spec/acceptance --format progress --require rspec_honeycomb_formatter --format RSpecHoneycombFormatter'
|
343
343
|
title = "#{target}, #{facts_from_node(inventory_hash, target)['platform']}"
|
344
344
|
options = {
|
345
345
|
env: {
|
@@ -366,6 +366,7 @@ namespace :litmus do
|
|
366
366
|
require 'parallel'
|
367
367
|
results = Parallel.map(payloads) do |title, test, options|
|
368
368
|
env = options[:env].nil? ? {} : options[:env]
|
369
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = Honecomb.current_span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
|
369
370
|
stdout, stderr, status = Open3.capture3(env, test)
|
370
371
|
["\n================\n#{title}\n", stdout, stderr, status]
|
371
372
|
end
|
@@ -384,6 +385,7 @@ namespace :litmus do
|
|
384
385
|
payloads.each do |title, test, options|
|
385
386
|
env = options[:env].nil? ? {} : options[:env]
|
386
387
|
spinners.register("[:spinner] #{title}") do |sp|
|
388
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = Honecomb.current_span.to_trace_header unless ENV['HTTP_X_HONEYCOMB_TRACE']
|
387
389
|
stdout, stderr, status = Open3.capture3(env, test)
|
388
390
|
if status.to_i.zero?
|
389
391
|
sp.success
|
@@ -3,6 +3,10 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
RSpec.describe PuppetLitmus::PuppetHelpers do
|
6
|
+
let(:inventory_hash) { { 'groups' => [{ 'name' => 'local', 'targets' => [{ 'uri' => 'some.host', 'config' => { 'transport' => 'local' }, 'facts' => facts_hash }] }] } }
|
7
|
+
let(:localhost_inventory_hash) { { 'groups' => [{ 'name' => 'local', 'targets' => [{ 'uri' => 'litmus_localhost', 'config' => { 'transport' => 'local' }, 'facts' => facts_hash }] }] } }
|
8
|
+
let(:facts_hash) { { 'provisioner' => 'docker', 'container_name' => 'litmusimage_debian_10-2222', 'platform' => 'litmusimage/debian:10' } }
|
9
|
+
|
6
10
|
context 'with idempotent_apply' do
|
7
11
|
let(:manifest) do
|
8
12
|
"include '::doot'"
|
@@ -19,7 +23,6 @@ RSpec.describe PuppetLitmus::PuppetHelpers do
|
|
19
23
|
describe '.apply_manifest' do
|
20
24
|
context 'when specifying a hiera config' do
|
21
25
|
let(:manifest) { "include '::doot'" }
|
22
|
-
let(:localhost_inventory_hash) { { 'groups' => [{ 'name' => 'local', 'nodes' => [{ 'name' => 'litmus_localhost', 'config' => { 'transport' => 'local' } }] }] } }
|
23
26
|
let(:result) { ['result' => { 'exit_code' => 0, 'stdout' => nil, 'stderr' => nil }] }
|
24
27
|
let(:command) { " puppet apply /bla.pp --modulepath #{Dir.pwd}/spec/fixtures/modules --hiera_config='/hiera.yaml'" }
|
25
28
|
|
@@ -35,7 +38,6 @@ RSpec.describe PuppetLitmus::PuppetHelpers do
|
|
35
38
|
|
36
39
|
context 'when using detailed-exitcodes' do
|
37
40
|
let(:manifest) { "include '::doot'" }
|
38
|
-
let(:localhost_inventory_hash) { { 'groups' => [{ 'name' => 'local', 'nodes' => [{ 'name' => 'litmus_localhost', 'config' => { 'transport' => 'local' } }] }] } }
|
39
41
|
let(:result) { ['result' => { 'exit_code' => 0, 'stdout' => nil, 'stderr' => nil }] }
|
40
42
|
let(:command) { " puppet apply /bla.pp --modulepath #{Dir.pwd}/spec/fixtures/modules --detailed-exitcodes" }
|
41
43
|
|
@@ -85,8 +87,6 @@ RSpec.describe PuppetLitmus::PuppetHelpers do
|
|
85
87
|
describe '.run_shell' do
|
86
88
|
let(:command_to_run) { "puts 'doot'" }
|
87
89
|
let(:result) { ['result' => { 'exit_code' => 0, 'exit_status' => 0, 'stdout' => nil, 'stderr' => nil }] }
|
88
|
-
let(:inventory_hash) { { 'groups' => [{ 'name' => 'ssh_nodes', 'nodes' => [{ 'name' => 'some.host' }] }] } }
|
89
|
-
let(:localhost_inventory_hash) { { 'groups' => [{ 'name' => 'local', 'nodes' => [{ 'name' => 'litmus_localhost', 'config' => { 'transport' => 'local' } }] }] } }
|
90
90
|
|
91
91
|
it 'responds to run_shell' do
|
92
92
|
expect(described_class).to respond_to(:run_shell).with(1..2).arguments
|
@@ -123,8 +123,6 @@ RSpec.describe PuppetLitmus::PuppetHelpers do
|
|
123
123
|
let(:result_success) {[{'node'=>'some.host','target'=>'some.host','action'=>'upload','object'=>'C:\foo\bar.ps1','status'=>'success','result'=>{'_output'=>'Uploaded \'C:\foo\bar.ps1\' to \'some.host:C:\bar\''}}]}
|
124
124
|
let(:result_failure) {[{'node'=>'some.host','target'=>'some.host','action'=>nil,'object'=>nil,'status'=>'failure','result'=>{'_error'=>{'kind'=>'puppetlabs.tasks/task_file_error','msg'=>'No such file or directory @ rb_sysopen - /nonexistant/file/path','details'=>{},'issue_code'=>'WRITE_ERROR'}}}]}
|
125
125
|
# rubocop:enable Layout/SpaceInsideHashLiteralBraces, Layout/SpaceInsideBlockBraces, Layout/SpaceAroundOperators, Layout/LineLength, Layout/SpaceAfterComma
|
126
|
-
let(:inventory_hash) { { 'groups' => [{ 'name' => 'local', 'nodes' => [{ 'name' => 'some.host', 'config' => { 'transport' => 'local' } }] }] } }
|
127
|
-
let(:localhost_inventory_hash) { { 'groups' => [{ 'name' => 'local', 'nodes' => [{ 'name' => 'litmus_localhost', 'config' => { 'transport' => 'local' } }] }] } }
|
128
126
|
|
129
127
|
it 'responds to run_shell' do
|
130
128
|
expect(described_class).to respond_to(:bolt_upload_file).with(2..3).arguments
|
@@ -177,8 +175,6 @@ RSpec.describe PuppetLitmus::PuppetHelpers do
|
|
177
175
|
describe '.bolt_run_script' do
|
178
176
|
let(:script) { '/tmp/script.sh' }
|
179
177
|
let(:result) { ['result' => { 'exit_code' => 0, 'stdout' => nil, 'stderr' => nil }] }
|
180
|
-
let(:inventory_hash) { { 'groups' => [{ 'name' => 'local', 'nodes' => [{ 'name' => 'some.host', 'config' => { 'transport' => 'local' } }] }] } }
|
181
|
-
let(:localhost_inventory_hash) { { 'groups' => [{ 'name' => 'local', 'nodes' => [{ 'name' => 'litmus_localhost', 'config' => { 'transport' => 'local' } }] }] } }
|
182
178
|
|
183
179
|
it 'responds to bolt_run_script' do
|
184
180
|
expect(described_class).to respond_to(:bolt_run_script).with(1..2).arguments
|
@@ -200,9 +196,9 @@ RSpec.describe PuppetLitmus::PuppetHelpers do
|
|
200
196
|
it 'does bolt_run_script against remote host without error' do
|
201
197
|
stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'some.host'))
|
202
198
|
expect(File).to receive(:exist?).with('inventory.yaml').and_return(true)
|
203
|
-
expect(described_class).to receive(:inventory_hash_from_inventory_file)
|
199
|
+
expect(described_class).to receive(:inventory_hash_from_inventory_file).and_return(inventory_hash)
|
204
200
|
expect(described_class).to receive(:target_in_inventory?).and_return(true)
|
205
|
-
expect(described_class).to receive(:run_script).with(script, 'some.host', [], options: {}, config: nil, inventory:
|
201
|
+
expect(described_class).to receive(:run_script).with(script, 'some.host', [], options: {}, config: nil, inventory: inventory_hash).and_return(result)
|
206
202
|
expect { described_class.bolt_run_script(script) }.not_to raise_error
|
207
203
|
end
|
208
204
|
end
|
@@ -230,7 +226,6 @@ RSpec.describe PuppetLitmus::PuppetHelpers do
|
|
230
226
|
let(:result_structured_task_success){ [{'node'=>'some.host','target'=>'some.host','action'=>'task','object'=>'testtask::structured','status'=>'success','result'=>{'key1'=>'foo','key2'=>'bar'}}]}
|
231
227
|
let(:result_failure) {[{'node'=>'some.host','target'=>'some.host','action'=>'task','object'=>'testtask::unstructured','status'=>'failure','result'=>{'_error'=>{'msg'=>'FAILURE!','kind'=>'puppetlabs.tasks/task-error','details'=>{'exitcode'=>123}}}}]}
|
232
228
|
# rubocop:enable Layout/SpaceInsideHashLiteralBraces, Layout/SpaceBeforeBlockBraces, Layout/SpaceInsideBlockBraces, Layout/SpaceAroundOperators, Layout/LineLength, Layout/SpaceAfterComma
|
233
|
-
let(:inventory_hash) { { 'groups' => [{ 'name' => 'local', 'nodes' => [{ 'name' => 'some.host', 'config' => { 'transport' => 'local' } }] }] } }
|
234
229
|
|
235
230
|
it 'responds to bolt_run_task' do
|
236
231
|
expect(described_class).to respond_to(:run_bolt_task).with(2..3).arguments
|
@@ -2,6 +2,27 @@
|
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
|
+
RSpec.shared_examples 'supported provisioner' do |args|
|
6
|
+
let(:provisioner) { args[:provisioner] }
|
7
|
+
let(:platform) { args[:platform] }
|
8
|
+
let(:inventory_vars) { args[:inventory_vars] }
|
9
|
+
let(:provision_hash) { args[:provision_hash] }
|
10
|
+
let(:results) { args[:results] }
|
11
|
+
let(:params) { args[:params] }
|
12
|
+
|
13
|
+
it 'calls function' do
|
14
|
+
allow(File).to receive(:directory?).and_call_original
|
15
|
+
allow(File).to receive(:directory?)
|
16
|
+
.with(File.join(DEFAULT_CONFIG_DATA['modulepath'], 'provision'))
|
17
|
+
.and_return(true)
|
18
|
+
allow_any_instance_of(BoltSpec::Run).to receive(:run_task)
|
19
|
+
.with("provision::#{provisioner}", 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil)
|
20
|
+
.and_return(results)
|
21
|
+
result = described_class.provision(provisioner, platform, inventory_vars)
|
22
|
+
expect(result).to eq(results)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
5
26
|
RSpec.describe PuppetLitmus::RakeHelper do
|
6
27
|
context 'with provision_list' do
|
7
28
|
let(:provision_hash) { { 'default' => { 'provisioner' => 'docker', 'images' => ['waffleimage/centos7'] } } }
|
@@ -14,15 +35,29 @@ RSpec.describe PuppetLitmus::RakeHelper do
|
|
14
35
|
end
|
15
36
|
|
16
37
|
context 'with provision' do
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
38
|
+
examples = [
|
39
|
+
{
|
40
|
+
provisioner: 'docker',
|
41
|
+
platform: 'waffleimage/centos7',
|
42
|
+
inventory_vars: nil,
|
43
|
+
provision_hash: { 'default' => { 'provisioner' => 'docker', 'images' => ['waffleimage/centos7'] } },
|
44
|
+
results: [],
|
45
|
+
params: { 'action' => 'provision', 'platform' => 'waffleimage/centos7', 'inventory' => Dir.pwd },
|
46
|
+
},
|
47
|
+
{
|
48
|
+
provisioner: 'vagrant',
|
49
|
+
platform: 'centos7',
|
50
|
+
inventory_vars: nil,
|
51
|
+
provision_hash: { 'default' => { 'provisioner' => 'vagrant', 'images' => ['centos7'] } },
|
52
|
+
results: [],
|
53
|
+
params: { 'action' => 'provision', 'platform' => 'centos7', 'inventory' => Dir.pwd },
|
54
|
+
},
|
55
|
+
].freeze
|
56
|
+
|
57
|
+
examples.each do |e|
|
58
|
+
describe e[:provisioner] do
|
59
|
+
it_behaves_like 'supported provisioner', e
|
60
|
+
end
|
26
61
|
end
|
27
62
|
end
|
28
63
|
|
@@ -89,12 +124,12 @@ RSpec.describe PuppetLitmus::RakeHelper do
|
|
89
124
|
it 'node available' do
|
90
125
|
allow(Open3).to receive(:capture3).with('cd .').and_return(['success', '', 0])
|
91
126
|
allow_any_instance_of(BoltSpec::Run).to receive(:run_command).with(command, targets, config: nil, inventory: inventory_hash).and_return([{ 'target' => 'some.host', 'status' => 'success' }])
|
92
|
-
described_class.check_connectivity?(inventory_hash,
|
127
|
+
described_class.check_connectivity?(inventory_hash, 'some.host')
|
93
128
|
end
|
94
129
|
|
95
130
|
it 'node unavailable' do
|
96
131
|
allow_any_instance_of(BoltSpec::Run).to receive(:run_command).with(command, targets, config: nil, inventory: inventory_hash).and_return([{ 'target' => 'some.host', 'status' => 'failure' }])
|
97
|
-
expect { described_class.check_connectivity?(inventory_hash,
|
132
|
+
expect { described_class.check_connectivity?(inventory_hash, 'some.host') }.to raise_error(RuntimeError, %r{Connectivity has failed on:})
|
98
133
|
end
|
99
134
|
end
|
100
135
|
|
@@ -129,4 +164,16 @@ RSpec.describe PuppetLitmus::RakeHelper do
|
|
129
164
|
expect(name).to eq('foo-bar')
|
130
165
|
end
|
131
166
|
end
|
167
|
+
|
168
|
+
context 'with provisioner_task' do
|
169
|
+
described_class::SUPPORTED_PROVISIONERS.each do |supported_provisioner|
|
170
|
+
it "returns supported provisioner task name for #{supported_provisioner}" do
|
171
|
+
expect(described_class.provisioner_task(supported_provisioner)).to eq("provision::#{supported_provisioner}")
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'returns an unsupported provisioner name' do
|
176
|
+
expect(described_class.provisioner_task('my_org::custom_provisioner')).to eql('my_org::custom_provisioner')
|
177
|
+
end
|
178
|
+
end
|
132
179
|
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.17.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-03-
|
11
|
+
date: 2020-03-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bolt
|
@@ -126,6 +126,20 @@ dependencies:
|
|
126
126
|
- - ">="
|
127
127
|
- !ruby/object:Gem::Version
|
128
128
|
version: '0'
|
129
|
+
- !ruby/object:Gem::Dependency
|
130
|
+
name: rspec_honeycomb_formatter
|
131
|
+
requirement: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
type: :runtime
|
137
|
+
prerelease: false
|
138
|
+
version_requirements: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
129
143
|
description: " Providing a simple command line tool for puppet content creators,
|
130
144
|
to enable simple and complex test deployments.\n"
|
131
145
|
email:
|
@@ -173,19 +187,20 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
173
187
|
- !ruby/object:Gem::Version
|
174
188
|
version: '0'
|
175
189
|
requirements: []
|
176
|
-
|
190
|
+
rubyforge_project:
|
191
|
+
rubygems_version: 2.7.6.2
|
177
192
|
signing_key:
|
178
193
|
specification_version: 4
|
179
194
|
summary: Providing a simple command line tool for puppet content creators, to enable
|
180
195
|
simple and complex test deployments.
|
181
196
|
test_files:
|
182
|
-
- spec/
|
197
|
+
- spec/data/doot.tar.gz
|
198
|
+
- spec/data/inventory.yaml
|
199
|
+
- spec/data/jim.yaml
|
200
|
+
- spec/lib/puppet_litmus/inventory_manipulation_spec.rb
|
183
201
|
- spec/lib/puppet_litmus/rake_tasks_spec.rb
|
184
|
-
- spec/lib/puppet_litmus/version_spec.rb
|
185
202
|
- spec/lib/puppet_litmus/util_spec.rb
|
186
|
-
- spec/lib/puppet_litmus/
|
187
|
-
- spec/lib/puppet_litmus/rake_helper_spec.rb
|
203
|
+
- spec/lib/puppet_litmus/version_spec.rb
|
188
204
|
- spec/lib/puppet_litmus/puppet_helpers_spec.rb
|
189
|
-
- spec/
|
190
|
-
- spec/
|
191
|
-
- spec/data/inventory.yaml
|
205
|
+
- spec/lib/puppet_litmus/rake_helper_spec.rb
|
206
|
+
- spec/spec_helper.rb
|