rspec-puppet-maestrodev 0.1.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,62 @@
1
+ module RSpec::Puppet
2
+ module DefineExampleGroup
3
+ include RSpec::Puppet::ManifestMatchers
4
+ include RSpec::Puppet::Support
5
+
6
+ def subject
7
+ @catalogue ||= catalogue
8
+ end
9
+
10
+ def catalogue
11
+ define_name = self.class.top_level_description.downcase
12
+
13
+ vardir = Dir.mktmpdir
14
+ Puppet[:vardir] = vardir
15
+ Puppet[:hiera_config] = File.join(vardir, "hiera.yaml") if Puppet[:hiera_config] == File.expand_path("/dev/null")
16
+ Puppet[:modulepath] = self.respond_to?(:module_path) ? module_path : RSpec.configuration.module_path
17
+ Puppet[:manifestdir] = self.respond_to?(:manifest_dir) ? manifest_dir : RSpec.configuration.manifest_dir
18
+ Puppet[:manifest] = self.respond_to?(:manifest) ? manifest : RSpec.configuration.manifest
19
+ Puppet[:templatedir] = self.respond_to?(:template_dir) ? template_dir : RSpec.configuration.template_dir
20
+ Puppet[:config] = self.respond_to?(:config) ? config : RSpec.configuration.config
21
+
22
+ # If we're testing a standalone module (i.e. one that's outside of a
23
+ # puppet tree), the autoloader won't work, so we need to fudge it a bit.
24
+ if File.exists?(File.join(Puppet[:modulepath], 'manifests', 'init.pp'))
25
+ path_to_manifest = File.join([Puppet[:modulepath], 'manifests', define_name.split('::')[1..-1]].flatten)
26
+ import_str = "import '#{Puppet[:modulepath]}/manifests/init.pp'\nimport '#{path_to_manifest}.pp'\n"
27
+ elsif File.exists?(Puppet[:modulepath])
28
+ import_str = "import '#{Puppet[:manifest]}'\n"
29
+ else
30
+ import_str = ""
31
+ end
32
+
33
+ if self.respond_to? :params
34
+ param_str = params.keys.map { |r|
35
+ "#{r.to_s} => #{params[r].inspect}"
36
+ }.join(', ')
37
+ else
38
+ param_str = ""
39
+ end
40
+
41
+ if self.respond_to? :pre_condition
42
+ pre_cond = pre_condition
43
+ else
44
+ pre_cond = ""
45
+ end
46
+
47
+ code = pre_cond + "\n" + import_str + define_name + " { \"" + title + "\": " + param_str + " }"
48
+
49
+ nodename = self.respond_to?(:node) ? node : Puppet[:certname]
50
+ facts_val = {
51
+ 'hostname' => nodename.split('.').first,
52
+ 'fqdn' => nodename,
53
+ 'domain' => nodename.split('.', 2).last,
54
+ }
55
+ facts_val.merge!(munge_facts(facts)) if self.respond_to?(:facts)
56
+
57
+ catalogue = build_catalog(nodename, facts_val, code)
58
+ FileUtils.rm_rf(vardir) if File.directory?(vardir)
59
+ catalogue
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,54 @@
1
+ require 'puppetlabs_spec_helper/puppetlabs_spec/puppet_internals'
2
+
3
+ module RSpec::Puppet
4
+ module FunctionExampleGroup
5
+ include RSpec::Puppet::FunctionMatchers
6
+ PuppetInternals = PuppetlabsSpec::PuppetInternals
7
+
8
+ def subject
9
+ function_name = self.class.top_level_description.downcase
10
+
11
+ Puppet[:modulepath] = self.respond_to?(:module_path) ? module_path : RSpec.configuration.module_path
12
+ Puppet[:libdir] = Dir["#{Puppet[:modulepath]}/*/lib"].entries.join(File::PATH_SEPARATOR)
13
+
14
+ # if we specify a pre_condition, we should ensure that we compile that code
15
+ # into a catalog that is accessible from the scope where the function is called
16
+ if self.respond_to? :pre_condition
17
+ Puppet[:code] = pre_condition
18
+ nodename = self.respond_to?(:node) ? node : Puppet[:certname]
19
+ facts_val = {
20
+ 'hostname' => nodename.split('.').first,
21
+ 'fqdn' => nodename,
22
+ 'domain' => nodename.split('.').last,
23
+ }
24
+ facts_val.merge!(munge_facts(facts)) if self.respond_to?(:facts)
25
+ # we need to get a compiler, b/c we can attach that to a scope
26
+ @compiler = build_compiler(nodename, facts_val)
27
+ else
28
+ @compiler = PuppetInternals.compiler
29
+ end
30
+
31
+ scope = PuppetInternals.scope(:compiler => @compiler)
32
+
33
+ # Return the method instance for the function. This can be used with
34
+ # method.call
35
+ method = PuppetInternals.function_method(function_name, :scope => scope)
36
+ end
37
+
38
+ def compiler
39
+ @compiler
40
+ end
41
+
42
+ # get a compiler with an attached compiled catalog
43
+ def build_compiler(node_name, fact_values)
44
+ node_options = {
45
+ :name => node_name,
46
+ :options => { :parameters => fact_values },
47
+ }
48
+ node = PuppetInternals.node(node_options)
49
+ compiler = PuppetInternals.compiler(:node => node)
50
+ compiler.compile
51
+ compiler
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,35 @@
1
+ module RSpec::Puppet
2
+ module HostExampleGroup
3
+ include RSpec::Puppet::ManifestMatchers
4
+ include RSpec::Puppet::Support
5
+
6
+ def subject
7
+ @catalogue ||= catalogue
8
+ end
9
+
10
+ def catalogue
11
+ vardir = Dir.mktmpdir
12
+ Puppet[:vardir] = vardir
13
+ Puppet[:hiera_config] = File.join(vardir, "hiera.yaml") if Puppet[:hiera_config] == File.expand_path("/dev/null")
14
+ Puppet[:modulepath] = self.respond_to?(:module_path) ? module_path : RSpec.configuration.module_path
15
+ Puppet[:manifestdir] = self.respond_to?(:manifest_dir) ? manifest_dir : RSpec.configuration.manifest_dir
16
+ Puppet[:manifest] = self.respond_to?(:manifest) ? manifest : RSpec.configuration.manifest
17
+ Puppet[:templatedir] = self.respond_to?(:template_dir) ? template_dir : RSpec.configuration.template_dir
18
+ Puppet[:config] = self.respond_to?(:config) ? config : RSpec.configuration.config
19
+ code = ""
20
+
21
+ nodename = self.class.top_level_description.downcase
22
+
23
+ facts_val = {
24
+ 'hostname' => nodename.split('.').first,
25
+ 'fqdn' => nodename,
26
+ 'domain' => nodename.split('.').last,
27
+ }
28
+ facts_val.merge!(munge_facts(facts)) if self.respond_to?(:facts)
29
+
30
+ catalogue = build_catalog(nodename, facts_val, code)
31
+ FileUtils.rm_rf(vardir) if File.directory?(vardir)
32
+ catalogue
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,4 @@
1
+ require 'rspec-puppet/matchers/create_generic'
2
+ require 'rspec-puppet/matchers/create_resource'
3
+ require 'rspec-puppet/matchers/include_class'
4
+ require 'rspec-puppet/matchers/run'
@@ -0,0 +1,133 @@
1
+ module RSpec::Puppet
2
+ module ManifestMatchers
3
+ class CreateGeneric
4
+ def initialize(*args, &block)
5
+ @exp_resource_type = args.shift.to_s.gsub(/^(create|contain)_/, '')
6
+ @args = args
7
+ @block = block
8
+ @referenced_type = referenced_type(@exp_resource_type)
9
+ @title = args[0]
10
+ end
11
+
12
+ def with(*args, &block)
13
+ params = args.shift
14
+ @expected_params = (@expected_params || []) | params.to_a
15
+ self
16
+ end
17
+
18
+ def without(*args, &block)
19
+ params = args.shift
20
+ @expected_undef_params = (@expected_undef_params || []) | Array(params)
21
+ self
22
+ end
23
+
24
+ def method_missing(method, *args, &block)
25
+ if method.to_s =~ /^with_/
26
+ param = method.to_s.gsub(/^with_/, '')
27
+ (@expected_params ||= []) << [param, args[0]]
28
+ self
29
+ elsif method.to_s =~ /^without_/
30
+ param = method.to_s.gsub(/^without_/, '')
31
+ (@expected_undef_params ||= []) << param
32
+ self
33
+ else
34
+ super
35
+ end
36
+ end
37
+
38
+ def matches?(catalogue)
39
+ ret = true
40
+ resource = catalogue.resource(@referenced_type, @title)
41
+
42
+ if resource.nil?
43
+ ret = false
44
+ else
45
+ rsrc_hsh = resource.to_hash
46
+ if @expected_params
47
+ @expected_params.each do |name, value|
48
+ if value.kind_of?(Regexp) then
49
+ unless rsrc_hsh[name.to_sym].to_s =~ value
50
+ ret = false
51
+ (@errors ||= []) << "#{name.to_s} matching `#{value.inspect}` but its value of `#{rsrc_hsh[name.to_sym].inspect}` does not"
52
+ end
53
+ elsif value.kind_of?(Array) then
54
+ unless Array(rsrc_hsh[name.to_sym]).flatten.join == value.flatten.join
55
+ ret = false
56
+ (@errors ||= []) << "#{name.to_s} set to `#{value.inspect}` but it is set to `#{rsrc_hsh[name.to_sym].inspect}` in the catalogue"
57
+ end
58
+ else
59
+ unless rsrc_hsh[name.to_sym].to_s == value.to_s
60
+ ret = false
61
+ (@errors ||= []) << "#{name.to_s} set to `#{value.inspect}` but it is set to `#{rsrc_hsh[name.to_sym].inspect}` in the catalogue"
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ if @expected_undef_params
68
+ @expected_undef_params.each do |name|
69
+ unless resource.send(:parameters)[name.to_sym].nil?
70
+ ret = false
71
+ (@errors ||= []) << "#{name.to_s} undefined"
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ ret
78
+ end
79
+
80
+ def failure_message_for_should
81
+ "expected that the catalogue would contain #{@referenced_type}[#{@title}]#{errors}"
82
+ end
83
+
84
+ def failure_message_for_should_not
85
+ "expected that the catalogue would not contain #{@referenced_type}[#{@title}]#{errors}"
86
+ end
87
+
88
+ def description
89
+ values = []
90
+ if @expected_params
91
+ @expected_params.each do |name, value|
92
+ if value.kind_of?(Regexp)
93
+ values << "#{name.to_s} matching #{value.inspect}"
94
+ else
95
+ values << "#{name.to_s} => #{value.inspect}"
96
+ end
97
+ end
98
+ end
99
+
100
+ if @expected_undef_params
101
+ @expected_undef_params.each do |name, value|
102
+ values << "#{name.to_s} undefined"
103
+ end
104
+ end
105
+
106
+ unless values.empty?
107
+ if values.length == 1
108
+ value_str = " with #{values.first}"
109
+ else
110
+ value_str = " with #{values[0..-2].join(", ")} and #{values[-1]}"
111
+ end
112
+ end
113
+
114
+ "contain #{@referenced_type}[#{@title}]#{value_str}"
115
+ end
116
+
117
+ private
118
+
119
+ def referenced_type(type)
120
+ type.split('__').map { |r| r.capitalize }.join('::')
121
+ end
122
+
123
+ def errors
124
+ @errors.nil? ? "" : " with #{@errors.join(', and parameter ')}"
125
+ end
126
+ end
127
+
128
+ def method_missing(method, *args, &block)
129
+ return RSpec::Puppet::ManifestMatchers::CreateGeneric.new(method, *args, &block) if method.to_s =~ /^(create|contain)_/
130
+ super
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,53 @@
1
+ module RSpec::Puppet
2
+ module ManifestMatchers
3
+ extend RSpec::Matchers::DSL
4
+
5
+ matcher :create_resource do |expected_type, expected_title|
6
+ match do |catalogue|
7
+ ret = true
8
+ resources = catalogue.resources.select { |r|
9
+ r.type == referenced_type(expected_type)
10
+ }.select { |r|
11
+ r.title == expected_title if r.respond_to? :title
12
+ }
13
+
14
+ unless resources.length == 1
15
+ ret = false
16
+ end
17
+
18
+ if @expected_params and resources.length != 0
19
+ @expected_params.each do |name, value|
20
+ unless resources.first.send(:parameters)[name.to_sym] == value
21
+ ret = false
22
+ (@errors ||= []) << "the parameter #{name.to_s} set to `#{value}`"
23
+ end
24
+ end
25
+ end
26
+
27
+ ret
28
+ end
29
+
30
+ def errors
31
+ @errors.nil? ? "" : " with #{@errors.join(', ')}"
32
+ end
33
+
34
+ def referenced_type(type)
35
+ type.split('::').map { |r| r.capitalize }.join('::')
36
+ end
37
+
38
+ chain :with_param do |param_name,param_value|
39
+ (@expected_params ||= []) << [param_name, param_value]
40
+ end
41
+
42
+ description do
43
+ type = referenced_type(expected_type)
44
+ "create #{type}['#{expected_title}']"
45
+ end
46
+
47
+ failure_message_for_should do |actual|
48
+ type = referenced_type(expected_type)
49
+ "expected that the catalogue would contain #{type}['#{expected_title}']#{errors}"
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,19 @@
1
+ module RSpec::Puppet
2
+ module ManifestMatchers
3
+ extend RSpec::Matchers::DSL
4
+
5
+ matcher :include_class do |expected_class|
6
+ match do |catalogue|
7
+ catalogue.classes.include? expected_class
8
+ end
9
+
10
+ description do
11
+ "include Class[#{expected_class}]"
12
+ end
13
+
14
+ failure_message_for_should do |actual|
15
+ "expected that the catalogue would include Class[#{expected_class}]"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,76 @@
1
+ module RSpec::Puppet
2
+ module FunctionMatchers
3
+ extend RSpec::Matchers::DSL
4
+
5
+ matcher :run do
6
+ match do |func_obj|
7
+ if @params
8
+ @func = lambda { func_obj.call(@params) }
9
+ else
10
+ @func = lambda { func_obj.call }
11
+ end
12
+
13
+ if @expected_error
14
+ begin
15
+ @func.call
16
+ rescue @expected_error
17
+ #XXX check error string here
18
+ true
19
+ rescue
20
+ false
21
+ end
22
+ else
23
+ if @expected_return
24
+ @func.call == @expected_return
25
+ else
26
+ begin
27
+ @func.call
28
+ rescue
29
+ false
30
+ end
31
+ true
32
+ end
33
+ end
34
+ end
35
+
36
+ chain :with_params do |*params|
37
+ @params = params
38
+ end
39
+
40
+ chain :and_return do |value|
41
+ @expected_return = value
42
+ end
43
+
44
+ # XXX support error string and regexp
45
+ chain :and_raise_error do |value|
46
+ @expected_error = value
47
+ end
48
+
49
+ failure_message_for_should do |func_obj|
50
+ func_name = func_obj.name.gsub(/^function_/, '')
51
+ func_params = @params.inspect[1..-2]
52
+
53
+ if @expected_return
54
+ "expected #{func_name}(#{func_params}) to have returned #{@expected_return.inspect} instead of #{@func.call.inspect}"
55
+ elsif @expected_error
56
+ "expected #{func_name}(#{func_params}) to have raised #{@expected_error.inspect}"
57
+ else
58
+ "expected #{func_name}(#{func_params}) to have run successfully"
59
+ end
60
+ end
61
+
62
+ failure_message_for_should_not do |func_obj|
63
+ func_name = func_obj.name.gsub(/^function_/, '')
64
+ func_params = @params.inspect[1..-2]
65
+
66
+ if @expected_return
67
+ "expected #{func_name}(#{func_params}) to not have returned #{@expected_return.inspect}"
68
+ elsif @expected_error
69
+ "expected #{func_name}(#{func_params}) to not have raised #{@expected_error.inspect}"
70
+ else
71
+ "expected #{func_name}(#{func_params}) to not have run successfully"
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,144 @@
1
+ require 'puppet'
2
+ require 'fileutils'
3
+
4
+ module RSpec::Puppet
5
+ class Setup
6
+ def self.run(module_name=nil)
7
+ unless is_module_dir?
8
+ $stderr.puts "Does not appear to be a Puppet module. Aborting"
9
+ return false
10
+ end
11
+
12
+ if module_name.nil?
13
+ module_name = get_module_name
14
+ if module_name.nil?
15
+ $stderr.puts "Unable to determine module name. Aborting"
16
+ return false
17
+ end
18
+ end
19
+
20
+ [
21
+ 'spec',
22
+ 'spec/classes',
23
+ 'spec/defines',
24
+ 'spec/functions',
25
+ 'spec/hosts',
26
+ 'spec/fixtures',
27
+ 'spec/fixtures/manifests',
28
+ 'spec/fixtures/modules',
29
+ "spec/fixtures/modules/#{module_name}",
30
+ ].each { |dir| safe_mkdir(dir) }
31
+
32
+ safe_touch('spec/fixtures/manifests/site.pp')
33
+
34
+ ['manifests','lib','files','templates'].each do |dir|
35
+ if File.exist? dir
36
+ safe_make_symlink("../../../../#{dir}", "spec/fixtures/modules/#{module_name}/#{dir}")
37
+ end
38
+ end
39
+
40
+ safe_create_spec_helper
41
+ safe_create_rakefile
42
+ end
43
+
44
+ protected
45
+ def self.get_module_name
46
+ p = Puppet::Parser::Lexer.new
47
+ module_name = nil
48
+ Dir["manifests/*.pp"].entries.each do |manifest|
49
+ p.string = File.read(manifest)
50
+ tokens = p.fullscan
51
+ i = tokens.index { |token| [:CLASS, :DEFINE].include? token.first }
52
+ unless i.nil?
53
+ module_name = tokens[i + 1].last[:value].split('::').first
54
+ break
55
+ end
56
+ end
57
+ module_name
58
+ end
59
+
60
+ def self.is_module_dir?
61
+ Dir["*"].entries.include? "manifests"
62
+ end
63
+
64
+ def self.safe_mkdir(dir)
65
+ if File.exists? dir
66
+ unless File.directory? dir
67
+ $stderr.puts "!! #{dir} already exists and is not a directory"
68
+ end
69
+ else
70
+ FileUtils.mkdir dir
71
+ puts " + #{dir}/"
72
+ end
73
+ end
74
+
75
+ def self.safe_touch(file)
76
+ if File.exists? file
77
+ unless File.file? file
78
+ $stderr.puts "!! #{file} already exists and is not a regular file"
79
+ end
80
+ else
81
+ FileUtils.touch file
82
+ puts " + #{file}"
83
+ end
84
+ end
85
+
86
+ def self.safe_create_spec_helper
87
+ content = <<-EOF
88
+ require 'rspec-puppet'
89
+
90
+ fixture_path = File.expand_path(File.join(__FILE__, '..', 'fixtures'))
91
+
92
+ RSpec.configure do |c|
93
+ c.module_path = File.join(fixture_path, 'modules')
94
+ c.manifest_dir = File.join(fixture_path, 'manifests')
95
+ end
96
+ EOF
97
+ if File.exists? 'spec/spec_helper.rb'
98
+ old_content = File.read('spec/spec_helper.rb')
99
+ if old_content != content
100
+ $stderr.puts "!! spec/spec_helper.rb already exists and differs from template"
101
+ end
102
+ else
103
+ File.open('spec/spec_helper.rb', 'w') do |f|
104
+ f.puts content
105
+ end
106
+ puts ' + spec/spec_helper.rb'
107
+ end
108
+ end
109
+
110
+ def self.safe_make_symlink(source, target)
111
+ if File.exists? target
112
+ unless File.symlink? target
113
+ $stderr.puts "!! #{file} already exists and is not a symlink"
114
+ end
115
+ else
116
+ FileUtils.ln_s(source, target)
117
+ puts " + #{target}"
118
+ end
119
+ end
120
+
121
+ def self.safe_create_rakefile
122
+ content = <<-EOF
123
+ require 'rake'
124
+
125
+ require 'rspec/core/rake_task'
126
+
127
+ RSpec::Core::RakeTask.new(:spec) do |t|
128
+ t.pattern = 'spec/*/*_spec.rb'
129
+ end
130
+ EOF
131
+ if File.exists? 'Rakefile'
132
+ old_content = File.read('Rakefile')
133
+ if old_content != content
134
+ $stderr.puts "!! Rakefile already exists and differs from template"
135
+ end
136
+ else
137
+ File.open('Rakefile', 'w') do |f|
138
+ f.puts content
139
+ end
140
+ puts ' + Rakefile'
141
+ end
142
+ end
143
+ end
144
+ end