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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e545da74266b708eae5c7cb6bab56480b78724ff51a9b8591943cfa98db3d7d
4
- data.tar.gz: fd3e61dbc511549ac3ab1c43d834b9fa8e729d5af4bb48a6b4fab50041539428
3
+ metadata.gz: c81ccd0186b54db2eb71534a0cc10e5191e1b6103201e8ef44231cc017cf5faa
4
+ data.tar.gz: dd027dff77884a4b09b66447c966c3636a2cf237e21c1b4f0ec1d28182188937
5
5
  SHA512:
6
- metadata.gz: 43adad4003cec09ff51204502f1f8f223c379ddd0c5ab93f38cb1285f0868ed965bbbc0117db72f022bc346f12315d9b9bfbb1deeed41c15467c17d2a86ebc43
7
- data.tar.gz: c8d670ec75ee889f29a02071be10574027a742575034f3666a348f2bbcb6dca85f5d748fe236a357ffff571eb4d3556d0c32140652d308fc765936b21c1cb8b7
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
@@ -37,6 +37,7 @@ module PuppetLitmus::InventoryManipulation
37
37
  'uri' => 'litmus_localhost',
38
38
  'config' => { 'transport' => 'local' },
39
39
  'feature' => 'puppet-agent',
40
+ 'facts' => { 'platform' => 'localhost' },
40
41
  },
41
42
  ],
42
43
  },
@@ -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
- manifest_file_location = create_manifest_file(manifest)
12
- apply_manifest(nil, expect_failures: false, manifest_file_location: manifest_file_location)
13
- apply_manifest(nil, catch_changes: true, manifest_file_location: manifest_file_location)
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
- # rubocop:enable Layout/TrailingWhitespace
40
- target_node_name = targeting_localhost? ? 'litmus_localhost' : ENV['TARGET_HOST']
41
- raise 'manifest and manifest_file_location in the opts hash are mutually exclusive arguments, pick one' if !manifest.nil? && !opts[:manifest_file_location].nil?
42
- raise 'please pass a manifest or the manifest_file_location in the opts hash' if (manifest.nil? || manifest == '') && opts[:manifest_file_location].nil?
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
- manifest_file_location = opts[:manifest_file_location] || create_manifest_file(manifest)
64
- inventory_hash = File.exist?('inventory.yaml') ? inventory_hash_from_inventory_file : localhost_inventory_hash
65
- raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
66
-
67
- command_to_run = "#{opts[:prefix_command]} puppet apply #{manifest_file_location}"
68
- command_to_run += " --modulepath #{Dir.pwd}/spec/fixtures/modules" if target_node_name == 'litmus_localhost'
69
- command_to_run += " --hiera_config='#{opts[:hiera_config]}'" unless opts[:hiera_config].nil?
70
- command_to_run += ' --debug' if !opts[:debug].nil? && (opts[:debug] == true)
71
- command_to_run += ' --noop' if !opts[:noop].nil? && (opts[:noop] == true)
72
- command_to_run += ' --detailed-exitcodes' if use_detailed_exit_codes == true
73
-
74
- result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
75
- status = result.first['result']['exit_code']
76
- if opts[:catch_changes] && !acceptable_exit_codes.include?(status)
77
- report_puppet_apply_change(command_to_run, result)
78
- elsif !acceptable_exit_codes.include?(status)
79
- report_puppet_apply_error(command_to_run, result, acceptable_exit_codes)
80
- end
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
- result = OpenStruct.new(exit_code: result.first['result']['exit_code'],
83
- stdout: result.first['result']['stdout'],
84
- stderr: result.first['result']['stderr'])
85
- yield result if block_given?
86
- if ENV['RSPEC_DEBUG']
87
- puts "apply manifest succeded\n #{command_to_run}\n======\nwith status #{result.exit_code}"
88
- puts result.stderr
89
- puts result.stdout
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
- require 'tmpdir'
100
- target_node_name = ENV['TARGET_HOST']
101
- tmp_filename = File.join(Dir.tmpdir, "manifest_#{Time.now.strftime('%Y%m%d')}_#{Process.pid}_#{rand(0x100000000).to_s(36)}.pp")
102
- manifest_file = File.open(tmp_filename, 'w')
103
- manifest_file.write(manifest)
104
- manifest_file.close
105
- if target_node_name.nil? || target_node_name == 'localhost'
106
- # no need to transfer
107
- manifest_file_location = manifest_file.path
108
- else
109
- # transfer to TARGET_HOST
110
- inventory_hash = inventory_hash_from_inventory_file
111
- manifest_file_location = "/tmp/#{File.basename(manifest_file)}"
112
- result = upload_file(manifest_file.path, manifest_file_location, target_node_name, options: {}, config: nil, inventory: inventory_hash)
113
- raise result.first['result'].to_s unless result.first['status'] == 'success'
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: 'litmus_runshell') do |span|
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
- result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
131
- if result.first['result']['exit_code'] != 0 && opts[:expect_failures] != true
132
- span.add_field('litmus_runshellfailure', result)
133
- raise "shell failed\n`#{command_to_run}`\n======\n#{result}"
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: result.first['result']['exit_code'],
137
- exit_status: result.first['result']['exit_code'],
138
- stdout: result.first['result']['stdout'],
139
- stderr: result.first['result']['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: 'litmus_uploadfile') do |span|
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
- result = upload_file(source, destination, target_node_name, options: options, config: nil, inventory: inventory_hash)
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: result.first['result']['_output'],
209
+ stdout: bolt_result.first['result']['_output'],
164
210
  stderr: nil,
165
- result: result.first['result'],
211
+ result: bolt_result.first['result'],
166
212
  }
