chef 10.14.0.beta.2 → 10.14.0.beta.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. data/distro/common/html/chef-client.8.html +3 -3
  2. data/distro/common/html/chef-expander.8.html +3 -3
  3. data/distro/common/html/chef-expanderctl.8.html +3 -3
  4. data/distro/common/html/chef-server-webui.8.html +3 -3
  5. data/distro/common/html/chef-server.8.html +3 -3
  6. data/distro/common/html/chef-solo.8.html +3 -3
  7. data/distro/common/html/chef-solr.8.html +3 -3
  8. data/distro/common/html/knife-bootstrap.1.html +3 -3
  9. data/distro/common/html/knife-client.1.html +3 -3
  10. data/distro/common/html/knife-configure.1.html +4 -4
  11. data/distro/common/html/knife-cookbook-site.1.html +6 -6
  12. data/distro/common/html/knife-cookbook.1.html +3 -3
  13. data/distro/common/html/knife-data-bag.1.html +3 -3
  14. data/distro/common/html/knife-environment.1.html +6 -6
  15. data/distro/common/html/knife-exec.1.html +4 -4
  16. data/distro/common/html/knife-index.1.html +3 -3
  17. data/distro/common/html/knife-node.1.html +3 -3
  18. data/distro/common/html/knife-role.1.html +3 -3
  19. data/distro/common/html/knife-search.1.html +3 -3
  20. data/distro/common/html/knife-ssh.1.html +4 -4
  21. data/distro/common/html/knife-status.1.html +4 -4
  22. data/distro/common/html/knife-tag.1.html +4 -4
  23. data/distro/common/html/knife.1.html +3 -3
  24. data/distro/common/html/shef.1.html +7 -7
  25. data/distro/common/man/man1/knife-bootstrap.1 +1 -1
  26. data/distro/common/man/man1/knife-client.1 +1 -1
  27. data/distro/common/man/man1/knife-configure.1 +1 -1
  28. data/distro/common/man/man1/knife-cookbook-site.1 +1 -1
  29. data/distro/common/man/man1/knife-cookbook.1 +1 -1
  30. data/distro/common/man/man1/knife-data-bag.1 +1 -1
  31. data/distro/common/man/man1/knife-environment.1 +1 -1
  32. data/distro/common/man/man1/knife-exec.1 +1 -1
  33. data/distro/common/man/man1/knife-index.1 +1 -1
  34. data/distro/common/man/man1/knife-node.1 +1 -1
  35. data/distro/common/man/man1/knife-role.1 +1 -1
  36. data/distro/common/man/man1/knife-search.1 +1 -1
  37. data/distro/common/man/man1/knife-ssh.1 +1 -1
  38. data/distro/common/man/man1/knife-status.1 +1 -1
  39. data/distro/common/man/man1/knife-tag.1 +1 -1
  40. data/distro/common/man/man1/knife.1 +1 -1
  41. data/distro/common/man/man1/shef.1 +1 -1
  42. data/distro/common/man/man8/chef-client.8 +1 -1
  43. data/distro/common/man/man8/chef-expander.8 +1 -1
  44. data/distro/common/man/man8/chef-expanderctl.8 +1 -1
  45. data/distro/common/man/man8/chef-server-webui.8 +1 -1
  46. data/distro/common/man/man8/chef-server.8 +1 -1
  47. data/distro/common/man/man8/chef-solo.8 +1 -1
  48. data/distro/common/man/man8/chef-solr.8 +1 -1
  49. data/lib/chef/application/client.rb +6 -2
  50. data/lib/chef/application/solo.rb +7 -3
  51. data/lib/chef/application/windows_service.rb +1 -2
  52. data/lib/chef/client.rb +72 -47
  53. data/lib/chef/config.rb +3 -1
  54. data/lib/chef/cookbook_uploader.rb +28 -18
  55. data/lib/chef/cookbook_version.rb +2 -2
  56. data/lib/chef/event_dispatch/base.rb +4 -0
  57. data/lib/chef/exceptions.rb +1 -0
  58. data/lib/chef/formatters/base.rb +13 -9
  59. data/lib/chef/formatters/error_inspectors/compile_error_inspector.rb +12 -6
  60. data/lib/chef/formatters/error_inspectors/resource_failure_inspector.rb +1 -2
  61. data/lib/chef/formatters/error_mapper.rb +13 -6
  62. data/lib/chef/knife/bootstrap.rb +4 -4
  63. data/lib/chef/knife/bootstrap/ubuntu12.04-gems.erb +2 -2
  64. data/lib/chef/knife/client_create.rb +1 -0
  65. data/lib/chef/knife/cookbook_upload.rb +60 -33
  66. data/lib/chef/knife/core/node_presenter.rb +1 -0
  67. data/lib/chef/knife/tag_delete.rb +1 -1
  68. data/lib/chef/mixin/file_class.rb +46 -0
  69. data/lib/chef/provider/cron.rb +1 -1
  70. data/lib/chef/provider/deploy.rb +1 -1
  71. data/lib/chef/provider/link.rb +2 -18
  72. data/lib/chef/provider/package/apt.rb +7 -4
  73. data/lib/chef/provider/remote_directory.rb +17 -5
  74. data/lib/chef/provider/user/useradd.rb +1 -0
  75. data/lib/chef/resource_reporter.rb +4 -4
  76. data/lib/chef/rest.rb +8 -0
  77. data/lib/chef/run_context.rb +4 -1
  78. data/lib/chef/solr_query/lucene.treetop +2 -2
  79. data/lib/chef/version.rb +1 -1
  80. data/spec/support/shared/functional/file_resource.rb +2 -1
  81. data/spec/support/shared/functional/securable_resource.rb +28 -12
  82. data/spec/unit/client_spec.rb +49 -13
  83. data/spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb +21 -1
  84. data/spec/unit/knife/config_file_selection_spec.rb +5 -4
  85. data/spec/unit/knife/cookbook_upload_spec.rb +4 -3
  86. data/spec/unit/mixin/enforce_ownership_and_permissions_spec.rb +21 -0
  87. data/spec/unit/provider/cookbook_file_spec.rb +2 -0
  88. data/spec/unit/provider/cron_spec.rb +46 -0
  89. data/spec/unit/provider/deploy_spec.rb +12 -0
  90. data/spec/unit/provider/execute_spec.rb +3 -0
  91. data/spec/unit/provider/file_spec.rb +9 -6
  92. data/spec/unit/provider/package/apt_spec.rb +13 -0
  93. data/spec/unit/provider/remote_directory_spec.rb +16 -5
  94. data/spec/unit/provider/template_spec.rb +10 -0
  95. data/spec/unit/provider/user/useradd_spec.rb +2 -2
  96. data/spec/unit/provider/user_spec.rb +28 -17
  97. data/spec/unit/resource/file_spec.rb +14 -7
  98. data/spec/unit/resource/template_spec.rb +13 -6
  99. data/spec/unit/solr_query/query_transform_spec.rb +4 -0
  100. metadata +5 -4
