puppet-retrospec 0.5.1 → 0.6.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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/VERSION +1 -1
  4. data/bin/retrospec +2 -5
  5. data/lib/retrospec.rb +41 -45
  6. data/lib/retrospec/exceptions.rb +4 -0
  7. data/lib/retrospec/puppet_module.rb +163 -0
  8. data/lib/retrospec/resource.rb +2 -3
  9. data/lib/retrospec/spec_object.rb +37 -0
  10. data/lib/retrospec/templates/gemfile.erb +12 -10
  11. data/lib/retrospec/templates/nodesets/centos-66-x64.yml +11 -0
  12. data/lib/retrospec/templates/rakefile.erb +3 -0
  13. data/lib/retrospec/templates/resource_spec_file.erb +3 -3
  14. data/lib/retrospec/templates/spec_helper_file.erb +1 -0
  15. data/lib/retrospec/templates/travis.yml.erb +14 -0
  16. data/lib/retrospec/type_code.rb +14 -2
  17. data/lib/retrospec/variable_store.rb +70 -9
  18. data/lib/retrospec/version.rb +1 -1
  19. data/puppet-retrospec.gemspec +14 -7
  20. data/spec/fixtures/fixture_modules/one_resource_module/manifests/{another_resource_class.pp → another_resource.pp} +4 -3
  21. data/spec/fixtures/fixture_modules/one_resource_module/manifests/inherits_params.pp +8 -0
  22. data/spec/fixtures/fixture_modules/one_resource_module/manifests/params.pp +19 -0
  23. data/spec/integration/retrospec_spec.rb +36 -0
  24. data/spec/unit/conditional_spec.rb +41 -23
  25. data/spec/unit/module_spec.rb +55 -0
  26. data/spec/unit/puppet-retrospec_spec.rb +15 -22
  27. data/spec/unit/resource_spec.rb +46 -38
  28. data/spec/unit/type_code_spec.rb +37 -19
  29. data/spec/unit/variable_store_spec.rb +72 -16
  30. metadata +40 -33
  31. data/lib/retrospec/module_utilities.rb +0 -80
  32. data/lib/retrospec/templates/nodesets/centos-65-x64.yml +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1b5058097bcc5d5c96332495e617db07cea74805
4
- data.tar.gz: 0a8f3072a91d5f72ff391c90d920336af8daa5d6
3
+ metadata.gz: 3132a94b4025368f76a74202f2ffacfe5906b941
4
+ data.tar.gz: d0adc560eb50a6a89dc3ad9810f53516cc6f2c07
5
5
  SHA512:
