chef 16.9.20-universal-mingw32 → 16.11.7-universal-mingw32

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  3. data/chef-universal-mingw32.gemspec +1 -1
  4. data/chef.gemspec +11 -2
  5. data/lib/chef/compliance/default_attributes.rb +6 -2
  6. data/lib/chef/compliance/fetcher/automate.rb +15 -4
  7. data/lib/chef/compliance/runner.rb +11 -4
  8. data/lib/chef/dsl/reboot_pending.rb +1 -1
  9. data/lib/chef/file_access_control/windows.rb +4 -4
  10. data/lib/chef/file_cache.rb +4 -4
  11. data/lib/chef/formatters/error_inspectors/resource_failure_inspector.rb +1 -1
  12. data/lib/chef/handler/json_file.rb +1 -1
  13. data/lib/chef/knife/bootstrap.rb +54 -4
  14. data/lib/chef/provider/mount/mount.rb +1 -1
  15. data/lib/chef/provider/package.rb +2 -2
  16. data/lib/chef/provider/package/dnf/dnf_helper.py +5 -1
  17. data/lib/chef/provider/package/yum/yum_helper.py +4 -0
  18. data/lib/chef/resource.rb +27 -3
  19. data/lib/chef/resource/chef_client_cron.rb +1 -1
  20. data/lib/chef/resource/windows_certificate.rb +47 -17
  21. data/lib/chef/resource_inspector.rb +5 -1
  22. data/lib/chef/shell.rb +32 -1
  23. data/lib/chef/util/dsc/configuration_generator.rb +1 -1
  24. data/lib/chef/version.rb +1 -1
  25. data/lib/chef/version_string.rb +1 -1
  26. data/spec/functional/resource/ohai_spec.rb +2 -10
  27. data/spec/integration/compliance/compliance_spec.rb +2 -1
  28. data/spec/integration/recipes/resource_action_spec.rb +14 -0
  29. data/spec/spec_helper.rb +1 -1
  30. data/spec/unit/compliance/fetcher/automate_spec.rb +8 -0
  31. data/spec/unit/compliance/runner_spec.rb +57 -9
  32. data/spec/unit/dsl/reboot_pending_spec.rb +2 -2
  33. data/spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb +2 -2
  34. data/spec/unit/knife/bootstrap_spec.rb +42 -3
  35. data/spec/unit/knife/supermarket_share_spec.rb +5 -6
  36. data/spec/unit/provider/mount/mount_spec.rb +1 -0
  37. data/spec/unit/provider/package/dnf/python_helper_spec.rb +7 -1
  38. data/spec/unit/resource/chef_client_cron_spec.rb +8 -8
  39. data/spec/unit/resource_inspector_spec.rb +7 -2
  40. data/spec/unit/resource_spec.rb +46 -0
  41. metadata +20 -14
@@ -213,7 +213,7 @@ class Chef
213
213
  #
214
214
  def log_command
215
215
  if new_resource.append_log_file
216
- "-L #{::File.join(new_resource.log_directory, new_resource.log_file_name)}"
216
+ ">> #{::File.join(new_resource.log_directory, new_resource.log_file_name)} 2>&1"
217
217
  else
218
218
  "> #{::File.join(new_resource.log_directory, new_resource.log_file_name)} 2>&1"
219
219
  end
@@ -76,7 +76,7 @@ class Chef
76
76
  default: "MY", equal_to: ["TRUSTEDPUBLISHER", "TrustedPublisher", "CLIENTAUTHISSUER", "REMOTE DESKTOP", "ROOT", "TRUSTEDDEVICES", "WEBHOSTING", "CA", "AUTHROOT", "TRUSTEDPEOPLE", "MY", "SMARTCARDROOT", "TRUST", "DISALLOWED"]
77
77
 
78
78
  property :user_store, [TrueClass, FalseClass],
79
- description: "Use the user store of the local machine store if set to false.",
79
+ description: "Use the `CurrentUser` store instead of the default `LocalMachine` store. Note: Prior to #{ChefUtils::Dist::Infra::CLIENT}. 16.10 this property was ignored.",
80
80
  default: false
81
81
 
82
82
  property :cert_path, String,
@@ -119,7 +119,7 @@ class Chef
119
119
  code_script << acl_script(hash)
120
120
  guard_script << cert_exists_script(hash)
121
121
 