@@ -102,6 +102,7 @@ class Chef
102
102
  #{key('Roles:')} #{Array(node[:roles]).join(', ')}
103
103
  #{key('Recipes:')} #{Array(node[:recipes]).join(', ')}
104
104
  #{key('Platform:')} #{node[:platform]} #{node[:platform_version]}
105
+ #{key('Tags:')} #{Array(node[:tags]).join(', ')}
105
106
  SUMMARY
106
107
  if config[:medium_output] || config[:long_output]
107
108
  summarized +=<<-MORE
@@ -51,7 +51,7 @@ class Chef
51
51
  message = if deleted_tags.empty?
52
52
  "Nothing has changed. The tags requested to be deleted do not exist."
53
53
  else
54
- "Deleted the following tags: #{deleted_tags.join(", ")}."
54
+ "Deleted tags #{deleted_tags.join(", ")} for node #{name}."
55
55
  end
56
56
  ui.info(message)
57
57
  end
@@ -0,0 +1,46 @@
1
+ #
2
+ # Author:: Mark Mzyk <mmzyk@opscode.com>
3
+ # Author:: Seth Chisamore <schisamo@opscode.com>
4
+ # Author:: Bryan McLellan <btm@opscode.com>
5
+ # Copyright:: Copyright (c) 2011-2012 Opscode, Inc.
6
+ # License:: Apache License, Version 2.0
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+
21
+ class Chef
22
+ module Mixin
23
+ module FileClass
24
+
25
+ def file_class
26
+ @host_os_file ||= if Chef::Platform.windows?
27
+ require 'chef/win32/file'
28
+ begin
29
+ Chef::ReservedNames::Win32::File.verify_links_supported!
30
+ rescue Chef::Exceptions::Win32APIFunctionNotImplemented => e
31
+ message = "Link resource is not supported on this version of Windows"
32
+ message << ": #{node[:kernel][:name]}" if node
33
+ message << " (#{node[:platform_version]})" if node
34
+ Chef::Log.fatal(message)
35
+ raise e
36
+ end
37
+ Chef::ReservedNames::Win32::File
38
+ else
39
+ ::File
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+
@@ -25,7 +25,7 @@ class Chef
25
25
  class Cron < Chef::Provider
