chef 12.16.42-universal-mingw32 → 12.17.44-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 (147) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -1
  3. data/README.md +20 -5
  4. data/Rakefile +17 -0
  5. data/VERSION +1 -1
  6. data/acceptance/Gemfile.lock +32 -23
  7. data/distro/common/markdown/man1/knife-configure.mkd +3 -2
  8. data/lib-backcompat/chef/chef_fs/file_system/already_exists_error.rb +1 -1
  9. data/lib-backcompat/chef/chef_fs/file_system/cookbook_frozen_error.rb +1 -1
  10. data/lib-backcompat/chef/chef_fs/file_system/default_environment_cannot_be_modified_error.rb +1 -1
  11. data/lib-backcompat/chef/chef_fs/file_system/file_system_error.rb +1 -1
  12. data/lib-backcompat/chef/chef_fs/file_system/must_delete_recursively_error.rb +1 -1
  13. data/lib-backcompat/chef/chef_fs/file_system/not_found_error.rb +1 -1
  14. data/lib-backcompat/chef/chef_fs/file_system/operation_failed_error.rb +1 -1
  15. data/lib-backcompat/chef/chef_fs/file_system/operation_not_allowed_error.rb +1 -1
  16. data/lib-backcompat/chef/chef_fs/file_system/repository/chef_repository_file_system_entry.rb +1 -1
  17. data/lib-backcompat/chef/chef_fs/file_system/repository/file_system_root_dir.rb +1 -1
  18. data/lib/chef/api_client.rb +1 -1
  19. data/lib/chef/application.rb +1 -1
  20. data/lib/chef/application/exit_code.rb +3 -3
  21. data/lib/chef/chef_class.rb +15 -5
  22. data/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_root_dir.rb +12 -1
  23. data/lib/chef/chef_fs/file_system/repository/nodes_dir.rb +19 -0
  24. data/lib/chef/client.rb +1 -0
  25. data/lib/chef/cookbook/metadata.rb +2 -2
  26. data/lib/chef/cookbook_version.rb +4 -4
  27. data/lib/chef/data_bag.rb +1 -1
  28. data/lib/chef/data_bag_item.rb +1 -1
  29. data/lib/chef/data_collector.rb +20 -13
  30. data/lib/chef/data_collector/messages.rb +0 -1
  31. data/lib/chef/data_collector/messages/helpers.rb +2 -2
  32. data/lib/chef/decorator/unchain.rb +2 -2
  33. data/lib/chef/deprecated.rb +190 -0
  34. data/lib/chef/deprecation/provider/remote_directory.rb +1 -1
  35. data/lib/chef/deprecation/warnings.rb +3 -4
  36. data/lib/chef/dsl/method_missing.rb +2 -2
  37. data/lib/chef/dsl/resources.rb +2 -2
  38. data/lib/chef/environment.rb +1 -1
  39. data/lib/chef/exceptions.rb +1 -1
  40. data/lib/chef/formatters/base.rb +11 -1
  41. data/lib/chef/formatters/doc.rb +13 -4
  42. data/lib/chef/key.rb +1 -1
  43. data/lib/chef/knife/client_delete.rb +12 -9
  44. data/lib/chef/knife/configure.rb +1 -1
  45. data/lib/chef/knife/core/bootstrap_context.rb +25 -1
  46. data/lib/chef/knife/core/subcommand_loader.rb +3 -3
  47. data/lib/chef/knife/core/ui.rb +1 -1
  48. data/lib/chef/knife/node_delete.rb +6 -6
  49. data/lib/chef/log.rb +1 -1
  50. data/lib/chef/mixin/deprecation.rb +4 -10
  51. data/lib/chef/mixin/powershell_type_coercions.rb +19 -19
  52. data/lib/chef/mixin/shell_out.rb +1 -1
  53. data/lib/chef/node.rb +2 -2
  54. data/lib/chef/node/attribute.rb +3 -4
  55. data/lib/chef/node/common_api.rb +1 -1
  56. data/lib/chef/node/mixin/state_tracking.rb +5 -2
  57. data/lib/chef/node_map.rb +2 -2
  58. data/lib/chef/org.rb +1 -1
  59. data/lib/chef/platform/rebooter.rb +3 -1
  60. data/lib/chef/policy_builder/expand_node_object.rb +1 -1
  61. data/lib/chef/property.rb +5 -5
  62. data/lib/chef/provider.rb +4 -4
  63. data/lib/chef/provider/launchd.rb +1 -1
  64. data/lib/chef/provider/link.rb +6 -0
  65. data/lib/chef/provider/mount.rb +2 -0
  66. data/lib/chef/provider/mount/mount.rb +1 -1
  67. data/lib/chef/provider/ohai.rb +5 -3
  68. data/lib/chef/provider/package/cab.rb +1 -1
  69. data/lib/chef/provider/package/chocolatey.rb +2 -2
  70. data/lib/chef/provider/package/easy_install.rb +2 -2
  71. data/lib/chef/provider/package/msu.rb +162 -0
  72. data/lib/chef/provider/package/powershell.rb +114 -0
  73. data/lib/chef/provider/package/yum.rb +1 -1
  74. data/lib/chef/provider/yum_repository.rb +6 -7
  75. data/lib/chef/provider_resolver.rb +2 -2
  76. data/lib/chef/providers.rb +2 -0
  77. data/lib/chef/resource.rb +3 -5
  78. data/lib/chef/resource/apt_update.rb +1 -1
  79. data/lib/chef/resource/chef_gem.rb +2 -3
  80. data/lib/chef/resource/file/verification.rb +1 -1
  81. data/lib/chef/resource/launchd.rb +48 -8
  82. data/lib/chef/resource/mount.rb +1 -1
  83. data/lib/chef/resource/msu_package.rb +47 -0
  84. data/lib/chef/resource/ohai.rb +5 -25
  85. data/lib/chef/resource/powershell_package.rb +41 -0
  86. data/lib/chef/resource/reboot.rb +1 -1
  87. data/lib/chef/resource/user.rb +2 -2
  88. data/lib/chef/resource_builder.rb +4 -4
  89. data/lib/chef/resource_resolver.rb +2 -3
  90. data/lib/chef/resources.rb +2 -0
  91. data/lib/chef/rest.rb +1 -1
  92. data/lib/chef/role.rb +1 -1
  93. data/lib/chef/run_context.rb +3 -3
  94. data/lib/chef/shell/ext.rb +2 -2
  95. data/lib/chef/user.rb +3 -3
  96. data/lib/chef/user_v1.rb +1 -1
  97. data/lib/chef/version.rb +1 -1
  98. data/lib/chef/win32/api/security.rb +12 -12
  99. data/spec/data/sample_msu1.xml +10 -0
  100. data/spec/data/sample_msu2.xml +14 -0
  101. data/spec/data/sample_msu3.xml +16 -0
  102. data/spec/functional/rebooter_spec.rb +3 -3
  103. data/spec/functional/resource/link_spec.rb +62 -1
  104. data/spec/functional/resource/msu_package_spec.rb +84 -0
  105. data/spec/functional/resource/registry_spec.rb +3 -3
  106. data/spec/functional/resource/rpm_spec.rb +7 -10
  107. data/spec/integration/solo/solo_spec.rb +50 -0
  108. data/spec/spec_helper.rb +3 -0
  109. data/spec/support/platform_helpers.rb +16 -8
  110. data/spec/unit/application/exit_code_spec.rb +3 -15
  111. data/spec/unit/data_collector_spec.rb +6 -16
  112. data/spec/unit/deprecated_spec.rb +59 -0
  113. data/spec/unit/deprecation_spec.rb +1 -8
  114. data/spec/unit/handler_spec.rb +2 -2
  115. data/spec/unit/knife/client_delete_spec.rb +16 -0
  116. data/spec/unit/knife/configure_spec.rb +1 -1
  117. data/spec/unit/knife/cookbook_metadata_spec.rb +116 -113
  118. data/spec/unit/knife/core/bootstrap_context_spec.rb +55 -5
  119. data/spec/unit/knife/node_delete_spec.rb +19 -10
  120. data/spec/unit/mixin/shell_out_spec.rb +0 -1
  121. data/spec/unit/node/immutable_collections_spec.rb +5 -0
  122. data/spec/unit/node/vivid_mash_spec.rb +11 -0
  123. data/spec/unit/node_spec.rb +2 -2
  124. data/spec/unit/provider/launchd_spec.rb +81 -3
  125. data/spec/unit/provider/mount/mount_spec.rb +1 -1
  126. data/spec/unit/provider/mount_spec.rb +7 -0
  127. data/spec/unit/provider/package/chocolatey_spec.rb +5 -5
  128. data/spec/unit/provider/package/easy_install_spec.rb +6 -6
  129. data/spec/unit/provider/package/msu_spec.rb +283 -0
  130. data/spec/unit/provider/package/powershell_spec.rb +337 -0
  131. data/spec/unit/provider/service/macosx_spec.rb +1 -1
  132. data/spec/unit/provider/subversion_spec.rb +9 -0
  133. data/spec/unit/provider/user/linux_spec.rb +7 -1
  134. data/spec/unit/recipe_spec.rb +43 -11
  135. data/spec/unit/resource/apt_update_spec.rb +17 -25
  136. data/spec/unit/resource/file/verification_spec.rb +1 -1
  137. data/spec/unit/resource/mount_spec.rb +2 -1
  138. data/spec/unit/resource/msu_package_spec.rb +49 -0
  139. data/spec/unit/resource/ohai_spec.rb +1 -1
  140. data/spec/unit/resource/powershell_package_spec.rb +68 -0
  141. data/spec/unit/resource_reporter_spec.rb +4 -4
  142. data/spec/unit/run_status_spec.rb +1 -1
  143. data/tasks/announce.rb +58 -0
  144. data/tasks/changelog.rb +26 -6
  145. data/tasks/templates/prerelease.md.erb +35 -0
  146. data/tasks/templates/release.md.erb +34 -0
  147. metadata +21 -4
