chef 11.10.4 → 11.12.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.md +6 -6
  3. data/README.md +1 -1
  4. data/lib/chef/api_client.rb +1 -3
  5. data/lib/chef/application.rb +2 -1
  6. data/lib/chef/application/client.rb +11 -1
  7. data/lib/chef/client.rb +24 -9
  8. data/lib/chef/cookbook/syntax_check.rb +107 -6
  9. data/lib/chef/dsl/reboot_pending.rb +61 -0
  10. data/lib/chef/exceptions.rb +12 -1
  11. data/lib/chef/formatters/error_descriptor.rb +1 -1
  12. data/lib/chef/http/remote_request_id.rb +46 -0
  13. data/lib/chef/knife/bootstrap.rb +1 -1
  14. data/lib/chef/knife/bootstrap/README.md +12 -0
  15. data/lib/chef/knife/bootstrap/chef-full.erb +3 -0
  16. data/lib/chef/knife/client_create.rb +6 -0
  17. data/lib/chef/knife/client_delete.rb +15 -1
  18. data/lib/chef/knife/raw.rb +1 -0
  19. data/lib/chef/node.rb +1 -1
  20. data/lib/chef/node/attribute_collections.rb +8 -1
  21. data/lib/chef/node/immutable_collections.rb +8 -1
  22. data/lib/chef/provider/deploy.rb +1 -1
  23. data/lib/chef/provider/group.rb +1 -1
  24. data/lib/chef/provider/ifconfig/debian.rb +19 -8
  25. data/lib/chef/provider/ohai.rb +6 -5
  26. data/lib/chef/provider/service/macosx.rb +68 -14
  27. data/lib/chef/recipe.rb +2 -0
  28. data/lib/chef/request_id.rb +37 -0
  29. data/lib/chef/resource.rb +2 -0
  30. data/lib/chef/resource_reporter.rb +7 -4
  31. data/lib/chef/rest.rb +5 -1
  32. data/lib/chef/run_status.rb +4 -1
  33. data/lib/chef/server_api.rb +3 -1
  34. data/lib/chef/version.rb +2 -2
  35. data/spec/functional/dsl/reboot_pending_spec.rb +118 -0
  36. data/spec/functional/resource/base.rb +1 -3
  37. data/spec/functional/resource/deploy_revision_spec.rb +192 -1
  38. data/spec/functional/resource/git_spec.rb +1 -1
  39. data/spec/functional/resource/ohai_spec.rb +65 -0
  40. data/spec/functional/resource/registry_spec.rb +4 -5
  41. data/spec/integration/client/client_spec.rb +14 -0
  42. data/spec/spec_helper.rb +1 -2
  43. data/spec/support/shared/functional/windows_script.rb +1 -2
  44. data/spec/unit/api_client_spec.rb +46 -0
  45. data/spec/unit/client_spec.rb +345 -229
  46. data/spec/unit/cookbook/syntax_check_spec.rb +0 -1
  47. data/spec/unit/dsl/reboot_pending_spec.rb +100 -0
  48. data/spec/unit/knife/client_create_spec.rb +29 -1
  49. data/spec/unit/knife/client_delete_spec.rb +44 -1
  50. data/spec/unit/knife_spec.rb +55 -0
  51. data/spec/unit/node/attribute_spec.rb +7 -0
  52. data/spec/unit/node/immutable_collections_spec.rb +5 -1
  53. data/spec/unit/provider/group_spec.rb +5 -0
  54. data/spec/unit/provider/ifconfig/debian_spec.rb +251 -24
  55. data/spec/unit/provider/ohai_spec.rb +2 -3
  56. data/spec/unit/provider/service/macosx_spec.rb +29 -11
  57. data/spec/unit/resource_reporter_spec.rb +1 -1
  58. data/spec/unit/rest_spec.rb +38 -13
  59. metadata +151 -194
@@ -31,7 +31,7 @@ class Chef
31
31
  end
32
32
 
33
33
  def section(heading, text)
34
- @sections << {heading => text}
34
+ @sections << {heading => (text or "")}
35
35
  end
36
36
 
37
37
  def display(out)
