puppet_litmus 0.16.0 → 0.18.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +12 -16
- data/lib/puppet_litmus.rb +0 -30
- data/lib/puppet_litmus/inventory_manipulation.rb +10 -1
- data/lib/puppet_litmus/puppet_helpers.rb +200 -137
- data/lib/puppet_litmus/rake_helper.rb +258 -96
- data/lib/puppet_litmus/rake_tasks.rb +96 -100
- data/lib/puppet_litmus/version.rb +1 -1
- data/spec/lib/puppet_litmus/inventory_manipulation_spec.rb +16 -16
- data/spec/lib/puppet_litmus/puppet_helpers_spec.rb +103 -117
- data/spec/lib/puppet_litmus/rake_helper_spec.rb +79 -27
- data/spec/lib/puppet_litmus/rake_tasks_spec.rb +14 -12
- data/spec/spec_helper.rb +7 -6
- metadata +77 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ace2d5a5ae33d1e795535b4704d84a54b9d97e48bd5a77b39866887c077e300
|
4
|
+
data.tar.gz: 611dbd8bd462331462506280920ac3355a732c18ca2a6edc2ce15e4da5071214
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f2024d6a7afed0976cdc5ab48ab1fe1e42ff17f75a39637feed9cf102d0a8defc8fd2c8bf54dac45512f9849fd9ec1a1862691455c6246e79afc55008376d23
|
7
|
+
data.tar.gz: 8a91349dc69667bdb7e701fa64a60fe71b0f8e311c017d5bda53b4fd0d1fa17c0276db50982076abdbf2d56408ea88cd6d9de8fac151ba609f41c75282c7125b
|
data/README.md
CHANGED
@@ -8,27 +8,23 @@
|
|
8
8
|
</div>
|
9
9
|
|
10
10
|
## Overview
|
11
|
-
Litmus provides a simple command line tool for Puppet content creators, to enable both simple and complex test deployments against specifically configured target systems. It is available as a gem, and can be installed by running ```gem install puppet_litmus```.
|
12
11
|
|
13
|
-
Litmus allows
|
14
|
-
* provision targets to test against,
|
15
|
-
* install the Puppet Agent,
|
16
|
-
* install a module,
|
17
|
-
* run tests, and
|
18
|
-
* tear down the infrastructure.
|
12
|
+
Litmus is a command line tool that allows you to run acceptance tests against Puppet modules.
|
19
13
|
|
20
|
-
|
14
|
+
Litmus allows you to:
|
15
|
+
* Provision targets to test against
|
16
|
+
* Install a Puppet agent
|
17
|
+
* Install a module
|
18
|
+
* Run tests
|
19
|
+
* Tear down the infrastructure
|
21
20
|
|
22
|
-
|
21
|
+
Litmus also facilitates parallel test runs and running tests in isolation. Each step is standalone, allowing other operations between test runs, such as debugging or configuration updates on the test targets.
|
22
|
+
|
23
|
+
Install Litmus as a gem by running ```gem install puppet_litmus```.
|
23
24
|
|
24
|
-
|
25
|
+
## Documentation
|
25
26
|
|
26
|
-
|
27
|
-
* [Architecture](https://github.com/puppetlabs/puppet_litmus/wiki/Architecture-of-puppet-litmus) with an explanation of what's going on under the hood
|
28
|
-
* [Step-by-step guide](https://github.com/puppetlabs/puppet_litmus/wiki/Tutorial:-use-Litmus-to-execute-acceptance-tests-with-a-sample-module-(MoTD)) of how to use Litmus with the popular and simple [MoTD Puppet module](https://forge.puppet.com/puppetlabs/motd).
|
29
|
-
* [How to guide](https://github.com/puppetlabs/puppet_litmus/wiki/Converting-a-module-to-use-Litmus) walking through how to use Litmus in a module
|
30
|
-
* [Helper functions](https://github.com/puppetlabs/puppet_litmus/wiki/Helper-Functions-for-Litmus) a guide to the various helper functions within Litmus.
|
31
|
-
## Known issues
|
27
|
+
For documentation, see our [Litmus Docs Site](https://puppetlabs.github.io/litmus/).
|
32
28
|
|
33
29
|
## Other Resources
|
34
30
|
|
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
|
@@ -235,7 +235,7 @@ module PuppetLitmus::InventoryManipulation
|
|
235
235
|
#
|
236
236
|
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
237
237
|
# @param feature_name [String] feature to locate in the node
|
238
|
-
# node_name [String] node of nodes to limit the search for the node_name in
|
238
|
+
# @param node_name [String] node of nodes to limit the search for the node_name in
|
239
239
|
# @return inventory.yaml file with feature removed from the node.
|
240
240
|
# @return [Hash] inventory_hash with feature added to node if node_name exists in inventory hash.
|
241
241
|
def remove_feature_from_node(inventory_hash, feature_name, node_name)
|
@@ -257,4 +257,13 @@ module PuppetLitmus::InventoryManipulation
|
|
257
257
|
def write_to_inventory_file(inventory_hash, inventory_full_path)
|
258
258
|
File.open(inventory_full_path, 'wb+') { |f| f.write(inventory_hash.to_yaml) }
|
259
259
|
end
|
260
|
+
|
261
|
+
# Add the `litmus.platform` with platform information for the target
|
262
|
+
#
|
263
|
+
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
264
|
+
# @param node_name [String] node of nodes to limit the search for the node_name in
|
265
|
+
def add_platform_field(inventory_hash, node_name)
|
266
|
+
facts = facts_from_node(inventory_hash, node_name)
|
267
|
+
Honeycomb.current_span.add_field('litmus.platform', facts&.dig('platform'))
|
268
|
+
end
|
260
269
|
end
|
@@ -8,87 +8,107 @@ 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
|
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
|
-
# :
|
35
|
-
# :
|
34
|
+
# :prefix_command [String] prefixes the puppet apply command; eg "export LANGUAGE='ja'".
|
35
|
+
# :trace [Boolean] run puppet apply with the trace flag (defaults to `true`).
|
36
|
+
# :debug [Boolean] run puppet apply with the debug flag.
|
37
|
+
# :noop [Boolean] run puppet apply with the noop flag.
|
36
38
|
# @yieldreturn [Block] this method will yield to a block of code passed by the caller; this can be used for additional validation, etc.
|
37
39
|
# @return [Object] A result object from the apply.
|
38
40
|
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
|
41
|
+
Honeycomb.start_span(name: 'litmus.apply_manifest') do |span|
|
42
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
|
43
|
+
span.add_field('litmus.manifest', manifest)
|
44
|
+
span.add_field('litmus.opts', opts)
|
62
45
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
46
|
+
target_node_name = targeting_localhost? ? 'litmus_localhost' : ENV['TARGET_HOST']
|
47
|
+
raise 'manifest and manifest_file_location in the opts hash are mutually exclusive arguments, pick one' if !manifest.nil? && !opts[:manifest_file_location].nil?
|
48
|
+
raise 'please pass a manifest or the manifest_file_location in the opts hash' if (manifest.nil? || manifest == '') && opts[:manifest_file_location].nil?
|
49
|
+
raise 'please specify only one of `catch_changes`, `expect_changes`, `catch_failures` or `expect_failures`' if
|
50
|
+
[opts[:catch_changes], opts[:expect_changes], opts[:catch_failures], opts[:expect_failures]].compact.length > 1
|
51
|
+
|
52
|
+
opts = { trace: true }.merge(opts)
|
53
|
+
|
54
|
+
if opts[:catch_changes]
|
55
|
+
use_detailed_exit_codes = true
|
56
|
+
acceptable_exit_codes = [0]
|
57
|
+
elsif opts[:catch_failures]
|
58
|
+
use_detailed_exit_codes = true
|
59
|
+
acceptable_exit_codes = [0, 2]
|
60
|
+
elsif opts[:expect_failures]
|
61
|
+
use_detailed_exit_codes = true
|
62
|
+
acceptable_exit_codes = [1, 4, 6]
|
63
|
+
elsif opts[:expect_changes]
|
64
|
+
use_detailed_exit_codes = true
|
65
|
+
acceptable_exit_codes = [2]
|
66
|
+
else
|
67
|
+
use_detailed_exit_codes = false
|
68
|
+
acceptable_exit_codes = [0]
|
69
|
+
end
|
81
70
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
71
|
+
manifest_file_location = opts[:manifest_file_location] || create_manifest_file(manifest)
|
72
|
+
inventory_hash = File.exist?('inventory.yaml') ? inventory_hash_from_inventory_file : localhost_inventory_hash
|
73
|
+
raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
|
74
|
+
|
75
|
+
span.add_field('litmus.node_name', target_node_name)
|
76
|
+
add_platform_field(inventory_hash, target_node_name)
|
77
|
+
|
78
|
+
command_to_run = "#{opts[:prefix_command]} puppet apply #{manifest_file_location}"
|
79
|
+
command_to_run += ' --trace' if !opts[:trace].nil? && (opts[:trace] == true)
|
80
|
+
command_to_run += " --modulepath #{Dir.pwd}/spec/fixtures/modules" if target_node_name == 'litmus_localhost'
|
81
|
+
command_to_run += " --hiera_config='#{opts[:hiera_config]}'" unless opts[:hiera_config].nil?
|
82
|
+
command_to_run += ' --debug' if !opts[:debug].nil? && (opts[:debug] == true)
|
83
|
+
command_to_run += ' --noop' if !opts[:noop].nil? && (opts[:noop] == true)
|
84
|
+
command_to_run += ' --detailed-exitcodes' if use_detailed_exit_codes == true
|
85
|
+
|
86
|
+
span.add_field('litmus.command_to_run', command_to_run)
|
87
|
+
span.add_field('litmus.target_node_name', target_node_name)
|
88
|
+
bolt_result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
|
89
|
+
span.add_field('litmus.bolt_result', bolt_result)
|
90
|
+
|
91
|
+
result = OpenStruct.new(exit_code: bolt_result.first['value']['exit_code'],
|
92
|
+
stdout: bolt_result.first['value']['stdout'],
|
93
|
+
stderr: bolt_result.first['value']['stderr'])
|
94
|
+
span.add_field('litmus.result', result.to_h)
|
95
|
+
|
96
|
+
status = result.exit_code
|
97
|
+
if opts[:catch_changes] && !acceptable_exit_codes.include?(status)
|
98
|
+
report_puppet_apply_change(command_to_run, bolt_result)
|
99
|
+
elsif !acceptable_exit_codes.include?(status)
|
100
|
+
report_puppet_apply_error(command_to_run, bolt_result, acceptable_exit_codes)
|
101
|
+
end
|
102
|
+
|
103
|
+
yield result if block_given?
|
104
|
+
|
105
|
+
if ENV['RSPEC_DEBUG']
|
106
|
+
puts "apply manifest succeded\n #{command_to_run}\n======\nwith status #{result.exit_code}"
|
107
|
+
puts result.stderr
|
108
|
+
puts result.stdout
|
109
|
+
end
|
110
|
+
result
|
90
111
|
end
|
91
|
-
result
|
92
112
|
end
|
93
113
|
|
94
114
|
# Creates a manifest file locally in a temp location, if its a remote target copy it to there.
|
@@ -96,23 +116,35 @@ module PuppetLitmus::PuppetHelpers
|
|
96
116
|
# @param manifest [String] puppet manifest code.
|
97
117
|
# @return [String] The path to the location of the manifest.
|
98
118
|
def create_manifest_file(manifest)
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
119
|
+
Honeycomb.start_span(name: 'litmus.create_manifest_file') do |span|
|
120
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
|
121
|
+
span.add_field('litmus.manifest', manifest)
|
122
|
+
|
123
|
+
require 'tmpdir'
|
124
|
+
target_node_name = ENV['TARGET_HOST']
|
125
|
+
tmp_filename = File.join(Dir.tmpdir, "manifest_#{Time.now.strftime('%Y%m%d')}_#{Process.pid}_#{rand(0x100000000).to_s(36)}.pp")
|
126
|
+
manifest_file = File.open(tmp_filename, 'w')
|
127
|
+
manifest_file.write(manifest)
|
128
|
+
manifest_file.close
|
129
|
+
if target_node_name.nil? || target_node_name == 'localhost'
|
130
|
+
# no need to transfer
|
131
|
+
manifest_file_location = manifest_file.path
|
132
|
+
else
|
133
|
+
# transfer to TARGET_HOST
|
134
|
+
inventory_hash = inventory_hash_from_inventory_file
|
135
|
+
span.add_field('litmus.node_name', target_node_name)
|
136
|
+
add_platform_field(inventory_hash, target_node_name)
|
137
|
+
|
138
|
+
manifest_file_location = File.basename(manifest_file)
|
139
|
+
bolt_result = upload_file(manifest_file.path, manifest_file_location, target_node_name, options: {}, config: nil, inventory: inventory_hash)
|
140
|
+
span.add_field('litmus.bolt_result', bolt_result)
|
141
|
+
raise bolt_result.first['value'].to_s unless bolt_result.first['status'] == 'success'
|
142
|
+
end
|
143
|
+
|
144
|
+
span.add_field('litmus.manifest_file_location', manifest_file_location)
|
145
|
+
|
146
|
+
manifest_file_location
|
114
147
|
end
|
115
|
-
manifest_file_location
|
116
148
|
end
|
117
149
|
|
118
150
|
# Runs a command against the target system
|
@@ -122,23 +154,31 @@ module PuppetLitmus::PuppetHelpers
|
|
122
154
|
# @yieldreturn [Block] this method will yield to a block of code passed by the caller; this can be used for additional validation, etc.
|
123
155
|
# @return [Object] A result object from the command.
|
124
156
|
def run_shell(command_to_run, opts = {})
|
125
|
-
Honeycomb.start_span(name: '
|
157
|
+
Honeycomb.start_span(name: 'litmus.run_shell') do |span|
|
158
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
|
159
|
+
span.add_field('litmus.command_to_run', command_to_run)
|
160
|
+
span.add_field('litmus.opts', opts)
|
161
|
+
|
126
162
|
target_node_name = targeting_localhost? ? 'litmus_localhost' : ENV['TARGET_HOST']
|
127
163
|
inventory_hash = File.exist?('inventory.yaml') ? inventory_hash_from_inventory_file : localhost_inventory_hash
|
128
164
|
raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
|
129
165
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
166
|
+
span.add_field('litmus.node_name', target_node_name)
|
167
|
+
add_platform_field(inventory_hash, target_node_name)
|
168
|
+
|
169
|
+
bolt_result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
|
170
|
+
span.add_field('litmus.bolt_result', bolt_result)
|
171
|
+
|
172
|
+
if bolt_result.first['value']['exit_code'] != 0 && opts[:expect_failures] != true
|
173
|
+
raise "shell failed\n`#{command_to_run}`\n======\n#{bolt_result}"
|
134
174
|
end
|
135
175
|
|
136
|
-
result = OpenStruct.new(exit_code:
|
137
|
-
exit_status:
|
138
|
-
stdout:
|
139
|
-
stderr:
|
176
|
+
result = OpenStruct.new(exit_code: bolt_result.first['value']['exit_code'],
|
177
|
+
exit_status: bolt_result.first['value']['exit_code'],
|
178
|
+
stdout: bolt_result.first['value']['stdout'],
|
179
|
+
stderr: bolt_result.first['value']['stderr'])
|
180
|
+
span.add_field('litmus.result', result.to_h)
|
140
181
|
yield result if block_given?
|
141
|
-
span.add_field('litmus_runshellsuccess', result)
|
142
182
|
result
|
143
183
|
end
|
144
184
|
end
|
@@ -151,52 +191,64 @@ module PuppetLitmus::PuppetHelpers
|
|
151
191
|
# @yieldreturn [Block] this method will yield to a block of code passed by the caller; this can be used for additional validation, etc.
|
152
192
|
# @return [Object] A result object from the command.
|
153
193
|
def bolt_upload_file(source, destination, opts = {}, options = {})
|
154
|
-
Honeycomb.start_span(name: '
|
194
|
+
Honeycomb.start_span(name: 'litmus.bolt_upload_file') do |span|
|
195
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
|
196
|
+
span.add_field('litmus.source', source)
|
197
|
+
span.add_field('litmus.destination', destination)
|
198
|
+
span.add_field('litmus.opts', opts)
|
199
|
+
span.add_field('litmus.options', options)
|
200
|
+
|
155
201
|
target_node_name = targeting_localhost? ? 'litmus_localhost' : ENV['TARGET_HOST']
|
156
202
|
inventory_hash = File.exist?('inventory.yaml') ? inventory_hash_from_inventory_file : localhost_inventory_hash
|
157
203
|
raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
|
158
204
|
|
159
|
-
|
205
|
+
span.add_field('litmus.node_name', target_node_name)
|
206
|
+
add_platform_field(inventory_hash, target_node_name)
|
207
|
+
|
208
|
+
bolt_result = upload_file(source, destination, target_node_name, options: options, config: nil, inventory: inventory_hash)
|
209
|
+
span.add_field('litmus.bolt_result', bolt_result)
|
160
210
|
|
161
211
|
result_obj = {
|
162
212
|
exit_code: 0,
|
163
|
-
stdout:
|
213
|
+
stdout: bolt_result.first['value']['_output'],
|
164
214
|
stderr: nil,
|
165
|
-
result:
|
215
|
+
result: bolt_result.first['value'],
|
166
216
|
}
|
167
217
|
|
168
|
-
if
|
218
|
+
if bolt_result.first['status'] != 'success'
|
169
219
|
if opts[:expect_failures] != true
|
170
|
-
span.add_field('litmus_uploadfilefailure',
|
171
|
-
raise "upload file failed\n======\n#{
|
220
|
+
span.add_field('litmus_uploadfilefailure', bolt_result)
|
221
|
+
raise "upload file failed\n======\n#{bolt_result}"
|
172
222
|
end
|
173
223
|
|
174
224
|
result_obj[:exit_code] = 255
|
175
|
-
result_obj[:stderr] =
|
225
|
+
result_obj[:stderr] = bolt_result.first['value']['_error']['msg']
|
176
226
|
end
|
177
227
|
|
178
228
|
result = OpenStruct.new(exit_code: result_obj[:exit_code],
|
179
229
|
stdout: result_obj[:stdout],
|
180
230
|
stderr: result_obj[:stderr])
|
231
|
+
span.add_field('litmus.result', result.to_h)
|
181
232
|
yield result if block_given?
|
182
|
-
span.add_field('litmus_uploadfilesucess', result)
|
183
233
|
result
|
184
234
|
end
|
185
235
|
end
|
186
236
|
|
187
|
-
# rubocop:disable Layout/TrailingWhitespace
|
188
|
-
|
189
237
|
# Runs a task against the target system.
|
190
238
|
#
|
191
239
|
# @param task_name [String] The name of the task to run.
|
192
240
|
# @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.
|
241
|
+
# @param opts [Hash] Alters the behaviour of the command. Valid options are
|
242
|
+
# :expect_failures [Boolean] doesnt return an exit code of non-zero if the command failed.
|
195
243
|
# :inventory_file [String] path to the inventory file to use with the task.
|
196
244
|
# @return [Object] A result object from the task.The values available are stdout, stderr and result.
|
197
|
-
# rubocop:enable Layout/TrailingWhitespace
|
198
245
|
def run_bolt_task(task_name, params = {}, opts = {})
|
199
|
-
Honeycomb.start_span(name: '
|
246
|
+
Honeycomb.start_span(name: 'litmus.run_task') do |span|
|
247
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
|
248
|
+
span.add_field('litmus.task_name', task_name)
|
249
|
+
span.add_field('litmus.params', params)
|
250
|
+
span.add_field('litmus.opts', opts)
|
251
|
+
|
200
252
|
config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
|
201
253
|
target_node_name = targeting_localhost? ? 'litmus_localhost' : ENV['TARGET_HOST']
|
202
254
|
inventory_hash = if !opts[:inventory_file].nil? && File.exist?(opts[:inventory_file])
|
@@ -208,34 +260,37 @@ module PuppetLitmus::PuppetHelpers
|
|
208
260
|
end
|
209
261
|
raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
|
210
262
|
|
211
|
-
|
263
|
+
span.add_field('litmus.node_name', target_node_name)
|
264
|
+
add_platform_field(inventory_hash, target_node_name)
|
265
|
+
|
266
|
+
bolt_result = run_task(task_name, target_node_name, params, config: config_data, inventory: inventory_hash)
|
212
267
|
result_obj = {
|
213
268
|
exit_code: 0,
|
214
269
|
stdout: nil,
|
215
270
|
stderr: nil,
|
216
|
-
result:
|
271
|
+
result: bolt_result.first['value'],
|
217
272
|
}
|
218
273
|
|
219
|
-
if
|
274
|
+
if bolt_result.first['status'] == 'success'
|
220
275
|
# stdout returns unstructured data if structured data is not available
|
221
|
-
result_obj[:stdout] = if
|
222
|
-
|
276
|
+
result_obj[:stdout] = if bolt_result.first['value']['_output'].nil?
|
277
|
+
bolt_result.first['value'].to_s
|
223
278
|
else
|
224
|
-
|
279
|
+
bolt_result.first['value']['_output']
|
225
280
|
end
|
226
281
|
|
227
282
|
else
|
228
283
|
if opts[:expect_failures] != true
|
229
|
-
span.add_field('litmus_runtaskfailure',
|
230
|
-
raise "task failed\n`#{task_name}`\n======\n#{
|
284
|
+
span.add_field('litmus_runtaskfailure', bolt_result)
|
285
|
+
raise "task failed\n`#{task_name}`\n======\n#{bolt_result}"
|
231
286
|
end
|
232
287
|
|
233
|
-
result_obj[:exit_code] = if
|
288
|
+
result_obj[:exit_code] = if bolt_result.first['value']['_error']['details'].nil?
|
234
289
|
255
|
235
290
|
else
|
236
|
-
|
291
|
+
bolt_result.first['value']['_error']['details'].fetch('exitcode', 255)
|
237
292
|
end
|
238
|
-
result_obj[:stderr] =
|
293
|
+
result_obj[:stderr] = bolt_result.first['value']['_error']['msg']
|
239
294
|
end
|
240
295
|
|
241
296
|
result = OpenStruct.new(exit_code: result_obj[:exit_code],
|
@@ -243,7 +298,7 @@ module PuppetLitmus::PuppetHelpers
|
|
243
298
|
stderr: result_obj[:stderr],
|
244
299
|
result: result_obj[:result])
|
245
300
|
yield result if block_given?
|
246
|
-
span.add_field('
|
301
|
+
span.add_field('litmus.result', result.to_h)
|
247
302
|
result
|
248
303
|
end
|
249
304
|
end
|
@@ -256,23 +311,31 @@ module PuppetLitmus::PuppetHelpers
|
|
256
311
|
# @yieldreturn [Block] this method will yield to a block of code passed by the caller; this can be used for additional validation, etc.
|
257
312
|
# @return [Object] A result object from the script run.
|
258
313
|
def bolt_run_script(script, opts = {}, arguments: [])
|
259
|
-
Honeycomb.start_span(name: '
|
314
|
+
Honeycomb.start_span(name: 'litmus.bolt_run_script') do |span|
|
315
|
+
ENV['HTTP_X_HONEYCOMB_TRACE'] = span.to_trace_header
|
316
|
+
span.add_field('litmus.script', script)
|
317
|
+
span.add_field('litmus.opts', opts)
|
318
|
+
span.add_field('litmus.arguments', arguments)
|
319
|
+
|
260
320
|
target_node_name = targeting_localhost? ? 'litmus_localhost' : ENV['TARGET_HOST']
|
261
321
|
inventory_hash = File.exist?('inventory.yaml') ? inventory_hash_from_inventory_file : localhost_inventory_hash
|
262
322
|
raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
|
263
323
|
|
264
|
-
|
324
|
+
span.add_field('litmus.node_name', target_node_name)
|
325
|
+
add_platform_field(inventory_hash, target_node_name)
|
326
|
+
|
327
|
+
bolt_result = run_script(script, target_node_name, arguments, options: opts, config: nil, inventory: inventory_hash)
|
265
328
|
|
266
|
-
if
|
267
|
-
span.add_field('litmus_runscriptfailure',
|
268
|
-
raise "script run failed\n`#{script}`\n======\n#{
|
329
|
+
if bolt_result.first['value']['exit_code'] != 0 && opts[:expect_failures] != true
|
330
|
+
span.add_field('litmus_runscriptfailure', bolt_result)
|
331
|
+
raise "script run failed\n`#{script}`\n======\n#{bolt_result}"
|
269
332
|
end
|
270
333
|
|
271
|
-
result = OpenStruct.new(exit_code:
|
272
|
-
stdout:
|
273
|
-
stderr:
|
334
|
+
result = OpenStruct.new(exit_code: bolt_result.first['value']['exit_code'],
|
335
|
+
stdout: bolt_result.first['value']['stdout'],
|
336
|
+
stderr: bolt_result.first['value']['stderr'])
|
274
337
|
yield result if block_given?
|
275
|
-
span.add_field('
|
338
|
+
span.add_field('litmus.result', result.to_h)
|
276
339
|
result
|
277
340
|
end
|
278
341
|
end
|
@@ -289,14 +352,14 @@ module PuppetLitmus::PuppetHelpers
|
|
289
352
|
# Report an error in the puppet run
|
290
353
|
#
|
291
354
|
# @param command [String] The puppet command causing the error.
|
292
|
-
# @param
|
293
|
-
def report_puppet_apply_error(command,
|
355
|
+
# @param bolt_result [Array] The result object from bolt
|
356
|
+
def report_puppet_apply_error(command, bolt_result, acceptable_exit_codes)
|
294
357
|
puppet_apply_error = <<~ERROR
|
295
358
|
apply manifest failed
|
296
359
|
`#{command}`
|
297
|
-
with exit code #{
|
360
|
+
with exit code #{bolt_result.first['value']['exit_code']} (expected: #{acceptable_exit_codes})
|
298
361
|
====== Start output of failed Puppet apply ======
|
299
|
-
#{puppet_output(
|
362
|
+
#{puppet_output(bolt_result)}
|
300
363
|
====== End output of failed Puppet apply ======
|
301
364
|
ERROR
|
302
365
|
raise puppet_apply_error
|
@@ -305,22 +368,22 @@ module PuppetLitmus::PuppetHelpers
|
|
305
368
|
# Report an unexpected change in the puppet run
|
306
369
|
#
|
307
370
|
# @param command [String] The puppet command causing the error.
|
308
|
-
# @param
|
309
|
-
def report_puppet_apply_change(command,
|
371
|
+
# @param bolt_result [Array] The result object from bolt
|
372
|
+
def report_puppet_apply_change(command, bolt_result)
|
310
373
|
puppet_apply_changes = <<~ERROR
|
311
374
|
apply manifest expected no changes
|
312
375
|
`#{command}`
|
313
376
|
====== Start output of Puppet apply with unexpected changes ======
|
314
|
-
#{puppet_output(
|
377
|
+
#{puppet_output(bolt_result)}
|
315
378
|
====== End output of Puppet apply with unexpected changes ======
|
316
379
|
ERROR
|
317
380
|
raise puppet_apply_changes
|
318
381
|
end
|
319
382
|
|
320
383
|
# Return the stdout of the puppet run
|
321
|
-
def puppet_output(
|
322
|
-
|
323
|
-
|
384
|
+
def puppet_output(bolt_result)
|
385
|
+
bolt_result.dig(0, 'value', 'stderr').to_s << \
|
386
|
+
bolt_result.dig(0, 'value', 'stdout').to_s
|
324
387
|
end
|
325
388
|
|
326
389
|
# Checks a puppet return status and returns true if it both
|