167
213
 
168
- if result.first['status'] != 'success'
214
+ if bolt_result.first['status'] != 'success'
169
215
  if opts[:expect_failures] != true
170
- span.add_field('litmus_uploadfilefailure', result)
171
- raise "upload file failed\n======\n#{result}"
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] = result.first['result']['_error']['msg']
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: 'litmus_runtask') do |span|
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
- result = run_task(task_name, target_node_name, params, config: config_data, inventory: inventory_hash)
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: result.first['result'],
267
+ result: bolt_result.first['result'],
217
268
  }
218
269
 
219
- if result.first['status'] == 'success'
270
+ if bolt_result.first['status'] == 'success'
220
271
  # stdout returns unstructured data if structured data is not available
221
- result_obj[:stdout] = if result.first['result']['_output'].nil?
222
- result.first['result'].to_s
272
+ result_obj[:stdout] = if bolt_result.first['result']['_output'].nil?
273
+ bolt_result.first['result'].to_s
223
274
  else
224
- result.first['result']['_output']
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', result)
230
- raise "task failed\n`#{task_name}`\n======\n#{result}"
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 result.first['result']['_error']['details'].nil?
284
+ result_obj[:exit_code] = if bolt_result.first['result']['_error']['details'].nil?
234
285
  255
235
286
  else
236
- result.first['result']['_error']['details'].fetch('exitcode', 255)
287
+ bolt_result.first['result']['_error']['details'].fetch('exitcode', 255)
237
288
  end
238
- result_obj[:stderr] = result.first['result']['_error']['msg']
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('litmus_runtasksuccess', result)
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: 'litmus_runscript') do |span|
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
- result = run_script(script, target_node_name, arguments, options: opts, config: nil, inventory: inventory_hash)
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 result.first['result']['exit_code'] != 0 && opts[:expect_failures] != true
267
- span.add_field('litmus_runscriptfailure', result)
268
- raise "script run failed\n`#{script}`\n======\n#{result}"
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: result.first['result']['exit_code'],
272
- stdout: result.first['result']['stdout'],
273
- stderr: result.first['result']['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('litmus_runscriptsuccess', result)
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 result [Array] The result struct containing the result
293
- def report_puppet_apply_error(command, result, acceptable_exit_codes)
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 #{result.first['result']['exit_code']} (expected: #{acceptable_exit_codes})
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(result)}
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 result [Array] The result struct containing the result
309
- def report_puppet_apply_change(command, result)
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(result)}
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(result)
322
- result.dig(0, 'result', 'stderr').to_s << \
323
- result.dig(0, 'result', 'stdout').to_s
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
- VALID_PROVISIONERS ||= %w[abs docker docker_exp vagrant vmpooler].freeze
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
- require 'open3'
56
- stdout, stderr, status = Open3.capture3(command)
57
- error_message = "Attempted to run\ncommand:'#{command}'\nstdout:#{stdout}\nstderr:#{stderr}"
58
- raise error_message unless status.to_i.zero?
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
- stdout
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
- unless VALID_PROVISIONERS.include?(provisioner)
93
- raise "Unknown provisioner '#{provisioner}', try #{VALID_PROVISIONERS.join('/')}"
94
- end
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
- params = if inventory_vars.nil?
97
- { 'action' => 'provision', 'platform' => platform, 'inventory' => Dir.pwd }
98
- else
99
- { 'action' => 'provision', 'platform' => platform, 'inventory' => Dir.pwd, 'vars' => inventory_vars }
100
- end
101
- run_task("provision::#{provisioner}", 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil)
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
- require 'bolt_spec/run'
118
- include BoltSpec::Run
119
- config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
120
- 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'))
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
- results = {}
123
- targets.each do |node_name|
124
- next if node_name == 'litmus_localhost'
173
+ results = {}
174
+ targets.each do |node_name|
175
+ next if node_name == 'litmus_localhost'
125
176
 