@@ -68,6 +68,8 @@ require "chef/config"
68
68
 
69
69
  require "chef/chef_fs/file_system_cache"
70
70
 
71
+ require "chef/api_client_v1"
72
+
71
73
  if ENV["CHEF_FIPS"] == "1"
72
74
  Chef::Config.init_openssl
73
75
  end
@@ -139,6 +141,7 @@ RSpec.configure do |config|
139
141
  config.filter_run_excluding :not_supported_on_gce => true if gce?
140
142
  config.filter_run_excluding :not_supported_on_nano => true if windows_nano_server?
141
143
  config.filter_run_excluding :win2k3_only => true unless windows_win2k3?
144
+ config.filter_run_excluding :win2012r2_only => true unless windows_2012r2?
142
145
  config.filter_run_excluding :windows_2008r2_or_later => true unless windows_2008r2_or_later?
143
146
  config.filter_run_excluding :windows64_only => true unless windows64?
144
147
  config.filter_run_excluding :windows32_only => true unless windows32?
@@ -50,23 +50,31 @@ end
50
50
 
51
51
  def windows_win2k3?
52
52
  return false unless windows?
53
- wmi = WmiLite::Wmi.new
54
- host = wmi.first_of("Win32_OperatingSystem")
55
- (host["version"] && host["version"].start_with?("5.2"))
53
+ (host_version && host_version.start_with?("5.2"))
56
54
  end
