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.
Files changed (120) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +30 -0
  3. data/LICENSE +22 -0
  4. data/Rakefile +85 -0
  5. data/chefspec-chef.gemspec +30 -0
  6. data/lib/chefspec/api/core.rb +217 -0
  7. data/lib/chefspec/api/described.rb +53 -0
  8. data/lib/chefspec/api/do_nothing.rb +26 -0
  9. data/lib/chefspec/api/include_any_recipe.rb +24 -0
  10. data/lib/chefspec/api/include_recipe.rb +28 -0
  11. data/lib/chefspec/api/link.rb +28 -0
  12. data/lib/chefspec/api/notifications.rb +40 -0
  13. data/lib/chefspec/api/reboot.rb +14 -0
  14. data/lib/chefspec/api/render_file.rb +37 -0
  15. data/lib/chefspec/api/state_attrs.rb +30 -0
  16. data/lib/chefspec/api/stubs.rb +183 -0
  17. data/lib/chefspec/api/stubs_for.rb +139 -0
  18. data/lib/chefspec/api/subscriptions.rb +37 -0
  19. data/lib/chefspec/api/user.rb +230 -0
  20. data/lib/chefspec/api.rb +39 -0
  21. data/lib/chefspec/berkshelf.rb +63 -0
  22. data/lib/chefspec/cacher.rb +64 -0
  23. data/lib/chefspec/coverage/filters.rb +82 -0
  24. data/lib/chefspec/coverage.rb +247 -0
  25. data/lib/chefspec/deprecations.rb +46 -0
  26. data/lib/chefspec/errors.rb +48 -0
  27. data/lib/chefspec/expect_exception.rb +51 -0
  28. data/lib/chefspec/extensions/chef/client.rb +21 -0
  29. data/lib/chefspec/extensions/chef/conditional.rb +16 -0
  30. data/lib/chefspec/extensions/chef/cookbook/gem_installer.rb +33 -0
  31. data/lib/chefspec/extensions/chef/cookbook_loader.rb +14 -0
  32. data/lib/chefspec/extensions/chef/cookbook_uploader.rb +12 -0
  33. data/lib/chefspec/extensions/chef/data_query.rb +49 -0
  34. data/lib/chefspec/extensions/chef/lwrp_base.rb +29 -0
  35. data/lib/chefspec/extensions/chef/provider.rb +39 -0
  36. data/lib/chefspec/extensions/chef/resource/freebsd_package.rb +17 -0
  37. data/lib/chefspec/extensions/chef/resource.rb +188 -0
  38. data/lib/chefspec/extensions/chef/run_context/cookbook_compiler.rb +84 -0
  39. data/lib/chefspec/extensions/chef/securable.rb +19 -0
  40. data/lib/chefspec/extensions/ohai/system.rb +11 -0
  41. data/lib/chefspec/extensions.rb +21 -0
  42. data/lib/chefspec/file_cache_path_proxy.rb +15 -0
  43. data/lib/chefspec/formatter.rb +282 -0
  44. data/lib/chefspec/librarian.rb +51 -0
  45. data/lib/chefspec/matchers/do_nothing_matcher.rb +52 -0
  46. data/lib/chefspec/matchers/include_any_recipe_matcher.rb +51 -0
  47. data/lib/chefspec/matchers/include_recipe_matcher.rb +46 -0
  48. data/lib/chefspec/matchers/link_to_matcher.rb +37 -0
  49. data/lib/chefspec/matchers/notifications_matcher.rb +143 -0
  50. data/lib/chefspec/matchers/render_file_matcher.rb +140 -0
  51. data/lib/chefspec/matchers/resource_matcher.rb +175 -0
  52. data/lib/chefspec/matchers/state_attrs_matcher.rb +71 -0
  53. data/lib/chefspec/matchers/subscribes_matcher.rb +72 -0
  54. data/lib/chefspec/matchers.rb +13 -0
  55. data/lib/chefspec/mixins/normalize.rb +22 -0
  56. data/lib/chefspec/policyfile.rb +69 -0
  57. data/lib/chefspec/renderer.rb +145 -0
  58. data/lib/chefspec/rspec.rb +21 -0
  59. data/lib/chefspec/runner.rb +8 -0
  60. data/lib/chefspec/server.rb +4 -0
  61. data/lib/chefspec/server_methods.rb +173 -0
  62. data/lib/chefspec/server_runner.rb +76 -0
  63. data/lib/chefspec/solo_runner.rb +516 -0
  64. data/lib/chefspec/stubs/command_registry.rb +11 -0
  65. data/lib/chefspec/stubs/command_stub.rb +37 -0
  66. data/lib/chefspec/stubs/data_bag_item_registry.rb +13 -0
  67. data/lib/chefspec/stubs/data_bag_item_stub.rb +25 -0
  68. data/lib/chefspec/stubs/data_bag_registry.rb +13 -0
  69. data/lib/chefspec/stubs/data_bag_stub.rb +23 -0
  70. data/lib/chefspec/stubs/registry.rb +32 -0
  71. data/lib/chefspec/stubs/search_registry.rb +13 -0
  72. data/lib/chefspec/stubs/search_stub.rb +25 -0
  73. data/lib/chefspec/stubs/stub.rb +38 -0
  74. data/lib/chefspec/util.rb +58 -0
  75. data/lib/chefspec/version.rb +3 -0
  76. data/lib/chefspec/zero_server.rb +142 -0
  77. data/lib/chefspec.rb +75 -0
  78. data/spec/spec_helper.rb +12 -0
  79. data/spec/support/hash.rb +35 -0
  80. data/spec/unit/cacher_spec.rb +70 -0
  81. data/spec/unit/coverage/filters_spec.rb +60 -0
  82. data/spec/unit/deprecations_spec.rb +52 -0
  83. data/spec/unit/errors_spec.rb +57 -0
  84. data/spec/unit/expect_exception_spec.rb +32 -0
  85. data/spec/unit/macros_spec.rb +119 -0
  86. data/spec/unit/matchers/do_nothing_matcher.rb +5 -0
  87. data/spec/unit/matchers/include_any_recipe_matcher_spec.rb +52 -0
  88. data/spec/unit/matchers/include_recipe_matcher_spec.rb +38 -0
  89. data/spec/unit/matchers/link_to_matcher_spec.rb +55 -0
  90. data/spec/unit/matchers/notifications_matcher_spec.rb +39 -0
  91. data/spec/unit/matchers/render_file_matcher_spec.rb +68 -0
  92. data/spec/unit/matchers/resource_matcher_spec.rb +5 -0
  93. data/spec/unit/matchers/state_attrs_matcher_spec.rb +68 -0
  94. data/spec/unit/matchers/subscribes_matcher_spec.rb +63 -0
  95. data/spec/unit/renderer_spec.rb +69 -0
  96. data/spec/unit/server_runner_spec.rb +28 -0
  97. data/spec/unit/solo_runner_spec.rb +171 -0
  98. data/spec/unit/stubs/command_registry_spec.rb +27 -0
  99. data/spec/unit/stubs/command_stub_spec.rb +61 -0
  100. data/spec/unit/stubs/data_bag_item_registry_spec.rb +39 -0
  101. data/spec/unit/stubs/data_bag_item_stub_spec.rb +36 -0
  102. data/spec/unit/stubs/data_bag_registry_spec.rb +39 -0
  103. data/spec/unit/stubs/data_bag_stub_spec.rb +35 -0
  104. data/spec/unit/stubs/registry_spec.rb +29 -0
  105. data/spec/unit/stubs/search_registry_spec.rb +39 -0
  106. data/spec/unit/stubs/search_stub_spec.rb +36 -0
  107. data/spec/unit/stubs/stub_spec.rb +64 -0
  108. data/templates/coverage/human.erb +22 -0
  109. data/templates/coverage/json.erb +8 -0
  110. data/templates/coverage/table.erb +14 -0
  111. data/templates/errors/cookbook_path_not_found.erb +3 -0
  112. data/templates/errors/erb_template_parse_error.erb +5 -0
  113. data/templates/errors/gem_load_error.erb +7 -0
  114. data/templates/errors/invalid_berkshelf_options.erb +4 -0
  115. data/templates/errors/may_need_to_specify_platform.erb +25 -0
  116. data/templates/errors/no_conversion_error.erb +1 -0
  117. data/templates/errors/not_stubbed.erb +7 -0
  118. data/templates/errors/shell_out_not_stubbed.erb +10 -0
  119. data/templates/errors/template_not_found.erb +9 -0
  120. metadata +221 -0
