puppet_litmus 0.12.0 → 0.13.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: 41776234e4a62f7965314cc8c40d33a91ae86c5718a3af4dc3041713c53b86f0
4
- data.tar.gz: a7060ae471bc8f197fc33af71a65bb5dd11cae2d54d139fb607c4e632c6c9590
3
+ metadata.gz: 8fc34d2ba2485c4d4e6cedecc99642a4f1acf98c0861598af4f63b732b1de088
4
+ data.tar.gz: 12cc1dec19bf0ef86b9941870939d0bf2b9cc6251495543be40a33481e747be7
5
5
  SHA512:
6
- metadata.gz: ff8a621cb6df184f8b30af5083fd1eefc945986ae2620df2cc62a8168bf2bee601ce093bbe41baa2ae922bd7796588a3be33413093ba003e1f8b39f72fee1ac0
7
- data.tar.gz: 4533bda3c00d1e45463ed4c4f52d90e719610f7895ace22d7b8f2d5d24f4c0407a0280d874dbb554d769e97d06f32e506d1a8a9b1faddde6bb85269417a114e9
6
+ metadata.gz: 0c1532fb9b58f22974322d139e0775b4821993b5bb03748d7dbff4b1704ea4b400e80e755225a9a410ae7f96c0d1489c59f58bf988923a612c26e8f190c78718
7
+ data.tar.gz: d88135655e933a2ce8da0b4edf552fca12a85ed552a3d02338e61538d80686b6333eee5ba3411071a41e0459a2437337a9687d471ed99972ab4234203f15c6c0
@@ -6,10 +6,13 @@ module PuppetLitmus; end
6
6
  require 'bolt_spec/run'
7
7
  require 'puppet_litmus/inventory_manipulation'
8
8
  require 'puppet_litmus/puppet_helpers'
9
+ require 'puppet_litmus/rake_helper'
10
+ require 'puppet_litmus/spec_helper_acceptance'
9
11
 
10
12
  # Helper methods for testing puppet content
11
13
  module PuppetLitmus
12
14
  include BoltSpec::Run
13
15
  include PuppetLitmus::InventoryManipulation
14
16
  include PuppetLitmus::PuppetHelpers
17
+ include PuppetLitmus::RakeHelper
15
18
  end
@@ -7,6 +7,7 @@ module PuppetLitmus::InventoryManipulation
7
7
  # @param inventory_full_path [String] path to the inventory.yaml file
8
8
  # @return [Hash] hash of the inventory.yaml file.
9
9
  def inventory_hash_from_inventory_file(inventory_full_path = nil)
10
+ require 'yaml'
10
11
  inventory_full_path = if inventory_full_path.nil?
11
12
  'inventory.yaml'
12
13
  else
@@ -44,6 +45,7 @@ module PuppetLitmus::InventoryManipulation
44
45
  # @param targets [Array]
45
46
  # @return [Array] array of targets.
46
47
  def find_targets(inventory_hash, targets)
48
+ require 'bolt/inventory'
47
49
  if targets.nil?
48
50
  inventory = Bolt::Inventory.new(inventory_hash, nil)
49
51
  targets = inventory.node_names.to_a
@@ -250,6 +252,6 @@ module PuppetLitmus::InventoryManipulation
250
252
  # @param inventory_hash [Hash] hash of the inventory.yaml file
251
253
  # @return inventory.yaml file with feature added to group.
252
254
  def write_to_inventory_file(inventory_hash, inventory_full_path)
253
- File.open(inventory_full_path, 'w+') { |f| f.write(inventory_hash.to_yaml) }
255
+ File.open(inventory_full_path, 'wb+') { |f| f.write(inventory_hash.to_yaml) }
254
256
  end
255
257
  end
@@ -130,6 +130,7 @@ module PuppetLitmus::PuppetHelpers
130
130
  raise "shell failed\n`#{command_to_run}`\n======\n#{result}" if result.first['result']['exit_code'] != 0 && opts[:expect_failures] != true
131
131
 
132
132
  result = OpenStruct.new(exit_code: result.first['result']['exit_code'],
133
+ exit_status: result.first['result']['exit_code'],
133
134
  stdout: result.first['result']['stdout'],
134
135
  stderr: result.first['result']['stderr'])
135
136
  yield result if block_given?
@@ -266,13 +267,13 @@ module PuppetLitmus::PuppetHelpers
266
267
  # @param command [String] The puppet command causing the error.
267
268
  # @param result [Array] The result struct containing the result
268
269
  def report_puppet_apply_error(command, result, acceptable_exit_codes)
269
- puppet_apply_error = <<-ERROR
270
- apply manifest failed
271
- `#{command}`
272
- with exit code #{result.first['result']['exit_code']} (expected: #{acceptable_exit_codes})
273
- ====== Start output of failed Puppet apply ======
274
- #{puppet_output(result)}
275
- ====== End output of failed Puppet apply ======
270
+ puppet_apply_error = <<~ERROR
271
+ apply manifest failed
272
+ `#{command}`
273
+ with exit code #{result.first['result']['exit_code']} (expected: #{acceptable_exit_codes})
274
+ ====== Start output of failed Puppet apply ======
275
+ #{puppet_output(result)}
276
+ ====== End output of failed Puppet apply ======
276
277
  ERROR
277
278
  raise puppet_apply_error
278
279
  end
@@ -282,12 +283,12 @@ with exit code #{result.first['result']['exit_code']} (expected: #{acceptable_ex
282
283
  # @param command [String] The puppet command causing the error.
283
284
  # @param result [Array] The result struct containing the result
284
285
  def report_puppet_apply_change(command, result)
