chef 11.12.0.alpha.1 → 11.12.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/lib/chef/api_client/registration.rb +46 -9
  3. data/lib/chef/application.rb +1 -0
  4. data/lib/chef/application/client.rb +25 -24
  5. data/lib/chef/client.rb +34 -0
  6. data/lib/chef/config.rb +11 -0
  7. data/lib/chef/cookbook/chefignore.rb +10 -2
  8. data/lib/chef/cookbook/metadata.rb +31 -3
  9. data/lib/chef/cookbook/synchronizer.rb +2 -2
  10. data/lib/chef/cookbook/syntax_check.rb +4 -4
  11. data/lib/chef/encrypted_data_bag_item.rb +37 -1
  12. data/lib/chef/exceptions.rb +1 -0
  13. data/lib/chef/guard_interpreter/default_guard_interpreter.rb +42 -0
  14. data/lib/chef/guard_interpreter/resource_guard_interpreter.rb +122 -0
  15. data/lib/chef/http.rb +0 -1
  16. data/lib/chef/http/decompressor.rb +7 -4
  17. data/lib/chef/http/simple.rb +5 -0
  18. data/lib/chef/http/validate_content_length.rb +28 -12
  19. data/lib/chef/knife.rb +1 -0
  20. data/lib/chef/knife/client_bulk_delete.rb +48 -9
  21. data/lib/chef/knife/client_delete.rb +4 -4
  22. data/lib/chef/knife/cookbook_bulk_delete.rb +1 -1
  23. data/lib/chef/knife/cookbook_upload.rb +17 -7
  24. data/lib/chef/knife/core/bootstrap_context.rb +1 -1
  25. data/lib/chef/knife/core/ui.rb +42 -5
  26. data/lib/chef/knife/node_run_list_add.rb +31 -2
  27. data/lib/chef/knife/ssh.rb +44 -31
  28. data/lib/chef/knife/ssl_check.rb +213 -0
  29. data/lib/chef/knife/ssl_fetch.rb +145 -0
  30. data/lib/chef/mixin/deep_merge.rb +13 -5
  31. data/lib/chef/mixin/shell_out.rb +9 -3
  32. data/lib/chef/node.rb +23 -4
  33. data/lib/chef/node/immutable_collections.rb +32 -0
  34. data/lib/chef/platform/provider_mapping.rb +21 -18
  35. data/lib/chef/platform/query_helpers.rb +10 -2
  36. data/lib/chef/policy_builder/expand_node_object.rb +3 -6
  37. data/lib/chef/provider/cron.rb +25 -3
  38. data/lib/chef/provider/mount/mount.rb +1 -1
  39. data/lib/chef/provider/package/dpkg.rb +2 -1
  40. data/lib/chef/provider/package/windows.rb +80 -0
  41. data/lib/chef/provider/package/windows/msi.rb +69 -0
  42. data/lib/chef/provider/powershell_script.rb +19 -6
  43. data/lib/chef/provider/service/solaris.rb +11 -7
  44. data/lib/chef/resource.rb +18 -5
  45. data/lib/chef/resource/conditional.rb +20 -7
  46. data/lib/chef/resource/cron.rb +18 -2
  47. data/lib/chef/resource/execute.rb +0 -2
  48. data/lib/chef/resource/powershell_script.rb +23 -1
  49. data/lib/chef/resource/script.rb +25 -0
  50. data/lib/chef/resource/subversion.rb +4 -0
  51. data/lib/chef/resource/windows_package.rb +79 -0
  52. data/lib/chef/resource/windows_script.rb +0 -5
  53. data/lib/chef/resources.rb +1 -0
  54. data/lib/chef/rest.rb +6 -1
  55. data/lib/chef/run_context.rb +22 -2
  56. data/lib/chef/run_context/cookbook_compiler.rb +12 -0
  57. data/lib/chef/util/editor.rb +92 -0
  58. data/lib/chef/util/file_edit.rb +22 -54
  59. data/lib/chef/version.rb +2 -2
  60. data/lib/chef/win32/api/installer.rb +166 -0
  61. data/lib/chef/win32/version.rb +8 -0
  62. data/spec/data/standalone_cookbook/Gemfile +1 -0
  63. data/spec/data/standalone_cookbook/chefignore +9 -0
  64. data/spec/data/standalone_cookbook/recipes/default.rb +3 -0
  65. data/spec/data/standalone_cookbook/vendor/bundle/ruby/2.0.0/gems/multi_json-1.9.0/lib/multi_json.rb +1 -0
  66. data/spec/functional/resource/powershell_spec.rb +262 -1
  67. data/spec/functional/win32/versions_spec.rb +3 -3
  68. data/spec/integration/knife/chefignore_spec.rb +1 -2
  69. data/spec/integration/knife/raw_spec.rb +8 -13
  70. data/spec/integration/knife/redirection_spec.rb +6 -14
  71. data/spec/integration/solo/solo_spec.rb +19 -0
  72. data/spec/support/shared/functional/windows_script.rb +1 -1
  73. data/spec/support/shared/integration/app_server_support.rb +42 -0
  74. data/spec/support/shared/integration/integration_helper.rb +1 -0
  75. data/spec/support/shared/unit/script_resource.rb +38 -0
  76. data/spec/unit/api_client/registration_spec.rb +109 -38
  77. data/spec/unit/application/client_spec.rb +48 -1
  78. data/spec/unit/cookbook/chefignore_spec.rb +10 -0
  79. data/spec/unit/cookbook/metadata_spec.rb +45 -1
  80. data/spec/unit/cookbook/syntax_check_spec.rb +28 -0
  81. data/spec/unit/cookbook_spec.rb +0 -10
  82. data/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb +56 -0
  83. data/spec/unit/http/simple_spec.rb +32 -0
  84. data/spec/unit/http/validate_content_length_spec.rb +187 -0
  85. data/spec/unit/knife/bootstrap_spec.rb +13 -4
  86. data/spec/unit/knife/client_bulk_delete_spec.rb +123 -38
  87. data/spec/unit/knife/client_delete_spec.rb +4 -4
  88. data/spec/unit/knife/cookbook_upload_spec.rb +181 -88
  89. data/spec/unit/knife/core/bootstrap_context_spec.rb +11 -1
  90. data/spec/unit/knife/core/ui_spec.rb +109 -38
  91. data/spec/unit/knife/node_run_list_add_spec.rb +24 -1
  92. data/spec/unit/knife/ssh_spec.rb +17 -6
  93. data/spec/unit/knife/ssl_check_spec.rb +187 -0
  94. data/spec/unit/knife/ssl_fetch_spec.rb +151 -0
  95. data/spec/unit/mixin/deep_merge_spec.rb +17 -0
  96. data/spec/unit/node/immutable_collections_spec.rb +55 -0
  97. data/spec/unit/node_spec.rb +9 -0
  98. data/spec/unit/platform/query_helpers_spec.rb +32 -0
  99. data/spec/unit/platform_spec.rb +193 -175
  100. data/spec/unit/policy_builder/expand_node_object_spec.rb +1 -1
  101. data/spec/unit/provider/cron_spec.rb +175 -1
  102. data/spec/unit/provider/mount/mount_spec.rb +33 -3
  103. data/spec/unit/provider/package/dpkg_spec.rb +4 -0
  104. data/spec/unit/provider/package/windows/msi_spec.rb +60 -0
  105. data/spec/unit/provider/package/windows_spec.rb +80 -0
  106. data/spec/unit/provider/service/macosx_spec.rb +3 -3
  107. data/spec/unit/provider/service/solaris_smf_service_spec.rb +35 -10
  108. data/spec/unit/pure_application_spec.rb +32 -0
  109. data/spec/unit/recipe_spec.rb +4 -0
  110. data/spec/unit/resource/conditional_spec.rb +13 -12
  111. data/spec/unit/resource/cron_spec.rb +7 -2
  112. data/spec/unit/resource/powershell_spec.rb +85 -2
  113. data/spec/unit/resource/subversion_spec.rb +5 -0
  114. data/spec/unit/resource/windows_package_spec.rb +74 -0
  115. data/spec/unit/resource_spec.rb +23 -1
  116. data/spec/unit/rest_spec.rb +15 -0
  117. data/spec/unit/run_context/cookbook_compiler_spec.rb +12 -0
  118. data/spec/unit/run_context_spec.rb +7 -0
  119. data/spec/unit/util/editor_spec.rb +152 -0
  120. data/spec/unit/util/file_edit_spec.rb +37 -1
  121. metadata +41 -30
