145 lines
5.1 KiB
Ruby
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
|