chefspec-chef 9.3.4
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 +7 -0
- data/Gemfile +30 -0
- data/LICENSE +22 -0
- data/Rakefile +85 -0
- data/chefspec-chef.gemspec +30 -0
- data/lib/chefspec/api/core.rb +217 -0
- data/lib/chefspec/api/described.rb +53 -0
- data/lib/chefspec/api/do_nothing.rb +26 -0
- data/lib/chefspec/api/include_any_recipe.rb +24 -0
- data/lib/chefspec/api/include_recipe.rb +28 -0
- data/lib/chefspec/api/link.rb +28 -0
- data/lib/chefspec/api/notifications.rb +40 -0
- data/lib/chefspec/api/reboot.rb +14 -0
- data/lib/chefspec/api/render_file.rb +37 -0
- data/lib/chefspec/api/state_attrs.rb +30 -0
- data/lib/chefspec/api/stubs.rb +183 -0
- data/lib/chefspec/api/stubs_for.rb +139 -0
- data/lib/chefspec/api/subscriptions.rb +37 -0
- data/lib/chefspec/api/user.rb +230 -0
- data/lib/chefspec/api.rb +39 -0
- data/lib/chefspec/berkshelf.rb +63 -0
- data/lib/chefspec/cacher.rb +64 -0
- data/lib/chefspec/coverage/filters.rb +82 -0
- data/lib/chefspec/coverage.rb +247 -0
- data/lib/chefspec/deprecations.rb +46 -0
- data/lib/chefspec/errors.rb +48 -0
- data/lib/chefspec/expect_exception.rb +51 -0
- data/lib/chefspec/extensions/chef/client.rb +21 -0
- data/lib/chefspec/extensions/chef/conditional.rb +16 -0
- data/lib/chefspec/extensions/chef/cookbook/gem_installer.rb +33 -0
- data/lib/chefspec/extensions/chef/cookbook_loader.rb +14 -0
- data/lib/chefspec/extensions/chef/cookbook_uploader.rb +12 -0
- data/lib/chefspec/extensions/chef/data_query.rb +49 -0
- data/lib/chefspec/extensions/chef/lwrp_base.rb +29 -0
- data/lib/chefspec/extensions/chef/provider.rb +39 -0
- data/lib/chefspec/extensions/chef/resource/freebsd_package.rb +17 -0
- data/lib/chefspec/extensions/chef/resource.rb +188 -0
- data/lib/chefspec/extensions/chef/run_context/cookbook_compiler.rb +84 -0
- data/lib/chefspec/extensions/chef/securable.rb +19 -0
- data/lib/chefspec/extensions/ohai/system.rb +11 -0
- data/lib/chefspec/extensions.rb +21 -0
- data/lib/chefspec/file_cache_path_proxy.rb +15 -0
- data/lib/chefspec/formatter.rb +282 -0
- data/lib/chefspec/librarian.rb +51 -0
- data/lib/chefspec/matchers/do_nothing_matcher.rb +52 -0
- data/lib/chefspec/matchers/include_any_recipe_matcher.rb +51 -0
- data/lib/chefspec/matchers/include_recipe_matcher.rb +46 -0
- data/lib/chefspec/matchers/link_to_matcher.rb +37 -0
- data/lib/chefspec/matchers/notifications_matcher.rb +143 -0
- data/lib/chefspec/matchers/render_file_matcher.rb +140 -0
- data/lib/chefspec/matchers/resource_matcher.rb +175 -0
- data/lib/chefspec/matchers/state_attrs_matcher.rb +71 -0
- data/lib/chefspec/matchers/subscribes_matcher.rb +72 -0
- data/lib/chefspec/matchers.rb +13 -0
- data/lib/chefspec/mixins/normalize.rb +22 -0
- data/lib/chefspec/policyfile.rb +69 -0
- data/lib/chefspec/renderer.rb +145 -0
- data/lib/chefspec/rspec.rb +21 -0
- data/lib/chefspec/runner.rb +8 -0
- data/lib/chefspec/server.rb +4 -0
- data/lib/chefspec/server_methods.rb +173 -0
- data/lib/chefspec/server_runner.rb +76 -0
- data/lib/chefspec/solo_runner.rb +516 -0
- data/lib/chefspec/stubs/command_registry.rb +11 -0
- data/lib/chefspec/stubs/command_stub.rb +37 -0
- data/lib/chefspec/stubs/data_bag_item_registry.rb +13 -0
- data/lib/chefspec/stubs/data_bag_item_stub.rb +25 -0
- data/lib/chefspec/stubs/data_bag_registry.rb +13 -0
- data/lib/chefspec/stubs/data_bag_stub.rb +23 -0
- data/lib/chefspec/stubs/registry.rb +32 -0
- data/lib/chefspec/stubs/search_registry.rb +13 -0
- data/lib/chefspec/stubs/search_stub.rb +25 -0
- data/lib/chefspec/stubs/stub.rb +38 -0
- data/lib/chefspec/util.rb +58 -0
- data/lib/chefspec/version.rb +3 -0
- data/lib/chefspec/zero_server.rb +142 -0
- data/lib/chefspec.rb +75 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/hash.rb +35 -0
- data/spec/unit/cacher_spec.rb +70 -0
- data/spec/unit/coverage/filters_spec.rb +60 -0
- data/spec/unit/deprecations_spec.rb +52 -0
- data/spec/unit/errors_spec.rb +57 -0
- data/spec/unit/expect_exception_spec.rb +32 -0
- data/spec/unit/macros_spec.rb +119 -0
- data/spec/unit/matchers/do_nothing_matcher.rb +5 -0
- data/spec/unit/matchers/include_any_recipe_matcher_spec.rb +52 -0
- data/spec/unit/matchers/include_recipe_matcher_spec.rb +38 -0
- data/spec/unit/matchers/link_to_matcher_spec.rb +55 -0
- data/spec/unit/matchers/notifications_matcher_spec.rb +39 -0
- data/spec/unit/matchers/render_file_matcher_spec.rb +68 -0
- data/spec/unit/matchers/resource_matcher_spec.rb +5 -0
- data/spec/unit/matchers/state_attrs_matcher_spec.rb +68 -0
- data/spec/unit/matchers/subscribes_matcher_spec.rb +63 -0
- data/spec/unit/renderer_spec.rb +69 -0
- data/spec/unit/server_runner_spec.rb +28 -0
- data/spec/unit/solo_runner_spec.rb +171 -0
- data/spec/unit/stubs/command_registry_spec.rb +27 -0
- data/spec/unit/stubs/command_stub_spec.rb +61 -0
- data/spec/unit/stubs/data_bag_item_registry_spec.rb +39 -0
- data/spec/unit/stubs/data_bag_item_stub_spec.rb +36 -0
- data/spec/unit/stubs/data_bag_registry_spec.rb +39 -0
- data/spec/unit/stubs/data_bag_stub_spec.rb +35 -0
- data/spec/unit/stubs/registry_spec.rb +29 -0
- data/spec/unit/stubs/search_registry_spec.rb +39 -0
- data/spec/unit/stubs/search_stub_spec.rb +36 -0
- data/spec/unit/stubs/stub_spec.rb +64 -0
- data/templates/coverage/human.erb +22 -0
- data/templates/coverage/json.erb +8 -0
- data/templates/coverage/table.erb +14 -0
- data/templates/errors/cookbook_path_not_found.erb +3 -0
- data/templates/errors/erb_template_parse_error.erb +5 -0
- data/templates/errors/gem_load_error.erb +7 -0
- data/templates/errors/invalid_berkshelf_options.erb +4 -0
- data/templates/errors/may_need_to_specify_platform.erb +25 -0
- data/templates/errors/no_conversion_error.erb +1 -0
- data/templates/errors/not_stubbed.erb +7 -0
- data/templates/errors/shell_out_not_stubbed.erb +10 -0
- data/templates/errors/template_not_found.erb +9 -0
- metadata +221 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module ChefSpec
|
|
2
|
+
#
|
|
3
|
+
# The cacher module allows for ultra-fast tests by caching the results of a
|
|
4
|
+
# CCR in memory across an example group. In testing, this can reduce the
|
|
5
|
+
# total testing time by a factor of 10x. This strategy is _not_ the default
|
|
6
|
+
# behavior, because it has implications surrounding stubbing and is _not_
|
|
7
|
+
# threadsafe!
|
|
8
|
+
#
|
|
9
|
+
# The credit for this approach and code belongs to Juri Timošin (DracoAter).
|
|
10
|
+
# Please see his original blog post below for an in-depth explanation of how
|
|
11
|
+
# and why this approach is faster.
|
|
12
|
+
#
|
|
13
|
+
# @example Using the Cacher module
|
|
14
|
+
# First, require the Cacher module in your +spec_helper.rb+:
|
|
15
|
+
#
|
|
16
|
+
# RSpec.configure do |config|
|
|
17
|
+
# config.extend(ChefSpec::Cacher)
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# Next, change your +let+ blocks to +cached+ blocks:
|
|
21
|
+
#
|
|
22
|
+
# let(:chef_run) { ... } #=> cached(:chef_run) { ... }
|
|
23
|
+
#
|
|
24
|
+
# Finally, celebrate!
|
|
25
|
+
#
|
|
26
|
+
# @warn
|
|
27
|
+
# This strategy is only recommended for advanced users, as it makes
|
|
28
|
+
# stubbing slightly more difficult and indirect!
|
|
29
|
+
#
|
|
30
|
+
# @see http://dracoater.blogspot.com/2013/12/testing-chef-cookbooks-part-25-speeding.html
|
|
31
|
+
#
|
|
32
|
+
module Cacher
|
|
33
|
+
@@cache = {}
|
|
34
|
+
FINALIZER = lambda { |id| @@cache.delete(id) }
|
|
35
|
+
|
|
36
|
+
def cached(name, &block)
|
|
37
|
+
location = ancestors.first.metadata[:location]
|
|
38
|
+
unless location.nil?
|
|
39
|
+
location += ancestors.first.metadata[:description] unless ancestors.first.metadata[:description].nil?
|
|
40
|
+
location += ancestors.first.metadata[:scoped_id] unless ancestors.first.metadata[:scoped_id].nil?
|
|
41
|
+
end
|
|
42
|
+
location ||= ancestors.first.metadata[:parent_example_group][:location]
|
|
43
|
+
|
|
44
|
+
define_method(name) do
|
|
45
|
+
key = [location, name.to_s].join(".")
|
|
46
|
+
unless @@cache.key?(Thread.current.object_id)
|
|
47
|
+
ObjectSpace.define_finalizer(Thread.current, FINALIZER)
|
|
48
|
+
end
|
|
49
|
+
@@cache[Thread.current.object_id] ||= {}
|
|
50
|
+
@@cache[Thread.current.object_id][key] ||= instance_eval(&block)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def cached!(name, &block)
|
|
55
|
+
cached(name, &block)
|
|
56
|
+
|
|
57
|
+
before { send(name) }
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
RSpec.configure do |config|
|
|
63
|
+
config.extend(ChefSpec::Cacher)
|
|
64
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module ChefSpec
|
|
2
|
+
class Coverage
|
|
3
|
+
class Filter
|
|
4
|
+
def initialize(filter)
|
|
5
|
+
@filter = filter
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def matches?
|
|
9
|
+
raise "Must override Filter#matches?"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
#
|
|
14
|
+
# @example Match resources based on a regular expression.
|
|
15
|
+
# add_filter /^test/
|
|
16
|
+
#
|
|
17
|
+
class RegexpFilter < Filter
|
|
18
|
+
def matches?(resource)
|
|
19
|
+
return true if resource.source_line.nil?
|
|
20
|
+
|
|
21
|
+
@filter =~ resource.source_line
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
#
|
|
26
|
+
# @example Match resources based on a regular expression.
|
|
27
|
+
# add_filter 'test/bar/zip'
|
|
28
|
+
#
|
|
29
|
+
class StringFilter < RegexpFilter
|
|
30
|
+
def initialize(filter)
|
|
31
|
+
super(Regexp.new("^#{filter}"))
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
#
|
|
36
|
+
# @example Match resources based on a custom block.
|
|
37
|
+
# # Ignore internal cookbooks
|
|
38
|
+
# add_filter do |resource|
|
|
39
|
+
# resource.name =~ /^acme-(.+)/
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
class BlockFilter < Filter
|
|
43
|
+
def matches?(resource)
|
|
44
|
+
return true if resource.source_line.nil?
|
|
45
|
+
|
|
46
|
+
@filter.call(resource)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
#
|
|
51
|
+
# @example Ignore dependent cookbooks (via Berkshelf)
|
|
52
|
+
# add_filter BerkshelfFilter.new(berksfile)
|
|
53
|
+
#
|
|
54
|
+
class BerkshelfFilter < Filter
|
|
55
|
+
def initialize(berksfile)
|
|
56
|
+
@berksfile = berksfile
|
|
57
|
+
|
|
58
|
+
@metadatas = if berksfile.respond_to?(:dependencies)
|
|
59
|
+
berksfile.dependencies
|
|
60
|
+
.select(&:metadata?)
|
|
61
|
+
.map(&:name)
|
|
62
|
+
else
|
|
63
|
+
berksfile.sources.collect do |source|
|
|
64
|
+
location = source.location
|
|
65
|
+
if location.respond_to?(:metadata?) && location.metadata?
|
|
66
|
+
source
|
|
67
|
+
else
|
|
68
|
+
nil
|
|
69
|
+
end
|
|
70
|
+
end.compact.map(&:name)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def matches?(resource)
|
|
75
|
+
return true if resource.source_line.nil?
|
|
76
|
+
|
|
77
|
+
normalized_source_line = resource.source_line.gsub("\\", "/")
|
|
78
|
+
normalized_source_line =~ %r{cookbooks/(?!#{@metadatas.join('|')})}
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
require_relative "coverage/filters"
|
|
2
|
+
|
|
3
|
+
module ChefSpec
|
|
4
|
+
class Coverage
|
|
5
|
+
EXIT_FAILURE = 1
|
|
6
|
+
EXIT_SUCCESS = 0
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def method_added(name)
|
|
10
|
+
# Only delegate public methods
|
|
11
|
+
if method_defined?(name)
|
|
12
|
+
instance_eval <<-EOH, __FILE__, __LINE__ + 1
|
|
13
|
+
def #{name}(*args, &block)
|
|
14
|
+
instance.public_send(:#{name}, *args, &block)
|
|
15
|
+
end
|
|
16
|
+
EOH
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
include Singleton
|
|
22
|
+
|
|
23
|
+
attr_reader :filters
|
|
24
|
+
|
|
25
|
+
#
|
|
26
|
+
# Create a new coverage object singleton.
|
|
27
|
+
#
|
|
28
|
+
def initialize
|
|
29
|
+
@collection = {}
|
|
30
|
+
@filters = {}
|
|
31
|
+
@outputs = []
|
|
32
|
+
add_output do |report|
|
|
33
|
+
|
|
34
|
+
erb = Erubis::Eruby.new(File.read(@template))
|
|
35
|
+
puts erb.evaluate(report)
|
|
36
|
+
rescue NameError => e
|
|
37
|
+
raise Error::ErbTemplateParseError.new(original_error: e.message)
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
@template = ChefSpec.root.join("templates", "coverage", "human.erb")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
#
|
|
44
|
+
# Start the coverage reporting analysis. This method also adds the the
|
|
45
|
+
# +at_exit+ handler for printing the coverage report.
|
|
46
|
+
#
|
|
47
|
+
def start!(&block)
|
|
48
|
+
warn("ChefSpec's coverage reporting is deprecated and will be removed in a future version")
|
|
49
|
+
instance_eval(&block) if block
|
|
50
|
+
at_exit { ChefSpec::Coverage.report! }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
#
|
|
54
|
+
# Add a filter to the coverage analysis.
|
|
55
|
+
#
|
|
56
|
+
# @param [Filter, String, Regexp] filter
|
|
57
|
+
# the filter to add
|
|
58
|
+
# @param [Proc] block
|
|
59
|
+
# the block to use as a filter
|
|
60
|
+
#
|
|
61
|
+
# @return [true]
|
|
62
|
+
#
|
|
63
|
+
def add_filter(filter = nil, &block)
|
|
64
|
+
id = "#{filter.inspect}/#{block.inspect}".hash
|
|
65
|
+
|
|
66
|
+
@filters[id] = if filter.is_a?(Filter)
|
|
67
|
+
filter
|
|
68
|
+
elsif filter.is_a?(String)
|
|
69
|
+
StringFilter.new(filter)
|
|
70
|
+
elsif filter.is_a?(Regexp)
|
|
71
|
+
RegexpFilter.new(filter)
|
|
72
|
+
elsif block
|
|
73
|
+
BlockFilter.new(block)
|
|
74
|
+
else
|
|
75
|
+
raise ArgumentError, "Please specify either a string, " \
|
|
76
|
+
"filter, or block to filter source files with!"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
true
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
#
|
|
83
|
+
# Add an output to send the coverage results to.
|
|
84
|
+
# @param [Proc] block
|
|
85
|
+
# the block to use as the output
|
|
86
|
+
#
|
|
87
|
+
# @return [true]
|
|
88
|
+
#
|
|
89
|
+
def add_output(&block)
|
|
90
|
+
@outputs << block
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
#
|
|
94
|
+
# Change the template for reporting of converage analysis.
|
|
95
|
+
#
|
|
96
|
+
# @param [string] path
|
|
97
|
+
# The template file to use for the output of the report
|
|
98
|
+
#
|
|
99
|
+
# @return [true]
|
|
100
|
+
#
|
|
101
|
+
def set_template(file = "human.erb")
|
|
102
|
+
@template = [
|
|
103
|
+
ChefSpec.root.join("templates", "coverage", file),
|
|
104
|
+
File.expand_path(file, Dir.pwd),
|
|
105
|
+
].find { |f| File.exist?(f) }
|
|
106
|
+
raise Error::TemplateNotFound.new(path: file) unless @template
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
#
|
|
110
|
+
# Add a resource to the resource collection. Only new resources are added
|
|
111
|
+
# and only resources that match the given filter are covered (which is *
|
|
112
|
+
# by default).
|
|
113
|
+
#
|
|
114
|
+
# @param [Chef::Resource] resource
|
|
115
|
+
#
|
|
116
|
+
def add(resource)
|
|
117
|
+
if !exists?(resource) && !filtered?(resource)
|
|
118
|
+
@collection[resource.to_s] = ResourceWrapper.new(resource)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
#
|
|
123
|
+
# Called when a resource is matched to indicate it has been tested.
|
|
124
|
+
#
|
|
125
|
+
# @param [Chef::Resource] resource
|
|
126
|
+
#
|
|
127
|
+
def cover!(resource)
|
|
128
|
+
wrapper = find(resource)
|
|
129
|
+
wrapper.touch! if wrapper
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
#
|
|
133
|
+
# Called to check if a resource belongs to a cookbook from the specified
|
|
134
|
+
# directories.
|
|
135
|
+
#
|
|
136
|
+
# @param [Chef::Resource] resource
|
|
137
|
+
#
|
|
138
|
+
def filtered?(resource)
|
|
139
|
+
filters.any? { |_, filter| filter.matches?(resource) }
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
#
|
|
143
|
+
# Generate a coverage report. This report **must** be generated +at_exit+
|
|
144
|
+
# or else the entire resource collection may not be complete!
|
|
145
|
+
#
|
|
146
|
+
# @example Generating a report
|
|
147
|
+
#
|
|
148
|
+
# ChefSpec::Coverage.report!
|
|
149
|
+
#
|
|
150
|
+
def report!
|
|
151
|
+
# Borrowed from simplecov#41
|
|
152
|
+
#
|
|
153
|
+
# If an exception is thrown that isn't a "SystemExit", we need to capture
|
|
154
|
+
# that error and re-raise.
|
|
155
|
+
if $!
|
|
156
|
+
exit_status = $!.is_a?(SystemExit) ? $!.status : EXIT_FAILURE
|
|
157
|
+
else
|
|
158
|
+
exit_status = EXIT_SUCCESS
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
report = {}.tap do |h|
|
|
162
|
+
h[:total] = @collection.size
|
|
163
|
+
h[:touched] = @collection.count { |_, resource| resource.touched? }
|
|
164
|
+
h[:coverage] = ((h[:touched] / h[:total].to_f) * 100).round(2)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
report[:untouched_resources] = @collection.collect do |_, resource|
|
|
168
|
+
resource unless resource.touched?
|
|
169
|
+
end.compact
|
|
170
|
+
report[:all_resources] = @collection.values
|
|
171
|
+
|
|
172
|
+
@outputs.each do |block|
|
|
173
|
+
instance_exec(report, &block)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Ensure we exit correctly (#351)
|
|
177
|
+
Kernel.exit(exit_status) if exit_status && exit_status > 0
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
private
|
|
181
|
+
|
|
182
|
+
def find(resource)
|
|
183
|
+
@collection[resource.to_s]
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def exists?(resource)
|
|
187
|
+
!find(resource).nil?
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
class ResourceWrapper
|
|
191
|
+
attr_reader :resource
|
|
192
|
+
|
|
193
|
+
def initialize(resource = nil)
|
|
194
|
+
@resource = resource
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def to_s
|
|
198
|
+
@resource.to_s
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def to_json
|
|
202
|
+
{
|
|
203
|
+
"source_file" => source_file,
|
|
204
|
+
"source_line" => source_line,
|
|
205
|
+
"touched" => touched?,
|
|
206
|
+
"resource" => to_s,
|
|
207
|
+
}.to_json
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def source_file
|
|
211
|
+
@source_file ||= if @resource.source_line
|
|
212
|
+
shortname(@resource.source_line.split(":").first)
|
|
213
|
+
else
|
|
214
|
+
"Unknown"
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def source_line
|
|
219
|
+
@source_line ||= if @resource.source_line
|
|
220
|
+
@resource.source_line.split(":", 2).last.to_i
|
|
221
|
+
else
|
|
222
|
+
"Unknown"
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def touch!
|
|
227
|
+
@touched = true
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def touched?
|
|
231
|
+
!!@touched
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
private
|
|
235
|
+
|
|
236
|
+
def shortname(file)
|
|
237
|
+
if file.include?(Dir.pwd)
|
|
238
|
+
file.split(Dir.pwd, 2).last
|
|
239
|
+
elsif file.include?("cookbooks")
|
|
240
|
+
file.split("cookbooks/", 2).last
|
|
241
|
+
else
|
|
242
|
+
file
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Kernel
|
|
2
|
+
# Kernel extension to print deprecation notices.
|
|
3
|
+
#
|
|
4
|
+
# @example printing a deprecation warning
|
|
5
|
+
# deprecated 'no longer in use' #=> "[DEPRECATION] no longer in use"
|
|
6
|
+
#
|
|
7
|
+
# @param [Array<String>] messages
|
|
8
|
+
def deprecated(*messages)
|
|
9
|
+
messages.each do |message|
|
|
10
|
+
calling_spec = caller.find { |line| line =~ %r{(/spec)|(_spec\.rb)} }
|
|
11
|
+
if calling_spec
|
|
12
|
+
calling_spec = "spec/" + calling_spec.split("/spec/").last
|
|
13
|
+
warn "[DEPRECATION] #{message} (called from #{calling_spec})"
|
|
14
|
+
else
|
|
15
|
+
warn "[DEPRECATION] #{message}"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module ChefSpec
|
|
22
|
+
class SoloRunner
|
|
23
|
+
# @deprecated {ChefSpec::Runner.define_runner_method} is deprecated. Please
|
|
24
|
+
# use {ChefSpec.define_matcher} instead.
|
|
25
|
+
def self.define_runner_method(resource_name)
|
|
26
|
+
deprecated "`ChefSpec::Runner.define_runner_method' is deprecated." \
|
|
27
|
+
" It is being used in the #{resource_name} resource matcher." \
|
|
28
|
+
" Please use `ChefSpec.define_matcher' instead."
|
|
29
|
+
|
|
30
|
+
ChefSpec.define_matcher(resource_name)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class Server
|
|
35
|
+
def self.method_missing(m, *args, &block)
|
|
36
|
+
deprecated "`ChefSpec::Server.#{m}' is deprecated. There is no longer" \
|
|
37
|
+
" a global Chef Server instance. Please use a ChefSpec::SoloRunner" \
|
|
38
|
+
" instead. More documentation can be found in the ChefSpec README."
|
|
39
|
+
raise ChefSpec::Error::NoConversionError
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
module ChefSpec::Error
|
|
45
|
+
class NoConversionError < ChefSpecError; end
|
|
46
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module ChefSpec
|
|
2
|
+
module Error
|
|
3
|
+
class ChefSpecError < StandardError
|
|
4
|
+
def initialize(options = {})
|
|
5
|
+
class_name = self.class.to_s.split("::").last
|
|
6
|
+
filename = options.delete(:_template) || Util.underscore(class_name)
|
|
7
|
+
template = ChefSpec.root.join("templates", "errors", "#{filename}.erb")
|
|
8
|
+
|
|
9
|
+
erb = Erubis::Eruby.new(File.read(template))
|
|
10
|
+
super erb.evaluate(options)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class NotStubbed < ChefSpecError
|
|
15
|
+
def initialize(options = {})
|
|
16
|
+
name = self.class.name.to_s.split("::").last
|
|
17
|
+
type = Util.underscore(name).gsub("_not_stubbed", "")
|
|
18
|
+
klass = Stubs.const_get(name.gsub("NotStubbed", "") + "Stub")
|
|
19
|
+
stub = klass.new(*options[:args]).and_return("...").signature
|
|
20
|
+
|
|
21
|
+
signature = "#{type}(#{options[:args].map(&:inspect).join(", ")})"
|
|
22
|
+
|
|
23
|
+
super({
|
|
24
|
+
type: type,
|
|
25
|
+
signature: signature,
|
|
26
|
+
stub: stub,
|
|
27
|
+
_template: :not_stubbed,
|
|
28
|
+
}.merge(options))
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class CommandNotStubbed < NotStubbed; end
|
|
33
|
+
class SearchNotStubbed < NotStubbed; end
|
|
34
|
+
class DataBagNotStubbed < NotStubbed; end
|
|
35
|
+
class DataBagItemNotStubbed < NotStubbed; end
|
|
36
|
+
class ShellOutNotStubbed < ChefSpecError; end
|
|
37
|
+
|
|
38
|
+
class CookbookPathNotFound < ChefSpecError; end
|
|
39
|
+
class GemLoadError < ChefSpecError; end
|
|
40
|
+
|
|
41
|
+
class MayNeedToSpecifyPlatform < ChefSpecError; end
|
|
42
|
+
|
|
43
|
+
class InvalidBerkshelfOptions < ChefSpecError; end
|
|
44
|
+
|
|
45
|
+
class TemplateNotFound < ChefSpecError; end
|
|
46
|
+
class ErbTemplateParseError < ChefSpecError; end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
class RSpec::Matchers::BuiltIn::RaiseError
|
|
2
|
+
class << self
|
|
3
|
+
attr_accessor :last_run
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
attr_reader :expected_message
|
|
7
|
+
|
|
8
|
+
def last_error_for_chefspec
|
|
9
|
+
@expected_error
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
alias_method :old_matches?, :matches?
|
|
13
|
+
def matches?(*args)
|
|
14
|
+
self.class.last_run = self
|
|
15
|
+
old_matches?(*args)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module ChefSpec
|
|
20
|
+
class ExpectException
|
|
21
|
+
def initialize(formatter_exception, formatter_message = nil)
|
|
22
|
+
@formatter_exception = formatter_exception
|
|
23
|
+
@formatter_message = formatter_message
|
|
24
|
+
@matcher = RSpec::Matchers::BuiltIn::RaiseError.last_run
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def expected?
|
|
28
|
+
return false if @matcher.nil?
|
|
29
|
+
|
|
30
|
+
exception_matched? && message_matched?
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def exception_matched?
|
|
36
|
+
@formatter_exception == @matcher.last_error_for_chefspec ||
|
|
37
|
+
@matcher.last_error_for_chefspec === @formatter_exception
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def message_matched?
|
|
41
|
+
case @formatter_message
|
|
42
|
+
when nil
|
|
43
|
+
true
|
|
44
|
+
when Regexp
|
|
45
|
+
@matcher.expected_message =~ @formatter_message
|
|
46
|
+
else
|
|
47
|
+
@matcher.expected_message == @formatter_message
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Force loading Chef Config to fix a bad dependency tree. See
|
|
2
|
+
# https://github.com/opscode/chef/issues/2703 for more information.
|
|
3
|
+
require "chef/config"
|
|
4
|
+
|
|
5
|
+
# Providers has to be included before client... probably a weird
|
|
6
|
+
# include missing in Chef-land, but we can make sure we get it right anyway.
|
|
7
|
+
require "chef/providers"
|
|
8
|
+
require "chef/client"
|
|
9
|
+
|
|
10
|
+
# @private
|
|
11
|
+
Chef::Client.prepend(Module.new do
|
|
12
|
+
#
|
|
13
|
+
# Don't actually run ohai (we have fake data for that)
|
|
14
|
+
#
|
|
15
|
+
# @see Chef::Client#run_ohai
|
|
16
|
+
#
|
|
17
|
+
def run_ohai
|
|
18
|
+
return super unless $CHEFSPEC_MODE
|
|
19
|
+
# noop
|
|
20
|
+
end
|
|
21
|
+
end)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require "chef/resource/conditional"
|
|
2
|
+
|
|
3
|
+
Chef::Resource::Conditional.prepend(Module.new do
|
|
4
|
+
# @see Chef::Resource::Conditional#evaluate_command
|
|
5
|
+
def evaluate_command
|
|
6
|
+
return super unless $CHEFSPEC_MODE
|
|
7
|
+
|
|
8
|
+
stub = ChefSpec::Stubs::CommandRegistry.stub_for(@command)
|
|
9
|
+
|
|
10
|
+
if stub.nil?
|
|
11
|
+
raise ChefSpec::Error::CommandNotStubbed.new(args: [@command])
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
stub.result
|
|
15
|
+
end
|
|
16
|
+
end)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require "chef/cookbook/gem_installer"
|
|
2
|
+
|
|
3
|
+
Chef::Cookbook::GemInstaller.prepend(Module.new do
|
|
4
|
+
# Installs the gems into the omnibus gemset.
|
|
5
|
+
def install
|
|
6
|
+
return super unless $CHEFSPEC_MODE
|
|
7
|
+
|
|
8
|
+
cookbook_gems = Hash.new { |h, k| h[k] = [] }
|
|
9
|
+
|
|
10
|
+
cookbook_collection.each do |cookbook_name, cookbook_version|
|
|
11
|
+
cookbook_version.metadata.gems.each do |args|
|
|
12
|
+
cookbook_gems[args.first] += args[1..-1]
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
events.cookbook_gem_start(cookbook_gems)
|
|
17
|
+
cookbook_gems.each { |gem_name, gem_requirements| locate_gem(gem_name, gem_requirements) }
|
|
18
|
+
events.cookbook_gem_finished
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def locate_gem(gem_name, gem_requirements)
|
|
24
|
+
::Gem::Specification.find_by_name(gem_name, gem_requirements)
|
|
25
|
+
rescue ::Gem::MissingSpecError
|
|
26
|
+
gem_cmd = "gem install #{gem_name} --version '#{gem_requirements.join(", ")}'"
|
|
27
|
+
gemfile_line = "gem '#{[gem_name, *gem_requirements].join("', '")}'"
|
|
28
|
+
warn "No matching version found for '#{gem_name}' in your gem environment.\n" \
|
|
29
|
+
" - if you are using Chef Workstation, run the following command: \"chef #{gem_cmd}\"\n" \
|
|
30
|
+
" - if you are using bundler, append \"#{gemfile_line}\" to your Gemfile and run \"bundle install\"\n" \
|
|
31
|
+
" - otherwise run: \"#{gem_cmd}\""
|
|
32
|
+
end
|
|
33
|
+
end)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require "chef/cookbook_loader"
|
|
2
|
+
|
|
3
|
+
Chef::CookbookLoader.prepend(Module.new do
|
|
4
|
+
def all_directories_in_repo_paths
|
|
5
|
+
return super unless $CHEFSPEC_MODE
|
|
6
|
+
|
|
7
|
+
if Chef::Config[:chefspec_cookbook_root]
|
|
8
|
+
# Hax.
|
|
9
|
+
[Chef::Config[:chefspec_cookbook_root]]
|
|
10
|
+
else
|
|
11
|
+
super
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require "chef/cookbook_uploader"
|
|
2
|
+
|
|
3
|
+
Chef::CookbookUploader.prepend(Module.new do |variable|
|
|
4
|
+
#
|
|
5
|
+
# Don't validate uploaded cookbooks. Validating a cookbook takes *forever*
|
|
6
|
+
# to complete. It's just not worth it...
|
|
7
|
+
#
|
|
8
|
+
def validate_cookbooks
|
|
9
|
+
return super unless $CHEFSPEC_MODE
|
|
10
|
+
# noop
|
|
11
|
+
end
|
|
12
|
+
end)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require "chef/dsl/data_query"
|
|
2
|
+
|
|
3
|
+
Chef::DSL::DataQuery.prepend(Module.new do
|
|
4
|
+
# @see Chef::DSL::DataQuery#search
|
|
5
|
+
def search(*args, &block)
|
|
6
|
+
return super unless Chef::Config[:solo] && $CHEFSPEC_MODE
|
|
7
|
+
|
|
8
|
+
type = args[0]
|
|
9
|
+
query = args[1] || "*:*"
|
|
10
|
+
stub = ChefSpec::Stubs::SearchRegistry.stub_for(type, query)
|
|
11
|
+
|
|
12
|
+
if stub.nil?
|
|
13
|
+
raise ChefSpec::Error::SearchNotStubbed.new(args: [type, query])
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
if block
|
|
17
|
+
Array(stub.result).each { |r| block.call(r) }
|
|
18
|
+
true
|
|
19
|
+
else
|
|
20
|
+
stub.result
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @see Chef::DSL::DataQuery#data_bag
|
|
25
|
+
def data_bag(bag)
|
|
26
|
+
return super unless Chef::Config[:solo] && $CHEFSPEC_MODE
|
|
27
|
+
|
|
28
|
+
stub = ChefSpec::Stubs::DataBagRegistry.stub_for(bag)
|
|
29
|
+
|
|
30
|
+
if stub.nil?
|
|
31
|
+
raise ChefSpec::Error::DataBagNotStubbed.new(args: [bag])
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
stub.result
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @see Chef::DSL::DataQuery#data_bag_item
|
|
38
|
+
def data_bag_item(bag, id, secret = nil)
|
|
39
|
+
return super unless Chef::Config[:solo] && $CHEFSPEC_MODE
|
|
40
|
+
|
|
41
|
+
stub = ChefSpec::Stubs::DataBagItemRegistry.stub_for(bag, id)
|
|
42
|
+
|
|
43
|
+
if stub.nil?
|
|
44
|
+
raise ChefSpec::Error::DataBagItemNotStubbed.new(args: [bag, id])
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
stub.result
|
|
48
|
+
end
|
|
49
|
+
end)
|