57
55
 
58
56
  def windows_2008r2_or_later?
59
57
  return false unless windows?
60
- wmi = WmiLite::Wmi.new
61
- host = wmi.first_of("Win32_OperatingSystem")
62
- version = host["version"]
63
- return false unless version
64
- components = version.split(".").map do |component|
58
+ return false unless host_version
59
+ components = host_version.split(".").map do |component|
65
60
  component.to_i
66
61
  end
67
62
  components.length >= 2 && components[0] >= 6 && components[1] >= 1
68
63
  end
69
64
 
65
+ def windows_2012r2?
66
+ return false unless windows?
67
+ (host_version && host_version.start_with?("6.3"))
68
+ end
69
+
70
+ def host_version
71
+ @host_version ||= begin
72
+ wmi = WmiLite::Wmi.new
73
+ host = wmi.first_of("Win32_OperatingSystem")
74
+ host["version"]
75
+ end
76
+ end
77
+
70
78
  def windows_powershell_dsc?
71
79
  return false unless windows?
72
80
  supports_dsc = false
@@ -77,11 +77,7 @@ describe Chef::Application::ExitCode do
77
77
  end
78
78
 
79
79
  it "writes a deprecation warning" do
80
- warn = "Chef RFC 062 (https://github.com/chef/chef-rfc/master/rfc062-exit-status.md) defines the" \
81
- " exit codes that should be used with Chef. Chef::Application::ExitCode defines valid exit codes" \
82
- " In a future release, non-standard exit codes will be redefined as" \
83
- " GENERIC_FAILURE unless `exit_status` is set to `:disabled` in your client.rb."
84
- expect(Chef).to receive(:log_deprecation).with(warn)
80
+ expect(Chef).to receive(:deprecated).with(:exit_code, /^Chef RFC 062/)
85
81
  expect(exit_codes.normalize_exit_code(151)).to eq(151)
86
82
  end
87
83
 
@@ -118,11 +114,7 @@ describe Chef::Application::ExitCode do
118
114
  end
119
115
 
120
116
  it "does not write a deprecation warning" do
121
- warn = "Chef RFC 062 (https://github.com/chef/chef-rfc/master/rfc062-exit-status.md) defines the" \
122
- " exit codes that should be used with Chef. Chef::Application::ExitCode defines valid exit codes" \
123
- " In a future release, non-standard exit codes will be redefined as" \
124
- " GENERIC_FAILURE unless `exit_status` is set to `:disabled` in your client.rb."
125
- expect(Chef).not_to receive(:log_deprecation).with(warn)
117
+ expect(Chef).not_to receive(:deprecated).with(:exit_code, /^Chef RFC 062/)
126
118
  expect(exit_codes.normalize_exit_code(151)).to eq(151)
127
119
  end
128
120
 
@@ -163,11 +155,7 @@ describe Chef::Application::ExitCode do
163
155
  end
