Skip to content

Commit 7bc7749

Browse files
author
Kevin Paulisse
committed
Re-namespace the CLI classes
1 parent af85f11 commit 7bc7749

5 files changed

Lines changed: 548 additions & 558 deletions

File tree

lib/octocatalog-diff/cli.rb

Lines changed: 154 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -15,172 +15,170 @@
1515
require 'socket'
1616

1717
module OctocatalogDiff
18-
module CatalogDiff
19-
# This is the CLI for catalog-diff. It's responsible for parsing the command line
20-
# arguments and then handing off to appropriate methods to perform the catalog-diff.
21-
class Cli
22-
# Version number
23-
VERSION = OctocatalogDiff::Version::VERSION
24-
25-
# Exit codes
26-
EXITCODE_SUCCESS_NO_DIFFS = 0
27-
EXITCODE_FAILURE = 1
28-
EXITCODE_SUCCESS_WITH_DIFFS = 2
29-
30-
# The default type+title+attribute to ignore in catalog-diff.
31-
DEFAULT_IGNORES = [
32-
{ type: 'Class' } # Don't care about classes themselves, only what they actually do!
33-
].freeze
34-
35-
# The default options.
36-
DEFAULT_OPTIONS = {
37-
from_env: 'origin/master',
38-
to_env: '.',
39-
colors: true,
40-
debug: false,
41-
quiet: false,
42-
format: :color_text,
43-
display_source_file_line: false,
44-
compare_file_text: true,
45-
display_datatype_changes: true,
46-
parallel: true,
47-
suppress_absent_file_details: true,
48-
hiera_path: 'hieradata'
49-
}.freeze
50-
51-
# This method is the one to call externally. It is possible to specify alternate
52-
# command line arguments, for testing.
53-
# @param argv [Array] Use specified arguments (defaults to ARGV)
54-
# @param logger [Logger] Logger object
55-
# @param opts [Hash] Additional options
56-
# @return [Fixnum] Exit code: 0=no diffs, 1=something went wrong, 2=worked but there are diffs
57-
def self.cli(argv = ARGV, logger = Logger.new(STDERR), opts = {})
58-
# Save a copy of argv to print out later in debugging
59-
argv_save = argv.dup
60-
61-
# Are there additional ARGV to munge, e.g. that have been supplied in the options from a
62-
# configuration file?
63-
if opts.key?(:additional_argv)
64-
raise ArgumentError, ':additional_argv must be array!' unless opts[:additional_argv].is_a?(Array)
65-
argv.concat opts[:additional_argv]
66-
end
67-
68-
# Parse command line
69-
options = parse_opts(argv)
70-
71-
# Additional options from hard-coded specified options. These are only processed if
72-
# there are not already values defined from command line options.
73-
# Note: do NOT use 'options[k] ||= v' here because if the value of options[k] is boolean(false)
74-
# it will then be overridden. Whereas the intent is to define values only for those keys that don't exist.
75-
opts.each { |k, v| options[k] = v unless options.key?(k) }
76-
veto_options = %w(enc header hiera_config include_tags)
77-
veto_options.each { |x| options.delete(x.to_sym) if options["no_#{x}".to_sym] }
78-
options[:ignore].concat opts.fetch(:additional_ignores, [])
79-
80-
# Incorporate default options where needed.
81-
# Note: do NOT use 'options[k] ||= v' here because if the value of options[k] is boolean(false)
82-
# it will then be overridden. Whereas the intent is to define values only for those keys that don't exist.
83-
DEFAULT_OPTIONS.each { |k, v| options[k] = v unless options.key?(k) }
84-
veto_with_none_options = %w(hiera_path hiera_path_strip)
85-
veto_with_none_options.each { |x| options.delete(x.to_sym) if options[x.to_sym] == :none }
86-
87-
# Fact overrides come in here - 'options' is modified
88-
setup_fact_overrides(options)
89-
90-
# Configure the logger and logger.debug initial information
91-
# 'logger' is modified and used
92-
setup_logger(logger, options, argv_save)
93-
94-
# --catalog-only is a special case that compiles the catalog for the "to" branch
95-
# and then exits, without doing any 'diff' whatsoever. Support that option.
96-
return catalog_only(logger, options) if options[:catalog_only]
97-
98-
# Set up the cached master directory - maintain it, adjust options if needed. However, if we
99-
# are getting the 'from' catalog from PuppetDB, then don't do this.
100-
unless options[:cached_master_dir].nil? || options[:from_puppetdb]
101-
OctocatalogDiff::CatalogUtil::CachedMasterDirectory.run(options, logger)
102-
end
103-
104-
# bootstrap_then_exit is a special case that only prepares directories and does not
105-
# depend on facts. This happens within the 'catalogs' object, since bootstrapping and
106-
# preparing catalogs are tightly coupled operations. However this does not actually
107-
# build catalogs.
108-
if options[:bootstrap_then_exit]
109-
catalogs_obj = OctocatalogDiff::Util::Catalogs.new(options, logger)
110-
return bootstrap_then_exit(logger, catalogs_obj)
111-
end
112-
113-
# Compile catalogs and do catalog-diff
114-
catalog_diff = OctocatalogDiff::API::V1::CatalogDiff.catalog_diff(options.merge(logger: logger))
115-
116-
# Display diffs
117-
printer_obj = OctocatalogDiff::Cli::Printer.new(options, logger)
118-
printer_obj.printer(catalog_diff.diffs, catalog_diff.from.compilation_dir, catalog_diff.to.compilation_dir)
119-
120-
# Return the diff object if requested (generally for testing) or otherwise return exit code
121-
return catalog_diff.diffs if opts[:RETURN_DIFFS]
122-
catalog_diff.diffs.any? ? EXITCODE_SUCCESS_WITH_DIFFS : EXITCODE_SUCCESS_NO_DIFFS
18+
# This is the CLI for catalog-diff. It's responsible for parsing the command line
19+
# arguments and then handing off to appropriate methods to perform the catalog-diff.
20+
class Cli
21+
# Version number
22+
VERSION = OctocatalogDiff::Version::VERSION
23+
24+
# Exit codes
25+
EXITCODE_SUCCESS_NO_DIFFS = 0
26+
EXITCODE_FAILURE = 1
27+
EXITCODE_SUCCESS_WITH_DIFFS = 2
28+
29+
# The default type+title+attribute to ignore in catalog-diff.
30+
DEFAULT_IGNORES = [
31+
{ type: 'Class' } # Don't care about classes themselves, only what they actually do!
32+
].freeze
33+
34+
# The default options.
35+
DEFAULT_OPTIONS = {
36+
from_env: 'origin/master',
37+
to_env: '.',
38+
colors: true,
39+
debug: false,
40+
quiet: false,
41+
format: :color_text,
42+
display_source_file_line: false,
43+
compare_file_text: true,
44+
display_datatype_changes: true,
45+
parallel: true,
46+
suppress_absent_file_details: true,
47+
hiera_path: 'hieradata'
48+
}.freeze
49+
50+
# This method is the one to call externally. It is possible to specify alternate
51+
# command line arguments, for testing.
52+
# @param argv [Array] Use specified arguments (defaults to ARGV)
53+
# @param logger [Logger] Logger object
54+
# @param opts [Hash] Additional options
55+
# @return [Fixnum] Exit code: 0=no diffs, 1=something went wrong, 2=worked but there are diffs
56+
def self.cli(argv = ARGV, logger = Logger.new(STDERR), opts = {})
57+
# Save a copy of argv to print out later in debugging
58+
argv_save = argv.dup
59+
60+
# Are there additional ARGV to munge, e.g. that have been supplied in the options from a
61+
# configuration file?
62+
if opts.key?(:additional_argv)
63+
raise ArgumentError, ':additional_argv must be array!' unless opts[:additional_argv].is_a?(Array)
64+
argv.concat opts[:additional_argv]
12365
end
12466

