puppet_litmus 0.9.1 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bc193f9e3e7b23027e022082d77a4349e4db737fc36638d0ca8d9dca433d776d
4
- data.tar.gz: 57e68de10c7c77f2b7059163e6ebd1b66852f058e203d3ce7a2d9656f1eb7bd7
3
+ metadata.gz: 2b4b8f9d03595ac86920511bc3ec0429c34e99ebb54fce2e0dd80bce3d233145
4
+ data.tar.gz: c4a3bd340f4e215faa647111b6ba933d5669a43e4c06474ab4bc41766fb36235
5
5
  SHA512:
6
- metadata.gz: f1b37b780a30f7cfca61513debb0d3931ee68524e72f25e4c2c963feeee4012ab56fc4dc7e32717f20de39d15cbefd076198f48659f227bd5bc98134280142cd
7
- data.tar.gz: 6b910d580dbc673723f5cb2a0bcaca27f5b88462ffabf5190590c9d0dac3ac5efa18735dada0ea8831a59bf52121763a787e31da4d63af6bc9f4dcd1885302aa
6
+ metadata.gz: 848d4c8ea4ff3b7b6e02f1404cbbd255d847a259fab02db44032b3982127c5c9a0358366557454f03a85645eb4e9a2ef35bc0e0da90d5fae2d68fb2094912b5d
7
+ data.tar.gz: 85195c4df6ad10c37290acaba4d14cd4cb2d31950e8095a7afc5104e1a9d3050912dd4ff4a28b6bdcc90b3ff9cc7a956e5b0e1342bad4715ced75542ea0836c4
@@ -18,6 +18,25 @@ module PuppetLitmus::InventoryManipulation
18
18
  inventory_hash
19
19
  end
20
20
 
21
+ # Provide a default hash for executing against localhost
22
+ #
23
+ # @return [Hash] inventory.yaml hash containing only an entry for localhost
24
+ def localhost_inventory_hash
25
+ {
26
+ 'groups' => [
27
+ {
28
+ 'name' => 'local',
29
+ 'nodes' => [
30
+ {
31
+ 'name' => 'litmus_localhost',
32
+ 'config' => { 'transport' => 'local' },
33
+ },
34
+ ],
35
+ },
36
+ ],
37
+ }
38
+ end
39
+
21
40
  # Finds targets to perform operations on from an inventory hash.
22
41
  #
23
42
  # @param inventory_hash [Hash] hash of the inventory.yaml file
@@ -51,6 +70,15 @@ module PuppetLitmus::InventoryManipulation
51
70
  exists
52
71
  end
53
72
 
73
+ # Determines if a node_name exists in the inventory_hash.
74
+ #
75
+ # @param inventory_hash [Hash] hash of the inventory.yaml file
76
+ # @param node_name [String] node to locate in the group
77
+ # @return [Boolean] true if node_name exists in the inventory_hash.
78
+ def target_in_inventory?(inventory_hash, node_name)
79
+ find_targets(inventory_hash, nil).include?(node_name)
80
+ end
81
+
54
82
  # Finds a config hash in the inventory hash by searching for a node name.
55
83
  #
56
84
  # @param inventory_hash [Hash] hash of the inventory.yaml file
@@ -83,6 +111,22 @@ module PuppetLitmus::InventoryManipulation
83
111
  raise "No facts were found for #{node_name}"
84
112
  end
85
113
 
114
+ # Finds a var hash in the inventory hash by searching for a node name.
115
+ #
116
+ # @param inventory_hash [Hash] hash of the inventory.yaml file
117
+ # @param node_name [String] node to locate in the group
118
+ # @return [Hash] vars for node of name node_name
119
+ def vars_from_node(inventory_hash, node_name)
120
+ inventory_hash['groups'].each do |group|
121
+ group['nodes'].each do |node|
122
+ if node['name'] == node_name
123
+ return node['vars']
124
+ end
125
+ end
126
+ end
127
+ {}
128
+ end
129
+
86
130
  # Adds a node to a group specified, if group_name exists in inventory hash.
87
131
  #
88
132
  # @param inventory_hash [Hash] hash of the inventory.yaml file
@@ -74,32 +74,6 @@ namespace :litmus do
74
74
  end
75
75
  end
76
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
77
  # Provisions a list of OSes from provision.yaml file e.g. 'bundle exec rake litmus:provision_list[default]'.
104
78
  #
105
79
  # @param :key [String] key that maps to a value for a provisioner and an image to be used for each OS provisioned.
@@ -107,6 +81,7 @@ namespace :litmus do
107
81
  task :provision_list, [:key] do |_task, args|
108
82
  provision_hash = YAML.load_file('./provision.yaml')
109
83
  provisioner = provision_hash[args[:key]]['provisioner']
84
+ inventory_vars = provision_hash[args[:key]]['vars']
110
85
  # Splat the params into environment variables to pass to the provision task but only in this runspace
111
86
  provision_hash[args[:key]]['params']&.each { |key, value| ENV[key.upcase] = value.to_s }
112
87
  failed_image_message = ''
@@ -114,7 +89,7 @@ namespace :litmus do
114
89
  # this is the only way to capture the stdout from the rake task, it will affect pry
115
90
  capture_rake_output = StringIO.new
116
91
  $stdout = capture_rake_output
117
- Rake::Task['litmus:provision'].invoke(provisioner, image)
92
+ Rake::Task['litmus:provision'].invoke(provisioner, image, inventory_vars)
118
93
  if $stdout.string =~ %r{.status.=>.failure}
119
94
  failed_image_message += "=====\n#{image}\n#{$stdout.string}\n"
120
95
  else
@@ -130,30 +105,43 @@ namespace :litmus do
130
105
  # @param :provisioner [String] provisioner to use in provisioning given platform.
131
106
  # @param :platform [String] OS platform for container or VM to use.
132
107
  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|
108
+ task :provision, [:provisioner, :platform, :inventory_vars] do |_task, args|
134
109
  include BoltSpec::Run
135
110
  Rake::Task['spec_prep'].invoke
136
111
  config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
137
112
  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
113
 
139
114
  unless %w[abs docker docker_exp vagrant vmpooler].include?(args[:provisioner])
140
- raise "Unknown provisioner '#{args[:provisioner]}', try abs/docker/vagrant/vmpooler"
115
+ raise "Unknown provisioner '#{args[:provisioner]}', try abs/docker/docker_exp/vagrant/vmpooler"
141
116
  end
