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.
@@ -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
- spinner = if (ENV['CI'] == 'true') || !ENV['DISTELLI_BUILDNUM'].nil?
136
- TTY::Spinner.new(':spinner', frames: ['.'], interval: 0.1)
137
- else
138
- TTY::Spinner.new("Provisioning #{args[:platform]} using #{args[:provisioner]} provisioner.[:spinner]")
139
- end
140
- spinner.auto_spin
141
- Rake::Task['spec_prep'].invoke
142
- config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
143
- 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'))
144
-
145
- unless %w[abs docker docker_exp vagrant vmpooler].include?(args[:provisioner])
146
- raise "Unknown provisioner '#{args[:provisioner]}', try abs/docker/vagrant/vmpooler"
147
- end
148
-
149
- params = { 'action' => 'provision', 'platform' => args[:platform], 'inventory' => Dir.pwd }
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