puppet_litmus 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/puppet_litmus/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:
|