@@ -22,7 +22,7 @@ if Chef::Platform.windows?
22
22
  require 'ruby-wmi'
23
23
  end
24
24
 
25
- describe "Chef::ReservedNames::Win32::Version", :windows_only do
25
+ describe "Chef::ReservedNames::Win32::Version", :windows_only, :not_supported_on_win2k3 do
26
26
  before do
27
27
 
28
28
  host = WMI::Win32_OperatingSystem.find(:first)
@@ -57,7 +57,7 @@ describe "Chef::ReservedNames::Win32::Version", :windows_only do
57
57
  end
58
58
  end
59
59
  end
60
-
60
+
61
61
  context "Win32 version object" do
62
62
  it "should have have one method for each marketing version" do
63
63
  versions = 0
@@ -88,7 +88,7 @@ describe "Chef::ReservedNames::Win32::Version", :windows_only do
88
88
  for_each_windows_version { |method_name| @version.send(method_name.to_sym) }
89
89
  end
90
90
  end
91
-
91
+
92
92
  context "Windows Operating System version" do
93
93
  it "should match the version from WMI" do
94
94
  @current_os_version.should include(@version.marketing_name)
@@ -38,14 +38,13 @@ describe 'chefignore tests' do
38
38
  file 'data_bags/bag1/chefignore', chefignore
