controlrepo 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +69 -15
- data/controlrepo.gemspec +2 -2
- data/lib/controlrepo.rb +6 -1
- data/lib/controlrepo/test.rb +37 -1
- data/lib/controlrepo/testconfig.rb +14 -13
- data/templates/acceptance_test_spec.rb.erb +12 -6
- data/templates/spec_helper_acceptance.rb.erb +0 -12
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b60c1c6a0508a99a08fefac24672a0e78d117a78
|
4
|
+
data.tar.gz: a0ec19878c1fa9a05db40bbf9f8f0a214cae5e53
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b042a8f6fbc832b9113826c526dcc24025be5ef2f6087c159f0f51c8b60cee0b9b4300c01933a74ed49a6dfd084b62bd7693353935640133a282a4efc6cc03e
|
7
|
+
data.tar.gz: eb0d0b250c11997aef57348e2f4a595aff90c468b1ca8036351c5d2588fe619523a837f8fa8f5e6d29985a96a3fba6542d88280aa6fd8be6b73eb70c7b998a9c
|
data/README.md
CHANGED
@@ -45,6 +45,10 @@ If we are doing spec testing we need sets of facts to compile the puppet code ag
|
|
45
45
|
|
46
46
|
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).
|
47
47
|
|
48
|
+
There is one thing that is not configured using config files and that is the **environment** to test. To change the environment that tests run in simply use the `CONTROLREPO_env` environment variable e.g.
|
49
|
+
|
50
|
+
`CONTROLREPO_env=development bundle exec rake controlrepo_spec`
|
51
|
+
|
48
52
|
### controlrepo.yaml
|
49
53
|
|
50
54
|
`spec/controlrepo.yaml`
|
@@ -55,11 +59,23 @@ Hopefully this config file will be fairly self explanatory once you see it, but
|
|
55
59
|
|
56
60
|
**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.
|
57
61
|
|
58
|
-
**
|
62
|
+
**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.
|
59
63
|
|
60
|
-
**
|
64
|
+
**class_groups:** The `class_groups` section is jmuch 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.
|
61
65
|
|
62
|
-
|
66
|
+
**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:
|
67
|
+
|
68
|
+
```yaml
|
69
|
+
- {nodes_to_test}:
|
70
|
+
classes: '{classes_to_test}'
|
71
|
+
tests: '{all_tests|acceptance|spec}' # One of the three
|
72
|
+
options: # Optional (haha, get it?)
|
73
|
+
{valid_option}: {value} # Check the doco for available options
|
74
|
+
```
|
75
|
+
|
76
|
+
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. In the example below we have not referred to the same group twice but we have referred to `centos6a` and `centos7b` in all of out 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.
|
77
|
+
|
78
|
+
A full example:
|
63
79
|
|
64
80
|
```yaml
|
65
81
|
classes:
|
@@ -75,22 +91,60 @@ nodes:
|
|
75
91
|
- server2008r2a
|
76
92
|
- ubuntu1404a
|
77
93
|
|
78
|
-
|
94
|
+
node_groups:
|
95
|
+
centos_severs:
|
96
|
+
- centos6a
|
97
|
+
- centos7b
|
98
|
+
non_windows_servers:
|
99
|
+
include: 'all_nodes'
|
100
|
+
exclude: 'server2008r2a'
|
101
|
+
|
102
|
+
class_groups:
|
79
103
|
windows_roles:
|
80
104
|
- 'roles::windows_server'
|
81
105
|
- 'roles::backend_dbserver'
|
82
|
-
|
83
|
-
|
84
|
-
|
106
|
+
non_windows_roles:
|
107
|
+
include: 'all_classes'
|
108
|
+
exclude: 'windows_roles'
|
85
109
|
|
86
110
|
test_matrix:
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
111
|
+
- all_nodes:
|
112
|
+
classes: 'all_classes'
|
113
|
+
tests: 'spec'
|
114
|
+
- non_windows_servers:
|
115
|
+
classes: 'all_classes'
|
116
|
+
tests: 'all_tests'
|
117
|
+
- centos_severs:
|
118
|
+
classes: 'roles::frontend_webserver'
|
119
|
+
tests: 'acceptance'
|
120
|
+
options:
|
121
|
+
runs_before_idempotency: 2
|
122
|
+
```
|
123
|
+
|
124
|
+
**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.
|
125
|
+
|
126
|
+
```yaml
|
127
|
+
node_groups:
|
128
|
+
windows_nodes: # This has to be defined first
|
129
|
+
- sevrer2008r2
|
130
|
+
- server2012r2
|
131
|
+
non_windows:
|
132
|
+
include: 'all_nodes' # Start with all nodes
|
133
|
+
exclude: 'windows_nodes' # Then remove the windows ones from that list
|
92
134
|
```
|
93
135
|
|
136
|
+
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?)
|
137
|
+
|
138
|
+
#### Test Options
|
139
|
+
|
140
|
+
**check_idempotency** *Default: true*
|
141
|
+
|
142
|
+
Weather or not to check that puppet will be idempotent
|
143
|
+
|
144
|
+
**runs_before_idempotency** *Default: 1*
|
145
|
+
|
146
|
+
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.
|
147
|
+
|
94
148
|
### factsets
|
95
149
|
|
96
150
|
`spec/factsets/*.yaml`
|
@@ -103,7 +157,7 @@ Which will give raw json output of every fact which puppet knows about. Usually
|
|
103
157
|
|
104
158
|
`puppet facts > fact_set_name.json`
|
105
159
|
|
106
|
-
Once we have our factset all we need to do is copy it into `spec/factsets/` inside out controlrepo and commit it to version control. Factsets are named based on their filename, not the
|
160
|
+
Once we have our factset all we need to do is copy it into `spec/factsets/` inside out 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:
|
107
161
|
|
108
162
|
`spec/factsets/server2008r2.yaml`
|
109
163
|
|
@@ -249,11 +303,11 @@ service { 'pe-puppetserver':
|
|
249
303
|
}
|
250
304
|
```
|
251
305
|
|
252
|
-
However this is going to
|
306
|
+
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 try 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.
|
253
307
|
|
254
308
|
```puppet
|
255
309
|
# We are not going to actually have this service anywhere on our servers but
|
256
|
-
# our code needs to refresh it. This is to
|
310
|
+
# our code needs to refresh it. This is to trick puppet into doing nothing
|
257
311
|
service { 'pe-puppetserver':
|
258
312
|
ensure => 'running',
|
259
313
|
enable => false,
|
data/controlrepo.gemspec
CHANGED
@@ -3,7 +3,7 @@ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "controlrepo"
|
6
|
-
s.version = "2.0.
|
6
|
+
s.version = "2.0.1"
|
7
7
|
s.authors = ["Dylan Ratcliffe"]
|
8
8
|
s.email = ["dylan.ratcliffe@puppetlabs.com"]
|
9
9
|
s.homepage = ""
|
@@ -24,4 +24,4 @@ Gem::Specification.new do |s|
|
|
24
24
|
s.add_runtime_dependency 'bundler'
|
25
25
|
s.add_runtime_dependency 'r10k'
|
26
26
|
s.add_runtime_dependency 'puppet'
|
27
|
-
end
|
27
|
+
end
|
data/lib/controlrepo.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'pry'
|
2
1
|
require 'r10k/puppetfile'
|
3
2
|
require 'erb'
|
4
3
|
require 'json'
|
@@ -6,6 +5,12 @@ require 'yaml'
|
|
6
5
|
require 'find'
|
7
6
|
require 'pathname'
|
8
7
|
require 'controlrepo/beaker'
|
8
|
+
begin
|
9
|
+
require 'pry'
|
10
|
+
rescue LoadError
|
11
|
+
# We don't care if i'ts not here, this is just used for
|
12
|
+
# debugging sometimes
|
13
|
+
end
|
9
14
|
|
10
15
|
class Controlrepo
|
11
16
|
attr_accessor :root
|
data/lib/controlrepo/test.rb
CHANGED
@@ -5,11 +5,32 @@ class Controlrepo
|
|
5
5
|
attr_accessor :nodes
|
6
6
|
attr_accessor :classes
|
7
7
|
attr_accessor :options
|
8
|
+
attr_reader :default_options
|
8
9
|
|
9
10
|
# This can accept a bunch of stuff. It can accept nodes, classes or groups anywhere
|
10
11
|
# it will then detect them and expand them out into their respective objects so that
|
11
12
|
# we just end up with a list of nodes and classes
|
12
13
|
def initialize(on_these,test_this,options = {})
|
14
|
+
# Turn options into an empty hash is someone passed in a nil value
|
15
|
+
options ||= {}
|
16
|
+
|
17
|
+
# I copied this code off the internet, basically it allows us
|
18
|
+
# to refer to each key as either a string or an object
|
19
|
+
options.default_proc = proc do |h, k|
|
20
|
+
case k
|
21
|
+
when String then sym = k.to_sym; h[sym] if h.key?(sym)
|
22
|
+
when Symbol then str = k.to_s; h[str] if h.key?(str)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
@default_options = {
|
27
|
+
'check_idempotency' => true,
|
28
|
+
'runs_before_idempotency' => 1
|
29
|
+
}
|
30
|
+
|
31
|
+
# Add defaults if they do not exist
|
32
|
+
options = @default_options.merge(options)
|
33
|
+
|
13
34
|
@nodes = []
|
14
35
|
@classes = []
|
15
36
|
@options = options
|
@@ -92,13 +113,28 @@ class Controlrepo
|
|
92
113
|
# This should take an array of tests and remove any duplicates from them
|
93
114
|
|
94
115
|
# this will be an array of arrays, or maybe hashes
|
116
|
+
# TODO: Rewrite this so that it merges options hashes, or takes one, decide on the right behaviour
|
95
117
|
combinations = []
|
96
118
|
new_tests = []
|
97
119
|
tests.each do |test|
|
98
120
|
test.nodes.each do |node|
|
99
121
|
test.classes.each do |cls|
|
100
122
|
combo = {node => cls}
|
101
|
-
|
123
|
+
if combinations.member?(combo)
|
124
|
+
|
125
|
+
# Find the right test object:
|
126
|
+
relevant_test = new_tests[new_tests.index do |a|
|
127
|
+
a.nodes[0] == node and a.classes[0] == cls
|
128
|
+
end]
|
129
|
+
|
130
|
+
# Delete all default values in the current options hash
|
131
|
+
test.options.delete_if do |key,value|
|
132
|
+
test.default_options[key] == value
|
133
|
+
end
|
134
|
+
|
135
|
+
# Merge the non-default options right on in there
|
136
|
+
relevant_test.options.merge!(test.options)
|
137
|
+
else
|
102
138
|
combinations << combo
|
103
139
|
new_tests << Controlrepo::Test.new(node,cls,test.options)
|
104
140
|
end
|
@@ -15,14 +15,16 @@ class Controlrepo
|
|
15
15
|
attr_accessor :acceptance_tests
|
16
16
|
attr_accessor :environment
|
17
17
|
|
18
|
-
def initialize(file, environment = '
|
18
|
+
def initialize(file, environment = ENV['CONTROLREPO_env'])
|
19
19
|
begin
|
20
20
|
config = YAML.load(File.read(file))
|
21
21
|
rescue YAML::ParserError
|
22
22
|
raise "Could not parse the YAML file, check that it is valid YAML and that the encoding is correct"
|
23
23
|
end
|
24
24
|
|
25
|
+
|
25
26
|
@environment = environment
|
27
|
+
@environment ||= 'production'
|
26
28
|
@classes = []
|
27
29
|
@nodes = []
|
28
30
|
@node_groups = []
|
@@ -39,19 +41,18 @@ class Controlrepo
|
|
39
41
|
config['node_groups'].each { |name, members| @node_groups << Controlrepo::Group.new(name, members) }
|
40
42
|
config['class_groups'].each { |name, members| @class_groups << Controlrepo::Group.new(name, members) }
|
41
43
|
|
42
|
-
config['test_matrix'].each do |
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
44
|
+
config['test_matrix'].each do |test_hash|
|
45
|
+
test_hash.each do |machines, settings|
|
46
|
+
if settings['tests'] == 'spec'
|
47
|
+
@spec_tests << Controlrepo::Test.new(machines,settings['classes'],settings['options'])
|
48
|
+
elsif settings['tests'] == 'acceptance'
|
49
|
+
@acceptance_tests << Controlrepo::Test.new(machines,settings['classes'],settings['options'])
|
50
|
+
elsif settings['tests'] == 'all_tests'
|
51
|
+
test = Controlrepo::Test.new(machines,settings['classes'],settings['options'])
|
52
|
+
@spec_tests << test
|
53
|
+
@acceptance_tests << test
|
54
|
+
end
|
51
55
|
end
|
52
|
-
# TODO: Work out some way to set per-test options like idempotency
|
53
|
-
#@spec_tests << Controlrepo::Test.new(machines,roles)
|
54
|
-
#@acceptance_tests
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
@@ -2,20 +2,24 @@ require 'spec_helper_acceptance'
|
|
2
2
|
|
3
3
|
describe "Acceptance Testing" do
|
4
4
|
before :each do |test|
|
5
|
+
# Steop out a bunch of time to make sure we are at 0
|
6
|
+
# Yes this stupid, i know
|
7
|
+
10.times { logger.step_out }
|
5
8
|
#try to find the indentation level and mirror it in beaker logger
|
6
|
-
padding = ""
|
7
9
|
# Get the depth and add that as padding
|
8
10
|
(test.metadata[:scoped_id].split(':').count - 1).times do
|
9
|
-
|
11
|
+
logger.step_in
|
10
12
|
end
|
11
|
-
logger.line_prefix = padding
|
12
13
|
end
|
13
14
|
<% tests.each do |test| -%>
|
14
15
|
<% test.nodes.each do |node| -%>
|
15
16
|
<% test.classes.each do |cls| -%>
|
16
17
|
describe "<%= cls.name %> on <%= node.name %>" do
|
17
18
|
after :all do
|
18
|
-
|
19
|
+
# We have to use step_in and step_out here
|
20
|
+
# otherwise everything will break, not my fault
|
21
|
+
10.times { logger.step_out }
|
22
|
+
2.times { logger.step_in }
|
19
23
|
$nwm.cleanup
|
20
24
|
end
|
21
25
|
|
@@ -71,7 +75,7 @@ describe "Acceptance Testing" do
|
|
71
75
|
}.not_to raise_exception
|
72
76
|
end
|
73
77
|
end
|
74
|
-
|
78
|
+
<% test.options['runs_before_idempotency'].times do %>
|
75
79
|
describe "running puppet" do
|
76
80
|
it "should run with no errors" do
|
77
81
|
expect {
|
@@ -86,7 +90,8 @@ CODE
|
|
86
90
|
}.not_to raise_exception
|
87
91
|
end
|
88
92
|
end
|
89
|
-
|
93
|
+
<% end -%>
|
94
|
+
<% if test.options['check_idempotency'] then %>
|
90
95
|
describe "checking for idempotency" do
|
91
96
|
it "should run with no changes" do
|
92
97
|
expect {
|
@@ -101,6 +106,7 @@ CODE
|
|
101
106
|
}.not_to raise_exception
|
102
107
|
end
|
103
108
|
end
|
109
|
+
<% end -%>
|
104
110
|
end
|
105
111
|
<% end -%>
|
106
112
|
<% end -%>
|
@@ -62,18 +62,6 @@ RSpec.configure do |c|
|
|
62
62
|
#c.provision
|
63
63
|
#c.validate
|
64
64
|
#c.configure
|
65
|
-
|
66
|
-
# Destroy nodes if no preserve hosts
|
67
|
-
c.after :suite do
|
68
|
-
case options[:destroy]
|
69
|
-
when 'no'
|
70
|
-
# Don't cleanup
|
71
|
-
when 'onpass'
|
72
|
-
c.cleanup if RSpec.world.filtered_examples.values.flatten.none?(&:exception)
|
73
|
-
else
|
74
|
-
c.cleanup
|
75
|
-
end
|
76
|
-
end
|
77
65
|
end
|
78
66
|
|
79
67
|
# Set the number of lines it will print
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: controlrepo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dylan Ratcliffe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-11-
|
11
|
+
date: 2015-11-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|