puppet_litmus 0.0.1 → 0.1.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
- SHA256:
3
- metadata.gz: 418acded5bb7ca19c9a011892b89b805aaedd642be7e94d004f038640d144c28
4
- data.tar.gz: 20082a2cba1921b73cfb1a0324709253b6f74318916c8c302c8eb917cf7ad2f3
2
+ SHA1:
3
+ metadata.gz: 9caf40c0204bbf7ee96cd7972dc94d1d08fe3789
4
+ data.tar.gz: f08c3e7468e1fc0c17a4ec66dd4df044cb7b21b4
5
5
  SHA512:
6
- metadata.gz: d55ceaa23d4df5e1aed93363f2d3ecd725817064003630728887fb5f10262f079e89e60dc69b86e05f22e560d98f9b9ea1dcf5cad4fc419ad2d56766b538e499
7
- data.tar.gz: d27be055768592ccdbc603b374787a2f4ce555f6c8649bcee071a5beaece65e699bd4cc2b7b22ece8706cb1ecc3ffa26752afd6d61477b9dd443866d20c50e68
6
+ metadata.gz: 630d0081008784dd6d8a0b822a2e69eea7e36c7eafec4263844c70bbed503936f804e2f3cac591d479ae7ccdc7fea7c0b00037a7e7da16063f80d86bdfb37506
7
+ data.tar.gz: 8362adbc00c895e436d1b587a3c4e9f8f66d5b9105fe1fab5d0c2c43a5c84a14db97fa6c9e08d5069a56a3c70ca5de9da57f6b3c9695aaa029b7507690b2a2d3
data/README.md CHANGED
@@ -1,7 +1,14 @@
1
1
  # Litmus
2
2
 
3
+ <div name="logo">
4
+ <img src="resources/litmus-dark-RGB.png"
5
+ style="display: block; margin-left: auto; margin-right: auto;"
6
+ width="50%"
7
+ alt="litmus logo">
8
+ </div>
9
+
3
10
  ## Overview
4
- Litmus provides a simple command line tool for Puppet content creators, to enable both simple and complex test deployments against specifically configured target systems. It is available as a gem, and can be installed by running ```gem install puppet-litmus```.
11
+ Litmus provides a simple command line tool for Puppet content creators, to enable both simple and complex test deployments against specifically configured target systems. It is available as a gem, and can be installed by running ```gem install puppet_litmus```.
5
12
 
6
13
  Litmus allows Puppet module developers to:
7
14
  * provision targets to test against,