39
39
  file 'cookbooks/cookbook1/chefignore', chefignore
40
40
 
41
- it 'nothing is ignored' do
41
+ it 'matching files and directories get ignored' do
42
42
  # NOTE: many of the "chefignore" files should probably not show up
43
43
  # themselves, but we have other tests that talk about that
44
44
  knife('list --local -Rfp /').should_succeed <<EOM
45
45
  /cookbooks/
46
46
  /cookbooks/cookbook1/
47
47
  /cookbooks/cookbook1/chefignore
48
- /cookbooks/cookbook1/x.json
49
48
  /data_bags/
50
49
  /data_bags/bag1/
51
50
  /data_bags/bag1/x.json
@@ -22,6 +22,7 @@ require 'chef/knife/show'
22
22
  describe 'knife raw' do
23
23
  extend IntegrationSupport
24
24
  include KnifeSupport
25
+ include AppServerSupport
25
26
 
26
27
  when_the_chef_server "has one of each thing" do
27
28
  client 'x', '{}'
@@ -55,7 +56,7 @@ EOM
55
56
  end
56
57
 
57
58
  it 'knife raw /blarghle returns 404' do
58
- knife('raw /blarghle').should_fail(/ERROR: Server responded with error 404 "Not Found"/)
59
+ knife('raw /blarghle').should_fail(/ERROR: Server responded with error 404 "Not Found\s*"/)
59
60
  end
60
61
 
61
62
  it 'knife raw -m DELETE /roles/x succeeds', :pending => (RUBY_VERSION < "1.9") do
@@ -165,19 +166,16 @@ EOM
165
166
 
166
167
  context 'When a server returns raw json' do
167
168
  before :each do
168
- @real_chef_server_url = Chef::Config.chef_server_url
169
169
  Chef::Config.chef_server_url = "http://127.0.0.1:9018"
170
170
  app = lambda do |env|
171
171
  [200, {'Content-Type' => 'application/json' }, ['{ "x": "y", "a": "b" }'] ]
172
172
  end
173
- @raw_server = Puma::Server.new(app, Puma::Events.new(STDERR, STDOUT))
174
- @raw_server.add_tcp_listener("127.0.0.1", 9018)
175
- @raw_server.run
173
+ @raw_server, @raw_server_thread = start_app_server(app, 9018)
176
174
  end
177
175
 
178
176
  after :each do
179
- Chef::Config.chef_server_url = @real_chef_server_url
180
- @raw_server.stop(true)
177
+ @raw_server.shutdown if @raw_server
178
+ @raw_server_thread.kill if @raw_server_thread
181
179
  end
182
180
 
183
181
  it 'knife raw /blah returns the prettified json', :pending => (RUBY_VERSION < "1.9") do
@@ -198,19 +196,16 @@ EOM
198
196
 
199
197
  context 'When a server returns text' do
200
198
  before :each do
201
- @real_chef_server_url = Chef::Config.chef_server_url
202
199
  Chef::Config.chef_server_url = "http://127.0.0.1:9018"
203
200
  app = lambda do |env|
204
201
  [200, {'Content-Type' => 'text' }, ['{ "x": "y", "a": "b" }'] ]
205
202
  end