285
- puppet_apply_changes = <<-ERROR
286
- apply manifest expected no changes
287
- `#{command}`
288
- ====== Start output of Puppet apply with unexpected changes ======
289
- #{puppet_output(result)}
290
- ====== End output of Puppet apply with unexpected changes ======
286
+ puppet_apply_changes = <<~ERROR
287
+ apply manifest expected no changes
288
+ `#{command}`
289
+ ====== Start output of Puppet apply with unexpected changes ======
290
+ #{puppet_output(result)}
291
+ ====== End output of Puppet apply with unexpected changes ======
291
292
  ERROR
292
293
  raise puppet_apply_changes
293
294
  end
@@ -0,0 +1,210 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppetLitmus; end # rubocop:disable Style/Documentation
4
+
5
+ # helper methods for the litmus rake tasks
6
+ module PuppetLitmus::RakeHelper
7
+ DEFAULT_CONFIG_DATA ||= { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }.freeze
8
+ VALID_PROVISIONERS ||= %w[abs docker docker_exp vagrant vmpooler].freeze
9
+
10
+ # Gets a string representing the operating system and version.
11
+ #
12
+ # @param metadata [Hash] metadata to parse for operating system info
13
+ # @return [String] the operating system string with version info for use in provisioning.
14
+ def get_metadata_operating_systems(metadata)
15
+ return unless metadata.is_a?(Hash)
16
+ return unless metadata['operatingsystem_support'].is_a?(Array)
17
+
18
+ metadata['operatingsystem_support'].each do |os_info|
19
+ next unless os_info['operatingsystem'] && os_info['operatingsystemrelease']
20
+
21
+ os_name = case os_info['operatingsystem']
22
+ when 'Amazon', 'Archlinux', 'AIX', 'OSX'
23
+ next
24
+ when 'OracleLinux'
25
+ 'oracle'
26
+ when 'Windows'
27
+ 'win'
28
+ else
29
+ os_info['operatingsystem'].downcase
30
+ end
31
+
32
+ os_info['operatingsystemrelease'].each do |release|
33
+ version = case os_name
34
+ when 'ubuntu', 'osx'
35
+ release.sub('.', '')
36
+ when 'sles'
37
+ release.gsub(%r{ SP[14]}, '')
38
+ when 'win'
39
+ release = release.delete('.') if release.include? '8.1'
40
+ release.sub('Server', '').sub('10', '10-pro')
41
+ else
42
+ release
43
+ end
44
+
45
+ yield "#{os_name}-#{version.downcase}-x86_64".delete(' ')
46
+ end
47
+ end
48
+ end
49
+
50
+ # Executes a command on the test runner.
51
+ #
52
+ # @param command [String] command to execute.
53
+ # @return [Object] the standard out stream.
54
+ def run_local_command(command)
55
+ require 'open3'
56
+ stdout, stderr, status = Open3.capture3(command)
57
+ error_message = "Attempted to run\ncommand:'#{command}'\nstdout:#{stdout}\nstderr:#{stderr}"
58
+ raise error_message unless status.to_i.zero?
59
+
60
+ stdout
61
+ end
62
+
63
+ # Builds all the modules in a specified module
64
+ #
65
+ # @param source_folder [String] the folder to get the modules from
66
+ # @return [Array] an array of module tar's
67
+ def build_modules_in_folder(source_folder)
68
+ folder_list = Dir.entries(source_folder).reject { |f| File.directory? f }
69
+ module_tars = []
70
+ folder_list.each do |folder|
71
+ file = File.new(File.join(source_folder, folder))
72
+ next if File.symlink?(file)
73
+
74
+ opts = {}
75
+ opts[:module_dir] = file.path
76
+ opts[:'target-dir'] = File.join(Dir.pwd, 'pkg')
77
+ opts[:force] = true
78
+ # remove old build folder if exists, before we build afresh
79
+ FileUtils.rm_rf(builder.build_dir) if File.directory?(builder.build_dir)
80
+
81
+ # build_module
82
+ module_tar = build_module(opts)
83
+ module_tars.push(File.new(module_tar))
84
+ end
85
+ module_tars
86
+ end
87
+
88
+ def provision(provisioner, platform, inventory_vars)
89
+ require 'bolt_spec/run'
90
+ include BoltSpec::Run
91
+ raise "the provision module was not found in #{DEFAULT_CONFIG_DATA['modulepath']}, please amend the .fixtures.yml file" unless
92
+ File.directory?(File.join(DEFAULT_CONFIG_DATA['modulepath'], 'provision'))
93
+
94
+ unless VALID_PROVISIONERS.include?(provisioner)
95
+ raise "Unknown provisioner '#{provisioner}', try #{VALID_PROVISIONERS.join('/')}"
96
+ end
97
+
98
+ params = if inventory_vars.nil?
99
+ { 'action' => 'provision', 'platform' => platform, 'inventory' => Dir.pwd }
100
+ else
101
+ { 'action' => 'provision', 'platform' => platform, 'inventory' => Dir.pwd, 'vars' => inventory_vars }
102
+ end
103
+ run_task("provision::#{provisioner}", 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil)
104
+ end
105
+
106
+ def provision_list(provision_hash, key)
107
+ provisioner = provision_hash[key]['provisioner']
108
+ inventory_vars = provision_hash[key]['vars']
109
+ # Splat the params into environment variables to pass to the provision task but only in this runspace
110
+ provision_hash[key]['params']&.each { |k, value| ENV[k.upcase] = value.to_s }
111
+ results = []
112
+ provision_hash[key]['images'].each do |image|
113
+ results << provision(provisioner, image, inventory_vars)
114
+ end
115
+ results
116
+ end
117
+
118
+ def tear_down_nodes(targets, inventory_hash)
119
+ require 'bolt_spec/run'
120
+ include BoltSpec::Run
121
+ config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
122
+ 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'))
123
+
124
+ results = {}
125
+ targets.each do |node_name|
126
+ next if node_name == 'litmus_localhost'
127
+
128
+ result = tear_down(node_name, inventory_hash)
129
+ results[node_name] = result unless result == []
130
+ end
131
+ results
132
+ end
133
+
134
+ def tear_down(node_name, inventory_hash)
135
+ # how do we know what provisioner to use
136
+ node_facts = facts_from_node(inventory_hash, node_name)
137
+ return [] unless VALID_PROVISIONERS.include?(node_facts['provisioner'])
138
+
139
+ params = { 'action' => 'tear_down', 'node_name' => node_name, 'inventory' => Dir.pwd }
140
+ run_task("provision::#{node_facts['provisioner']}", 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil)
141
+ end
142
+
143
+ def install_agent(collection, targets, inventory_hash)
144
+ require 'bolt_spec/run'
145
+ include BoltSpec::Run
146
+ params = if collection.nil?
147
+ {}
148
+ else
149
+ { 'collection' => collection }
150
+ end
151
+ raise "puppet_agent was not found in #{DEFAULT_CONFIG_DATA['modulepath']}, please amend the .fixtures.yml file" unless File.directory?(File.join(DEFAULT_CONFIG_DATA['modulepath'], 'puppet_agent'))
152
+
153
+ run_task('puppet_agent::install', targets, params, config: DEFAULT_CONFIG_DATA, inventory: inventory_hash)
154
+ end
155
+
156
+ def configure_path(inventory_hash)
157
+ results = []
158
+ # fix the path on ssh_nodes
159
+ unless inventory_hash['groups'].select { |group| group['name'] == 'ssh_nodes' }.size.zero?
160
+ results = run_command('echo PATH="$PATH:/opt/puppetlabs/puppet/bin" > /etc/environment',
161
+ 'ssh_nodes', config: nil, inventory: inventory_hash)
162
+ end
163
+ results
164
+ end
165
+
166
+ def build_module(opts)
167
+ # old cli_way
168
+ # pdk_build_command = 'bundle exec pdk build --force'
169
+ # stdout, stderr, _status = Open3.capture3(pdk_build_command)
170
+ # raise "Failed to run 'pdk_build_command',#{stdout} and #{stderr}" if (stderr =~ %r{completed successfully}).nil?
171
+ require 'pdk/module/build'
172
+ require 'pdk/util'
173
+
174
+ builder = PDK::Module::Build.new(opts)
175
+ builder.build
176
+ end
177
+
178
+ def install_module(inventory_hash, target_node_name, module_tar)
179
+ require 'bolt_spec/run'
180
+ include BoltSpec::Run
181
+ target_nodes = find_targets(inventory_hash, target_node_name)
182
+ target_string = if target_node_name.nil?
183
+ 'all'
184
+ else
185
+ target_node_name
186
+ end
187
+ run_local_command("bundle exec bolt file upload \"#{module_tar}\" /tmp/#{File.basename(module_tar)} --nodes #{target_string} --inventoryfile inventory.yaml")
188
+ install_module_command = "puppet module install /tmp/#{File.basename(module_tar)}"
189
+ run_command(install_module_command, target_nodes, config: nil, inventory: inventory_hash)
190
+ end
191
+
192
+ def metadata_module_name
193
+ require 'json'
194
+ raise 'Could not find metadata.json' unless File.exist?(File.join(Dir.pwd, 'metadata.json'))
195
+
196
+ metadata = JSON.parse(File.read(File.join(Dir.pwd, 'metadata.json')))
197
+ raise 'Could not read module name from metadata.json' if metadata['name'].nil?
198
+
199
+ metadata['name']
200
+ end
201
+
202
+ def uninstall_module(inventory_hash, target_node_name, module_to_remove = nil)
203
+ require 'bolt_spec/run'
204
+ include BoltSpec::Run
205
+ module_name = module_to_remove || metadata_module_name
206
+ target_nodes = find_targets(inventory_hash, target_node_name)
207
+ install_module_command = "puppet module uninstall #{module_name}"
208
+ run_command(install_module_command, target_nodes, config: nil, inventory: inventory_hash)
209
+ end
210
+ end
@@ -1,100 +1,8 @@
1
1
  # frozen_string_literal: true