164
156
 
165
157
  it "does write a deprecation warning" do
166
- warn = "Chef RFC 062 (https://github.com/chef/chef-rfc/master/rfc062-exit-status.md) defines the" \
167
- " exit codes that should be used with Chef. Chef::Application::ExitCode defines valid exit codes" \
168
- " In a future release, non-standard exit codes will be redefined as" \
169
- " GENERIC_FAILURE unless `exit_status` is set to `:disabled` in your client.rb."
170
- expect(Chef).to receive(:log_deprecation).with(warn)
158
+ expect(Chef).to receive(:deprecated).with(:exit_code, /^Chef RFC 062/)
171
159
  expect(exit_codes.normalize_exit_code(151)).to eq(1)
172
160
  end
173
161
 
@@ -371,12 +371,10 @@ describe Chef::DataCollector::Reporter do
371
371
  end
372
372
 
373
373
  context "when resource is not a nested resource" do
374
- it "creates the resource report and stores it as the current one" do
374
+ it "initializes the resource report" do
375
375
  allow(reporter).to receive(:nested_resource?).and_return(false)
376
- expect(reporter).to receive(:create_resource_report)
376
+ expect(reporter).to receive(:initialize_resource_report_if_needed)
377
377
  .with(new_resource, action, current_resource)
378
- .and_return(resource_report)
379
- expect(reporter).to receive(:update_current_resource_report).with(resource_report)
380
378
  reporter.resource_current_state_loaded(new_resource, action, current_resource)
381
379
  end
382
380
  end
@@ -418,7 +416,6 @@ describe Chef::DataCollector::Reporter do
418
416
 
419
417
  before do
420
418
  allow(reporter).to receive(:nested_resource?)
421
- allow(reporter).to receive(:create_resource_report).and_return(resource_report)
422
419
  allow(resource_report).to receive(:skipped)
423
420
  end
424
421
 
@@ -431,17 +428,10 @@ describe Chef::DataCollector::Reporter do
431
428
  end
432
429
 
433
430
  context "when the resource is not a nested resource" do
434
- it "creates the resource report and stores it as the current one" do
431
+ it "initializes the resource report and marks it as skipped" do
435
432
  allow(reporter).to receive(:nested_resource?).and_return(false)
436
- expect(reporter).to receive(:create_resource_report)
437
- .with(new_resource, action)
438
- .and_return(resource_report)
439
- expect(reporter).to receive(:update_current_resource_report).with(resource_report)
440
- reporter.resource_skipped(new_resource, action, conditional)
441
- end
442
-
443
- it "marks the resource report as skipped" do
444
- allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(false)
433
+ allow(reporter).to receive(:current_resource_report).and_return(resource_report)
434
+ expect(reporter).to receive(:initialize_resource_report_if_needed).with(new_resource, action)
445
435
  expect(resource_report).to receive(:skipped).with(conditional)
446
436
  reporter.resource_skipped(new_resource, action, conditional)
447
437
  end
@@ -548,7 +538,7 @@ describe Chef::DataCollector::Reporter do
548
538
  end
549
539
 
550
540
  it "nils out the current resource report" do
551
- expect(reporter).to receive(:update_current_resource_report).with(nil)
541
+ expect(reporter).to receive(:clear_current_resource_report)
552
542
  reporter.resource_completed(new_resource)
553
543
  end
554
544
  end
@@ -0,0 +1,59 @@
1
+ #
2
+ # Copyright:: Copyright 2013-2016, Chef Software Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require "spec_helper"
19
+ require "chef/deprecated"
20
+
21
+ describe Chef::Deprecated do
22
+ class TestDeprecation < Chef::Deprecated::Base
23
+ def id; 999; end
24
+
25
+ def target; "test.html"; end
26
+
27
+ def link; "#{Chef::Deprecated::Base::BASE_URL}test.html"; end
28
+ end
29
+
30
+ context "loading a deprecation class" do
31
+ it "loads the correct class" do
32
+ expect(Chef::Deprecated.create(:test_deprecation)).to be_an_instance_of(Chef::Deprecated::TestDeprecation)
33
+ end
34
+
35
+ it "optionally sets a message" do
36
+ deprecation = Chef::Deprecated.create(:test_deprecation, "A test message")
37
+ expect(deprecation.message).to eql("A test message")
38
+ end
39
+
40
+ it "optionally sets the location" do
41
+ deprecation = Chef::Deprecated.create(:test_deprecation, nil, "A test location")
42
+ expect(deprecation.location).to eql("A test location")
43
+ end
44
+ end
45
+
46
+ context "formatting deprecation warnings" do
47
+ let(:base_url) { Chef::Deprecated::Base::BASE_URL }
48
+ let(:message) { "A test message" }
49
+ let(:location) { "the location" }
50
+
51
+ it "displays the full URL" do
52
+ expect(Chef::Deprecated::TestDeprecation.new().url).to eql("#{base_url}test.html")
53
+ end
54
+
55
+ it "formats a complete deprecation message" do
56
+ expect(Chef::Deprecated::TestDeprecation.new(message, location).inspect).to eql("#{message} (CHEF-999)#{location}.\nhttps://docs.chef.io/deprecations_test.html")
57
+ end
58
+ end
59
+ end
@@ -65,15 +65,8 @@ describe Chef::Deprecation do
65
65
  end
