diff options
author | Chris Doherty <randomcamel@users.noreply.github.com> | 2014-09-12 17:24:16 -0700 |
---|---|---|
committer | Chris Doherty <randomcamel@users.noreply.github.com> | 2014-09-12 17:24:16 -0700 |
commit | cb61daebfb0d255cae928ca1a92db29b055755cf (patch) | |
tree | ac2936ed19cfe5f608189aec18ccebe6ee4ad647 /lib | |
parent | 1241ea6f9866d0e61d11129bb32e5fc96cd2bac0 (diff) | |
parent | b6cc74645ead494c35d1ea4ecac1ce039fcd02b4 (diff) | |
download | chef-cb61daebfb0d255cae928ca1a92db29b055755cf.tar.gz |
Merge pull request #1979 from opscode/cdoherty-reboot-core
Add a Reboot resource into core.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/chef/client.rb | 9 | ||||
-rw-r--r-- | lib/chef/dsl/reboot_pending.rb | 9 | ||||
-rw-r--r-- | lib/chef/platform/rebooter.rb | 54 | ||||
-rw-r--r-- | lib/chef/provider/reboot.rb | 69 | ||||
-rw-r--r-- | lib/chef/providers.rb | 1 | ||||
-rw-r--r-- | lib/chef/resource/reboot.rb | 48 | ||||
-rw-r--r-- | lib/chef/resources.rb | 1 | ||||
-rw-r--r-- | lib/chef/run_context.rb | 25 |
8 files changed, 212 insertions, 4 deletions
diff --git a/lib/chef/client.rb b/lib/chef/client.rb index 2de3ca3e64..161ecddb0f 100644 --- a/lib/chef/client.rb +++ b/lib/chef/client.rb @@ -44,6 +44,7 @@ require 'chef/resource_reporter' require 'chef/run_lock' require 'chef/policy_builder' require 'chef/request_id' +require 'chef/platform/rebooter' require 'ohai' require 'rbconfig' @@ -427,7 +428,9 @@ class Chef run_context = setup_run_context - converge(run_context) + catch (:end_client_run_early) do + converge(run_context) + end save_updated_node @@ -435,6 +438,10 @@ class Chef Chef::Log.info("Chef Run complete in #{run_status.elapsed_time} seconds") run_completed_successfully @events.run_completed(node) + + # rebooting has to be the last thing we do, no exceptions. + Chef::Platform::Rebooter.reboot_if_needed!(node) + true rescue Exception => e # CHEF-3336: Send the error first in case something goes wrong below and we don't know why diff --git a/lib/chef/dsl/reboot_pending.rb b/lib/chef/dsl/reboot_pending.rb index 9f80d38c61..a81debce99 100644 --- a/lib/chef/dsl/reboot_pending.rb +++ b/lib/chef/dsl/reboot_pending.rb @@ -27,10 +27,13 @@ class Chef include Chef::DSL::PlatformIntrospection # Returns true if the system needs a reboot or is expected to reboot - # Raises UnsupportedPlatform if this functionality isn't provided yet + # Note that we will silently miss any other platform-specific reboot notices besides Windows+Ubuntu. def reboot_pending? - if platform?("windows") + # don't break when used as a mixin in contexts without #node (e.g. specs). + if self.respond_to?(:node, true) && node.run_context.reboot_requested? + true + elsif platform?("windows") # PendingFileRenameOperations contains pairs (REG_MULTI_SZ) of filenames that cannot be updated # due to a file being in use (usually a temporary file and a system file) # \??\c:\temp\test.sys!\??\c:\winnt\system32\test.sys @@ -53,7 +56,7 @@ class Chef # This should work for Debian as well if update-notifier-common happens to be installed. We need an API for that. File.exists?('/var/run/reboot-required') else - raise Chef::Exceptions::UnsupportedPlatform.new(node[:platform]) + false end end end diff --git a/lib/chef/platform/rebooter.rb b/lib/chef/platform/rebooter.rb new file mode 100644 index 0000000000..b46f0e394c --- /dev/null +++ b/lib/chef/platform/rebooter.rb @@ -0,0 +1,54 @@ +# +# Author:: Chris Doherty <cdoherty@getchef.com>) +# Copyright:: Copyright (c) 2014 Chef, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/dsl/reboot_pending' +require 'chef/log' +require 'chef/platform' + +class Chef + class Platform + module Rebooter + extend Chef::Mixin::ShellOut + + class << self + + def reboot!(node) + reboot_info = node.run_context.reboot_info + + cmd = if Chef::Platform.windows? + # should this do /f as well? do we then need a minimum delay to let apps quit? + "shutdown /r /t #{reboot_info[:delay_mins]} /c \"#{reboot_info[:reason]}\"" + else + # probably Linux-only. + "shutdown -r +#{reboot_info[:delay_mins]} \"#{reboot_info[:reason]}\"" + end + + Chef::Log.warn "Rebooting server at a recipe's request. Details: #{reboot_info.inspect}" + shell_out!(cmd) + end + + # this is a wrapper function so Chef::Client only needs a single line of code. + def reboot_if_needed!(node) + if node.run_context.reboot_requested? + reboot!(node) + end + end + end + end + end +end diff --git a/lib/chef/provider/reboot.rb b/lib/chef/provider/reboot.rb new file mode 100644 index 0000000000..8dde4653ec --- /dev/null +++ b/lib/chef/provider/reboot.rb @@ -0,0 +1,69 @@ +# +# Author:: Chris Doherty <cdoherty@getchef.com>) +# Copyright:: Copyright (c) 2014 Chef, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/log' +require 'chef/provider' + +class Chef + class Provider + class Reboot < Chef::Provider + + def whyrun_supported? + true + end + + def load_current_resource + @current_resource ||= Chef::Resource::Reboot.new(@new_resource.name) + @current_resource.reason(@new_resource.reason) + @current_resource.delay_mins(@new_resource.delay_mins) + @current_resource + end + + def request_reboot + node.run_context.request_reboot( + :delay_mins => @new_resource.delay_mins, + :reason => @new_resource.reason, + :timestamp => Time.now, + :requested_by => @new_resource.name + ) + end + + def action_request_reboot + converge_by("request a system reboot to occur if the run succeeds") do + Chef::Log.warn "Reboot requested:'#{@new_resource.name}'" + request_reboot + end + end + + def action_reboot_now + converge_by("rebooting the system immediately") do + Chef::Log.warn "Rebooting system immediately, requested by '#{@new_resource.name}'" + request_reboot + throw :end_client_run_early + end + end + + def action_cancel + converge_by("cancel any existing end-of-run reboot request") do + Chef::Log.warn "Reboot canceled: '#{@new_resource.name}'" + node.run_context.cancel_reboot + end + end + end + end +end diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb index 3c9e94e6f7..1b0ff3ffff 100644 --- a/lib/chef/providers.rb +++ b/lib/chef/providers.rb @@ -39,6 +39,7 @@ require 'chef/provider/mdadm' require 'chef/provider/mount' require 'chef/provider/package' require 'chef/provider/powershell_script' +require 'chef/provider/reboot' require 'chef/provider/remote_directory' require 'chef/provider/remote_file' require 'chef/provider/route' diff --git a/lib/chef/resource/reboot.rb b/lib/chef/resource/reboot.rb new file mode 100644 index 0000000000..d6caafdea8 --- /dev/null +++ b/lib/chef/resource/reboot.rb @@ -0,0 +1,48 @@ +# +# Author:: Chris Doherty <cdoherty@getchef.com>) +# Copyright:: Copyright (c) 2014 Chef, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/resource' + +# In using this resource via notifications, it's important to *only* use +# immediate notifications. Delayed notifications produce unintuitive and +# probably undesired results. +class Chef + class Resource + class Reboot < Chef::Resource + def initialize(name, run_context=nil) + super + @resource_name = :reboot + @provider = Chef::Provider::Reboot + @allowed_actions = [:request_reboot, :reboot_now, :cancel] + + @reason = "Reboot by Chef" + @delay_mins = 0 + + # no default action. + end + + def reason(arg=nil) + set_or_return(:reason, arg, :kind_of => String) + end + + def delay_mins(arg=nil) + set_or_return(:delay_mins, arg, :kind_of => Fixnum) + end + end + end +end diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb index 93ff682288..8c2f71bd30 100644 --- a/lib/chef/resources.rb +++ b/lib/chef/resources.rb @@ -53,6 +53,7 @@ require 'chef/resource/perl' require 'chef/resource/portage_package' require 'chef/resource/powershell_script' require 'chef/resource/python' +require 'chef/resource/reboot' require 'chef/resource/registry_key' require 'chef/resource/remote_directory' require 'chef/resource/remote_file' diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb index 3dd53f0f8f..bbe2f9eba0 100644 --- a/lib/chef/run_context.rb +++ b/lib/chef/run_context.rb @@ -61,6 +61,9 @@ class Chef # Event dispatcher for this run. attr_reader :events + # Hash of factoids for a reboot request. + attr_reader :reboot_info + # Creates a new Chef::RunContext object and populates its fields. This object gets # used by the Chef Server to generate a fully compiled recipe list for a node. # @@ -76,6 +79,7 @@ class Chef @loaded_recipes = {} @loaded_attributes = {} @events = events + @reboot_info = {} @node.run_context = self @@ -271,6 +275,27 @@ ERROR_MESSAGE end end + # there are options for how to handle multiple calls to these functions: + # 1. first call always wins (never change @reboot_info once set). + # 2. last call always wins (happily change @reboot_info whenever). + # 3. raise an exception on the first conflict. + # 4. disable reboot after this run if anyone ever calls :cancel. + # 5. raise an exception on any second call. + # 6. ? + def request_reboot(reboot_info) + Chef::Log::info "Changing reboot status from #{@reboot_info.inspect} to #{reboot_info.inspect}" + @reboot_info = reboot_info + end + + def cancel_reboot + Chef::Log::info "Changing reboot status from #{@reboot_info.inspect} to {}" + @reboot_info = {} + end + + def reboot_requested? + @reboot_info.size > 0 + end + private def loaded_recipe(cookbook, recipe) |