@@ -0,0 +1,46 @@
1
+ # Author:: Prajakta Purohit (<prajakta@opscode.com>)
2
+ # Copyright:: Copyright (c) 2009, 2010, 2013, 2014 Opscode, 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 'chef/request_id'
19
+
20
+ class Chef
21
+ class HTTP
22
+ class RemoteRequestID
23
+
24
+ def initialize(opts={})
25
+ end
26
+
27
+ def handle_request(method, url, headers={}, data=false)
28
+ headers.merge!({'X-REMOTE-REQUEST-ID' => Chef::RequestID.instance.request_id})
29
+ [method, url, headers, data]
30
+ end
31
+
32
+ def handle_response(http_response, rest_request, return_value)
33
+ [http_response, rest_request, return_value]
34
+ end
35
+
36
+ def stream_response_handler(response)
37
+ nil
38
+ end
39
+
40
+ def handle_stream_complete(http_response, rest_request, return_value)
41
+ [http_response, rest_request, return_value]
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -201,7 +201,7 @@ class Chef
201
201
 
202
202
  $stdout.sync = true
203
203
 
204
- ui.info("Bootstrapping Chef on #{ui.color(@node_name, :bold)}")
204
+ ui.info("Connecting to #{ui.color(@node_name, :bold)}")
205
205
 
206
206
  begin
207
207
  knife_ssh.run
