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
 |