Class: CemAcpt::Config::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/cem_acpt/config/base.rb

Overview

Base class for other config classes Child classes should provide the following constant:

- VALID_KEYS - provide an array of valid top-level keys for the config as symbols

Child classes should implement the following methods:

- defaults - provide a hash of default values for the config
- env_var_prefix - provide a string to prefix environment variables with (defaults to 'CEM_ACPT').
                   This will be converted to uppercase, have all non-alphanumeric characters replaced with
                   underscores, and be joined with the key name with an underscore to form the environment
                   variable name.

However, they can override any of the methods in this class.

Direct Known Subclasses

CemAcpt, CemAcptImage, CemAcptScan

Constant Summary collapse

BASE_VALID_KEYS =
%i[
  ci_mode
  config_file
  log_level
  log_file
  log_format
  no_destroy_nodes
  no_ephemeral_ssh_key
  platform
  puppet
  quiet
  secrets
  terraform
  user_config
  verbose
].freeze
DEEP_MERGE_OPTS =
{
  overwrite_arrays: true,
  merge_nil_values: true,
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts: {}, config_file: nil, load_user_config: true) ⇒ Base

Returns a new instance of Base.



84
85
86
87
88
# File 'lib/cem_acpt/config/base.rb', line 84

def initialize(opts: {}, config_file: nil, load_user_config: true)
  @load_user_config = load_user_config
  @explanation = {}
  load(opts: opts, config_file: config_file)
end

Instance Attribute Details

#configObject Also known as: to_h

Returns the value of attribute config.



82
83
84
# File 'lib/cem_acpt/config/base.rb', line 82

def config
  @config
end

#env_varsObject (readonly)

Returns the value of attribute env_vars.



82
83
84
# File 'lib/cem_acpt/config/base.rb', line 82

def env_vars
  @env_vars
end

Class Method Details

.inherited(subclass) ⇒ Object



68
69
70
# File 'lib/cem_acpt/config/base.rb', line 68

def self.inherited(subclass)
  subclass.instance_variable_set(:@load_hook, nil)
end

.load_hook(&block) ⇒ Object

Add a block to be called after the config is loaded but before it is validated and frozen. The block will be passed the config hash and can be used to modify the config before it is finalized.



74
75
76
77
78
79
80
# File 'lib/cem_acpt/config/base.rb', line 74

def self.load_hook(&block)
  if block_given?
    @load_hook = block
  else
    @load_hook
  end
end

Instance Method Details

#[](key) ⇒ Object



164
165
166
167
168
169
170
171
172
# File 'lib/cem_acpt/config/base.rb', line 164

def [](key)
  if key.is_a?(Symbol)
    @config[key].dup
  elsif key.is_a?(String)
    get(key)
  else
    raise ArgumentError, "Invalid key type '#{key.class}'"
  end
end

#ci_mode?Boolean Also known as: ci?

Returns:

  • (Boolean)


187
188
189
# File 'lib/cem_acpt/config/base.rb', line 187

def ci_mode?
  !!get('ci_mode') || !!(ENV['GITHUB_ACTIONS'] || ENV['CI'])
end

#debug_mode?Boolean Also known as: debug?

Returns:

  • (Boolean)


202
203
204
# File 'lib/cem_acpt/config/base.rb', line 202

def debug_mode?
  get('log_level') == 'debug'
end

#defaultsHash

Returns The default configuration.

Returns:

  • (Hash)

    The default configuration



109
110
111
# File 'lib/cem_acpt/config/base.rb', line 109

def defaults
  {}
end

#empty?Boolean

Returns:

  • (Boolean)


183
184
185
# File 'lib/cem_acpt/config/base.rb', line 183

def empty?
  @config.empty?
end

#env_var_prefixString

Returns The prefix for environment variables.

Returns:

  • (String)

    The prefix for environment variables



95
96
97
# File 'lib/cem_acpt/config/base.rb', line 95

def env_var_prefix
  'CEM_ACPT'
end

#explainObject

Returns a string representation of how the config was loaded



158
159
160
161
162
# File 'lib/cem_acpt/config/base.rb', line 158

def explain
  @explanation.each_with_object([]) { |(key, values), ary|
    ary << "#{key}:\n  -->#{values.join("\n  -->")}"
  }.join("\n")
end

#get(dot_key) ⇒ Object Also known as: dget



174
175
176
# File 'lib/cem_acpt/config/base.rb', line 174

def get(dot_key)
  @dot_key_cache[dot_key] ||= @config.dget(dot_key).dup
end