2
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
- require 'tty-spinner'
11
-
12
- # helper methods for the litmus rake tasks
13
- module LitmusRakeHelper
14
- # Gets a string representing the operating system and version.
15
- #
16
- # @param metadata [Hash] metadata to parse for operating system info
17
- # @return [String] the operating system string with version info for use in provisioning.
18
- def get_metadata_operating_systems(metadata)
19
- return unless metadata.is_a?(Hash)
20
- return unless metadata['operatingsystem_support'].is_a?(Array)
21
-
22
- metadata['operatingsystem_support'].each do |os_info|
23
- next unless os_info['operatingsystem'] && os_info['operatingsystemrelease']
24
-
25
- os_name = case os_info['operatingsystem']
26
- when 'Amazon', 'Archlinux', 'AIX', 'OSX'
27
- next
28
- when 'OracleLinux'
29
- 'oracle'
30
- when 'Windows'
31
- 'win'
32
- else
33
- os_info['operatingsystem'].downcase
34
- end
35
-
36
- os_info['operatingsystemrelease'].each do |release|
37
- version = case os_name
38
- when 'ubuntu', 'osx'
39
- release.sub('.', '')
40
- when 'sles'
41
- release.gsub(%r{ SP[14]}, '')
42
- when 'win'
43
- release = release.delete('.') if release.include? '8.1'
44
- release.sub('Server', '').sub('10', '10-pro')
45
- else
46
- release
47
- end
48
-
49
- yield "#{os_name}-#{version.downcase}-x86_64".delete(' ')
50
- end
51
- end
52
- end
53
-
54
- # Executes a command on the test runner.
55
- #
56
- # @param command [String] command to execute.
57
- # @return [Object] the standard out stream.
58
- def run_local_command(command)
59
- stdout, stderr, status = Open3.capture3(command)
60
- error_message = "Attempted to run\ncommand:'#{command}'\nstdout:#{stdout}\nstderr:#{stderr}"
61
- raise error_message unless status.to_i.zero?
62
-
63
- stdout
64
- end
65
-
66
- # Builds all the modules in a specified module
67
- #
68
- # @param source_folder [String] the folder to get the modules from
69
- # @return [Array] an array of module tar's
70
- def build_modules_in_folder(source_folder)
71
- require 'pdk/module/build'
72
- require 'pdk/util'
73
- folder_list = Dir.entries(source_folder).reject { |f| File.directory? f }
74
- module_tars = []
75
- folder_list.each do |folder|
76
- file = File.new(File.join(source_folder, folder))
77
- next if File.symlink?(file)
78
-
79
- opts = {}
80
- opts[:module_dir] = file.path
81
- opts[:'target-dir'] = File.join(Dir.pwd, 'pkg')
82
- opts[:force] = true
83
- builder = PDK::Module::Build.new(opts)
84
-
85
- # remove old build folder if exists, before we build afresh
86
- FileUtils.rm_rf(builder.build_dir) if File.directory?(builder.build_dir)
87
-
88
- # build_module
89
- module_tar = builder.build
90
- module_tars.push(File.new(module_tar))
91
- end
92
- module_tars
93
- end
94
- end
95
-
96
3
  namespace :litmus do
