puppet-retrospec 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|