lib/contrast/configuration.rb in contrast-agent-3.14.0 vs lib/contrast/configuration.rb in contrast-agent-3.15.0
- old
+ new
@@ -22,12 +22,10 @@
def_delegator :root, :assign_value_to_path_array
attr_reader :default_name, :root
DEFAULT_YAML_PATH = 'contrast_security.yaml'
- DEFAULT_HOST = '127.0.0.1'
- DEFAULT_PORT = '30555'
MILLISECOND_MARKER = '_ms'
CONVERSION = {
'agent.service.enable' => 'agent.start_bundled_service'
}.cs__freeze
CONFIG_BASE_PATHS = [
@@ -35,13 +33,10 @@
'config/',
'/etc/contrast/ruby/',
'/etc/contrast/',
'/etc/'
].cs__freeze
- REMOVE_FIELDS = [
- 'contrast'
- ].cs__freeze
def initialize cli_options = nil, default_name = DEFAULT_YAML_PATH
@default_name = default_name
# Load config_kv from file
@@ -51,11 +46,10 @@
cli_options = deep_stringify_all_keys(cli_options)
config_kv = deep_merge(cli_options, config_kv) if cli_options
# Some in-flight rewrites to maintain backwards compatibility
config_kv = update_prop_keys(config_kv)
- config_kv = deprecate_fields(config_kv)
@root = Contrast::Config::RootConfiguration.new(config_kv)
end
# Because we call this method to determine the need for scoping, it itself
@@ -71,82 +65,71 @@
def respond_to_missing? method_name, *args
root&.cs__respond_to?(method_name) || super
end
+ # Get a loggable YAML format of this configuration
+ # @return [String] the current active configuration of the Agent,
+ # represented as a YAML string
+ def loggable
+ convert_to_hash.to_yaml
+ end
+
protected
# TODO: RUBY-546 move utility methods to auxiliary classes
def load_config
config = {}
configuration_paths.find do |path|
- found = File.exist?(path)
- next unless found
+ next unless File.exist?(path)
- readable = File.readable?(path)
- unless readable
- puts "!!! Contrast - Configuration file at #{ path } is not readable by current user"
+ unless File.readable?(path)
+ log_file_read_error(path)
next
end
config = yaml_to_hash(path) || {}
break
end
- if config.empty?
- puts "!!! Contrast - working directory: #{ Dir.pwd }"
- puts '!!! Contrast - valid configuration file could not be found at any of the search paths'
- puts 'Valid configuration paths are: '
- configuration_paths.each do |path|
- puts(path)
- end
- end
config
end
def yaml_to_hash path
if path && File.readable?(path)
begin
yaml = IO.read(path)
yaml = ERB.new(yaml).result if defined?(ERB)
return YAML.safe_load(yaml)
+ rescue Psych::Exception => e
+ log_yaml_parse_error(path, e)
rescue RuntimeError => e
- puts "ERROR: unable to load configuration from path due to #{ e }"
- puts "ERROR: path=#{ path } pwd=#{ Dir.pwd }"
+ puts "WARN: Unable to load configuration. #{ e }; path: #{ path }, pwd: #{ Dir.pwd }"
end
end
{}
end
# We're updating properties loaded from the configuration
# files to match the new agreed upon standard configuration
# names, so that one file works for all agents
def update_prop_keys config
- converted = false
CONVERSION.each_pair do |old_method, new_method|
# See if the old value was set and needs to be translated
deprecated_keys = old_method.split('.')
-
old_value = config
deprecated_keys.each do |key|
old_value = old_value[key]
break if old_value.nil?
end
+ next if old_value.nil? # have to account for literal false
- next if old_value.nil?
-
- converted = true
-
- puts "The deprecated property #{ old_method } is being set."
- puts "Please update your config to use the property #{ new_method } instead."
-
+ log_deprecated_property(old_method, new_method)
new_keys = new_method.split('.')
-
# We changed the seconds values into ms values. Multiply them accordingly
old_value = old_value.to_i * 1000 if new_method.end_with?(MILLISECOND_MARKER)
-
new_value = config
end_idx = new_keys.length - 1
new_keys.each_with_index do |new_key, index|
if index == end_idx
new_value[new_key] = old_value if new_value[new_key].nil?
@@ -159,25 +142,10 @@
end
config
end
- def deprecate_fields hash
- REMOVE_FIELDS.each do |field|
- path = field.split('.')
- active_path = hash
- path.each_with_index do |delete_path, index|
- if index == path.length - 1 && active_path
- active_path.delete(delete_path)
- elsif active_path
- active_path = active_path[delete_path]
- end
- end
- end
- hash
- end
-
# Base paths to check for the contrast configuration file, sorted by
# reverse order of precedence (first is most important).
def configuration_paths
@_configuration_paths ||= begin
basename = default_name.split('.').first
@@ -205,8 +173,81 @@
new_hash = {}
hash.each do |key, value|
new_hash[key.to_s] = value.is_a?(Hash) ? deep_stringify_all_keys(value) : value
end
new_hash
+ end
+
+ private
+
+ # We cannot use all access components at this point, unfortunately, as they
+ # may not have been initialized. Instead, we need to access the logger
+ # directly.
+ def logger
+ @_logger ||= (Contrast::Logger::Log.instance.logger if defined?(Contrast::Logger::Log))
+ end
+
+ # When we fail to parse a configuration because it is misformatted, log an
+ # appropriate message based on the Agent Onboarding specification
+ def log_yaml_parse_error path, exception
+ hash = {
+ path: path,
+ pwd: Dir.pwd
+ }
+ if exception.is_a?(Psych::SyntaxError)
+ hash[:context] = exception.context
+ hash[:column] = exception.column
+ hash[:line] = exception.line
+ hash[:offset] = exception.offset
+ hash[:problem] = exception.problem
+ end
+
+ if logger
+ logger.warn('YAML validator found an error', hash)
+ else
+ puts "CONTRAST - WARN: YAML validator found an error. #{ hash.inspect }"
+ end
+ end
+
+ def log_file_read_error path
+ if logger
+ logger.warn('Configuration file is not readable by current user', path: path)
+ else
+ puts "CONTRAST - WARN: Configuration file is not readable by current user; path: #{ path }"
+ end
+ end
+
+ def log_deprecated_property old_method, new_method
+ if logger
+ logger.warn('Deprecated property in use', old_method: old_method, new_method: new_method)
+ else
+ puts "CONTRAST - WARN: Deprecated property in use; old_method: #{ old_method }, new_method: #{ new_method }"
+ end
+ end
+
+ # Convert this entire configuration into a hash, walking down the entries
+ # in the thing to convert and setting them in the given hash. For now, this
+ # logs every possible key, whether set or not. If we want to change that
+ # behavior, we can skip adding keys to the hash if the value is nil, blank,
+ # or Contrast::Config::DefaultValue depending on desired behavior
+ #
+ # @param hash [Hash] the hash to populate
+ # @param convert [Contrast::Config::BaseConfiguration, Object] the level of
+ # configuration from which to convert. Note that at least one top level
+ # Contrast::Config::BaseConfiguration is required for anything to be set
+ # in the hash
+ # @return [Hash, Object] the leaf of each
+ # Contrast::Config::BaseConfiguration will be returned in the N > 0 steps
+ # the Hash will be returned at the end of the 0 level
+ def convert_to_hash convert=root, hash={}
+ case convert
+ when Contrast::Config::BaseConfiguration
+ convert.cs__class::KEYS.each_key do |key|
+ hash[key] = convert_to_hash(convert.send(key), {})
+ end
+ hash
+ else
+ convert
+ end
end
end
end