class Chef::Application::Base

This is a temporary class being used as a part of an effort to reduce duplication between Chef::Application::Client and Chef::Application::Solo.

If you are looking to make edits to the Client/Solo behavior please make changes here.

If you are looking to reference or subclass this class, use Chef::Application::Client instead. This base class will be removed once the work is complete and external code will break.

@deprecated use Chef::Application::Client instead, this will be removed in Chef-16

Constants

IMMEDIATE_RUN_SIGNAL
RECONFIGURE_SIGNAL
SELF_PIPE

Mimic self_pipe sleep from Unicorn to capture signals safely

Attributes

chef_client_json[R]

Public Instance Methods

run_application() click to toggle source

Run the chef client, optionally daemonizing or looping at intervals.

# File lib/chef/application/base.rb, line 344
def run_application
  if Chef::Config[:version]
    puts "#{ChefUtils::Dist::Infra::PRODUCT} version: #{::Chef::VERSION}"
  end

  if !Chef::Config[:client_fork] || Chef::Config[:once]
    begin
      # run immediately without interval sleep, or splay
      run_chef_client(Chef::Config[:specific_recipes])
    rescue SystemExit
      raise
    rescue Exception => e
      Chef::Application.fatal!("#{e.class}: #{e.message}", e)
    end
  else
    interval_run_chef_client
  end
end
setup_application() click to toggle source
# File lib/chef/application/base.rb, line 320
def setup_application
  Chef::Daemon.change_privilege
end
setup_signal_handlers() click to toggle source
Calls superclass method Chef::Application#setup_signal_handlers
# File lib/chef/application/base.rb, line 324
def setup_signal_handlers
  super

  unless Chef::Platform.windows?
    SELF_PIPE.replace IO.pipe

    trap("USR1") do
      Chef::Log.info("SIGUSR1 received, will run now or after the current run")
      SELF_PIPE[1].putc(IMMEDIATE_RUN_SIGNAL) # wakeup master process from select
    end

    # Override the trap setup in the parent so we can avoid running reconfigure during a run
    trap("HUP") do
      Chef::Log.info("SIGHUP received, will reconfigure now or after the current run")
      SELF_PIPE[1].putc(RECONFIGURE_SIGNAL) # wakeup master process from select
    end
  end
end

Private Instance Methods

fetch_recipe_tarball(url, path) click to toggle source
# File lib/chef/application/base.rb, line 379
def fetch_recipe_tarball(url, path)
  require "open-uri" unless defined?(OpenURI)
  uri = URI.parse(url)

  Chef::Log.trace("Download recipes tarball from #{url} to #{path}")
  if File.exist?(url)
    FileUtils.cp(url, path)
  elsif uri.scheme == "s3"
    require "aws-sdk-s3" unless defined?(Aws::S3)

    s3 = Aws::S3::Client.new
    object = s3.get_object(bucket: uri.hostname, key: uri.path[1..-1])
    File.open(path, "wb") do |f|
      f.write(object.body.read)
    end
  elsif URI::DEFAULT_PARSER.make_regexp.match?(url)
    File.open(path, "wb") do |f|
      URI.open(url) do |r|
        f.write(r.read)
      end
    end
  else
    Chef::Application.fatal! "You specified --recipe-url but the value is neither a valid URL, an S3 bucket nor a path to a file that exists on disk." +
      "Please confirm the location of the tarball and try again."
  end
end
for_ezra() click to toggle source
# File lib/chef/application/base.rb, line 470
  def for_ezra
    puts <<~EOH
      For Ezra Zygmuntowicz:
        The man who brought you Chef Solo
        Early contributor to Chef
        Kind hearted open source advocate
        Rest in peace, Ezra.
    EOH
  end
interval_run_chef_client() click to toggle source
# File lib/chef/application/base.rb, line 406
def interval_run_chef_client
  if Chef::Config[:daemonize]
    Chef::Daemon.daemonize(ChefUtils::Dist::Infra::PRODUCT)

    # Start first daemonized run after configured number of seconds
    if Chef::Config[:daemonize].is_a?(Integer)
      sleep_then_run_chef_client(Chef::Config[:daemonize])
    end
  end

  loop do
    sleep_then_run_chef_client(time_to_sleep)
    Chef::Application.exit!("Exiting", 0) unless Chef::Config[:interval]
  end
end
interval_sleep(sec) click to toggle source

sleep and handle queued signals

# File lib/chef/application/base.rb, line 451
def interval_sleep(sec)
  unless SELF_PIPE.empty?
    # mimic sleep with a timeout on IO.select, listening for signals setup in #setup_signal_handlers
    return unless IO.select([ SELF_PIPE[0] ], nil, nil, sec)

    signal = SELF_PIPE[0].getc.chr

    return if signal == IMMEDIATE_RUN_SIGNAL # be explicit about this behavior

    # we need to sleep again after reconfigure to avoid stampeding when logrotate runs out of cron
    if signal == RECONFIGURE_SIGNAL
      reconfigure
      interval_sleep(sec)
    end
  else
    sleep(sec)
  end
end
sleep_then_run_chef_client(sleep_sec) click to toggle source
# File lib/chef/application/base.rb, line 422
def sleep_then_run_chef_client(sleep_sec)
  Chef::Log.trace("Sleeping for #{sleep_sec} seconds")

  # interval_sleep will return early if we received a signal (unless on windows)
  interval_sleep(sleep_sec)

  run_chef_client(Chef::Config[:specific_recipes])

  reconfigure
rescue SystemExit => e
  raise
rescue Exception => e
  if Chef::Config[:interval]
    Chef::Log.error("#{e.class}: #{e}")
    Chef::Log.trace("#{e.class}: #{e}\n#{e.backtrace.join("\n")}")
    retry
  end

  Chef::Application.fatal!("#{e.class}: #{e.message}", e)
end
time_to_sleep() click to toggle source
# File lib/chef/application/base.rb, line 443
def time_to_sleep
  duration = 0
  duration += rand(Chef::Config[:splay]) if Chef::Config[:splay]
  duration += Chef::Config[:interval] if Chef::Config[:interval]
  duration
end
unforked_interval_error_message() click to toggle source
# File lib/chef/application/base.rb, line 372
def unforked_interval_error_message
  "Unforked #{ChefUtils::Dist::Infra::PRODUCT} interval runs are disabled by default." +
    "\nConfiguration settings:" +
    ("\n  interval  = #{Chef::Config[:interval]} seconds" if Chef::Config[:interval]).to_s +
    "\nEnable #{ChefUtils::Dist::Infra::PRODUCT} interval runs by setting `:client_fork = true` in your config file or adding `--fork` to your command line options."
end
windows_interval_error_message() click to toggle source
# File lib/chef/application/base.rb, line 365
def windows_interval_error_message
  "Windows #{ChefUtils::Dist::Infra::PRODUCT} interval runs are not supported in #{ChefUtils::Dist::Infra::PRODUCT} 15 and later." +
    "\nConfiguration settings:" +
    ("\n  interval  = #{Chef::Config[:interval]} seconds" if Chef::Config[:interval]).to_s +
    "\nPlease manage #{ChefUtils::Dist::Infra::PRODUCT} as a scheduled task instead."
end