206
- @raw_server = Puma::Server.new(app, Puma::Events.new(STDERR, STDOUT))
207
- @raw_server.add_tcp_listener("127.0.0.1", 9018)
208
- @raw_server.run
203
+ @raw_server, @raw_server_thread = start_app_server(app, 9018)
209
204
  end
210
205
 
211
206
  after :each do
212
- Chef::Config.chef_server_url = @real_chef_server_url
213
- @raw_server.stop(true)
207
+ @raw_server.shutdown if @raw_server
208
+ @raw_server_thread.kill if @raw_server_thread
214
209
  end
215
210
 
216
211
  it 'knife raw /blah returns the raw text' do
@@ -15,38 +15,30 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
 
18
- require 'puma'
19
18
  require 'support/shared/integration/integration_helper'
20
19
  require 'chef/knife/list'
21
20
 
22
21
  describe 'redirection' do
23
22
  extend IntegrationSupport
24
23
  include KnifeSupport
24
+ include AppServerSupport
25
25
 
26
26
  when_the_chef_server 'has a role' do
27
27
  role 'x', {}
28
28
 
29
29
  context 'and another server redirects to it with 302' do
30
30
  before :each do
31
- @real_chef_server_url = Chef::Config.chef_server_url
31
+ real_chef_server_url = Chef::Config.chef_server_url
32
32
  Chef::Config.chef_server_url = "http://127.0.0.1:9018"
33
33
  app = lambda do |env|
34
- [302, {'Content-Type' => 'text','Location' => "#{@real_chef_server_url}#{env['PATH_INFO']}" }, ['302 found'] ]
35
- end
36
- @redirector_server = Puma::Server.new(app, Puma::Events.new(STDERR, STDOUT))
37
- @redirector_server.add_tcp_listener("127.0.0.1", 9018)
38
- @redirector_server.run
39
- Timeout::timeout(5) do
40
- until @redirector_server.running
41
- sleep(0.01)
42
- end
43
- raise @server_error if @server_error
34
+ [302, {'Content-Type' => 'text','Location' => "#{real_chef_server_url}#{env['PATH_INFO']}" }, ['302 found'] ]
44
35
  end
36
+ @redirector_server, @redirector_server_thread = start_app_server(app, 9018)
45
37
  end
46
38
 
47
39
  after :each do
48
- Chef::Config.chef_server_url = @real_chef_server_url
49
- @redirector_server.stop(true)
40
+ @redirector_server.shutdown if @redirector_server
41
+ @redirector_thread.kill if @redirector_thread
50
42
  end
51
43
 
52
44
  it 'knife list /roles returns the role' do
@@ -42,6 +42,25 @@ E
42
42
 
43
43
  end
44
44
 
45
+ when_the_repository "has a cookbook with an undeclared dependency" do
46
+ file 'cookbooks/x/metadata.rb', 'version "1.0.0"'
47
+ file 'cookbooks/x/recipes/default.rb', 'include_recipe "ancient::aliens"'
48
+
49
+ file 'cookbooks/ancient/metadata.rb', 'version "1.0.0"'
50
+ file 'cookbooks/ancient/recipes/aliens.rb', 'print "it was aliens"'
51
+
52
+ it "should exit with an error" do
53
+ file 'config/solo.rb', <<EOM
54
+ cookbook_path "#{path_to('cookbooks')}"
55
+ file_cache_path "#{path_to('config/cache')}"
56
+ EOM
57
+ result = shell_out("ruby bin/chef-solo -c \"#{path_to('config/solo.rb')}\" -o 'x::default' -l debug", :cwd => chef_dir)
58
+ result.exitstatus.should == 0 # For CHEF-5120 this becomes 1
59
+ result.stdout.should include("WARN: MissingCookbookDependency")
60
+ end
61
+ end
62
+
63
+
45
64
  when_the_repository "has a cookbook with a recipe with sleep" do
46
65
  directory 'logs'
47
66
  file 'logs/runs.log', ''
@@ -38,7 +38,7 @@ shared_context Chef::Resource::WindowsScript do
38
38
  end
39
39
 
40
40
  before(:each) do
41
- k File.delete(script_output_path) if File.exists?(script_output_path)
41
+ File.delete(script_output_path) if File.exists?(script_output_path)
42
42
  end
43
43
 
44
44
  after(:each) do