97
- include LitmusRakeHelper
4
+ require 'puppet_litmus/rake_helper'
5
+ include PuppetLitmus::RakeHelper
98
6
  # Prints all supported OSes from metadata.json file.
99
7
  desc 'print all supported OSes from metadata'
100
8
  task :metadata do
@@ -115,23 +23,43 @@ namespace :litmus do
115
23
  provision_hash = YAML.load_file('./provision.yaml')
116
24
  raise "No key #{args[:key]} in ./provision.yaml, see https://github.com/puppetlabs/puppet_litmus/wiki/Overview-of-Litmus#provisioning-via-yaml for examples" if provision_hash[args[:key]].nil?
117
25
 
26
+ Rake::Task['spec_prep'].invoke
27
+
118
28
  provisioner = provision_hash[args[:key]]['provisioner']
119
29
  inventory_vars = provision_hash[args[:key]]['vars']
120
30
  # Splat the params into environment variables to pass to the provision task but only in this runspace
121
- provision_hash[args[:key]]['params']&.each { |key, value| ENV[key.upcase] = value.to_s }
31
+ provision_hash[args[:key]]['params']&.each { |k, value| ENV[k.upcase] = value.to_s }
32
+ results = []
122
33
  failed_image_message = ''
123
34
  provision_hash[args[:key]]['images'].each do |image|
124
- # this is the only way to capture the stdout from the rake task, it will affect pry
125
- capture_rake_output = StringIO.new
126
- $stdout = capture_rake_output
127
- Rake::Task['litmus:provision'].invoke(provisioner, image, inventory_vars)
128
- if $stdout.string =~ %r{.status.=>.failure}
129
- failed_image_message += "=====\n#{image}\n#{$stdout.string}\n"
35
+ if (ENV['CI'] == 'true') || !ENV['DISTELLI_BUILDNUM'].nil?
36
+ progress = Thread.new do
37
+ loop do
38
+ printf '.'
39
+ sleep(10)
40
+ end
41
+ end
42
+ else
43
+ require 'tty-spinner'
44
+ spinner = TTY::Spinner.new("Provisioning #{image} using #{provisioner} provisioner.[:spinner]")
45
+ spinner.auto_spin
46
+ end
47
+ result = provision(provisioner, image, inventory_vars)
48
+
49
+ if (ENV['CI'] == 'true') || !ENV['DISTELLI_BUILDNUM'].nil?
50
+ Thread.kill(progress)
130
51
  else
131
- STDOUT.puts $stdout.string
52
+ spinner.success
132
53
  end
133
- Rake::Task['litmus:provision'].reenable
54
+
55
+ if result.first['status'] != 'success'
56
+ failed_image_message += "=====\n#{result.first['node']}\n#{result.first['result']['_output']}\n"
57
+ else
58
+ STDOUT.puts "#{result.first['result']['node_name']}, #{image}"
59
+ end
60
+ results << result
134
61
  end
62
+
135
63
  raise "Failed to provision with '#{provisioner}'\n #{failed_image_message}" unless failed_image_message.empty?
136
64
  end
137
65
 
@@ -141,21 +69,7 @@ namespace :litmus do
141
69
  # @param :platform [String] OS platform for container or VM to use.
142
70
  desc "provision container/VM - abs/docker/vagrant/vmpooler eg 'bundle exec rake 'litmus:provision[vmpooler, ubuntu-1604-x86_64]'"
143
71
  task :provision, [:provisioner, :platform, :inventory_vars] do |_task, args|
144
- include BoltSpec::Run
145
72
  Rake::Task['spec_prep'].invoke
146
- config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
147
- 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'))
148
-
149
- unless %w[abs docker docker_exp vagrant vmpooler].include?(args[:provisioner])
150
- raise "Unknown provisioner '#{args[:provisioner]}', try abs/docker/docker_exp/vagrant/vmpooler"
151
- end
152
-
153
- params = if args[:inventory_vars].nil?
154
- { 'action' => 'provision', 'platform' => args[:platform], 'inventory' => Dir.pwd }
155
- else
156
- { 'action' => 'provision', 'platform' => args[:platform], 'inventory' => Dir.pwd, 'vars' => args[:inventory_vars] }
157
- end
158
-
159
73
  if (ENV['CI'] == 'true') || !ENV['DISTELLI_BUILDNUM'].nil?
160
74
  progress = Thread.new do