66
66
 
67
67
  context "deprecation warning messages" do
68
- RSpec::Matchers.define_negated_matcher :a_non_empty_array, :be_empty
69
-
70
68
  it "should be enabled for deprecated methods" do
71
- expect(Chef::Log).to receive(:warn).with(a_non_empty_array)
72
- TestClass.new.deprecated_method(10)
73
- end
74
-
75
- it "should contain stack trace" do
76
- expect(Chef::Log).to receive(:warn).with(a_string_including(".rb"))
69
+ expect(Chef).to receive(:deprecated).with(:internal_api, /Method.*of 'TestClass'/)
77
70
  TestClass.new.deprecated_method(10)
78
71
  end
79
72
  end
@@ -37,7 +37,7 @@ describe Chef::Handler do
37
37
  @run_status.exception = @exception
38
38
  @run_context = Chef::RunContext.new(@node, {}, @events)
39
39
  @all_resources = [Chef::Resource::Cat.new("lolz"), Chef::Resource::ZenMaster.new("tzu")]
40
- @all_resources.first.updated = true
40
+ @all_resources.first.updated_by_last_action true
41
41
  @run_context.resource_collection.all_resources.replace(@all_resources)
42
42
  @run_status.run_context = @run_context
43
43
  @start_time = Time.now
@@ -118,7 +118,7 @@ describe Chef::Handler do
118
118
  before do
119
119
  @run_context = Chef::RunContext.new(@node, {}, @events)
120
120
  @all_resources = [Chef::Resource::Cat.new("foo"), Chef::Resource::ZenMaster.new("moo")]
121
- @all_resources.first.updated = true
121
+ @all_resources.first.updated_by_last_action true
122
122
  @run_context.resource_collection.all_resources.replace(@all_resources)
123
123
  @run_status.run_context = @run_context
124
124
  @start_time = Time.now
@@ -34,6 +34,22 @@ describe Chef::Knife::ClientDelete do
34
34
  @knife.run
35
35
  end
36
36
 
37
+ context "receives multiple clients" do
38
+ let(:clients) { %w{ "adam", "ben", "charlie" } }
39
+
40
+ before(:each) do
41
+ @knife.name_args = clients
42
+ end
43
+
44
+ it "deletes all clients" do
45
+ clients.each do |client|
46
+ expect(@knife).to receive(:delete_object).with(Chef::ApiClientV1, client, "client")
47
+ end
48
+
49
+ @knife.run
50
+ end
51
+ end
52
+
37
53
  it "should print usage and exit when a client name is not provided" do
38
54
  @knife.name_args = []
39
55
  expect(@knife).to receive(:show_usage)
@@ -38,7 +38,7 @@ describe Chef::Knife::Configure do
38
38
  let(:default_validator_key) { "/etc/chef-server/chef-validator.pem" }
39
39
  let(:default_validator_key_win32) { File.expand_path(default_validator_key) }
40
40
 
41
- let(:default_server_url) { "https://#{fqdn}:443" }
41
+ let(:default_server_url) { "https://#{fqdn}/organizations/myorg" }
42
42
 
43
43
  it "asks the user for the URL of the chef server" do
44
44
  @knife.ask_user_for_config
@@ -19,63 +19,96 @@
19
19
  require "spec_helper"
20
20
 
21
21
  describe Chef::Knife::CookbookMetadata do
22
+ let(:knife) do
23
+ knife = Chef::Knife::CookbookMetadata.new
24
+ knife.name_args = ["foobar"]
25
+ knife
26
+ end
27
+
28
+ let(:cookbook_dir) { Dir.mktmpdir }
29
+
30
+ let(:stdout) { StringIO.new }
31
+
32
+ let(:stderr) { StringIO.new }
33
+
22
34
  before(:each) do
