class Chef::Application

Public Class Methods

chef_zero_server() click to toggle source
# File lib/chef/application.rb, line 209
def self.chef_zero_server
  @chef_zero_server
end
destroy_server_connectivity() click to toggle source
# File lib/chef/application.rb, line 213
def self.destroy_server_connectivity
  if @chef_zero_server
    @chef_zero_server.stop
    @chef_zero_server = nil
  end
end
new() click to toggle source
Calls superclass method
# File lib/chef/application.rb, line 33
def initialize
  super

  @chef_client = nil
  @chef_client_json = nil

  # Always switch to a readable directory. Keeps subsequent Dir.chdir() {}
  # from failing due to permissions when launched as a less privileged user.
end
setup_server_connectivity() click to toggle source
# File lib/chef/application.rb, line 186
def self.setup_server_connectivity
  if Chef::Config.chef_zero.enabled
    destroy_server_connectivity

    require 'chef_zero/server'
    require 'chef/chef_fs/chef_fs_data_store'
    require 'chef/chef_fs/config'

    chef_fs = Chef::ChefFS::Config.new.local_fs
    chef_fs.write_pretty_json = true
    data_store = Chef::ChefFS::ChefFSDataStore.new(chef_fs)
    server_options = {}
    server_options[:data_store] = data_store
    server_options[:log_level] = Chef::Log.level
    server_options[:host] = Chef::Config.chef_zero.host
    server_options[:port] = Chef::Config.chef_zero.port
    Chef::Log.info("Starting chef-zero on host #{Chef::Config.chef_zero.host}, port #{Chef::Config.chef_zero.port} with repository at #{chef_fs.fs_description}")
    @chef_zero_server = ChefZero::Server.new(server_options)
    @chef_zero_server.start_background
    Chef::Config.chef_server_url = @chef_zero_server.url
  end
end

Private Class Methods

debug_stacktrace(e) click to toggle source
# File lib/chef/application.rb, line 332
def debug_stacktrace(e)
  message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
  chef_stacktrace_out = "Generated at #{Time.now.to_s}\n"
  chef_stacktrace_out += message

  Chef::FileCache.store("chef-stacktrace.out", chef_stacktrace_out)
  Chef::Log.fatal("Stacktrace dumped to #{Chef::FileCache.load("chef-stacktrace.out", false)}")
  Chef::Log.debug(message)
  true
end
exit!(msg, err = -1) click to toggle source
# File lib/chef/application.rb, line 349
def exit!(msg, err = -1)
  Chef::Log.debug(msg)
  Process.exit err
end
fatal!(msg, err = -1) click to toggle source

Log a fatal error message to both STDERR and the Logger, exit the application

# File lib/chef/application.rb, line 344
def fatal!(msg, err = -1)
  Chef::Log.fatal(msg)
  Process.exit err
end

Public Instance Methods

auto_log_level?() click to toggle source
# File lib/chef/application.rb, line 150
def auto_log_level?
  Chef::Config[:log_level] == :auto
end
configure_chef() click to toggle source

Parse configuration (options and config file)

# File lib/chef/application.rb, line 76
def configure_chef
  parse_options
  load_config_file
end
configure_logging() click to toggle source

Initialize and configure the logger.

Loggers and Formatters

In Chef 10.x and previous, the Logger was the primary/only way that Chef communicated information to the user. In Chef 10.14, a new system, “output formatters” was added, and in Chef 11.0+ it is the default when running chef in a console (detected by `STDOUT.tty?`). Because output formatters are more complex than the logger system and users have less experience with them, the config option `force_logger` is provided to restore the Chef 10.x behavior.

Conversely, for users who want formatter output even when chef is running unattended, the `force_formatter` option is provided.

Auto Log Level

When `log_level` is set to `:auto` (default), the log level will be `:warn` when the primary output mode is an output formatter (see using_output_formatter?) and `:info` otherwise.

Automatic STDOUT Logging

When `force_logger` is configured (e.g., Chef 10 mode), a second logger with output on STDOUT is added when running in a console (STDOUT is a tty) and the configured log_location isn't STDOUT. This accounts for the case that a user has configured a log_location in client.rb, but is running chef-client by hand to troubleshoot a problem.

# File lib/chef/application.rb, line 121
def configure_logging
  Chef::Log.init(MonoLogger.new(Chef::Config[:log_location]))
  if want_additional_logger?
    configure_stdout_logger
  end
  Chef::Log.level = resolve_log_level
