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 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
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
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 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 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
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
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
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
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