@@ -0,0 +1,140 @@
1
+ module ChefSpec::Matchers
2
+ class RenderFileMatcher
3
+ attr_reader :expected_content
4
+ def initialize(path)
5
+ @path = path
6
+ @expected_content = []
7
+ end
8
+
9
+ def matches?(runner)
10
+ @runner = runner
11
+
12
+ if resource
13
+ ChefSpec::Coverage.cover!(resource)
14
+ has_create_action? && matches_content?
15
+ else
16
+ false
17
+ end
18
+ end
19
+
20
+ def with_content(expected_content = nil, &block)
21
+ if expected_content && block
22
+ raise ArgumentError, "Cannot specify expected content and a block!"
23
+ elsif expected_content
24
+ @expected_content << expected_content
25
+ elsif block_given?
26
+ @expected_content << block
27
+ else
28
+ raise ArgumentError, "Must specify expected content or a block!"
29
+ end
30
+
31
+ self
32
+ end
33
+
34
+ def description
35
+ message = %Q{render file "#{@path}"}
36
+ @expected_content.each do |expected|
37
+ if expected.to_s.include?("\n")
38
+ message << " with content <suppressed>"
39
+ else
40
+ message << " with content #{expected.inspect}"
41
+ end
42
+ end
43
+ message
44
+ end
45
+
46
+ def failure_message
47
+ message = %Q{expected Chef run to render "#{@path}"}
48
+ unless @expected_content.empty?
49
+ message << " matching:"
50
+ message << "\n\n"
51
+ message << expected_content_message
52
+ message << "\n\n"
53
+ message << "but got:"
54
+ message << "\n\n"
55
+ message << @actual_content.to_s
56
+ message << "\n "
57
+ end
58
+ message
59
+ end
60
+
61
+ def failure_message_when_negated
62
+ message = %Q{expected file "#{@path}"}
63
+ unless @expected_content.empty?
64
+ message << " matching:"
65
+ message << "\n\n"
66
+ message << expected_content_message
67
+ message << "\n\n"
68
+ end
69
+ message << " to not be in Chef run"
70
+ message
71
+ end
72
+
73
+ private
74
+
75
+ def expected_content_message
76
+ messages = @expected_content.collect do |expected|
77
+ if RSpec::Matchers.is_a_matcher?(expected) && expected.respond_to?(:description)
78
+ expected.description
79
+ elsif expected.is_a?(Proc)
80
+ "(the result of a proc)"
81
+ else
82
+ expected.to_s
83
+ end
84
+ end
85
+ messages.join("\n\n")
86
+ end
87
+
88
+ def resource
89
+ @resource ||= @runner.find_resource(:cookbook_file, @path) ||
90
+ @runner.find_resource(:file, @path) ||
91
+ @runner.find_resource(:template, @path)
92
+ end
93
+
94
+ #
95
+ # Determines if the given resource has a create-like action.
96
+ #
97
+ # @param [Chef::Resource] resource
98
+ #
99
+ # @return [true, false]
100
+ #
101
+ def has_create_action?
102
+ %i{create create_if_missing}.any? { |action| resource.performed_action?(action) }
103
+ end
104
+
105
+ #
106
+ # Determines if the resources content matches the expected content.
107
+ #
108
+ # @param [Chef::Resource] resource
109
+ #
110
+ # @return [true, false]
111
+ #
112
+ def matches_content?
113
+ return true if @expected_content.empty?
114
+
115
+ @actual_content = ChefSpec::Renderer.new(@runner, resource).content
116
+
117
+ return false if @actual_content.nil?
118
+
119
+ # Knock out matches that pass. When we're done, we pass if the list is
120
+ # empty. Otherwise, @expected_content is the list of matchers that
121
+ # failed
122
+ @expected_content.delete_if do |expected|
123
+ if expected.is_a?(Regexp)
124
+ @actual_content =~ expected
125
+ elsif RSpec::Matchers.is_a_matcher?(expected)
126
+ expected.matches?(@actual_content)
127
+ elsif expected.is_a?(Proc)
128
+ expected.call(@actual_content)
129
+ # Weird RSpecish, but that block will return false for a negated check,
130
+ # so we always return true. The block will raise an exception if the
131
+ # assertion fails.
132
+ true
133
+ else
134
+ @actual_content.include?(expected)
135
+ end
136
+ end
137
+ @expected_content.empty?
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,175 @@
1
+ require "rspec/matchers/expecteds_for_multiple_diffs"
2
+ require "rspec/expectations/fail_with"
3
+
4
+ module ChefSpec::Matchers
5
+ class ResourceMatcher
6
+ def initialize(resource_name, expected_action, expected_identity)
7
+ @resource_name = resource_name
8
+ @expected_action = expected_action
9
+ @expected_identity = expected_identity
10
+ end
11
+
12
+ def with(parameters = {})
13
+ params.merge!(parameters)
14
+ self
15
+ end
16
+
17
+ def at_compile_time
18
+ raise ArgumentError, "Cannot specify both .at_converge_time and .at_compile_time!" if @converge_time
19
+
20
+ @compile_time = true
21
+ self
22
+ end
23
+
24
+ def at_converge_time
25
+ raise ArgumentError, "Cannot specify both .at_compile_time and .at_converge_time!" if @compile_time
26
+
27
+ @converge_time = true
28
+ self
29
+ end
30
+
31
+ #
32
+ # Allow users to specify fancy #with matchers.
33
+ #
34
+ def method_missing(m, *args, &block)
35
+ if m.to_s =~ /^with_(.+)$/
36
+ with($1.to_sym => args.first)
37
+ self
38
+ else
39
+ super
40
+ end
41
+ end
42
+
43
+ def description
44
+ %Q{#{@expected_action} #{@resource_name} "#{@expected_identity}"}
45
+ end
46
+
47
+ def matches?(runner)
48
+ @runner = runner
49
+
50
+ if resource
51
+ ChefSpec::Coverage.cover!(resource)
52
+ unmatched_parameters.empty? && correct_phase?
53
+ end
54
+ end
55
+
56
+ def failure_message
57
+ if resource
58
+ if unmatched_parameters.empty?
59
+ if @compile_time
60
+ %Q{expected "#{resource}" to be run at compile time}
61
+ else
62
+ %Q{expected "#{resource}" to be run at converge time}
63
+ end
64
+ else
65
+ message = %Q{expected "#{resource}" to have parameters:} \
66
+ "\n\n" \
67
+ " " + unmatched_parameters.collect { |parameter, h|
68
+ msg = "#{parameter} #{h[:expected].inspect}, was #{h[:actual].inspect}"
69
+ diff = ::RSpec::Matchers::ExpectedsForMultipleDiffs.from(h[:expected]) \
70
+ .message_with_diff(message, ::RSpec::Expectations.differ, h[:actual])
71
+ msg += diff if diff
72
+ msg
73
+ }.join("\n ")
74
+ end
75
+ else
76
+ %Q{expected "#{@resource_name}[#{@expected_identity}]"} \
77
+ " with action :#{@expected_action} to be in Chef run." \
78
+ " Other #{@resource_name} resources:" \
79
+ "\n\n" \
80
+ " " + similar_resources.map(&:to_s).join("\n ") + "\n "
81
+ end
82
+ end
83
+
84
+ def failure_message_when_negated
85
+ if resource
86
+ message = %Q{expected "#{resource}" actions #{resource.performed_actions.inspect} to not exist}
87
+ else
88
+ message = %Q{expected "#{resource}" to not exist}
89
+ end
90
+
91
+ message << " at compile time" if @compile_time
92
+ message << " at converge time" if @converge_time
93
+ message
94
+ end
95
+
96
+ private
97
+
98
+ def unmatched_parameters
99
+ return @_unmatched_parameters if @_unmatched_parameters
100
+
101
+ @_unmatched_parameters = {}
102
+
103
+ params.each do |parameter, expected|
104
+ unless matches_parameter?(parameter, expected)
105
+ @_unmatched_parameters[parameter] = {
106
+ expected: expected,
107
+ actual: safe_send(parameter),
108
+ }
109
+ end
110
+ end
111
+
112
+ @_unmatched_parameters
113
+ end
114
+
115
+ def matches_parameter?(parameter, expected)
116
+ value = safe_send(parameter)
117
+ if parameter == :source
118
+ # Chef 11+ stores the source parameter internally as an Array
119
+ Array(expected) == Array(value)
120
+ elsif expected.is_a?(Class)
121
+ # Ruby can't compare classes with ===
122
+ expected == value
123
+ else
124
+ expected === value
125
+ end
126
+ end
127
+
128
+ def correct_phase?
129
+ if @compile_time
130
+ resource.performed_action(@expected_action)[:compile_time]
131
+ elsif @converge_time
132
+ resource.performed_action(@expected_action)[:converge_time]
133
+ else
134
+ true
135
+ end
136
+ end
137
+
138
+ def safe_send(parameter)
139
+ resource.send(parameter)
140
+ rescue NoMethodError
141
+ nil
142
+ end
143
+
144
+ #
145
+ # Any other resources in the Chef run that have the same resource
146
+ # type. Used by {failure_message} to be ultra helpful.
147
+ #
148
+ # @return [Array<Chef::Resource>]
149
+ #
150
+ def similar_resources
151
+ @_similar_resources ||= @runner.find_resources(@resource_name)
152
+ end
153
+
154
+ #
155
+ # Find the resource in the Chef run by the given class name and
156
+ # resource identity/name.
157
+ #
158
+ # @see ChefSpec::SoloRunner#find_resource
159
+ #
160
+ # @return [Chef::Resource, nil]
161
+ #
162
+ def resource
163
+ @_resource ||= @runner.find_resource(@resource_name, @expected_identity, @expected_action)
164
+ end
165
+
166
+ #
167
+ # The list of parameters passed to the {with} matcher.
168
+ #
169
+ # @return [Hash]
170
+ #
171
+ def params
172
+ @_params ||= {}
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,71 @@
1
+ module ChefSpec::Matchers
2
+ class StateAttrsMatcher
3
+ #
4
+ # Create a new state_attrs matcher.
5
+ #
6
+ # @param [Array] state_attrs
7
+ #
8
+ def initialize(state_attrs)
9
+ @expected_attrs = state_attrs.map(&:to_sym)
10
+ end
11
+
12
+ def matches?(resource)
13
+ @resource = resource
14
+ @resource && matches_state_attrs?
15
+ end
16
+
17
+ def description
18
+ %Q{have state attributes #{@expected_attrs.inspect}}
19
+ end
20
+
21
+ def failure_message
22
+ if @resource
23
+ "expected #{state_attrs.inspect} to equal #{@expected_attrs.inspect}"
24
+ else
25
+ "expected _something_ to have state attributes, but the " \
26
+ "_something_ you gave me was nil!" \
27
+ "\n" \
28
+ "Ensure the resource exists before making assertions:" \
29
+ "\n\n" \
30
+ " expect(resource).to be" \
31
+ "\n "
32
+ end
33
+ end
34
+
35
+ def failure_message_when_negated
36
+ if @resource
37
+ "expected #{state_attrs.inspect} to not equal " \
38
+ "#{@expected_attrs.inspect}"
39
+ else
40
+ "expected _something_ to not have state attributes, but the " \
41
+ "_something_ you gave me was nil!" \
42
+ "\n" \
43
+ "Ensure the resource exists before making assertions:" \
44
+ "\n\n" \
45
+ " expect(resource).to be" \
46
+ "\n "
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ #
53
+ # Determine if all the expected state attributes are present on the
54
+ # given resource.
55
+ #
56
+ # @return [true, false]
57
+ #
58
+ def matches_state_attrs?
59
+ @expected_attrs == state_attrs
60
+ end
61
+
62
+ #
63
+ # The list of state attributes declared on the given resource.
64
+ #
65
+ # @return [Array<Symbol>]
66
+ #
67
+ def state_attrs
68
+ @resource.class.state_attrs.map(&:to_sym)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,72 @@
1
+ module ChefSpec::Matchers
2
+ class SubscribesMatcher
3
+ include ChefSpec::Normalize
4
+
5
+ def initialize(signature)
6
+ signature.match(/^([^\[]*)\[(.*)\]$/)
7
+ @expected_resource_type = $1
8
+ @expected_resource_name = $2
9
+ end
10
+
11
+ def matches?(resource)
12
+ @instance = ChefSpec::Matchers::NotificationsMatcher.new(resource.to_s)
13
+
14
+ if @action
15
+ @instance.to(@action)
16
+ end
17
+
18
+ if @immediately
19
+ @instance.immediately
20
+ end
21
+
22
+ if @delayed
23
+ @instance.delayed
24
+ end
25
+
26
+ if @before
27
+ @instance.before
28
+ end
29
+
30
+ if resource
31
+ runner = resource.run_context.node.runner
32
+ expected = runner.find_resource(@expected_resource_type, @expected_resource_name)
33
+
34
+ @instance.matches?(expected)
35
+ else
36
+ @instance.matches?(nil)
37
+ end
38
+ end
39
+
40
+ def on(action)
41
+ @action = action
42
+ self
43
+ end
44
+
45
+ def immediately
46
+ @immediately = true
47
+ self
48
+ end
49
+
50
+ def delayed
51
+ @delayed = true
52
+ self
53
+ end
54
+
55
+ def before
56
+ @before = true
57
+ self
58
+ end
59
+
60
+ def description
61
+ @instance.description
62
+ end
63
+
64
+ def failure_message
65
+ @instance.failure_message
66
+ end
67
+
68
+ def failure_message_when_negated
69
+ @instance.failure_message_when_negated
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,13 @@
1
+ module ChefSpec
2
+ module Matchers
3
+ require_relative "matchers/do_nothing_matcher"
4
+ require_relative "matchers/include_any_recipe_matcher"
5
+ require_relative "matchers/include_recipe_matcher"
6
+ require_relative "matchers/link_to_matcher"
7
+ require_relative "matchers/notifications_matcher"
8
+ require_relative "matchers/render_file_matcher"
9
+ require_relative "matchers/resource_matcher"
10
+ require_relative "matchers/state_attrs_matcher"
11
+ require_relative "matchers/subscribes_matcher"
12
+ end
13
+ end
@@ -0,0 +1,22 @@
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
+ if thing.respond_to?(:declared_type) && thing.declared_type
13
+ name = thing.declared_type
14
+ elsif thing.respond_to?(:resource_name)
15
+ name = thing.resource_name
16
+ else
17
+ name = thing
18
+ end
19
+ name.to_s.gsub("-", "_").to_sym
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,69 @@
1
+ begin
2
+ require "chef-cli/policyfile_services/export_repo"
3
+ require "chef-cli/policyfile_services/install"
4
+ require "chef/workstation_config_loader"
5
+ rescue LoadError
6
+ raise ChefSpec::Error::GemLoadError.new(gem: "chef-cli", name: "ChefCLI")
7
+ end
8
+
9
+ module ChefSpec
10
+ class Policyfile
11
+ class << self
12
+ extend Forwardable
13
+ def_delegators :instance, :setup!, :teardown!
14
+ end
15
+
16
+ include Singleton
17
+
18
+ def initialize
19
+ @tmpdir = Dir.mktmpdir
20
+ end
21
+
22
+ #
23
+ # Setup and install the necessary dependencies in the temporary directory
24
+ #
25
+ def setup!
26
+ policyfile_path = RSpec.configuration.policyfile_path
27
+ if policyfile_path.nil?
28
+ policyfile_path = File.join(Dir.pwd, "Policyfile.rb")
29
+ end
30
+
31
+ Chef::WorkstationConfigLoader.new(nil).load
32
+
33
+ installer = ChefCLI::PolicyfileServices::Install.new(
34
+ policyfile: policyfile_path,
35
+ ui: ChefCLI::UI.null,
36
+ config: Chef::Config
37
+ )
38
+
39
+ installer.run
40
+
41
+ exporter = ChefCLI::PolicyfileServices::ExportRepo.new(
42
+ policyfile: policyfile_path,
43
+ export_dir: @tmpdir
44
+ )
45
+
46
+ FileUtils.rm_rf(@tmpdir)
47
+ exporter.run
48
+
49
+ ::RSpec.configure do |config|
50
+ config.cookbook_path = [
51
+ File.join(@tmpdir, "cookbooks"),
52
+ File.join(@tmpdir, "cookbook_artifacts"),
53
+ ]
54
+ end
55
+ end
56
+
57
+ #
58
+ # Remove the temporary directory
59
+ #
60
+ def teardown!
61
+ FileUtils.rm_rf(@tmpdir) if File.exist?(@tmpdir)
62
+ end
63
+ end
64
+ end
65
+
66
+ RSpec.configure do |config|
67
+ config.before(:suite) { ChefSpec::Policyfile.setup! }
68
+ config.after(:suite) { ChefSpec::Policyfile.teardown! }
69
+ end
@@ -0,0 +1,145 @@
1
+ begin
2
+ require "chef/mixin/template"
3
+ require "chef/provider/template_finder"
4
+ rescue LoadError
5
+ end
6
+
7
+ module ChefSpec
8
+ class Renderer
9
+ include ChefSpec::Normalize
10
+
11
+ # @return [Chef::Runner]
12
+ attr_reader :chef_run
13
+
14
+ # @return [Chef::Resource]
15
+ attr_reader :resource
16
+
17
+ #
18
+ # Create a new Renderer for the given Chef run and resource.
19
+ #
20
+ # @param [Chef::Runner] chef_run
21
+ # the Chef run containing the resources
22
+ # @param [Chef::Resource] resource
23
+ # the resource to render content from
24
+ #
25
+ def initialize(chef_run, resource)
26
+ @chef_run = chef_run
27
+ @resource = resource
28
+ end
29
+
30
+ #
31
+ # The content of the resource (this method delegates to the)
32
+ # various private rendering methods.
33
+ #
34
+ # @return [String, nil]
35
+ # the contents of the file as a string, or nil if the resource
36
+ # does not contain or respond to a content renderer.
37
+ #
38
+ def content
39
+ case resource_name(resource)
40
+ when :template
41
+ content_from_template(chef_run, resource)
42
+ when :file
43
+ content_from_file(chef_run, resource)
44
+ when :cookbook_file
45
+ content_from_cookbook_file(chef_run, resource)
46
+ else
47
+ nil
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ #
54
+ # Compute the contents of a template using Chef's templating logic.
55
+ #
56
+ # @param [Chef::RunContext] chef_run
57
+ # the run context for the node
58
+ # @param [Chef::Provider::Template] template
59
+ # the template resource
60
+ #
61
+ # @return [String]
62
+ #
63
+ def content_from_template(chef_run, template)
64
+ cookbook_name = template.cookbook || template.cookbook_name
65
+ template_location = cookbook_collection(chef_run.node)[cookbook_name].preferred_filename_on_disk_location(chef_run.node, :templates, template.source)
66
+
67
+ if Chef::Mixin::Template.const_defined?(:TemplateContext) # Chef 11+
68
+ template_context = Chef::Mixin::Template::TemplateContext.new([])
69
+ template_context.update({
70
+ node: chef_run.node,
71
+ template_finder: template_finder(chef_run, cookbook_name),
72
+ }.merge(template.variables))
73
+ if template.respond_to?(:helper_modules) # Chef 11.4+
74
+ template_context._extend_modules(template.helper_modules)
75
+ end
76
+ template_context.render_template(template_location)
77
+ else
78
+ template.provider.new(template, chef_run.run_context).send(:render_with_context, template_location) do |file|
79
+ File.read(file.path)
80
+ end
81
+ end
82
+ end
83
+
84
+ #
85
+ # Get the contents of a file resource.
86
+ #
87
+ # @param [Chef::RunContext] chef_run
88
+ # the run context for the node
89
+ # @param [Chef::Provider::File] file
90
+ # the file resource
91
+ #
92
+ # @return [String]
93
+ #
94
+ def content_from_file(chef_run, file)
95
+ file.content
96
+ end
97
+
98
+ #
99
+ # Get the contents of a cookbook file using Chef.
100
+ #
101
+ # @param [Chef::RunContext] chef_run
102
+ # the run context for the node
103
+ # @param [Chef::Provider::CookbookFile] cookbook_file
104
+ # the file resource
105
+ #
106
+ def content_from_cookbook_file(chef_run, cookbook_file)
107
+ cookbook_name = cookbook_file.cookbook || cookbook_file.cookbook_name
108
+ cookbook = cookbook_collection(chef_run.node)[cookbook_name]
109
+ File.read(cookbook.preferred_filename_on_disk_location(chef_run.node, :files, cookbook_file.source))
110
+ end
111
+
112
+ # The cookbook collection for the current Chef run context. Handles
113
+ # the differing cases between Chef 10 and Chef 11.
114
+ #
115
+ # @param [Chef::Node] node
116
+ # the Chef node to get the cookbook collection from
117
+ #
118
+ # @return [Array<Chef::Cookbook>]
119
+ def cookbook_collection(node)
120
+ if chef_run.respond_to?(:run_context)
121
+ chef_run.run_context.cookbook_collection # Chef 11.8+
122
+ elsif node.respond_to?(:run_context)
123
+ node.run_context.cookbook_collection # Chef 11+
124
+ else
125
+ node.cookbook_collection # Chef 10
126
+ end
127
+ end
128
+
129
+ # Return a new instance of the TemplateFinder if we are running on Chef 11.
130
+ #
131
+ # @param [Chef::RunContext] chef_run
132
+ # the run context for the noe
133
+ # @param [String] cookbook_name
134
+ # the name of the cookbook
135
+ #
136
+ # @return [Chef::Provider::TemplateFinder, nil]
137
+ def template_finder(chef_run, cookbook_name)
138
+ if Chef::Provider.const_defined?(:TemplateFinder) # Chef 11+
139
+ Chef::Provider::TemplateFinder.new(chef_run.run_context, cookbook_name, chef_run.node)
140
+ else
141
+ nil
142
+ end
143
+ end
144
+ end
145
+ end