rspec-puppet-maestrodev 0.1.5.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Tim Sharpe
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,292 @@
1
+ # RSpec tests for your Puppet manifests & modules
2
+
3
+ ## Installation
4
+
5
+ gem install rspec-puppet
6
+
7
+ ## Naming conventions
8
+
9
+ For clarity and consistency, I recommend that you use the following directory
10
+ structure and naming convention.
11
+
12
+ module
13
+ |
14
+ +-- manifests
15
+ |
16
+ +-- lib
17
+ |
18
+ +-- spec
19
+ |
20
+ +-- spec_helper.rb
21
+ |
22
+ +-- classes
23
+ | |
24
+ | +-- <class_name>_spec.rb
25
+ |
26
+ +-- defines
27
+ | |
28
+ | +-- <define_name>_spec.rb
29
+ |
30
+ +-- functions
31
+ | |
32
+ | +-- <function_name>_spec.rb
33
+ |
34
+ +-- hosts
35
+ |
36
+ +-- <host_name>_spec.rb
37
+
38
+ ## Example groups
39
+
40
+ If you use the above directory structure, your examples will automatically be
41
+ placed in the correct groups and have access to the custom matchers. *If you
42
+ choose not to*, you can force the examples into the required groups as follows.
43
+
44
+ ```ruby
45
+ describe 'myclass', :type => :class do
46
+ ...
47
+ end
48
+
49
+ describe 'mydefine', :type => :define do
50
+ ...
51
+ end
52
+
53
+ describe 'myfunction', :type => :puppet_function do
54
+ ...
55
+ end
56
+
57
+ describe 'myhost.example.com', :type => :host do
58
+ ...
59
+ end
60
+ ```
61
+
62
+ ## Defined Types & Classes
63
+
64
+ ### Matchers
65
+
66
+ #### Checking if a class has been included
67
+
68
+ You can test if a class has been included in the catalogue with the
69
+ `include_class` matcher. It takes the class name as a string as its only
70
+ argument
71
+
72
+ ```ruby
73
+ it { should include_class('foo') }
74
+ ```
75
+
76
+ #### Checking if a resource exists
77
+
78
+ You can test if a resource exists in the catalogue with the generic
79
+ `contain_<resource type>` matcher.
80
+
81
+ ```ruby
82
+ it { should contain_augeas('bleh') }
83
+ ```
84
+
85
+ If your resource type includes :: (e.g.
86
+ `foo::bar` simply replace the :: with __ (two underscores).
87
+
88
+ ```ruby
89
+ it { should contain_foo__bar('baz') }
90
+ ```
91
+
92
+ You can further test the parameters that have been passed to the resources with
93
+ the generic `with_<parameter>` chains.
94
+
95
+ ```ruby
96
+ it { should contain_package('mysql-server').with_ensure('present') }
97
+ ```
98
+
99
+ You can use the `with` method to verify the value of multiple parameters.
100
+
101
+ ```ruby
102
+ it do should contain_service('keystone').with(
103
+ 'ensure' => 'running',
104
+ 'enable' => 'true',
105
+ 'hasstatus' => 'true',
106
+ 'hasrestart' => 'true'
107
+ ) end
108
+ ```
109
+
110
+ You can also test that specific parameters have been left undefined with the
111
+ generic `without_<parameter>` chains.
112
+
113
+ ```ruby
114
+ it { should contain_file('/foo/bar').without_mode }
115
+ ```
116
+
117
+ You can use the without method to verify that a list of parameters have not been
118
+ defined
119
+
120
+ ```ruby
121
+ it { should contain_service('keystone').without(
122
+ ['restart', 'status']
123
+ )}
124
+ ```
125
+
126
+ ### Writing tests
127
+
128
+ #### Basic test structure
129
+
130
+ To test that
131
+
132
+ sysctl { 'baz'
133
+ value => 'foo',
134
+ }
135
+
136
+ Will cause the following resource to be in included in catalogue for a host
137
+
138
+ exec { 'sysctl/reload':
139
+ command => '/sbin/sysctl -p /etc/sysctl.conf',
140
+ }
141
+
142
+ We can write the following testcase (in `spec/defines/sysctl_spec.rb`)
143
+
144
+ ```ruby
145
+ describe 'sysctl' do
146
+ let(:title) { 'baz' }
147
+ let(:params) { { :value => 'foo' } }
148
+
149
+ it { should contain_exec('sysctl/reload').with_command("/sbin/sysctl -p /etc/sysctl.conf") }
150
+ end
151
+ ```
152
+
153
+ #### Specifying the title of a resource
154
+
155
+ ```ruby
156
+ let(:title) { 'foo' }
157
+ ```
158
+
159
+ #### Specifying the parameters to pass to a resources or parametised class
160
+
161
+ ```ruby
162
+ let(:params) { {:ensure => 'present', ...} }
163
+ ```
164
+
165
+ #### Specifying the FQDN of the test node
166
+
167
+ If the manifest you're testing expects to run on host with a particular name,
168
+ you can specify this as follows
169
+
170
+ ```ruby
171
+ let(:node) { 'testhost.example.com' }
172
+ ```
173
+
174
+ #### Specifying the facts that should be available to your manifest
175
+
176
+ By default, the test environment contains no facts for your manifest to use.
177
+ You can set them with a hash
178
+
179
+ ```ruby
180
+ let(:facts) { {:operatingsystem => 'Debian', :kernel => 'Linux', ...} }
181
+ ```
182
+
183
+ #### Specifying the path to find your modules
184
+
185
+ I recommend setting a default module path by adding the following code to your
186
+ `spec_helper.rb`
187
+
188
+ ```ruby
189
+ RSpec.configure do |c|
190
+ c.module_path = '/path/to/your/module/dir'
191
+ end
192
+ ```
193
+
194
+ However, if you want to specify it in each example, you can do so
195
+
196
+ ```ruby
197
+ let(:module_path) { '/path/to/your/module/dir' }
198
+ ```
199
+
200
+ ## Functions
201
+
202
+ ### Matchers
203
+
204
+ All of the standard RSpec matchers are available for you to use when testing
205
+ Puppet functions.
206
+
207
+ ```ruby
208
+ it 'should be able to do something' do
209
+ subject.call('foo') == 'bar'
210
+ end
211
+ ```
212
+
213
+ For your convenience though, a `run` matcher exists to provide easier to
214
+ understand test cases.
215
+
216
+ ```ruby
217
+ it { should run.with_params('foo').and_return('bar') }
218
+ ```
219
+
220
+ ### Writing tests
221
+
222
+ #### Basic test structure
223
+
224
+ ```ruby
225
+ require 'spec_helper'
226
+
227
+ describe '<function name>' do
228
+ ...
229
+ end
230
+ ```
231
+
232
+ #### Specifying the name of the function to test
233
+
234
+ The name of the function must be provided in the top level description, e.g.
235
+
236
+ ```ruby
237
+ describe 'split' do
238
+ ```
239
+
240
+ #### Specifying the arguments to pass to the function
241
+
242
+ You can specify the arguments to pass to your function during the test(s) using
243
+ either the `with_params` chain method in the `run` matcher
244
+
245
+ ```ruby
246
+ it { should run.with_params('foo', 'bar', ['baz']) }
247
+ ```
248
+
249
+ Or by using the `call` method on the subject directly
250
+
251
+ ```ruby
252
+ it 'something' do
253
+ subject.call('foo', 'bar', ['baz'])
254
+ end
255
+ ```
256
+
257
+ #### Testing the results of the function
258
+
259
+ You can test the result of a function (if it produces one) using either the
260
+ `and_returns` chain method in the `run` matcher
261
+
262
+ ```ruby
263
+ it { should run.with_params('foo').and_return('bar') }
264
+ ```
265
+
266
+ Or by using any of the existing RSpec matchers on the subject directly
267
+
268
+ ```ruby
269
+ it 'something' do
270
+ subject.call('foo') == 'bar'
271
+ subject.call('baz').should be_an Array
272
+ end
273
+ ```
274
+
275
+ #### Testing the errors thrown by the function
276
+
277
+ You can test whether the function throws an exception using either the
278
+ `and_raises_error` chain method in the `run` matcher
279
+
280
+ ```ruby
281
+ it { should run.with_params('a', 'b').and_raise_error(Puppet::ParseError) }
282
+ it { should_not run.with_params('a').and_raise_error(Puppet::ParseError) }
283
+ ```
284
+
285
+ Or by using the existing `raises_error` RSpec matcher
286
+
287
+ ```ruby
288
+ it 'something' do
289
+ expect { subject.call('a', 'b') }.should raise_error(Puppet::ParseError)
290
+ expect { subject.call('a') }.should_not raise_error(Puppet::ParseError)
291
+ end
292
+ ```
@@ -0,0 +1,7 @@
1
+ require 'rake'
2
+ require 'rspec/core/rake_task'
3
+
4
+ task :default => :test
5
+ task :spec => :test
6
+
7
+ RSpec::Core::RakeTask.new(:test)
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
4
+
5
+ require 'rspec-puppet'
6
+ require 'optparse'
7
+
8
+ options = {
9
+ :module_name => nil,
10
+ }
11
+
12
+ OptionParser.new do |opts|
13
+ opts.banner = "Usage: rspec-puppet-init [options]"
14
+
15
+ opts.on('-n', '--name NAME', 'The name of the module (override autodetection)') do |v|
16
+ options[:module_name] = v
17
+ end
18
+ end.parse!
19
+
20
+ RSpec::Puppet::Setup.run(options[:module_name])
@@ -0,0 +1,15 @@
1
+ require 'puppet'
2
+ require 'rspec'
3
+ require 'fileutils'
4
+ require 'tmpdir'
5
+ require 'rspec-puppet/matchers'
6
+ require 'rspec-puppet/example'
7
+ require 'rspec-puppet/setup'
8
+
9
+ RSpec.configure do |c|
10
+ c.add_setting :module_path, :default => '/etc/puppet/modules'
11
+ c.add_setting :manifest_dir, :default => nil
12
+ c.add_setting :manifest, :default => nil
13
+ c.add_setting :template_dir, :default => nil
14
+ c.add_setting :config, :default => nil
15
+ end
@@ -0,0 +1,27 @@
1
+ require 'rspec-puppet/support'
2
+ require 'rspec-puppet/example/define_example_group'
3
+ require 'rspec-puppet/example/class_example_group'
4
+ require 'rspec-puppet/example/function_example_group'
5
+ require 'rspec-puppet/example/host_example_group'
6
+
7
+ RSpec::configure do |c|
8
+ def c.escaped_path(*parts)
9
+ Regexp.compile(parts.join('[\\\/]'))
10
+ end
11
+
12
+ c.include RSpec::Puppet::DefineExampleGroup, :type => :define, :example_group => {
13
+ :file_path => c.escaped_path(%w[spec defines])
14
+ }
15
+
16
+ c.include RSpec::Puppet::ClassExampleGroup, :type => :class, :example_group => {
17
+ :file_path => c.escaped_path(%w[spec classes])
18
+ }
19
+
20
+ c.include RSpec::Puppet::FunctionExampleGroup, :type => :puppet_function, :example_group => {
21
+ :file_path => c.escaped_path(%w[spec functions])
22
+ }
23
+
24
+ c.include RSpec::Puppet::HostExampleGroup, :type => :host, :example_group => {
25
+ :file_path => c.escaped_path(%w[spec hosts])
26
+ }
27
+ end
@@ -0,0 +1,60 @@
1
+ module RSpec::Puppet
2
+ module ClassExampleGroup
3
+ include RSpec::Puppet::ManifestMatchers
4
+ include RSpec::Puppet::Support
5
+
6
+ def subject
7
+ @catalogue ||= catalogue
8
+ end
9
+
10
+ def catalogue
11
+ vardir = Dir.mktmpdir
12
+ Puppet[:vardir] = vardir
13
+ Puppet[:hiera_config] = File.join(vardir, "hiera.yaml") if Puppet[:hiera_config] == File.expand_path("/dev/null")
14
+ Puppet[:modulepath] = self.respond_to?(:module_path) ? module_path : RSpec.configuration.module_path
15
+ Puppet[:manifestdir] = self.respond_to?(:manifest_dir) ? manifest_dir : RSpec.configuration.manifest_dir
16
+ Puppet[:manifest] = self.respond_to?(:manifest) ? manifest : RSpec.configuration.manifest
17
+ Puppet[:templatedir] = self.respond_to?(:template_dir) ? template_dir : RSpec.configuration.template_dir
18
+ Puppet[:config] = self.respond_to?(:config) ? config : RSpec.configuration.config
19
+
20
+ klass_name = self.class.top_level_description.downcase
21
+
22
+ # If we're testing a standalone module (i.e. one that's outside of a
23
+ # puppet tree), the autoloader won't work, so we need to fudge it a bit.
24
+ if File.exists?(File.join(Puppet[:modulepath], 'manifests', 'init.pp'))
25
+ path_to_manifest = File.join([Puppet[:modulepath], 'manifests', klass_name.split('::')[1..-1]].flatten)
26
+ import_str = "import '#{Puppet[:modulepath]}/manifests/init.pp'\nimport '#{path_to_manifest}.pp'\n"
27
+ elsif File.exists?(Puppet[:modulepath])
28
+ import_str = "import '#{Puppet[:manifest]}'\n"
29
+ else
30
+ import_str = ""
31
+ end
32
+
33
+ if self.respond_to? :pre_condition
34
+ pre_cond = pre_condition
35
+ else
36
+ pre_cond = ''
37
+ end
38
+
39
+ if !self.respond_to?(:params) || params == {}
40
+ code = import_str + "include #{klass_name}"
41
+ else
42
+ code = import_str + 'class' + " { \"" + klass_name + "\": " + params.keys.map { |r| "#{r.to_s} => #{params[r].inspect}"
43
+ }.join(',' ) + " }"
44
+ end
45
+ code = pre_cond + "\n" + code
46
+
47
+ nodename = self.respond_to?(:node) ? node : Puppet[:certname]
48
+ facts_val = {
49
+ 'hostname' => nodename.split('.').first,
50
+ 'fqdn' => nodename,
51
+ 'domain' => nodename.split('.').last,
52
+ }
53
+ facts_val.merge!(munge_facts(facts)) if self.respond_to?(:facts)
54
+
55
+ catalogue = build_catalog(nodename, facts_val, code)
56
+ FileUtils.rm_rf(vardir) if File.directory?(vardir)
57
+ catalogue
58
+ end
59
+ end
60
+ end