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 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