23
- @knife = Chef::Knife::CookbookMetadata.new
24
- @knife.name_args = ["foobar"]
25
- @cookbook_dir = Dir.mktmpdir
26
- @json_data = '{ "version": "1.0.0" }'
27
- @stdout = StringIO.new
28
- @stderr = StringIO.new
29
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
30
- allow(@knife.ui).to receive(:stderr).and_return(@stderr)
35
+ allow(knife.ui).to receive(:stdout).and_return(stdout)
36
+ allow(knife.ui).to receive(:stderr).and_return(stderr)
37
+ end
38
+
39
+ def create_metadata_rb(**kwargs)
40
+ name = kwargs[:name]
41
+ Dir.mkdir("#{cookbook_dir}/#{name}")
42
+ File.open("#{cookbook_dir}/#{name}/metadata.rb", "w+") do |f|
43
+ kwargs.each do |key, value|
44
+ if value.is_a?(Array)
45
+ f.puts "#{key} #{value.map { |v| "\"#{v}\"" }.join(", ")}"
46
+ else
47
+ f.puts "#{key} \"#{value}\""
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ def create_metadata_json(**kwargs)
54
+ name = kwargs[:name]
55
+ Dir.mkdir("#{cookbook_dir}/#{name}")
56
+ File.open("#{cookbook_dir}/#{name}/metadata.json", "w+") do |f|
57
+ f.write(FFI_Yajl::Encoder.encode(kwargs))
58
+ end
59
+ end
60
+
61
+ def create_invalid_json
62
+ Dir.mkdir("#{cookbook_dir}/foobar")
63
+ File.open("#{cookbook_dir}/foobar/metadata.json", "w+") do |f|
64
+ f.write <<-EOH
65
+ { "version": "1.0.0", {ImInvalid}}
66
+ EOH
67
+ end
31
68
  end
32
69
 
33
70
  describe "run" do
34
71
  it "should print an error and exit if a cookbook name was not provided" do
35
- @knife.name_args = []
36
- expect(@knife.ui).to receive(:error).with(/you must specify the cookbook.+use the --all/i)
37
- expect { @knife.run }.to raise_error(SystemExit)
72
+ knife.name_args = []
73
+ expect(knife.ui).to receive(:error).with(/you must specify the cookbook.+use the --all/i)
74
+ expect { knife.run }.to raise_error(SystemExit)
38
75
  end
39
76
 
40
77
  it "should print an error and exit if an empty cookbook name was provided" do
41
- @knife.name_args = [""]
42
- expect(@knife.ui).to receive(:error).with(/you must specify the cookbook.+use the --all/i)
43
- expect { @knife.run }.to raise_error(SystemExit)
78
+ knife.name_args = [""]
79
+ expect(knife.ui).to receive(:error).with(/you must specify the cookbook.+use the --all/i)
80
+ expect { knife.run }.to raise_error(SystemExit)
44
81
  end
45
82
 
46
83
  it "should generate the metadata for the cookbook" do
47
- expect(@knife).to receive(:generate_metadata).with("foobar")
48
- @knife.run
84
+ expect(knife).to receive(:generate_metadata).with("foobar")
85
+ knife.run
49
86
  end
50
87
 
51
88
  describe "with -a or --all" do
52
89
  before(:each) do
53
- @knife.config[:all] = true
54
- @foo = Chef::CookbookVersion.new("foo", "/tmp/blah")
55
- @foo.version = "1.0.0"
56
- @bar = Chef::CookbookVersion.new("bar", "/tmp/blah")
57
- @bar.version = "2.0.0"
58
- @cookbook_loader = {
59
- "foo" => @foo,
60
- "bar" => @bar,
61
- }
62
- expect(@cookbook_loader).to receive(:load_cookbooks).and_return(@cookbook_loader)
63
- expect(@knife).to receive(:generate_metadata).with("foo")
64
- expect(@knife).to receive(:generate_metadata).with("bar")
90
+ Chef::Config[:cookbook_path] = cookbook_dir
91
+ knife.config[:all] = true
92
+ create_metadata_rb(name: "foo", version: "1.0.0")
93
+ create_metadata_rb(name: "bar", version: "2.0.0")
94
+ expect(knife).to receive(:generate_metadata).with("foo").and_call_original
95
+ expect(knife).to receive(:generate_metadata).with("bar").and_call_original
65
96
  end
66
97
 
67
98
  it "should generate the metadata for each cookbook" do
68
- Chef::Config[:cookbook_path] = @cookbook_dir
69
- expect(Chef::CookbookLoader).to receive(:new).with(@cookbook_dir).and_return(@cookbook_loader)
70
- @knife.run
99
+ expect(Chef::CookbookLoader).to receive(:new).with(cookbook_dir).and_call_original
100
+ knife.run
101
+ expect(stderr.string).to match /generating metadata for foo from #{cookbook_dir}\/foo\/metadata\.rb/im
102
+ expect(stderr.string).to match /generating metadata for bar from #{cookbook_dir}\/bar\/metadata\.rb/im
71
103
  end
72
104
 