26
26
  include Chef::Mixin::Command
27
27
 
28
- CRON_PATTERN = /\A([-0-9*,\/]+)\s([-0-9*,\/]+)\s([-0-9*,\/]+)\s([-0-9*,\/]+)\s([-0-9*,\/]+)\s(.*)/
28
+ CRON_PATTERN = /\A([-0-9*,\/]+)\s([-0-9*,\/]+)\s([-0-9*,\/]+)\s([-0-9*,\/]+|[a-zA-Z]{3})\s([-0-9*,\/]+|[a-zA-Z]{3})\s(.*)/
29
29
  ENV_PATTERN = /\A(\S+)=(\S*)/
30
30
 
31
31
  CRON_ATTRIBUTES = [:minute, :hour, :day, :month, :weekday, :command, :mailto, :path, :shell, :home, :environment]
@@ -148,9 +148,9 @@ class Chef
148
148
  end
149
149
 
150
150
  def deploy
151
- enforce_ownership
152
151
  verify_directories_exist
153
152
  update_cached_repo # no converge-by - scm provider will dothis
153
+ enforce_ownership
154
154
  copy_cached_repo
155
155
  install_gems
156
156
  enforce_ownership
@@ -19,6 +19,7 @@
19
19
  require 'chef/config'
20
20
  require 'chef/log'
21
21
  require 'chef/mixin/shell_out'
22
+ require 'chef/mixin/file_class'
22
23
  require 'chef/resource/link'
23
24
  require 'chef/provider'
24
25
  require 'chef/scan_access_control'
@@ -27,24 +28,7 @@ class Chef
27
28
  class Provider
28
29
  class Link < Chef::Provider
29
30
  include Chef::Mixin::ShellOut
30
-
31
- def file_class
32
- @host_os_file ||= if Chef::Platform.windows?
33
- require 'chef/win32/file'
34
- begin
35
- Chef::ReservedNames::Win32::File.verify_links_supported!
36
- rescue Chef::Exceptions::Win32APIFunctionNotImplemented => e
37
- message = "Link resource is not supported on this version of Windows"
38
- message << ": #{node[:kernel][:name]}" if node
39
- message << " (#{node[:platform_version]})" if node
40
- Chef::Log.fatal(message)
41
- raise e
42
- end
43
- Chef::ReservedNames::Win32::File
44
- else
45
- ::File
46
- end
47
- end
31
+ include Chef::Mixin::FileClass
48
32
 
49
33
  def negative_complement(big)
50
34
  if big > 1073741823 # Fixnum max
@@ -37,13 +37,16 @@ class Chef
37
37
  @current_resource
38
38
  end
39
39
 
