chefspec 3.0.2 → 3.1.0.beta.1
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 +4 -4
- data/lib/chefspec.rb +3 -4
- data/lib/chefspec/cacher.rb +59 -0
- data/lib/chefspec/coverage.rb +144 -0
- data/lib/chefspec/deprecations.rb +3 -0
- data/lib/chefspec/extensions/chef/data_query.rb +9 -0
- data/lib/chefspec/extensions/chef/lwrp_base.rb +51 -36
- data/lib/chefspec/extensions/chef/resource.rb +2 -0
- data/lib/chefspec/librarian.rb +49 -0
- data/lib/chefspec/matchers/notifications_matcher.rb +5 -3
- data/lib/chefspec/matchers/resource_matcher.rb +1 -0
- data/lib/chefspec/mixins/normalize.rb +16 -0
- data/lib/chefspec/renderer.rb +6 -4
- data/lib/chefspec/runner.rb +11 -5
- data/lib/chefspec/server.rb +200 -0
- data/lib/chefspec/version.rb +1 -1
- metadata +25 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ecd2e2844bf5859e413abf186d1f71ed0f5af281
|
4
|
+
data.tar.gz: 72f1818ef26032cc0cb039bf3f51923eaae47800
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac4ce6148c62de28889fd679ca8ba7306ec0f9be180b3bb81fbeb752c7103285fc47a7091865bbc4c28e27d608bd3d94d485caaeb56a2998b12727294c83c47b
|
7
|
+
data.tar.gz: ca086443be533996952f5dd17aa634bf520a8a46e4f972f0d1374481365642eca928915e1713c30cea40b92d4409c7183a314466f372d98e008ae6fc37913e77
|
data/lib/chefspec.rb
CHANGED
@@ -7,6 +7,8 @@ require_relative 'chefspec/extensions/chef/lwrp_base'
|
|
7
7
|
require_relative 'chefspec/extensions/chef/resource'
|
8
8
|
require_relative 'chefspec/extensions/chef/securable'
|
9
9
|
|
10
|
+
require_relative 'chefspec/mixins/normalize'
|
11
|
+
|
10
12
|
require_relative 'chefspec/stubs/command_registry'
|
11
13
|
require_relative 'chefspec/stubs/command_stub'
|
12
14
|
require_relative 'chefspec/stubs/data_bag_item_registry'
|
@@ -19,6 +21,7 @@ require_relative 'chefspec/stubs/search_registry'
|
|
19
21
|
require_relative 'chefspec/stubs/search_stub'
|
20
22
|
|
21
23
|
require_relative 'chefspec/api'
|
24
|
+
require_relative 'chefspec/coverage'
|
22
25
|
require_relative 'chefspec/errors'
|
23
26
|
require_relative 'chefspec/expect_exception'
|
24
27
|
require_relative 'chefspec/formatter'
|
@@ -28,7 +31,3 @@ require_relative 'chefspec/renderer'
|
|
28
31
|
require_relative 'chefspec/rspec'
|
29
32
|
require_relative 'chefspec/runner'
|
30
33
|
require_relative 'chefspec/version'
|
31
|
-
|
32
|
-
# Load deprecations module last, so it can monkey patch and print out nasty
|
33
|
-
# deprecation warnings for us :)
|
34
|
-
require_relative 'chefspec/deprecations'
|
@@ -0,0 +1,59 @@
|
|
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[:example_group][:location]
|
38
|
+
|
39
|
+
define_method(name) do
|
40
|
+
key = [location, name.to_s].join('.')
|
41
|
+
unless @@cache.has_key?(Thread.current.object_id)
|
42
|
+
ObjectSpace.define_finalizer(Thread.current, FINALIZER)
|
43
|
+
end
|
44
|
+
@@cache[Thread.current.object_id] ||= {}
|
45
|
+
@@cache[Thread.current.object_id][key] ||= instance_eval(&block)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def cached!(name, &block)
|
50
|
+
cached(name, &block)
|
51
|
+
|
52
|
+
before { send(name) }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
RSpec.configure do |config|
|
58
|
+
config.extend(ChefSpec::Cacher)
|
59
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module ChefSpec
|
2
|
+
class Coverage
|
3
|
+
class << self
|
4
|
+
extend Forwardable
|
5
|
+
def_delegators :instance, :add, :cover!, :report!
|
6
|
+
end
|
7
|
+
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
#
|
11
|
+
# Create a new coverage object singleton.
|
12
|
+
#
|
13
|
+
def initialize
|
14
|
+
@collection = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# Add a resource to the resource collection.
|
19
|
+
#
|
20
|
+
# @param [Chef::Resource] resource
|
21
|
+
#
|
22
|
+
def add(resource)
|
23
|
+
@collection[resource.to_s] = ResourceWrapper.new(resource)
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Called when a resource is matched to indicate it has been tested.
|
28
|
+
#
|
29
|
+
# @param [Chef::Resource] resource
|
30
|
+
#
|
31
|
+
def cover!(resource)
|
32
|
+
if wrapper = find(resource)
|
33
|
+
wrapper.touch!
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Generate a coverage report. This report **must** be generated +at_exit+
|
39
|
+
# or else the entire resource collection may not be complete!
|
40
|
+
#
|
41
|
+
# @example Generating a report
|
42
|
+
#
|
43
|
+
# at_exit { ChefSpec::Coverage.report! }
|
44
|
+
#
|
45
|
+
# @example Generating a custom report without announcing
|
46
|
+
#
|
47
|
+
# at_exit { ChefSpec::Coverage.report!('/custom/path', false) }
|
48
|
+
#
|
49
|
+
#
|
50
|
+
# @param [String] output
|
51
|
+
# the path to output the report on disk (default: '.coverage/results.json')
|
52
|
+
# @param [Boolean] announce
|
53
|
+
# print the results to standard out
|
54
|
+
#
|
55
|
+
def report!(output = '.coverage/results.json', announce = true)
|
56
|
+
report = {}
|
57
|
+
|
58
|
+
report[:total] = @collection.size
|
59
|
+
report[:touched] = @collection.count { |_, resource| resource.touched? }
|
60
|
+
report[:untouched] = report[:total] - report[:touched]
|
61
|
+
report[:coverage] = ((report[:touched].to_f/report[:total].to_f)*100).round(2)
|
62
|
+
|
63
|
+
report[:detailed] = Hash[*@collection.map do |name, wrapper|
|
64
|
+
[name, wrapper.to_hash]
|
65
|
+
end.flatten]
|
66
|
+
|
67
|
+
output = File.expand_path(output)
|
68
|
+
FileUtils.mkdir_p(File.dirname(output))
|
69
|
+
File.open(File.join(output), 'w') do |f|
|
70
|
+
f.write(JSON.pretty_generate(report) + "\n")
|
71
|
+
end
|
72
|
+
|
73
|
+
if announce
|
74
|
+
puts <<-EOH.gsub(/^ {10}/, '')
|
75
|
+
|
76
|
+
WARNING: ChefSpec Coverage reporting is in beta. Please use with caution.
|
77
|
+
|
78
|
+
ChefSpec Coverage report generated at '#{output}':
|
79
|
+
|
80
|
+
Total Resources: #{report[:total]}
|
81
|
+
Touched Resources: #{report[:touched]}
|
82
|
+
Touch Coverage: #{report[:coverage]}%
|
83
|
+
|
84
|
+
Untouched Resources:
|
85
|
+
|
86
|
+
#{
|
87
|
+
report[:detailed]
|
88
|
+
.select { |_, resource| !resource[:touched] }
|
89
|
+
.sort_by { |_, resource| [resource[:source][:file], resource[:source][:line]] }
|
90
|
+
.map do |name, resource|
|
91
|
+
" #{name} #{resource[:source][:file]}:#{resource[:source][:line]}"
|
92
|
+
end
|
93
|
+
.flatten
|
94
|
+
.join("\n")
|
95
|
+
}
|
96
|
+
|
97
|
+
EOH
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def find(resource)
|
104
|
+
@collection[resource.to_s]
|
105
|
+
end
|
106
|
+
|
107
|
+
class ResourceWrapper
|
108
|
+
attr_reader :resource
|
109
|
+
|
110
|
+
def initialize(resource = nil)
|
111
|
+
@resource = resource
|
112
|
+
end
|
113
|
+
|
114
|
+
def to_s
|
115
|
+
@resource.to_s
|
116
|
+
end
|
117
|
+
|
118
|
+
def source
|
119
|
+
return {} unless @resource.source_line
|
120
|
+
file, line, *_ = @resource.source_line.split(':')
|
121
|
+
|
122
|
+
{
|
123
|
+
file: file,
|
124
|
+
line: line.to_i,
|
125
|
+
}
|
126
|
+
end
|
127
|
+
|
128
|
+
def to_hash
|
129
|
+
{
|
130
|
+
source: source,
|
131
|
+
touched: touched?,
|
132
|
+
}
|
133
|
+
end
|
134
|
+
|
135
|
+
def touch!
|
136
|
+
@touched = true
|
137
|
+
end
|
138
|
+
|
139
|
+
def touched?
|
140
|
+
!!@touched
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -2,7 +2,10 @@ require 'chef/dsl/data_query'
|
|
2
2
|
|
3
3
|
module Chef::DSL::DataQuery
|
4
4
|
# @see Chef::DSL::DataQuery#search
|
5
|
+
alias_method :old_search, :search
|
5
6
|
def search(*args, &block)
|
7
|
+
return old_search(*args, &block) unless Chef::Config[:solo]
|
8
|
+
|
6
9
|
type = args[0]
|
7
10
|
query = args[1] || '*:*'
|
8
11
|
stub = ChefSpec::Stubs::SearchRegistry.stub_for(type, query)
|
@@ -12,7 +15,10 @@ module Chef::DSL::DataQuery
|
|
12
15
|
end
|
13
16
|
|
14
17
|
# @see Chef::DSL::DataQuery#data_bag
|
18
|
+
alias_method :old_data_bag, :data_bag
|
15
19
|
def data_bag(bag)
|
20
|
+
return old_data_bag(bag) unless Chef::Config[:solo]
|
21
|
+
|
16
22
|
stub = ChefSpec::Stubs::DataBagRegistry.stub_for(bag)
|
17
23
|
raise ChefSpec::DataBagNotStubbedError.new(bag) if stub.nil?
|
18
24
|
|
@@ -20,7 +26,10 @@ module Chef::DSL::DataQuery
|
|
20
26
|
end
|
21
27
|
|
22
28
|
# @see Chef::DSL::DataQuery#data_bag_item
|
29
|
+
alias_method :old_data_bag_item, :data_bag_item
|
23
30
|
def data_bag_item(bag, id)
|
31
|
+
return old_data_bag_item(bag, id) unless Chef::Config[:solo]
|
32
|
+
|
24
33
|
stub = ChefSpec::Stubs::DataBagItemRegistry.stub_for(bag, id)
|
25
34
|
raise ChefSpec::DataBagItemNotStubbedError.new(bag, id) if stub.nil?
|
26
35
|
|
@@ -1,46 +1,54 @@
|
|
1
1
|
# Override Chef LWRP creation to remove existing class to avoid redefinition warnings.
|
2
2
|
class Chef
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
alias_method :
|
3
|
+
module RemoveExistingLWRP
|
4
|
+
def self.extended(klass)
|
5
|
+
class << klass
|
6
|
+
alias_method :build_from_file_without_removal, :build_from_file
|
7
|
+
alias_method :build_from_file, :build_from_file_with_removal
|
8
|
+
end
|
9
|
+
end
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
11
|
+
#
|
12
|
+
# Override Opscode provider to remove any existing LWRPs to suppress
|
13
|
+
# constant re-definition warnings.
|
14
|
+
#
|
15
|
+
# @param [String] cookbook_name
|
16
|
+
# the name of the cookbook
|
17
|
+
# @param [String] filename
|
18
|
+
# file to load as a LWRP
|
19
|
+
# @param [Chef::RunContext] run_context
|
20
|
+
# context of a Chef Run
|
21
|
+
#
|
22
|
+
# @return [Chef::Provider]
|
23
|
+
#
|
24
|
+
def build_from_file_with_removal(cookbook_name, filename, run_context)
|
25
|
+
provider_name = filename_to_qualified_string(cookbook_name, filename)
|
26
|
+
class_name = convert_to_class_name(provider_name)
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
-
|
28
|
+
remove_existing_lwrp(class_name)
|
29
|
+
build_from_file_without_removal(cookbook_name, filename, run_context)
|
30
|
+
end
|
29
31
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
end
|
32
|
+
#
|
33
|
+
# Remove any existing Chef provider or resource with the specified name.
|
34
|
+
#
|
35
|
+
# @param [String] class_name
|
36
|
+
# The class name. Must be a valid constant name.
|
37
|
+
#
|
38
|
+
def remove_existing_lwrp(class_name)
|
39
|
+
[self, superclass].each do |resource_holder|
|
40
|
+
look_in_parents = false
|
41
|
+
if resource_holder.const_defined?(class_name, look_in_parents)
|
42
|
+
resource_holder.send(:remove_const, class_name)
|
42
43
|
end
|
43
44
|
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Provider
|
49
|
+
# Chef provider for a resource
|
50
|
+
class LWRPBase < Provider
|
51
|
+
extend RemoveExistingLWRP
|
44
52
|
|
45
53
|
module InlineResources
|
46
54
|
module ClassMethods
|
@@ -60,4 +68,11 @@ class Chef
|
|
60
68
|
end
|
61
69
|
end
|
62
70
|
end
|
71
|
+
|
72
|
+
class Resource
|
73
|
+
# Chef provider for a resource
|
74
|
+
class LWRPBase < Resource
|
75
|
+
extend RemoveExistingLWRP
|
76
|
+
end
|
77
|
+
end
|
63
78
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
begin
|
2
|
+
require 'librarian/chef/environment'
|
3
|
+
require 'librarian/action/resolve'
|
4
|
+
require 'librarian/action/install'
|
5
|
+
rescue LoadError
|
6
|
+
raise RuntimeError, "Librarian not found! You must have the librarian-chef" \
|
7
|
+
" gem installed on your system before requiring chefspec/librarian." \
|
8
|
+
" Install it by running:\n\n gem install librarian-chef\n\nor add" \
|
9
|
+
" Librarian to your Gemfile:\n\n gem 'librarian-chef'\n\n"
|
10
|
+
end
|
11
|
+
|
12
|
+
module ChefSpec
|
13
|
+
class Librarian
|
14
|
+
class << self
|
15
|
+
extend Forwardable
|
16
|
+
def_delegators :instance, :setup!, :teardown!
|
17
|
+
end
|
18
|
+
|
19
|
+
include Singleton
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@tmpdir = Dir.mktmpdir
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# Setup and install the necessary dependencies in the temporary directory.
|
27
|
+
#
|
28
|
+
def setup!
|
29
|
+
env = ::Librarian::Chef::Environment.new(project_path: Dir.pwd)
|
30
|
+
env.config_db.local['path'] = @tmpdir
|
31
|
+
::Librarian::Action::Resolve.new(env).run
|
32
|
+
::Librarian::Action::Install.new(env).run
|
33
|
+
|
34
|
+
::RSpec.configure { |config| config.cookbook_path = @tmpdir }
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Remove the temporary directory.
|
39
|
+
#
|
40
|
+
def teardown!
|
41
|
+
FileUtils.rm_rf(@tmpdir) if File.exists?(@tmpdir)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
RSpec.configure do |config|
|
47
|
+
config.before(:suite) { ChefSpec::Librarian.setup! }
|
48
|
+
config.after(:suite) { ChefSpec::Librarian.teardown! }
|
49
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module ChefSpec::Matchers
|
2
2
|
class NotificationsMatcher
|
3
|
+
include ChefSpec::Normalize
|
4
|
+
|
3
5
|
def initialize(signature)
|
4
6
|
signature.match(/^([^\[]*)\[(.*)\]$/)
|
5
7
|
@expected_resource_type = $1
|
@@ -11,7 +13,7 @@ module ChefSpec::Matchers
|
|
11
13
|
|
12
14
|
if @resource
|
13
15
|
block = Proc.new do |notified|
|
14
|
-
notified.resource.
|
16
|
+
resource_name(notified.resource).to_s == @expected_resource_type &&
|
15
17
|
(@expected_resource_name === notified.resource.identity.to_s || @expected_resource_name === notified.resource.name.to_s) &&
|
16
18
|
matches_action?(notified)
|
17
19
|
end
|
@@ -51,7 +53,7 @@ module ChefSpec::Matchers
|
|
51
53
|
|
52
54
|
def failure_message_for_should
|
53
55
|
if @resource
|
54
|
-
message = %Q{expected "#{@resource
|
56
|
+
message = %Q{expected "#{resource_name(@resource)}[#{@resource.name}]" to notify "#{@expected_resource_type}[#{@expected_resource_name}]"}
|
55
57
|
message << " with action :#{@action}" if @action
|
56
58
|
message << " immediately" if @immediately
|
57
59
|
message << " delayed" if @delayed
|
@@ -98,7 +100,7 @@ module ChefSpec::Matchers
|
|
98
100
|
resource = notification.resource
|
99
101
|
type = notification.notifying_resource.immediate_notifications.include?(notification) ? :immediately : :delayed
|
100
102
|
|
101
|
-
%Q{ "#{notifying_resource.to_s}" notifies "#{resource
|
103
|
+
%Q{ "#{notifying_resource.to_s}" notifies "#{resource_name(resource)}[#{resource.name}]" to :#{notification.action}, :#{type}}
|
102
104
|
end
|
103
105
|
|
104
106
|
def format_notifications
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ChefSpec
|
2
|
+
module Normalize
|
3
|
+
#
|
4
|
+
# Calculate the name of a resource, replacing dashes with underscores
|
5
|
+
# and converting symbols to strings and back again.
|
6
|
+
#
|
7
|
+
# @param [String, Chef::Resource] thing
|
8
|
+
#
|
9
|
+
# @return [Symbol]
|
10
|
+
#
|
11
|
+
def resource_name(thing)
|
12
|
+
name = thing.respond_to?(:resource_name) ? thing.resource_name : thing
|
13
|
+
name.to_s.gsub('-', '_').to_sym
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/chefspec/renderer.rb
CHANGED
@@ -6,6 +6,8 @@ end
|
|
6
6
|
|
7
7
|
module ChefSpec
|
8
8
|
class Renderer
|
9
|
+
include ChefSpec::Normalize
|
10
|
+
|
9
11
|
# @return [Chef::Runner]
|
10
12
|
attr_reader :chef_run
|
11
13
|
|
@@ -34,12 +36,12 @@ module ChefSpec
|
|
34
36
|
# does not contain or respond to a content renderer.
|
35
37
|
#
|
36
38
|
def content
|
37
|
-
case resource
|
38
|
-
when
|
39
|
+
case resource_name(resource)
|
40
|
+
when :template
|
39
41
|
content_from_template(chef_run, resource)
|
40
|
-
when
|
42
|
+
when :file
|
41
43
|
content_from_file(chef_run, resource)
|
42
|
-
when
|
44
|
+
when :cookbook_file
|
43
45
|
content_from_cookbook_file(chef_run, resource)
|
44
46
|
else
|
45
47
|
nil
|
data/lib/chefspec/runner.rb
CHANGED
@@ -6,6 +6,8 @@ require 'chef/resources'
|
|
6
6
|
|
7
7
|
module ChefSpec
|
8
8
|
class Runner
|
9
|
+
include ChefSpec::Normalize
|
10
|
+
|
9
11
|
#
|
10
12
|
# Defines a new runner method on the +ChefSpec::Runner+.
|
11
13
|
#
|
@@ -149,8 +151,12 @@ module ChefSpec
|
|
149
151
|
expand_run_list!
|
150
152
|
|
151
153
|
# Setup the run_context
|
154
|
+
client.register unless Chef::Config[:solo]
|
152
155
|
@run_context = client.setup_run_context
|
153
156
|
|
157
|
+
# Allow stubbing/mocking after the cookbook has been compiled but before the converge
|
158
|
+
yield if block_given?
|
159
|
+
|
154
160
|
@converging = true
|
155
161
|
@client.converge(@run_context)
|
156
162
|
self
|
@@ -201,7 +207,7 @@ module ChefSpec
|
|
201
207
|
rescue Chef::Exceptions::ResourceNotFound; end
|
202
208
|
|
203
209
|
resource_collection.all_resources.find do |resource|
|
204
|
-
resource
|
210
|
+
resource_name(resource) == type && (name === resource.identity || name === resource.name)
|
205
211
|
end
|
206
212
|
end
|
207
213
|
|
@@ -209,7 +215,7 @@ module ChefSpec
|
|
209
215
|
# Find the resource with the declared type.
|
210
216
|
#
|
211
217
|
# @example Find all template resources
|
212
|
-
# chef_run.find_resources(
|
218
|
+
# chef_run.find_resources(:template) #=> [#<Chef::Resource::Template>, #...]
|
213
219
|
#
|
214
220
|
#
|
215
221
|
# @param [Symbol] type
|
@@ -220,7 +226,7 @@ module ChefSpec
|
|
220
226
|
#
|
221
227
|
def find_resources(type)
|
222
228
|
resource_collection.all_resources.select do |resource|
|
223
|
-
resource
|
229
|
+
resource_name(resource) == type.to_sym
|
224
230
|
end
|
225
231
|
end
|
226
232
|
|
@@ -249,8 +255,8 @@ module ChefSpec
|
|
249
255
|
# @return [Boolean]
|
250
256
|
#
|
251
257
|
def step_into?(resource)
|
252
|
-
key =
|
253
|
-
Array(options[:step_into]).map(
|
258
|
+
key = resource_name(resource)
|
259
|
+
Array(options[:step_into]).map(&method(:resource_name)).include?(key)
|
254
260
|
end
|
255
261
|
|
256
262
|
#
|
@@ -0,0 +1,200 @@
|
|
1
|
+
begin
|
2
|
+
require 'chef_zero/server'
|
3
|
+
rescue LoadError
|
4
|
+
raise RuntimeError, "chef-zero not found! You must have the chef-zero gem" \
|
5
|
+
" installed on your system before requiring chefspec/server. Install" \
|
6
|
+
" Chef Zero by running:\n\n gem install chef-zero\n\nor add Chef Zero" \
|
7
|
+
" to your Gemfile:\n\n gem 'chef-zero'\n\n"
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'chef/cookbook_loader'
|
11
|
+
require 'chef/cookbook_uploader'
|
12
|
+
|
13
|
+
class Chef::CookbookUploader
|
14
|
+
#
|
15
|
+
# Don't validate uploaded cookbooks. Validating a cookbook takes *forever*
|
16
|
+
# to complete. It's just not worth it...
|
17
|
+
#
|
18
|
+
def validate_cookbooks
|
19
|
+
# noop
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class ChefSpec::Runner
|
24
|
+
alias_method :old_initialize, :initialize
|
25
|
+
|
26
|
+
#
|
27
|
+
# Override the existing initialize method, setting the appropriate
|
28
|
+
# configuration to use a real Chef Server instead.
|
29
|
+
#
|
30
|
+
# @see ChefSpec::Runner#initialize
|
31
|
+
#
|
32
|
+
def initialize(options = {}, &block)
|
33
|
+
old_initialize(options, &block)
|
34
|
+
|
35
|
+
Chef::Config[:client_key] = ChefSpec::Server.client_key
|
36
|
+
Chef::Config[:client_name] = 'chefspec'
|
37
|
+
Chef::Config[:node_name] = 'chefspec'
|
38
|
+
Chef::Config[:file_cache_path] = Dir.mktmpdir
|
39
|
+
Chef::Config[:solo] = false
|
40
|
+
|
41
|
+
upload_cookbooks!
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
#
|
47
|
+
# Upload the cookbooks to the Chef Server.
|
48
|
+
#
|
49
|
+
def upload_cookbooks!
|
50
|
+
loader = Chef::CookbookLoader.new(Chef::Config[:cookbook_path])
|
51
|
+
loader.load_cookbooks
|
52
|
+
|
53
|
+
uploader = Chef::CookbookUploader.new(loader.cookbooks, loader.cookbook_paths)
|
54
|
+
uploader.upload_cookbooks
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module ChefSpec
|
59
|
+
class Server
|
60
|
+
#
|
61
|
+
# Delegate all methods to the singleton instance.
|
62
|
+
#
|
63
|
+
def self.method_missing(m, *args, &block)
|
64
|
+
instance.send(m, *args, &block)
|
65
|
+
end
|
66
|
+
|
67
|
+
include Singleton
|
68
|
+
|
69
|
+
attr_reader :server
|
70
|
+
|
71
|
+
#
|
72
|
+
# Create a new instance of the +ChefSpec::Server+ singleton. This method
|
73
|
+
# also starts the Chef Zero server in the background.
|
74
|
+
#
|
75
|
+
def initialize
|
76
|
+
@server = ChefZero::Server.new(
|
77
|
+
log_level: RSpec.configuration.log_level || :warn,
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# The path to the insecure Chef Zero private key on disk. Because Chef
|
83
|
+
# requires the path to a file instead of the contents of the key (why),
|
84
|
+
# this method dynamically writes the +ChefZero::PRIVATE_KEY+ to disk and
|
85
|
+
# then returns that path.
|
86
|
+
#
|
87
|
+
# @return [String]
|
88
|
+
# the path to the client key on disk
|
89
|
+
#
|
90
|
+
def client_key
|
91
|
+
@client_key ||= begin
|
92
|
+
path = File.join(cache_dir, 'client.pem')
|
93
|
+
File.open(path, 'w') { |f| f.write(ChefZero::PRIVATE_KEY) }
|
94
|
+
path
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
#
|
99
|
+
# Start the Chef Zero server in the background, updating the +Chef::Config+
|
100
|
+
# with the proper +chef_server_url+.
|
101
|
+
#
|
102
|
+
def start!
|
103
|
+
unless @server.running?
|
104
|
+
@server.start_background
|
105
|
+
Chef::Config[:chef_server_url] = @server.url
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# Clear the contents of the server (used between examples)
|
111
|
+
#
|
112
|
+
def reset!
|
113
|
+
@server.clear_data
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
# Stop the Chef Zero server, if it is running. This method also runs any
|
118
|
+
# cleanup hooks, such as clearing the cache directories.
|
119
|
+
#
|
120
|
+
def stop!
|
121
|
+
@server.stop if @server.running?
|
122
|
+
FileUtils.rm_rf(cache_dir)
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
# Create a client on the Chef Server.
|
127
|
+
#
|
128
|
+
def create_client(name, data = {})
|
129
|
+
load_data(:clients, name, data)
|
130
|
+
end
|
131
|
+
|
132
|
+
#
|
133
|
+
# Create a data_bag on the Chef Server.
|
134
|
+
#
|
135
|
+
def create_data_bag(name, data = {})
|
136
|
+
@server.load_data('data' => { name => data })
|
137
|
+
end
|
138
|
+
|
139
|
+
#
|
140
|
+
# Create an environment on the Chef Server.
|
141
|
+
#
|
142
|
+
def create_environment(name, data = {})
|
143
|
+
load_data(:environments, name, data)
|
144
|
+
end
|
145
|
+
|
146
|
+
#
|
147
|
+
# Create a node on the Chef Server.
|
148
|
+
#
|
149
|
+
# @note
|
150
|
+
# The current node (chefspec) is automatically registered and added to
|
151
|
+
# the Chef Server
|
152
|
+
#
|
153
|
+
def create_node(name, data = {})
|
154
|
+
load_data(:nodes, name, data)
|
155
|
+
end
|
156
|
+
|
157
|
+
#
|
158
|
+
# Create a role on the Chef Server.
|
159
|
+
#
|
160
|
+
def create_role(name, data = {})
|
161
|
+
load_data(:roles, name, data)
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
#
|
167
|
+
# The directory where any cache information (such as private keys) should
|
168
|
+
# be stored. This cache is destroyed at the end of the run.
|
169
|
+
#
|
170
|
+
# @return [String]
|
171
|
+
# the path to the cache directory on disk
|
172
|
+
#
|
173
|
+
def cache_dir
|
174
|
+
@cache_dir ||= Dir.mktmpdir(['chefspec', 'cache'])
|
175
|
+
end
|
176
|
+
|
177
|
+
#
|
178
|
+
# Shortcut method for loading data into Chef Zero.
|
179
|
+
#
|
180
|
+
# @param [String, Symbol] key
|
181
|
+
# the key to load
|
182
|
+
# @param [String] name
|
183
|
+
# the name or id of the item to load
|
184
|
+
# @param [Hash] data
|
185
|
+
# the data for the object, which will be converted to JSON and uploaded
|
186
|
+
# to the server
|
187
|
+
#
|
188
|
+
def load_data(key, name, data = {})
|
189
|
+
@server.load_data(key.to_s => { name => JSON.fast_generate(data) })
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
ChefSpec::Server.start!
|
195
|
+
|
196
|
+
RSpec.configure do |config|
|
197
|
+
config.after(:each) { ChefSpec::Server.reset! }
|
198
|
+
end
|
199
|
+
|
200
|
+
at_exit { ChefSpec::Server.stop! }
|
data/lib/chefspec/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chefspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.1.0.beta.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Crump
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-12-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: chef
|
@@ -53,6 +53,20 @@ dependencies:
|
|
53
53
|
- - ~>
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: '2.14'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: chef-zero
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '1.7'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.7'
|
56
70
|
- !ruby/object:Gem::Dependency
|
57
71
|
name: rake
|
58
72
|
requirement: !ruby/object:Gem::Requirement
|
@@ -165,6 +179,8 @@ files:
|
|
165
179
|
- lib/chefspec/api/yum_package.rb
|
166
180
|
- lib/chefspec/api.rb
|
167
181
|
- lib/chefspec/berkshelf.rb
|
182
|
+
- lib/chefspec/cacher.rb
|
183
|
+
- lib/chefspec/coverage.rb
|
168
184
|
- lib/chefspec/deprecations.rb
|
169
185
|
- lib/chefspec/errors.rb
|
170
186
|
- lib/chefspec/expect_exception.rb
|
@@ -175,6 +191,7 @@ files:
|
|
175
191
|
- lib/chefspec/extensions/chef/resource.rb
|
176
192
|
- lib/chefspec/extensions/chef/securable.rb
|
177
193
|
- lib/chefspec/formatter.rb
|
194
|
+
- lib/chefspec/librarian.rb
|
178
195
|
- lib/chefspec/macros.rb
|
179
196
|
- lib/chefspec/matchers/include_recipe_matcher.rb
|
180
197
|
- lib/chefspec/matchers/link_to_matcher.rb
|
@@ -183,9 +200,11 @@ files:
|
|
183
200
|
- lib/chefspec/matchers/resource_matcher.rb
|
184
201
|
- lib/chefspec/matchers/state_attrs_matcher.rb
|
185
202
|
- lib/chefspec/matchers.rb
|
203
|
+
- lib/chefspec/mixins/normalize.rb
|
186
204
|
- lib/chefspec/renderer.rb
|
187
205
|
- lib/chefspec/rspec.rb
|
188
206
|
- lib/chefspec/runner.rb
|
207
|
+
- lib/chefspec/server.rb
|
189
208
|
- lib/chefspec/stubs/command_registry.rb
|
190
209
|
- lib/chefspec/stubs/command_stub.rb
|
191
210
|
- lib/chefspec/stubs/data_bag_item_registry.rb
|
@@ -213,14 +232,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
213
232
|
version: '1.9'
|
214
233
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
215
234
|
requirements:
|
216
|
-
- - '
|
235
|
+
- - '>'
|
217
236
|
- !ruby/object:Gem::Version
|
218
|
-
version:
|
237
|
+
version: 1.3.1
|
219
238
|
requirements: []
|
220
239
|
rubyforge_project:
|
221
|
-
rubygems_version: 2.1.
|
240
|
+
rubygems_version: 2.1.11
|
222
241
|
signing_key:
|
223
242
|
specification_version: 4
|
224
|
-
summary: chefspec-3.0.
|
243
|
+
summary: chefspec-3.1.0.beta.1
|
225
244
|
test_files: []
|
226
245
|
has_rdoc:
|