73
- describe "and with -o or --cookbook-path" do
74
- it "should look in the provided path and generate cookbook metadata" do
75
- @knife.config[:cookbook_path] = "/opt/chef/cookbooks"
76
- expect(Chef::CookbookLoader).to receive(:new).with("/opt/chef/cookbooks").and_return(@cookbook_loader)
77
- @knife.run
78
- end
105
+ it "with -o or --cookbook_path should look in the provided path and generate cookbook metadata" do
106
+ Chef::Config[:cookbook_path] = "/dev/null"
107
+ knife.config[:cookbook_path] = cookbook_dir
108
+ expect(Chef::CookbookLoader).to receive(:new).with(cookbook_dir).and_call_original
109
+ knife.run
110
+ expect(stderr.string).to match /generating metadata for foo from #{cookbook_dir}\/foo\/metadata\.rb/im
111
+ expect(stderr.string).to match /generating metadata for bar from #{cookbook_dir}\/bar\/metadata\.rb/im
79
112
  end
80
113
  end
81
114
 
@@ -83,97 +116,67 @@ describe Chef::Knife::CookbookMetadata do
83
116
 
84
117
  describe "generate_metadata" do
85
118
  before(:each) do
86
- @knife.config[:cookbook_path] = @cookbook_dir
87
- allow(File).to receive(:expand_path).with("#{@cookbook_dir}/foobar/metadata.rb").
88
- and_return("#{@cookbook_dir}/foobar/metadata.rb")
119
+ Chef::Config[:cookbook_path] = cookbook_dir
89
120
  end
90
121
 
91
122
  it "should generate the metadata from metadata.rb if it exists" do
92
- expect(File).to receive(:exists?).with("#{@cookbook_dir}/foobar/metadata.rb").
93
- and_return(true)
94
- expect(@knife).to receive(:generate_metadata_from_file).with("foobar", "#{@cookbook_dir}/foobar/metadata.rb")
95
- @knife.run
123
+ create_metadata_rb(name: "foobar", version: "1.0.0")
124
+ expect(knife).to receive(:generate_metadata_from_file).with("foobar", "#{cookbook_dir}/foobar/metadata.rb").and_call_original
125
+ knife.run
126
+ expect(File.exist?("#{cookbook_dir}/foobar/metadata.json")).to be true
127
+ json = FFI_Yajl::Parser.parse(IO.read("#{cookbook_dir}/foobar/metadata.json"))
128
+ expect(json["name"]).to eql("foobar")
129
+ expect(json["version"]).to eql("1.0.0")
96
130
  end
97
131
 
98
132
  it "should validate the metadata json if metadata.rb does not exist" do
99
- expect(File).to receive(:exists?).with("#{@cookbook_dir}/foobar/metadata.rb").
100
- and_return(false)
101
- expect(@knife).to receive(:validate_metadata_json).with(@cookbook_dir, "foobar")
102
- @knife.run
133
+ create_metadata_json(name: "foobar", version: "1.0.0")
134
+ expect(knife).to receive(:validate_metadata_json).with(cookbook_dir, "foobar").and_call_original
135
+ knife.run
103
136
  end
104
137
  end
105
138
 
106
- describe "generate_metadata_from_file" do
139
+ describe "validation errors" do
107
140
  before(:each) do
108
- @metadata_mock = double("metadata")
109
- @json_file_mock = double("json_file")
110
- end
111
-
112
- it "should generate the metatdata json from metatdata.rb" do
113
- allow(Chef::Cookbook::Metadata).to receive(:new).and_return(@metadata_mock)
114
- expect(@metadata_mock).to receive(:name).with("foobar")
115
- expect(@metadata_mock).to receive(:from_file).with("#{@cookbook_dir}/foobar/metadata.rb")
116
- expect(File).to receive(:open).with("#{@cookbook_dir}/foobar/metadata.json", "w").
117
- and_yield(@json_file_mock)
118
- expect(@json_file_mock).to receive(:write).with(@json_data)
119
- expect(Chef::JSONCompat).to receive(:to_json_pretty).with(@metadata_mock).
120
- and_return(@json_data)
121
- @knife.generate_metadata_from_file("foobar", "#{@cookbook_dir}/foobar/metadata.rb")
122
- expect(@stderr.string).to match /generating metadata for foobar from #{@cookbook_dir}\/foobar\/metadata\.rb/im
123
- end
124
-
125
- { Chef::Exceptions::ObsoleteDependencySyntax => "obsolote dependency",
126
- Chef::Exceptions::InvalidVersionConstraint => "invalid version constraint",
127
- }.each_pair do |klass, description|
128
- it "should print an error and exit when an #{description} syntax exception is encountered" do
129
- exception = klass.new("#{description} blah")
130
- allow(Chef::Cookbook::Metadata).to receive(:new).and_raise(exception)
131
- expect do
132
- @knife.generate_metadata_from_file("foobar", "#{@cookbook_dir}/foobar/metadata.rb")
133
- end.to raise_error(SystemExit)
134
- expect(@stderr.string).to match /error: the cookbook 'foobar' contains invalid or obsolete metadata syntax/im
135
- expect(@stderr.string).to match /in #{@cookbook_dir}\/foobar\/metadata\.rb/im
136
- expect(@stderr.string).to match /#{description} blah/im
137
- end
141
+ Chef::Config[:cookbook_path] = cookbook_dir
138
142
  end