rescue StandardError => error
  Chef::Log.fatal("Failed to open or create log file at #{Chef::Config[:log_location]}: #{error.class} (#{error.message})")
  Chef::Application.fatal!("Aborting due to invalid 'log_location' configuration", 2)
end
configure_proxy_environment_variables() click to toggle source

Configure and set any proxy environment variables according to the config.

# File lib/chef/application.rb, line 169
def configure_proxy_environment_variables
  configure_http_proxy
  configure_https_proxy
  configure_ftp_proxy
  configure_no_proxy
end
configure_stdout_logger() click to toggle source
# File lib/chef/application.rb, line 132
def configure_stdout_logger
  stdout_logger = MonoLogger.new(STDOUT)
  stdout_logger.formatter = Chef::Log.logger.formatter
  Chef::Log.loggers <<  stdout_logger
end
load_config_file() click to toggle source

Parse the config file

# File lib/chef/application.rb, line 82
def load_config_file
  config_fetcher = Chef::ConfigFetcher.new(config[:config_file], Chef::Config.config_file_jail)
  if config[:config_file].nil?
    Chef::Log.warn("No config file found or specified on command line, using command line options.")
  elsif config_fetcher.config_missing?
    Chef::Log.warn("*****************************************")
    Chef::Log.warn("Did not find config file: #{config[:config_file]}, using command line options.")
    Chef::Log.warn("*****************************************")
  else
    config_content = config_fetcher.read_config
    apply_config(config_content, config[:config_file])
  end
  Chef::Config.merge!(config)
end
reconfigure() click to toggle source

Reconfigure the application. You'll want to override and super this method.

# File lib/chef/application.rb, line 44
def reconfigure
  configure_chef
  configure_logging
  configure_proxy_environment_variables
end
resolve_log_level() click to toggle source

if log_level is `:auto`, convert it to :warn (when using output formatter) or :info (no output formatter). See also using_output_formatter?

# File lib/chef/application.rb, line 156
def resolve_log_level
  if auto_log_level?
    if using_output_formatter?
      :warn
    else
      :info
    end
  else
    Chef::Config[:log_level]
  end
end
run() click to toggle source

Get this party started

# File lib/chef/application.rb, line 51
def run
  setup_signal_handlers
  reconfigure
  setup_application
  run_application
end
run_application() click to toggle source

Actually run the application

# File lib/chef/application.rb, line 182
def run_application
  raise Chef::Exceptions::Application, "#{self.to_s}: you must override run_application"
end
run_chef_client(specific_recipes = []) click to toggle source

Initializes Chef::Client instance and runs it

# File lib/chef/application.rb, line 221
def run_chef_client(specific_recipes = [])
  Chef::Application.setup_server_connectivity

  override_runlist = config[:override_runlist]
  if specific_recipes.size > 0
    override_runlist ||= []
  end
  @chef_client = Chef::Client.new(
    @chef_client_json,
    :override_runlist => config[:override_runlist],
    :specific_recipes => specific_recipes,
    :runlist => config[:runlist]
  )
  @chef_client_json = nil

  @chef_client.run
  @chef_client = nil

  Chef::Application.destroy_server_connectivity
end
setup_application() click to toggle source

Called prior to starting the application, by the run method

# File lib/chef/application.rb, line 177
def setup_application
  raise Chef::Exceptions::Application, "#{self.to_s}: you must override setup_application"
end
setup_signal_handlers() click to toggle source
# File lib/chef/application.rb, line 58
def setup_signal_handlers
  trap("INT") do
    Chef::Application.fatal!("SIGINT received, stopping", 2)
  end

  unless Chef::Platform.windows?
    trap("QUIT") do
      Chef::Log.info("SIGQUIT received, call stack:\n  " + caller.join("\n  "))
    end

    trap("HUP") do
      Chef::Log.info("SIGHUP received, reconfiguring")
      reconfigure
    end
  end
end
using_output_formatter?() click to toggle source

Use of output formatters is assumed if `force_formatter` is set or if `force_logger` is not set and STDOUT is to a console (tty)

# File lib/chef/application.rb, line 146
def using_output_formatter?
  Chef::Config[:force_formatter] || (!Chef::Config[:force_logger] && STDOUT.tty?)
end
want_additional_logger?() click to toggle source