161
75
  loop do
@@ -164,10 +78,11 @@ namespace :litmus do
164
78
  end
165
79
  end
166
80
  else
81
+ require 'tty-spinner'
167
82
  spinner = TTY::Spinner.new("Provisioning #{args[:platform]} using #{args[:provisioner]} provisioner.[:spinner]")
168
83
  spinner.auto_spin
169
84
  end
170
- results = run_task("provision::#{args[:provisioner]}", 'localhost', params, config: config_data, inventory: nil)
85
+ results = provision(args[:provisioner], args[:platform], args[:inventory_vars])
171
86
  if results.first['status'] != 'success'
172
87
  raise "Failed provisioning #{args[:platform]} using #{args[:provisioner]}\n#{results.first}"
173
88
  end
@@ -193,17 +108,11 @@ namespace :litmus do
193
108
  exit 0
194
109
  end
195
110
  puts 'install_agent'
111
+ require 'bolt_spec/run'
196
112
  include BoltSpec::Run
197
113
  Rake::Task['spec_prep'].invoke
198
- config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
199
- params = if args[:collection].nil?
200
- {}
201
- else
202
- { 'collection' => args[:collection] }
203
- end
204
- 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'))
205
-
206
- results = run_task('puppet_agent::install', targets, params, config: config_data, inventory: inventory_hash)
114
+
115
+ results = install_agent(args[:collection], targets, inventory_hash)
207
116
  results.each do |result|
208
117
  if result['status'] != 'success'
209
118
  command_to_run = "bolt task run puppet_agent::install --targets #{result['node']} --inventoryfile inventory.yaml --modulepath #{config_data['modulepath']}"
@@ -217,10 +126,7 @@ namespace :litmus do
217
126
  write_to_inventory_file(inventory_hash, 'inventory.yaml')
218
127
 
219
128
  # fix the path on ssh_nodes
220
- unless inventory_hash['groups'].select { |group| group['name'] == 'ssh_nodes' }.size.zero?
221
- results = run_command('echo PATH="$PATH:/opt/puppetlabs/puppet/bin" > /etc/environment',
222
- 'ssh_nodes', config: nil, inventory: inventory_hash)
223
- end
129
+ results = configure_path(inventory_hash)
224
130
 
225
131
  results.each do |result|
226
132
  if result['status'] != 'success'
@@ -253,6 +159,7 @@ namespace :litmus do
253
159
  module_tars.each do |module_tar|
254
160
  print "#{File.basename(module_tar)} "
255
161
  end
162
+ require 'bolt_spec/run'
256
163
  include BoltSpec::Run
257
164
  puts "\nSending"
258
165
  module_tars.each do |module_tar|
@@ -279,33 +186,18 @@ namespace :litmus do
279
186
  puts 'No targets found'
280
187
  exit 0
281
188
  end
282
- include BoltSpec::Run
283
- # old cli_way
284
- # pdk_build_command = 'bundle exec pdk build --force'
285
- # stdout, stderr, _status = Open3.capture3(pdk_build_command)
286
- # raise "Failed to run 'pdk_build_command',#{stdout} and #{stderr}" if (stderr =~ %r{completed successfully}).nil?
287
- require 'pdk/module/build'
288
- require 'pdk/util'
289
189
 
290
190
  opts = {}
291
191
  opts[:force] = true
292
- builder = PDK::Module::Build.new(opts)
293
- module_tar = builder.build
192
+ module_tar = build_module(opts)
294
193
  puts 'Built'
295
194
 
296
195
  # module_tar = Dir.glob('pkg/*.tar.gz').max_by { |f| File.mtime(f) }
297
196
  raise "Unable to find package in 'pkg/*.tar.gz'" if module_tar.nil?
298
197
 
299
- target_string = if args[:target_node_name].nil?
300
- 'all'
301
- else
302
- args[:target_node_name]
303
- end
304
- run_local_command("bundle exec bolt file upload \"#{module_tar}\" /tmp/#{File.basename(module_tar)} --nodes #{target_string} --inventoryfile inventory.yaml")
305
- install_module_command = "puppet module install /tmp/#{File.basename(module_tar)}"
306
- result = run_command(install_module_command, target_nodes, config: nil, inventory: inventory_hash)
198
+ result = install_module(inventory_hash, args[:target_node_name], module_tar)
307
199
 
308
- raise "Failed trying to run '#{install_module_command}' against inventory." unless result.is_a?(Array)
200
+ raise "Failed trying to run 'puppet module install /tmp/#{File.basename(module_tar)}' against inventory." unless result.is_a?(Array)
309
201
 
310
202
  result.each do |node|
311
203
  puts "#{node['node']} failed #{node['result']}" if node['status'] != 'success'
@@ -337,25 +229,14 @@ namespace :litmus do
337
229
  puts 'No targets found'
338
230
  exit 0
339
231
  end
340
- include BoltSpec::Run
341
232
  Rake::Task['spec_prep'].invoke
342
- config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
343
- 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'))
344
-
345
233
  bad_results = []
346
- targets.each do |node_name|
347
- next if node_name == 'litmus_localhost'
348
-
349
- # how do we know what provisioner to use
350
- node_facts = facts_from_node(inventory_hash, node_name)
351
- next unless %w[abs docker docker_exp vagrant vmpooler].include?(node_facts['provisioner'])
352
-
353
- params = { 'action' => 'tear_down', 'node_name' => node_name, 'inventory' => Dir.pwd }
354
- result = run_task("provision::#{node_facts['provisioner']}", 'localhost', params, config: config_data, inventory: nil)
234
+ results = tear_down_nodes(targets, inventory_hash)
235
+ results.each do |node, result|
355
236
  if result.first['status'] != 'success'
356
- bad_results << "#{node_name}, #{result.first['result']['_error']['msg']}"
237
+ bad_results << "#{node}, #{result.first['result']['_error']['msg']}"
357
238
  else
