class OctocatalogDiff::Cli

This is the CLI for catalog-diff. It's responsible for parsing the command line arguments and then handing off to appropriate methods to perform the catalog-diff.

Constants

DEFAULT_IGNORES

The default type+title+attribute to ignore in catalog-diff.

DEFAULT_OPTIONS

The default options.

EXITCODE_FAILURE
EXITCODE_SUCCESS_NO_DIFFS

Exit codes

EXITCODE_SUCCESS_WITH_DIFFS
VERSION

Version number

Public Class Methods

bootstrap_then_exit(logger, catalogs_obj) click to toggle source

–bootstrap-then-exit command

# File lib/octocatalog-diff/cli.rb, line 226
def self.bootstrap_then_exit(logger, catalogs_obj)
  catalogs_obj.bootstrap_then_exit
  return EXITCODE_SUCCESS_NO_DIFFS
rescue OctocatalogDiff::Errors::BootstrapError => exc
  logger.fatal("--bootstrap-then-exit error: bootstrap failed (#{exc})")
  return EXITCODE_FAILURE
end
catalog_only(logger, options) click to toggle source

Compile the catalog only

# File lib/octocatalog-diff/cli.rb, line 207
def self.catalog_only(logger, options)
  opts = options.merge(logger: logger)
  to_catalog = OctocatalogDiff::API::V1.catalog(opts)

  # If the catalog compilation failed, an exception would have been thrown. So if
  # we get here, the catalog succeeded. Dump the catalog to the appropriate place
  # and exit successfully.
  if options[:output_file]
    File.open(options[:output_file], 'w') { |f| f.write(to_catalog.to_json) }
    logger.info "Wrote catalog to #{options[:output_file]}"
  else
    puts to_catalog.to_json
  end

  return { exitcode: EXITCODE_SUCCESS_NO_DIFFS, to: to_catalog } if options[:INTEGRATION] # For integration testing
  EXITCODE_SUCCESS_NO_DIFFS
end
cli(argv = ARGV, logger = Logger.new(STDERR), opts = {}) click to toggle source

This method is the one to call externally. It is possible to specify alternate command line arguments, for testing. @param argv [Array] Use specified arguments (defaults to ARGV) @param logger [Logger] Logger object @param opts [Hash] Additional options @return [Integer] Exit code: 0=no diffs, 1=something went wrong, 2=worked but there are diffs

# File lib/octocatalog-diff/cli.rb, line 57
def self.cli(argv = ARGV, logger = Logger.new(STDERR), opts = {})
  # Save a copy of argv to print out later in debugging
  argv_save = OctocatalogDiff::Util::Util.deep_dup(argv)

  # Are there additional ARGV to munge, e.g. that have been supplied in the options from a
  # configuration file?
  if opts.key?(:additional_argv)
    raise ArgumentError, ':additional_argv must be array!' unless opts[:additional_argv].is_a?(Array)
    argv.concat opts[:additional_argv]
  end

  # Parse command line
  options = parse_opts(argv)

  # Additional options from hard-coded specified options. These are only processed if
  # there are not already values defined from command line options.
  # Note: do NOT use 'options[k] ||= v' here because if the value of options[k] is boolean(false)
  # it will then be overridden. Whereas the intent is to define values only for those keys that don't exist.
  opts.each { |k, v| options[k] = v unless options.key?(k) }
  veto_options = %w(enc header include_tags)
  veto_options.each { |x| options.delete(x.to_sym) if options["no_#{x}".to_sym] }
  if options[:no_hiera_config]
    vetoes = %w[hiera_config to_hiera_config from_hiera_config]
    vetoes.each do |key|
      options.delete(key.to_sym)
    end
  end
  options[:ignore].concat opts.fetch(:additional_ignores, [])

  # Incorporate default options where needed.
  # Note: do NOT use 'options[k] ||= v' here because if the value of options[k] is boolean(false)
  # it will then be overridden. Whereas the intent is to define values only for those keys that don't exist.
  DEFAULT_OPTIONS.each { |k, v| options[k] = v unless options.key?(k) }
  veto_with_none_options = %w(hiera_path hiera_path_strip)
  veto_with_none_options.each { |x| options.delete(x.to_sym) if options[x.to_sym] == :none }

  # Fact and ENC overrides come in here - 'options' is modified
  setup_fact_overrides(options)
  setup_enc_overrides(options)

  # Configure the logger and logger.debug initial information
  # 'logger' is modified and used
  setup_logger(logger, options, argv_save)

  # --catalog-only is a special case that compiles the catalog for the "to" branch
  # and then exits, without doing any 'diff' whatsoever. Support that option.
  return catalog_only(logger, options) if options[:catalog_only]

  # Set up the cached master directory - maintain it, adjust options if needed. However, if we
  # are getting the 'from' catalog from PuppetDB, then don't do this.
  unless options[:cached_master_dir].nil? || options[:from_puppetdb]
    OctocatalogDiff::CatalogUtil::CachedMasterDirectory.run(options, logger)
  end

  # bootstrap_then_exit is a special case that only prepares directories and does not
  # depend on facts. This happens within the 'catalogs' object, since bootstrapping and
  # preparing catalogs are tightly coupled operations. However this does not actually
  # build catalogs.
  if options[:bootstrap_then_exit]
    catalogs_obj = OctocatalogDiff::Util::Catalogs.new(options, logger)
    return bootstrap_then_exit(logger, catalogs_obj)
  end

  # Compile catalogs and do catalog-diff
  node_set = options.delete(:node)
  node_set = [node_set] unless node_set.is_a?(Array)

  # run multiple node diffs in parallel
  catalog_diffs = if node_set.size == 1
    [run_octocatalog_diff(node_set.first, options, logger)]
  else
    ::Parallel.map(node_set, in_threads: 4) { |node| run_octocatalog_diff(node, options, logger) }
  end

  # Return the resulting diff object if requested (generally for testing)
  # or otherwise return exit code
  return catalog_diffs.first if opts[:INTEGRATION]

  all_diffs = catalog_diffs.map(&:diffs)

  all_diffs.each do |diff|
    next unless diff.any?
    return EXITCODE_SUCCESS_WITH_DIFFS
  end

  EXITCODE_SUCCESS_NO_DIFFS
