chefspec-chef 9.3.4

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