358
- print "#{node_name}, "
239
+ puts "#{node}: #{result.first['status']}"
359
240
  end
360
241
  end
361
242
  puts ''
@@ -366,9 +247,44 @@ namespace :litmus do
366
247
  end
367
248
  end
368
249
 
250
+ # Uninstall the puppet module under test on a collection of nodes
251
+ #
252
+ # @param :target_node_name [Array] nodes on which to install a puppet module for testing.
253
+ # @param :module_name [String] module name to be uninstalled
254
+ desc 'uninstall_module - uninstall module'
255
+ task :uninstall_module, [:target_node_name, :module_name] do |_task, args|
256
+ inventory_hash = inventory_hash_from_inventory_file
257
+ target_nodes = find_targets(inventory_hash, args[:target_node_name])
258
+ if target_nodes.empty?
259
+ puts 'No targets found'
260
+ exit 0
261
+ end
262
+
263
+ result = uninstall_module(inventory_hash, args[:target_node_name], args[:module_name])
264
+
265
+ raise "Failed trying to run 'puppet module uninstall #{module_name}' against inventory." unless result.is_a?(Array)
266
+
267
+ result.each do |node|
268
+ puts "#{node['node']} failed #{node['result']}" if node['status'] != 'success'
269
+ end
270
+
271
+ puts 'Uninstalled'
272
+ end
273
+
274
+ # Reinstall the puppet module under test on a collection of nodes
275
+ #
276
+ # @param :target_node_name [Array] nodes on which to install a puppet module for testing.
277
+ desc 'reinstall_module - reinstall module'
278
+ task :reinstall_module, [:target_node_name] do |_task, args|
279
+ Rake::Task['litmus:uninstall_module'].invoke(args[:target_node_name])
280
+ Rake::Task['litmus:install_module'].invoke(args[:target_node_name])
281
+ end
282
+
369
283
  namespace :acceptance do
370
- include PuppetLitmus::InventoryManipulation
284
+ require 'rspec/core/rake_task'
371
285
  if File.file?('inventory.yaml')
286
+ require 'puppet_litmus/inventory_manipulation'
287
+ include PuppetLitmus::InventoryManipulation
372
288
  inventory_hash = inventory_hash_from_inventory_file
373
289
  targets = find_targets(inventory_hash, nil)
374
290
 
@@ -406,6 +322,7 @@ namespace :litmus do
406
322
  end
407
323
  end
408
324
 
325
+ require 'parallel'
409
326
  results = Parallel.map(payloads) do |title, test, options|
410
327
  env = options[:env].nil? ? {} : options[:env]
411
328
  stdout, stderr, status = Open3.capture3(env, test)
@@ -421,6 +338,7 @@ namespace :litmus do
421
338
  end
422
339
  Thread.kill(progress)
423
340
  else
341
+ require 'tty-spinner'
424
342
  spinners = TTY::Spinner::Multi.new("[:spinner] Running against #{targets.size} targets.")
425
343
  payloads.each do |title, test, options|
426
344
  env = options[:env].nil? ? {} : options[:env]
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Helper methods for testing puppet content
4
+ module PuppetLitmus
5
+ extend PuppetLitmus::InventoryManipulation
6
+
7
+ def self.configure!
8
+ require 'serverspec'
9
+
10
+ RSpec.configure do |config|
11
+ config.include PuppetLitmus
12
+ end
13
+
14
+ if ENV['TARGET_HOST'].nil? || ENV['TARGET_HOST'] == 'localhost'
15
+ puts 'Running tests against this machine !'
16
+ if Gem.win_platform?
17
+ set :backend, :cmd
18
+ else
19
+ set :backend, :exec
20
+ end
21
+ else
22
+ # load inventory
23
+ inventory_hash = inventory_hash_from_inventory_file
24
+ node_config = config_from_node(inventory_hash, ENV['TARGET_HOST'])
25
+
26
+ if target_in_group(inventory_hash, ENV['TARGET_HOST'], 'docker_nodes')
27
+ host = ENV['TARGET_HOST']
28
+ set :backend, :docker
29
+ set :docker_container, host
30
+ elsif target_in_group(inventory_hash, ENV['TARGET_HOST'], 'ssh_nodes')
31
+ set :backend, :ssh
32
+ options = Net::SSH::Config.for(host)
33
+ options[:user] = node_config.dig('ssh', 'user') unless node_config.dig('ssh', 'user').nil?
34
+ options[:port] = node_config.dig('ssh', 'port') unless node_config.dig('ssh', 'port').nil?
35
+ options[:keys] = node_config.dig('ssh', 'private-key') unless node_config.dig('ssh', 'private-key').nil?
36
+ options[:password] = node_config.dig('ssh', 'password') unless node_config.dig('ssh', 'password').nil?
37
+ # Support both net-ssh 4 and 5.
38
+ # rubocop:disable Metrics/BlockNesting
39
+ options[:verify_host_key] = if node_config.dig('ssh', 'host-key-check').nil?
40
+ # Fall back to SSH behavior. This variable will only be set in net-ssh 5.3+.
41
+ if @strict_host_key_checking.nil? || @strict_host_key_checking
42
+ Net::SSH::Verifiers::Always.new
43
+ else
44
+ # SSH's behavior with StrictHostKeyChecking=no: adds new keys to known_hosts.
45
+ # If known_hosts points to /dev/null, then equivalent to :never where it
46
+ # accepts any key beacuse they're all new.
47
+ Net::SSH::Verifiers::AcceptNewOrLocalTunnel.new
48
+ end
49
+ elsif node_config.dig('ssh', 'host-key-check')
50
+ if defined?(Net::SSH::Verifiers::Always)
51
+ Net::SSH::Verifiers::Always.new
52
+ else
53
+ Net::SSH::Verifiers::Secure.new
54
+ end
55
+ elsif defined?(Net::SSH::Verifiers::Never)
56
+ Net::SSH::Verifiers::Never.new
57
+ else
58
+ Net::SSH::Verifiers::Null.new
59
+ end
60
+ # rubocop:enable Metrics/BlockNesting
61
+ host = if ENV['TARGET_HOST'].include?(':')
62
+ ENV['TARGET_HOST'].split(':').first
63
+ else
64
+ ENV['TARGET_HOST']
65
+ end
66
+ set :host, options[:host_name] || host
67
+ set :ssh_options, options
68
+ set :request_pty, true
69
+ elsif target_in_group(inventory_hash, ENV['TARGET_HOST'], 'winrm_nodes')
70
+ require 'winrm'
71
+
72
+ set :backend, :winrm
73
+ set :os, family: 'windows'
74
+ user = node_config.dig('winrm', 'user') unless node_config.dig('winrm', 'user').nil?
75
+ pass = node_config.dig('winrm', 'password') unless node_config.dig('winrm', 'password').nil?
76
+ endpoint = "http://#{ENV['TARGET_HOST']}:5985/wsman"
77
+
78
+ opts = {
79
+ user: user,
80
+ password: pass,
81
+ endpoint: endpoint,
82
+ operation_timeout: 300,
83
+ }
84
+
85
+ winrm = WinRM::Connection.new opts
86
+ Specinfra.configuration.winrm = winrm
87
+ end
88
+ end
89
+ end
90
+ end
@@ -2,5 +2,5 @@
2
2
 