@@ -20,25 +27,9 @@ All our documentation is currently available in the [Wiki](https://github.com/pu
20
27
  * [Architecture](https://github.com/puppetlabs/puppet_litmus/wiki/Architecture-of-puppet-litmus) with an explanation of what's going on under the hood
21
28
  * [Step-by-step guide](https://github.com/puppetlabs/puppet_litmus/wiki/Tutorial:-use-Litmus-to-execute-acceptance-tests-with-a-sample-module-(MoTD)) of how to use Litmus with the popular and simple [MoTD Puppet module](https://forge.puppet.com/puppetlabs/motd).
22
29
  * [How to guide](https://github.com/puppetlabs/puppet_litmus/wiki/Converting-a-module-to-use-Litmus) walking through how to use Litmus in a module
23
-
30
+ * [Helper functions](https://github.com/puppetlabs/puppet_litmus/wiki/Helper-Functions-for-Litmus) a guide to the various helper functions within Litmus.
24
31
  ## Known issues
25
32
 
26
- ### PDK and Bolt dependencies infers support on Ruby and Puppet versions
27
-
28
- We are actively working towards the point that where we declare our gem dependencies for PDK and Bolt gems in the puppet_litmus gemspec file [here](https://github.com/puppetlabs/puppet_litmus/blame/master/puppet_litmus.gemspec#L23)
29
-
30
- Bolt has a hard dependency on Puppet 6, and CRI 2.15.1 which is ruby version specific
31
-
32
- PDK depends on CRI 2.10.0 which is ruby version specific
33
-
34
- We have work in progress to create a pathway through this. A fix is up for PDK, which allows CRI 2.15 and 2.10 to be used (here)[https://github.com/puppetlabs/pdk/pull/638]. This also allows the use of multiple ruby versions.
35
-
36
- A PR (here)[https://github.com/puppetlabs/puppetlabs-motd/pull/200] shows what needs to happen within a module, to use litmus for puppet 5 unit tests, and puppet 6 unit tests. Also for running acceptance tests against puppet 5 and puppet 6 with litmus.
37
-
38
- (Gem changes for a module)[https://github.com/puppetlabs/puppetlabs-motd/blob/29d8c9b0ceceb4b0114a66077ce0f473a49481a5/Gemfile#L34-L39] it only installs litmus if your are running puppet 6 and system tests. Also to use a version of the pdk that has the CRI fixes. You can still test against puppet 5 targets in acceptance tests. IF you want to run unit tests against the puppet 5 gem you will have to run something like (this)[https://github.com/puppetlabs/puppetlabs-motd/blob/29d8c9b0ceceb4b0114a66077ce0f473a49481a5/.travis.yml#L107]
39
-
40
- Thank you for your patience.
41
-
42
33
  ## Other Resources
43
34
 
44
35
  * [Is it Worth the Time?](https://xkcd.com/1205/)
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ # helper functions for manipulating and reading a bolt inventory file
4
+ module PuppetLitmus::InventoryManipulation
5
+ def inventory_hash_from_inventory_file(inventory_full_path = nil)
6
+ inventory_full_path = if inventory_full_path.nil?
7
+ 'inventory.yaml'
8
+ else
9
+ inventory_full_path
10
+ end
11
+ raise "There is no inventory file at '#{inventory_full_path}'" unless File.exist?(inventory_full_path)
12
+
13
+ inventory_hash = YAML.load_file(inventory_full_path)
14
+ inventory_hash
15
+ end
16
+
17
+ def find_targets(inventory_hash, targets)
18
+ if targets.nil?
19
+ inventory = Bolt::Inventory.new(inventory_hash, nil)
20
+ targets = inventory.node_names.to_a
21
+ else
22
+ targets = [targets]
23
+ end
24
+ targets
25
+ end
26
+
27
+ def target_in_group(inventory_hash, node_name, group_name)
28
+ exists = false
29
+ inventory_hash['groups'].each do |group|
30
+ next unless group['name'] == group_name
31
+
32
+ group['nodes'].each do |node|
33
+ exists = true if node['name'] == node_name
34
+ end
35
+ end
36
+ exists
37
+ end
38
+
39
+ def config_from_node(inventory_hash, node_name)
40
+ inventory_hash['groups'].each do |group|
41
+ group['nodes'].each do |node|
42
+ if node['name'] == node_name
43
+ return node['config']
44
+ end
45
+ end
46
+ end
47
+ raise "No config was found for #{node_name}"
48
+ end
49
+
50
+ def facts_from_node(inventory_hash, node_name)
51
+ inventory_hash['groups'].each do |group|
52
+ group['nodes'].each do |node|
53
+ if node['name'] == node_name
54
+ return node['facts']
55
+ end
56
+ end
57
+ end
58
+ raise "No config was found for #{node_name}"
59
+ end
60
+
61
+ def add_node_to_group(inventory_hash, node_name, group_name)
62
+ inventory_hash['groups'].each do |group|
63
+ if group['name'] == group_name
64
+ group['nodes'].push node_name
65
+ end
66
+ end
67
+ inventory_hash
68
+ end
69
+
70
+ def remove_node(inventory_hash, node_name)
71
+ inventory_hash['groups'].each do |group|
72
+ group['nodes'].delete_if { |i| i['name'] == node_name }
73
+ end
74
+ inventory_hash
75
+ end
76
+ end
@@ -66,7 +66,7 @@ namespace :litmus do
66
66
  metadata = JSON.parse(File.read('metadata.json'))
67
67
  get_metadata_operating_systems(metadata) do |os_and_version|
68
68
  puts os_and_version
69
- include PuppetLitmus
69
+ include BoltSpec::Run
70
70
  Rake::Task['spec_prep'].invoke
71
71
  config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
72
72
  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'))
@@ -87,15 +87,25 @@ namespace :litmus do
87
87
  task :provision_list, [:key] do |_task, args|
88
88
  provision_hash = YAML.load_file('./provision.yaml')
89
89
  provisioner = provision_hash[args[:key]]['provisioner']
90
+ failed_image_message = ''
90
91
  provision_hash[args[:key]]['images'].each do |image|
92
+ # this is the only way to capture the stdout from the rake task, it will affect pry
93
+ capture_rake_output = StringIO.new
94
+ $stdout = capture_rake_output
91
95
  Rake::Task['litmus:provision'].invoke(provisioner, image)
96
+ if $stdout.string =~ %r{.status.=>.failure}
97
+ failed_image_message += "=====\n#{image}\n#{$stdout.string}\n"
98
+ else
99
+ STDOUT.puts $stdout.string
100
+ end
92
101
  Rake::Task['litmus:provision'].reenable
93
102
  end
103
+ raise "Failed to provision with '#{provisioner}'\n #{failed_image_message}" unless failed_image_message.empty?
94
104
  end
95
105
 
96
106
  desc "provision container/VM - abs/docker/vmpooler eg 'bundle exec rake 'litmus:provision[vmpooler, ubuntu-1604-x86_64]'"
97
107
  task :provision, [:provisioner, :platform] do |_task, args|
98
- include PuppetLitmus
108
+ include BoltSpec::Run
99
109
  Rake::Task['spec_prep'].invoke
100
110
  config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
101
111
  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'))
@@ -197,6 +207,7 @@ namespace :litmus do
197
207
 
198
208
  desc 'tear-down - decommission machines'
199
209
  task :tear_down, [:target] do |_task, args|
210
+ include BoltSpec::Run
200
211
  Rake::Task['spec_prep'].invoke
201
212
  config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
202
213
  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'))
@@ -226,7 +237,7 @@ namespace :litmus do
226
237
  end
227
238
 
228
239
  namespace :acceptance do
229
- include PuppetLitmus
240
+ include PuppetLitmus::InventoryManipulation
230
241
  if File.file?('inventory.yaml')
231
242
  inventory_hash = inventory_hash_from_inventory_file
232
243
  hosts = find_targets(inventory_hash, nil)
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ # helper functions for running puppet commands, and helpers
4
+ module PuppetLitmus::Serverspec
5
+ def idempotent_apply(manifest)
6
+ manifest_file_location = create_manifest_file(manifest)
7
+ apply_manifest(nil, catch_failures: true, manifest_file_location: manifest_file_location)
8
+ apply_manifest(nil, catch_changes: true, manifest_file_location: manifest_file_location)
9
+ end
10
+
11
+ def apply_manifest(manifest, opts = {})
12
+ target_node_name = ENV['TARGET_HOST']
13
+ raise 'manifest and manifest_file_location in the opts hash are mutually exclusive arguments, pick one' if !manifest.nil? && !opts[:manifest_file_location].nil?
14
+ raise 'please pass a manifest or the manifest_file_location in the opts hash' if (manifest.nil? || manifest == '') && opts[:manifest_file_location].nil?
15
+
16
+ manifest_file_location = opts[:manifest_file_location] || create_manifest_file(manifest)
17
+ inventory_hash = if target_node_name.nil? || target_node_name == 'localhost'
18
+ nil
19
+ else
20
+ inventory_hash_from_inventory_file
21
+ end
22
+ command_to_run = "puppet apply #{manifest_file_location}"
23
+ command_to_run += " --modulepath #{Dir.pwd}/spec/fixtures/modules" if target_node_name.nil? || target_node_name == 'localhost'
24
+ command_to_run += ' --detailed-exitcodes' if !opts[:catch_changes].nil? && (opts[:catch_changes] == true)
25
+ # BOLT-608
26
+ if Gem.win_platform?
27
+ stdout, stderr, status = Open3.capture3(command_to_run)
28
+ status_text = if status.to_i.zero?
29
+ 'success'
30
+ else
31
+ 'failure'
32
+ end
33
+ result = [{ 'node' => 'localhost', 'status' => status_text, 'result' => { 'exit_code' => status.to_i, 'stderr' => stderr, 'stdout' => stdout } }]
34
+ else
35
+ result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
36
+ end
37
+
38
+ raise "apply mainfest failed\n`#{command_to_run}`\n======\n#{result}" if result.first['result']['exit_code'] != 0 && opts[:expect_failures] != true
39
+
40
+ result.first
41
+ end
42
+
43
+ # creates a temp manifest file locally & remote depending on target
44
+ def create_manifest_file(manifest)
45
+ target_node_name = ENV['TARGET_HOST']
46
+ manifest_file = Tempfile.new(['manifest_', '.pp'])
47
+ manifest_file.write(manifest)
48
+ manifest_file.close
49
+ if target_node_name.nil? || target_node_name == 'localhost'
50
+ # no need to transfer
51
+ manifest_file_location = manifest_file.path
52
+ else
53
+ # transfer to TARGET_HOST
54
+ inventory_hash = inventory_hash_from_inventory_file
55
+ manifest_file_location = "/tmp/#{File.basename(manifest_file)}"
56
+ result = upload_file(manifest_file.path, manifest_file_location, target_node_name, options: {}, config: nil, inventory: inventory_hash)
57
+ raise result.first['result'].to_s unless result.first['status'] == 'success'
58
+ end
59
+ manifest_file_location
60
+ end
61
+
62
+ def run_shell(command_to_run, opts = {})
63
+ inventory_hash = inventory_hash_from_inventory_file
64
+ target_node_name = ENV['TARGET_HOST']
65
+ result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
66
+
67
+ raise "shell failed\n`#{command_to_run}`\n======\n#{result}" if result.first['result']['exit_code'] != 0 && opts[:expect_failures] != true
68
+
69
+ result
70
+ end
71
+
72
+ # Runs a selected task against the target host. Parameters should be passed in with a hash format.
73
+ def run_bolt_task(task_name, params = {})
74
+ config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
75
+ inventory_hash = inventory_hash_from_inventory_file
76
+ target_node_name = ENV['TARGET_HOST'] if target_node_name.nil?
77
+
78
+ result = run_task(task_name, target_node_name, params, config: config_data, inventory: inventory_hash)
79
+
80
+ raise "task failed\n`#{task_name}`\n======\n#{result}" if result.first['status'] != 'success'
81
+
82
+ result
83
+ end
84
+ end
@@ -2,5 +2,5 @@
2
2
 
3
3
  # version of this gem
4
4
  module PuppetLitmus
5
- VERSION ||= '0.0.1'
5
+ VERSION ||= '0.1.0'
6
6
  end
data/lib/puppet_litmus.rb CHANGED
@@ -1,161 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bolt_spec/run'
4
+ require 'puppet_litmus/inventory_manipulation'
5
+ require 'puppet_litmus/serverspec'
4
6
 
5
7
  # Helper methods for testing puppet content
6
8
  module PuppetLitmus
7
9
  include BoltSpec::Run
8
- def apply_manifest(manifest, opts = {})
9
- target_node_name = ENV['TARGET_HOST']
10
- raise 'manifest and manifest_file_location in the opts hash are mutually exclusive arguments, pick one' if !manifest.nil? && !opts[:manifest_file_location].nil?
11
- raise 'please pass a manifest or the manifest_file_location in the opts hash' if (manifest.nil? || manifest == '') && opts[:manifest_file_location].nil?
12
-
13
- manifest_file_location = opts[:manifest_file_location] || create_manifest_file(manifest)
14
- inventory_hash = if target_node_name.nil? || target_node_name == 'localhost'
15
- nil
16
- else
17
- inventory_hash_from_inventory_file
18
- end
19
- command_to_run = "puppet apply #{manifest_file_location}"
20
- command_to_run += " --modulepath #{Dir.pwd}/spec/fixtures/modules" if target_node_name.nil? || target_node_name == 'localhost'
21
- command_to_run += ' --detailed-exitcodes' if !opts[:catch_changes].nil? && (opts[:catch_changes] == true)
22
- # BOLT-608
23
- if Gem.win_platform?
24
- stdout, stderr, status = Open3.capture3(command_to_run)
25
- status_text = if status.to_i.zero?
26
- 'success'
27
- else
28
- 'failure'
29
- end
30
- result = [{ 'node' => 'localhost', 'status' => status_text, 'result' => { 'exit_code' => status.to_i, 'stderr' => stderr, 'stdout' => stdout } }]
31
- else
32
- result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
33
- end
34
-
35
- raise "apply mainfest failed\n`#{command_to_run}`\n======\n#{result}" if result.first['result']['exit_code'] != 0 && opts[:expect_failures] != true
36
-
37
- result
38
- end
39
-
40
- # creates a temp manifest file locally & remote depending on target
41
- def create_manifest_file(manifest)
42
- target_node_name = ENV['TARGET_HOST']
43
- manifest_file = Tempfile.new(['manifest_', '.pp'])
44
- manifest_file.write(manifest)
45
- manifest_file.close
46
- if target_node_name.nil? || target_node_name == 'localhost'
47
- # no need to transfer
48
- manifest_file_location = manifest_file.path
49
- else
50
- # transfer to TARGET_HOST
51
- command = "bundle exec bolt file upload #{manifest_file.path} /tmp/#{File.basename(manifest_file)} --nodes #{target_node_name} --inventoryfile inventory.yaml"
52
- stdout, stderr, status = Open3.capture3(command)
53
- error_message = "Attempted to run\ncommand:'#{command}'\nstdout:#{stdout}\nstderr:#{stderr}"
54
- raise error_message unless status.to_i.zero?
55
-
56
- manifest_file_location = "/tmp/#{File.basename(manifest_file)}"
57
- end
58
- manifest_file_location
59
- end
60
-
61
- def apply_manifest_and_idempotent(manifest)
62
- manifest_file_location = create_manifest_file(manifest)
63
- apply_manifest(nil, catch_failures: true, manifest_file_location: manifest_file_location)
64
- apply_manifest(nil, catch_changes: true, manifest_file_location: manifest_file_location)
65
- end
66
-
67
- def run_shell(command_to_run, opts = {})
68
- inventory_hash = inventory_hash_from_inventory_file
69
- target_node_name = ENV['TARGET_HOST']
70
- result = run_command(command_to_run, target_node_name, config: nil, inventory: inventory_hash)
71
-
72
- raise "shell failed\n`#{command_to_run}`\n======\n#{result}" if result.first['result']['exit_code'] != 0 && opts[:expect_failures] != true
73
-
74
- result
75
- end
76
-
77
- def inventory_hash_from_inventory_file(inventory_full_path = nil)
78
- inventory_full_path = if inventory_full_path.nil?
79
- 'inventory.yaml'
80
- else
81
- inventory_full_path
82
- end
83
- raise "There is no inventory file at '#{inventory_full_path}'" unless File.exist?(inventory_full_path)
84
-
85
- inventory_hash = YAML.load_file(inventory_full_path)
86
- inventory_hash
87
- end
88
-
89
- def find_targets(inventory_hash, targets)
90
- if targets.nil?
91
- inventory = Bolt::Inventory.new(inventory_hash, nil)
92
- targets = inventory.node_names.to_a
93
- else
94
- targets = [targets]
95
- end
96
- targets
97
- end
98
-
99
- def target_in_group(inventory_hash, node_name, group_name)
100
- exists = false
101
- inventory_hash['groups'].each do |group|
102
- next unless group['name'] == group_name
103
-
104
- group['nodes'].each do |node|
105
- exists = true if node['name'] == node_name
106
- end
107
- end
108
- exists
109
- end
110
-
111
- def config_from_node(inventory_hash, node_name)
112
- inventory_hash['groups'].each do |group|
113
- group['nodes'].each do |node|
114
- if node['name'] == node_name
115
- return node['config']
116
- end
117
- end
118
- end
119
- raise "No config was found for #{node_name}"
120
- end
121
-
122
- def facts_from_node(inventory_hash, node_name)
123
- inventory_hash['groups'].each do |group|
124
- group['nodes'].each do |node|
125
- if node['name'] == node_name
126
- return node['facts']
127
- end
128
- end
129
- end
130
- raise "No config was found for #{node_name}"
131
- end
132
-
133
- def add_node_to_group(inventory_hash, node_name, group_name)
134
- inventory_hash['groups'].each do |group|
135
- if group['name'] == group_name
136
- group['nodes'].push node_name
137
- end
138
- end
139
- inventory_hash
140
- end
141
-
142
- def remove_node(inventory_hash, node_name)
143
- inventory_hash['groups'].each do |group|
144
- group['nodes'].delete_if { |i| i['name'] == node_name }
145
- end
146
- inventory_hash
147
- end
148
-
149
- # Runs a selected task against the target host. Parameters should be passed in with a hash format.
150
- def task_run(task_name, params)
151
- config_data = { 'modulepath' => File.join(Dir.pwd, 'spec', 'fixtures', 'modules') }
152
- inventory_hash = inventory_hash_from_inventory_file
153
- target_node_name = ENV['TARGET_HOST'] if target_node_name.nil?
154
-
155
- result = run_task(task_name, target_node_name, params, config: config_data, inventory: inventory_hash)
156
-
157
- raise "task failed\n`#{task_name}`\n======\n#{result}" if result.first['status'] != 'success'
158
-
159
- result
160
- end
10
+ include PuppetLitmus::InventoryManipulation
11
+ include PuppetLitmus::Serverspec
161
12
  end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe PuppetLitmus do
6
+ it 'has a version number' do
7
+ expect(described_class::VERSION).not_to be nil
8
+ expect(described_class::VERSION).to be_a_kind_of(String)
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec'
4
+ require 'puppet_litmus'
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.0.1
4
+ version: 0.1.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-04-03 00:00:00.000000000 Z
11
+ date: 2019-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bolt
@@ -61,8 +61,12 @@ files:
61
61
  - LICENSE
62
62
  - README.md
63
63
  - lib/puppet_litmus.rb
64
+ - lib/puppet_litmus/inventory_manipulation.rb
64
65
  - lib/puppet_litmus/rake_tasks.rb
66
+ - lib/puppet_litmus/serverspec.rb
65
67
  - lib/puppet_litmus/version.rb
68
+ - spec/lib/puppet_litmus_spec.rb
69
+ - spec/spec_helper.rb
66
70
  homepage: https://github.com/puppetlabs/puppet_litmus
67
71
  licenses:
68
72
  - Apache-2.0
@@ -83,9 +87,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
83
87
  version: '0'
84
88
  requirements: []
85
89
  rubyforge_project:
86
- rubygems_version: 2.7.6
90
+ rubygems_version: 2.6.14.1
87
91
  signing_key:
88
92
  specification_version: 4
89
93
  summary: Providing a simple command line tool for puppet content creators, to enable
90
94
  simple and complex test deployments.
91
- test_files: []
95
+ test_files:
96
+ - spec/spec_helper.rb
97
+ - spec/lib/puppet_litmus_spec.rb