125-
# Parse command line options with 'optparse'. Returns a hash with the parsed arguments.
126-
# @param argv [Array] Command line arguments (MUST be specified)
127-
# @return [Hash] Options
128-
def self.parse_opts(argv)
129-
options = { ignore: DEFAULT_IGNORES.dup }
130-
Options.parse_options(argv, options)
67+
# Parse command line
68+
options = parse_opts(argv)
69+
70+
# Additional options from hard-coded specified options. These are only processed if
71+
# there are not already values defined from command line options.
72+
# Note: do NOT use 'options[k] ||= v' here because if the value of options[k] is boolean(false)
73+
# it will then be overridden. Whereas the intent is to define values only for those keys that don't exist.
74+
opts.each { |k, v| options[k] = v unless options.key?(k) }
75+
veto_options = %w(enc header hiera_config include_tags)
76+
veto_options.each { |x| options.delete(x.to_sym) if options["no_#{x}".to_sym] }
77+
options[:ignore].concat opts.fetch(:additional_ignores, [])
78+
79+
# Incorporate default options where needed.
80+
# Note: do NOT use 'options[k] ||= v' here because if the value of options[k] is boolean(false)
81+
# it will then be overridden. Whereas the intent is to define values only for those keys that don't exist.
82+
DEFAULT_OPTIONS.each { |k, v| options[k] = v unless options.key?(k) }
83+
veto_with_none_options = %w(hiera_path hiera_path_strip)
84+
veto_with_none_options.each { |x| options.delete(x.to_sym) if options[x.to_sym] == :none }
85+
86+
# Fact overrides come in here - 'options' is modified
87+
setup_fact_overrides(options)
88+
89+
# Configure the logger and logger.debug initial information
90+
# 'logger' is modified and used
91+
setup_logger(logger, options, argv_save)
92+
93+
# --catalog-only is a special case that compiles the catalog for the "to" branch
94+
# and then exits, without doing any 'diff' whatsoever. Support that option.
95+
return catalog_only(logger, options) if options[:catalog_only]
96+
97+
# Set up the cached master directory - maintain it, adjust options if needed. However, if we
98+
# are getting the 'from' catalog from PuppetDB, then don't do this.
99+
unless options[:cached_master_dir].nil? || options[:from_puppetdb]
100+
OctocatalogDiff::CatalogUtil::CachedMasterDirectory.run(options, logger)
131101
end
132102

