puppet_litmus 0.7.0 → 0.7.1
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/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
|