puppet_litmus 0.7.0 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +201 -201
- data/README.md +35 -35
- data/lib/puppet_litmus.rb +15 -15
- data/lib/puppet_litmus/inventory_manipulation.rb +159 -159
- data/lib/puppet_litmus/rake_tasks.rb +445 -445
- data/lib/puppet_litmus/serverspec.rb +203 -189
- data/lib/puppet_litmus/version.rb +6 -6
- data/spec/data/inventory.yaml +16 -16
- data/spec/lib/puppet_litmus/inventory_manipulation_spec.rb +102 -102
- data/spec/lib/puppet_litmus/rake_tasks_spec.rb +55 -55
- data/spec/lib/puppet_litmus/serverspec_spec.rb +196 -150
- data/spec/lib/puppet_litmus/version_spec.rb +10 -10
- data/spec/spec_helper.rb +29 -29
- metadata +3 -3
data/lib/puppet_litmus.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Helper methods for testing puppet content
|
4
|
-
module PuppetLitmus; end
|
5
|
-
|
6
|
-
require 'bolt_spec/run'
|
7
|
-
require 'puppet_litmus/inventory_manipulation'
|
8
|
-
require 'puppet_litmus/serverspec'
|
9
|
-
|
10
|
-
# Helper methods for testing puppet content
|
11
|
-
module PuppetLitmus
|
12
|
-
include BoltSpec::Run
|
13
|
-
include PuppetLitmus::InventoryManipulation
|
14
|
-
include PuppetLitmus::Serverspec
|
15
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Helper methods for testing puppet content
|
4
|
+
module PuppetLitmus; end
|
5
|
+
|
6
|
+
require 'bolt_spec/run'
|
7
|
+
require 'puppet_litmus/inventory_manipulation'
|
8
|
+
require 'puppet_litmus/serverspec'
|
9
|
+
|
10
|
+
# Helper methods for testing puppet content
|
11
|
+
module PuppetLitmus
|
12
|
+
include BoltSpec::Run
|
13
|
+
include PuppetLitmus::InventoryManipulation
|
14
|
+
include PuppetLitmus::Serverspec
|
15
|
+
end
|
@@ -1,159 +1,159 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# helper functions for manipulating and reading a bolt inventory file
|
4
|
-
module PuppetLitmus::InventoryManipulation
|
5
|
-
# Creates an inventory hash from the inventory.yaml.
|
6
|
-
#
|
7
|
-
# @param inventory_full_path [String] path to the inventory.yaml file
|
8
|
-
# @return [Hash] hash of the inventory.yaml file.
|
9
|
-
def inventory_hash_from_inventory_file(inventory_full_path = nil)
|
10
|
-
inventory_full_path = if inventory_full_path.nil?
|
11
|
-
'inventory.yaml'
|
12
|
-
else
|
13
|
-
inventory_full_path
|
14
|
-
end
|
15
|
-
raise "There is no inventory file at '#{inventory_full_path}'" unless File.exist?(inventory_full_path)
|
16
|
-
|
17
|
-
inventory_hash = YAML.load_file(inventory_full_path)
|
18
|
-
inventory_hash
|
19
|
-
end
|
20
|
-
|
21
|
-
# Finds targets to perform operations on from an inventory hash.
|
22
|
-
#
|
23
|
-
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
24
|
-
# @param targets [Array]
|
25
|
-
# @return [Array] array of targets.
|
26
|
-
def find_targets(inventory_hash, targets)
|
27
|
-
if targets.nil?
|
28
|
-
inventory = Bolt::Inventory.new(inventory_hash, nil)
|
29
|
-
targets = inventory.node_names.to_a
|
30
|
-
else
|
31
|
-
targets = [targets]
|
32
|
-
end
|
33
|
-
targets
|
34
|
-
end
|
35
|
-
|
36
|
-
# Determines if a node_name exists in a group in the inventory_hash.
|
37
|
-
#
|
38
|
-
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
39
|
-
# @param node_name [String] node to locate in the group
|
40
|
-
# @param group_name [String] group of nodes to limit the search for the node_name in
|
41
|
-
# @return [Boolean] true if node_name exists in group_name.
|
42
|
-
def target_in_group(inventory_hash, node_name, group_name)
|
43
|
-
exists = false
|
44
|
-
inventory_hash['groups'].each do |group|
|
45
|
-
next unless group['name'] == group_name
|
46
|
-
|
47
|
-
group['nodes'].each do |node|
|
48
|
-
exists = true if node['name'] == node_name
|
49
|
-
end
|
50
|
-
end
|
51
|
-
exists
|
52
|
-
end
|
53
|
-
|
54
|
-
# Finds a config hash in the inventory hash by searching for a node name.
|
55
|
-
#
|
56
|
-
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
57
|
-
# @param node_name [String] node to locate in the group
|
58
|
-
# @return [Hash] config for node of name node_name
|
59
|
-
def config_from_node(inventory_hash, node_name)
|
60
|
-
inventory_hash['groups'].each do |group|
|
61
|
-
group['nodes'].each do |node|
|
62
|
-
if node['name'] == node_name
|
63
|
-
return node['config']
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
raise "No config was found for #{node_name}"
|
68
|
-
end
|
69
|
-
|
70
|
-
# Finds a facts hash in the inventory hash by searching for a node name.
|
71
|
-
#
|
72
|
-
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
73
|
-
# @param node_name [String] node to locate in the group
|
74
|
-
# @return [Hash] facts for node of name node_name
|
75
|
-
def facts_from_node(inventory_hash, node_name)
|
76
|
-
inventory_hash['groups'].each do |group|
|
77
|
-
group['nodes'].each do |node|
|
78
|
-
if node['name'] == node_name
|
79
|
-
return node['facts']
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
raise "No facts were found for #{node_name}"
|
84
|
-
end
|
85
|
-
|
86
|
-
# Adds a node to a group specified, if group_name exists in inventory hash.
|
87
|
-
#
|
88
|
-
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
89
|
-
# @param node_name [String] node to locate in the group
|
90
|
-
# group_name [String] group of nodes to limit the search for the node_name in
|
91
|
-
# @return [Hash] inventory_hash with node added to group if group_name exists in inventory hash.
|
92
|
-
def add_node_to_group(inventory_hash, node_name, group_name)
|
93
|
-
inventory_hash['groups'].each do |group|
|
94
|
-
if group['name'] == group_name
|
95
|
-
group['nodes'].push node_name
|
96
|
-
end
|
97
|
-
end
|
98
|
-
inventory_hash
|
99
|
-
end
|
100
|
-
|
101
|
-
# Removes named node from a group inside an inventory_hash.
|
102
|
-
#
|
103
|
-
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
104
|
-
# @param node_name [String] node to locate in the group
|
105
|
-
# @return [Hash] inventory_hash with node of node_name removed.
|
106
|
-
def remove_node(inventory_hash, node_name)
|
107
|
-
inventory_hash['groups'].each do |group|
|
108
|
-
group['nodes'].delete_if { |i| i['name'] == node_name }
|
109
|
-
end
|
110
|
-
inventory_hash
|
111
|
-
end
|
112
|
-
|
113
|
-
# Adds a feature to the group specified/
|
114
|
-
#
|
115
|
-
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
116
|
-
# @param feature_name [String] feature to locate in the group
|
117
|
-
# group_name [String] group of nodes to limit the search for the group_name in
|
118
|
-
# @return inventory.yaml file with feature added to group.
|
119
|
-
# @return [Hash] inventory_hash with feature added to group if group_name exists in inventory hash.
|
120
|
-
def add_feature_to_group(inventory_hash, feature_name, group_name)
|
121
|
-
i = 0
|
122
|
-
inventory_hash['groups'].each do |group|
|
123
|
-
if group['name'] == group_name
|
124
|
-
if group['features'].nil? == true
|
125
|
-
group = group.merge('features' => [])
|
126
|
-
end
|
127
|
-
group['features'].push feature_name
|
128
|
-
inventory_hash['groups'][i] = group
|
129
|
-
end
|
130
|
-
i += 1
|
131
|
-
end
|
132
|
-
inventory_hash
|
133
|
-
end
|
134
|
-
|
135
|
-
# Removes a feature from the group specified/
|
136
|
-
#
|
137
|
-
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
138
|
-
# @param feature_name [String] feature to locate in the group
|
139
|
-
# group_name [String] group of nodes to limit the search for the group_name in
|
140
|
-
# @return inventory.yaml file with feature removed from the group.
|
141
|
-
# @return [Hash] inventory_hash with feature added to group if group_name exists in inventory hash.
|
142
|
-
def remove_feature_from_group(inventory_hash, feature_name, group_name)
|
143
|
-
inventory_hash['groups'].each do |group|
|
144
|
-
if group['name'] == group_name && group['features'].nil? != true
|
145
|
-
group['features'].delete(feature_name)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
inventory_hash
|
149
|
-
end
|
150
|
-
|
151
|
-
# Write inventory_hash to inventory_yaml file/
|
152
|
-
#
|
153
|
-
# @param inventory_full_path [String] path to the inventory.yaml file
|
154
|
-
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
155
|
-
# @return inventory.yaml file with feature added to group.
|
156
|
-
def write_to_inventory_file(inventory_hash, inventory_full_path)
|
157
|
-
File.open(inventory_full_path, 'w+') { |f| f.write(inventory_hash.to_yaml) }
|
158
|
-
end
|
159
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# helper functions for manipulating and reading a bolt inventory file
|
4
|
+
module PuppetLitmus::InventoryManipulation
|
5
|
+
# Creates an inventory hash from the inventory.yaml.
|
6
|
+
#
|
7
|
+
# @param inventory_full_path [String] path to the inventory.yaml file
|
8
|
+
# @return [Hash] hash of the inventory.yaml file.
|
9
|
+
def inventory_hash_from_inventory_file(inventory_full_path = nil)
|
10
|
+
inventory_full_path = if inventory_full_path.nil?
|
11
|
+
'inventory.yaml'
|
12
|
+
else
|
13
|
+
inventory_full_path
|
14
|
+
end
|
15
|
+
raise "There is no inventory file at '#{inventory_full_path}'" unless File.exist?(inventory_full_path)
|
16
|
+
|
17
|
+
inventory_hash = YAML.load_file(inventory_full_path)
|
18
|
+
inventory_hash
|
19
|
+
end
|
20
|
+
|
21
|
+
# Finds targets to perform operations on from an inventory hash.
|
22
|
+
#
|
23
|
+
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
24
|
+
# @param targets [Array]
|
25
|
+
# @return [Array] array of targets.
|
26
|
+
def find_targets(inventory_hash, targets)
|
27
|
+
if targets.nil?
|
28
|
+
inventory = Bolt::Inventory.new(inventory_hash, nil)
|
29
|
+
targets = inventory.node_names.to_a
|
30
|
+
else
|
31
|
+
targets = [targets]
|
32
|
+
end
|
33
|
+
targets
|
34
|
+
end
|
35
|
+
|
36
|
+
# Determines if a node_name exists in a group in the inventory_hash.
|
37
|
+
#
|
38
|
+
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
39
|
+
# @param node_name [String] node to locate in the group
|
40
|
+
# @param group_name [String] group of nodes to limit the search for the node_name in
|
41
|
+
# @return [Boolean] true if node_name exists in group_name.
|
42
|
+
def target_in_group(inventory_hash, node_name, group_name)
|
43
|
+
exists = false
|
44
|
+
inventory_hash['groups'].each do |group|
|
45
|
+
next unless group['name'] == group_name
|
46
|
+
|
47
|
+
group['nodes'].each do |node|
|
48
|
+
exists = true if node['name'] == node_name
|
49
|
+
end
|
50
|
+
end
|
51
|
+
exists
|
52
|
+
end
|
53
|
+
|
54
|
+
# Finds a config hash in the inventory hash by searching for a node name.
|
55
|
+
#
|
56
|
+
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
57
|
+
# @param node_name [String] node to locate in the group
|
58
|
+
# @return [Hash] config for node of name node_name
|
59
|
+
def config_from_node(inventory_hash, node_name)
|
60
|
+
inventory_hash['groups'].each do |group|
|
61
|
+
group['nodes'].each do |node|
|
62
|
+
if node['name'] == node_name
|
63
|
+
return node['config']
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
raise "No config was found for #{node_name}"
|
68
|
+
end
|
69
|
+
|
70
|
+
# Finds a facts hash in the inventory hash by searching for a node name.
|
71
|
+
#
|
72
|
+
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
73
|
+
# @param node_name [String] node to locate in the group
|
74
|
+
# @return [Hash] facts for node of name node_name
|
75
|
+
def facts_from_node(inventory_hash, node_name)
|
76
|
+
inventory_hash['groups'].each do |group|
|
77
|
+
group['nodes'].each do |node|
|
78
|
+
if node['name'] == node_name
|
79
|
+
return node['facts']
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
raise "No facts were found for #{node_name}"
|
84
|
+
end
|
85
|
+
|
86
|
+
# Adds a node to a group specified, if group_name exists in inventory hash.
|
87
|
+
#
|
88
|
+
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
89
|
+
# @param node_name [String] node to locate in the group
|
90
|
+
# group_name [String] group of nodes to limit the search for the node_name in
|
91
|
+
# @return [Hash] inventory_hash with node added to group if group_name exists in inventory hash.
|
92
|
+
def add_node_to_group(inventory_hash, node_name, group_name)
|
93
|
+
inventory_hash['groups'].each do |group|
|
94
|
+
if group['name'] == group_name
|
95
|
+
group['nodes'].push node_name
|
96
|
+
end
|
97
|
+
end
|
98
|
+
inventory_hash
|
99
|
+
end
|
100
|
+
|
101
|
+
# Removes named node from a group inside an inventory_hash.
|
102
|
+
#
|
103
|
+
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
104
|
+
# @param node_name [String] node to locate in the group
|
105
|
+
# @return [Hash] inventory_hash with node of node_name removed.
|
106
|
+
def remove_node(inventory_hash, node_name)
|
107
|
+
inventory_hash['groups'].each do |group|
|
108
|
+
group['nodes'].delete_if { |i| i['name'] == node_name }
|
109
|
+
end
|
110
|
+
inventory_hash
|
111
|
+
end
|
112
|
+
|
113
|
+
# Adds a feature to the group specified/
|
114
|
+
#
|
115
|
+
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
116
|
+
# @param feature_name [String] feature to locate in the group
|
117
|
+
# group_name [String] group of nodes to limit the search for the group_name in
|
118
|
+
# @return inventory.yaml file with feature added to group.
|
119
|
+
# @return [Hash] inventory_hash with feature added to group if group_name exists in inventory hash.
|
120
|
+
def add_feature_to_group(inventory_hash, feature_name, group_name)
|
121
|
+
i = 0
|
122
|
+
inventory_hash['groups'].each do |group|
|
123
|
+
if group['name'] == group_name
|
124
|
+
if group['features'].nil? == true
|
125
|
+
group = group.merge('features' => [])
|
126
|
+
end
|
127
|
+
group['features'].push feature_name unless group['features'].include?(feature_name)
|
128
|
+
inventory_hash['groups'][i] = group
|
129
|
+
end
|
130
|
+
i += 1
|
131
|
+
end
|
132
|
+
inventory_hash
|
133
|
+
end
|
134
|
+
|
135
|
+
# Removes a feature from the group specified/
|
136
|
+
#
|
137
|
+
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
138
|
+
# @param feature_name [String] feature to locate in the group
|
139
|
+
# group_name [String] group of nodes to limit the search for the group_name in
|
140
|
+
# @return inventory.yaml file with feature removed from the group.
|
141
|
+
# @return [Hash] inventory_hash with feature added to group if group_name exists in inventory hash.
|
142
|
+
def remove_feature_from_group(inventory_hash, feature_name, group_name)
|
143
|
+
inventory_hash['groups'].each do |group|
|
144
|
+
if group['name'] == group_name && group['features'].nil? != true
|
145
|
+
group['features'].delete(feature_name)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
inventory_hash
|
149
|
+
end
|
150
|
+
|
151
|
+
# Write inventory_hash to inventory_yaml file/
|
152
|
+
#
|
153
|
+
# @param inventory_full_path [String] path to the inventory.yaml file
|
154
|
+
# @param inventory_hash [Hash] hash of the inventory.yaml file
|
155
|
+
# @return inventory.yaml file with feature added to group.
|
156
|
+
def write_to_inventory_file(inventory_hash, inventory_full_path)
|
157
|
+
File.open(inventory_full_path, 'w+') { |f| f.write(inventory_hash.to_yaml) }
|
158
|
+
end
|
159
|
+
end
|
@@ -1,445 +1,445 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'rspec/core/rake_task'
|
4
|
-
require 'puppet_litmus'
|
5
|
-
require 'bolt_spec/run'
|
6
|
-
require 'open3'
|
7
|
-
require 'pdk'
|
8
|
-
require 'json'
|
9
|
-
require 'parallel'
|
10
|
-
|
11
|
-
# helper methods for the litmus rake tasks
|
12
|
-
module LitmusRakeHelper
|
13
|
-
# Gets a string representing the operating system and version.
|
14
|
-
#
|
15
|
-
# @param metadata [Hash] metadata to parse for operating system info
|
16
|
-
# @return [String] the operating system string with version info for use in provisioning.
|
17
|
-
def get_metadata_operating_systems(metadata)
|
18
|
-
return unless metadata.is_a?(Hash)
|
19
|
-
return unless metadata['operatingsystem_support'].is_a?(Array)
|
20
|
-
|
21
|
-
metadata['operatingsystem_support'].each do |os_info|
|
22
|
-
next unless os_info['operatingsystem'] && os_info['operatingsystemrelease']
|
23
|
-
|
24
|
-
os_name = case os_info['operatingsystem']
|
25
|
-
when 'Amazon', 'Archlinux', 'AIX', 'OSX'
|
26
|
-
next
|
27
|
-
when 'OracleLinux'
|
28
|
-
'oracle'
|
29
|
-
when 'Windows'
|
30
|
-
'win'
|
31
|
-
else
|
32
|
-
os_info['operatingsystem'].downcase
|
33
|
-
end
|
34
|
-
|
35
|
-
os_info['operatingsystemrelease'].each do |release|
|
36
|
-
version = case os_name
|
37
|
-
when 'ubuntu', 'osx'
|
38
|
-
release.sub('.', '')
|
39
|
-
when 'sles'
|
40
|
-
release.gsub(%r{ SP[14]}, '')
|
41
|
-
when 'win'
|
42
|
-
release = release.delete('.') if release.include? '8.1'
|
43
|
-
release.sub('Server', '').sub('10', '10-pro')
|
44
|
-
else
|
45
|
-
release
|
46
|
-
end
|
47
|
-
|
48
|
-
yield "#{os_name}-#{version.downcase}-x86_64".delete(' ')
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# Executes a command on the test runner.
|
54
|
-
#
|
55
|
-
# @param command [String] command to execute.
|
56
|
-
# @return [Object] the standard out stream.
|
57
|
-
def run_local_command(command)
|
58
|
-
stdout, stderr, status = Open3.capture3(command)
|
59
|
-
error_message = "Attempted to run\ncommand:'#{command}'\nstdout:#{stdout}\nstderr:#{stderr}"
|
60
|
-
raise error_message unless status.to_i.zero?
|
61
|
-
|
62
|
-
stdout
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
namespace :litmus do
|
67
|
-
include LitmusRakeHelper
|
68
|
-
# Prints all supported OSes from metadata.json file.
|
69
|
-
desc 'print all supported OSes from metadata'
|
70
|
-
task :metadata do
|
71
|
-
metadata = JSON.parse(File.read('metadata.json'))
|
72
|
-
get_metadata_operating_systems(metadata) do |os_and_version|
|
73
|
-
puts os_and_version
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
# DEPRECATED - Provisions all supported OSes with provisioner eg 'bundle exec rake litmus:provision_from_metadata['vmpooler']'.
|
78
|
-
#
|
79
|
-
# @param :provisioner [String] provisioner to use in provisioning all OSes.
|
80
|
-
desc "DEPRECATED: provision_from_metadata task is deprecated.
|
81
|
-
Provision all supported OSes with provisioner eg 'bundle exec rake 'litmus:provision_from_metadata'"
|
82
|
-
task :provision_from_metadata, [:provisioner] do |_task, args|
|
83
|
-
metadata = JSON.parse(File.read('metadata.json'))
|
84
|
-
get_metadata_operating_systems(metadata) do |os_and_version|
|
85
|
-
puts os_and_version
|
86
|
-
include BoltSpec::Run
|
87
|
-
Rake::Task['spec_prep'].invoke
|
88
|
-
config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
|
89
|
-
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'))
|
90
|
-
|
91
|
-
params = { 'action' => 'provision', 'platform' => os_and_version, 'inventory' => Dir.pwd }
|
92
|
-
results = run_task("provision::#{args[:provisioner]}", 'localhost', params, config: config_data, inventory: nil)
|
93
|
-
results.each do |result|
|
94
|
-
if result['status'] != 'success'
|
95
|
-
puts "Failed on #{result['node']}\n#{result}"
|
96
|
-
else
|
97
|
-
puts "Provisioned #{result['result']['node_name']}"
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
# Provisions a list of OSes from provision.yaml file e.g. 'bundle exec rake litmus:provision_list[default]'.
|
104
|
-
#
|
105
|
-
# @param :key [String] key that maps to a value for a provisioner and an image to be used for each OS provisioned.
|
106
|
-
desc "provision list of machines from provision.yaml file. 'bundle exec rake 'litmus:provision_list[default]'"
|
107
|
-
task :provision_list, [:key] do |_task, args|
|
108
|
-
provision_hash = YAML.load_file('./provision.yaml')
|
109
|
-
provisioner = provision_hash[args[:key]]['provisioner']
|
110
|
-
# Splat the params into environment variables to pass to the provision task but only in this runspace
|
111
|
-
provision_hash[args[:key]]['params']&.each { |key, value| ENV["LITMUS_#{key.upcase}"] = value }
|
112
|
-
failed_image_message = ''
|
113
|
-
provision_hash[args[:key]]['images'].each do |image|
|
114
|
-
# this is the only way to capture the stdout from the rake task, it will affect pry
|
115
|
-
capture_rake_output = StringIO.new
|
116
|
-
$stdout = capture_rake_output
|
117
|
-
Rake::Task['litmus:provision'].invoke(provisioner, image)
|
118
|
-
if $stdout.string =~ %r{.status.=>.failure}
|
119
|
-
failed_image_message += "=====\n#{image}\n#{$stdout.string}\n"
|
120
|
-
else
|
121
|
-
STDOUT.puts $stdout.string
|
122
|
-
end
|
123
|
-
Rake::Task['litmus:provision'].reenable
|
124
|
-
end
|
125
|
-
raise "Failed to provision with '#{provisioner}'\n #{failed_image_message}" unless failed_image_message.empty?
|
126
|
-
end
|
127
|
-
|
128
|
-
# Provision a container or VM with a given platform 'bundle exec rake 'litmus:provision[vmpooler, ubuntu-1604-x86_64]'.
|
129
|
-
#
|
130
|
-
# @param :provisioner [String] provisioner to use in provisioning given platform.
|
131
|
-
# @param :platform [String] OS platform for container or VM to use.
|
132
|
-
desc "provision container/VM - abs/docker/vagrant/vmpooler eg 'bundle exec rake 'litmus:provision[vmpooler, ubuntu-1604-x86_64]'"
|
133
|
-
task :provision, [:provisioner, :platform] do |_task, args|
|
134
|
-
include BoltSpec::Run
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
results = run_task("provision::#{args[:provisioner]}", 'localhost', params, config: config_data, inventory: nil)
|
151
|
-
if results.first['status'] != 'success'
|
152
|
-
spinner.error
|
153
|
-
raise "Failed provisioning #{args[:platform]} using #{args[:provisioner]}\n#{results.first}"
|
154
|
-
end
|
155
|
-
|
156
|
-
spinner.success
|
157
|
-
puts "#{results.first['result']['node_name']}, #{args[:platform]}"
|
158
|
-
end
|
159
|
-
|
160
|
-
# Install puppet agent on a collection of nodes
|
161
|
-
#
|
162
|
-
# @param :collection [String] parameters to pass to the puppet agent install command.
|
163
|
-
# @param :target_node_name [Array] nodes on which to install puppet agent.
|
164
|
-
desc 'install puppet agent, [:collection, :target_node_name]'
|
165
|
-
task :install_agent, [:collection, :target_node_name] do |_task, args|
|
166
|
-
inventory_hash = inventory_hash_from_inventory_file
|
167
|
-
targets = find_targets(inventory_hash, args[:target_node_name])
|
168
|
-
if targets.empty?
|
169
|
-
puts 'No targets found'
|
170
|
-
exit 0
|
171
|
-
end
|
172
|
-
puts 'install_agent'
|
173
|
-
include BoltSpec::Run
|
174
|
-
Rake::Task['spec_prep'].invoke
|
175
|
-
config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
|
176
|
-
params = if args[:collection].nil?
|
177
|
-
{}
|
178
|
-
else
|
179
|
-
{ 'collection' => args[:collection] }
|
180
|
-
end
|
181
|
-
raise "puppet_agent was not found in #{config_data['modulepath']}, please amend the .fixtures.yml file" unless File.directory?(File.join(config_data['modulepath'], 'puppet_agent'))
|
182
|
-
|
183
|
-
results = run_task('puppet_agent::install', targets, params, config: config_data, inventory: inventory_hash)
|
184
|
-
results.each do |result|
|
185
|
-
if result['status'] != 'success'
|
186
|
-
command_to_run = "bolt task run puppet_agent::install --targets #{result['node']} --inventoryfile inventory.yaml --modulepath #{config_data['modulepath']}"
|
187
|
-
raise "Failed on #{result['node']}\n#{result}\ntry running '#{command_to_run}'"
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
# fix the path on ssh_nodes
|
192
|
-
results = run_command('echo PATH="$PATH:/opt/puppetlabs/puppet/bin" > /etc/environment', 'ssh_nodes', config: nil, inventory: inventory_hash) unless inventory_hash['groups'].select { |group| group['name'] == 'ssh_nodes' }.size.zero? # rubocop:disable Metrics/LineLength
|
193
|
-
results.each do |result|
|
194
|
-
if result['status'] != 'success'
|
195
|
-
puts "Failed on #{result['node']}\n#{result}"
|
196
|
-
end
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
# Install puppet enterprise - for internal puppet employees only - Requires an el7 provisioned machine - experimental feature [:target_node_name]'
|
201
|
-
#
|
202
|
-
# @param :target_node_name [Array] nodes on which to install puppet agent.
|
203
|
-
desc 'install puppet enterprise - for internal puppet employees only - Requires an el7 provisioned machine - experimental feature [:target_node_name]'
|
204
|
-
task :install_pe, [:target_node_name] do |_task, args|
|
205
|
-
inventory_hash = inventory_hash_from_inventory_file
|
206
|
-
target_nodes = find_targets(inventory_hash, args[:target_node_name])
|
207
|
-
if target_nodes.empty?
|
208
|
-
puts 'No targets found'
|
209
|
-
exit 0
|
210
|
-
end
|
211
|
-
puts 'install_pe'
|
212
|
-
include BoltSpec::Run
|
213
|
-
Rake::Task['spec_prep'].invoke
|
214
|
-
config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
|
215
|
-
|
216
|
-
puts 'Setting up parameters'
|
217
|
-
|
218
|
-
PE_RELEASE = 2019.0
|
219
|
-
pe_latest_cmd = "curl http://enterprise.delivery.puppetlabs.net/#{PE_RELEASE}/ci-ready/LATEST"
|
220
|
-
pe_latest = run_command(pe_latest_cmd, target_nodes, config: config_data, inventory: inventory_hash)
|
221
|
-
pe_latest_string = pe_latest[0]['result']['stdout'].delete("\n")
|
222
|
-
PE_FILE_NAME = "puppet-enterprise-#{pe_latest_string}-el-7-x86_64"
|
223
|
-
TAR_FILE = "#{PE_FILE_NAME}.tar"
|
224
|
-
DOWNLOAD_URL = "http://enterprise.delivery.puppetlabs.net/#{PE_RELEASE}/ci-ready/#{TAR_FILE}"
|
225
|
-
|
226
|
-
puts 'Initiating PE download'
|
227
|
-
|
228
|
-
# Download PE
|
229
|
-
download_pe_cmd = "wget -q #{DOWNLOAD_URL}"
|
230
|
-
run_command(download_pe_cmd, target_nodes, config: config_data, inventory: inventory_hash)
|
231
|
-
|
232
|
-
puts 'PE successfully downloaded, running installer (this may take 5 or so minutes, please be patient)'
|
233
|
-
|
234
|
-
# Install PE
|
235
|
-
untar_cmd = "tar xvf #{TAR_FILE}"
|
236
|
-
run_command(untar_cmd, target_nodes, config: config_data, inventory: inventory_hash)
|
237
|
-
puts run_command("cd #{PE_FILE_NAME} && 1 | ./puppet-enterprise-installer", target_nodes, config: nil, inventory: inventory_hash)[0]['result']['stdout']
|
238
|
-
|
239
|
-
puts 'Autosigning Certificates'
|
240
|
-
|
241
|
-
# Set Autosign
|
242
|
-
autosign_cmd = "echo 'autosign = true' >> /etc/puppetlabs/puppet/puppet.conf"
|
243
|
-
run_command(autosign_cmd, target_nodes, config: config_data, inventory: inventory_hash)
|
244
|
-
|
245
|
-
puts 'Finishing installation with a Puppet Agent run'
|
246
|
-
|
247
|
-
run_command('puppet agent -t', target_nodes, config: config_data, inventory: inventory_hash)
|
248
|
-
|
249
|
-
puts 'PE Installation is now complete'
|
250
|
-
end
|
251
|
-
|
252
|
-
# Install the puppet module under test on a collection of nodes
|
253
|
-
#
|
254
|
-
# @param :target_node_name [Array] nodes on which to install a puppet module for testing.
|
255
|
-
desc 'install_module - build and install module'
|
256
|
-
task :install_module, [:target_node_name] do |_task, args|
|
257
|
-
inventory_hash = inventory_hash_from_inventory_file
|
258
|
-
target_nodes = find_targets(inventory_hash, args[:target_node_name])
|
259
|
-
if target_nodes.empty?
|
260
|
-
puts 'No targets found'
|
261
|
-
exit 0
|
262
|
-
end
|
263
|
-
include BoltSpec::Run
|
264
|
-
# old cli_way
|
265
|
-
# pdk_build_command = 'bundle exec pdk build --force'
|
266
|
-
# stdout, stderr, _status = Open3.capture3(pdk_build_command)
|
267
|
-
# raise "Failed to run 'pdk_build_command',#{stdout} and #{stderr}" if (stderr =~ %r{completed successfully}).nil?
|
268
|
-
require 'pdk/module/build'
|
269
|
-
opts = {}
|
270
|
-
opts[:force] = true
|
271
|
-
builder = PDK::Module::Build.new(opts)
|
272
|
-
module_tar = builder.build
|
273
|
-
puts 'Built'
|
274
|
-
|
275
|
-
# module_tar = Dir.glob('pkg/*.tar.gz').max_by { |f| File.mtime(f) }
|
276
|
-
raise "Unable to find package in 'pkg/*.tar.gz'" if module_tar.nil?
|
277
|
-
|
278
|
-
target_string = if args[:target_node_name].nil?
|
279
|
-
'all'
|
280
|
-
else
|
281
|
-
args[:target_node_name]
|
282
|
-
end
|
283
|
-
run_local_command("bundle exec bolt file upload #{module_tar} /tmp/#{File.basename(module_tar)} --nodes #{target_string} --inventoryfile inventory.yaml")
|
284
|
-
install_module_command = "puppet module install /tmp/#{File.basename(module_tar)}"
|
285
|
-
result = run_command(install_module_command, target_nodes, config: nil, inventory: inventory_hash)
|
286
|
-
# rubocop:disable Style/GuardClause
|
287
|
-
if result.is_a?(Array)
|
288
|
-
result.each do |node|
|
289
|
-
puts "#{node['node']} failed #{node['result']}" if node['status'] != 'success'
|
290
|
-
end
|
291
|
-
else
|
292
|
-
raise "Failed trying to run '#{install_module_command}' against inventory."
|
293
|
-
end
|
294
|
-
# rubocop:enable Style/GuardClause
|
295
|
-
puts 'Installed'
|
296
|
-
end
|
297
|
-
|
298
|
-
# Provision a list of machines, install a puppet agent, and install the puppet module under test on a collection of nodes
|
299
|
-
#
|
300
|
-
# @param :key [String] key that maps to a value for a provisioner and an image to be used for each OS provisioned.
|
301
|
-
# @param :collection [String] parameters to pass to the puppet agent install command.
|
302
|
-
desc 'provision_install - provision a list of machines, install an agent, and the module.'
|
303
|
-
task :provision_install, [:key, :collection] do |_task, args|
|
304
|
-
Rake::Task['spec_prep'].invoke
|
305
|
-
Rake::Task['litmus:provision_list'].invoke(args[:key])
|
306
|
-
Rake::Task['litmus:install_agent'].invoke(args[:collection])
|
307
|
-
Rake::Task['litmus:install_module'].invoke
|
308
|
-
end
|
309
|
-
|
310
|
-
# Decommissions test machines.
|
311
|
-
#
|
312
|
-
# @param :target [Array] nodes to remove from test environemnt and decommission.
|
313
|
-
desc 'tear-down - decommission machines'
|
314
|
-
task :tear_down, [:target] do |_task, args|
|
315
|
-
inventory_hash = inventory_hash_from_inventory_file
|
316
|
-
targets = find_targets(inventory_hash, args[:target])
|
317
|
-
if targets.empty?
|
318
|
-
puts 'No targets found'
|
319
|
-
exit 0
|
320
|
-
end
|
321
|
-
include BoltSpec::Run
|
322
|
-
Rake::Task['spec_prep'].invoke
|
323
|
-
config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
|
324
|
-
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'))
|
325
|
-
|
326
|
-
bad_results = []
|
327
|
-
targets.each do |node_name|
|
328
|
-
# how do we know what provisioner to use
|
329
|
-
node_facts = facts_from_node(inventory_hash, node_name)
|
330
|
-
next unless %w[abs docker docker_exp vagrant vmpooler].include?(node_facts['provisioner'])
|
331
|
-
|
332
|
-
params = { 'action' => 'tear_down', 'node_name' => node_name, 'inventory' => Dir.pwd }
|
333
|
-
result = run_task("provision::#{node_facts['provisioner']}", 'localhost', params, config: config_data, inventory: nil)
|
334
|
-
if result.first['status'] != 'success'
|
335
|
-
bad_results << "#{node_name}, #{result.first['result']['_error']['msg']}"
|
336
|
-
else
|
337
|
-
print "#{node_name}, "
|
338
|
-
end
|
339
|
-
end
|
340
|
-
puts ''
|
341
|
-
# output the things that went wrong, after the successes
|
342
|
-
puts 'something went wrong:' unless bad_results.size.zero?
|
343
|
-
bad_results.each do |result|
|
344
|
-
puts result
|
345
|
-
end
|
346
|
-
end
|
347
|
-
|
348
|
-
namespace :acceptance do
|
349
|
-
include PuppetLitmus::InventoryManipulation
|
350
|
-
if File.file?('inventory.yaml')
|
351
|
-
inventory_hash = inventory_hash_from_inventory_file
|
352
|
-
targets = find_targets(inventory_hash, nil)
|
353
|
-
|
354
|
-
# Run acceptance tests against all machines in the inventory file in parallel.
|
355
|
-
desc 'Run tests in parallel against all machines in the inventory file'
|
356
|
-
task :parallel do
|
357
|
-
if targets.empty?
|
358
|
-
puts 'No targets found'
|
359
|
-
exit 0
|
360
|
-
end
|
361
|
-
spinners = TTY::Spinner::Multi.new("Running against #{targets.size} targets.[:spinner]", frames: ['.'], interval: 0.1)
|
362
|
-
payloads = []
|
363
|
-
# Generate list of targets to provision
|
364
|
-
targets.each do |target|
|
365
|
-
test = 'bundle exec bundle exec rspec ./spec/acceptance --format progress'
|
366
|
-
title = "#{target}, #{facts_from_node(inventory_hash, target)['platform']}"
|
367
|
-
options = {
|
368
|
-
env: {
|
369
|
-
'TARGET_HOST' => target,
|
370
|
-
},
|
371
|
-
}
|
372
|
-
payloads << [title, test, options]
|
373
|
-
end
|
374
|
-
|
375
|
-
results = []
|
376
|
-
success_list = []
|
377
|
-
failure_list = []
|
378
|
-
# Provision targets depending on what environment we're in
|
379
|
-
if (ENV['CI'] == 'true') || !ENV['DISTELLI_BUILDNUM'].nil?
|
380
|
-
# CI systems are strange beasts, we only output a '.' every wee while to keep the terminal alive.
|
381
|
-
puts "Running against #{targets.size} targets.\n"
|
382
|
-
spinner = TTY::Spinner.new(':spinner', frames: ['.'], interval: 0.1)
|
383
|
-
spinner.auto_spin
|
384
|
-
results = Parallel.map(payloads) do |title, test, options|
|
385
|
-
env = options[:env].nil? ? {} : options[:env]
|
386
|
-
stdout, stderr, status = Open3.capture3(env, test)
|
387
|
-
["================\n#{title}\n", stdout, stderr, status]
|
388
|
-
end
|
389
|
-
# because we cannot modify variables inside of Parallel
|
390
|
-
results.each do |result|
|
391
|
-
if result.last.to_i.zero?
|
392
|
-
success_list.push(result.first.scan(%r{.*})[2])
|
393
|
-
else
|
394
|
-
failure_list.push(result.first.scan(%r{.*})[2])
|
395
|
-
end
|
396
|
-
end
|
397
|
-
spinner.success
|
398
|
-
else
|
399
|
-
spinners = TTY::Spinner::Multi.new("[:spinner] Running against #{targets.size} targets.")
|
400
|
-
payloads.each do |title, test, options|
|
401
|
-
env = options[:env].nil? ? {} : options[:env]
|
402
|
-
spinners.register("[:spinner] #{title}") do |sp|
|
403
|
-
stdout, stderr, status = Open3.capture3(env, test)
|
404
|
-
if status.to_i.zero?
|
405
|
-
sp.success
|
406
|
-
success_list.push(title)
|
407
|
-
else
|
408
|
-
sp.error
|
409
|
-
failure_list.push(title)
|
410
|
-
end
|
411
|
-
results.push(["================\n#{title}\n", stdout, stderr, status])
|
412
|
-
end
|
413
|
-
end
|
414
|
-
spinners.auto_spin
|
415
|
-
spinners.success
|
416
|
-
end
|
417
|
-
|
418
|
-
# output test results
|
419
|
-
results.each do |result|
|
420
|
-
puts result
|
421
|
-
end
|
422
|
-
|
423
|
-
# output test summary
|
424
|
-
puts "Successful on #{success_list.size} nodes: #{success_list}" if success_list.any?
|
425
|
-
puts "Failed on #{failure_list.size} nodes: #{failure_list}" if failure_list.any?
|
426
|
-
exit 1 if failure_list.any?
|
427
|
-
end
|
428
|
-
|
429
|
-
targets.each do |target|
|
430
|
-
desc "Run serverspec against #{target}"
|
431
|
-
RSpec::Core::RakeTask.new(target.to_sym) do |t|
|
432
|
-
t.pattern = 'spec/acceptance/**{,/*/**}/*_spec.rb'
|
433
|
-
ENV['TARGET_HOST'] = target
|
434
|
-
end
|
435
|
-
end
|
436
|
-
end
|
437
|
-
# add localhost separately
|
438
|
-
desc 'Run serverspec against localhost, USE WITH CAUTION, this action can be potentially dangerous.'
|
439
|
-
host = 'localhost'
|
440
|
-
RSpec::Core::RakeTask.new(host.to_sym) do |t|
|
441
|
-
t.pattern = 'spec/acceptance/**{,/*/**}/*_spec.rb'
|
442
|
-
ENV['TARGET_HOST'] = host
|
443
|
-
end
|
444
|
-
end
|
445
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
require 'puppet_litmus'
|
5
|
+
require 'bolt_spec/run'
|
6
|
+
require 'open3'
|
7
|
+
require 'pdk'
|
8
|
+
require 'json'
|
9
|
+
require 'parallel'
|
10
|
+
|
11
|
+
# helper methods for the litmus rake tasks
|
12
|
+
module LitmusRakeHelper
|
13
|
+
# Gets a string representing the operating system and version.
|
14
|
+
#
|
15
|
+
# @param metadata [Hash] metadata to parse for operating system info
|
16
|
+
# @return [String] the operating system string with version info for use in provisioning.
|
17
|
+
def get_metadata_operating_systems(metadata)
|
18
|
+
return unless metadata.is_a?(Hash)
|
19
|
+
return unless metadata['operatingsystem_support'].is_a?(Array)
|
20
|
+
|
21
|
+
metadata['operatingsystem_support'].each do |os_info|
|
22
|
+
next unless os_info['operatingsystem'] && os_info['operatingsystemrelease']
|
23
|
+
|
24
|
+
os_name = case os_info['operatingsystem']
|
25
|
+
when 'Amazon', 'Archlinux', 'AIX', 'OSX'
|
26
|
+
next
|
27
|
+
when 'OracleLinux'
|
28
|
+
'oracle'
|
29
|
+
when 'Windows'
|
30
|
+
'win'
|
31
|
+
else
|
32
|
+
os_info['operatingsystem'].downcase
|
33
|
+
end
|
34
|
+
|
35
|
+
os_info['operatingsystemrelease'].each do |release|
|
36
|
+
version = case os_name
|
37
|
+
when 'ubuntu', 'osx'
|
38
|
+
release.sub('.', '')
|
39
|
+
when 'sles'
|
40
|
+
release.gsub(%r{ SP[14]}, '')
|
41
|
+
when 'win'
|
42
|
+
release = release.delete('.') if release.include? '8.1'
|
43
|
+
release.sub('Server', '').sub('10', '10-pro')
|
44
|
+
else
|
45
|
+
release
|
46
|
+
end
|
47
|
+
|
48
|
+
yield "#{os_name}-#{version.downcase}-x86_64".delete(' ')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Executes a command on the test runner.
|
54
|
+
#
|
55
|
+
# @param command [String] command to execute.
|
56
|
+
# @return [Object] the standard out stream.
|
57
|
+
def run_local_command(command)
|
58
|
+
stdout, stderr, status = Open3.capture3(command)
|
59
|
+
error_message = "Attempted to run\ncommand:'#{command}'\nstdout:#{stdout}\nstderr:#{stderr}"
|
60
|
+
raise error_message unless status.to_i.zero?
|
61
|
+
|
62
|
+
stdout
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
namespace :litmus do
|
67
|
+
include LitmusRakeHelper
|
68
|
+
# Prints all supported OSes from metadata.json file.
|
69
|
+
desc 'print all supported OSes from metadata'
|
70
|
+
task :metadata do
|
71
|
+
metadata = JSON.parse(File.read('metadata.json'))
|
72
|
+
get_metadata_operating_systems(metadata) do |os_and_version|
|
73
|
+
puts os_and_version
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# DEPRECATED - Provisions all supported OSes with provisioner eg 'bundle exec rake litmus:provision_from_metadata['vmpooler']'.
|
78
|
+
#
|
79
|
+
# @param :provisioner [String] provisioner to use in provisioning all OSes.
|
80
|
+
desc "DEPRECATED: provision_from_metadata task is deprecated.
|
81
|
+
Provision all supported OSes with provisioner eg 'bundle exec rake 'litmus:provision_from_metadata'"
|
82
|
+
task :provision_from_metadata, [:provisioner] do |_task, args|
|
83
|
+
metadata = JSON.parse(File.read('metadata.json'))
|
84
|
+
get_metadata_operating_systems(metadata) do |os_and_version|
|
85
|
+
puts os_and_version
|
86
|
+
include BoltSpec::Run
|
87
|
+
Rake::Task['spec_prep'].invoke
|
88
|
+
config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
|
89
|
+
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'))
|
90
|
+
|
91
|
+
params = { 'action' => 'provision', 'platform' => os_and_version, 'inventory' => Dir.pwd }
|
92
|
+
results = run_task("provision::#{args[:provisioner]}", 'localhost', params, config: config_data, inventory: nil)
|
93
|
+
results.each do |result|
|
94
|
+
if result['status'] != 'success'
|
95
|
+
puts "Failed on #{result['node']}\n#{result}"
|
96
|
+
else
|
97
|
+
puts "Provisioned #{result['result']['node_name']}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Provisions a list of OSes from provision.yaml file e.g. 'bundle exec rake litmus:provision_list[default]'.
|
104
|
+
#
|
105
|
+
# @param :key [String] key that maps to a value for a provisioner and an image to be used for each OS provisioned.
|
106
|
+
desc "provision list of machines from provision.yaml file. 'bundle exec rake 'litmus:provision_list[default]'"
|
107
|
+
task :provision_list, [:key] do |_task, args|
|
108
|
+
provision_hash = YAML.load_file('./provision.yaml')
|
109
|
+
provisioner = provision_hash[args[:key]]['provisioner']
|
110
|
+
# Splat the params into environment variables to pass to the provision task but only in this runspace
|
111
|
+
provision_hash[args[:key]]['params']&.each { |key, value| ENV["LITMUS_#{key.upcase}"] = value }
|
112
|
+
failed_image_message = ''
|
113
|
+
provision_hash[args[:key]]['images'].each do |image|
|
114
|
+
# this is the only way to capture the stdout from the rake task, it will affect pry
|
115
|
+
capture_rake_output = StringIO.new
|
116
|
+
$stdout = capture_rake_output
|
117
|
+
Rake::Task['litmus:provision'].invoke(provisioner, image)
|
118
|
+
if $stdout.string =~ %r{.status.=>.failure}
|
119
|
+
failed_image_message += "=====\n#{image}\n#{$stdout.string}\n"
|
120
|
+
else
|
121
|
+
STDOUT.puts $stdout.string
|
122
|
+
end
|
123
|
+
Rake::Task['litmus:provision'].reenable
|
124
|
+
end
|
125
|
+
raise "Failed to provision with '#{provisioner}'\n #{failed_image_message}" unless failed_image_message.empty?
|
126
|
+
end
|
127
|
+
|
128
|
+
# Provision a container or VM with a given platform 'bundle exec rake 'litmus:provision[vmpooler, ubuntu-1604-x86_64]'.
|
129
|
+
#
|
130
|
+
# @param :provisioner [String] provisioner to use in provisioning given platform.
|
131
|
+
# @param :platform [String] OS platform for container or VM to use.
|
132
|
+
desc "provision container/VM - abs/docker/vagrant/vmpooler eg 'bundle exec rake 'litmus:provision[vmpooler, ubuntu-1604-x86_64]'"
|
133
|
+
task :provision, [:provisioner, :platform] do |_task, args|
|
134
|
+
include BoltSpec::Run
|
135
|
+
Rake::Task['spec_prep'].invoke
|
136
|
+
config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
|
137
|
+
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'))
|
138
|
+
|
139
|
+
unless %w[abs docker docker_exp vagrant vmpooler].include?(args[:provisioner])
|
140
|
+
raise "Unknown provisioner '#{args[:provisioner]}', try abs/docker/vagrant/vmpooler"
|
141
|
+
end
|
142
|
+
|
143
|
+
params = { 'action' => 'provision', 'platform' => args[:platform], 'inventory' => Dir.pwd }
|
144
|
+
spinner = if (ENV['CI'] == 'true') || !ENV['DISTELLI_BUILDNUM'].nil?
|
145
|
+
TTY::Spinner.new(':spinner', frames: ['.'], interval: 0.1)
|
146
|
+
else
|
147
|
+
TTY::Spinner.new("Provisioning #{args[:platform]} using #{args[:provisioner]} provisioner.[:spinner]")
|
148
|
+
end
|
149
|
+
spinner.auto_spin
|
150
|
+
results = run_task("provision::#{args[:provisioner]}", 'localhost', params, config: config_data, inventory: nil)
|
151
|
+
if results.first['status'] != 'success'
|
152
|
+
spinner.error
|
153
|
+
raise "Failed provisioning #{args[:platform]} using #{args[:provisioner]}\n#{results.first}"
|
154
|
+
end
|
155
|
+
|
156
|
+
spinner.success
|
157
|
+
puts "#{results.first['result']['node_name']}, #{args[:platform]}"
|
158
|
+
end
|
159
|
+
|
160
|
+
# Install puppet agent on a collection of nodes
|
161
|
+
#
|
162
|
+
# @param :collection [String] parameters to pass to the puppet agent install command.
|
163
|
+
# @param :target_node_name [Array] nodes on which to install puppet agent.
|
164
|
+
desc 'install puppet agent, [:collection, :target_node_name]'
|
165
|
+
task :install_agent, [:collection, :target_node_name] do |_task, args|
|
166
|
+
inventory_hash = inventory_hash_from_inventory_file
|
167
|
+
targets = find_targets(inventory_hash, args[:target_node_name])
|
168
|
+
if targets.empty?
|
169
|
+
puts 'No targets found'
|
170
|
+
exit 0
|
171
|
+
end
|
172
|
+
puts 'install_agent'
|
173
|
+
include BoltSpec::Run
|
174
|
+
Rake::Task['spec_prep'].invoke
|
175
|
+
config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
|
176
|
+
params = if args[:collection].nil?
|
177
|
+
{}
|
178
|
+
else
|
179
|
+
{ 'collection' => args[:collection] }
|
180
|
+
end
|
181
|
+
raise "puppet_agent was not found in #{config_data['modulepath']}, please amend the .fixtures.yml file" unless File.directory?(File.join(config_data['modulepath'], 'puppet_agent'))
|
182
|
+
|
183
|
+
results = run_task('puppet_agent::install', targets, params, config: config_data, inventory: inventory_hash)
|
184
|
+
results.each do |result|
|
185
|
+
if result['status'] != 'success'
|
186
|
+
command_to_run = "bolt task run puppet_agent::install --targets #{result['node']} --inventoryfile inventory.yaml --modulepath #{config_data['modulepath']}"
|
187
|
+
raise "Failed on #{result['node']}\n#{result}\ntry running '#{command_to_run}'"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# fix the path on ssh_nodes
|
192
|
+
results = run_command('echo PATH="$PATH:/opt/puppetlabs/puppet/bin" > /etc/environment', 'ssh_nodes', config: nil, inventory: inventory_hash) unless inventory_hash['groups'].select { |group| group['name'] == 'ssh_nodes' }.size.zero? # rubocop:disable Metrics/LineLength
|
193
|
+
results.each do |result|
|
194
|
+
if result['status'] != 'success'
|
195
|
+
puts "Failed on #{result['node']}\n#{result}"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Install puppet enterprise - for internal puppet employees only - Requires an el7 provisioned machine - experimental feature [:target_node_name]'
|
201
|
+
#
|
202
|
+
# @param :target_node_name [Array] nodes on which to install puppet agent.
|
203
|
+
desc 'install puppet enterprise - for internal puppet employees only - Requires an el7 provisioned machine - experimental feature [:target_node_name]'
|
204
|
+
task :install_pe, [:target_node_name] do |_task, args|
|
205
|
+
inventory_hash = inventory_hash_from_inventory_file
|
206
|
+
target_nodes = find_targets(inventory_hash, args[:target_node_name])
|
207
|
+
if target_nodes.empty?
|
208
|
+
puts 'No targets found'
|
209
|
+
exit 0
|
210
|
+
end
|
211
|
+
puts 'install_pe'
|
212
|
+
include BoltSpec::Run
|
213
|
+
Rake::Task['spec_prep'].invoke
|
214
|
+
config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
|
215
|
+
|
216
|
+
puts 'Setting up parameters'
|
217
|
+
|
218
|
+
PE_RELEASE = 2019.0
|
219
|
+
pe_latest_cmd = "curl http://enterprise.delivery.puppetlabs.net/#{PE_RELEASE}/ci-ready/LATEST"
|
220
|
+
pe_latest = run_command(pe_latest_cmd, target_nodes, config: config_data, inventory: inventory_hash)
|
221
|
+
pe_latest_string = pe_latest[0]['result']['stdout'].delete("\n")
|
222
|
+
PE_FILE_NAME = "puppet-enterprise-#{pe_latest_string}-el-7-x86_64"
|
223
|
+
TAR_FILE = "#{PE_FILE_NAME}.tar"
|
224
|
+
DOWNLOAD_URL = "http://enterprise.delivery.puppetlabs.net/#{PE_RELEASE}/ci-ready/#{TAR_FILE}"
|
225
|
+
|
226
|
+
puts 'Initiating PE download'
|
227
|
+
|
228
|
+
# Download PE
|
229
|
+
download_pe_cmd = "wget -q #{DOWNLOAD_URL}"
|
230
|
+
run_command(download_pe_cmd, target_nodes, config: config_data, inventory: inventory_hash)
|
231
|
+
|
232
|
+
puts 'PE successfully downloaded, running installer (this may take 5 or so minutes, please be patient)'
|
233
|
+
|
234
|
+
# Install PE
|
235
|
+
untar_cmd = "tar xvf #{TAR_FILE}"
|
236
|
+
run_command(untar_cmd, target_nodes, config: config_data, inventory: inventory_hash)
|
237
|
+
puts run_command("cd #{PE_FILE_NAME} && 1 | ./puppet-enterprise-installer", target_nodes, config: nil, inventory: inventory_hash)[0]['result']['stdout']
|
238
|
+
|
239
|
+
puts 'Autosigning Certificates'
|
240
|
+
|
241
|
+
# Set Autosign
|
242
|
+
autosign_cmd = "echo 'autosign = true' >> /etc/puppetlabs/puppet/puppet.conf"
|
243
|
+
run_command(autosign_cmd, target_nodes, config: config_data, inventory: inventory_hash)
|
244
|
+
|
245
|
+
puts 'Finishing installation with a Puppet Agent run'
|
246
|
+
|
247
|
+
run_command('puppet agent -t', target_nodes, config: config_data, inventory: inventory_hash)
|
248
|
+
|
249
|
+
puts 'PE Installation is now complete'
|
250
|
+
end
|
251
|
+
|
252
|
+
# Install the puppet module under test on a collection of nodes
|
253
|
+
#
|
254
|
+
# @param :target_node_name [Array] nodes on which to install a puppet module for testing.
|
255
|
+
desc 'install_module - build and install module'
|
256
|
+
task :install_module, [:target_node_name] do |_task, args|
|
257
|
+
inventory_hash = inventory_hash_from_inventory_file
|
258
|
+
target_nodes = find_targets(inventory_hash, args[:target_node_name])
|
259
|
+
if target_nodes.empty?
|
260
|
+
puts 'No targets found'
|
261
|
+
exit 0
|
262
|
+
end
|
263
|
+
include BoltSpec::Run
|
264
|
+
# old cli_way
|
265
|
+
# pdk_build_command = 'bundle exec pdk build --force'
|
266
|
+
# stdout, stderr, _status = Open3.capture3(pdk_build_command)
|
267
|
+
# raise "Failed to run 'pdk_build_command',#{stdout} and #{stderr}" if (stderr =~ %r{completed successfully}).nil?
|
268
|
+
require 'pdk/module/build'
|
269
|
+
opts = {}
|
270
|
+
opts[:force] = true
|
271
|
+
builder = PDK::Module::Build.new(opts)
|
272
|
+
module_tar = builder.build
|
273
|
+
puts 'Built'
|
274
|
+
|
275
|
+
# module_tar = Dir.glob('pkg/*.tar.gz').max_by { |f| File.mtime(f) }
|
276
|
+
raise "Unable to find package in 'pkg/*.tar.gz'" if module_tar.nil?
|
277
|
+
|
278
|
+
target_string = if args[:target_node_name].nil?
|
279
|
+
'all'
|
280
|
+
else
|
281
|
+
args[:target_node_name]
|
282
|
+
end
|
283
|
+
run_local_command("bundle exec bolt file upload #{module_tar} /tmp/#{File.basename(module_tar)} --nodes #{target_string} --inventoryfile inventory.yaml")
|
284
|
+
install_module_command = "puppet module install /tmp/#{File.basename(module_tar)}"
|
285
|
+
result = run_command(install_module_command, target_nodes, config: nil, inventory: inventory_hash)
|
286
|
+
# rubocop:disable Style/GuardClause
|
287
|
+
if result.is_a?(Array)
|
288
|
+
result.each do |node|
|
289
|
+
puts "#{node['node']} failed #{node['result']}" if node['status'] != 'success'
|
290
|
+
end
|
291
|
+
else
|
292
|
+
raise "Failed trying to run '#{install_module_command}' against inventory."
|
293
|
+
end
|
294
|
+
# rubocop:enable Style/GuardClause
|
295
|
+
puts 'Installed'
|
296
|
+
end
|
297
|
+
|
298
|
+
# Provision a list of machines, install a puppet agent, and install the puppet module under test on a collection of nodes
|
299
|
+
#
|
300
|
+
# @param :key [String] key that maps to a value for a provisioner and an image to be used for each OS provisioned.
|
301
|
+
# @param :collection [String] parameters to pass to the puppet agent install command.
|
302
|
+
desc 'provision_install - provision a list of machines, install an agent, and the module.'
|
303
|
+
task :provision_install, [:key, :collection] do |_task, args|
|
304
|
+
Rake::Task['spec_prep'].invoke
|
305
|
+
Rake::Task['litmus:provision_list'].invoke(args[:key])
|
306
|
+
Rake::Task['litmus:install_agent'].invoke(args[:collection])
|
307
|
+
Rake::Task['litmus:install_module'].invoke
|
308
|
+
end
|
309
|
+
|
310
|
+
# Decommissions test machines.
|
311
|
+
#
|
312
|
+
# @param :target [Array] nodes to remove from test environemnt and decommission.
|
313
|
+
desc 'tear-down - decommission machines'
|
314
|
+
task :tear_down, [:target] do |_task, args|
|
315
|
+
inventory_hash = inventory_hash_from_inventory_file
|
316
|
+
targets = find_targets(inventory_hash, args[:target])
|
317
|
+
if targets.empty?
|
318
|
+
puts 'No targets found'
|
319
|
+
exit 0
|
320
|
+
end
|
321
|
+
include BoltSpec::Run
|
322
|
+
Rake::Task['spec_prep'].invoke
|
323
|
+
config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
|
324
|
+
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'))
|
325
|
+
|
326
|
+
bad_results = []
|
327
|
+
targets.each do |node_name|
|
328
|
+
# how do we know what provisioner to use
|
329
|
+
node_facts = facts_from_node(inventory_hash, node_name)
|
330
|
+
next unless %w[abs docker docker_exp vagrant vmpooler].include?(node_facts['provisioner'])
|
331
|
+
|
332
|
+
params = { 'action' => 'tear_down', 'node_name' => node_name, 'inventory' => Dir.pwd }
|
333
|
+
result = run_task("provision::#{node_facts['provisioner']}", 'localhost', params, config: config_data, inventory: nil)
|
334
|
+
if result.first['status'] != 'success'
|
335
|
+
bad_results << "#{node_name}, #{result.first['result']['_error']['msg']}"
|
336
|
+
else
|
337
|
+
print "#{node_name}, "
|
338
|
+
end
|
339
|
+
end
|
340
|
+
puts ''
|
341
|
+
# output the things that went wrong, after the successes
|
342
|
+
puts 'something went wrong:' unless bad_results.size.zero?
|
343
|
+
bad_results.each do |result|
|
344
|
+
puts result
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
namespace :acceptance do
|
349
|
+
include PuppetLitmus::InventoryManipulation
|
350
|
+
if File.file?('inventory.yaml')
|
351
|
+
inventory_hash = inventory_hash_from_inventory_file
|
352
|
+
targets = find_targets(inventory_hash, nil)
|
353
|
+
|
354
|
+
# Run acceptance tests against all machines in the inventory file in parallel.
|
355
|
+
desc 'Run tests in parallel against all machines in the inventory file'
|
356
|
+
task :parallel do
|
357
|
+
if targets.empty?
|
358
|
+
puts 'No targets found'
|
359
|
+
exit 0
|
360
|
+
end
|
361
|
+
spinners = TTY::Spinner::Multi.new("Running against #{targets.size} targets.[:spinner]", frames: ['.'], interval: 0.1)
|
362
|
+
payloads = []
|
363
|
+
# Generate list of targets to provision
|
364
|
+
targets.each do |target|
|
365
|
+
test = 'bundle exec bundle exec rspec ./spec/acceptance --format progress'
|
366
|
+
title = "#{target}, #{facts_from_node(inventory_hash, target)['platform']}"
|
367
|
+
options = {
|
368
|
+
env: {
|
369
|
+
'TARGET_HOST' => target,
|
370
|
+
},
|
371
|
+
}
|
372
|
+
payloads << [title, test, options]
|
373
|
+
end
|
374
|
+
|
375
|
+
results = []
|
376
|
+
success_list = []
|
377
|
+
failure_list = []
|
378
|
+
# Provision targets depending on what environment we're in
|
379
|
+
if (ENV['CI'] == 'true') || !ENV['DISTELLI_BUILDNUM'].nil?
|
380
|
+
# CI systems are strange beasts, we only output a '.' every wee while to keep the terminal alive.
|
381
|
+
puts "Running against #{targets.size} targets.\n"
|
382
|
+
spinner = TTY::Spinner.new(':spinner', frames: ['.'], interval: 0.1)
|
383
|
+
spinner.auto_spin
|
384
|
+
results = Parallel.map(payloads) do |title, test, options|
|
385
|
+
env = options[:env].nil? ? {} : options[:env]
|
386
|
+
stdout, stderr, status = Open3.capture3(env, test)
|
387
|
+
["================\n#{title}\n", stdout, stderr, status]
|
388
|
+
end
|
389
|
+
# because we cannot modify variables inside of Parallel
|
390
|
+
results.each do |result|
|
391
|
+
if result.last.to_i.zero?
|
392
|
+
success_list.push(result.first.scan(%r{.*})[2])
|
393
|
+
else
|
394
|
+
failure_list.push(result.first.scan(%r{.*})[2])
|
395
|
+
end
|
396
|
+
end
|
397
|
+
spinner.success
|
398
|
+
else
|
399
|
+
spinners = TTY::Spinner::Multi.new("[:spinner] Running against #{targets.size} targets.")
|
400
|
+
payloads.each do |title, test, options|
|
401
|
+
env = options[:env].nil? ? {} : options[:env]
|
402
|
+
spinners.register("[:spinner] #{title}") do |sp|
|
403
|
+
stdout, stderr, status = Open3.capture3(env, test)
|
404
|
+
if status.to_i.zero?
|
405
|
+
sp.success
|
406
|
+
success_list.push(title)
|
407
|
+
else
|
408
|
+
sp.error
|
409
|
+
failure_list.push(title)
|
410
|
+
end
|
411
|
+
results.push(["================\n#{title}\n", stdout, stderr, status])
|
412
|
+
end
|
413
|
+
end
|
414
|
+
spinners.auto_spin
|
415
|
+
spinners.success
|
416
|
+
end
|
417
|
+
|
418
|
+
# output test results
|
419
|
+
results.each do |result|
|
420
|
+
puts result
|
421
|
+
end
|
422
|
+
|
423
|
+
# output test summary
|
424
|
+
puts "Successful on #{success_list.size} nodes: #{success_list}" if success_list.any?
|
425
|
+
puts "Failed on #{failure_list.size} nodes: #{failure_list}" if failure_list.any?
|
426
|
+
exit 1 if failure_list.any?
|
427
|
+
end
|
428
|
+
|
429
|
+
targets.each do |target|
|
430
|
+
desc "Run serverspec against #{target}"
|
431
|
+
RSpec::Core::RakeTask.new(target.to_sym) do |t|
|
432
|
+
t.pattern = 'spec/acceptance/**{,/*/**}/*_spec.rb'
|
433
|
+
ENV['TARGET_HOST'] = target
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
# add localhost separately
|
438
|
+
desc 'Run serverspec against localhost, USE WITH CAUTION, this action can be potentially dangerous.'
|
439
|
+
host = 'localhost'
|
440
|
+
RSpec::Core::RakeTask.new(host.to_sym) do |t|
|
441
|
+
t.pattern = 'spec/acceptance/**{,/*/**}/*_spec.rb'
|
442
|
+
ENV['TARGET_HOST'] = host
|
443
|
+
end
|
444
|
+
end
|
445
|
+
end
|