133-
# Fact overrides come in here
134-
def self.setup_fact_overrides(options)
135-
[:from_fact_override, :to_fact_override].each do |key|
136-
o = options["#{key}_in".to_sym]
137-
next unless o.is_a?(Array)
138-
next unless o.any?
139-
options[key] ||= []
140-
options[key].concat o.map { |x| OctocatalogDiff::Cli::Helpers::FactOverride.new(x) }
141-
end
103+
# bootstrap_then_exit is a special case that only prepares directories and does not
104+
# depend on facts. This happens within the 'catalogs' object, since bootstrapping and
105+
# preparing catalogs are tightly coupled operations. However this does not actually
106+
# build catalogs.
107+
if options[:bootstrap_then_exit]
108+
catalogs_obj = OctocatalogDiff::Util::Catalogs.new(options, logger)
109+
return bootstrap_then_exit(logger, catalogs_obj)
142110
end
143111

144-
# Helper method: Configure and setup logger
145-
def self.setup_logger(logger, options, argv_save)
146-
# Configure the logger
147-
logger.level = Logger::INFO
148-
logger.level = Logger::DEBUG if options[:debug]
149-
logger.level = Logger::ERROR if options[:quiet]
150-
151-
# Some debugging information up front
152-
version_display = ENV['OCTOCATALOG_DIFF_CUSTOM_VERSION'] || VERSION
153-
logger.debug "Running octocatalog-diff #{version_display} with ruby #{RUBY_VERSION}"
154-
logger.debug "Command line arguments: #{argv_save.inspect}"
155-
logger.debug "Running on host #{Socket.gethostname} (#{RUBY_PLATFORM})"
156-
end
112+
# Compile catalogs and do catalog-diff
113+
catalog_diff = OctocatalogDiff::API::V1::CatalogDiff.catalog_diff(options.merge(logger: logger))
114+
115+
# Display diffs
116+
printer_obj = OctocatalogDiff::Cli::Printer.new(options, logger)
117+
printer_obj.printer(catalog_diff.diffs, catalog_diff.from.compilation_dir, catalog_diff.to.compilation_dir)
118+
119+
# Return the diff object if requested (generally for testing) or otherwise return exit code
120+
return catalog_diff.diffs if opts[:RETURN_DIFFS]
121+
catalog_diff.diffs.any? ? EXITCODE_SUCCESS_WITH_DIFFS : EXITCODE_SUCCESS_NO_DIFFS
122+
end
123+
124+
# Parse command line options with 'optparse'. Returns a hash with the parsed arguments.
125+
# @param argv [Array] Command line arguments (MUST be specified)
126+
# @return [Hash] Options
127+
def self.parse_opts(argv)
128+
options = { ignore: DEFAULT_IGNORES.dup }
129+
Options.parse_options(argv, options)
130+
end
157131

