puppet_litmus 0.7.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
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 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
+ # 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
- 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
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