Based on config and whether or not STDOUT is a tty, should we setup a secondary logger for stdout?

# File lib/chef/application.rb, line 140
def want_additional_logger?
  ( Chef::Config[:log_location] != STDOUT ) && STDOUT.tty? && (!Chef::Config[:daemonize]) && (Chef::Config[:force_logger])
end

Private Instance Methods

apply_config(config_content, config_file_path) click to toggle source
# File lib/chef/application.rb, line 244
def apply_config(config_content, config_file_path)
  Chef::Config.from_string(config_content, config_file_path)
rescue Exception => error
  Chef::Log.fatal("Configuration error #{error.class}: #{error.message}")
  filtered_trace = error.backtrace.grep(/#{Regexp.escape(config_file_path)}/)
  filtered_trace.each {|line| Chef::Log.fatal("  " + line )}
  Chef::Application.fatal!("Aborting due to error in '#{config_file_path}'", 2)
end
configure_ftp_proxy() click to toggle source

Set ENV

# File lib/chef/application.rb, line 274
def configure_ftp_proxy
  if ftp_proxy = Chef::Config[:ftp_proxy]
    ftp_proxy_string = configure_proxy("ftp", ftp_proxy,
      Chef::Config[:ftp_proxy_user], Chef::Config[:ftp_proxy_pass])
    env['ftp_proxy'] = ftp_proxy_string unless env['ftp_proxy']
    env['FTP_PROXY'] = ftp_proxy_string unless env['FTP_PROXY']
  end
end
configure_http_proxy() click to toggle source

Set ENV

# File lib/chef/application.rb, line 254
def configure_http_proxy
  if http_proxy = Chef::Config[:http_proxy]
    http_proxy_string = configure_proxy("http", http_proxy,
        Chef::Config[:http_proxy_user], Chef::Config[:http_proxy_pass])
    env['http_proxy'] = http_proxy_string unless env['http_proxy']
    env['HTTP_PROXY'] = http_proxy_string unless env['HTTP_PROXY']
  end
end
configure_https_proxy() click to toggle source

Set ENV

# File lib/chef/application.rb, line 264
def configure_https_proxy
  if https_proxy = Chef::Config[:https_proxy]
    https_proxy_string = configure_proxy("https", https_proxy,
        Chef::Config[:https_proxy_user], Chef::Config[:https_proxy_pass])
    env['https_proxy'] = https_proxy_string unless env['https_proxy']
    env['HTTPS_PROXY'] = https_proxy_string unless env['HTTPS_PROXY']
  end
end
configure_no_proxy() click to toggle source

Set ENV

# File lib/chef/application.rb, line 284
def configure_no_proxy
  if Chef::Config[:no_proxy]
    env['no_proxy'] = Chef::Config[:no_proxy] unless env['no_proxy']
    env['NO_PROXY'] = Chef::Config[:no_proxy] unless env['NO_PROXY']
  end
end
configure_proxy(scheme, path, user, pass) click to toggle source

Builds a proxy uri. Examples:

http://username:password@hostname:port
https://username@hostname:port
ftp://hostname:port

when

scheme = "http", "https", or "ftp"
hostport = hostname:port
user = username
pass = password
# File lib/chef/application.rb, line 300
def configure_proxy(scheme, path, user, pass)
  begin
    path = "#{scheme}://#{path}" unless path.include?('://')
    # URI.split returns the following parts:
    # [scheme, userinfo, host, port, registry, path, opaque, query, fragment]
    parts = URI.split(URI.encode(path))
    # URI::Generic.build requires an integer for the port, but URI::split gives
    # returns a string for the port.
    parts[3] = parts[3].to_i if parts[3]
    if user
      userinfo = URI.encode(URI.encode(user), '@:')
      if pass
        userinfo << ":#{URI.encode(URI.encode(pass), '@:')}"
      end
      parts[1] = userinfo
    end

    return URI::Generic.build(parts).to_s
  rescue URI::Error => e
    # URI::Error messages generally include the offending string. Including a message
    # for which proxy config item has the issue should help deduce the issue when
    # the URI::Error message is vague.
    raise Chef::Exceptions::BadProxyURI, "Cannot configure #{scheme} proxy. Does not comply with URI scheme. #{e.message}"
  end
end
env() click to toggle source

This is a hook for testing

# File lib/chef/application.rb, line 327
def env
  ENV
end