142
117
 
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
118
+ params = if args[:inventory_vars].nil?
119
+ { 'action' => 'provision', 'platform' => args[:platform], 'inventory' => Dir.pwd }
120
+ else
121
+ { 'action' => 'provision', 'platform' => args[:platform], 'inventory' => Dir.pwd, 'vars' => args[:inventory_vars] }
122
+ end
123
+
124
+ if (ENV['CI'] == 'true') || !ENV['DISTELLI_BUILDNUM'].nil?
125
+ progress = Thread.new do
126
+ loop do
127
+ printf '.'
128
+ sleep(10)
129
+ end
130
+ end
131
+ else
132
+ spinner = TTY::Spinner.new("Provisioning #{args[:platform]} using #{args[:provisioner]} provisioner.[:spinner]")
133
+ spinner.auto_spin
134
+ end
150
135
  results = run_task("provision::#{args[:provisioner]}", 'localhost', params, config: config_data, inventory: nil)
151
136
  if results.first['status'] != 'success'
152
- spinner.error
153
137
  raise "Failed provisioning #{args[:platform]} using #{args[:provisioner]}\n#{results.first}"
154
138
  end
155
139
 
156
- spinner.success
140
+ if (ENV['CI'] == 'true') || !ENV['DISTELLI_BUILDNUM'].nil?
141
+ Thread.kill(progress)
142
+ else
143
+ spinner.success
144
+ end
157
145
  puts "#{results.first['result']['node_name']}, #{args[:platform]}"
158
146
  end
159
147
 
@@ -189,7 +177,10 @@ namespace :litmus do
189
177
  end
190
178
 
191
179
  # 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
180
+ unless inventory_hash['groups'].select { |group| group['name'] == 'ssh_nodes' }.size.zero?
181
+ results = run_command('echo PATH="$PATH:/opt/puppetlabs/puppet/bin" > /etc/environment',
182
+ 'ssh_nodes', config: nil, inventory: inventory_hash)
183
+ end
193
184
  results.each do |result|
194
185
  if result['status'] != 'success'
195
186
  puts "Failed on #{result['node']}\n#{result}"
@@ -197,58 +188,6 @@ namespace :litmus do
197
188
  end
198
189
  end
199
190
 
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
191
  # Install the puppet module under test on a collection of nodes
253
192
  #
254
193
  # @param :target_node_name [Array] nodes on which to install a puppet module for testing.
@@ -283,15 +222,13 @@ namespace :litmus do
283
222
  run_local_command("bundle exec bolt file upload \"#{module_tar}\" /tmp/#{File.basename(module_tar)} --nodes #{target_string} --inventoryfile inventory.yaml")
284
223
  install_module_command = "puppet module install /tmp/#{File.basename(module_tar)}"
285
224
  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."
225
+
226
+ raise "Failed trying to run '#{install_module_command}' against inventory." unless result.is_a?(Array)
227
+
228
+ result.each do |node|
229
+ puts "#{node['node']} failed #{node['result']}" if node['status'] != 'success'
293
230
  end
294
- # rubocop:enable Style/GuardClause
231
+
295
232
  puts 'Installed'
296
233
  end
297
234
 
@@ -325,6 +262,8 @@ namespace :litmus do
325
262
 
326
263
  bad_results = []
327
264
  targets.each do |node_name|
265
+ next if node_name == 'litmus_localhost'
266
+
328
267
  # how do we know what provisioner to use
329
268
  node_facts = facts_from_node(inventory_hash, node_name)
330
269
  next unless %w[abs docker docker_exp vagrant vmpooler].include?(node_facts['provisioner'])
@@ -361,7 +300,7 @@ namespace :litmus do
361
300
  payloads = []
362
301
  # Generate list of targets to provision
363
302
  targets.each do |target|
364
- test = 'bundle exec bundle exec rspec ./spec/acceptance --format progress'
303
+ test = 'bundle exec rspec ./spec/acceptance --format progress'
365
304
  title = "#{target}, #{facts_from_node(inventory_hash, target)['platform']}"