@@ -0,0 +1,12 @@
1
+ This directory contains bootstrap templates which can be used with the -d flag
2
+ to 'knife bootstrap' to install Chef in different ways. To simplify installation,
3
+ and reduce the matrix of common installation patterns to support, we have
4
+ standardized on the [Omnibus](https://github.com/opscode/omnibus-ruby) built installation
5
+ packages.
6
+
7
+ The 'chef-full' template downloads a script which is used to determine the correct
8
+ Omnibus package for this system from the [Omnitruck](https://github.com/opscode/opscode-omnitruck) API. All other templates in this directory are deprecated and will be removed
9
+ in the future.
10
+
11
+ You can still utilize custom bootstrap templates on your system if your installation
12
+ needs are unique. Additional information can be found on the [docs site](http://docs.opscode.com/knife_bootstrap.html#custom-templates).
@@ -23,6 +23,7 @@ install_sh="https://www.opscode.com/chef/install.sh"
23
23
  version_string="-v <%= chef_version %>"
24
24
 
25
25
  if ! exists /usr/bin/chef-client; then
26
+ echo "Installing Chef Client..."
26
27
  if exists wget; then
27
28
  bash <(wget <%= "--proxy=on " if knife_config[:bootstrap_proxy] %> ${install_sh} -O -) ${version_string}
28
29
  elif exists curl; then
@@ -66,4 +67,6 @@ cat > /etc/chef/first-boot.json <<'EOP'
66
67
  <%= first_boot.to_json %>
67
68
  EOP
68
69
 
70
+ echo "Starting first Chef Client run..."
71
+
69
72
  <%= start_chef %>'
@@ -38,6 +38,11 @@ class Chef
38
38
  :description => "Create the client as an admin",
39
39
  :boolean => true
40
40
 
41
+ option :validator,
42
+ :long => "--validator",
43
+ :description => "Create the client as a validator",
44
+ :boolean => true
45
+
41
46
  banner "knife client create CLIENT (options)"
42
47
 
43
48
  def run
@@ -52,6 +57,7 @@ class Chef
52
57
  client = Chef::ApiClient.new
53
58
  client.name(@client_name)
54
59
  client.admin(config[:admin])
60
+ client.validator(config[:validator])
55
61
 
56
62
  output = edit_data(client)
57
63
 
@@ -27,6 +27,11 @@ class Chef
27
27
  require 'chef/json_compat'
28
28
  end
29
29
 
30
+ option :force,
31
+ :short => "-f",
32
+ :long => "--force",
33
+ :description => "Force deletion of client if it's a validator"
34
+
30
35
  banner "knife client delete CLIENT (options)"
31
36
 
32
37
  def run
@@ -38,7 +43,16 @@ class Chef
38
43
  exit 1
39
44
  end
40
45
 
41
- delete_object(Chef::ApiClient, @client_name)
46
+ delete_object(Chef::ApiClient, @client_name, 'client') {
47
+ object = Chef::ApiClient.load(@client_name)
48
+ if object.validator
49
+ unless config[:force]
50
+ ui.fatal("You must specify --force to delete the validator client #{@client_name}")
51
+ exit 2
52
+ end
53
+ end
54
+ object.destroy
55
+ }
42
56
  end
43
57
 
44
58
  end
@@ -42,6 +42,7 @@ class Chef
42
42
  use Chef::HTTP::CookieManager
43
43
  use Chef::HTTP::Decompressor
44
44
  use Chef::HTTP::Authenticator
45
+ use Chef::HTTP::RemoteRequestID
45
46
  end
46
47
 
47
48
  def run
@@ -312,7 +312,7 @@ class Chef
312
312
  if attrs.key?("recipes") || attrs.key?("run_list")
313
313
  raise Chef::Exceptions::AmbiguousRunlistSpecification, "please set the node's run list using the 'run_list' attribute only."
314
314
  end
315
- Chef::Log.info("Setting the run_list to #{new_run_list.inspect} from JSON")
315
+ Chef::Log.info("Setting the run_list to #{new_run_list.inspect} from CLI options")
316
316
  run_list(new_run_list)
317
317
  end
318
318
  attrs
@@ -76,8 +76,15 @@ class Chef
76
76
  super(data)
77
77
  end
78
78
 
79
+ # For elements like Fixnums, true, nil...
80
+ def safe_dup(e)
81
+ e.dup
82
+ rescue TypeError
83
+ e
84
+ end
85
+
79
86
  def dup
80
- Array.new(map {|e| e.dup})
87
+ Array.new(map {|e| safe_dup(e)})
81
88
  end
82
89
 
83
90
  end
@@ -85,8 +85,15 @@ class Chef
85
85
  METHOD_DEFN
86
86
  end
87
87
 
88
+ # For elements like Fixnums, true, nil...
89
+ def safe_dup(e)
90
+ e.dup
91
+ rescue TypeError
92
+ e
93
+ end
94
+
88
95
  def dup
89
- Array.new(map {|e| e.dup })
96
+ Array.new(map {|e| safe_dup(e)})
90
97
  end
91
98
 
92
99
  end
@@ -266,7 +266,7 @@ class Chef
266
266
 
267
267
  def copy_cached_repo
268
268
  target_dir_path = @new_resource.deploy_to + "/releases"
269
- converge_by("deploy from repo to #{@target_dir_path} ") do
269
+ converge_by("deploy from repo to #{target_dir_path} ") do
270
270
  FileUtils.rm_rf(release_path) if ::File.exist?(release_path)
271
271
  FileUtils.mkdir_p(target_dir_path)
272
272
  FileUtils.cp_r(::File.join(@new_resource.destination, "."), release_path, :preserve => true)
@@ -84,7 +84,7 @@ class Chef
84
84
  # <false>:: If a change is not required
85
85
  def compare_group
86
86
  @change_desc = [ ]
87
- if @new_resource.gid != @current_resource.gid
87
+ if @new_resource.gid.to_s != @current_resource.gid.to_s
88
88
  @change_desc << "change gid #{@current_resource.gid} to #{@new_resource.gid}"
89
89
  end
90
90
 
@@ -24,6 +24,9 @@ class Chef
24
24
  class Ifconfig
25
25
  class Debian < Chef::Provider::Ifconfig
26
26
 
27
+ INTERFACES_FILE = "/etc/network/interfaces"
28
+ INTERFACES_DOT_D_DIR = "/etc/network/interfaces.d"
29
+
27
30
  def initialize(new_resource, run_context)
28
31
  super(new_resource, run_context)
29
32
  @config_template = %{
@@ -46,22 +49,30 @@ iface <%= @new_resource.device %> inet static
46
49
  <% end %>
47
50
  <% end %>
48
51
  }
49
- @config_path = "/etc/network/interfaces.d/ifcfg-#{@new_resource.device}"
52
+ @config_path = "#{INTERFACES_DOT_D_DIR}/ifcfg-#{@new_resource.device}"
50
53
  end
51
54
 
52
55
  def generate_config
53
- check_interfaces_config
56
+ enforce_interfaces_dot_d_sanity
54
57
  super
55
58
  end
56
59
 
57
60
  protected
58
61
 
59
- def check_interfaces_config
60
- converge_by ('modify configuration file : /etc/network/interfaces') do
61
- Dir.mkdir('/etc/network/interfaces.d') unless ::File.directory?('/etc/network/interfaces.d')
62
- conf = Chef::Util::FileEdit.new('/etc/network/interfaces')
63
- conf.insert_line_if_no_match('^\s*source\s+/etc/network/interfaces[.]d/[*]\s*$', 'source /etc/network/interfaces.d/*')
64
- conf.write_file
62
+ def enforce_interfaces_dot_d_sanity
63
+ # create /etc/network/interfaces.d via dir resource (to get reporting, etc)
64
+ dir = Chef::Resource::Directory.new(INTERFACES_DOT_D_DIR, run_context)
65
+ dir.run_action(:create)
66
+ new_resource.updated_by_last_action(true) if dir.updated_by_last_action?
67
+ # roll our own file_edit resource, this will not get reported until we have a file_edit resource
68
+ interfaces_dot_d_for_regexp = INTERFACES_DOT_D_DIR.gsub(/\./, '\.') # escape dots for the regexp
69
+ regexp = %r{^\s*source\s+#{interfaces_dot_d_for_regexp}/\*\s*$}
70
+ unless ::File.exists?(INTERFACES_FILE) && regexp.match(IO.read(INTERFACES_FILE))
71
+ converge_by("modifying #{INTERFACES_FILE} to source #{INTERFACES_DOT_D_DIR}") do
72
+ conf = Chef::Util::FileEdit.new(INTERFACES_FILE)
73
+ conf.insert_line_if_no_match(regexp, "source #{INTERFACES_DOT_D_DIR}/*")
74
+ conf.write_file
75
+ end
65
76
  end
66
77
  end
67
78
 
@@ -33,11 +33,12 @@ class Chef
33
33
  def action_reload
34
34
  converge_by("re-run ohai and merge results into node attributes") do
35
35
  ohai = ::Ohai::System.new
36
- if @new_resource.plugin
37
- ohai.require_plugin @new_resource.plugin
38
- else
39
- ohai.all_plugins
40
- end
36
+
37
+ # If @new_resource.plugin is nil, ohai will reload all the plugins
38
+ # Otherwise it will only reload the specified plugin
39
+ # Note that any changes to plugins, or new plugins placed on
40
+ # the path are picked up by ohai.
41
+ ohai.all_plugins @new_resource.plugin
41
42
  node.automatic_attrs.merge! ohai.data
42
43
  Chef::Log.info("#{@new_resource} reloaded")
43
44
  end
@@ -17,6 +17,7 @@
17
17
  #
18
18
 
19
19
  require 'chef/provider/service'
20
+ require 'rexml/document'
20
21
 
21
22
  class Chef
22
23
  class Provider
@@ -41,6 +42,7 @@ class Chef
41
42
  @current_resource.service_name(@new_resource.service_name)
42
43
  @plist_size = 0
43
44
  @plist = find_service_plist
45
+ @service_label = find_service_label
44
46
  set_service_status
45
47
 
46
48
  @current_resource
@@ -48,14 +50,6 @@ class Chef
48
50
 
49
51
  def define_resource_requirements
50
52
  #super
51
- requirements.assert(:enable) do |a|
52
- a.failure_message Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :enable"
53
- end
54
-
55
- requirements.assert(:disable) do |a|
56
- a.failure_message Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :disable"
57
- end
58
-
59
53
  requirements.assert(:reload) do |a|
60
54
  a.failure_message Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :reload"
61
55
  end
@@ -65,6 +59,12 @@ class Chef
65
59
  a.failure_message Chef::Exceptions::Service, "Several plist files match service name. Please use full service name."
66
60
  end
67
61
 
62
+ requirements.assert(:all_actions) do |a|
63
+ a.assertion { !@service_label.to_s.empty? }
64
+ a.failure_message Chef::Exceptions::Service,
65
+ "Could not find service's label in plist file '#{@plist}'!"
66
+ end
67
+
68
68
  requirements.assert(:all_actions) do |a|
69
69
  a.assertion { @plist_size > 0 }
70
70
  # No failrue here in original code - so we also will not
@@ -74,7 +74,6 @@ class Chef
74
74
  @current_resource.running(false)
75
75
  end
76
76
  end
77
-
78
77
  end
79
78
 
80
79
  def start_service
@@ -111,19 +110,56 @@ class Chef
111
110
  end
112
111
  end
113
112
 
113
+ # On OS/X, enabling a service has the side-effect of starting it,
114
+ # and disabling a service has the side-effect of stopping it.
115
+ #
116
+ # This makes some sense on OS/X since launchctl is an "init"-style
117
+ # supervisor that will restart daemons that are crashing, etc.
118
+ def enable_service
119
+ if @current_resource.enabled
120
+ Chef::Log.debug("#{@new_resource} already enabled, not enabling")
121
+ else
122
+ shell_out!(
123
+ "launchctl load -w '#{@plist}'",
124
+ :user => @owner_uid, :group => @owner_gid
125
+ )
126
+ end
127
+ end
128
+
129
+ def disable_service
130
+ unless @current_resource.enabled
131
+ Chef::Log.debug("#{@new_resource} not enabled, not disabling")
132
+ else
133
+ shell_out!(
134
+ "launchctl unload -w '#{@plist}'",
135
+ :user => @owner_uid, :group => @owner_gid
136
+ )
137
+ end
138
+ end
114
139
 
115
140
  def set_service_status
116
- return if @plist == nil
141
+ return if @plist == nil or @service_label.to_s.empty?
117
142
 
118
- @current_resource.enabled(!@plist.nil?)
143
+ cmd = shell_out(
144
+ "launchctl list #{@service_label}",
145
+ :user => @owner_uid, :group => @owner_gid
146
+ )
147
+
148
+ if cmd.exitstatus == 0
149
+ @current_resource.enabled(true)
150
+ else
151
+ @current_resource.enabled(false)
152
+ end
119
153
 
120
154
  if @current_resource.enabled
121
155
  @owner_uid = ::File.stat(@plist).uid
122
156
  @owner_gid = ::File.stat(@plist).gid
123
157
 
124
- shell_out!("launchctl list", :user => @owner_uid, :group => @owner_gid).stdout.each_line do |line|
158
+ shell_out!(
159
+ "launchctl list", :user => @owner_uid, :group => @owner_gid
160
+ ).stdout.each_line do |line|
125
161
  case line
126
- when /(\d+|-)\s+(?:\d+|-)\s+(.*\.?)#{@current_resource.service_name}/
162
+ when /(\d+|-)\s+(?:\d+|-)\s+(.*\.?)#{@service_label}/
127
163
  pid = $1
128
164
  @current_resource.running(!pid.to_i.zero?)
129
165
  end
@@ -135,9 +171,27 @@ class Chef
135
171
 
136
172
  private
137
173
 
174
+ def find_service_label
175
+ # Most services have the same internal label as the name of the
176
+ # plist file. However, there is no rule saying that *has* to be
177
+ # the case, and some core services (notably, ssh) do not follow
178
+ # this rule.
179
+
180
+ # plist files can come in XML or Binary formats. this command
181
+ # will make sure we get XML every time.
182
+ plist_xml = shell_out!("plutil -convert xml1 -o - #{@plist}").stdout
183
+
184
+ plist_doc = REXML::Document.new(plist_xml)
185
+ plist_doc.elements[
186
+ "/plist/dict/key[text()='Label']/following::string[1]/text()"]
187
+ end
188
+
138
189
  def find_service_plist
139
190
  plists = PLIST_DIRS.inject([]) do |results, dir|
140
- entries = Dir.glob("#{::File.expand_path(dir)}/*#{@current_resource.service_name}*.plist")
191
+ edir = ::File.expand_path(dir)
192
+ entries = Dir.glob(
193
+ "#{edir}/*#{@current_resource.service_name}*.plist"
194
+ )
141
195
  entries.any? ? results << entries : results
142
196
  end
143
197
  plists.flatten!
@@ -23,6 +23,7 @@ require 'chef/dsl/data_query'
23
23
  require 'chef/dsl/platform_introspection'
24
24
  require 'chef/dsl/include_recipe'
25
25
  require 'chef/dsl/registry_helper'
26
+ require 'chef/dsl/reboot_pending'
26
27
 
27
28
  require 'chef/mixin/from_file'
28
29
 
@@ -38,6 +39,7 @@ class Chef
38
39
  include Chef::DSL::IncludeRecipe
39
40
  include Chef::DSL::Recipe
40
41
  include Chef::DSL::RegistryHelper
42
+ include Chef::DSL::RebootPending
41
43
 
42
44
  include Chef::Mixin::FromFile
43
45
  include Chef::Mixin::Deprecation
@@ -0,0 +1,37 @@
1
+ # Author:: Prajakta Purohit (<prajakta@opscode.com>)
2
+ # Copyright:: Copyright (c) 2009, 2010, 2013, 2014 Opscode, 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 'chef/monkey_patches/securerandom'
19
+ require 'singleton'
20
+
21
+ class Chef
22
+ class RequestID
23
+ include Singleton
24
+
25
+ def reset_request_id
26
+ @request_id = nil
27
+ end
28
+
29
+ def request_id
30
+ @request_id ||= generate_request_id
31
+ end
32
+
33
+ def generate_request_id
34
+ SecureRandom.uuid
35
+ end
36
+ end
37
+ end