onceover 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/Gemfile +6 -0
  4. data/Gemfile.lock +17 -0
  5. data/README.md +504 -0
  6. data/Rakefile +2 -0
  7. data/bin/onceover +17 -0
  8. data/controlrepo.gemspec +38 -0
  9. data/factsets/CentOS-5.11-32.json +263 -0
  10. data/factsets/CentOS-5.11-64.json +263 -0
  11. data/factsets/CentOS-6.6-32.json +305 -0
  12. data/factsets/CentOS-6.6-64.json +342 -0
  13. data/factsets/CentOS-7.0-64.json +352 -0
  14. data/factsets/Debian-6.0.10-32.json +322 -0
  15. data/factsets/Debian-6.0.10-64.json +322 -0
  16. data/factsets/Debian-7.8-32.json +338 -0
  17. data/factsets/Debian-7.8-64.json +338 -0
  18. data/factsets/Ubuntu-12.04-32.json +328 -0
  19. data/factsets/Ubuntu-12.04-64.json +328 -0
  20. data/factsets/Ubuntu-14.04-32.json +337 -0
  21. data/factsets/Ubuntu-14.04-64.json +373 -0
  22. data/factsets/Windows_Server-2008r2-64.json +183 -0
  23. data/factsets/Windows_Server-2012r2-64.json +164 -0
  24. data/lib/onceover/beaker.rb +225 -0
  25. data/lib/onceover/beaker/spec_helper.rb +70 -0
  26. data/lib/onceover/class.rb +29 -0
  27. data/lib/onceover/cli.rb +46 -0
  28. data/lib/onceover/cli/init.rb +31 -0
  29. data/lib/onceover/cli/run.rb +72 -0
  30. data/lib/onceover/cli/show.rb +74 -0
  31. data/lib/onceover/cli/update.rb +48 -0
  32. data/lib/onceover/controlrepo.rb +527 -0
  33. data/lib/onceover/group.rb +85 -0
  34. data/lib/onceover/logger.rb +31 -0
  35. data/lib/onceover/node.rb +44 -0
  36. data/lib/onceover/rake_tasks.rb +113 -0
  37. data/lib/onceover/runner.rb +90 -0
  38. data/lib/onceover/test.rb +157 -0
  39. data/lib/onceover/testconfig.rb +233 -0
  40. data/templates/.fixtures.yml.erb +24 -0
  41. data/templates/Rakefile.erb +6 -0
  42. data/templates/acceptance_test_spec.rb.erb +66 -0
  43. data/templates/controlrepo.yaml.erb +38 -0
  44. data/templates/factsets_README.md.erb +7 -0
  45. data/templates/nodeset.yaml.erb +12 -0
  46. data/templates/pre_conditions_README.md.erb +24 -0
  47. data/templates/spec_helper.rb.erb +16 -0
  48. data/templates/spec_helper_acceptance.rb.erb +1 -0
  49. data/templates/test_spec.rb.erb +34 -0
  50. metadata +345 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8aa3d9b58d0daa14aee75e1561e2b74525524c0a