@@ -0,0 +1,42 @@
1
+ #
2
+ # Author:: John Keiser (<jkeiser@opscode.com>)
3
+ # Author:: Ho-Sheng Hsiao (<hosh@opscode.com>)
4
+ # Copyright:: Copyright (c) 2012, 2013 Opscode, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'rack'
21
+ require 'stringio'
22
+
23
+ module AppServerSupport
24
+ def start_app_server(app, port)
25
+ server = nil
26
+ thread = Thread.new do
27
+ Rack::Handler::WEBrick.run(app,
28
+ :Port => 9018,
29
+ :AccessLog => [],
30
+ :Logger => WEBrick::Log::new(StringIO.new, 7)
31
+ ) do |found_server|
32
+ server = found_server
33
+ end
34
+ end
35
+ Timeout::timeout(5) do
36
+ until server && server.status == :Running
37
+ sleep(0.01)
38
+ end
39
+ end
40
+ [server, thread]
41
+ end
42
+ end
@@ -23,6 +23,7 @@ require 'chef/config'
23
23
  require 'chef_zero/rspec'
24
24
  require 'json'
25
25
  require 'support/shared/integration/knife_support'
26
+ require 'support/shared/integration/app_server_support'
26
27
  require 'spec_helper'
27
28
 
28
29
  module IntegrationSupport
@@ -48,5 +48,43 @@ shared_examples_for "a script resource" do
48
48
  @resource.flags.should eql("-f")
49
49
  end
50
50
 
51
+ describe "when executing guards" do
52
+ let(:resource) { @resource }
53
+
54
+ before(:each) do
55
+ node = Chef::Node.new
56
+
57
+ node.automatic[:platform] = "debian"
58
+ node.automatic[:platform_version] = "6.0"
59
+
60
+ events = Chef::EventDispatch::Dispatcher.new
61
+ run_context = Chef::RunContext.new(node, {}, events)
62
+ resource.run_context = run_context
63
+ resource.code 'echo hi'
64
+ end
65
+
66
+ it "inherits exactly the :cwd, :environment, :group, :path, :user, and :umask attributes from a parent resource class" do
67
+ inherited_difference = Chef::Resource::Script.guard_inherited_attributes -
68
+ [:cwd, :environment, :group, :path, :user, :umask ]
69
+
70
+ inherited_difference.should == []
71
+ end
72
+
73
+ it "when guard_interpreter is set to the default value, the guard command string should be evaluated by command execution and not through a resource" do
74
+ Chef::Resource::Conditional.any_instance.should_not_receive(:evaluate_block)
75
+ Chef::Resource::Conditional.any_instance.should_receive(:evaluate_command).and_return(true)
76
+ Chef::GuardInterpreter::ResourceGuardInterpreter.any_instance.should_not_receive(:evaluate_action)
77
+ resource.only_if 'echo hi'
78
+ resource.should_skip?(:run).should == nil
79
+ end
80
+
81
+ it "when a valid guard_interpreter resource is specified, a block should be used to evaluate the guard" do
82
+ Chef::GuardInterpreter::DefaultGuardInterpreter.any_instance.should_not_receive(:evaluate)
83
+ Chef::GuardInterpreter::ResourceGuardInterpreter.any_instance.should_receive(:evaluate_action).and_return(true)
84
+ resource.guard_interpreter :script
85
+ resource.only_if 'echo hi'
86
+ resource.should_skip?(:run).should == nil
87
+ end
88
+ end
51
89
  end
52
90
 
@@ -22,16 +22,45 @@ require 'tempfile'
22
22
  require 'chef/api_client/registration'
23
23
 
24
24
  describe Chef::ApiClient::Registration do
25
+
25
26
  let(:key_location) do
26
27
  make_tmpname("client-registration-key")
27
28
  end
28
29
 
29
- let(:registration) { Chef::ApiClient::Registration.new("silent-bob", key_location) }
30
+ let(:client_name) { "silent-bob" }
31
+
32
+ subject(:registration) { Chef::ApiClient::Registration.new(client_name, key_location) }
30
33
 
31
- let :private_key_data do
34
+ let(:private_key_data) do
32
35
  File.open(Chef::Config[:validation_key], "r") {|f| f.read.chomp }
33
36
  end
34
37
 