3
3
  # version of this gem
4
4
  module PuppetLitmus
5
- VERSION ||= '0.12.0'
5
+ VERSION ||= '0.13.0'
6
6
  end
@@ -87,7 +87,7 @@ RSpec.describe PuppetLitmus::PuppetHelpers do
87
87
 
88
88
  describe '.run_shell' do
89
89
  let(:command_to_run) { "puts 'doot'" }
90
- let(:result) { ['result' => { 'exit_code' => 0, 'stdout' => nil, 'stderr' => nil }] }
90
+ let(:result) { ['result' => { 'exit_code' => 0, 'exit_status' => 0, 'stdout' => nil, 'stderr' => nil }] }
91
91
  let(:inventory_hash) { { 'groups' => [{ 'name' => 'ssh_nodes', 'nodes' => [{ 'name' => 'some.host' }] }] } }
92
92
  let(:localhost_inventory_hash) { { 'groups' => [{ 'name' => 'local', 'nodes' => [{ 'name' => 'litmus_localhost', 'config' => { 'transport' => 'local' } }] }] } }
93
93
 
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ load File.expand_path('../../../lib/puppet_litmus/rake_helper.rb', __dir__)
6
+
7
+ RSpec.describe PuppetLitmus::RakeHelper do
8
+ context 'with provision_list' do
9
+ let(:provision_hash) { { 'default' => { 'provisioner' => 'docker', 'images' => ['waffleimage/centos7'] } } }
10
+ let(:results) { [] }
11
+
12
+ it 'calls function' do
13
+ expect(described_class).to receive(:provision).with('docker', 'waffleimage/centos7', nil).and_return(results)
14
+ described_class.provision_list(provision_hash, 'default')
15
+ end
16
+ end
17
+
18
+ context 'with provision' do
19
+ let(:provision_hash) { { 'default' => { 'provisioner' => 'docker', 'images' => ['waffleimage/centos7'] } } }
20
+ let(:results) { [] }
21
+ let(:params) { { 'action' => 'provision', 'platform' => 'waffleimage/centos7', 'inventory' => Dir.pwd } }
22
+
23
+ it 'calls function' do
24
+ allow(File).to receive(:directory?).and_call_original
25
+ allow(File).to receive(:directory?).with(File.join(DEFAULT_CONFIG_DATA['modulepath'], 'provision')).and_return(true)
26
+ allow_any_instance_of(BoltSpec::Run).to receive(:run_task).with('provision::docker', 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil).and_return(results)
27
+ described_class.provision('docker', 'waffleimage/centos7', nil)
28
+ end
29
+ end
30
+
31
+ context 'with tear_down' do
32
+ let(:inventory_hash) do
33
+ { 'groups' =>
34
+ [{ 'name' => 'ssh_nodes', 'nodes' =>
35
+ [{ 'name' => 'some.host', 'facts' => { 'provisioner' => 'docker', 'container_name' => 'foo', 'platform' => 'some.host' } }] }] }
36
+ end
37
+ let(:targets) { ['some.host'] }
38
+ let(:params) { { 'action' => 'tear_down', 'node_name' => 'some.host', 'inventory' => Dir.pwd } }
39
+
40
+ it 'calls function' do
41
+ allow(File).to receive(:directory?).with(File.join(DEFAULT_CONFIG_DATA['modulepath'], 'provision')).and_return(true)
42
+ allow_any_instance_of(BoltSpec::Run).to receive(:run_task).with('provision::docker', 'localhost', params, config: DEFAULT_CONFIG_DATA, inventory: nil).and_return([])
43
+ described_class.tear_down_nodes(targets, inventory_hash)
44
+ end
45
+ end
46
+
47
+ context 'with install_agent' do
48
+ let(:inventory_hash) do
49
+ { 'groups' =>
50
+ [{ 'name' => 'ssh_nodes', 'nodes' =>
51
+ [{ 'name' => 'some.host', 'facts' => { 'provisioner' => 'docker', 'container_name' => 'foo', 'platform' => 'some.host' } }] }] }
52
+ end
53
+ let(:targets) { ['some.host'] }
54
+ let(:params) { { 'collection' => 'puppet6' } }
55
+
56
+ it 'calls function' do
57
+ allow(File).to receive(:directory?).with(File.join(DEFAULT_CONFIG_DATA['modulepath'], 'puppet_agent')).and_return(true)
58
+ allow_any_instance_of(BoltSpec::Run).to receive(:run_task).with('puppet_agent::install', targets, params, config: DEFAULT_CONFIG_DATA, inventory: inventory_hash).and_return([])
59
+ described_class.install_agent('puppet6', targets, inventory_hash)
60
+ end
61
+ end
62
+
63
+ context 'with install_module' do
64
+ let(:inventory_hash) do
65
+ { 'groups' =>
66
+ [{ 'name' => 'ssh_nodes', 'nodes' =>
67
+ [{ 'name' => 'some.host', 'facts' => { 'provisioner' => 'docker', 'container_name' => 'foo', 'platform' => 'some.host' } }] }] }
68
+ end
69
+ let(:module_tar) { '/tmp/foo.tar.gz' }
70
+ let(:targets) { ['some.host'] }
71
+ let(:install_module_command) { "puppet module install /tmp/#{File.basename(module_tar)}" }
72
+
73
+ it 'calls function' do
74
+ allow(Open3).to receive(:capture3).with("bundle exec bolt file upload \"#{module_tar}\" /tmp/#{File.basename(module_tar)} --nodes all --inventoryfile inventory.yaml")
75
+ .and_return(['success', '', 0])
76
+ allow_any_instance_of(BoltSpec::Run).to receive(:run_command).with(install_module_command, targets, config: nil, inventory: inventory_hash).and_return([])
77
+ described_class.install_module(inventory_hash, nil, module_tar)
78
+ end
79
+ end
80
+
81
+ context 'with uninstall module' do
82
+ let(:inventory_hash) do
83
+ { 'groups' =>
84
+ [{ 'name' => 'ssh_nodes', 'nodes' =>
85
+ [{ 'name' => 'some.host', 'facts' => { 'provisioner' => 'docker', 'container_name' => 'foo', 'platform' => 'some.host' } }] }] }
86
+ end
87
+ let(:targets) { ['some.host'] }
88
+ let(:uninstall_module_command) { 'puppet module uninstall foo-bar' }
89
+
90
+ it 'uninstalls module' do
91
+ allow_any_instance_of(BoltSpec::Run).to receive(:run_command).with(uninstall_module_command, targets, config: nil, inventory: inventory_hash).and_return([])
92
+ expect(described_class).to receive(:metadata_module_name).and_return('foo-bar')
93
+ described_class.uninstall_module(inventory_hash, nil)
94
+ end
95
+
96
+ it 'and custom name' do
97
+ allow_any_instance_of(BoltSpec::Run).to receive(:run_command).with(uninstall_module_command, targets, config: nil, inventory: inventory_hash).and_return([])
98
+ described_class.uninstall_module(inventory_hash, nil, 'foo-bar')
99
+ end
100
+ end
101
+
102
+ context 'with module name' do
103
+ let(:metadata) { '{ "name" : "foo-bar" }' }
104
+
105
+ it 'reads module name' do
106
+ allow(File).to receive(:exist?).with(File.join(Dir.pwd, 'metadata.json')).and_return(true)
107
+ allow(File).to receive(:read).with(File.join(Dir.pwd, 'metadata.json')).and_return(metadata)
108
+ name = described_class.metadata_module_name
109
+ expect(name).to eq('foo-bar')
110
+ end
111
+ end
112
+ end
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.12.0
4
+ version: 0.13.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-10-15 00:00:00.000000000 Z
11
+ date: 2019-12-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bolt
@@ -90,6 +90,34 @@ dependencies:
90
90
  - - "<"
