2017-06-16 11:25:49 +02:00

145 lines
5.1 KiB
Ruby

#
# Cookbook:: chef_client_updater
# Resource:: updater
#
# Copyright:: 2016-2017, Will Jordan
# Copyright:: 2016-2017, Chef Software, Inc.
#
# 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.
#
provides 'chef_client_updater'
property :channel, [String, Symbol], default: :stable
property :prevent_downgrade, [true, false], default: false
property :version, [String, Symbol], default: :latest
property :post_install_action, String, default: 'exec'
property :exec_command, String, default: $PROGRAM_NAME.split(' ').first
property :exec_args, Array, default: ARGV
property :download_url_override, String
property :checksum, String
action :update do
if update_necessary?
converge_by "Upgraded chef-client #{current_version} to #{desired_version}" do
upgrade_command = Mixlib::ShellOut.new(mixlib_install.install_command)
upgrade_command.run_command
run_post_install_action
end
end
end
action_class do
def load_mixlib_install
gem 'mixlib-install', '~> 3.2', '>= 3.2.1'
require 'mixlib/install'
rescue LoadError
Chef::Log.info('mixlib-install gem not found. Installing now')
chef_gem 'mixlib-install' do
version '>= 3.2.1'
compile_time true if respond_to?(:compile_time)
end
require 'mixlib/install'
end
def load_mixlib_versioning
require 'mixlib/versioning'
rescue LoadError
Chef::Log.info('mixlib-versioning gem not found. Installing now')
chef_gem 'mixlib-versioning' do
compile_time true if respond_to?(:compile_time)
end
require 'mixlib/versioning'
end
def mixlib_install
load_mixlib_install
options = {
product_name: 'chef',
platform_version_compatibility_mode: true,
channel: new_resource.channel.to_sym,
product_version: new_resource.version == 'latest' ? :latest : new_resource.version,
}
if new_resource.download_url_override && new_resource.checksum
options[:install_command_options] = { download_url_override: new_resource.download_url_override, checksum: new_resource.checksum }
end
Mixlib::Install.new(options)
end
# why would we use this when mixlib-install has a current_version method?
# well mixlib-version parses the manifest JSON, which might not be there.
# ohai handles this better IMO
def current_version
node['chef_packages']['chef']['version']
end
def desired_version
new_resource.version.to_sym == :latest ? mixlib_install.available_versions.last : new_resource.version
end
# why wouldn't we use the built in update_available? method in mixlib-install?
# well that would use current_version from mixlib-install and it has no
# concept or preventing downgrades
def update_necessary?
load_mixlib_versioning
cur_version = Mixlib::Versioning.parse(current_version)
des_version = Mixlib::Versioning.parse(desired_version)
Chef::Log.debug("The current chef-client version is #{cur_version} and the desired version is #{desired_version}")
new_resource.prevent_downgrade ? (des_version > cur_version) : (des_version != cur_version)
end
def eval_post_install_action
return unless new_resource.post_install_action == 'exec'
if Chef::Config[:interval]
Chef::Log.warn 'post_install_action "exec" not supported for long-running client process -- changing to "kill".'
new_resource.post_install_action = 'kill'
end
return if Process.respond_to?(:exec)
Chef::Log.warn 'post_install_action Process.exec not available -- changing to "kill".'
new_resource.post_install_action = 'kill'
end
def run_post_install_action
# make sure the passed action will actually work
eval_post_install_action
case new_resource.post_install_action
when 'exec'
if Chef::Config[:local_mode]
Chef::Log.info 'Shutting down local-mode server.'
if Chef::Application.respond_to?(:destroy_server_connectivity)
Chef::Application.destroy_server_connectivity
elsif defined?(Chef::LocalMode) && Chef::LocalMode.respond_to?(:destroy_server_connectivity)
Chef::LocalMode.destroy_server_connectivity
end
end
Chef::Log.warn 'Replacing ourselves with the new version of Chef to continue the run.'
Kernel.exec(new_resource.exec_command, *new_resource.exec_args)
when 'kill'
if Chef::Config[:client_fork] && Process.ppid != 1
Chef::Log.warn 'Chef client is defined for forked runs. Sending TERM to parent process!'
Process.kill('TERM', Process.ppid)
end
Chef::Log.warn 'New chef-client installed. Forcing chef exit!'
exit(213)
else
raise "Unexpected post_install_action behavior: #{new_resource.post_install_action}"
end
end
end