puppet-retrospec 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +5 -6
- data/LICENSE +661 -20
- data/README.md +93 -45
- data/VERSION +1 -1
- data/bin/retrospec +6 -2
- data/lib/{puppet-retrospec.rb → retrospec.rb} +13 -50
- data/lib/retrospec/conditional.rb +53 -0
- data/lib/{helpers.rb → retrospec/helpers.rb} +4 -0
- data/lib/retrospec/resource.rb +37 -0
- data/lib/{templates → retrospec/templates}/acceptance_spec_test.erb +0 -0
- data/lib/{templates → retrospec/templates}/fixtures_file.erb +1 -1
- data/lib/{templates → retrospec/templates}/gemfile.erb +1 -1
- data/lib/{templates → retrospec/templates}/nodesets/centos-59-x64.yml +0 -0
- data/lib/{templates → retrospec/templates}/nodesets/centos-64-x64-pe.yml +0 -0
- data/lib/{templates → retrospec/templates}/nodesets/centos-64-x64.yml +0 -0
- data/lib/{templates → retrospec/templates}/nodesets/centos-65-x64.yml +0 -0
- data/lib/{templates → retrospec/templates}/nodesets/debian-607-x64.yml +0 -0
- data/lib/{templates → retrospec/templates}/nodesets/debian-70rc1-x64.yml +0 -0
- data/lib/{templates → retrospec/templates}/nodesets/debian-73-i386.yml +0 -0
- data/lib/{templates → retrospec/templates}/nodesets/debian-73-x64.yml +0 -0
- data/lib/{templates → retrospec/templates}/nodesets/default.yml +0 -0
- data/lib/{templates → retrospec/templates}/nodesets/fedora-18-x64.yml +0 -0
- data/lib/{templates → retrospec/templates}/nodesets/sles-11sp1-x64.yml +0 -0
- data/lib/{templates → retrospec/templates}/nodesets/ubuntu-server-10044-x64.yml +0 -0
- data/lib/{templates → retrospec/templates}/nodesets/ubuntu-server-12042-x64.yml +0 -0
- data/lib/{templates → retrospec/templates}/nodesets/ubuntu-server-1310-x64.yml +0 -0
- data/lib/{templates → retrospec/templates}/nodesets/ubuntu-server-1404-x64.yml +0 -0
- data/lib/{templates → retrospec/templates}/rakefile.erb +0 -0
- data/lib/retrospec/templates/resource_spec_file.erb +45 -0
- data/lib/{templates → retrospec/templates}/shared_context.erb +3 -1
- data/lib/{templates → retrospec/templates}/spec_helper_acceptance.rb.erb +0 -0
- data/lib/{templates → retrospec/templates}/spec_helper_file.erb +0 -0
- data/lib/retrospec/type_code.rb +18 -0
- data/lib/retrospec/variable_store.rb +82 -0
- data/lib/retrospec/version.rb +3 -0
- data/puppet-retrospec.gemspec +54 -33
- data/spec/fixtures/fixture_modules/one_resource_module/manifests/another_resource_class.pp +28 -0
- data/spec/fixtures/fixture_modules/one_resource_module/manifests/one_resource_class.pp +6 -0
- data/spec/fixtures/fixture_modules/zero_resource_module/manifests/empty_class.pp +3 -0
- data/spec/spec_helper.rb +2 -1
- data/spec/unit/conditional_spec.rb +43 -0
- data/spec/unit/puppet-retrospec_spec.rb +4 -41
- data/spec/unit/resource_spec.rb +62 -0
- data/spec/unit/type_code_spec.rb +39 -0
- data/spec/unit/variable_store_spec.rb +82 -0
- metadata +107 -53
- data/lib/templates/resource_spec_file.erb +0 -40
data/README.md
CHANGED
@@ -24,11 +24,11 @@ Run from the command line
|
|
24
24
|
```
|
25
25
|
$ retrospec -h
|
26
26
|
Options:
|
27
|
-
--module-path, -m <s>:
|
28
|
-
--template-dir, -t <s>:
|
29
|
-
--enable-user-templates, -e:
|
30
|
-
--enable-beaker-tests, -n:
|
31
|
-
--help, -h:
|
27
|
+
--module-path, -m <s>: The path (relative or absolute) to the module directory
|
28
|
+
--template-dir, -t <s>: Path to templates directory (only for overriding Retrospec templates)
|
29
|
+
--enable-user-templates, -e: Use Retrospec templates from ~/.puppet_retrospec_templates
|
30
|
+
--enable-beaker-tests, -n: Enable the creation of beaker tests
|
31
|
+
--help, -h: Show this message
|
32
32
|
|
33
33
|
retrospec -m ~/projects/puppet_modules/apache
|
34
34
|
```
|
@@ -71,53 +71,88 @@ $ retrospec
|
|
71
71
|
|
72
72
|
```
|
73
73
|
|
74
|
-
Looking at the file we can see that it did a lot of work for us.
|
75
|
-
|
74
|
+
Looking at the file we can see that it did a lot of work for us. Retrospec generate three tests automatically.
|
75
|
+
However the variable resolution isn't perfect so you will need to manually resolve all variables. This doesn't produce
|
76
|
+
100% coverage but all you did was press enter to produce all this anyways.
|
77
|
+
Below is the defines/instance_spec.rb file
|
76
78
|
|
77
79
|
```ruby
|
78
80
|
require 'spec_helper'
|
79
81
|
require 'shared_contexts'
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
82
|
+
|
83
|
+
describe 'tomcat' do
|
84
|
+
# by default the hiera integration uses hirea data from the shared_contexts.rb file
|
85
|
+
# but basically to mock hiera you first need to add a key/value pair
|
86
|
+
# to the specific context in the spec/shared_contexts.rb file
|
87
|
+
# Note: you can only use a single hiera context per describe/context block
|
88
|
+
# rspec-puppet does not allow you to swap out hiera data on a per test block
|
89
|
+
#include_context :hiera
|
90
|
+
|
91
|
+
|
92
|
+
# below is the facts hash that gives you the ability to mock
|
93
|
+
# facts on a per describe/context block. If you use a fact in your
|
94
|
+
# manifest you should mock the facts below.
|
95
|
+
let(:facts) do
|
96
|
+
{}
|
97
|
+
end
|
98
|
+
# below is a list of the resource parameters that you can override.
|
99
|
+
# By default all non-required parameters are commented out,
|
100
|
+
# while all required parameters will require you to add a value
|
101
|
+
let(:params) do
|
102
|
+
{
|
103
|
+
#:catalina_home => "$::tomcat::params::catalina_home",
|
104
|
+
#:user => $::tomcat::params::user,
|
105
|
+
#:group => $::tomcat::params::group,
|
106
|
+
#:install_from_source => true,
|
107
|
+
#:purge_connectors => false,
|
108
|
+
#:manage_user => true,
|
109
|
+
#:manage_group => true,
|
110
|
+
}
|
111
|
+
end
|
112
|
+
# add these two lines in a single test block to enable puppet and hiera debug mode
|
113
|
+
# Puppet::Util::Log.level = :debug
|
114
|
+
# Puppet::Util::Log.newdestination(:console)
|
115
|
+
it do
|
116
|
+
should contain_file('$::tomcat::params::catalina_home').
|
117
|
+
with({"ensure"=>"directory",
|
118
|
+
"owner"=>"$::tomcat::params::user",
|
119
|
+
"group"=>"$::tomcat::params::group"})
|
120
|
+
end
|
121
|
+
it do
|
122
|
+
should contain_user('$::tomcat::params::user').
|
123
|
+
with({"ensure"=>"present",
|
124
|
+
"gid"=>"$::tomcat::params::group"})
|
125
|
+
end
|
126
|
+
it do
|
127
|
+
should contain_group('$::tomcat::params::group').
|
128
|
+
with({"ensure"=>"present"})
|
129
|
+
end
|
130
|
+
end
|
112
131
|
|
113
132
|
```
|
114
133
|
|
115
134
|
About the test suite
|
116
135
|
======================
|
117
|
-
At this time the test suite that is automatically generated is
|
118
|
-
code
|
136
|
+
At this time the test suite that is automatically generated is very basic. Essentially it just creates a test for every
|
137
|
+
resource not in a code block with the exception of conditional code blocks. While this might be all you need, the more
|
138
|
+
complex your code is the less retrospec will generate until further improvements to the generator are made.
|
139
|
+
However, one of the major stumbling blocks is just constructing everything in the spec
|
119
140
|
directory which retrospec does for you automatically. Its now up to you to further enhance your test suite with more
|
120
|
-
tests and conditional logic
|
141
|
+
tests and conditional logic using describe blocks and such. You will notice that some variables are not resolved.
|
142
|
+
Currently this is a limitation that I hope to overcome, but until now its up to you to manually resolve those variables
|
143
|
+
prefixed with a '$'.
|
144
|
+
|
145
|
+
Example:
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
should contain_file('$::tomcat::params::catalina_home').
|
149
|
+
with({"ensure"=>"directory",
|
150
|
+
"owner"=>"$::tomcat::params::user",
|
151
|
+
"group"=>"$::tomcat::params::group"})
|
152
|
+
|
153
|
+
```
|
154
|
+
|
155
|
+
For now you will probably want to read up on the following documentation:
|
121
156
|
|
122
157
|
* [Puppet Rspec](http://rspec-puppet.com)
|
123
158
|
* [Puppet spec helper](https://github.com/puppetlabs/puppetlabs_spec_helper/blob/master/README.markdown)
|
@@ -126,7 +161,7 @@ tests and conditional logic. For now you will probably want to read up on the f
|
|
126
161
|
How Does it do this
|
127
162
|
=======================
|
128
163
|
Basically Retrospec uses the puppet lexer and parser to scan your code in order to fill out some basic templates that will retrofit
|
129
|
-
your puppet module with unit tests.
|
164
|
+
your puppet module with unit tests. Currently I rely on the old AST parser to generate all this
|
130
165
|
|
131
166
|
Overriding the templates
|
132
167
|
=======================
|
@@ -141,7 +176,7 @@ To override these templates just set **one** of the following cli options.
|
|
141
176
|
|
142
177
|
Once one of the options is set, retrospec will copy over all the templates from the gem location to the default
|
143
178
|
or specified override templates path.
|
144
|
-
If you have already created the
|
179
|
+
If you have already created the erb file in the templates location, then puppet-retrospec will not overwrite the file.
|
145
180
|
You can set multiple template paths if you use them for different projects so just be sure the set the correct
|
146
181
|
template option when running retrospec.
|
147
182
|
|
@@ -199,11 +234,24 @@ Otherwise to save time we skip the removal of test puppet modules therefore we d
|
|
199
234
|
bundle exec rake spec
|
200
235
|
```
|
201
236
|
|
237
|
+
Understanding Variable Resolution
|
238
|
+
=============
|
239
|
+
I do my best to try and resolve all the variables. Because the code does not rely on catalog compilation we have to
|
240
|
+
build our own scope through non trival methods. Some variables will get resolved while others will not. As this code
|
241
|
+
progresses we might find a better way at resolving variables. For now, some variable will require manual interpolation.
|
242
|
+
|
243
|
+
Resolution workflow.
|
244
|
+
|
245
|
+
1. load code in parser and find all parameters. Store these parameter values.
|
246
|
+
2. Find all vardef objects, resolve them if possible and store the values
|
247
|
+
3. Anything contained in a block of code is currently ignored, until later refinement.
|
248
|
+
|
202
249
|
Todo
|
203
250
|
============
|
204
251
|
- Add support to fill out used facts in the unit tests automatically
|
205
252
|
- Add describe blocks around conditions in test code that change the catalog compilation
|
206
|
-
- Auto add
|
253
|
+
- Auto add dependencies to fixtures file
|
254
|
+
- Show a diff of the test file when retrospec is run multiple times and the test file is already created.
|
207
255
|
|
208
256
|
Support
|
209
257
|
============
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.0
|
data/bin/retrospec
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'trollop'
|
3
|
-
require '
|
3
|
+
require 'retrospec'
|
4
|
+
require 'retrospec/version'
|
4
5
|
|
5
6
|
opts = Trollop::options do
|
6
7
|
opt :module_path, "The path (relative or absolute) to the module directory (Defaults to current directory) " ,
|
@@ -10,7 +11,10 @@ opts = Trollop::options do
|
|
10
11
|
opt :enable_user_templates, "Use Retrospec templates from #{File.expand_path('~/.puppet_retrospec_templates')}",
|
11
12
|
:require => false, :type => :boolean
|
12
13
|
opt :enable_beaker_tests, "Enable the creation of beaker tests", :require => false, :type => :boolean
|
14
|
+
opt :version, "The Version of the Gem", :required => false, :type => :boolean
|
15
|
+
end
|
16
|
+
if opts[:version]
|
17
|
+
puts "Puppet Retrospec Version: #{Puppet_Retrospec::VERSION}"
|
13
18
|
end
|
14
|
-
|
15
19
|
retro = Retrospec.new(opts[:module_path], opts)
|
16
20
|
retro.create_files
|
@@ -1,14 +1,14 @@
|
|
1
1
|
require 'erb'
|
2
2
|
require 'puppet'
|
3
|
-
require 'helpers'
|
3
|
+
require 'retrospec/helpers'
|
4
4
|
require 'fileutils'
|
5
|
+
require 'retrospec/resource'
|
6
|
+
require 'retrospec/conditional'
|
7
|
+
require 'retrospec/variable_store'
|
5
8
|
|
6
9
|
class Retrospec
|
7
|
-
attr_reader :included_declarations
|
8
10
|
attr_reader :module_path
|
9
11
|
attr_reader :tmp_module_path
|
10
|
-
attr_accessor :default_modules
|
11
|
-
attr_accessor :facts_used
|
12
12
|
attr_accessor :module_name
|
13
13
|
attr_reader :template_dir
|
14
14
|
|
@@ -27,17 +27,13 @@ class Retrospec
|
|
27
27
|
end
|
28
28
|
@enable_beaker_tests = opts[:enable_beaker_tests]
|
29
29
|
@module_path = validate_module_dir(module_path)
|
30
|
-
tmp_module_path
|
30
|
+
tmp_module_path # this is required to finish initialization
|
31
31
|
end
|
32
32
|
|
33
33
|
def enable_beaker_tests?
|
34
34
|
@enable_beaker_tests == true
|
35
35
|
end
|
36
36
|
|
37
|
-
def default_modules
|
38
|
-
@default_modules ||= ['stdlib']
|
39
|
-
end
|
40
|
-
|
41
37
|
def create_files
|
42
38
|
safe_create_spec_helper
|
43
39
|
safe_create_fixtures_file
|
@@ -126,10 +122,11 @@ class Retrospec
|
|
126
122
|
|
127
123
|
# returns the name of the module ie. mysql::config => mysql
|
128
124
|
def module_name
|
129
|
-
|
130
|
-
@module_name
|
125
|
+
begin
|
126
|
+
@module_name ||= types.first.name.split('::').first
|
127
|
+
rescue
|
128
|
+
@module_name = module_dir_name
|
131
129
|
end
|
132
|
-
@module_name
|
133
130
|
end
|
134
131
|
|
135
132
|
# creates a tmp module directory so puppet can work correctly
|
@@ -143,48 +140,14 @@ class Retrospec
|
|
143
140
|
@modules_dir
|
144
141
|
end
|
145
142
|
|
146
|
-
def modules_included
|
147
|
-
@modules_included ||= default_modules + referenced_modules
|
148
|
-
end
|
149
|
-
|
150
|
-
def referenced_modules
|
151
|
-
[]
|
152
|
-
end
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
# finds all the included resources so we can test and depend on in the fixtures file
|
157
|
-
# def included_declarations(file)
|
158
|
-
# @included_declarations = {}
|
159
|
-
# includes = []
|
160
|
-
# p = Puppet::Parser::Lexer.new
|
161
|
-
# p.string = File.read(file)
|
162
|
-
# tokens = p.fullscan
|
163
|
-
# k = 0
|
164
|
-
# typename = nil
|
165
|
-
# tokens.each do | token|
|
166
|
-
# next if not token.last.is_a?(Hash)
|
167
|
-
# if typename.nil? and [:CLASS, :DEFINE].include? token.first
|
168
|
-
# j = tokens.index { |token| [:NAME].include? token.first }
|
169
|
-
# typename = tokens[j].last[:value]
|
170
|
-
# end
|
171
|
-
# if token.last.fetch(:value, nil) == 'include'
|
172
|
-
# key = token.last[:value]
|
173
|
-
# value = tokens[k + 1].last[:value]
|
174
|
-
# includes << value
|
175
|
-
# end
|
176
|
-
# k = k + 1
|
177
|
-
# end
|
178
|
-
# @included_declarations[typename] = includes
|
179
|
-
# @included_declarations
|
180
|
-
# end
|
181
|
-
|
182
|
-
|
183
|
-
|
184
143
|
# Creates an associated spec file for each type and even creates the subfolders for nested classes one::two::three
|
185
144
|
def safe_create_resource_spec_files(type,template='resource_spec_file.erb')
|
186
145
|
@parameters = type.arguments
|
187
146
|
@type = type
|
147
|
+
@resources = Resource.all(type)
|
148
|
+
# pass the type to the variable store and it will discover all the variables and try to resolve them.
|
149
|
+
VariableStore.populate(type)
|
150
|
+
@resources += Conditional.all(type)
|
188
151
|
file_path = generate_file_path(type, false)
|
189
152
|
safe_create_template_file(file_path, template)
|
190
153
|
file_path
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'retrospec/resource'
|
2
|
+
|
3
|
+
class Conditional
|
4
|
+
attr_reader :test, :value, :statements
|
5
|
+
|
6
|
+
# things I need: a key/value store for variables
|
7
|
+
# types of variables
|
8
|
+
# those that can be changed
|
9
|
+
# those that can be influenced (facts, other variables that contain variables)
|
10
|
+
# takes a subtype of Puppet::AST::Branch that contains conditional logic
|
11
|
+
def initialize(branch, parameters)
|
12
|
+
@statements = branch.statements
|
13
|
+
end
|
14
|
+
|
15
|
+
# get the attributes for the given resources found in the type code passed in
|
16
|
+
# this will return a array of hashes, one for each resource found
|
17
|
+
def self.all(type)
|
18
|
+
r_attrs = []
|
19
|
+
generate_conditionals(type).each do |c|
|
20
|
+
r_attrs << Resource.all(c.statements)
|
21
|
+
end
|
22
|
+
r_attrs.flatten
|
23
|
+
end
|
24
|
+
|
25
|
+
# a array of types the are known to contain conditional code and statements
|
26
|
+
def self.types
|
27
|
+
#test, statement, value
|
28
|
+
# if I don't have a statement that I am part of a bigger code block
|
29
|
+
# [Puppet::Parser::AST::IfStatement, Puppet::Parser::AST::CaseStatement, Puppet::Parser::AST::Else,
|
30
|
+
# Puppet::Parser::AST::CaseOpt, Puppet::Parser::AST::Selector]
|
31
|
+
[Puppet::Parser::AST::IfStatement, Puppet::Parser::AST::Else]
|
32
|
+
end
|
33
|
+
|
34
|
+
# returns a array of branch subtypes
|
35
|
+
def self.find_conditionals(type)
|
36
|
+
conds = []
|
37
|
+
if type.code.respond_to?(:find_all)
|
38
|
+
conds = type.code.find_all {|c| types.include?(c.class) }
|
39
|
+
end
|
40
|
+
conds
|
41
|
+
end
|
42
|
+
|
43
|
+
# find and create an array of conditionals
|
44
|
+
# we need the type so we can look through the code to find conditional statements
|
45
|
+
def self.generate_conditionals(type)
|
46
|
+
conditionals = []
|
47
|
+
find_conditionals(type).each do |cond|
|
48
|
+
conditionals << Conditional.new(cond, type.arguments)
|
49
|
+
end
|
50
|
+
conditionals
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'retrospec/variable_store'
|
2
|
+
|
3
|
+
class Resource
|
4
|
+
|
5
|
+
attr_reader :type, :title, :parameters, :scope_name
|
6
|
+
|
7
|
+
def initialize(type_name,instance)
|
8
|
+
@parameters = Hash[instance.parameters.map { |k| [k.param, VariableStore.resolve(k.value).gsub("\"", '')]}]
|
9
|
+
@type = type_name
|
10
|
+
@title = VariableStore.resolve(instance.title).gsub("\"", '')
|
11
|
+
end
|
12
|
+
|
13
|
+
# Gets all resources in the type that are not in a code block
|
14
|
+
def self.all(statements)
|
15
|
+
if statements.respond_to?(:code)
|
16
|
+
# store the class params
|
17
|
+
statements.arguments.each {|k,v| VariableStore.add(k,v)}
|
18
|
+
# if we accidently pass a type in without specifing the code
|
19
|
+
statements = statements.code unless statements.nil?
|
20
|
+
end
|
21
|
+
a = []
|
22
|
+
# sometimes the code is empty
|
23
|
+
if statements.respond_to?(:find_all)
|
24
|
+
res = statements.find_all { |s| s.instance_of?(Puppet::Parser::AST::Resource)}
|
25
|
+
res.each do |r|
|
26
|
+
r.instances.each do |i|
|
27
|
+
a << Resource.new(r.type, i)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
a
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.all_resources
|
35
|
+
ObjectSpace.each_object(Puppet::Parser::AST::Resource).map {|x| x}
|
36
|
+
end
|
37
|
+
end
|
File without changes
|
@@ -14,7 +14,7 @@ group :development do
|
|
14
14
|
gem "travis"
|
15
15
|
gem "travis-lint"
|
16
16
|
<%- if enable_beaker_tests? -%>
|
17
|
-
gem "beaker", :git => 'https://github.com/
|
17
|
+
gem "beaker", :git => 'https://github.com/puppetlabs/beaker.git'
|
18
18
|
gem "beaker-rspec", :git => 'https://github.com/puppetlabs/beaker-rspec.git'
|
19
19
|
gem "vagrant-wrapper"
|
20
20
|
<%- end -%>
|
File without changes
|
File without changes
|
File without changes
|