366
305
  options = {
367
306
  env: {
@@ -446,6 +385,8 @@ namespace :litmus do
446
385
 
447
386
  targets.each do |target|
448
387
  desc "Run serverspec against #{target}"
388
+ next if target == 'litmus_localhost'
389
+
449
390
  RSpec::Core::RakeTask.new(target.to_sym) do |t|
450
391
  t.pattern = 'spec/acceptance/**{,/*/**}/*_spec.rb'
451
392
  ENV['TARGET_HOST'] = target
@@ -9,7 +9,7 @@ module PuppetLitmus::Serverspec
9
9
  # @return [Boolean] The result of the 2 apply manifests.
10
10
  def idempotent_apply(manifest)
11
11
  manifest_file_location = create_manifest_file(manifest)
12
- apply_manifest(nil, catch_failures: true, manifest_file_location: manifest_file_location)
12
+ apply_manifest(nil, expect_failures: false, manifest_file_location: manifest_file_location)
13
13
  apply_manifest(nil, catch_changes: true, manifest_file_location: manifest_file_location)
14
14
  end
15
15
 
@@ -24,8 +24,10 @@ module PuppetLitmus::Serverspec
24
24
  #
25
25
  # @param manifest [String] puppet manifest code to be applied.
26
26
  # @param opts [Hash] Alters the behaviour of the command. Valid options are:
27
- # :catch_changes [Boolean] exit status of 1 if there were changes.
28
- # :expect_failures [Boolean] doesnt return an exit code of non-zero if the apply failed.
27
+ # :catch_changes [Boolean] (false) We're after idempotency so allow exit code 0 only.
28
+ # :expect_changes [Boolean] (false) We're after changes specifically so allow exit code 2 only.
29
+ # :catch_failures [Boolean] (false) We're after only complete success so allow exit codes 0 and 2 only.
30
+ # :expect_failures [Boolean] (false) We're after failures specifically so allow exit codes 1, 4, and 6 only.
29
31
  # :manifest_file_location [Path] The place on the target system.
30
32
  # :hiera_config [Path] The path to the hiera.yaml configuration on the runner.
31
33
  # :prefix_command [String] prefixes the puppet apply command; eg "export LANGUAGE='ja'".
@@ -35,25 +37,47 @@ module PuppetLitmus::Serverspec
35
37
  # @return [Object] A result object from the apply.
36
38
  def apply_manifest(manifest, opts = {})
37
39
  # rubocop:enable Layout/TrailingWhitespace
38
- target_node_name = ENV['TARGET_HOST']
40
+ target_node_name = targeting_localhost? ? 'litmus_localhost' : ENV['TARGET_HOST']
39
41
  raise 'manifest and manifest_file_location in the opts hash are mutually exclusive arguments, pick one' if !manifest.nil? && !opts[:manifest_file_location].nil?
40
42
  raise 'please pass a manifest or the manifest_file_location in the opts hash' if (manifest.nil? || manifest == '') && opts[:manifest_file_location].nil?
43
+ raise 'please specify only one of `catch_changes`, `expect_changes`, `catch_failures` or `expect_failures`' if
44
+ [opts[:catch_changes], opts[:expect_changes], opts[:catch_failures], opts[:expect_failures]].compact.length > 1
45
+
46
+ if opts[:catch_changes]
47
+ use_detailed_exit_codes = true
48
+ acceptable_exit_codes = [0]
49
+ elsif opts[:catch_failures]
50
+ use_detailed_exit_codes = true
51
+ acceptable_exit_codes = [0, 2]
52
+ elsif opts[:expect_failures]
53
+ use_detailed_exit_codes = true
54
+ acceptable_exit_codes = [1, 4, 6]
55
+ elsif opts[:expect_changes]
56
+ use_detailed_exit_codes = true
57
+ acceptable_exit_codes = [2]
58
+ else
59
+ use_detailed_exit_codes = false
60
+ acceptable_exit_codes = [0]
61
+ end
41
62
 
42
63
  manifest_file_location = opts[:manifest_file_location] || create_manifest_file(manifest)
43
- inventory_hash = if target_node_name.nil? || target_node_name == 'localhost'
44
- nil
45
- else
46
- inventory_hash_from_inventory_file
47
- end
64
+ inventory_hash = File.exist?('inventory.yaml') ? inventory_hash_from_inventory_file : localhost_inventory_hash
65
+ raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
66
+
48
67
  command_to_run = "#{opts[:prefix_command]} puppet apply #{manifest_file_location}"
49
- command_to_run += " --modulepath #{Dir.pwd}/spec/fixtures/modules" if target_node_name.nil? || target_node_name == 'localhost'
68
+ command_to_run += " --modulepath #{Dir.pwd}/spec/fixtures/modules" if target_node_name == 'litmus_localhost'
50
69
  command_to_run += " --hiera_config='#{opts[:hiera_config]}'" unless opts[:hiera_config].nil?
51
- command_to_run += ' --detailed-exitcodes' if !opts[:catch_changes].nil? && (opts[:catch_changes] == true)
52
70
  command_to_run += ' --debug' if !opts[:debug].nil? && (opts[:debug] == true)
53
71
  command_to_run += ' --noop' if !opts[:noop].nil? && (opts[:noop] == true)
54
- result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
72
+ command_to_run += ' --detailed-exitcodes' if use_detailed_exit_codes == true
55
73
 
56
- raise "apply manifest failed\n`#{command_to_run}`\n======\n#{result}" if result.first['result']['exit_code'] != 0 && opts[:expect_failures] != true
74
+ result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
75
+ status = result.first['result']['exit_code']
76
+ if opts[:catch_changes] && !acceptable_exit_codes.include?(status)
77
+ report_puppet_apply_change(command_to_run, result)
78
+ elsif !acceptable_exit_codes.include?(status)
79
+ report_puppet_apply_error(command_to_run, result, acceptable_exit_codes)
80
+ end
57
81
 
58
82
  result = OpenStruct.new(exit_code: result.first['result']['exit_code'],
59
83
  stdout: result.first['result']['stdout'],
@@ -98,12 +122,10 @@ module PuppetLitmus::Serverspec
98
122
  # @yieldreturn [Block] this method will yield to a block of code passed by the caller; this can be used for additional validation, etc.
99
123
  # @return [Object] A result object from the command.
100
124
  def run_shell(command_to_run, opts = {})
101
- target_node_name = ENV['TARGET_HOST']
102
- inventory_hash = if target_node_name.nil? || target_node_name == 'localhost'
103
- nil
104
- else
105
- inventory_hash_from_inventory_file
106
- end
125
+ target_node_name = targeting_localhost? ? 'litmus_localhost' : ENV['TARGET_HOST']
126
+ inventory_hash = File.exist?('inventory.yaml') ? inventory_hash_from_inventory_file : localhost_inventory_hash
127
+ raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
128
+
107
129
  result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
108
130
  raise "shell failed\n`#{command_to_run}`\n======\n#{result}" if result.first['result']['exit_code'] != 0 && opts[:expect_failures] != true
109
131
 
@@ -122,12 +144,9 @@ module PuppetLitmus::Serverspec
122
144
  # @yieldreturn [Block] this method will yield to a block of code passed by the caller; this can be used for additional validation, etc.
123
145
  # @return [Object] A result object from the command.
124
146
  def bolt_upload_file(source, destination, opts = {}, options = {})
125
- target_node_name = ENV['TARGET_HOST'] if target_node_name.nil?
126
- inventory_hash = if target_node_name.nil? || target_node_name == 'localhost'
127
- nil
128
- else
129
- inventory_hash_from_inventory_file
130
- end
147
+ target_node_name = targeting_localhost? ? 'litmus_localhost' : ENV['TARGET_HOST']
148
+ inventory_hash = File.exist?('inventory.yaml') ? inventory_hash_from_inventory_file : localhost_inventory_hash
149
+ raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
131
150
 
132
151
  result = upload_file(source, destination, target_node_name, options: options, config: nil, inventory: inventory_hash)
133
152
 
@@ -160,12 +179,9 @@ module PuppetLitmus::Serverspec
160
179
  # @return [Object] A result object from the task.The values available are stdout, stderr and result.
161
180
  def run_bolt_task(task_name, params = {}, opts = {})
162
181
  config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
163
- target_node_name = ENV['TARGET_HOST'] if target_node_name.nil?
164
- inventory_hash = if target_node_name.nil? || target_node_name == 'localhost'
165
- nil
166
- else
167
- inventory_hash_from_inventory_file
168
- end
182
+ target_node_name = targeting_localhost? ? 'litmus_localhost' : ENV['TARGET_HOST']
183
+ inventory_hash = File.exist?('inventory.yaml') ? inventory_hash_from_inventory_file : localhost_inventory_hash
184
+ raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
169
185
 
170
186
  result = run_task(task_name, target_node_name, params, config: config_data, inventory: inventory_hash)
171
187
  result_obj = {
@@ -210,12 +226,9 @@ module PuppetLitmus::Serverspec
210
226
  # @yieldreturn [Block] this method will yield to a block of code passed by the caller; this can be used for additional validation, etc.
211
227
  # @return [Object] A result object from the script run.
212
228
  def bolt_run_script(script, opts = {}, arguments: [])
213
- target_node_name = ENV['TARGET_HOST'] if target_node_name.nil?
214
- inventory_hash = if target_node_name.nil? || target_node_name == 'localhost'
215
- nil
216
- else
217
- inventory_hash_from_inventory_file
218
- end
229
+ target_node_name = targeting_localhost? ? 'litmus_localhost' : ENV['TARGET_HOST']
230
+ inventory_hash = File.exist?('inventory.yaml') ? inventory_hash_from_inventory_file : localhost_inventory_hash
231
+ raise "Target '#{target_node_name}' not found in inventory.yaml" unless target_in_inventory?(inventory_hash, target_node_name)
219
232
 
220
233
  result = run_script(script, target_node_name, arguments, options: opts, config: nil, inventory: inventory_hash)
221
234
 
@@ -227,4 +240,67 @@ module PuppetLitmus::Serverspec
227
240
  yield result if block_given?
228
241
  result
229
242
  end
243
+
244
+ # Determines if the current execution is targeting localhost or not
245
+ #
246
+ # @return [Boolean] true if targeting localhost in the tests
247
+ def targeting_localhost?
248
+ ENV['TARGET_HOST'].nil? || ENV['TARGET_HOST'] == 'localhost'
249
+ end
250
+
251
+ private
252
+
253
+ # Report an error in the puppet run
254
+ #
255
+ # @param command [String] The puppet command causing the error.
256
+ # @param result [Array] The result struct containing the result
257
+ def report_puppet_apply_error(command, result, acceptable_exit_codes)
258
+ puppet_apply_error = <<-ERROR
259
+ apply manifest failed
260
+ `#{command}`
261
+ with exit code #{result.first['result']['exit_code']} (expected: #{acceptable_exit_codes})
262
+ ====== Start output of failed Puppet apply ======
263
+ #{puppet_output(result)}
264
+ ====== End output of failed Puppet apply ======
265
+ ERROR
266
+ raise puppet_apply_error
267
+ end
268
+
269
+ # Report an unexpected change in the puppet run
270
+ #
271
+ # @param command [String] The puppet command causing the error.
272
+ # @param result [Array] The result struct containing the result
273
+ def report_puppet_apply_change(command, result)
274
+ puppet_apply_changes = <<-ERROR
275
+ apply manifest expected no changes
276
+ `#{command}`
277
+ ====== Start output of Puppet apply with unexpected changes ======
278
+ #{puppet_output(result)}
279
+ ====== End output of Puppet apply with unexpected changes ======
280
+ ERROR
281
+ raise puppet_apply_changes
282
+ end
283
+
284
+ # Return the stdout of the puppet run
285
+ def puppet_output(result)
286
+ result.dig(0, 'result', 'stderr').to_s << \
287
+ result.dig(0, 'result', 'stdout').to_s
288
+ end
289
+
290
+ # Checks a puppet return status and returns true if it both
291
+ # the catalog compiled and the apply was successful. Either
292
+ # with or without changes
293
+ #
294
+ # @param exit_status [Integer] The status of the puppet run.
295
+ def puppet_successful?(exit_status)
296
+ [0, 2].include?(exit_status)
297
+ end
298
+
299
+ # Checks a puppet return status and returns true if
300
+ # puppet reported any changes
301
+ #
302
+ # @param exit_status [Integer] The status of the puppet run.
303
+ def puppet_changes?(exit_status)
304
+ [2, 6].include?(exit_status)
305
+ end
230
306
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  # version of this gem
4
4
  module PuppetLitmus
5
- VERSION ||= '0.9.1'
5
+ VERSION ||= '0.10.0'
6
6
  end
@@ -25,7 +25,8 @@ RSpec.describe PuppetLitmus::InventoryManipulation do
25
25
  'nodes' =>
26
26
  [{ 'name' => 'test.delivery.puppetlabs.net',
27
27
  'config' => { 'transport' => 'ssh', 'ssh' => { 'user' => 'root', 'password' => 'Qu@lity!', 'host-key-check' => false } },
28
- 'facts' => { 'provisioner' => 'vmpooler', 'platform' => 'centos-5-x86_64' } }] },
28
+ 'facts' => { 'provisioner' => 'vmpooler', 'platform' => 'centos-5-x86_64' },
29
+ 'vars' => { 'role' => 'agent' } }] },
29
30
  { 'name' => 'winrm_nodes', 'nodes' => [] }] }
30
31
  end
31
32
 
@@ -98,6 +99,14 @@ RSpec.describe PuppetLitmus::InventoryManipulation do
98
99
  { 'user' => 'root', 'password' => 'Qu@lity!', 'host-key-check' => false })
99
100
  end
100
101
 
102
+ it 'facts exists, and returns' do
103
+ expect(dummy_class.facts_from_node(config_hash, 'test.delivery.puppetlabs.net')).to eq('provisioner' => 'vmpooler', 'platform' => 'centos-5-x86_64')
104
+ end
105
+
106
+ it 'vars exists, and returns' do
107
+ expect(dummy_class.vars_from_node(config_hash, 'test.delivery.puppetlabs.net')).to eq('role' => 'agent')
108
+ end
109
+
101
110
  it 'no feature exists for the group, and returns hash with feature added' do
102
111
  expect(dummy_class.add_feature_to_group(no_feature_hash, 'puppet-agent', 'ssh_nodes')).to eq('groups' => [{ 'features' => ['puppet-agent'], 'name' => 'ssh_nodes', 'nodes' => [{ 'config' => { 'ssh' => { 'host-key-check' => false, 'password' => 'Qu@lity!', 'user' => 'root' }, 'transport' => 'ssh' }, 'facts' => { 'platform' => 'centos-5-x86_64', 'provisioner' => 'vmpooler' }, 'name' => 'test.delivery.puppetlabs.net' }] }, { 'name' => 'winrm_nodes', 'nodes' => [] }]) # rubocop:disable Metrics/LineLength: Line is too long
