puppet_litmus 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/puppet_litmus/rake_tasks.rb +61 -38
- data/lib/puppet_litmus/serverspec.rb +49 -6
- data/lib/puppet_litmus/version.rb +1 -1
- metadata +22 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 071a673e5f77fa53f54031196856229f59a8869b
|
4
|
+
data.tar.gz: d35953f4b2d720a7f185c558ff71ea21964db698
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da87c6046e0eddf2873ce385a95fb772cdbc92acf62525f588d1e86f6b10b43bde52634a9a228ceb99ad3f22a48ae16c44d0f387e0c31a069e59a8e7378dc9c8
|
7
|
+
data.tar.gz: ca340854e2b2f81d0205c30a9a07a13343c0f75ad67879a38c405f1d5962cc427ca783a71e301d90e1804c69e0f23105eb0b4110746f0768f41d22d68a4f3d46
|
@@ -6,7 +6,6 @@ require 'bolt_spec/run'
|
|
6
6
|
require 'open3'
|
7
7
|
require 'pdk'
|
8
8
|
require 'json'
|
9
|
-
require 'parallel'
|
10
9
|
|
11
10
|
def get_metadata_operating_systems(metadata)
|
12
11
|
return unless metadata.is_a?(Hash)
|
@@ -110,28 +109,18 @@ namespace :litmus do
|
|
110
109
|
config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
|
111
110
|
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'))
|
112
111
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
params = { 'action' => 'provision', 'platform' => args[:platform], 'inventory' => Dir.pwd }
|
125
|
-
results = run_task('provision::docker', 'localhost', params, config: config_data, inventory: nil)
|
126
|
-
results.each do |result|
|
127
|
-
if result['status'] != 'success'
|
128
|
-
puts "Failed #{result['node']}\n#{result}"
|
129
|
-
else
|
130
|
-
puts "#{result['result']['node_name']}, #{args[:platform]}"
|
131
|
-
end
|
112
|
+
unless %w[abs vmpooler docker vagrant].include?(args[:provisioner])
|
113
|
+
raise "Unknown provisioner '#{args[:provisioner]}', try abs/docker/vmpooler/vagrant"
|
114
|
+
end
|
115
|
+
|
116
|
+
params = { 'action' => 'provision', 'platform' => args[:platform], 'inventory' => Dir.pwd }
|
117
|
+
results = run_task("provision::#{args[:provisioner]}", 'localhost', params, config: config_data, inventory: nil)
|
118
|
+
results.each do |result|
|
119
|
+
if result['status'] != 'success'
|
120
|
+
puts "Failed #{result['node']}\n#{result}"
|
121
|
+
else
|
122
|
+
puts "#{result['result']['node_name']}, #{args[:platform]}"
|
132
123
|
end
|
133
|
-
else
|
134
|
-
raise "Unknown provisioner '#{args[:provisioner]}', try abs/docker/vmpooler"
|
135
124
|
end
|
136
125
|
end
|
137
126
|
|
@@ -218,7 +207,7 @@ namespace :litmus do
|
|
218
207
|
targets.each do |node_name|
|
219
208
|
# how do we know what provisioner to use
|
220
209
|
node_facts = facts_from_node(inventory_hash, node_name)
|
221
|
-
next unless %w[abs docker vmpooler].include?(node_facts['provisioner'])
|
210
|
+
next unless %w[abs docker vmpooler vagrant].include?(node_facts['provisioner'])
|
222
211
|
|
223
212
|
params = { 'action' => 'tear_down', 'node_name' => node_name, 'inventory' => Dir.pwd }
|
224
213
|
result = run_task("provision::#{node_facts['provisioner']}", 'localhost', params, config: config_data, inventory: nil)
|
@@ -240,32 +229,66 @@ namespace :litmus do
|
|
240
229
|
include PuppetLitmus::InventoryManipulation
|
241
230
|
if File.file?('inventory.yaml')
|
242
231
|
inventory_hash = inventory_hash_from_inventory_file
|
243
|
-
|
232
|
+
targets = find_targets(inventory_hash, nil)
|
244
233
|
|
245
234
|
desc 'Run tests in parallel against all machines in the inventory file'
|
246
235
|
task :parallel do
|
247
|
-
|
248
|
-
|
249
|
-
|
236
|
+
spinners = TTY::Spinner::Multi.new("Running against #{targets.size} targets.[:spinner]", frames: ['.'], interval: 0.1)
|
237
|
+
payloads = []
|
238
|
+
targets.each do |target|
|
239
|
+
test = "TARGET_HOST=#{target} bundle exec rspec ./spec/acceptance --format progress"
|
240
|
+
title = "#{target}, #{facts_from_node(inventory_hash, target)['platform']}"
|
241
|
+
payloads << [title, test]
|
250
242
|
end
|
251
|
-
|
252
|
-
|
253
|
-
|
243
|
+
|
244
|
+
results = []
|
245
|
+
success_list = []
|
246
|
+
failure_list = []
|
247
|
+
if (ENV['CI'] == 'true') || !ENV['DISTELLI_BUILDNUM'].nil?
|
248
|
+
# CI systems are strange beasts, we only output a '.' every wee while to keep the terminal alive.
|
249
|
+
puts "Running against #{targets.size} targets.\n"
|
250
|
+
spinner = TTY::Spinner.new(':spinner', frames: ['.'], interval: 0.1)
|
251
|
+
spinner.auto_spin
|
252
|
+
results = Parallel.map(payloads) do |title, test|
|
253
|
+
stdout, stderr, status = Open3.capture3(test)
|
254
|
+
["================\n#{title}\n", stdout, stderr, status]
|
255
|
+
end
|
256
|
+
spinner.success
|
257
|
+
else
|
258
|
+
spinners = TTY::Spinner::Multi.new("[:spinner] Running against #{targets.size} targets.")
|
259
|
+
payloads.each do |title, test|
|
260
|
+
spinners.register("[:spinner] #{title}") do |sp|
|
261
|
+
stdout, stderr, status = Open3.capture3(test)
|
262
|
+
if status.to_i.zero?
|
263
|
+
sp.success
|
264
|
+
success_list.push(title)
|
265
|
+
else
|
266
|
+
sp.error
|
267
|
+
failure_list.push(title)
|
268
|
+
end
|
269
|
+
results.push(["================\n#{title}\n", stdout, stderr, status])
|
270
|
+
end
|
271
|
+
end
|
272
|
+
spinners.auto_spin
|
273
|
+
spinners.success
|
254
274
|
end
|
255
|
-
|
256
|
-
|
275
|
+
|
276
|
+
# output test results
|
257
277
|
results.each do |result|
|
258
|
-
failures = true unless result.last.exitstatus.zero?
|
259
278
|
puts result
|
260
279
|
end
|
261
|
-
|
280
|
+
|
281
|
+
# output test summary
|
282
|
+
puts "Successful on #{success_list.size} nodes: #{success_list}" if success_list.any?
|
283
|
+
puts "Failed on #{failure_list.size} nodes: #{failure_list}" if failure_list.any?
|
284
|
+
exit 1 if failure_list.any?
|
262
285
|
end
|
263
286
|
|
264
|
-
|
265
|
-
desc "Run serverspec against #{
|
266
|
-
RSpec::Core::RakeTask.new(
|
287
|
+
targets.each do |target|
|
288
|
+
desc "Run serverspec against #{target}"
|
289
|
+
RSpec::Core::RakeTask.new(target.to_sym) do |t|
|
267
290
|
t.pattern = 'spec/acceptance/**{,/*/**}/*_spec.rb'
|
268
|
-
ENV['TARGET_HOST'] =
|
291
|
+
ENV['TARGET_HOST'] = target
|
269
292
|
end
|
270
293
|
end
|
271
294
|
end
|
@@ -1,14 +1,33 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# helper functions for running puppet commands
|
3
|
+
# helper functions for running puppet commands. They execute a target system specified by ENV['TARGET_HOST']
|
4
|
+
# heavily uses functions from here https://github.com/puppetlabs/bolt/blob/master/developer-docs/bolt_spec-run.md
|
4
5
|
module PuppetLitmus::Serverspec
|
6
|
+
# Applies a manifest twice. First checking for errors. Secondly to make sure no changes occur.
|
7
|
+
#
|
8
|
+
# @param manifest [String] puppet manifest code to be applied.
|
9
|
+
# @return [Boolean] The result of the 2 apply manifests.
|
5
10
|
def idempotent_apply(manifest)
|
6
11
|
manifest_file_location = create_manifest_file(manifest)
|
7
12
|
apply_manifest(nil, catch_failures: true, manifest_file_location: manifest_file_location)
|
8
13
|
apply_manifest(nil, catch_changes: true, manifest_file_location: manifest_file_location)
|
9
14
|
end
|
10
15
|
|
16
|
+
# rubocop:disable Layout/TrailingWhitespace
|
17
|
+
|
18
|
+
# Applies a manifest. returning the result of that apply. Mimics the apply_manifest from beaker
|
19
|
+
#
|
20
|
+
# @param manifest [String] puppet manifest code to be applied.
|
21
|
+
# @param opts [Hash] Alters the behaviour of the command. Valid options are:
|
22
|
+
# :catch_changes [Boolean] exit status of 1 if there were changes.
|
23
|
+
# :expect_failures [Boolean] doesnt return an exit code of non-zero if the apply failed.
|
24
|
+
# :manifest_file_location [Path] The place on the target system.
|
25
|
+
# :prefix_command [String] prefixes the puppet apply command; eg "export LANGUAGE='ja'".
|
26
|
+
# :debug [Boolean] run puppet apply with the debug flag.
|
27
|
+
# @param [Block] his method will yield to a block of code passed by the caller; this can be used for additional validation, etc.
|
28
|
+
# @return [Object] A result object from the apply.
|
11
29
|
def apply_manifest(manifest, opts = {})
|
30
|
+
# rubocop:enable Layout/TrailingWhitespace
|
12
31
|
target_node_name = ENV['TARGET_HOST']
|
13
32
|
raise 'manifest and manifest_file_location in the opts hash are mutually exclusive arguments, pick one' if !manifest.nil? && !opts[:manifest_file_location].nil?
|
14
33
|
raise 'please pass a manifest or the manifest_file_location in the opts hash' if (manifest.nil? || manifest == '') && opts[:manifest_file_location].nil?
|
@@ -19,9 +38,10 @@ module PuppetLitmus::Serverspec
|
|
19
38
|
else
|
20
39
|
inventory_hash_from_inventory_file
|
21
40
|
end
|
22
|
-
command_to_run = "puppet apply #{manifest_file_location}"
|
41
|
+
command_to_run = "#{opts[:prefix_command]} puppet apply #{manifest_file_location}"
|
23
42
|
command_to_run += " --modulepath #{Dir.pwd}/spec/fixtures/modules" if target_node_name.nil? || target_node_name == 'localhost'
|
24
43
|
command_to_run += ' --detailed-exitcodes' if !opts[:catch_changes].nil? && (opts[:catch_changes] == true)
|
44
|
+
command_to_run += ' --debug' if !opts[:debug].nil? && (opts[:debug] == true)
|
25
45
|
# BOLT-608
|
26
46
|
if Gem.win_platform?
|
27
47
|
stdout, stderr, status = Open3.capture3(command_to_run)
|
@@ -35,13 +55,22 @@ module PuppetLitmus::Serverspec
|
|
35
55
|
result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
|
36
56
|
end
|
37
57
|
|
38
|
-
raise "apply
|
58
|
+
raise "apply manifest failed\n`#{command_to_run}`\n======\n#{result}" if result.first['result']['exit_code'] != 0 && opts[:expect_failures] != true
|
39
59
|
|
40
|
-
result.first
|
60
|
+
result = OpenStruct.new(exit_code: result.first['result']['exit_code'],
|
61
|
+
stdout: result.first['result']['stdout'],
|
62
|
+
stderr: result.first['result']['stderr'])
|
63
|
+
yield result if block_given?
|
64
|
+
result
|
41
65
|
end
|
42
66
|
|
43
|
-
#
|
67
|
+
# Creates a manifest file locally, if running against localhost create in a temp location. Or create it on the target system.
|
68
|
+
#
|
69
|
+
# @param manifest [String] puppet manifest code.
|
70
|
+
# @return [String] The path to the location of the manifest.
|
44
71
|
def create_manifest_file(manifest)
|
72
|
+
require 'tempfile'
|
73
|
+
|
45
74
|
target_node_name = ENV['TARGET_HOST']
|
46
75
|
manifest_file = Tempfile.new(['manifest_', '.pp'])
|
47
76
|
manifest_file.write(manifest)
|
@@ -59,6 +88,12 @@ module PuppetLitmus::Serverspec
|
|
59
88
|
manifest_file_location
|
60
89
|
end
|
61
90
|
|
91
|
+
# Runs a command against the target system
|
92
|
+
#
|
93
|
+
# @param command_to_run [String] The command to execute.
|
94
|
+
# @param opts [Hash] Alters the behaviour of the command. Valid options are :expect_failures [Boolean] doesnt return an exit code of non-zero if the command failed.
|
95
|
+
# @param [Block] his method will yield to a block of code passed by the caller; this can be used for additional validation, etc.
|
96
|
+
# @return [Object] A result object from the command.
|
62
97
|
def run_shell(command_to_run, opts = {})
|
63
98
|
inventory_hash = inventory_hash_from_inventory_file
|
64
99
|
target_node_name = ENV['TARGET_HOST']
|
@@ -66,10 +101,18 @@ module PuppetLitmus::Serverspec
|
|
66
101
|
|
67
102
|
raise "shell failed\n`#{command_to_run}`\n======\n#{result}" if result.first['result']['exit_code'] != 0 && opts[:expect_failures] != true
|
68
103
|
|
104
|
+
result = OpenStruct.new(exit_code: result.first['result']['exit_code'],
|
105
|
+
stdout: result.first['result']['stdout'],
|
106
|
+
stderr: result.first['result']['stderr'])
|
107
|
+
yield result if block_given?
|
69
108
|
result
|
70
109
|
end
|
71
110
|
|
72
|
-
# Runs a
|
111
|
+
# Runs a task against the target system.
|
112
|
+
#
|
113
|
+
# @param task_name [String] The name of the task to run.
|
114
|
+
# @param params [Hash] key : value pairs to be passed to the task.
|
115
|
+
# @return [Object] A result object from the task.
|
73
116
|
def run_bolt_task(task_name, params = {})
|
74
117
|
config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
|
75
118
|
inventory_hash = inventory_hash_from_inventory_file
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puppet_litmus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.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: 2019-
|
11
|
+
date: 2019-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bolt
|
@@ -50,6 +50,26 @@ dependencies:
|
|
50
50
|
- - "<"
|
51
51
|
- !ruby/object:Gem::Version
|
52
52
|
version: 2.0.0
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: tty-spinner
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 0.5.0
|
60
|
+
- - "<"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 1.0.0
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 0.5.0
|
70
|
+
- - "<"
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 1.0.0
|
53
73
|
description: " Providing a simple command line tool for puppet content creators,
|
54
74
|
to enable simple and complex test deployments.\n"
|
55
75
|
email:
|