122
- powershell_script "setting the acls on #{new_resource.source} in #{cert_location}\\#{new_resource.store_name}" do
122
+ powershell_script "setting the acls on #{new_resource.source} in #{ps_cert_location}\\#{new_resource.store_name}" do
123
123
  convert_boolean_return true
124
124
  code code_script
125
125
  only_if guard_script
@@ -161,25 +161,47 @@ class Chef
161
161
  end
162
162
 
163
163
  action_class do
164
+
165
+ CERT_SYSTEM_STORE_LOCAL_MACHINE = 0x00020000
166
+ CERT_SYSTEM_STORE_CURRENT_USER = 0x00010000
167
+
164
168
  def add_cert(cert_obj)
165
- store = ::Win32::Certstore.open(new_resource.store_name)
169
+ store = ::Win32::Certstore.open(new_resource.store_name, store_location: native_cert_location)
166
170
  store.add(cert_obj)
167
171
  end
168
172
 
169
173
  def add_pfx_cert
170
174
  exportable = new_resource.exportable ? 1 : 0
171
- store = ::Win32::Certstore.open(new_resource.store_name)
175
+ store = ::Win32::Certstore.open(new_resource.store_name, store_location: native_cert_location)
172
176
  store.add_pfx(new_resource.source, new_resource.pfx_password, exportable)
173
177
  end
174
178
 
175
179
  def delete_cert
176
- store = ::Win32::Certstore.open(new_resource.store_name)
177
- store.delete(new_resource.source)
180
+ store = ::Win32::Certstore.open(new_resource.store_name, store_location: native_cert_location)
181
+ store.delete(resolve_thumbprint(new_resource.source))
178
182
  end
179
183
 
180
184
  def fetch_cert
181
- store = ::Win32::Certstore.open(new_resource.store_name)
182
- store.get(new_resource.source)
185
+ store = ::Win32::Certstore.open(new_resource.store_name, store_location: native_cert_location)
186
+ store.get(resolve_thumbprint(new_resource.source))
187
+ end
188
+
189
+ # Thumbprints should be exactly 40 Hex characters
190
+ def valid_thumbprint?(string)
191
+ string.scan(/\H/).empty? && string.length == 40
192
+ end
193
+
194
+ def get_thumbprint(store_name, location, source)
195
+ <<-GETTHUMBPRINTCODE
196
+ $content = Get-ChildItem -Path Cert:\\#{location}\\#{store_name} | Where-Object {$_.Subject -Match "#{source}"} | Select-Object Thumbprint
197
+ $content.thumbprint
198
+ GETTHUMBPRINTCODE
199
+ end
200
+
201
+ def resolve_thumbprint(thumbprint)
202
+ return thumbprint if valid_thumbprint?(thumbprint)
203
+
204
+ powershell_exec!(get_thumbprint(new_resource.store_name, ps_cert_location, new_resource.source)).result
183
205
  end
184
206
 
185
207
  # Checks whether a certificate with the given thumbprint
@@ -187,9 +209,11 @@ class Chef
187
209
  # If the certificate is not present, verify_cert returns a String: "Certificate not found"
188
210
  # But if it is present but expired, it returns a Boolean: false
189
211
  # Otherwise, it returns a Boolean: true
212
+ # updated this method to accept either a subject name or a thumbprint - 1/29/2021
213
+
190
214
  def verify_cert(thumbprint = new_resource.source)
191
- store = ::Win32::Certstore.open(new_resource.store_name)
192
- store.valid?(thumbprint)
215
+ store = ::Win32::Certstore.open(new_resource.store_name, store_location: native_cert_location)
216
+ store.valid?(resolve_thumbprint(thumbprint))
193
217
  end
194
218
 
195
219
  def show_or_store_cert(cert_obj)
@@ -230,13 +254,19 @@ class Chef
230
254
  out_file.close
231
255
  end
232
256
 
233
- def cert_location
234
- @location ||= new_resource.user_store ? "CurrentUser" : "LocalMachine"
257
+ # this array structure is solving 2 problems. The first is that we need to have support for both the CurrentUser AND LocalMachine stores
258
+ # Secondly, we need to pass the proper constant name for each store to win32-certstore but also pass the short name to powershell scripts used here
259
+ def ps_cert_location
260
+ new_resource.user_store ? "CurrentUser" : "LocalMachine"
261
+ end
262
+
263
+ def native_cert_location
264
+ new_resource.user_store ? CERT_SYSTEM_STORE_CURRENT_USER : CERT_SYSTEM_STORE_LOCAL_MACHINE
235
265
  end