103
112
  end
@@ -16,7 +16,7 @@ RSpec.describe PuppetLitmus::Serverspec do
16
16
 
17
17
  it 'calls all functions' do
18
18
  expect(dummy_class).to receive(:create_manifest_file).with(manifest).and_return('/bla.pp')
19
- expect(dummy_class).to receive(:apply_manifest).with(nil, catch_failures: true, manifest_file_location: '/bla.pp')
19
+ expect(dummy_class).to receive(:apply_manifest).with(nil, expect_failures: false, manifest_file_location: '/bla.pp')
20
20
  expect(dummy_class).to receive(:apply_manifest).with(nil, catch_changes: true, manifest_file_location: '/bla.pp')
21
21
  dummy_class.idempotent_apply(manifest)
22
22
  end
@@ -25,21 +25,74 @@ RSpec.describe PuppetLitmus::Serverspec do
25
25
  describe '.apply_manifest' do
26
26
  context 'when specifying a hiera config' do
27
27
  let(:manifest) { "include '::doot'" }
28
+ let(:localhost_inventory_hash) { { 'groups' => [{ 'name' => 'local', 'nodes' => [{ 'name' => 'litmus_localhost', 'config' => { 'transport' => 'local' } }] }] } }
28
29
  let(:result) { ['result' => { 'exit_code' => 0, 'stdout' => nil, 'stderr' => nil }] }
29
30
  let(:command) { " puppet apply /bla.pp --modulepath #{Dir.pwd}/spec/fixtures/modules --hiera_config='/hiera.yaml'" }
30
31
 
31
32
  it 'passes the --hiera_config flag if the :hiera_config opt is specified' do
33
+ expect(File).to receive(:exist?).with('inventory.yaml').and_return(false)
34
+ expect(dummy_class).to receive(:localhost_inventory_hash).and_return(localhost_inventory_hash)
35
+ expect(dummy_class).to receive(:target_in_inventory?).and_return(true)
32
36
  expect(dummy_class).to receive(:create_manifest_file).with(manifest).and_return('/bla.pp')
33
- expect(dummy_class).to receive(:run_command).with(command, nil, config: nil, inventory: nil).and_return(result)
37
+ expect(dummy_class).to receive(:run_command).with(command, 'litmus_localhost', config: nil, inventory: localhost_inventory_hash).and_return(result)
34
38
  dummy_class.apply_manifest(manifest, hiera_config: '/hiera.yaml')
35
39
  end
36
40
  end
41
+
42
+ context 'when using detailed-exitcodes' do
43
+ let(:manifest) { "include '::doot'" }
44
+ let(:localhost_inventory_hash) { { 'groups' => [{ 'name' => 'local', 'nodes' => [{ 'name' => 'litmus_localhost', 'config' => { 'transport' => 'local' } }] }] } }
45
+ let(:result) { ['result' => { 'exit_code' => 0, 'stdout' => nil, 'stderr' => nil }] }
46
+ let(:command) { " puppet apply /bla.pp --modulepath #{Dir.pwd}/spec/fixtures/modules --detailed-exitcodes" }
47
+
48
+ it 'uses detailed-exitcodes with expect_failures' do
49
+ expect(File).to receive(:exist?).with('inventory.yaml').and_return(false)
50
+ expect(dummy_class).to receive(:localhost_inventory_hash).and_return(localhost_inventory_hash)
51
+ expect(dummy_class).to receive(:target_in_inventory?).and_return(true)
52
+ expect(dummy_class).to receive(:create_manifest_file).with(manifest).and_return('/bla.pp')
53
+ expect(dummy_class).to receive(:run_command).with(command, 'litmus_localhost', config: nil, inventory: localhost_inventory_hash).and_return(result)
54
+ expect { dummy_class.apply_manifest(manifest, expect_failures: true) }.to raise_error(RuntimeError)
55
+ end
56
+
57
+ it 'uses detailed-exitcodes with catch_failures' do
58
+ expect(File).to receive(:exist?).with('inventory.yaml').and_return(false)
59
+ expect(dummy_class).to receive(:localhost_inventory_hash).and_return(localhost_inventory_hash)
60
+ expect(dummy_class).to receive(:target_in_inventory?).and_return(true)
61
+ expect(dummy_class).to receive(:create_manifest_file).with(manifest).and_return('/bla.pp')
62
+ expect(dummy_class).to receive(:run_command).with(command, 'litmus_localhost', config: nil, inventory: localhost_inventory_hash).and_return(result)
63
+ dummy_class.apply_manifest(manifest, catch_failures: true)
64
+ end
65
+
66
+ it 'uses detailed-exitcodes with expect_changes' do
67
+ expect(File).to receive(:exist?).with('inventory.yaml').and_return(false)
68
+ expect(dummy_class).to receive(:localhost_inventory_hash).and_return(localhost_inventory_hash)
69
+ expect(dummy_class).to receive(:target_in_inventory?).and_return(true)
70
+ expect(dummy_class).to receive(:create_manifest_file).with(manifest).and_return('/bla.pp')
71
+ expect(dummy_class).to receive(:run_command).with(command, 'litmus_localhost', config: nil, inventory: localhost_inventory_hash).and_return(result)
72
+ expect { dummy_class.apply_manifest(manifest, expect_changes: true) }.to raise_error(RuntimeError)
73
+ end
74
+
75
+ it 'uses detailed-exitcodes with catch_changes' do
76
+ expect(File).to receive(:exist?).with('inventory.yaml').and_return(false)
77
+ expect(dummy_class).to receive(:localhost_inventory_hash).and_return(localhost_inventory_hash)
78
+ expect(dummy_class).to receive(:target_in_inventory?).and_return(true)
79
+ expect(dummy_class).to receive(:create_manifest_file).with(manifest).and_return('/bla.pp')
80
+ expect(dummy_class).to receive(:run_command).with(command, 'litmus_localhost', config: nil, inventory: localhost_inventory_hash).and_return(result)
81
+ dummy_class.apply_manifest(manifest, catch_changes: true)
82
+ end
83
+
84
+ it 'uses raises exception for multiple options' do
85
+ expect { dummy_class.apply_manifest(manifest, catch_changes: true, expect_failures: true) }
86
+ .to raise_error(RuntimeError, 'please specify only one of `catch_changes`, `expect_changes`, `catch_failures` or `expect_failures`')
87
+ end
88
+ end
37
89
  end
