puppet_litmus 0.16.0 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|