139
- end
140
143
 
141
- describe "validate_metadata_json" do
142
- it "should validate the metadata json" do
143
- expect(File).to receive(:exist?).with("#{@cookbook_dir}/foobar/metadata.json").
144
- and_return(true)
145
- expect(IO).to receive(:read).with("#{@cookbook_dir}/foobar/metadata.json").
146
- and_return(@json_data)
147
- expect(Chef::Cookbook::Metadata).to receive(:validate_json).with(@json_data)
148
- @knife.validate_metadata_json(@cookbook_dir, "foobar")
144
+ it "should fail for obsolete operators in metadata.rb" do
145
+ create_metadata_rb(name: "foobar", version: "1.0.0", depends: [ "foo:bar", ">> 0.2" ])
146
+ expect(Chef::Cookbook::Metadata).not_to receive(:validate_json)
147
+ expect { knife.run }.to raise_error(SystemExit)
148
+ expect(stderr.string).to match /error: the cookbook 'foobar' contains invalid or obsolete metadata syntax/im
149
149
  end
150
150
 
151
- it "should not try to validate the metadata json if the file does not exist" do
152
- expect(File).to receive(:exist?).with("#{@cookbook_dir}/foobar/metadata.json").
153
- and_return(false)
154
- expect(IO).not_to receive(:read)
151
+ it "should fail for obsolete format in metadata.rb (sadly)" do
152
+ create_metadata_rb(name: "foobar", version: "1.0.0", depends: [ "foo:bar", "> 0.2", "< 1.0" ])
155
153
  expect(Chef::Cookbook::Metadata).not_to receive(:validate_json)
156
- @knife.validate_metadata_json(@cookbook_dir, "foobar")
157
- end
158
-
159
- { Chef::Exceptions::ObsoleteDependencySyntax => "obsolote dependency",
160
- Chef::Exceptions::InvalidVersionConstraint => "invalid version constraint",
161
- }.each_pair do |klass, description|
162
- it "should print an error and exit when an #{description} syntax exception is encountered" do
163
- expect(File).to receive(:exist?).with("#{@cookbook_dir}/foobar/metadata.json").
164
- and_return(true)
165
- expect(IO).to receive(:read).with("#{@cookbook_dir}/foobar/metadata.json").
166
- and_return(@json_data)
167
- exception = klass.new("#{description} blah")
168
- allow(Chef::Cookbook::Metadata).to receive(:validate_json).and_raise(exception)
169
- expect do
170
- @knife.validate_metadata_json(@cookbook_dir, "foobar")
171
- end.to raise_error(SystemExit)
172
- expect(@stderr.string).to match /error: the cookbook 'foobar' contains invalid or obsolete metadata syntax/im
173
- expect(@stderr.string).to match /in #{@cookbook_dir}\/foobar\/metadata\.json/im
174
- expect(@stderr.string).to match /#{description} blah/im
175
- end
154
+ expect { knife.run }.to raise_error(SystemExit)
155
+ expect(stderr.string).to match /error: the cookbook 'foobar' contains invalid or obsolete metadata syntax/im
156
+ end
157
+
158
+ it "should fail for obsolete operators in metadata.json" do
159
+ create_metadata_json(name: "foobar", version: "1.0.0", dependencies: { "foo:bar" => ">> 0.2" })
160
+ expect { knife.run }.to raise_error(SystemExit)
161
+ expect(stderr.string).to match /error: the cookbook 'foobar' contains invalid or obsolete metadata syntax/im
176
162
  end
177
- end
178
163
 
164
+ it "should not fail for unknown field in metadata.rb" do
165
+ create_metadata_rb(name: "sounders", version: "2.0.0", beats: "toronto")
166
+ expect(Chef::Cookbook::Metadata).not_to receive(:validate_json)
167
+ expect { knife.run }.not_to raise_error
168
+ expect(stderr.string).to eql("")
169
+ end
170
+
171
+ it "should not fail for unknown field in metadata.json" do
172
+ create_metadata_json(name: "sounders", version: "2.0.0", beats: "toronto")
173
+ expect { knife.run }.not_to raise_error
174
+ expect(stderr.string).to eql("")
175
+ end
176
+
177
+ it "should fail on unparsable json" do
178
+ create_invalid_json
179
+ expect { knife.run }.to raise_error(Chef::Exceptions::JSON::ParseError)
180
+ end
181
+ end
179
182
  end