38
90
 
39
91
  describe '.run_shell' do
40
92
  let(:command_to_run) { "puts 'doot'" }
41
93
  let(:result) { ['result' => { 'exit_code' => 0, 'stdout' => nil, 'stderr' => nil }] }
42
- let(:inventory_hash) { Hash.new(0) }
94
+ let(:inventory_hash) { { 'groups' => [{ 'name' => 'ssh_nodes', 'nodes' => [{ 'name' => 'some.host' }] }] } }
95
+ let(:localhost_inventory_hash) { { 'groups' => [{ 'name' => 'local', 'nodes' => [{ 'name' => 'litmus_localhost', 'config' => { 'transport' => 'local' } }] }] } }
43
96
 
44
97
  it 'responds to run_shell' do
45
98
  expect(dummy_class).to respond_to(:run_shell).with(1..2).arguments
@@ -48,7 +101,10 @@ RSpec.describe PuppetLitmus::Serverspec do
48
101
  context 'when running against localhost and no inventory.yaml file' do
49
102
  it 'does run_shell against localhost without error' do
50
103
  stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'localhost'))
51
- expect(dummy_class).to receive(:run_command).with(command_to_run, 'localhost', config: nil, inventory: nil).and_return(result)
104
+ expect(File).to receive(:exist?).with('inventory.yaml').and_return(false)
105
+ expect(dummy_class).to receive(:localhost_inventory_hash).and_return(localhost_inventory_hash)
106
+ expect(dummy_class).to receive(:target_in_inventory?).and_return(true)
107
+ expect(dummy_class).to receive(:run_command).with(command_to_run, 'litmus_localhost', config: nil, inventory: localhost_inventory_hash).and_return(result)
52
108
  expect { dummy_class.run_shell(command_to_run) }.not_to raise_error
53
109
  end
54
110
  end
@@ -56,7 +112,9 @@ RSpec.describe PuppetLitmus::Serverspec do
56
112
  context 'when running against remote host' do
57
113
  it 'does run_shell against remote host without error' do
58
114
  stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'some.host'))
115
+ expect(File).to receive(:exist?).with('inventory.yaml').and_return(true)
59
116
  expect(dummy_class).to receive(:inventory_hash_from_inventory_file).and_return(inventory_hash)
117
+ expect(dummy_class).to receive(:target_in_inventory?).and_return(true)
60
118
  expect(dummy_class).to receive(:run_command).with(command_to_run, 'some.host', config: nil, inventory: inventory_hash).and_return(result)
61
119
  expect { dummy_class.run_shell(command_to_run) }.not_to raise_error
62
120
  end
@@ -71,7 +129,8 @@ RSpec.describe PuppetLitmus::Serverspec do
71
129
  let(:result_success) {[{'node'=>'some.host','target'=>'some.host','action'=>'upload','object'=>'C:\foo\bar.ps1','status'=>'success','result'=>{'_output'=>'Uploaded \'C:\foo\bar.ps1\' to \'some.host:C:\bar\''}}]}
72
130
  let(:result_failure) {[{'node'=>'some.host','target'=>'some.host','action'=>nil,'object'=>nil,'status'=>'failure','result'=>{'_error'=>{'kind'=>'puppetlabs.tasks/task_file_error','msg'=>'No such file or directory @ rb_sysopen - /nonexistant/file/path','details'=>{},'issue_code'=>'WRITE_ERROR'}}}]}
73
131
  # rubocop:enable SpaceInsideHashLiteralBraces, SpaceInsideBlockBraces, SpaceAroundOperators, LineLength, SpaceAfterComma
74
- let(:inventory_hash) { Hash.new(0) }
132
+ let(:inventory_hash) { { 'groups' => [{ 'name' => 'local', 'nodes' => [{ 'name' => 'some.host', 'config' => { 'transport' => 'local' } }] }] } }
133
+ let(:localhost_inventory_hash) { { 'groups' => [{ 'name' => 'local', 'nodes' => [{ 'name' => 'litmus_localhost', 'config' => { 'transport' => 'local' } }] }] } }
75
134
 
76
135
  it 'responds to run_shell' do
77
136
  expect(dummy_class).to respond_to(:bolt_upload_file).with(2..3).arguments
@@ -80,14 +139,19 @@ RSpec.describe PuppetLitmus::Serverspec do
80
139
  context 'when upload returns success' do
81
140
  it 'does upload_file against remote host without error' do
82
141
  stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'some.host'))
142
+ expect(File).to receive(:exist?).with('inventory.yaml').and_return(true)
83
143
  expect(dummy_class).to receive(:inventory_hash_from_inventory_file).and_return(inventory_hash)
144
+ expect(dummy_class).to receive(:target_in_inventory?).and_return(true)
84
145
  expect(dummy_class).to receive(:upload_file).with(local, remote, 'some.host', options: {}, config: nil, inventory: inventory_hash).and_return(result_success)
85
146
  expect { dummy_class.bolt_upload_file(local, remote) }.not_to raise_error
86
147
  end
87
148
  it 'does upload_file against localhost without error' do
88
149
  stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'localhost'))
150
+ expect(File).to receive(:exist?).with('inventory.yaml').and_return(false)
151
+ expect(dummy_class).to receive(:localhost_inventory_hash).and_return(localhost_inventory_hash)
89
152
  expect(dummy_class).not_to receive(:inventory_hash_from_inventory_file)
90
- expect(dummy_class).to receive(:upload_file).with(local, remote, 'localhost', options: {}, config: nil, inventory: nil).and_return(result_success)
153
+ expect(dummy_class).to receive(:target_in_inventory?).and_return(true)
154
+ expect(dummy_class).to receive(:upload_file).with(local, remote, 'litmus_localhost', options: {}, config: nil, inventory: localhost_inventory_hash).and_return(result_success)
91
155
  expect { dummy_class.bolt_upload_file(local, remote) }.not_to raise_error