4
+ data.tar.gz: 86c3a7df65a1d3bbbf00335e86eaf8fc72e52e66
5
+ SHA512:
6
+ metadata.gz: 735d48410601305d2943c804441a8c092cddc7166a3af581ac03ec1a061d6452ac2b5a5ab988a34158de1d16ad9dcd16a3e5a995bf99d68632a1c8bd0c842b22
7
+ data.tar.gz: 0bd02b2bbfb6b16565431511dfb433d706928aed3f4b09304ca07108d6e32b32d37802ef0b072fa9bd8f7b0bffc3e727e03d22ea09c4a91a97231fe6bd54a32c
@@ -0,0 +1,2 @@
1
+ .DS_Store
2
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+
4
+ gem 'rubygems-tasks'
5
+ gem 'git'
6
+ gem 'rake'
@@ -0,0 +1,17 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ git (1.2.9.1)
5
+ rake (10.5.0)
6
+ rubygems-tasks (0.2.4)
7
+
8
+ PLATFORMS
9
+ ruby
10
+
11
+ DEPENDENCIES
12
+ git
13
+ rake
14
+ rubygems-tasks
15
+
16
+ BUNDLED WITH
17
+ 1.10.6
@@ -0,0 +1,504 @@
1
+ # Onceover
2
+
3
+ *The gateway drug to automated infrastructure testing with Puppet*
4
+
5
+ ## Table of Contents
6
+
7
+ - [Overview](#overview)
8
+ - [Quick Start](#quick-start)
9
+ - [Installation](#installation)
10
+ - [Config files](#config-files)
11
+ - [onceover.yaml](#onceoveryaml)
12
+ - [factsets](#factsets)
13
+ - [nodesets](#nodesets)
14
+ - [Hiera Data](#hiera-data)
15
+ - [Spec testing](#spec-testing)
16
+ - [Adding your own spec tests](#adding-your-own-spec-tests)
17
+ - [Acceptance testing](#acceptance-testing)
18
+ - [Using Workarounds](#using-workarounds)
19
+ - [Extra tooling](#extra-tooling)
20
+ - [Accessing fact sets in a traditional RSpec test](#accessing-fact-sets-in-a-traditional-rspec-test)
21
+ - [Accessing Roles in a traditional RSpec test](#accessing-roles-in-a-traditional-rspec-test)
22
+ - [Filtering](#filtering)
23
+ - [Extra Configuration](#extra-configuration)
24
+ - [Rake tasks](#rake-tasks)
25
+ - [generate_fixtures](#generate_fixtures)
26
+
27
+ ## Quick Start
28
+
29
+ **Note:** This assumes you are inside the root of your controlrepo.
30
+
31
+ Install the Gem:
32
+
33
+ `gem install onceover`
34
+
35
+ Set up your config:
36
+
37
+ `onceover init`
38
+
39
+ Run your spec tests!
40
+
41
+ `onceover run spec`
42
+
43
+ **Hint:** Don't forget you can use Bundler to install onceover by adding this to your gemfile:
44
+ ```ruby
45
+ gem 'onceover'
46
+ ```
47
+
48
+ ## Overview
49
+
50
+ This gem provides a toolset for testing Puppet Controlrepos (Repos used with r10k). The main purpose of this project is to provide a set of tools to help smooth out the process of setting up and running both spec and acceptance tests for a controlrepo. Due to the fact that controlrepos are fairly standardised in nature it seemed ridiculous that you would need to set up the same testing framework that we would normally use within a module for a controlrepo. This is because at this level we are normally just running very basic tests that cover a lot of code. It would also mean that we would need to essentially duplicated our `Puppetfile` into a `.fixtures.yml` file, along with a few other things.
51
+
52
+ This toolset requires some config before it can be used so definitely read that section before getting started.
53
+
54
+ ## Installation
55
+
56
+ `gem install onceover`
57
+
58
+ This gem can just be installed using `gem install` however I would recommend using [Bundler](http://bundler.io/) to manage your gems.
59
+
60
+ ## Config Files
61
+
62
+ This project uses one main config file to determine what classes we should be testing and how, this is [onceover.yaml](#onceoveryaml). The `onceover.yaml` config file provides information about what classes to test when, however it needs more information than that:
63
+
64
+ If we are doing spec testing we need sets of facts to compile the puppet code against, these are stored in [factsets](#factsets). (A few are provided out of the box for you)
65
+
66
+ If we are doing acceptance testing then we need information about how to spin up VMs to do the testing on, these are configured in [nodesets](#nodesets). (Once again these are auto-generated with `onceover init`)
67
+
68
+ ### onceover.yaml
69
+
70
+ `spec/onceover.yaml`
71
+
72
+ Hopefully this config file will be fairly self explanatory once you see it, but basically this is the place where we define what classes we want to test and the [factsets](#factsets)/[nodesets](#nodesets) that we want to test them against. The config file must contain the following sections:
73
+
74
+ **classes:** A list (array) of classes that we want to test, usually this would be your roles, possibly profiles if you want. (If you don't know what roles and profiles are please [READ THIS](http://garylarizza.com/blog/2014/02/17/puppet-workflow-part-2/))
75
+
76
+ **nodes:** The nodes that we want to test against. The nodes that we list here map directly to either a [factset](#factsets) or a [nodeset](#nodesets) depending on weather we are running spec or acceptance tests respectively.
77
+
78
+ **node_groups:** The `node_groups` section is just for saving us some typing. Here we can set up groups of nodes which we can then refer to in our test matrix. We can create groups by simply specifying an array of servers to be in the group, or we can use the subtractive *include/exclude* syntax.
79
+
80
+ **class_groups:** The `class_groups` section is much the same as the `node_groups` sections, except that it creates groups of classes, not groups of nodes (duh). All the same rules apply and you can also use the *include/exclude* syntax.
81
+
82
+ **test_matrix:** This where the action happens! This is the section where we set up which classes are going to be tested against which nodes. It should be an array of hashes with the following format:
83
+
84
+ ```yaml
85
+ - {nodes_to_test}: # The name of a node or node group
86
+ classes: '{classes_to_test}' # the name of a class or
87
+ tests: '{all_tests|acceptance|spec}' # One of the three
88
+ {valid_option}: {value} # Check the doco for available options
89
+ ```
90
+
91
+ Why an array of hashes? Well, that is so that we can refer to the same node or node group twice, which we may want/need to do.
92
+
93
+ In the example below we have referred to `centos6a` and `centos7b` in all of our tests as they are in `all_nodes`, `non_windows_servers` and `centos_severs`. However we have *left the more specific references to last*. This is because entries in the test_matrix will override entries above them if applicable. Meaning that we are still only testing each class on the two Centos servers once (Because the gem does de-duplication before running the tests), but also making sure we run `roles::frontend_webserver` twice before checking for idempotency.
94
+
95
+ **functions** In this section we can add functions that we want to mock when running spec tests. Each function takes the following agruments:
96
+ - **type** *statement or rvalue*
97
+ - **returns** *Optional: A value to return*
98
+
99
+ A full example:
100
+
101
+ ```yaml
102
+ classes:
103
+ - 'roles::backend_dbserver'
104
+ - 'roles::frontend_webserver'
105
+ - 'roles::load_balancer'
106
+ - 'roles::syd_f5_load_balancer'
107
+ - 'roles::windows_server'
108
+
109
+ nodes:
110
+ - centos6a
111
+ - centos7b
112
+ - server2008r2a
113
+ - ubuntu1404a
114
+
115
+ node_groups:
116
+ centos_severs:
117
+ - centos6a
118
+ - centos7b
119
+ non_windows_servers:
120
+ include: 'all_nodes'
121
+ exclude: 'server2008r2a'
122
+
123
+ class_groups:
124
+ windows_roles:
125
+ - 'roles::windows_server'
126
+ - 'roles::backend_dbserver'
127
+ non_windows_roles:
128
+ include: 'all_classes'
129
+ exclude: 'windows_roles'
130
+
131
+ test_matrix:
132
+ - all_nodes:
133
+ classes: 'all_classes'
134
+ tests: 'spec'
135
+ - non_windows_servers:
136
+ classes: 'all_classes'
137
+ tests: 'all_tests'
138
+ - centos_severs:
139
+ classes: 'roles::frontend_webserver'
140
+ tests: 'acceptance'
141
+ runs_before_idempotency: 2
142
+ tags:
143
+ - 'frontend'
144
+
145
+ functions:
146
+ query_resources:
147
+ type: rvalue
148
+ returns: []
149
+ ```
150
+
151
+ **Include/Exclude syntax:** This can be used with either `node_groups` or `class_groups` and allows us to save some time by using existing groups to create new ones e.g.
152
+
153
+ ```yaml
154
+ node_groups:
155
+ windows_nodes: # This has to be defined first
156
+ - sevrer2008r2
157
+ - server2012r2
158
+ non_windows:
159
+ include: 'all_nodes' # Start with all nodes
160
+ exclude: 'windows_nodes' # Then remove the windows ones from that list
161
+ ```
162
+
163
+ It's important to note that in order to reference a group using the *include/exclude* syntax is has to have been defined already i.e. it has to come above the group that references it (Makes sense right?)
164
+
165
+ #### Optional test parameters
166
+
167
+ **check_idempotency** *Default: true*
168
+
169
+ Weather or not to check that puppet will be idempotent (Acceptance testing only)
170
+
171
+ **runs_before_idempotency** *Default: 1*
172
+
173
+ The number of runs to try before checking that it is idempotent. Required for some things that require restarts of the server or restarts of puppet. (Acceptance testing only)
174
+
175
+ **tags** *Default: nil*
176
+
177
+ One or many tags that tests in this group should be tagged with. This allows you to run only certain tests using the `--tags` command line parameter. **NOTE:** Custom spec tests will always be run as they are not subject to tags
178
+
179
+ ### factsets
180
+
181
+ This gem comes with a few pre-canned factsets. These are listed under the `nodes` sections of `onceover.yaml` when you run `onceover init`. You can also add your own factsets by putting them in:
182
+
183
+ `spec/factsets/*.yaml`
184
+
185
+ Factsets are used by the controlrepo gem to generate spec tests, which compile a given class against a certain set of facts. To create these factsets all we need to do is log onto a real machine that has puppet installed and run:
186
+
187
+ `puppet facts`
188
+
189
+ Which will give raw json output of every fact which puppet knows about. Usually I would recommend running this on each of the types of machines that you run in your infrastructure so that you have good coverage. To make life easier you might want to direct it into a file instead of copying it from the command line:
190
+
191
+ `puppet facts > fact_set_name.json`
192
+
193
+ Once we have our factset all we need to do is copy it into `spec/factsets/` inside our controlrepo and commit it to version control. Factsets are named based on their filename, not the name of the server they came from (Although you can, if you want). i.e the following factset file:
194
+
195
+ `spec/factsets/server2008r2.yaml`
196
+
197
+ Would map to a node named `server2008r2` in `onceover.yaml`
198
+
199
+ ### nodesets
200
+
201
+ `spec/acceptance/nodesets/onceover-nodes.yml`
202
+
203
+ Nodesets are used when running acceptance tests. They instruct the onceover gem how to spin up virtual machines to run the code on. Actually, that's a lie... What's really happening with nodesets is that we are using [Beaker](https://github.com/puppetlabs/beaker) to spin up the machines and then a combination of Beaker and RSpec to test them. But you don't need to worry about that too much. Due to the fact that we are using beaker to do the heavy lifting here the nodeset files follow the same format they would for normal Beaker tests, which at the time of writing supports the following hypervisors:
204
+
205
+ - [VMWare Fusion](https://github.com/puppetlabs/beaker/blob/master/docs/VMWare-Fusion-Support.md)
206
+ - [Amazon EC2](https://github.com/puppetlabs/beaker/blob/master/docs/EC2-Support.md)
207
+ - [vSphere](https://github.com/puppetlabs/beaker/blob/master/docs/vSphere-Support.md)
208
+ - [Vagrant](https://github.com/puppetlabs/beaker/blob/master/docs/Vagrant-Support.md)
209
+ - [Google Compute Engine](https://github.com/puppetlabs/beaker/blob/master/docs/Google-Compute-Engine-Support.md)
210
+ - [Docker](https://github.com/puppetlabs/beaker/blob/master/docs/Docker-Support.md)
211
+ - [Openstack](https://github.com/puppetlabs/beaker/blob/master/docs/Openstack-Support.md)
212
+ - [Solaris](https://github.com/puppetlabs/beaker/blob/master/docs/Solaris-Support.md)
213
+
214
+ Before we configure a hypervisor to spin up a node however, we have to make sure that it can clone from a machine which is ready. The onceover gem **requires it's VMs to have puppet pre-installed.** It doesn't matter what version of puppet, as long as it is on the PATH and the `type` setting is configured correctly i.e.
215
+
216
+ ```yaml
217
+ type: AIO # For machines that have the all-in-one agent installed (>=4.0 or >=2015.2)
218
+ # OR
219
+ type: pe # For puppet enterprise agents <2015.2
220
+ # OR
221
+ type: foss # For open source puppet <4.0
222
+ ```
223
+
224
+ Here is an example of a nodeset file that you can use yourselves. It uses freely available Vagrant boxes from Puppet and Virtualbox as the Vagrant provider. (`onceover init` will generate most of this for you)
225
+
226
+ ```yaml
227
+ HOSTS:
228
+ centos6a:
229
+ roles:
230
+ - agent
231
+ type: aio
232
+ platform: el-6-64
233
+ box: puppetlabs/centos-6.6-64-puppet
234
+ box_url: https://atlas.hashicorp.com/puppetlabs/boxes/centos-6.6-64-puppet
235
+ hypervisor: vagrant_virtualbox
236
+ centos7b:
237
+ roles:
238
+ - agent
239
+ type: aio
240
+ platform: el-7-64
241
+ box: puppetlabs/centos-7.0-64-puppet
242
+ box_url: https://atlas.hashicorp.com/puppetlabs/boxes/centos-7.0-64-puppet
243
+ hypervisor: vagrant_virtualbox
244
+ ubuntu1204:
245
+ roles:
246
+ - agent
247
+ type: aio
248
+ platform: ubuntu-12.04-32
249
+ box: puppetlabs/ubuntu-12.04-32-puppet
250
+ box_url: https://atlas.hashicorp.com/puppetlabs/boxes/ubuntu-12.04-32-puppet
251
+ hypervisor: vagrant_virtualbox
252
+ debian78:
253
+ roles:
254
+ - agent
255
+ type: aio
256
+ platform: debian-7.8-64
257
+ box: puppetlabs/debian-7.8-64-puppet
258
+ box_url: https://atlas.hashicorp.com/puppetlabs/boxes/debian-7.8-64-puppet
259
+ hypervisor: vagrant_virtualbox
260
+ ```
261
+
262
+ ### Hiera Data
263
+
264
+ If you have hiera data inside your controlrepo (or somewhere else) the Controlrepo gem can be configured to use it. Just dump your `hiera.yaml` file from the puppet master into the `spec/` directory or the root of your controlrepo and you are good to go.
265
+
266
+ **NOTE:** This assumes that the path to your hiera data (datadir) is relative to the root of the controlrepo, if not it will fall over.
267
+
268
+ **Alternatively:**, if you are using cool new per-environment hiera config made available in puppet 4.x, the tool will automatically detect this and everything should work.
269
+
270
+ ## Spec testing
271
+
272
+ Once you have your `onceover.yaml` and factsets set up you are ready to go with spec testing.
273
+
274
+ To run the tests:
275
+
276
+ `onceover run spec`
277
+
278
+ This will do the following things:
279
+
280
+ 1. Create a temporary directory under `.onceover`
281
+ 2. Clone all repos in the Puppetfile into the temporary directory
282
+ 3. Generate tests that use rspec-puppet
283
+ 4. Run the tests
284
+
285
+ ### Adding your own spec tests
286
+
287
+ When using this gem adding your own spec tests is exactly the same as if you were to add them to a module, simply create them under `spec/{classes,defines,etc.}` in the Controlrepo and they will be run like normal, along with all of the `it { should compile }` tests.
288
+
289
+ ## Acceptance testing
290
+
291
+ Acceptance testing works in much the same way as spec testing except that it requires a nodeset file along with `onceover.yaml`
292
+
293
+ To run the tests:
294
+
295
+ `onceover run acceptance`
296
+
297
+ This will do the following things:
298
+
299
+ 1. Create a temporary directory under `.onceover`
300
+ 2. Clone all repos in the Puppetfile into the temporary directory
301
+ 3. Generate tests that use RSpec and Beaker
302
+ 4. Run the tests, each test consists of:
303
+ - Spin up the VM
304
+ - Copy over the code
305
+ - Run puppet and catch any errors
306
+ - Run puppet again to catch anything that might not be idempotent
307
+ - Destroy the VM
308
+
309
+ ## Using workarounds
310
+
311
+ There may be situations where you cannot test everything that is in your puppet code, some common reasons for this include:
312
+
313
+ - Code is destined for a Puppet Master but the VM image is not a Puppet Master which means we can't restart certain services etc.
314
+ - A file is being pulled from somewhere that is only accessible in production
315
+ - Something is trying to connect to something else that does not exist
316
+
317
+ Fear not! There is a solution for this, it's also a good way to practice writing *nasty* puppet code. For this exact purpose I have added the ability for onceover to include extra bits of code in the tests to fix things like this. All you need to do is put a file/s containing puppet code here:
318
+
319
+ `spec/pre_conditions/*.pp`
320
+
321
+ What this will do is it will take any puppet code from any files it finds in that directory and have it executed alongside the code that you are actually testing. For example if we are testing some code that notifies the `pe-puppetserver` service, but are not managing that service in our code because it is managed by the PE module that ships with Puppet Enterprise the following code will fail:
322
+
323
+ ```puppet
324
+ file { '/etc/puppetlabs/puppet/puppet.conf':
325
+ ensure => file,
326
+ content => '#nothing',
327
+ notify => Service['pe-puppetserver'], # This will fail without the PE module!
328
+ }
329
+ ```
330
+
331
+ To fox this we can add the service to the pre_conditions to make sure that our catalogs can compile e.g.
332
+
333
+ ```puppet
334
+ # spec/pre_conditions/services.pp
335
+ service { 'pe-puppetserver':
336
+ ensure => 'running',
337
+ }
338
+ ```
339
+
340
+ However this is going to pose an issue when we get to acceptance testing. Due to the fact that acceptance tests actually run the code, not just tries to compile a catalog, it will not be able to find the 'pe-pupetserver' service and will fail. One way to get around this is to use some of the optional parameters to the service resource e.g.
341
+
342
+ ```puppet
343
+ # We are not going to actually have this service anywhere on our servers but
344
+ # our code needs to refresh it. This is to trick puppet into doing nothing
345
+ service { 'pe-puppetserver':
346
+ ensure => 'running',
347
+ enable => false,
348
+ hasrestart => false, # Force Puppet to use start and stop to restart
349
+ start => 'echo "Start"', # This will always exit 0
350
+ stop => 'echo "Stop"', # This will also always exit 0
351
+ hasstatus => false, # Force puppet to use our command for status
352
+ status => 'echo "Status"', # This will always exit 0 and therefore Puppet will think the service is running
353
+ provider => 'base',
354
+ }
355
+ ```
356
+
357
+ Here we are specifying custom commands to run for starting, stopping and checking the status of a service. We know what the exit codes of these commands are going to be so we know what puppet will think the service is doing because we have [read the documentation](https://docs.puppetlabs.com/references/latest/type.html#service-attributes). If there are things other than services you need to check then I would recommend checking the documentation to see if you can mock things like we have here. Alternatively you might need to create specific VM images that are pre-prepared.
358
+
359
+ [Resource collectors](https://docs.puppetlabs.com/puppet/latest/reference/lang_resources_advanced.html#amending-attributes-with-a-collector) are likely to come in handy here too. They allow you to override values of resources that match given criteria. This way we can override things for the sake of testing without having to change the code.
360
+
361
+ **NOTE:** If you need to run some pre_conditions during acceptance tests but not spec tests or vice versa you can check the status of the `$controlrepo_accpetance` variable. It will be `true` when run as an acceptance test and `undef` otherwise. If you want to limit pre_conditions to only certain nodes just use conditional logic based on facts like you normally would.
362
+
363
+ ## Extra Tooling
364
+
365
+ Is this all too simple for you? Great! This is supposed to be a gateway to writing your own super-awesome really complicated tests using more traditional tools. If you want to ditch this tool in favour of doing it yourself, go ahead, but take these ruby methods as a parting gift:
366
+
367
+ ### Accessing fact sets in a traditional RSpec test
368
+
369
+ We can access all of our fact sets using `Onceover::Controlrepo.facts`. Normally it would be implemented something like this:
370
+
371
+ ```ruby
372
+ Onceover::Controlrepo.facts.each do |facts|
373
+ context "on #{facts['fqdn']}" do
374
+ let(:facts) { facts }
375
+ it { should compile }
376
+ end
377
+ end
378
+ ```
379
+
380
+ ### Accessing Roles in a traditional RSpec test
381
+
382
+ The following code will test all roles on all nodes in native rspec:
383
+
384
+ ```ruby
385
+ require 'spec_helper'
386
+ require 'onceover/controlrepo'
387
+ Onceover::Controlrepo.roles.each do |role|
388
+ describe role do
389
+ Onceover::Controlrepo.facts.each do |facts|
390
+ context "on #{facts['fqdn']}" do
391
+ let(:facts) { facts }
392
+ it { should compile }
393
+ end
394
+ end
395
+ end
396
+ end
397
+ ```
398
+
399
+ This will iterate over each role in the controlrepo and test that it compiles with each set of facts.
400
+
401
+ The same can also be done with profiles just by using the profiles method instead:
402
+
403
+ ```ruby
404
+ require 'spec_helper'
405
+ require 'controlrepo'
406
+ Onceover::Controlrepo.profiles.each do |profile|
407
+ describe profile do
408
+ Onceover::Controlrepo.facts.each do |facts|
409
+ context "on #{facts['fqdn']}" do
410
+ let(:facts) { facts }
411
+ it { should compile }
412
+ end
413
+ end
414
+ end
415
+ end
416
+ ```
417
+
418
+ It is not limited to just doing simple "It should compile" tests. You can put any tests you want in here.
419
+
420
+ Also since the `profiles`, `roles` and `facts` methods simply return arrays, you can iterate over them however you would like i.e. you could write a different set of tests for each profile and then just use the `facts` method to run those tests on every fact set.
421
+
422
+ ### Filtering
423
+
424
+ You can also filter your fact sets based on the value of any fact, including structured facts. (It will drill down into nested hashes and match those too, it's not just a dumb equality match)
425
+
426
+ Just pass a hash to the `facts` method and it will return only the fact sets with facts that match the hash e.g. Testing a certain profile on against only your Windows fact sets:
427
+
428
+ ```ruby
429
+ require 'spec_helper'
430
+ require 'controlrepo'
431
+
432
+ describe 'profile::windows_appserver' do
433
+ Onceover::Controlrepo.facts({
434
+ 'kernel' => 'windows'
435
+ }).each do |facts|
436
+ context "on #{facts['fqdn']}" do
437
+ let(:facts) { facts }
438
+ it { should compile }
439
+ end
440
+ end
441
+ end
442
+ ```
443
+
444
+ ### Extra Configuration
445
+
446
+ You can modify the regexes that the gem uses to filter classes that it finds into roles and profiles. Just set up a Controlrepo object and pass regexes to the below settings.
447
+
448
+ ```ruby
449
+ repo = Onceover::Controlrepo.new()
450
+ repo.role_regex = /.*/ # Tells the class how to find roles, will be applied to repo.classes
451
+ repo.profile_regex = /.*/ # Tells the class how to find profiles, will be applied to repo.classes
452
+ ```
453
+
454
+ Note that you will need to call the `roles` and `profiles` methods on the object you just instantiated, not the main class e.g. `repo.roles` not Onceover::Controlrepo.roles`
455
+
456
+ ### Rake tasks
457
+
458
+ I have included a couple of little rake tasks to help get you started with testing your control repos. Set them up by adding this to your `Rakefile`
459
+
460
+ ```ruby
461
+ require 'controlrepo/rake_tasks'
462
+ ```
463
+
464
+ The tasks are as follows:
465
+
466
+ #### generate_fixtures
467
+
468
+ `bundle exec rake generate_fixtures`
469
+
470
+ This task will go though your Puppetfile, grab all of the modules in there and convert them into a `.fixtures.yml` file. (You only need this if you are writing your own custom spec tests) It will also take the `environment.conf` file into account, check to see if you have any relative pathed directories and also include them into the `.fixtures.yml` as symlinks. e.g. If your files look like this:
471
+
472
+ **Puppetfile**
473
+ ```ruby
474
+ forge "http://forgeapi.puppetlabs.com"
475
+
476
+ # Modules from the Puppet Forge
477
+ mod "puppetlabs/stdlib", "4.6.0"
478
+ mod "puppetlabs/apache", "1.5.0"
479
+ ```
480
+
481
+ **environment.conf**
482
+ ```ini
483
+ modulepath = site:modules:$basemodulepath
484
+ environment_timeout = 0
485
+ ```
486
+
487
+ Then the `.fixtures.yml` file that this rake task will create will look like this:
488
+
489
+ ```yaml
490
+ ---
491
+ fixtures:
492
+ symlinks:
493
+ profiles: site/profiles
494
+ roles: site/roles
495
+ forge_modules:
496
+ stdlib:
497
+ repo: puppetlabs/stdlib
498
+ ref: 4.6.0
499
+ apache:
500
+ repo: puppetlabs/apache
501
+ ref: 1.5.0
502
+ ```
503
+
504
+ Notice that the symlinks are not the ones that we provided in `environment.conf`? This is because the rake task will go into each of directories, find the modules and create a symlink for each of them (This is what rspec expects).