158-
# Compile the catalog only
159-
def self.catalog_only(logger, options)
160-
opts = options.merge(logger: logger)
161-
to_catalog = OctocatalogDiff::API::V1::CatalogCompile.catalog(opts)
162-
163-
# If the catalog compilation failed, an exception would have been thrown. So if
164-
# we get here, the catalog succeeded. Dump the catalog to the appropriate place
165-
# and exit successfully.
166-
if options[:output_file]
167-
File.open(options[:output_file], 'w') { |f| f.write(to_catalog.catalog_json) }
168-
logger.info "Wrote catalog to #{options[:output_file]}"
169-
else
170-
puts to_catalog.catalog_json
171-
end
172-
return [OctocatalogDiff::Catalog.new(backend: :noop), to_catalog] if options[:RETURN_DIFFS] # For integration testing
173-
EXITCODE_SUCCESS_NO_DIFFS
132+
# Fact overrides come in here
133+
def self.setup_fact_overrides(options)
134+
[:from_fact_override, :to_fact_override].each do |key|
135+
o = options["#{key}_in".to_sym]
136+
next unless o.is_a?(Array)
137+
next unless o.any?
138+
options[key] ||= []
139+
options[key].concat o.map { |x| OctocatalogDiff::Cli::Helpers::FactOverride.new(x) }
174140
end
141+
end
142+
143+
# Helper method: Configure and setup logger
144+
def self.setup_logger(logger, options, argv_save)
145+
# Configure the logger
146+
logger.level = Logger::INFO
147+
logger.level = Logger::DEBUG if options[:debug]
148+
logger.level = Logger::ERROR if options[:quiet]
149+
150+
# Some debugging information up front
151+
version_display = ENV['OCTOCATALOG_DIFF_CUSTOM_VERSION'] || VERSION
152+
logger.debug "Running octocatalog-diff #{version_display} with ruby #{RUBY_VERSION}"
153+
logger.debug "Command line arguments: #{argv_save.inspect}"
154+
logger.debug "Running on host #{Socket.gethostname} (#{RUBY_PLATFORM})"
155+
end
175156

176-
# --bootstrap-then-exit command
177-
def self.bootstrap_then_exit(logger, catalogs_obj)
178-
catalogs_obj.bootstrap_then_exit
179-
return EXITCODE_SUCCESS_NO_DIFFS
180-
rescue OctocatalogDiff::Util::Catalogs::BootstrapError => exc
181-
logger.fatal("--bootstrap-then-exit error: bootstrap failed (#{exc})")
182-
return EXITCODE_FAILURE
157+
# Compile the catalog only
158+
def self.catalog_only(logger, options)
159+
opts = options.merge(logger: logger)
160+
to_catalog = OctocatalogDiff::API::V1::CatalogCompile.catalog(opts)
161+
162+
# If the catalog compilation failed, an exception would have been thrown. So if
163+
# we get here, the catalog succeeded. Dump the catalog to the appropriate place
164+
# and exit successfully.
165+
if options[:output_file]
166+
File.open(options[:output_file], 'w') { |f| f.write(to_catalog.catalog_json) }
167+
logger.info "Wrote catalog to #{options[:output_file]}"
168+
else
169+
puts to_catalog.catalog_json
183170
end
171+
return [OctocatalogDiff::Catalog.new(backend: :noop), to_catalog] if options[:RETURN_DIFFS] # For integration testing
172+
EXITCODE_SUCCESS_NO_DIFFS
173+
end
174+
175+
# --bootstrap-then-exit command
176+
def self.bootstrap_then_exit(logger, catalogs_obj)
177+
catalogs_obj.bootstrap_then_exit
178+
return EXITCODE_SUCCESS_NO_DIFFS
179+
rescue OctocatalogDiff::Util::Catalogs::BootstrapError => exc
180+
logger.fatal("--bootstrap-then-exit error: bootstrap failed (#{exc})")
181+
return EXITCODE_FAILURE
184182
end
185183
end
186184
end

0 commit comments

Comments
 (0)