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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d27924d8a371e6fcc60c20a749013cb8e9eb1240
4
- data.tar.gz: 9425a314523f65fe3e5321958f5019b0e7452dab
3
+ metadata.gz: b60c1c6a0508a99a08fefac24672a0e78d117a78
4
+ data.tar.gz: a0ec19878c1fa9a05db40bbf9f8f0a214cae5e53
5
5
  SHA512:
6
- metadata.gz: c428c645689a91b7e8bc939e6d69d993ddee30880173249b518fb853f94fc24def1b1994596204aca98d95e7139b3118a9322cef1d6798f2d7bd86c897c81e2e
7
- data.tar.gz: 4ccca700d874ad4c42364c75270811d6b04b2050feb5096337a8b8444661acbf467be5ad929ea6fe52b2daea3b764a63b0140a06dab4137d5538f25d0e207f89
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
- **groups:** The groups section is just for saving us some typing. Here we can set up groups of classes *or* nodes (not a combination of the two) which we can then refer to in our test matrix. There are also two default groups automatically created which you can use, **all_classes** and **all_nodes**. If you can't guess what they are go have a coffee and come back.
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
- **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 a hash with the node/s as the key and class/es as the values. This is where we would use groups if we had them (you can see us using the `windows_roles` and `centos_servers` groups in the below example). We can also use groups to subtractively build up lists of classes by using `include` and `exclude` as per the example below.
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
- Don't be afraid if your test matrix produces duplicate tests, the controlrepo gem de-duplicates the test matrix before flattening it and generating the tests, as a result there should never be any duplicate tests. This also means that tests might not come out in the order that you expect D:
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
- groups:
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
- centos_servers:
83
- - centos6a
84
- - centos7b
106
+ non_windows_roles:
107
+ include: 'all_classes'
108
+ exclude: 'windows_roles'
85
109
 
86
110
  test_matrix:
87
- server2008r2a: windows_roles
88
- ubuntu1404a: 'roles::frontend_webserver'
89
- centos_servers:
90
- include: all_classes
91
- exclude: windows_roles
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 ane of the server they came from (Although you can, if you want). i.e the following factset file:
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 pos 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.
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 trck puppet into doing nothing
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,
@@ -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.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
@@ -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
@@ -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
- unless combinations.member?(combo)
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 = 'production')
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 |machines, settings|
43
- if settings['tests'] == 'spec'
44
- @spec_tests << Controlrepo::Test.new(machines,settings['classes'],settings['options'])
45
- elsif settings['tests'] == 'acceptance'
46
- @acceptance_tests << Controlrepo::Test.new(machines,settings['classes'],settings['options'])
47
- elsif settings['tests'] == 'all_tests'
48
- test = Controlrepo::Test.new(machines,settings['classes'],settings['options'])
49
- @spec_tests << test
50
- @acceptance_tests << test
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
- padding << " "
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
- logger.line_prefix = " "
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.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 00:00:00.000000000 Z
11
+ date: 2015-11-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake