Module: Legion::Settings::Resolver
- Extended by:
- Logging::Helper
- Defined in:
- lib/legion/settings/resolver.rb
Constant Summary collapse
- VAULT_PATTERN =
%r{\Avault://(.+?)#(.+)\z}- ENV_PATTERN =
%r{\Aenv://(.+)\z}- LEASE_PATTERN =
%r{\Alease://(.+?)#(.+)\z}- URI_PATTERN =
%r{\A(?:vault|env|lease)://}
Class Method Summary collapse
- .count_lease_refs(hash) ⇒ Object
- .count_vault_refs(hash) ⇒ Object
- .handle_array_value(container, key, value, current_path) ⇒ Object
- .handle_string_value(container, key, value, current_path) ⇒ Object
-
.has_vault_refs?(hash) ⇒ Boolean
rubocop:disable Naming/PredicatePrefix.
- .lease_manager_available? ⇒ Boolean
- .register_lease_ref(value, path_string) ⇒ Object
- .register_lease_refs_from_chain(arr, path_string) ⇒ Object
- .resolvable_chain?(arr) ⇒ Boolean
- .resolve_chain(arr) ⇒ Object
- .resolve_lease(name, key) ⇒ Object
- .resolve_logger_settings ⇒ Object
- .resolve_secrets!(settings_hash) ⇒ Object
- .resolve_single(str) ⇒ Object
- .resolve_value(value) ⇒ Object
- .resolve_vault(path, key) ⇒ Object
- .vault_connected? ⇒ Boolean
- .walk(hash, path:) ⇒ Object
- .walk_value(container, key, value, current_path) ⇒ Object
Class Method Details
.count_lease_refs(hash) ⇒ Object
232 233 234 235 236 237 238 239 |
# File 'lib/legion/settings/resolver.rb', line 232 def count_lease_refs(hash) case hash when String then hash.match?(LEASE_PATTERN) ? 1 : 0 when Array then hash.sum { |value| count_lease_refs(value) } when Hash then hash.sum { |_key, value| count_lease_refs(value) } else 0 end end |
.count_vault_refs(hash) ⇒ Object
87 88 89 90 91 92 93 94 |
# File 'lib/legion/settings/resolver.rb', line 87 def count_vault_refs(hash) case hash when String then hash.match?(VAULT_PATTERN) ? 1 : 0 when Array then hash.sum { |value| count_vault_refs(value) } when Hash then hash.sum { |_key, value| count_vault_refs(value) } else 0 end end |
.handle_array_value(container, key, value, current_path) ⇒ Object
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/legion/settings/resolver.rb', line 148 def handle_array_value(container, key, value, current_path, &) if resolvable_chain?(value) && value.all? { |entry| !entry.is_a?(Hash) && !entry.is_a?(Array) } resolved = resolve_chain(value) if resolved.nil? log.warn("Settings resolver: fallback chain exhausted for #{current_path}") yield(:unresolved) if block_given? else container[key] = resolved register_lease_refs_from_chain(value, current_path) yield(:resolved) if block_given? end else walk(value, path: current_path, &) end end |
.handle_string_value(container, key, value, current_path) ⇒ Object
134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/legion/settings/resolver.rb', line 134 def handle_string_value(container, key, value, current_path) return unless value.match?(URI_PATTERN) resolved = resolve_single(value) if resolved.nil? log.warn("Settings resolver: could not resolve #{current_path} (#{value})") yield(:unresolved) if block_given? else container[key] = resolved register_lease_ref(value, current_path) if value.match?(LEASE_PATTERN) yield(:resolved) if block_given? end end |
.has_vault_refs?(hash) ⇒ Boolean
rubocop:disable Naming/PredicatePrefix
83 84 85 |
# File 'lib/legion/settings/resolver.rb', line 83 def has_vault_refs?(hash) # rubocop:disable Naming/PredicatePrefix count_vault_refs(hash).positive? end |
.lease_manager_available? ⇒ Boolean
198 199 200 201 202 203 |
# File 'lib/legion/settings/resolver.rb', line 198 def lease_manager_available? defined?(Legion::Crypt::LeaseManager) rescue StandardError => e log.debug("Legion::Settings::Resolver#lease_manager_available? failed: #{e.}") false end |
.register_lease_ref(value, path_string) ⇒ Object
209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/legion/settings/resolver.rb', line 209 def register_lease_ref(value, path_string) return unless lease_manager_available? m = value.match(LEASE_PATTERN) return unless m path_parts = path_string.split('.').map(&:to_sym) Legion::Crypt::LeaseManager.instance.register_ref(m[1], m[2], path_parts) rescue StandardError => e log.debug("Legion::Settings::Resolver#register_lease_ref failed for #{path_string}: #{e.}") nil end |
.register_lease_refs_from_chain(arr, path_string) ⇒ Object
222 223 224 225 226 227 228 229 230 |
# File 'lib/legion/settings/resolver.rb', line 222 def register_lease_refs_from_chain(arr, path_string) return unless lease_manager_available? arr.each do |entry| next unless entry.is_a?(String) register_lease_ref(entry, path_string) if entry.match?(LEASE_PATTERN) end end |
.resolvable_chain?(arr) ⇒ Boolean
205 206 207 |
# File 'lib/legion/settings/resolver.rb', line 205 def resolvable_chain?(arr) arr.any? { |v| v.is_a?(String) && v.match?(URI_PATTERN) } end |
.resolve_chain(arr) ⇒ Object
71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/legion/settings/resolver.rb', line 71 def resolve_chain(arr) arr.each do |entry| result = if entry.is_a?(String) && entry.match?(URI_PATTERN) resolve_single(entry) else entry end return result unless result.nil? end nil end |
.resolve_lease(name, key) ⇒ Object
189 190 191 192 193 194 195 196 |
# File 'lib/legion/settings/resolver.rb', line 189 def resolve_lease(name, key) return nil unless lease_manager_available? Legion::Crypt::LeaseManager.instance.fetch(name, key) rescue StandardError => e log.debug("Settings resolver: lease fetch failed for #{name}##{key}: #{e.}") nil end |
.resolve_logger_settings ⇒ Object
241 242 243 244 |
# File 'lib/legion/settings/resolver.rb', line 241 def resolve_logger_settings raw_logging = Legion::Settings.loader&.settings&.dig(:logging) if Legion::Settings.respond_to?(:loader) raw_logging.is_a?(Hash) ? raw_logging : Legion::Logging::Settings.default end |
.resolve_secrets!(settings_hash) ⇒ Object
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/legion/settings/resolver.rb', line 17 def resolve_secrets!(settings_hash) return settings_hash unless settings_hash.is_a?(Hash) @vault_available = vault_connected? @vault_cache = {} vault_count = count_vault_refs(settings_hash) log.warn("Vault not connected — #{vault_count} vault:// reference(s) will not be resolved") if vault_count.positive? && !@vault_available lease_count = count_lease_refs(settings_hash) log.warn("LeaseManager not available — #{lease_count} lease:// reference(s) will not be resolved") if lease_count.positive? && !lease_manager_available? resolved = 0 unresolved = 0 walk(settings_hash, path: '') do |result| if result == :resolved resolved += 1 elsif result == :unresolved unresolved += 1 end end log.info("Settings resolver: #{resolved} resolved, #{unresolved} unresolved") if resolved.positive? || unresolved.positive? settings_hash end |
.resolve_single(str) ⇒ Object
59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/legion/settings/resolver.rb', line 59 def resolve_single(str) if (m = str.match(VAULT_PATTERN)) resolve_vault(m[1], m[2]) elsif (m = str.match(LEASE_PATTERN)) resolve_lease(m[1], m[2]) elsif (m = str.match(ENV_PATTERN)) ENV.fetch(m[1], nil) else str end end |
.resolve_value(value) ⇒ Object
44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/legion/settings/resolver.rb', line 44 def resolve_value(value) case value when String return value unless value.match?(URI_PATTERN) resolve_single(value) when Array return value unless resolvable_chain?(value) resolve_chain(value) else value end end |
.resolve_vault(path, key) ⇒ Object
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/legion/settings/resolver.rb', line 164 def resolve_vault(path, key) log.debug("resolve_vault: path=#{path}, key=#{key}, vault_available=#{@vault_available}") return nil unless @vault_available @vault_cache[path] ||= begin log.debug("resolve_vault: calling Legion::Crypt.read(#{path.inspect})") result = Legion::Crypt.read(path) log.debug("resolve_vault: read returned #{result.nil? ? 'nil' : "keys=#{result.keys.inspect}"}") result rescue StandardError => e log.warn("Settings resolver: vault read failed for #{path}: #{e.class}=#{e.}") nil end data = @vault_cache[path] unless data.is_a?(Hash) log.debug("resolve_vault: data at #{path} is #{data.class}, returning nil") return nil end value = data[key.to_sym] || data[key.to_s] log.debug("resolve_vault: #{path}##{key} = #{value.nil? ? 'nil' : '<present>'}") value end |
.vault_connected? ⇒ Boolean
96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/legion/settings/resolver.rb', line 96 def vault_connected? return false unless defined?(Legion::Crypt) return false unless defined?(Legion::Settings) return Legion::Crypt.vault_connected? if Legion::Crypt.respond_to?(:vault_connected?) Legion::Settings[:crypt][:vault][:connected] == true || (Legion::Crypt.respond_to?(:connected_clusters) && Legion::Crypt.connected_clusters.any?) rescue StandardError => e log.debug("Legion::Settings::Resolver#vault_connected? failed: #{e.}") false end |
.walk(hash, path:) ⇒ Object
108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/legion/settings/resolver.rb', line 108 def walk(hash, path:, &) case hash when Hash hash.each do |key, value| current_path = path.empty? ? key.to_s : "#{path}.#{key}" walk_value(hash, key, value, current_path, &) end when Array hash.each_with_index do |value, index| current_path = "#{path}[#{index}]" walk_value(hash, index, value, current_path, &) end end end |
.walk_value(container, key, value, current_path) ⇒ Object
123 124 125 126 127 128 129 130 131 132 |
# File 'lib/legion/settings/resolver.rb', line 123 def walk_value(container, key, value, current_path, &) case value when Hash walk(value, path: current_path, &) when String handle_string_value(container, key, value, current_path) { |status| yield(status) if block_given? } when Array handle_array_value(container, key, value, current_path) { |status| yield(status) if block_given? } end end |