91
91
  - !ruby/object:Gem::Version
92
92
  version: 2.0.0
93
+ - !ruby/object:Gem::Dependency
94
+ name: parallel
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ type: :runtime
101
+ prerelease: false
102
+ version_requirements: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ - !ruby/object:Gem::Dependency
108
+ name: rspec
109
+ requirement: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ type: :runtime
115
+ prerelease: false
116
+ version_requirements: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
93
121
  description: " Providing a simple command line tool for puppet content creators,
94
122
  to enable simple and complex test deployments.\n"
95
123
  email:
@@ -103,13 +131,16 @@ files:
103
131
  - lib/puppet_litmus.rb
104
132
  - lib/puppet_litmus/inventory_manipulation.rb
105
133
  - lib/puppet_litmus/puppet_helpers.rb
134
+ - lib/puppet_litmus/rake_helper.rb
106
135
  - lib/puppet_litmus/rake_tasks.rb
136
+ - lib/puppet_litmus/spec_helper_acceptance.rb
107
137
  - lib/puppet_litmus/version.rb
108
138
  - spec/data/doot.tar.gz
109
139
  - spec/data/inventory.yaml
110
140
  - spec/data/jim.yaml
111
141
  - spec/lib/puppet_litmus/inventory_manipulation_spec.rb
112
142
  - spec/lib/puppet_litmus/puppet_helpers_spec.rb
143
+ - spec/lib/puppet_litmus/rake_helper_spec.rb
113
144
  - spec/lib/puppet_litmus/rake_tasks_spec.rb
114
145
  - spec/lib/puppet_litmus/version_spec.rb
115
146
  - spec/spec_helper.rb
@@ -143,6 +174,7 @@ test_files:
143
174
  - spec/data/jim.yaml
144
175
  - spec/lib/puppet_litmus/inventory_manipulation_spec.rb
145
176
  - spec/lib/puppet_litmus/puppet_helpers_spec.rb
177
+ - spec/lib/puppet_litmus/rake_helper_spec.rb
146
178
  - spec/lib/puppet_litmus/rake_tasks_spec.rb
147
179
  - spec/lib/puppet_litmus/version_spec.rb
148
180
  - spec/spec_helper.rb