92
156
  end
93
157
  end
@@ -95,13 +159,17 @@ RSpec.describe PuppetLitmus::Serverspec do
95
159
  context 'when upload returns failure' do
96
160
  it 'does upload_file gives runtime error for failure' do
97
161
  stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'some.host'))
162
+ expect(File).to receive(:exist?).with('inventory.yaml').and_return(true)
98
163
  expect(dummy_class).to receive(:inventory_hash_from_inventory_file).and_return(inventory_hash)
164
+ expect(dummy_class).to receive(:target_in_inventory?).and_return(true)
99
165
  expect(dummy_class).to receive(:upload_file).with(local, remote, 'some.host', options: {}, config: nil, inventory: inventory_hash).and_return(result_failure)
100
166
  expect { dummy_class.bolt_upload_file(local, remote) }.to raise_error(RuntimeError, %r{upload file failed})
101
167
  end
102
168
  it 'returns the exit code and error message when expecting failure' do
103
169
  stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'some.host'))
170
+ expect(File).to receive(:exist?).with('inventory.yaml').and_return(true)
104
171
  expect(dummy_class).to receive(:inventory_hash_from_inventory_file).and_return(inventory_hash)
172
+ expect(dummy_class).to receive(:target_in_inventory?).and_return(true)
105
173
  expect(dummy_class).to receive(:upload_file).with(local, remote, 'some.host', options: {}, config: nil, inventory: inventory_hash).and_return(result_failure)
106
174
  method_result = dummy_class.bolt_upload_file(local, remote, expect_failures: true)
107
175
  expect(method_result.exit_code).to be(255)
@@ -113,7 +181,8 @@ RSpec.describe PuppetLitmus::Serverspec do
113
181
  describe '.bolt_run_script' do
114
182
  let(:script) { '/tmp/script.sh' }
115
183
  let(:result) { ['result' => { 'exit_code' => 0, 'stdout' => nil, 'stderr' => nil }] }
116
- let(:inventory_hash) { Hash.new(0) }
184
+ let(:inventory_hash) { { 'groups' => [{ 'name' => 'local', 'nodes' => [{ 'name' => 'some.host', 'config' => { 'transport' => 'local' } }] }] } }
185
+ let(:localhost_inventory_hash) { { 'groups' => [{ 'name' => 'local', 'nodes' => [{ 'name' => 'litmus_localhost', 'config' => { 'transport' => 'local' } }] }] } }
117
186
 
118
187
  it 'responds to bolt_run_script' do
119
188
  expect(dummy_class).to respond_to(:bolt_run_script).with(1..2).arguments
@@ -122,8 +191,11 @@ RSpec.describe PuppetLitmus::Serverspec do
122
191
  context 'when running against localhost and no inventory.yaml file' do
123
192
  it 'does bolt_run_script against localhost without error' do
124
193
  stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'localhost'))
194
+ expect(File).to receive(:exist?).with('inventory.yaml').and_return(false)
195
+ expect(dummy_class).to receive(:localhost_inventory_hash).and_return(localhost_inventory_hash)
125
196
  expect(dummy_class).not_to receive(:inventory_hash_from_inventory_file)
126
- expect(dummy_class).to receive(:run_script).with(script, 'localhost', [], options: {}, config: nil, inventory: nil).and_return(result)
197
+ expect(dummy_class).to receive(:target_in_inventory?).and_return(true)
198
+ expect(dummy_class).to receive(:run_script).with(script, 'litmus_localhost', [], options: {}, config: nil, inventory: localhost_inventory_hash).and_return(result)
127
199
  expect { dummy_class.bolt_run_script(script) }.not_to raise_error
128
200
  end
129
201
  end
@@ -131,7 +203,9 @@ RSpec.describe PuppetLitmus::Serverspec do
131
203
  context 'when running against remote host' do
132
204
  it 'does bolt_run_script against remote host without error' do
133
205
  stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'some.host'))
206
+ expect(File).to receive(:exist?).with('inventory.yaml').and_return(true)
134
207
  expect(dummy_class).to receive(:inventory_hash_from_inventory_file)
208
+ expect(dummy_class).to receive(:target_in_inventory?).and_return(true)
135
209
  expect(dummy_class).to receive(:run_script).with(script, 'some.host', [], options: {}, config: nil, inventory: nil).and_return(result)
136
210
  expect { dummy_class.bolt_run_script(script) }.not_to raise_error
137
211
  end
@@ -140,8 +214,11 @@ RSpec.describe PuppetLitmus::Serverspec do
140
214
  context 'when running with arguments' do
141
215
  it 'does bolt_run_script with arguments without error' do
142
216
  stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'localhost'))
217
+ expect(File).to receive(:exist?).with('inventory.yaml').and_return(false)
218
+ expect(dummy_class).to receive(:localhost_inventory_hash).and_return(localhost_inventory_hash)
143
219
  expect(dummy_class).not_to receive(:inventory_hash_from_inventory_file)
144
- expect(dummy_class).to receive(:run_script).with(script, 'localhost', ['doot'], options: {}, config: nil, inventory: nil).and_return(result)
220
+ expect(dummy_class).to receive(:target_in_inventory?).and_return(true)
221
+ expect(dummy_class).to receive(:run_script).with(script, 'litmus_localhost', ['doot'], options: {}, config: nil, inventory: localhost_inventory_hash).and_return(result)
145
222
  expect { dummy_class.bolt_run_script(script, arguments: ['doot']) }.not_to raise_error
146
223
  end
147
224
  end
@@ -157,7 +234,7 @@ RSpec.describe PuppetLitmus::Serverspec do
157
234
  let(:result_structured_task_success){ [{'node'=>'some.host','target'=>'some.host','action'=>'task','object'=>'testtask::structured','status'=>'success','result'=>{'key1'=>'foo','key2'=>'bar'}}]}
158
235
  let(:result_failure) {[{'node'=>'some.host','target'=>'some.host','action'=>'task','object'=>'testtask::unstructured','status'=>'failure','result'=>{'_error'=>{'msg'=>'FAILURE!','kind'=>'puppetlabs.tasks/task-error','details'=>{'exitcode'=>123}}}}]}
159
236
  # rubocop:enable SpaceInsideHashLiteralBraces, SpaceBeforeBlockBraces, SpaceInsideBlockBraces, SpaceAroundOperators, LineLength, SpaceAfterComma