236
266
 
237
267
  def cert_script(persist)
238
268
  cert_script = "$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2"
239
- file = Chef::Util::PathHelper.cleanpath(new_resource.source)
269
+ file = Chef::Util::PathHelper.cleanpath(new_resource.source, ps_cert_location)
240
270
  cert_script << " \"#{file}\""
241
271
  if ::File.extname(file.downcase) == ".pfx"
242
272
  cert_script << ", \"#{new_resource.pfx_password}\""
@@ -252,14 +282,14 @@ class Chef
252
282
  def cert_exists_script(hash)
253
283
  <<-EOH
254
284
  $hash = #{hash}
255
- Test-Path "Cert:\\#{cert_location}\\#{new_resource.store_name}\\$hash"
285
+ Test-Path "Cert:\\#{ps_cert_location}\\#{new_resource.store_name}\\$hash"
256
286
  EOH
257
287
  end
258
288
 
259
289
  def within_store_script
260
290
  inner_script = yield "$store"
261
291
  <<-EOH
262
- $store = New-Object System.Security.Cryptography.X509Certificates.X509Store "#{new_resource.store_name}", ([System.Security.Cryptography.X509Certificates.StoreLocation]::#{cert_location})
292
+ $store = New-Object System.Security.Cryptography.X509Certificates.X509Store "#{new_resource.store_name}", ([System.Security.Cryptography.X509Certificates.StoreLocation]::#{ps_cert_location})
263
293
  $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
264
294
  #{inner_script}
265
295
  $store.Close()
@@ -273,7 +303,7 @@ class Chef
273
303
  # and from https://msdn.microsoft.com/en-us/library/windows/desktop/bb204778(v=vs.85).aspx
274
304
  set_acl_script = <<-EOH
275
305
  $hash = #{hash}
276
- $storeCert = Get-ChildItem "cert:\\#{cert_location}\\#{new_resource.store_name}\\$hash"
306
+ $storeCert = Get-ChildItem "cert:\\#{ps_cert_location}\\#{new_resource.store_name}\\$hash"
277
307
  if ($storeCert -eq $null) { throw 'no key exists.' }
278
308
  $keyname = $storeCert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
279
309
  if ($keyname -eq $null) { throw 'no private key exists.' }
@@ -340,7 +370,7 @@ class Chef
340
370
  if verify_cert(thumbprint) == true
341
371
  Chef::Log.debug("Certificate is already present")
342
372
  else
343
- converge_by("Adding certificate #{new_resource.source} into Store #{new_resource.store_name}") do
373
+ converge_by("Adding certificate #{new_resource.source} into #{ps_cert_location} Store #{new_resource.store_name}") do
344
374
  if is_pfx
345
375
  add_pfx_cert
346
376
  else
@@ -41,7 +41,11 @@ class Chef
41
41
  data[:description] = resource.description
42
42
  # data[:deprecated] = resource.deprecated || false
43
43
  data[:default_action] = resource.default_action
44
- data[:actions] = resource.allowed_actions
44
+ data[:actions] = {}
45
+ resource.allowed_actions.each do |action|
46
+ data[:actions][action] = resource.action_description(action)
47
+ end
48
+
45
49
  data[:examples] = resource.examples
46
50
  data[:introduced] = resource.introduced
47
51
  data[:preview] = resource.preview_resource
data/lib/chef/shell.rb CHANGED
@@ -25,6 +25,7 @@ require "pp" unless defined?(PP)
25
25
  require "etc" unless defined?(Etc)
26
26
  require "mixlib/cli" unless defined?(Mixlib::CLI)
27
27
  require "chef-utils/dist" unless defined?(ChefUtils::Dist)
28
+ require "chef-config/mixin/dot_d"
28
29
 
29
30
  require_relative "../chef"
30
31
  require_relative "version"
@@ -211,6 +212,7 @@ module Shell
211
212
 
212
213
  class Options
213
214
  include Mixlib::CLI
215
+ include ChefConfig::Mixin::DotD
214
216
 
215
217
  def self.footer(text = nil)
216
218
  @footer = text if text
@@ -341,15 +343,44 @@ module Shell
341
343
  # We have to nuke ARGV to make sure irb's option parser never sees it.
342
344
  # otherwise, IRB complains about command line switches it doesn't recognize.
343
345
  ARGV.clear
346
+
347
+ # This code should not exist.
348
+ # We should be using Application::Client and then calling load_config_file
349
+ # which does all this properly. However this will do for now.
344
350
  config[:config_file] = config_file_for_shell_mode(environment)
345
351
  config_msg = config[:config_file] || "none (standalone session)"
346
352
  puts "loading configuration: #{config_msg}"
347
- Chef::Config.from_file(config[:config_file]) if !config[:config_file].nil? && File.exist?(config[:config_file]) && File.readable?(config[:config_file])
353
+
354
+ # load the config (if we have one)
355
+ unless config[:config_file].nil?
356
+ if File.exist?(config[:config_file]) && File.readable?(config[:config_file])
357
+ Chef::Config.from_file(config[:config_file])
358
+ end
359
+
360
+ # even if we couldn't load that, we need to tell Chef::Config what
361
+ # the file was so it sets conf dir and d_dir and such properly
362
+ Chef::Config[:config_file] = config[:config_file]
363
+
364
+ # now attempt to load any relevant dot-dirs
365
+ load_dot_d(Chef::Config[:client_d_dir]) if Chef::Config[:client_d_dir]
366
+ end
367
+
368
+ # finally merge command-line options in
348
369
  Chef::Config.merge!(config)
349
370
  end
350
371
 
351
372
  private
352
373
 
374
+ # shamelessly lifted from application.rb
375
+ def apply_config(config_content, config_file_path)
376
+ Chef::Config.from_string(config_content, config_file_path)
377
+ rescue Exception => error
378
+ logger.fatal("Configuration error #{error.class}: #{error.message}")
379
+ filtered_trace = error.backtrace.grep(/#{Regexp.escape(config_file_path)}/)
380
+ filtered_trace.each { |line| logger.fatal(" " + line ) }
381
+ raise Chef::Exceptions::ConfigurationError.new("Aborting due to error in '#{config_file_path}': #{error}")
382
+ end
383
+
353
384
  def config_file_for_shell_mode(environment)
354
385
  dot_chef_dir = Chef::Util::PathHelper.home(".chef")
355
386
  if config[:config_file]
@@ -105,7 +105,7 @@ class Chef::Util::DSC
105
105
  # The name may not be null or empty, and should start with a letter.
106
106
  def validate_configuration_name!(configuration_name)
107
107
  if !!(configuration_name =~ /\A[A-Za-z]+[_a-zA-Z0-9]*\Z/) == false
108
- raise ArgumentError, 'Configuration `#{configuration_name}` is not a valid PowerShell cmdlet name'
108
+ raise ArgumentError, "Configuration `#{configuration_name}` is not a valid PowerShell cmdlet name"
109
109
  end
110
110
  end
111
111
 
data/lib/chef/version.rb CHANGED
@@ -23,7 +23,7 @@ require_relative "version_string"
23
23
 
24
24
  class Chef
25
25
  CHEF_ROOT = File.expand_path("..", __dir__)
26
- VERSION = Chef::VersionString.new("16.9.20")
26
+ VERSION = Chef::VersionString.new("16.11.7")
27
27
  end
28
28
 
29
29
  #
@@ -13,7 +13,7 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
- require "chef-utils/version_string"
16
+ require "chef-utils/version_string" unless defined?(ChefUtils::VersionString)
17
17
 
18
18
  class Chef
19
19
  VersionString = ChefUtils::VersionString
@@ -19,10 +19,6 @@
19
19
  require "spec_helper"
20
20
 
21
21
  describe Chef::Resource::Ohai do
22
- let(:ohai) do
23
- OHAI_SYSTEM
24
- end
25
-
26
22
  let(:node) { Chef::Node.new }
27
23
 
28
24
  let(:run_context) do
@@ -34,13 +30,9 @@ describe Chef::Resource::Ohai do
34
30
 
35
31
  shared_examples_for "reloaded :uptime" do
36
32
  it "should reload :uptime" do
37
- initial_uptime = ohai[:uptime]
38
-
39
- # Sleep for a second so the uptime gets updated.
40
- sleep 1
41
-
33
+ expect(node[:uptime_seconds]).to be nil
42
34
  ohai_resource.run_action(:reload)
43
- expect(node[:uptime]).not_to eq(initial_uptime)
35
+ expect(Integer(node[:uptime_seconds])).to be_an(Integer)
44
36
  end
45
37
  end
46
38
 
@@ -4,7 +4,7 @@ require "support/shared/integration/integration_helper"
4
4
  require "chef/mixin/shell_out"
5
5
  require "chef-utils/dist"
6
6
 
7
- describe "chef-client with audit mode" do
7
+ describe "chef-client with compliance phase" do
8
8
 
9
9
  include IntegrationSupport
10
10
  include Chef::Mixin::ShellOut
@@ -46,6 +46,7 @@ describe "chef-client with audit mode" do
46
46
  file "attributes.json", <<~FILE
47
47
  {
48
48
  "audit": {
49
+ "compliance_phase": true,
49
50
  "json_file": {
50
51
  "location": "#{report_file}"
51
52
  },
@@ -223,6 +223,10 @@ module ResourceActionSpec
223
223
  ActionJackson.succeeded = ActionJackson.ruby_block_converged
224
224
  end
225
225
 
226
+ action :test1, description: "Original description" do
227
+ true
228
+ end
229
+
226
230
  def foo_public
227
231
  "foo_public!"
228
232
  end
@@ -293,7 +297,12 @@ module ResourceActionSpec
293
297
  ActionJackalope.jackalope_ran = :access_attribute
294
298
  ActionJackalope.succeeded = ActionJackson.succeeded
295
299
  end
300
+
301
+ action :test1, description: "An old action with a new description" do
302
+ super
303
+ end
296
304
  end
305
+
297
306
  before do
298
307
  ActionJackalope.jackalope_ran = nil
299
308
  ActionJackalope.load_current_resource_ran = nil
@@ -344,6 +353,11 @@ module ResourceActionSpec
344
353
  expect(ActionJackalope.succeeded).to eq "foo!alope blarghle! bar!alope"
345
354
  end
346
355
 
356
+ it "allows overridden action to have a description separate from the action defined in the base resource" do
357
+ expect(ActionJackson.action_description(:test1)).to eql "Original description"
358
+ expect(ActionJackalope.action_description(:test1)).to eql "An old action with a new description"
359
+ end
360
+
347
361
  it "non-overridden actions run and can access overridden and non-overridden variables (but not necessarily new ones)" do
348
362
  converge do
349
363
  action_jackalope "hi" do
data/spec/spec_helper.rb CHANGED
@@ -87,7 +87,7 @@ Dir["spec/support/**/*.rb"]
87
87
  .each { |f| require f }
88
88
 
89
89
  OHAI_SYSTEM = Ohai::System.new
90
- OHAI_SYSTEM.all_plugins(["platform", "hostname", "languages/powershell"])
90
+ OHAI_SYSTEM.all_plugins(["platform", "hostname", "languages/powershell", "uptime"])
91
91
 
92
92
  test_node = Chef::Node.new
93
93
  test_node.automatic["os"] = (OHAI_SYSTEM["os"] || "unknown_os").dup.freeze
@@ -21,6 +21,14 @@ describe Chef::Compliance::Fetcher::Automate do
21
21
  expect(res.target).to eq(expected)
22
22
  end
23
23
 
24
+ it "should resolve a compliance URL with a @ in the namespace" do
25
+ res = Chef::Compliance::Fetcher::Automate.resolve("compliance://name@space/profile_name")
26
+
27
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::Automate)
28
+ expected = "https://automate.test/compliance/profiles/name@space/profile_name/tar"
29
+ expect(res.target).to eq(expected)
30
+ end
31
+
24
32
  it "raises an exception with no data collector token" do
25
33
  Chef::Config[:data_collector].delete(:token)
26
34
 
@@ -12,38 +12,86 @@ describe Chef::Compliance::Runner do
12
12
  end
13
13
 
14
14
  describe "#enabled?" do
15
- it "is true if the node attributes have audit profiles and the audit cookbook is not present" do
15
+
16
+ it "is true if the node attributes have audit profiles and the audit cookbook is not present, and the compliance mode attribute is nil" do
16
17
  node.normal["audit"]["profiles"]["ssh"] = { 'compliance': "base/ssh" }
17
- node.automatic["recipes"] = %w{ fancy_cookbook::fanciness tacobell::nachos }
18
+ node.normal["audit"]["compliance_phase"] = nil
18
19
 
19
20
  expect(runner).to be_enabled
20
21
  end
21
22
 
22
- it "is false if the node attributes have audit profiles and the audit cookbook is present" do
23
+ it "is true if the node attributes have audit profiles and the audit cookbook is not present, and the compliance mode attribute is true" do
23
24
  node.normal["audit"]["profiles"]["ssh"] = { 'compliance': "base/ssh" }
24
- node.automatic["recipes"] = %w{ audit::default fancy_cookbook::fanciness tacobell::nachos }
25
+ node.normal["audit"]["compliance_phase"] = true
26
+
27
+ expect(runner).to be_enabled
28
+ end
29
+
30
+ it "is false if the node attributes have audit profiles and the audit cookbook is not present, and the compliance mode attribute is false" do
31
+ node.normal["audit"]["profiles"]["ssh"] = { 'compliance': "base/ssh" }
32
+ node.normal["audit"]["compliance_phase"] = false
25
33
 
26
34
  expect(runner).not_to be_enabled
27
35
  end
28
36
 
29
- it "is false if the node attributes do not have audit profiles and the audit cookbook is not present" do
30
- node.normal["audit"]["profiles"] = {}
31
- node.automatic["recipes"] = %w{ fancy_cookbook::fanciness tacobell::nachos }
37
+ it "is false if the node attributes have audit profiles and the audit cookbook is present, and the complince mode attribute is nil" do
38
+ stub_const("::Reporter::ChefAutomate", true)
39
+ node.normal["audit"]["profiles"]["ssh"] = { 'compliance': "base/ssh" }
40
+ node.normal["audit"]["compliance_phase"] = nil
32
41
 
33
42
  expect(runner).not_to be_enabled
34
43
  end
35
44
 
36
- it "is false if the node attributes do not have audit profiles and the audit cookbook is present" do
45
+ it "is true if the node attributes have audit profiles and the audit cookbook is present, and the complince mode attribute is true" do
46
+ stub_const("::Reporter::ChefAutomate", true)
47
+ node.normal["audit"]["profiles"]["ssh"] = { 'compliance': "base/ssh" }
48
+ node.normal["audit"]["compliance_phase"] = true
49
+
50
+ expect(runner).to be_enabled
51
+ end
52
+
53
+ it "is false if the node attributes do not have audit profiles and the audit cookbook is not present, and the complince mode attribute is nil" do
37
54
  node.normal["audit"]["profiles"] = {}
55
+ node.normal["audit"]["compliance_phase"] = nil
56
+
57
+ expect(runner).not_to be_enabled
58
+ end
59
+
60
+ it "is false if the node attributes do not have audit profiles and the audit cookbook is present, and the complince mode attribute is nil" do
61
+ stub_const("::Reporter::ChefAutomate", true)
38
62
  node.automatic["recipes"] = %w{ audit::default fancy_cookbook::fanciness tacobell::nachos }
63
+ node.normal["audit"]["compliance_phase"] = nil
39
64
 
40
65
  expect(runner).not_to be_enabled
41
66
  end
42
67
 
43
- it "is false if the node attributes do not have audit attributes and the audit cookbook is not present" do
68
+ it "is false if the node attributes do not have audit attributes and the audit cookbook is not present, and the complince mode attribute is nil" do
44
69
  node.automatic["recipes"] = %w{ fancy_cookbook::fanciness tacobell::nachos }
70
+ node.normal["audit"]["compliance_phase"] = nil
71
+
45
72
  expect(runner).not_to be_enabled
46
73
  end
74
+
75
+ it "is true if the node attributes do not have audit profiles and the audit cookbook is not present, and the complince mode attribute is true" do
76
+ node.normal["audit"]["profiles"] = {}
77
+ node.normal["audit"]["compliance_phase"] = true
78
+
79
+ expect(runner).to be_enabled
80
+ end
81
+
82
+ it "is true if the node attributes do not have audit profiles and the audit cookbook is present, and the complince mode attribute is true" do
83
+ stub_const("::Reporter::ChefAutomate", true)
84
+ node.automatic["recipes"] = %w{ audit::default fancy_cookbook::fanciness tacobell::nachos }
85
+ node.normal["audit"]["compliance_phase"] = true
86
+
87
+ expect(runner).to be_enabled
88
+ end
89
+
90
+ it "is true if the node attributes do not have audit attributes and the audit cookbook is not present, and the complince mode attribute is true" do
91
+ node.automatic["recipes"] = %w{ fancy_cookbook::fanciness tacobell::nachos }
92
+ node.normal["audit"]["compliance_phase"] = true
93
+ expect(runner).to be_enabled
94
+ end
47
95
  end
48
96
 
49
97
  describe "#inspec_profiles" do