40
+ def default_release_options
41
+ # Use apt::Default-Release option only if provider was explicitly defined
42
+ "-o APT::Default-Release=#{@new_resource.default_release}" if @new_resource.provider && @new_resource.default_release
43
+ end
44
+
40
45
  def check_package_state(package)
41
46
  Chef::Log.debug("#{@new_resource} checking package status for #{package}")
42
47
  installed = false
43
- # Use apt cache release option only if provider was explicitly defined
44
- aptcache_options = "-o APT::Default-Release=#{@new_resource.default_release}" if @new_resource.provider && @new_resource.default_release
45
48
 
46
- shell_out!("apt-cache#{expand_options(aptcache_options)} policy #{package}").stdout.each_line do |line|
49
+ shell_out!("apt-cache#{expand_options(default_release_options)} policy #{package}").stdout.each_line do |line|
47
50
  case line
48
51
  when /^\s{2}Installed: (.+)$/
49
52
  installed_version = $1
@@ -88,7 +91,7 @@ class Chef
88
91
  package_name = "#{name}=#{version}"
89
92
  package_name = name if @is_virtual_package
90
93
  run_command_with_systems_locale(
91
- :command => "apt-get -q -y#{expand_options(@new_resource.options)} install #{package_name}",
94
+ :command => "apt-get -q -y#{expand_options(default_release_options)}#{expand_options(@new_resource.options)} install #{package_name}",
92
95
  :environment => {
93
96
  "DEBIAN_FRONTEND" => "noninteractive"
94
97
  }
@@ -20,6 +20,7 @@ require 'chef/provider/file'
20
20
  require 'chef/provider/directory'
21
21
  require 'chef/resource/directory'
22
22
  require 'chef/resource/remote_file'
23
+ require 'chef/mixin/file_class'
23
24
  require 'chef/platform'
24
25
  require 'uri'
25
26
  require 'tempfile'
@@ -29,6 +30,7 @@ require 'set'
29
30
  class Chef
30
31
  class Provider
31
32
  class RemoteDirectory < Chef::Provider::Directory
33
+ include Chef::Mixin::FileClass
32
34
 
33
35
  def action_create
34
36
  super
@@ -62,11 +64,14 @@ class Chef
62
64
  def purge_unmanaged_files(unmanaged_files)
63
65
  if @new_resource.purge
64
66
  unmanaged_files.sort.reverse.each do |f|
65
- if ::File.directory?(f) && !::File.symlink?(f)
66
- converge_by("delete unmanaged directory #{f}") do
67
- Dir::rmdir(f)
68
- Chef::Log.debug("#{@new_resource} removed directory #{f}")
69
- end
67
+ # file_class comes from Chef::Mixin::FileClass
68
+ if ::File.directory?(f) && !Chef::Platform.windows? && !file_class.symlink?(f.dup)
69
+ # Linux treats directory symlinks as files
70
+ # Remove a directory as a directory when not on windows if it is not a symlink
71
+ purge_directory(f)
72
+ elsif ::File.directory?(f) && Chef::Platform.windows?
73
+ # Windows treats directory symlinks as directories so we delete them here
74
+ purge_directory(f)
70
75
  else
71
76
  converge_by("delete unmanaged file #{f}") do
72
77
  ::File.delete(f)
@@ -77,6 +82,13 @@ class Chef
77
82
  end
78
83
  end
79
84
 
85
+ def purge_directory(dir)
86
+ converge_by("delete unmanaged directory #{dir}") do
87
+ Dir::rmdir(dir)
88
+ Chef::Log.debug("#{@new_resource} removed directory #{dir}")
89
+ end
90
+ end
91
+
80
92
  def files_to_transfer
81
93
  cookbook = run_context.cookbook_collection[resource_cookbook]
82
94
  files = cookbook.relative_filenames_in_preferred_directory(node, :files, @new_resource.source)
@@ -35,6 +35,7 @@ class Chef
35
35
 
36
36
  def manage_user
37
37
  command = compile_command("usermod") { |u| u << universal_options }
38
+ command << " -m" if managing_home_dir?
38
39
  run_command(:command => command)
39
40
  end
40
41
 
@@ -137,7 +137,7 @@ class Chef
137
137
  @pending_update ||= ResourceReport.new_for_exception(new_resource, action)
138
138
  @pending_update.exception = exception
139
139
  end
140
- description = Formatters::ErrorMapper.resource_failed_helper(new_resource, action, exception)
140
+ description = Formatters::ErrorMapper.resource_failed(new_resource, action, exception)
141
141
  @error_descriptions = description.for_json
142
142
  end
143
143
 
@@ -187,17 +187,17 @@ class Chef
187
187
  end
188
188
 
189
189
  def run_list_expand_failed(node, exception)
190
- description = Formatters::ErrorMapper.run_list_expand_failed_helper(node, exception)
190
+ description = Formatters::ErrorMapper.run_list_expand_failed(node, exception)
191
191
  @error_descriptions = description.for_json
192
192
  end
193
193
 
194
194
  def cookbook_resolution_failed(expanded_run_list, exception)
195
- description = Formatters::ErrorMapper.cookbook_resolution_failed_helper(expanded_run_list, exception)
195
+ description = Formatters::ErrorMapper.cookbook_resolution_failed(expanded_run_list, exception)
196
196
  @error_descriptions = description.for_json
197
197
  end
198
198
 
199
199
  def cookbook_sync_failed(cookbooks, exception)
200
- description = Formatters::ErrorMapper.cookbook_sync_failed_helper(cookbooks, exception)
200
+ description = Formatters::ErrorMapper.cookbook_sync_failed(cookbooks, exception)
201
201
  @error_descriptions = description.for_json
202
202
  end
203
203
 
@@ -246,6 +246,14 @@ class Chef
246
246
  begin
247
247
  response = rest_request.call {|r| r.read_body}
248
248
 
249
+ Chef::Log.debug("---- HTTP Status and Header Data: ----")
250
+ Chef::Log.debug("HTTP #{response.http_version} #{response.code} #{response.msg}")
251
+
252
+ response.each do |header, value|
253
+ Chef::Log.debug("#{header}: #{value}")
254
+ end
255
+ Chef::Log.debug("---- End HTTP Status/Header Data ----")
256
+
249
257
  response_body = decompress_body(response)
250
258
 
251
259
  if response.kind_of?(Net::HTTPSuccess)
@@ -77,6 +77,9 @@ class Chef
77
77
  # TODO: timh/cw, 5-14-2010: It's distasteful to be including
78
78
  # the DSL in a class outside the context of the DSL
79
79
  include_recipe(recipe)
80
+ rescue Chef::Exceptions::RecipeNotFound => e
81
+ @events.recipe_not_found(e)
82
+ raise
80
83
  rescue Exception => e
81
84
  path = resolve_recipe(recipe)
82
85
  @events.recipe_file_load_failed(path, e)
@@ -138,7 +141,7 @@ class Chef
138
141
  @events.library_file_loaded(filename)
139
142
  rescue Exception => e
140
143
  # TODO wrap/munge exception to highlight syntax/name/no method errors.
141
- @events.library_load_failed(filename, e)
144
+ @events.library_file_load_failed(filename, e)
142
145
  raise
143
146
  end
144
147
  end
@@ -129,11 +129,11 @@ grammar Lucene
129
129
  end
130
130
 
131
131
  rule valid_letter
132
- start_letter+ ([a-zA-Z0-9*?_.-] / '\\' special_char)*
132
+ start_letter+ ([a-zA-Z0-9@*?_.-] / '\\' special_char)*
133
133
  end
134
134
 
135
135
  rule start_letter
136
- [a-zA-Z0-9._*] / '\\' special_char
136
+ [a-zA-Z0-9@._*] / '\\' special_char
137
137
  end
138
138
 
139
139
  rule end_letter
@@ -17,7 +17,7 @@
17
17
 
18
18
  class Chef
19
19
  CHEF_ROOT = File.dirname(File.expand_path(File.dirname(__FILE__)))
20
- VERSION = '10.14.0.beta.2'
20
+ VERSION = '10.14.0.beta.3'
21
21
  end
22
22
 
23
23
  # NOTE: the Chef::Version class is defined in version_class.rb
@@ -18,6 +18,7 @@
18
18
 
19
19
  shared_examples_for "a file with the wrong content" do
20
20
  it "overwrites the file with the updated content when the :create action is run" do
21
+ Chef::Config[:file_backup_path] = CHEF_SPEC_BACKUP_PATH
21
22
  sleep 1
22
23
  resource.run_action(:create)
23
24
  File.stat(path).mtime.should > @expected_mtime
@@ -59,7 +60,7 @@ shared_examples_for "a file with the correct content" do
59
60
  it "does not update the mtime/atime of the file when the :create action is run" do
60
61
  sleep 1
61
62
  File.stat(path).mtime.should == @expected_mtime
62
- File.stat(path).atime.should be_within(1).of(@expected_atime)
63
+ File.stat(path).atime.should be_within(2).of(@expected_atime)
63
64
  end
64
65
 
65
66
  it "doesn't overwrite the file when the :create_if_missing action is run" do
@@ -24,24 +24,40 @@
24
24
  require 'etc'
25
25
 
26
26
  shared_context "setup correct permissions" do
27
- before :each, { :requires_root => true, :unix_only => true } do
28
- File.chown(Etc.getpwnam('nobody').uid, 1337, path)
29
- File.chmod(0776, path)
30
- end
31
- before :each, { :requires_unprivileged_user => true, :unix_only => true } do
32
- File.chmod(0776, path)
27
+ context "on unix", :unix_only do
28
+ context "with root", :requires_root do
29
+ before :each do
30
+ File.chown(Etc.getpwnam('nobody').uid, 1337, path)
31
+ File.chmod(0776, path)
32
+ end
33
+ end
34
+
35
+ context "without root", :requires_unprivileged_user do
36
+ before :each do
37
+ File.chmod(0776, path)
38
+ end
39
+ end
33
40
  end
41
+
34
42
  # FIXME: windows
35
43
  end
36
44
 
37
45
  shared_context "setup broken permissions" do
38
- before :each, { :requires_root => true, :unix_only => true } do
39
- File.chown(0, 0, path)
40
- File.chmod(0644, path)
41
- end
42
- before :each, { :requires_unprivileged_user => true, :unix_only => true } do
43
- File.chmod(0644, path)
46
+ context "on unix", :unix_only do
47
+ context "with root", :requires_root do
48
+ before :each do
49
+ File.chown(0, 0, path)
50
+ File.chmod(0644, path)
51
+ end
52
+ end
53
+
54
+ context "without root", :requires_unprivileged_user do
55
+ before :each do
56
+ File.chmod(0644, path)
57
+ end
58
+ end
44
59
  end
60
+
45
61
  # FIXME: windows
46
62
  end
47
63
 
@@ -24,7 +24,7 @@ require 'chef/run_context'
24
24
  require 'chef/rest'
25
25
  require 'rbconfig'
26
26
 
27
- describe Chef::Client do
27
+ shared_examples_for Chef::Client do
28
28
  before do
29
29
  Chef::Log.logger = Logger.new(StringIO.new)
30
30
 
@@ -52,24 +52,26 @@ describe Chef::Client do
52
52
  end
53
53
 
54
54
  describe "run" do
55
+
55
56
  it "should identify the node and run ohai, then register the client" do
56
57
  mock_chef_rest_for_node = mock("Chef::REST (node)")
57
58
  mock_chef_rest_for_client = mock("Chef::REST (client)")
58
59
  mock_chef_rest_for_node_save = mock("Chef::REST (node save)")
59
60
  mock_chef_runner = mock("Chef::Runner")
60
61
 
61
- # --Client#register
62
+ # --Client.register
62
63
  # Make sure Client#register thinks the client key doesn't
63
64
  # exist, so it tries to register and create one.
64
65
  File.should_receive(:exists?).with(Chef::Config[:client_key]).exactly(1).times.and_return(false)
65
66
 
66
- # Client#register will register with the validation client name.
67
- Chef::REST.should_receive(:new).with(Chef::Config[:client_url], Chef::Config[:validation_client_name], Chef::Config[:validation_key]).and_return(mock_chef_rest_for_client)
68
- mock_chef_rest_for_client.should_receive(:register).with(@fqdn, Chef::Config[:client_key]).and_return(true)
69
- # Client#register will then turn around create another
67
+ # Client.register will register with the validation client name.
68
+ Chef::REST.should_receive(:new).with(Chef::Config[:client_url], Chef::Config[:validation_client_name], Chef::Config[:validation_key]).exactly(1).and_return(mock_chef_rest_for_client)
69
+ mock_chef_rest_for_client.should_receive(:register).with(@fqdn, Chef::Config[:client_key]).exactly(1).and_return(true)
70
+ # Client.register will then turn around create another
71
+
70
72
  # Chef::REST object, this time with the client key it got from the
71
73
  # previous step.
72
- Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url], @fqdn, Chef::Config[:client_key]).and_return(mock_chef_rest_for_node)
74
+ Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url], @fqdn, Chef::Config[:client_key]).exactly(1).and_return(mock_chef_rest_for_node)
73
75
 