end
parse_opts(argv) click to toggle source

Parse command line options with 'optparse'. Returns a hash with the parsed arguments. @param argv [Array] Command line arguments (MUST be specified) @return [Hash] Options

# File lib/octocatalog-diff/cli.rb, line 166
def self.parse_opts(argv)
  options = { ignore: OctocatalogDiff::Util::Util.deep_dup(DEFAULT_IGNORES) }
  Options.parse_options(argv, options)
end
run_octocatalog_diff(node, options, logger) click to toggle source

Run the octocatalog-diff process for a given node. Return the diffs for a contribution to the final exit status. node - String with the node options - All of the currently defined options logger - Logger object

# File lib/octocatalog-diff/cli.rb, line 150
def self.run_octocatalog_diff(node, options, logger)
  options_copy = options.merge(node: node)
  catalog_diff = OctocatalogDiff::API::V1.catalog_diff(options_copy.merge(logger: logger))
  diffs = catalog_diff.diffs

  # Display diffs
  printer_obj = OctocatalogDiff::Cli::Printer.new(options_copy, logger)
  printer_obj.printer(diffs, catalog_diff.from.compilation_dir, catalog_diff.to.compilation_dir)

  # Return catalog-diff object.
  catalog_diff
end
setup_enc_overrides(options) click to toggle source

ENC parameter overrides come in here

# File lib/octocatalog-diff/cli.rb, line 187
def self.setup_enc_overrides(options)
  setup_overrides(:from_enc_override, options)
  setup_overrides(:to_enc_override, options)
end
setup_fact_overrides(options) click to toggle source

Fact overrides come in here

# File lib/octocatalog-diff/cli.rb, line 181
def self.setup_fact_overrides(options)
  setup_overrides(:from_fact_override, options)
  setup_overrides(:to_fact_override, options)
end
setup_logger(logger, options, argv_save) click to toggle source

Helper method: Configure and setup logger

# File lib/octocatalog-diff/cli.rb, line 193
def self.setup_logger(logger, options, argv_save)
  # Configure the logger
  logger.level = Logger::INFO
  logger.level = Logger::DEBUG if options[:debug]
  logger.level = Logger::ERROR if options[:quiet]

  # Some debugging information up front
  version_display = ENV['OCTOCATALOG_DIFF_CUSTOM_VERSION'] || VERSION
  logger.debug "Running octocatalog-diff #{version_display} with ruby #{RUBY_VERSION}"
  logger.debug "Command line arguments: #{argv_save.inspect}"
  logger.debug "Running on host #{Socket.gethostname} (#{RUBY_PLATFORM})"
end
setup_overrides(key, options) click to toggle source

Generic overrides

# File lib/octocatalog-diff/cli.rb, line 172
def self.setup_overrides(key, options)
  o = options["#{key}_in".to_sym]
  return unless o.is_a?(Array)
  return unless o.any?
  options[key] ||= []
  options[key].concat o.map { |x| OctocatalogDiff::API::V1::Override.create_from_input(x) }
end