6
- metadata.gz: c44aa9a6c8db80d14842a06662722da2a0d35c5bc1da46b843e61a71dff7222e1d13e14af74393d008ace304c9811e8e16f4b61cb8929090cd89dc61ace9200a
7
- data.tar.gz: db3a54af5802009798cda488a5649e47ccf8678627e3d32f5fe064a2967108e6de90107ff61f98dfd26b7d0f04839da97f8b57d8d06a5b0f2e7a13015ce6d2e9
6
+ metadata.gz: e4fdd60ac3de7650f4ca6a0054fb292a9a55f25f68337d8b01b6f10e1db46915b8c6db0826f02d8ffb5447250cd4d223654a16a747653abc8c4f7303bf78fcc4
7
+ data.tar.gz: cce4b3c789ab9b41d16839d557ed2a83c6b2a25f062e3e4cd534ddf785e20591f157d93b59b22617b07bb556cfd553a1b85fe841a9154185483cd10f8c0f88c6
data/README.md CHANGED
@@ -72,7 +72,7 @@ $ retrospec
72
72
  ```
73
73
 
74
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
75
+ However the variable resolution isn't perfect so you will need to manually resolve some variables. This doesn't produce
76
76
  100% coverage but all you did was press enter to produce all this anyways.
77
77
  Below is the defines/instance_spec.rb file
78
78
 
@@ -161,7 +161,7 @@ For now you will probably want to read up on the following documentation:
161
161
  How Does it do this
162
162
  =======================
163
163
  Basically Retrospec uses the puppet lexer and parser to scan your code in order to fill out some basic templates that will retrofit
164
- your puppet module with unit tests. Currently I rely on the old AST parser to generate all this
164
+ your puppet module with unit tests. Currently I rely on the old AST parser to generate all this. This is why
165
165
 
166
166
  Overriding the templates
167
167
  =======================
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.1
1
+ 0.6.0
data/bin/retrospec CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'trollop'
3
3
  require 'retrospec'
4
- require 'retrospec/version'
5
4
 
6
5
  opts = Trollop::options do
7
6
  opt :module_path, "The path (relative or absolute) to the module directory (Defaults to current directory) " ,
@@ -11,10 +10,8 @@ opts = Trollop::options do
11
10
  opt :enable_user_templates, "Use Retrospec templates from #{File.expand_path('~/.puppet_retrospec_templates')}",
12
11
  :require => false, :type => :boolean
13
12
  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}"
18
13
  end
14
+ include Puppet_Retrospec
15
+
19
16
  retro = Retrospec.new(opts[:module_path], opts)
20
17
  retro.create_files
data/lib/retrospec.rb CHANGED
@@ -5,20 +5,23 @@ require 'fileutils'
5
5
  require 'retrospec/resource'
6
6
  require 'retrospec/conditional'
7
7
  require 'retrospec/variable_store'
8
- require 'retrospec/module_utilities'
8
+ require 'retrospec/puppet_module'
9
+ require 'retrospec/spec_object'
10
+ require 'retrospec/exceptions'
11
+ require 'retrospec/version'
9
12
 
10
13
  class Retrospec
11
- include PuppetModule::Utilities
12
14
 
13
- attr_reader :module_path
14
15
  attr_reader :template_dir
16
+ attr_reader :module_path
17
+ attr_reader :spec_object
15
18
 
16
19
  # module path is the relative or absolute path to the module that should retro fitted
17
20
  # opts hash contains additional flags and options that can be user to control the creation of the tests
18
21
  # opts[:enable_user_templates]
19
22
  # opts[:enable_beaker_tests]
20
23
  # opts[:template_dir]
21
- def initialize(module_path=nil,opts={})
24
+ def initialize(supplied_module_path=nil,opts={})
22
25
  # user supplied a template path or user wants to use local templates
23
26
  if opts[:template_dir] or opts[:enable_user_templates]
24
27
  @template_dir = Helpers.setup_user_template_dir(opts[:template_dir])
@@ -26,13 +29,13 @@ class Retrospec
26
29
  # if user doesn't supply template directory we assume we should use the templates in this gem
27
30
  @template_dir = Helpers.gem_template_dir
28
31
  end
29
- @enable_beaker_tests = opts[:enable_beaker_tests]
30
- @module_path = validate_module_dir(module_path)
31
- create_tmp_module_path(module_path) # this is required to finish initialization
32
- end
33
-
34
- def enable_beaker_tests?
35
- @enable_beaker_tests == true
32
+ begin
33
+ Utilities::PuppetModule.instance.module_path = supplied_module_path
34
+ Utilities::PuppetModule.create_tmp_module_path # this is required to finish initialization
35
+ @module_path = Utilities::PuppetModule.module_path
36
+ @spec_object = Utilities::SpecObject.new(Utilities::PuppetModule.instance)
37
+ spec_object.enable_beaker_tests = opts[:enable_beaker_tests]
38
+ end
36
39
  end
37
40
 
38
41
  def create_files
@@ -41,15 +44,19 @@ class Retrospec
41
44
  safe_create_gemfile
42
45
  safe_create_rakefile
43
46
  safe_make_shared_context
44
- safe_create_acceptance_spec_helper if enable_beaker_tests?
45
- safe_create_node_sets if enable_beaker_tests?
46
- types.each do |type|
47
- safe_create_resource_spec_files(type)
48
- if enable_beaker_tests?
49
- safe_create_acceptance_tests(type)
50
- end
47
+ safe_create_ci_file
48
+ safe_create_acceptance_spec_helper if spec_object.enable_beaker_tests?
49
+ safe_create_node_sets if spec_object.enable_beaker_tests?
50
+ # a Type is nothing more than a defined type or puppet class
51
+ # we could have named this manifest but there could be multiple types
52
+ # in a manifest.
53
+ spec_object.types.each do |type|
54
+ safe_create_resource_spec_files(type)
55
+ if spec_object.enable_beaker_tests?
56
+ safe_create_acceptance_tests(type)
57
+ end
51
58
  end
52
- FileUtils.remove_entry_secure tmp_modules_dir # ensure we remove the temporary directory
59
+ Utilities::PuppetModule.clean_tmp_modules_dir
53
60
  true
54
61
  end
55
62
 
@@ -61,6 +68,10 @@ class Retrospec
61
68
  end
62
69
  end
63
70
 
71
+ def safe_create_ci_file(template='travis.yml.erb')
72
+ safe_create_template_file('.travis.yml.erb', template)
73
+ end
74
+
64
75
  def safe_create_acceptance_spec_helper(template='spec_helper_acceptance.rb.erb')
65
76
  safe_create_template_file(File.join('spec', 'spec_helper_acceptance.rb'), template)
66
77
  end
@@ -94,7 +105,7 @@ class Retrospec
94
105
  template_path = File.join(template_dir, template)
95
106
  File.open(template_path) do |file|
96
107
  renderer = ERB.new(file.read, 0, '-')
97
- content = renderer.result binding
108
+ content = renderer.result spec_object.get_binding
98
109
  dest_path = File.expand_path(File.join(module_path,path))
99
110
  Helpers.safe_create_file(dest_path, content)
100
111
  end
@@ -102,12 +113,13 @@ class Retrospec
102
113
 
103
114
  # Creates an associated spec file for each type and even creates the subfolders for nested classes one::two::three
104
115
  def safe_create_resource_spec_files(type,template='resource_spec_file.erb')
105
- @parameters = type.arguments
106
- @type = type
107
- @resources = Resource.all(type)
108
- # pass the type to the variable store and it will discover all the variables and try to resolve them.
116
+ spec_object.parameters = type.arguments
117
+ spec_object.type = type
109
118
  VariableStore.populate(type)
110
- @resources += Conditional.all(type)
119
+ spec_object.resources = Resource.all(type)
120
+ # pass the type to the variable store and it will discover all the variables and try to resolve them.
121
+ # this does not get deep nested conditional blocks
122
+ spec_object.resources += Conditional.all(type)
111
123
  file_path = generate_file_path(type, false)
112
124
  safe_create_template_file(file_path, template)
113
125
  file_path
@@ -146,11 +158,13 @@ class Retrospec
146
158
  file_name = generate_file_name(type.name)
147
159
  tokens = type.name.split('::')
148
160
  # if there are only two tokens ie. tomcat::params we dont need to create a subdirectory
149
- if tokens.count > 1
161
+ if tokens.count > 2
150
162
  # this is a deep level resource ie. tomcat::config::server::connector
151
163
  # however we don't need the tomcat directory so we can just remove it
152
164
  # this should leave us with config/server/connector_spec.rb
153
165
  tokens.delete_at(0)
166
+ # remove the last token since its the class name
167
+ tokens.pop
154
168
  # so lets make a directory structure out of it
155
169
  dir_name = File.join(tokens) # config/server
156
170
  dir_name = File.join(type_dir_name,dir_name, file_name) # spec/classes/tomcat/config/server
@@ -169,23 +183,5 @@ class Retrospec
169
183
 
170
184
  private
171
185
 
172
- # processes a directory and expands to its full path, assumes './'
173
- # returns the validated dir
174
- def validate_module_dir(dir)
175
- # first check to see if manifests directory even exists when path is nil
176
- if dir.nil?
177
- dir = '.'
178
- elsif dir.instance_of?(Array)
179
- raise "Retrospec - an array of moudule paths is not supported at this time"
180
- end
181
- dir = File.expand_path(dir)
182
- manifest_dir = File.join(dir,'manifests')
183
- if ! File.exist?(manifest_dir)
184
- raise "No manifest directory in #{manifest_dir}, cannot validate this is a module"
185
- else
186
- files = Dir.glob("#{manifest_dir}/**/*.pp")
187
- warn 'No puppet manifest files found at #' if files.length < 1
188
- end
189
- dir
190
- end
186
+
191
187
  end
@@ -0,0 +1,4 @@
1
+ class ParentFoundException < StandardError
2
+ end
3
+ class TypeNotFoundException < StandardError
4
+ end
@@ -0,0 +1,163 @@
1
+ require 'singleton'
2
+
3
+ module Utilities
4
+ class PuppetModule
5
+ attr_writer :module_path
6
+
7
+ include Singleton
8
+
9
+ def self.tmp_module_path
10
+ Utilities::PuppetModule.instance.tmp_module_path
11
+ end
12
+
13
+ def self.tmp_modules_dir
14
+ Utilities::PuppetModule.instance.tmp_modules_dir
15
+ end
16
+
17
+ def self.module_path
18
+ Utilities::PuppetModule.instance.module_path
19
+ end
20
+
21
+ def self.module_name
22
+ Utilities::PuppetModule.instance.module_name
23
+ end
24
+
25
+ def self.module_dir_name
26
+ Utilities::PuppetModule.instance.module_dir_name
27
+ end
28
+
29
+ def self.module_types
30
+ Utilities::PuppetModule.instance.types
31
+ end
32
+
33
+ # create the temporary module create, validate the
34
+ def self.create_tmp_module_path
35
+ Utilities::PuppetModule::instance.create_tmp_module_path(module_path)
36
+ end
37
+
38
+ def self.clean_tmp_modules_dir
39
+ FileUtils.remove_entry_secure instance.tmp_modules_dir # ensure we remove the temporary directory
40
+ end
41
+
42
+ def module_path
43
+ @module_path
44
+ end
45
+
46
+ # validate and set the module path
47
+ def module_path=(path)
48
+ @module_path = validate_module_dir(path)
49
+ end
50
+
51
+ # gets an instance of the module class. The create_tmp_module_path must first be called
52
+ # before instance can do anything useful.
53
+ def self.instance
54
+ @@instance ||= new
55
+ end
56
+
57
+ # processes a directory and expands to its full path, assumes './'
58
+ # returns the validated dir
59
+ def validate_module_dir(dir)
60
+ # first check to see if manifests directory even exists when path is nil
61
+ if dir.nil?
62
+ dir = '.'
63
+ elsif dir.instance_of?(Array)
64
+ puts "Retrospec - an array of module paths is not supported at this time"
65
+ exit 1
66
+ end
67
+ dir = File.expand_path(dir)
68
+ manifest_dir = File.join(dir,'manifests')
69
+ if ! File.exist?(manifest_dir)
70
+ puts "No manifest directory in #{manifest_dir}, cannot validate this is a module"
71
+ exit 1
72
+ else
73
+ files = Dir.glob("#{manifest_dir}/**/*.pp")
74
+ warn "No puppet manifest files found at #{manifest_dir}" if files.length < 1
75
+ end
76
+ dir
77
+ end
78
+
79
+ # puts a symlink in that module directory that points back to the user supplied module path
80
+ def create_tmp_module_path(module_path)
81
+ raise "ModulePathNotFound" unless module_path
82
+ path = File.join(tmp_modules_dir, module_dir_name)
83
+ unless File.exists?(path) # only create if it doesn't already exist
84
+ # create a link where source is the current repo and dest is /tmp/modules/module_name
85
+ FileUtils.ln_s(module_path, path)
86
+ end
87
+ path
88
+ end
89
+
90
+ def tmp_module_path
91
+ @tmp_module_path ||= File.join(tmp_modules_dir, module_dir_name)
92
+ end
93
+
94
+ # the directory name of the module
95
+ # usually this is the same as the module name but it can be namespaced sometimes
96
+ def module_dir_name
97
+ raise "ModulePathNotFound" unless module_path
98
+ @module_dir_name ||= File.basename(module_path)
99
+ end
100
+
101
+ def module_type_names
102
+ types.map {|x| x.name}
103
+ end
104
+
105
+ # returns the name of the module ie. mysql::config => mysql
106
+ def module_name
107
+ begin
108
+ @module_name ||= types.first.name.split('::').first
109
+ rescue
110
+ @module_name = module_dir_name
111
+ end
112
+ end
113
+
114
+ # creates a tmp module directory so puppet can work correctly
115
+ def tmp_modules_dir
116
+ if @tmp_modules_dir.nil? or not File.exists?(@tmp_modules_dir)
117
+ dir = Dir.mktmpdir
118
+ tmp_path = File.expand_path(File.join(dir, 'modules'))
119
+ FileUtils.mkdir_p(tmp_path)
120
+ @tmp_modules_dir = tmp_path
121
+ end
122
+ @tmp_modules_dir
123
+ end
124
+
125
+
126
+ # creates a puppet environment given a module path and environment name
127
+ def puppet_environment
128
+ @puppet_environment ||= Puppet::Node::Environment.create('production', [tmp_modules_dir])
129
+ end
130
+
131
+ # creates a puppet resource request to be used indirectly
132
+ def request(key, method)
133
+ instance = Puppet::Indirector::Indirection.instance(:resource_type)
134
+ indirection_name = 'test'
135
+ @request = Puppet::Indirector::Request.new(indirection_name, method, key, instance)
136
+ @request.environment = puppet_environment
137
+ @request
138
+ end
139
+
140
+ # creates an instance of the resource type parser
141
+ def resource_type_parser
142
+ @resource_type_parser ||= Puppet::Indirector::ResourceType::Parser.new
143
+ end
144
+
145
+ # returns the resource type object given a resource name ie. tomcat::connector
146
+ def find_resource(resource_name)
147
+ request = request(resource_name, 'find')
148
+ resource_type_parser.find(request)
149
+ end
150
+
151
+ # returns the resource types found in the module
152
+ def search_module(pattern='*')
153
+ request = request(pattern, 'search')
154
+ resource_type_parser.search(request)
155
+ end
156
+
157
+ # TODO we need to parse the types and find all the types that inherit other types and then order them so we can load the files first
158
+ def types
159
+ @types ||= search_module || []
160
+ end
161
+ end
162
+ end
163
+
@@ -5,6 +5,7 @@ class Resource
5
5
  attr_reader :type, :title, :parameters, :scope_name
6
6
 
7
7
  def initialize(type_name,instance)
8
+ # we don't store the resource parameters in the variablestore because they are never referenced
8
9
  @parameters = Hash[instance.parameters.map { |k| [k.param, VariableStore.resolve(k.value).gsub("\"", '')]}]
9
10
  @type = type_name
10
11
  @title = VariableStore.resolve(instance.title).gsub("\"", '')
@@ -13,9 +14,7 @@ class Resource
13
14
  # Gets all resources in the type that are not in a code block
14
15
  def self.all(statements)
15
16
  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
17
+ # if we accidentally pass a type in without specifying the code
19
18
  statements = statements.code unless statements.nil?
20
19
  end
21
20
  a = []
@@ -0,0 +1,37 @@
1
+
2
+ module Utilities
3
+ class SpecObject
4
+ attr_reader :instance
5
+ attr_accessor :enable_beaker_tests, :parameters, :types, :resources, :type
6
+
7
+ def initialize(mod_instance)
8
+ @instance = mod_instance
9
+ end
10
+
11
+ def types
12
+ instance.types
13
+ end
14
+
15
+ def module_name
16
+ instance.module_name
17
+ end
18
+
19
+ def module_path
20
+ instance.module_path
21
+ end
22
+
23
+ def get_binding
24
+ binding
25
+ end
26
+
27
+ def enable_beaker_tests?
28
+ @enable_beaker_tests == true
29
+ end
30
+
31
+ # allows the user to use the variable store to resolve the variable if it exists
32
+ def variable_value(key)
33
+ VariableStore.resolve(key)
34
+ end
35
+
36
+ end
37
+ end
@@ -2,26 +2,28 @@ source "https://rubygems.org"
2
2
 
3
3
  group :test do
4
4
  gem "rake"
5
- gem "puppet", ENV['PUPPET_VERSION'] || '~> 3.7.0'
5
+ gem "puppet", ENV['PUPPET_VERSION'] || '~> 3.7.3'
6
6
  gem "rspec-puppet", :git => 'https://github.com/rodjek/rspec-puppet.git'
7
7
  gem "puppetlabs_spec_helper"
8
8
  gem 'rspec-puppet-utils', :git => 'https://github.com/Accuity/rspec-puppet-utils.git'
9
9
  gem 'hiera-puppet-helper', :git => 'https://github.com/bobtfish/hiera-puppet-helper.git'
10
-
10
+ <%- if enable_beaker_tests? -%>
11
+ gem "beaker", :git => 'https://github.com/puppetlabs/beaker.git'
12
+ gem "beaker-rspec", :git => 'https://github.com/puppetlabs/beaker-rspec.git'
13
+ gem "vagrant-wrapper"
14
+ gem 'serverspec'
15
+ <%- end -%>
16
+ # there seems to be a bug with puppet-blacksmith and metadata-json-lint
17
+ # removing metadata for now
18
+ gem "metadata-json-lint"
19
+ gem 'puppet-syntax'
20
+ gem 'puppet-lint'
11
21
  end
12
22
 
13
23
  group :development do
14
24
  gem "travis"
15
25
  gem "travis-lint"
16
- <%- if enable_beaker_tests? -%>
17
- gem "beaker", :git => 'https://github.com/puppetlabs/beaker.git'
18
- gem "beaker-rspec", :git => 'https://github.com/puppetlabs/beaker-rspec.git'
19
- gem "vagrant-wrapper"
20
- <%- end -%>
21
26
  gem "puppet-blacksmith"
22
27
  gem "guard-rake"
23
- gem "metadata-json-lint"
24
- gem 'puppet-syntax'
25
- gem 'serverspec'
26
28
  end
27
29