126
- result = tear_down(node_name, inventory_hash)
127
- results[node_name] = result unless result == []
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
- # how do we know what provisioner to use
134
- node_facts = facts_from_node(inventory_hash, node_name)
135
- return [] unless VALID_PROVISIONERS.include?(node_facts['provisioner'])
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
- params = { 'action' => 'tear_down', 'node_name' => node_name, 'inventory' => Dir.pwd }
138
- run_task("provision::#{node_facts['provisioner']}", 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil)
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
- require 'bolt_spec/run'
143
- include BoltSpec::Run
144
- params = if collection.nil?
145
- {}
146
- else
147
- { 'collection' => collection }
148
- end
149
- raise "puppet_agent was not found in #{DEFAULT_CONFIG_DATA['modulepath']}, please amend the .fixtures.yml file" unless File.directory?(File.join(DEFAULT_CONFIG_DATA['modulepath'], 'puppet_agent'))
150
-
151
- # using boltspec, when the runner is called it changes the inventory_hash dropping the version field. The clone works around this
152
- run_task('puppet_agent::install', targets, params, config: DEFAULT_CONFIG_DATA, inventory: inventory_hash.clone)
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
- run_command(install_module_command, target_nodes, config: nil, inventory: inventory_hash)
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
- require 'bolt_spec/run'
217
- include BoltSpec::Run
218
- target_nodes = find_targets(inventory_hash, target_node_name)
219
- results = run_command('cd .', target_nodes, config: nil, inventory: inventory_hash)
220
- failed = []
221
- results.each do |result|
222
- failed.push(result['target']) if result['status'] == 'failure'
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
- raise "Connectivity has failed on: #{failed}" unless failed.length.zero?
309
+ end
225
310
 
226
- true
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
@@ -2,5 +2,5 @@
2
2
 
3
3
  # version of this gem
4
4
  module PuppetLitmus
5
- VERSION ||= '0.16.0'
5
+ VERSION ||= '0.17.0'
6
6
  end
@@ -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: nil).and_return(result)
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
- let(:provision_hash) { { 'default' => { 'provisioner' => 'docker', 'images' => ['waffleimage/centos7'] } } }
18
- let(:results) { [] }
19
- let(:params) { { 'action' => 'provision', 'platform' => 'waffleimage/centos7', 'inventory' => Dir.pwd } }
20
-
21
- it 'calls function' do
22
- allow(File).to receive(:directory?).and_call_original
23
- allow(File).to receive(:directory?).with(File.join(DEFAULT_CONFIG_DATA['modulepath'], 'provision')).and_return(true)
24
- allow_any_instance_of(BoltSpec::Run).to receive(:run_task).with('provision::docker', 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil).and_return(results)
25
- described_class.provision('docker', 'waffleimage/centos7', nil)
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, nil)
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, nil) }.to raise_error(RuntimeError, %r{Connectivity has failed on:})
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.16.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-09 00:00:00.000000000 Z
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
- rubygems_version: 3.1.2
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/spec_helper.rb
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/inventory_manipulation_spec.rb
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/data/doot.tar.gz
190
- - spec/data/jim.yaml
191
- - spec/data/inventory.yaml
205
+ - spec/lib/puppet_litmus/rake_helper_spec.rb
206
+ - spec/spec_helper.rb