38
+ let(:http_mock) { double("Chef::REST mock") }
39
+
40
+ let(:expected_post_data) do
41
+ { :name => client_name, :admin => false }
42
+ end
43
+
44
+ let(:expected_put_data) do
45
+ { :name => client_name, :admin => false, :private_key => true }
46
+ end
47
+
48
+ let(:server_v10_response) do
49
+ {"uri" => "https://chef.local/clients/#{client_name}",
50
+ "private_key" => "--begin rsa key etc--"}
51
+ end
52
+
53
+ # Server v11 includes `json_class` on all replies
54
+ let(:server_v11_response) do
55
+ response = Chef::ApiClient.new
56
+ response.name(client_name)
57
+ response.private_key("--begin rsa key etc--")
58
+ response
59
+ end
60
+
61
+ let(:response_409) { Net::HTTPConflict.new("1.1", "409", "Conflict") }
62
+ let(:exception_409) { Net::HTTPServerException.new("409 conflict", response_409) }
63
+
35
64
  before do
36
65
  Chef::Config[:validation_client_name] = "test-validator"
37
66
  Chef::Config[:validation_key] = File.expand_path('ssl/private_key.pem', CHEF_SPEC_DATA)
@@ -39,8 +68,6 @@ describe Chef::ApiClient::Registration do
39
68
 
40
69
  after do
41
70
  File.unlink(key_location) if File.exist?(key_location)
42
- Chef::Config[:validation_client_name] = nil
43
- Chef::Config[:validation_key] = nil
44
71
  end
45
72
 
46
73
  it "has an HTTP client configured with validator credentials" do
@@ -50,57 +77,107 @@ describe Chef::ApiClient::Registration do
50
77
  end
51
78
 
52
79
  describe "when creating/updating the client on the server" do
53
- let(:http_mock) { double("Chef::REST mock") }
54
-
55
80
  before do
56
81
  registration.stub(:http_api).and_return(http_mock)
57
82
  end
58
83
 
59
84
  it "creates a new ApiClient on the server using the validator identity" do
60
- response = {"uri" => "https://chef.local/clients/silent-bob",
61
- "private_key" => "--begin rsa key etc--"}
62
85
  http_mock.should_receive(:post).
63
- with("clients", :name => 'silent-bob', :admin => false).
64
- and_return(response)
65
- registration.create_or_update.should == response
86
+ with("clients", expected_post_data).
87
+ and_return(server_v10_response)
88
+ registration.create_or_update.should == server_v10_response
66
89
  registration.private_key.should == "--begin rsa key etc--"
67
90
  end
68
91
 
69
92
  context "and the client already exists on a Chef 10 server" do
70
93
  it "requests a new key from the server and saves it" do
71
- response = {"name" => "silent-bob", "private_key" => "--begin rsa key etc--" }
72
-
73
- response_409 = Net::HTTPConflict.new("1.1", "409", "Conflict")
74
- exception_409 = Net::HTTPServerException.new("409 conflict", response_409)
75
-
76
- http_mock.should_receive(:post).and_raise(exception_409)
94
+ http_mock.should_receive(:post).with("clients", expected_post_data).
95
+ and_raise(exception_409)
77
96
  http_mock.should_receive(:put).
78
- with("clients/silent-bob", :name => 'silent-bob', :admin => false, :private_key => true).
79
- and_return(response)
80
- registration.create_or_update.should == response
97
+ with("clients/#{client_name}", expected_put_data).
98
+ and_return(server_v10_response)
99
+ registration.create_or_update.should == server_v10_response
81
100
  registration.private_key.should == "--begin rsa key etc--"
82
101
  end
83
102
  end
84
103
 
85
104
  context "and the client already exists on a Chef 11 server" do
86
105
  it "requests a new key from the server and saves it" do
87
- response = Chef::ApiClient.new
88
- response.name("silent-bob")
89
- response.private_key("--begin rsa key etc--")
90
-
91
- response_409 = Net::HTTPConflict.new("1.1", "409", "Conflict")
92
- exception_409 = Net::HTTPServerException.new("409 conflict", response_409)
93
-
94
106
  http_mock.should_receive(:post).and_raise(exception_409)
95
107
  http_mock.should_receive(:put).
96
- with("clients/silent-bob", :name => 'silent-bob', :admin => false, :private_key => true).
97
- and_return(response)
98
- registration.create_or_update.should == response
108
+ with("clients/#{client_name}", expected_put_data).
109
+ and_return(server_v11_response)
110
+ registration.create_or_update.should == server_v11_response
99
111
  registration.private_key.should == "--begin rsa key etc--"