74
76
  # --Client#build_node
75
77
  # looks up the node, which we will return, then later saves it.
@@ -98,19 +100,42 @@ describe Chef::Client do
98
100
  Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url]).and_return(mock_chef_rest_for_node_save)
99
101
  mock_chef_rest_for_node_save.should_receive(:put_rest).with("nodes/#{@fqdn}", @node).and_return(true)
100
102
 
103
+ # Post conditions: check that node has been filled in correctly
101
104
  @client.should_receive(:run_started)
102
105
  @client.should_receive(:run_completed_successfully)
103
106
 
104
-
107
+ if(Chef::Config[:client_fork])
108
+ require 'stringio'
109
+ if(Chef::Config[:pipe_node])
110
+ pipe_sim = StringIO.new
111
+ pipe_sim.should_receive(:close).exactly(4).and_return(nil)
112
+ res = ''
113
+ pipe_sim.should_receive(:puts) do |string|
114
+ res.replace(string)
115
+ end
116
+ pipe_sim.should_receive(:gets).and_return(res)
117
+ Chef::CouchDB.should_receive(:new).and_return(nil)
118
+ IO.should_receive(:pipe).and_return([pipe_sim, pipe_sim])
119
+ IO.should_receive(:select).and_return(true)
120
+ end
121
+ proc_ret = Class.new.new
122
+ proc_ret.should_receive(:success?).and_return(true)
123
+ Process.should_receive(:waitpid2).and_return([1, proc_ret])
124
+ @client.should_receive(:exit).and_return(nil)
125
+ @client.should_receive(:fork) do |&block|
126
+ block.call
127
+ end
128
+ end
129
+
105
130
  # This is what we're testing.
106
131
  @client.run
107
132
 
108
-
109
- # Post conditions: check that node has been filled in correctly
110
- @node.automatic_attrs[:platform].should == "example-platform"
111
- @node.automatic_attrs[:platform_version].should == "example-platform-1.0"
133
+ if(!Chef::Config[:client_fork] || Chef::Config[:pipe_node])
134
+ @node.automatic_attrs[:platform].should == "example-platform"
135
+ @node.automatic_attrs[:platform_version].should == "example-platform-1.0"
136
+ end
112
137
  end
113
-
138
+
114
139
  describe "when notifying other objects of the status of the chef run" do
115
140
  before do
116
141
  Chef::Client.clear_notifications
@@ -224,3 +249,14 @@ describe Chef::Client do
224
249
  end
225
250
 
226
251
  end
252
+
253
+ describe Chef::Client do
254
+ it_behaves_like Chef::Client
255
+ end
256
+
257
+ describe "Chef::Client Forked" do
258
+ it_behaves_like Chef::Client
259
+ before do
260
+ Chef::Config[:client_fork] = true
261
+ end
262
+ end