160
- let(:inventory_hash) { Hash.new(0) }
237
+ let(:inventory_hash) { { 'groups' => [{ 'name' => 'local', 'nodes' => [{ 'name' => 'some.host', 'config' => { 'transport' => 'local' } }] }] } }
161
238
 
162
239
  it 'responds to bolt_run_task' do
163
240
  expect(dummy_class).to respond_to(:run_bolt_task).with(2..3).arguments
@@ -166,20 +243,26 @@ RSpec.describe PuppetLitmus::Serverspec do
166
243
  context 'when bolt returns success' do
167
244
  it 'does bolt_task_run gives no runtime error for success' do
168
245
  stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'some.host'))
246
+ expect(File).to receive(:exist?).with('inventory.yaml').and_return(true)
169
247
  expect(dummy_class).to receive(:inventory_hash_from_inventory_file).and_return(inventory_hash)
248
+ expect(dummy_class).to receive(:target_in_inventory?).and_return(true)
170
249
  expect(dummy_class).to receive(:run_task).with(task_name, 'some.host', params, config: config_data, inventory: inventory_hash).and_return(result_unstructured_task_success)
171
250
  expect { dummy_class.run_bolt_task(task_name, params, opts: {}) }.not_to raise_error
172
251
  end
173
252
  it 'returns stdout for unstructured-data tasks' do
174
253
  stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'some.host'))
254
+ expect(File).to receive(:exist?).with('inventory.yaml').and_return(true)
175
255
  expect(dummy_class).to receive(:inventory_hash_from_inventory_file).and_return(inventory_hash)
256
+ expect(dummy_class).to receive(:target_in_inventory?).and_return(true)
176
257
  expect(dummy_class).to receive(:run_task).with(task_name, 'some.host', params, config: config_data, inventory: inventory_hash).and_return(result_unstructured_task_success)
177
258
  method_result = dummy_class.run_bolt_task(task_name, params, opts: {})
178
259
  expect(method_result.stdout).to eq('SUCCESS!')
179
260
  end
180
261
  it 'returns structured output for structured-data tasks' do
181
262
  stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'some.host'))
263
+ expect(File).to receive(:exist?).with('inventory.yaml').and_return(true)
182
264
  expect(dummy_class).to receive(:inventory_hash_from_inventory_file).and_return(inventory_hash)
265
+ expect(dummy_class).to receive(:target_in_inventory?).and_return(true)
183
266
  expect(dummy_class).to receive(:run_task).with(task_name, 'some.host', params, config: config_data, inventory: inventory_hash).and_return(result_structured_task_success)
184
267
  method_result = dummy_class.run_bolt_task(task_name, params, opts: {})
185
268
  expect(method_result.stdout).to eq('{"key1"=>"foo", "key2"=>"bar"}')
@@ -191,13 +274,17 @@ RSpec.describe PuppetLitmus::Serverspec do
191
274
  context 'when bolt returns failure' do
192
275
  it 'does bolt_task_run gives runtime error for failure' do
193
276
  stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'some.host'))
277
+ expect(File).to receive(:exist?).with('inventory.yaml').and_return(true)
194
278
  expect(dummy_class).to receive(:inventory_hash_from_inventory_file).and_return(inventory_hash)
279
+ expect(dummy_class).to receive(:target_in_inventory?).and_return(true)
195
280
  expect(dummy_class).to receive(:run_task).with(task_name, 'some.host', params, config: config_data, inventory: inventory_hash).and_return(result_failure)
196
281
  expect { dummy_class.run_bolt_task(task_name, params, opts: {}) }.to raise_error(RuntimeError, %r{task failed})
197
282
  end
198
283
  it 'returns the exit code and error message when expecting failure' do
199
284
  stub_const('ENV', ENV.to_hash.merge('TARGET_HOST' => 'some.host'))
285
+ expect(File).to receive(:exist?).with('inventory.yaml').and_return(true)
200
286
  expect(dummy_class).to receive(:inventory_hash_from_inventory_file).and_return(inventory_hash)
287
+ expect(dummy_class).to receive(:target_in_inventory?).and_return(true)
201
288
  expect(dummy_class).to receive(:run_task).with(task_name, 'some.host', params, config: config_data, inventory: inventory_hash).and_return(result_failure)
202
289
  method_result = dummy_class.run_bolt_task(task_name, params, expect_failures: true)
203
290
  expect(method_result.exit_code).to be(123)
data/spec/spec_helper.rb CHANGED
@@ -6,9 +6,14 @@ require 'puppet_litmus'
6
6
  if ENV['COVERAGE'] == 'yes'
7
7
  require 'simplecov'
8
8
 
9
- SimpleCov.formatters = [
10
- SimpleCov::Formatter::HTMLFormatter,
11
- ]
9
+ if ENV['CI'] == 'true'
10
+ require 'codecov'
11
+ SimpleCov.formatter = SimpleCov::Formatter::Codecov
12
+ else
13
+ SimpleCov.formatters = [
14
+ SimpleCov::Formatter::HTMLFormatter,
15
+ ]
16
+ end
12
17
  SimpleCov.start do
13
18
  track_files 'lib/**/*.rb'
14
19
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppet_litmus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-08-30 00:00:00.000000000 Z
11
+ date: 2019-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bolt
@@ -111,15 +111,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
111
111
  version: '0'
112
112
  requirements: []
113
113
  rubyforge_project:
114
- rubygems_version: 2.7.6
114
+ rubygems_version: 2.7.10
115
115
  signing_key:
116
116
  specification_version: 4
117
117
  summary: Providing a simple command line tool for puppet content creators, to enable
118
118
  simple and complex test deployments.
119
119
  test_files:
120
- - spec/data/inventory.yaml
121
- - spec/lib/puppet_litmus/serverspec_spec.rb
122
- - spec/lib/puppet_litmus/inventory_manipulation_spec.rb
120
+ - spec/spec_helper.rb
123
121
  - spec/lib/puppet_litmus/rake_tasks_spec.rb
124
122
  - spec/lib/puppet_litmus/version_spec.rb
125
- - spec/spec_helper.rb
123
+ - spec/lib/puppet_litmus/inventory_manipulation_spec.rb
124
+ - spec/lib/puppet_litmus/serverspec_spec.rb
125
+ - spec/data/inventory.yaml