controlrepo 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/README.md +274 -0
- data/controlrepo.gemspec +26 -0
- data/lib/controlrepo.rb +323 -0
- data/lib/controlrepo/beaker.rb +185 -0
- data/lib/controlrepo/class.rb +28 -0
- data/lib/controlrepo/group.rb +61 -0
- data/lib/controlrepo/node.rb +43 -0
- data/lib/controlrepo/rake_tasks.rb +150 -0
- data/lib/controlrepo/test.rb +124 -0
- data/lib/controlrepo/testconfig.rb +136 -0
- data/templates/.fixtures.yml.erb +24 -0
- data/templates/Gemfile.erb +5 -0
- data/templates/Rakefile.erb +1 -0
- data/templates/acceptance_test_spec.rb.erb +18 -0
- data/templates/nodeset.yaml.erb +9 -0
- data/templates/spec_helper.rb.erb +7 -0
- data/templates/spec_helper_acceptance.rb.erb +24 -0
- data/templates/test_spec.rb.erb +13 -0
- metadata +162 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 074cfbffbc367cb76428c30a900b1bc1b2dc7525
|
4
|
+
data.tar.gz: f2901badd0097e028d2cc835ae0fe5c5eae7bc8a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ceb858045e2bbc73fab0873d65a617338b980c1d2ecfd06aa37dcb60be0c821ec91f1671fb19a1155438735cce20cace0bf2a9206097f63f799879487e93b995
|
7
|
+
data.tar.gz: 281451f8f1c470f8ff9a31c8c57f1f2a4f51566b26bf72833a7ce731fac1647575a0cb608daab0671fe2f2d00f6c47c61fd3fdd80dee0a9acf82f51d1dac08ae
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
.DS_Store
|
data/README.md
ADDED
@@ -0,0 +1,274 @@
|
|
1
|
+
# Controlrepo Toolset
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
This gem gives you a bunch of tools to use for testing and generally managing Puppet controlrepos. 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 standardise 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 test 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.
|
6
|
+
|
7
|
+
This toolset has two distinct ways of being used, easy mode and hard mode.
|
8
|
+
|
9
|
+
### Easy Mode
|
10
|
+
|
11
|
+
The object of *easy mode* is to allow people to run simple `it { should compile }` acceptance tests without needing to set up any of the extra stuff required by the rspec-puppet testing framework.
|
12
|
+
|
13
|
+
`rake tasks go here once they are done`
|
14
|
+
|
15
|
+
A stretch goal is to also include acceptance testing, allowing people to spin up boxes for each role they have and test them before merging code into development environments or production. At the moment we can't do this, hold tight.
|
16
|
+
|
17
|
+
#### Easy mode config
|
18
|
+
|
19
|
+
The whole idea of easy mode is that we should just be able to write down which classes we want to test on which machines and this tool should be able to do the rest. This all has to be set up somewhere, this is **spec/controlrepo.yaml** which looks something like this:
|
20
|
+
|
21
|
+
```yaml
|
22
|
+
classes:
|
23
|
+
- 'roles::backend_dbserver'
|
24
|
+
- 'roles::frontend_webserver'
|
25
|
+
- 'roles::load_balancer'
|
26
|
+
- 'roles::syd_f5_load_balancer'
|
27
|
+
- 'roles::windows_server'
|
28
|
+
|
29
|
+
nodes:
|
30
|
+
- centos6a
|
31
|
+
- centos7b
|
32
|
+
- server2008r2a
|
33
|
+
- ubuntu1404a
|
34
|
+
|
35
|
+
groups:
|
36
|
+
windows_roles:
|
37
|
+
- 'roles::windows_server'
|
38
|
+
- 'roles::backend_dbserver'
|
39
|
+
centos_servers:
|
40
|
+
- centos6a
|
41
|
+
- centos7b
|
42
|
+
|
43
|
+
test_matrix:
|
44
|
+
server2008r2a: windows_roles
|
45
|
+
ubuntu1404a: 'roles::frontend_webserver'
|
46
|
+
centos_servers:
|
47
|
+
include: all_classes
|
48
|
+
exclude: windows_roles
|
49
|
+
```
|
50
|
+
|
51
|
+
It consists of the following sections:
|
52
|
+
|
53
|
+
##### Classes:
|
54
|
+
|
55
|
+
This is where we list all of the classes that we want to test, normally this will just be a list of roles. Note that these classes must *actually exist* for reasons that should be obvious.
|
56
|
+
|
57
|
+
##### Nodes:
|
58
|
+
|
59
|
+
Each node in the list refers one of two things depending on weather we are running **spec** or **acceptance** tests. If we are running **spec** tests each node refers to the name of a [fact set](#fact-sets) because this will be the set of facts that the `it { should compile }` test will be run against. If we are are running **acceptance** tests then each node will refer to a *nodeset* file which we can generate (or at least try to) using the `generate_nodesets` rake task. For acceptance testing the nodeset file will tell us how to spin up the VMs for each machine.
|
60
|
+
|
61
|
+
##### Groups:
|
62
|
+
|
63
|
+
Groups are used to save us a bit of time and code (if you can call yaml that). Unsurprisingly a group is a way to bundle either classes or nodes into a group that we can refer to but it's name instead of repeating ourselves a whole bunch. There are 2 **default groups:**
|
64
|
+
|
65
|
+
- all_nodes
|
66
|
+
- all_classes
|
67
|
+
|
68
|
+
You can guess what they are for I hope.
|
69
|
+
|
70
|
+
*Note that groups CANNOT contain a mix of classes and nodes, only one or the other.*
|
71
|
+
|
72
|
+
##### Test Matrix:
|
73
|
+
|
74
|
+
This is the section of th config file that makes the magic happen. In the test matrix we choose on which nodes we will tests which classes. You can use groups anywhere here as you can see in the example above. We also have the option of using *include* and *exclude* which will be useful if you have a lot of groups.
|
75
|
+
|
76
|
+
For example if we want to test all our non-windows roles on all of our linux boxes we can do something like this:
|
77
|
+
|
78
|
+
```yaml
|
79
|
+
test_matrix:
|
80
|
+
linux_nodes:
|
81
|
+
include: 'all_classes'
|
82
|
+
exclude: 'windows_roles'
|
83
|
+
```
|
84
|
+
|
85
|
+
This is assuming that you have all of your linux nodes in the `linux_nodes` group and all of your Windows roles in the `windows_roles` group.
|
86
|
+
|
87
|
+
When setting up your tests matrix don't worry too much about using groups that will cause duplicate combinations of `node -> class` pairs. The rake tasks run deduplication before running any of the tests to make sure that we are not wasting time. This happens at runtime and does not affect the file or anything.
|
88
|
+
|
89
|
+
#### Lets go!
|
90
|
+
|
91
|
+
Now to run the spec tests just do a `bundler exec rake controlrepo_spec`
|
92
|
+
|
93
|
+
|
94
|
+
### Hard mode
|
95
|
+
|
96
|
+
The point of *hard mode* is to give people who are familiar with RSpec testing with puppet a set of useful tools that they can mix into their tests to save some hassle. We also want to help in getting your tests set up by automatically generating `.fixtures.yml` and nodesets.
|
97
|
+
|
98
|
+
## Fact sets
|
99
|
+
|
100
|
+
This gem introduces the concept of fact sets. Instead of manually specifying facts in each rspec test we can just dump the actual facts from the actual machines in our environment into a folder then test against them. To do this we first need to dump the facts into a json file:
|
101
|
+
|
102
|
+
`puppet facts > server01.json`
|
103
|
+
|
104
|
+
Then grab this file and put it in `spec/facts` inside your control repo. It doesn't matter what you name these files, as long as they are in that directory and they end with `json` we will be able to find them. Fact sets are also heavily used by **easy mode**.
|
105
|
+
|
106
|
+
**That's it!**
|
107
|
+
|
108
|
+
Now we can access all of these fact sets using `Controlrepo.facts`. Normally it would be implemented something like this:
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
Controlrepo.facts.each do |facts|
|
112
|
+
context "on #{facts['fqdn']}" do
|
113
|
+
let(:facts) { facts }
|
114
|
+
it { should compile }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
119
|
+
|
120
|
+
## Rake tasks
|
121
|
+
|
122
|
+
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`
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
require 'controlrepo/rake_tasks'
|
126
|
+
```
|
127
|
+
|
128
|
+
The tasks are as follows:
|
129
|
+
|
130
|
+
### generate_fixtures
|
131
|
+
|
132
|
+
`bundle exec rake generate_fixtures`
|
133
|
+
|
134
|
+
This task will go though your Puppetfile, grab all of the modules in there and convert them into a `.fixtures.yml` file. 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:
|
135
|
+
|
136
|
+
**Puppetfile**
|
137
|
+
```ruby
|
138
|
+
forge "http://forgeapi.puppetlabs.com"
|
139
|
+
|
140
|
+
# Modules from the Puppet Forge
|
141
|
+
mod "puppetlabs/stdlib", "4.6.0"
|
142
|
+
mod "puppetlabs/apache", "1.5.0"
|
143
|
+
```
|
144
|
+
|
145
|
+
**environment.conf**
|
146
|
+
```ini
|
147
|
+
modulepath = site:modules:$basemodulepath
|
148
|
+
environment_timeout = 0
|
149
|
+
```
|
150
|
+
|
151
|
+
Then the `.fixtures.yml` file that this rake task will create will look like this:
|
152
|
+
|
153
|
+
```yaml
|
154
|
+
---
|
155
|
+
fixtures:
|
156
|
+
symlinks:
|
157
|
+
profiles: site/profiles
|
158
|
+
roles: site/roles
|
159
|
+
forge_modules:
|
160
|
+
stdlib:
|
161
|
+
repo: puppetlabs/stdlib
|
162
|
+
ref: 4.6.0
|
163
|
+
apache:
|
164
|
+
repo: puppetlabs/apache
|
165
|
+
ref: 1.5.0
|
166
|
+
```
|
167
|
+
|
168
|
+
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).
|
169
|
+
|
170
|
+
### generate_nodesets
|
171
|
+
|
172
|
+
`bundle exec rake generate_nodesets`
|
173
|
+
|
174
|
+
This task will generate nodeset file required by beaker, based on the fact sets that exist in the repository. If you have any fact sets for which puppetlabs has a compatible vagrant box (i.e. centos, debian, ubuntu) it will detect the version specifics and set up the nodeset file, complete with box URL. If it doesn't know how to deal with a fact set it will output a boilerplate nodeset file that will need to be altered before it can be used.
|
175
|
+
|
176
|
+
### hiera_setup
|
177
|
+
|
178
|
+
`bundle exec rake hiera_setup`
|
179
|
+
|
180
|
+
Automatically modifies your hiera.yaml to point at the hieradata relative to it's position.
|
181
|
+
|
182
|
+
This rake task will look for a hiera.yaml file (Using the same method we use for [this](#using-hiera-data)). It will then look for a hieradata directory in the root for your control repo (needs to match [this](http://rubular.com/?regex=%2Fhiera%28%3F%3A.%2Adata%29%3F%2Fi)). It will then modify the datadirs of any backends it finds in `hiera.yaml` to point at these directories.
|
183
|
+
|
184
|
+
## Running the tests (Hard mode)
|
185
|
+
|
186
|
+
This gem allows us to easily and dynamically test all of the roles and profiles in our environment against fact sets from all of the nodes to which they will be applied. All we need to do is create a spec test that calls out to the `Controlrepo` ruby class:
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
require 'spec_helper'
|
190
|
+
require 'controlrepo'
|
191
|
+
|
192
|
+
Controlrepo.roles.each do |role|
|
193
|
+
describe role do
|
194
|
+
Controlrepo.facts.each do |facts|
|
195
|
+
context "on #{facts['fqdn']}" do
|
196
|
+
let(:facts) { facts }
|
197
|
+
it { should compile }
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
```
|
203
|
+
|
204
|
+
This will iterate over each role in the controlrepo and test that it compiles with each set of facts.
|
205
|
+
|
206
|
+
The same can also be done with profiles just by using the profiles method instead:
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
require 'spec_helper'
|
210
|
+
require 'controlrepo'
|
211
|
+
|
212
|
+
Controlrepo.profiles.each do |profile|
|
213
|
+
describe profile do
|
214
|
+
Controlrepo.facts.each do |facts|
|
215
|
+
context "on #{facts['fqdn']}" do
|
216
|
+
let(:facts) { facts }
|
217
|
+
it { should compile }
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
```
|
223
|
+
|
224
|
+
It is not limited to just doing simple "It should compile" tests. You can put any tests you want in here.
|
225
|
+
|
226
|
+
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.
|
227
|
+
|
228
|
+
## Filtering (Hard mode)
|
229
|
+
|
230
|
+
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)
|
231
|
+
|
232
|
+
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:
|
233
|
+
|
234
|
+
```ruby
|
235
|
+
require 'spec_helper'
|
236
|
+
require 'controlrepo'
|
237
|
+
|
238
|
+
describe 'profile::windows_appserver' do
|
239
|
+
Controlrepo.facts({
|
240
|
+
'kernel' => 'windows'
|
241
|
+
}).each do |facts|
|
242
|
+
context "on #{facts['fqdn']}" do
|
243
|
+
let(:facts) { facts }
|
244
|
+
it { should compile }
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
```
|
249
|
+
|
250
|
+
## Using hiera data (Hard Mode)
|
251
|
+
|
252
|
+
You can also point these tests at your hiera data, you do this as you [normally would](https://github.com/rodjek/rspec-puppet#enabling-hiera-lookups) with rspec tests. However we do provide one helper to make this marginally easier. `Controlrepo.hiera_config` will look for hiera.yaml in the root of your control repo and also the spec directory, you will however need to set up the file itself e.g.
|
253
|
+
|
254
|
+
```ruby
|
255
|
+
require 'controlrepo'
|
256
|
+
|
257
|
+
RSpec.configure do |c|
|
258
|
+
c.hiera_config = Controlrepo.hiera_config_file
|
259
|
+
end
|
260
|
+
```
|
261
|
+
|
262
|
+
|
263
|
+
## Extra Configuration
|
264
|
+
|
265
|
+
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.
|
266
|
+
|
267
|
+
```ruby
|
268
|
+
repo = Controlrepo.new()
|
269
|
+
repo.role_regex = /.*/ # Tells the class how to find roles, will be applied to repo.classes
|
270
|
+
repo.profile_regex = /.*/ # Tells the class how to find profiles, will be applied to repo.classes
|
271
|
+
```
|
272
|
+
|
273
|
+
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 `Controlrepo.roles`
|
274
|
+
|
data/controlrepo.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "controlrepo"
|
6
|
+
s.version = "1.0.0"
|
7
|
+
s.authors = ["Dylan Ratcliffe"]
|
8
|
+
s.email = ["dylan.ratcliffe@puppetlabs.com"]
|
9
|
+
s.homepage = ""
|
10
|
+
s.summary = ""
|
11
|
+
s.description = ""
|
12
|
+
s.licenses = 'Apache-2.0'
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
#s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
|
18
|
+
# Runtime dependencies, but also probably dependencies of requiring projects
|
19
|
+
s.add_runtime_dependency 'rake'
|
20
|
+
s.add_runtime_dependency 'json'
|
21
|
+
s.add_runtime_dependency 'beaker-rspec'
|
22
|
+
s.add_runtime_dependency 'rspec-puppet'
|
23
|
+
s.add_runtime_dependency 'rspec'
|
24
|
+
s.add_runtime_dependency 'bundler'
|
25
|
+
s.add_runtime_dependency 'puppet'
|
26
|
+
end
|
data/lib/controlrepo.rb
ADDED
@@ -0,0 +1,323 @@
|
|
1
|
+
require 'pry'
|
2
|
+
require 'r10k/puppetfile'
|
3
|
+
require 'erb'
|
4
|
+
require 'json'
|
5
|
+
require 'yaml'
|
6
|
+
require 'find'
|
7
|
+
require 'pathname'
|
8
|
+
require 'controlrepo/beaker'
|
9
|
+
|
10
|
+
class Controlrepo
|
11
|
+
attr_accessor :root
|
12
|
+
attr_accessor :puppetfile
|
13
|
+
attr_accessor :facts_files
|
14
|
+
attr_accessor :role_regex
|
15
|
+
attr_accessor :profile_regex
|
16
|
+
attr_accessor :temp_environmentpath
|
17
|
+
attr_accessor :tempdir
|
18
|
+
attr_accessor :spec_dir
|
19
|
+
attr_accessor :temp_modulepath
|
20
|
+
|
21
|
+
# Create methods on self so that we can access these basic things without
|
22
|
+
# having to actually instantiate the class, I'm debating how much stuff
|
23
|
+
# I should be putting in here, we don't reeeally need to instantiate the
|
24
|
+
# object unless we want to modify it's parameters, so maybe everything.
|
25
|
+
# We shall see...
|
26
|
+
#
|
27
|
+
# And yeah I know this makes little sense, but it will look nicer to type, promise
|
28
|
+
#
|
29
|
+
# Also it's probably pretty memory hungry, but let's be honest, how many
|
30
|
+
# times would be be calling this? If we call it over and over you can just
|
31
|
+
# instantiate it anyway
|
32
|
+
def self.root
|
33
|
+
Controlrepo.new.root
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.puppetfile
|
37
|
+
Controlrepo.new.puppetfile
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.facts_files
|
41
|
+
Controlrepo.new.facts_files
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.classes
|
45
|
+
Controlrepo.new.classes
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.roles
|
49
|
+
Controlrepo.new.roles
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.profiles
|
53
|
+
Controlrepo.new.profiles
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.config
|
57
|
+
Controlrepo.new.config
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.facts(filter = nil)
|
61
|
+
Controlrepo.new.facts(filter)
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.hiera_config_file
|
65
|
+
Controlrepo.new.hiera_config_file
|
66
|
+
end
|
67
|
+
#
|
68
|
+
# End class methods
|
69
|
+
#
|
70
|
+
|
71
|
+
def initialize(search_path = Dir.pwd)
|
72
|
+
# When we initialize the object it is going to set some instance vars
|
73
|
+
begin
|
74
|
+
# Find the root of the control repo by traversing up
|
75
|
+
until File.exist?(File.expand_path('./Puppetfile',search_path)) do
|
76
|
+
search_path = File.expand_path('..',search_path)
|
77
|
+
end
|
78
|
+
rescue => e
|
79
|
+
raise " Could not find Puppetfile"
|
80
|
+
raise e
|
81
|
+
end
|
82
|
+
@root = search_path
|
83
|
+
@puppetfile = File.expand_path('./Puppetfile',@root)
|
84
|
+
@environment_conf = File.expand_path('./environment.conf',@root)
|
85
|
+
@facts_dir = File.expand_path('./spec/facts',@root)
|
86
|
+
@spec_dir = File.expand_path('./spec',@root)
|
87
|
+
@facts_files = Dir["#{@facts_dir}/*.json"]
|
88
|
+
@role_regex = /role[s]?:{2}/
|
89
|
+
@profile_regex = /profile[s]?:{2}/
|
90
|
+
@temp_environmentpath = nil
|
91
|
+
@tempdir = nil
|
92
|
+
$temp_modulepath = nil
|
93
|
+
end
|
94
|
+
|
95
|
+
def roles
|
96
|
+
classes.keep_if { |c| c =~ @role_regex }
|
97
|
+
end
|
98
|
+
|
99
|
+
def profiles
|
100
|
+
classes.keep_if { |c| c =~ @profile_regex }
|
101
|
+
end
|
102
|
+
|
103
|
+
def classes
|
104
|
+
# Get all of the possible places for puppet code and look for classes
|
105
|
+
code_dirs = self.config['modulepath']
|
106
|
+
# Remove relative references
|
107
|
+
code_dirs.delete_if { |dir| dir[0] == '$'}
|
108
|
+
|
109
|
+
# Get all the classes from all of the manifests
|
110
|
+
classes = []
|
111
|
+
code_dirs.each do |dir|
|
112
|
+
classes << get_classes(dir)
|
113
|
+
end
|
114
|
+
classes.flatten
|
115
|
+
end
|
116
|
+
|
117
|
+
def facts(filter = nil)
|
118
|
+
# Returns an array facts hashes
|
119
|
+
all_facts = []
|
120
|
+
@facts_files.each do |file|
|
121
|
+
all_facts << read_facts(file)['values']
|
122
|
+
end
|
123
|
+
if filter
|
124
|
+
# Allow us to pass a hash of facts to filter by
|
125
|
+
raise "Filter param must be a hash" unless filter.is_a?(Hash)
|
126
|
+
all_facts.keep_if do |hash|
|
127
|
+
matches = []
|
128
|
+
filter.each do |filter_fact,value|
|
129
|
+
matches << keypair_is_in_hash(hash,filter_fact,value)
|
130
|
+
end
|
131
|
+
if matches.include? false
|
132
|
+
false
|
133
|
+
else
|
134
|
+
true
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
return all_facts
|
139
|
+
end
|
140
|
+
|
141
|
+
def fixtures
|
142
|
+
# Load up the Puppetfile using R10k
|
143
|
+
puppetfile = R10K::Puppetfile.new(@root)
|
144
|
+
modules = puppetfile.load
|
145
|
+
|
146
|
+
# Iterate over everything and seperate it out for the sake of readability
|
147
|
+
symlinks = []
|
148
|
+
forge_modules = []
|
149
|
+
repositories = []
|
150
|
+
|
151
|
+
modules.each do |mod|
|
152
|
+
# This logic could probably be cleaned up. A lot.
|
153
|
+
if mod.is_a? R10K::Module::Forge
|
154
|
+
if mod.expected_version.is_a?(Hash)
|
155
|
+
# Set it up as a symlink, because we are using local files in the Puppetfile
|
156
|
+
symlinks << {
|
157
|
+
'name' => mod.name,
|
158
|
+
'dir' => mod.expected_version[:path]
|
159
|
+
}
|
160
|
+
elsif mod.expected_version.is_a?(String)
|
161
|
+
# Set it up as a normal firge module
|
162
|
+
forge_modules << {
|
163
|
+
'name' => mod.name,
|
164
|
+
'repo' => mod.title,
|
165
|
+
'ref' => mod.expected_version
|
166
|
+
}
|
167
|
+
end
|
168
|
+
elsif mod.is_a? R10K::Module::Git
|
169
|
+
# Set it up as a git repo
|
170
|
+
repositories << {
|
171
|
+
'name' => mod.name,
|
172
|
+
# I know I shouldn't be doing this, but trust me, there are no methods
|
173
|
+
# anywhere that expose this value, I looked.
|
174
|
+
'repo' => mod.instance_variable_get(:@remote),
|
175
|
+
'ref' => mod.version
|
176
|
+
}
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# also add synlinks for anything that is in environment.conf
|
181
|
+
code_dirs = self.config['modulepath']
|
182
|
+
code_dirs.delete_if { |dir| dir[0] == '$'}
|
183
|
+
code_dirs.each do |dir|
|
184
|
+
# We need to traverse down into these directories and create a symlink for each
|
185
|
+
# module we find because fixtures.yml is expecting the module's root not the
|
186
|
+
# root of modulepath
|
187
|
+
Dir["#{dir}/*"].each do |mod|
|
188
|
+
symlinks << {
|
189
|
+
'name' => File.basename(mod),
|
190
|
+
'dir' => Pathname.new(File.expand_path(mod)).relative_path_from(Pathname.new(@root))#File.expand_path(mod)
|
191
|
+
}
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Use an ERB template to write the files
|
196
|
+
template_dir = File.expand_path('../templates',File.dirname(__FILE__))
|
197
|
+
fixtures_template = File.read(File.expand_path('./.fixtures.yml.erb',template_dir))
|
198
|
+
fixtures_yaml = ERB.new(fixtures_template, nil, '-').result(binding)
|
199
|
+
return fixtures_yaml
|
200
|
+
end
|
201
|
+
|
202
|
+
def hiera_config_file
|
203
|
+
# try to find the hiera.iyaml file
|
204
|
+
hiera_config_file = File.expand_path('./hiera.yaml',@spec_dir) if File.exist?(File.expand_path('./hiera.yaml',@spec_dir))
|
205
|
+
hiera_config_file = File.expand_path('./hiera.yaml',@root) if File.exist?(File.expand_path('./hiera.yaml',@root))
|
206
|
+
hiera_config_file
|
207
|
+
end
|
208
|
+
|
209
|
+
def hiera_config
|
210
|
+
YAML.load_file(hiera_config_file)
|
211
|
+
end
|
212
|
+
|
213
|
+
def hiera_config=(data)
|
214
|
+
File.write(hiera_config_file,data.to_yaml)
|
215
|
+
end
|
216
|
+
|
217
|
+
def hiera_data
|
218
|
+
# This is going to try to find your hiera data directory, if you have named it something
|
219
|
+
# unexpected it won't work
|
220
|
+
possibe_datadirs = Dir["#{@root}/*/"]
|
221
|
+
possibe_datadirs.keep_if { |dir| dir =~ /hiera(?:.*data)?/i }
|
222
|
+
raise "There were too many directories that looked like hiera data: #{possibe_datadirs}" if possibe_datadirs.count > 1
|
223
|
+
File.expand_path(possibe_datadirs[0])
|
224
|
+
end
|
225
|
+
|
226
|
+
def config
|
227
|
+
# Parse the file
|
228
|
+
env_conf = File.read(@environment_conf)
|
229
|
+
env_conf = env_conf.split("\n")
|
230
|
+
|
231
|
+
# Map the lines into a hash
|
232
|
+
environment_config = {}
|
233
|
+
env_conf.each do |line|
|
234
|
+
environment_config.merge!(Hash[*line.split('=').map { |s| s.strip}])
|
235
|
+
end
|
236
|
+
|
237
|
+
# Finally, split the modulepath values and return
|
238
|
+
begin
|
239
|
+
environment_config['modulepath'] = environment_config['modulepath'].split(':')
|
240
|
+
rescue
|
241
|
+
raise "modulepath was not found in environment.conf, don't know where to look for roles & profiles"
|
242
|
+
end
|
243
|
+
return environment_config
|
244
|
+
end
|
245
|
+
|
246
|
+
def r10k_config_file
|
247
|
+
r10k_config_file = File.expand_path('./r10k.yaml',@spec_dir) if File.exist?(File.expand_path('./r10k.yaml',@spec_dir))
|
248
|
+
r10k_config_file = File.expand_path('./r10k.yaml',@root) if File.exist?(File.expand_path('./r10k.yaml',@root))
|
249
|
+
r10k_config_file
|
250
|
+
end
|
251
|
+
|
252
|
+
def r10k_config
|
253
|
+
YAML.load_file(r10k_config_file)
|
254
|
+
end
|
255
|
+
|
256
|
+
def r10k_config=(data)
|
257
|
+
File.write(r10k_config_file,data.to_yaml)
|
258
|
+
end
|
259
|
+
|
260
|
+
private
|
261
|
+
|
262
|
+
def read_facts(facts_file)
|
263
|
+
file = File.read(facts_file)
|
264
|
+
begin
|
265
|
+
result = JSON.parse(file)
|
266
|
+
rescue JSON::ParserError
|
267
|
+
raise "Could not parse the JSON file, check that it is valid JSON and that the encoding is correct"
|
268
|
+
end
|
269
|
+
result
|
270
|
+
end
|
271
|
+
|
272
|
+
def keypair_is_in_hash(first_hash, key, value)
|
273
|
+
matches = []
|
274
|
+
if first_hash.has_key?(key)
|
275
|
+
if value.is_a?(Hash)
|
276
|
+
value.each do |k,v|
|
277
|
+
matches << keypair_is_in_hash(first_hash[key],k,v)
|
278
|
+
end
|
279
|
+
else
|
280
|
+
if first_hash[key] == value
|
281
|
+
matches << true
|
282
|
+
else
|
283
|
+
matches << false
|
284
|
+
end
|
285
|
+
end
|
286
|
+
else
|
287
|
+
matches << false
|
288
|
+
end
|
289
|
+
if matches.include? false
|
290
|
+
false
|
291
|
+
else
|
292
|
+
true
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def get_classes(dir)
|
297
|
+
classes = []
|
298
|
+
# Recurse over all the pp files under the dir we are given
|
299
|
+
Dir["#{dir}/**/*.pp"].each do |manifest|
|
300
|
+
classname = find_classname(manifest)
|
301
|
+
# Add it to the array as long as it is not nil
|
302
|
+
classes << classname if classname
|
303
|
+
end
|
304
|
+
classes
|
305
|
+
end
|
306
|
+
|
307
|
+
def find_classname(filename)
|
308
|
+
file = File.new(filename, "r")
|
309
|
+
while (line = file.gets)
|
310
|
+
if line =~ /^class (\w+(?:::\w+)*)/
|
311
|
+
return $1
|
312
|
+
end
|
313
|
+
end
|
314
|
+
return nil
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
|
319
|
+
|
320
|
+
|
321
|
+
|
322
|
+
|
323
|
+
|