chef 12.16.42 → 12.17.44

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