100
112
  end
101
113
  end
102
114
  end
103
115
 
116
+ context "when local key generation is enabled", :nofocus do
117
+ let(:generated_private_key_pem) { IO.read(File.expand_path('ssl/private_key.pem', CHEF_SPEC_DATA)) }
118
+ let(:generated_private_key) { OpenSSL::PKey::RSA.new(generated_private_key_pem) }
119
+ let(:generated_public_key) { generated_private_key.public_key }
120
+
121
+ let(:expected_post_data) do
122
+ { :name => client_name, :admin => false, :public_key => generated_public_key.to_pem }
123
+ end
124
+
125
+ let(:expected_put_data) do
126
+ { :name => client_name, :admin => false, :public_key => generated_public_key.to_pem }
127
+ end
128
+
129
+ let(:create_with_pkey_response) do
130
+ {
131
+ "uri" => "",
132
+ "public_key" => generated_public_key.to_pem
133
+ }
134
+ end
135
+
136
+ let(:update_with_pkey_response) do
137
+ {"name"=>client_name,
138
+ "admin"=>false,
139
+ "public_key"=> generated_public_key,
140
+ "validator"=>false,
141
+ "private_key"=>false,
142
+ "clientname"=>client_name}
143
+ end
144
+
145
+
146
+ before do
147
+ registration.stub(:http_api).and_return(http_mock)
148
+ Chef::Config.local_key_generation = true
149
+ OpenSSL::PKey::RSA.should_receive(:generate).with(2048).and_return(generated_private_key)
150
+ end
151
+
152
+ it "posts a locally generated public key to the server to create a client" do
153
+ http_mock.should_receive(:post).
154
+ with("clients", expected_post_data).
155
+ and_return(create_with_pkey_response)
156
+ registration.create_or_update.should == create_with_pkey_response
157
+ registration.private_key.should == generated_private_key_pem
158
+ end
159
+
160
+ it "puts a locally generated public key to the server to update a client" do
161
+ http_mock.should_receive(:post).
162
+ with("clients", expected_post_data).
163
+ and_raise(exception_409)
164
+ http_mock.should_receive(:put).
165
+ with("clients/#{client_name}", expected_put_data).
166
+ and_return(update_with_pkey_response)
167
+ registration.create_or_update.should == update_with_pkey_response
168
+ registration.private_key.should == generated_private_key_pem
169
+ end
170
+
171
+ it "writes the generated private key to disk" do
172
+ http_mock.should_receive(:post).
173
+ with("clients", expected_post_data).
174
+ and_return(create_with_pkey_response)
175
+ registration.run
176
+ IO.read(key_location).should == generated_private_key_pem
177
+ end
178
+
179
+ end
180
+
104
181
  describe "when writing the private key to disk" do
105
182
  before do
106
183
  registration.stub(:private_key).and_return('--begin rsa key etc--')
@@ -125,16 +202,12 @@ describe Chef::ApiClient::Registration do
125
202
 
126
203
  describe "when registering a client" do
127
204
 
128
- let(:http_mock) { double("Chef::REST mock") }
129
-
130
205
  before do
131
206
  registration.stub(:http_api).and_return(http_mock)
132
207
  end
133
208
 
134
209
  it "creates the client on the server and writes the key" do
135
- response = {"uri" => "http://chef.local/clients/silent-bob",
136
- "private_key" => "--begin rsa key etc--" }
137
- http_mock.should_receive(:post).ordered.and_return(response)
210
+ http_mock.should_receive(:post).ordered.and_return(server_v10_response)
138
211
  registration.run
139
212
  IO.read(key_location).should == "--begin rsa key etc--"
140
213
  end
@@ -149,9 +222,7 @@ describe Chef::ApiClient::Registration do
149
222
  http_mock.should_receive(:post).ordered.and_raise(exception_500) # 4
150
223
  http_mock.should_receive(:post).ordered.and_raise(exception_500) # 5
151
224
 
152
- response = {"uri" => "http://chef.local/clients/silent-bob",
153
- "private_key" => "--begin rsa key etc--" }
154
- http_mock.should_receive(:post).ordered.and_return(response)
225
+ http_mock.should_receive(:post).ordered.and_return(server_v10_response)
155
226
  registration.run
156
227
  IO.read(key_location).should == "--begin rsa key etc--"
157
228
  end