#has?(dot_key) ⇒ Boolean

Returns:

  • (Boolean)


179
180
181
# File 'lib/cem_acpt/config/base.rb', line 179

def has?(dot_key)
  !!get(dot_key)
end

#inspectObject



90
91
92
# File 'lib/cem_acpt/config/base.rb', line 90

def inspect
  "#<#{self.class}:#{object_id.to_s(16)}>"
end

#load(opts: {}, config_file: nil) ⇒ self

Load the configuration from the environment variables, config file, and opts The order of precedence is:

1. static options (set in this class)
2. Runtime options
3. Runtime config file
4. User config file
5. Environment variables
6. Defaults

Parameters:

  • opts (Hash) (defaults to: {})

    The options to load

  • config_file (String) (defaults to: nil)

    The config file to load

Returns:

  • (self)

    This object with the config loaded



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/cem_acpt/config/base.rb', line 124

def load(opts: {}, config_file: nil)
  create_config_dirs!
  init_config!(opts: opts, config_file: config_file)
  add_env_vars!(@config)
  @config.deep_merge!(user_config, **DEEP_MERGE_OPTS) if user_config && @load_user_config
  @config.deep_merge!(config_from_file, **DEEP_MERGE_OPTS) if config_from_file
  if @options
    @config.deep_merge!(@options, **DEEP_MERGE_OPTS)
    @options.each do |key, _value|
      add_config_explanation(key, "runtime option")
    end
  end
  add_static_options!(@config)
  # Run the load hook if it is defined. This allows child classes to modify the config after it has been loaded
  # but before it is validated and frozen.
  block = self.class.load_hook
  instance_eval(&block) if block
  @config.format! # Symbolize keys of all hashes
  # Remove any keys that are not in the valid keys list for this config. This prevents invalid config options
  # from being set.
  @config.select! { |key, _| valid_keys.include?(key) }
  # Wrap secrets in the config with the Secret class to prevent them from being accidentally printed in logs
  # or error messages. WARNING: Secrets can leak from Terraform logging.
  wrap_secrets!
  validate_config!
  @dot_key_cache = {}
  # Freeze the config so it can't be modified
  # This helps with thread safety and deterministic behavior
  @config.freeze
  self
end

#quiet_mode?Boolean Also known as: quiet?

Returns:

  • (Boolean)


212
213
214
# File 'lib/cem_acpt/config/base.rb', line 212

def quiet_mode?
  !!get('quiet')
end

#scan?Boolean

Returns:

  • (Boolean)


198
199
200
# File 'lib/cem_acpt/config/base.rb', line 198

def scan?
  scan_mode?
end

#scan_mode?Boolean

Whether this config represents a scan run (cem_acpt_scan). Subclasses that drive a scan workflow override to true.

Returns:

  • (Boolean)


194
195
196
# File 'lib/cem_acpt/config/base.rb', line 194

def scan_mode?
  false
end

#to_json(*args, expose_secrets: false) ⇒ Object



224
225
226
227
228
229
# File 'lib/cem_acpt/config/base.rb', line 224

def to_json(*args, expose_secrets: false)
  return @config.to_json(*args) unless @config.key?(:secrets)

  serializable_secrets = @config[:secrets].transform_values { |v| v.is_a?(Secret) ? (expose_secrets ? v.value : v.to_s) : v }
  @config.merge(secrets: serializable_secrets).to_json(*args)
end

#to_yaml(expose_secrets: false) ⇒ Object



217
218
219
220
221
222
# File 'lib/cem_acpt/config/base.rb', line 217

def to_yaml(expose_secrets: false)
  return @config.to_yaml unless @config.key?(:secrets)

  serializable_secrets = @config[:secrets].transform_values { |v| v.is_a?(Secret) ? (expose_secrets ? v.value : v.to_s) : v }
  @config.merge(secrets: serializable_secrets).to_yaml
end

#valid_keysArray<Symbol>

Returns Valid top-level keys for the config.

Returns:

  • (Array<Symbol>)

    Valid top-level keys for the config



100
101
102
103
104
105
106
# File 'lib/cem_acpt/config/base.rb', line 100

def valid_keys
  if self.class.const_defined?(:VALID_KEYS)
    (BASE_VALID_KEYS + self.class.const_get(:VALID_KEYS)).uniq
  else
    BASE_VALID_KEYS
  end
end

#verbose_mode?Boolean Also known as: verbose?

Returns:

  • (Boolean)


207
208
209
# File 'lib/cem_acpt/config/base.rb', line 207

def verbose_mode?
  !!get('verbose')
end