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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -6
  3. data/LICENSE +661 -20
  4. data/README.md +93 -45
  5. data/VERSION +1 -1
  6. data/bin/retrospec +6 -2
  7. data/lib/{puppet-retrospec.rb → retrospec.rb} +13 -50
  8. data/lib/retrospec/conditional.rb +53 -0
  9. data/lib/{helpers.rb → retrospec/helpers.rb} +4 -0
  10. data/lib/retrospec/resource.rb +37 -0
  11. data/lib/{templates → retrospec/templates}/acceptance_spec_test.erb +0 -0
  12. data/lib/{templates → retrospec/templates}/fixtures_file.erb +1 -1
  13. data/lib/{templates → retrospec/templates}/gemfile.erb +1 -1
  14. data/lib/{templates → retrospec/templates}/nodesets/centos-59-x64.yml +0 -0
  15. data/lib/{templates → retrospec/templates}/nodesets/centos-64-x64-pe.yml +0 -0
  16. data/lib/{templates → retrospec/templates}/nodesets/centos-64-x64.yml +0 -0
  17. data/lib/{templates → retrospec/templates}/nodesets/centos-65-x64.yml +0 -0
  18. data/lib/{templates → retrospec/templates}/nodesets/debian-607-x64.yml +0 -0
  19. data/lib/{templates → retrospec/templates}/nodesets/debian-70rc1-x64.yml +0 -0
  20. data/lib/{templates → retrospec/templates}/nodesets/debian-73-i386.yml +0 -0
  21. data/lib/{templates → retrospec/templates}/nodesets/debian-73-x64.yml +0 -0
  22. data/lib/{templates → retrospec/templates}/nodesets/default.yml +0 -0
  23. data/lib/{templates → retrospec/templates}/nodesets/fedora-18-x64.yml +0 -0
  24. data/lib/{templates → retrospec/templates}/nodesets/sles-11sp1-x64.yml +0 -0
  25. data/lib/{templates → retrospec/templates}/nodesets/ubuntu-server-10044-x64.yml +0 -0
  26. data/lib/{templates → retrospec/templates}/nodesets/ubuntu-server-12042-x64.yml +0 -0
  27. data/lib/{templates → retrospec/templates}/nodesets/ubuntu-server-1310-x64.yml +0 -0
  28. data/lib/{templates → retrospec/templates}/nodesets/ubuntu-server-1404-x64.yml +0 -0
  29. data/lib/{templates → retrospec/templates}/rakefile.erb +0 -0
  30. data/lib/retrospec/templates/resource_spec_file.erb +45 -0
  31. data/lib/{templates → retrospec/templates}/shared_context.erb +3 -1
  32. data/lib/{templates → retrospec/templates}/spec_helper_acceptance.rb.erb +0 -0
  33. data/lib/{templates → retrospec/templates}/spec_helper_file.erb +0 -0
  34. data/lib/retrospec/type_code.rb +18 -0
  35. data/lib/retrospec/variable_store.rb +82 -0
  36. data/lib/retrospec/version.rb +3 -0
  37. data/puppet-retrospec.gemspec +54 -33
  38. data/spec/fixtures/fixture_modules/one_resource_module/manifests/another_resource_class.pp +28 -0
  39. data/spec/fixtures/fixture_modules/one_resource_module/manifests/one_resource_class.pp +6 -0
  40. data/spec/fixtures/fixture_modules/zero_resource_module/manifests/empty_class.pp +3 -0
  41. data/spec/spec_helper.rb +2 -1
  42. data/spec/unit/conditional_spec.rb +43 -0
  43. data/spec/unit/puppet-retrospec_spec.rb +4 -41
  44. data/spec/unit/resource_spec.rb +62 -0
  45. data/spec/unit/type_code_spec.rb +39 -0
  46. data/spec/unit/variable_store_spec.rb +82 -0
  47. metadata +107 -53
  48. 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>: The path (relative or absolute) to the module directory (Defaults to current 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
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
- Below is the classes/tomcat_spec.rb file
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
- describe 'tomcat' do
81
- # by default the hiera integration is commented out
82
- # but basically to mock hiera you first need to add a key/value pair
83
- # to the specific context in the spec/shared_contexts.rb file
84
- # Note: you can only use a single hiera context per describe/context block
85
- # rspec-puppet does not allow you to swap out hiera data on a per test block
86
- #include_context :hiera
87
- # below is the facts hash that gives you the ability to mock
88
- # facts on a per describe/context block. If you use a fact in your
89
- # manifest you should mock the facts below.
90
- let(:facts) do
91
- {}
92
- end
93
- # below is a list of the resource parameters that you can override
94
- # by default all non-required parameters are commented out
95
- # while all required parameters will require you to add a value
96
- let(:params) do
97
- {
98
- #:catalina_home => $::tomcat::params::catalina_home,
99
- #:user => $::tomcat::params::user,
100
- #:group => $::tomcat::params::group,
101
- #:install_from_source => true,
102
- #:purge_connectors => false,
103
- #:manage_user => true,
104
- #:manage_group => true,
105
- }
106
- end
107
- # add these two lines in a single test block to enable puppet and hiera debug mode
108
- # Puppet::Util::Log.level = :debug
109
- # Puppet::Util::Log.newdestination(:console)
110
- it { should compile }
111
- end
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 extremely basic. Essentially it just ensures that your
118
- code will compile correctly. However, one of the major stumbling blocks is just constructing everything in the spec
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. For now you will probably want to read up on the following documentation:
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 a erb file in the templates location, then puppet-retrospec will not overwrite the file.
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 dependicies to fixtures file
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.4.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 'puppet-retrospec'
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
- if @module_name.nil?
130
- @module_name = types.first.name.split('::').first if types.length > 0
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
@@ -80,6 +80,10 @@ class Helpers
80
80
  File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
81
81
  end
82
82
 
83
+
84
+
85
+
86
+
83
87
  def self.safe_copy_file(src, dest)
84
88
  if File.exists?(dest) and not File.zero?(dest)
85
89
  $stderr.puts "!! #{dest} already exists"
@@ -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
@@ -1,6 +1,6 @@
1
1
  fixtures:
2
2
  symlinks:
3
- <%= @module_name %>: "#{source_dir}"
3
+ <%= module_name %>: "#{source_dir}"
4
4
  repositories:
5
5
  stdlib:
6
6
  repo: https://github.com/puppetlabs/puppetlabs-stdlib.git
@@ -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/logicminds/beaker.git', :branch => 'sshd_restart'
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 -%>