puppet-retrospec 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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