Lazily load secrets whenever needed

This commit is contained in:
Donal McBreen
2024-08-05 14:41:50 +01:00
committed by Donal McBreen
parent 6a06efc9d9
commit 56754fe40c
43 changed files with 391 additions and 529 deletions

View File

@@ -16,7 +16,7 @@ class Kamal::Configuration::Accessory
@env = Kamal::Configuration::Env.new \
config: accessory_config.fetch("env", {}),
secrets_file: File.join(config.host_env_directory, "accessories", "#{service_name}.env"),
secrets: config.secrets,
context: "accessories/#{name}/env"
end

View File

@@ -62,7 +62,7 @@ class Kamal::Configuration::Builder
end
def secrets
builder_config["secrets"] || []
(builder_config["secrets"] || []).to_h { |key| [ key, config.secrets[key] ] }
end
def dockerfile

View File

@@ -1,36 +1,34 @@
class Kamal::Configuration::Env
include Kamal::Configuration::Validation
attr_reader :secrets_keys, :clear, :secrets_file, :context
attr_reader :context, :secrets
attr_reader :clear, :secret_keys
delegate :argumentize, to: Kamal::Utils
def initialize(config:, secrets_file: nil, context: "env")
def initialize(config:, secrets:, context: "env")
@clear = config.fetch("clear", config.key?("secret") || config.key?("tags") ? {} : config)
@secrets_keys = config.fetch("secret", [])
@secrets_file = secrets_file
@secrets = secrets
@secret_keys = config.fetch("secret", [])
@context = context
validate! config, context: context, with: Kamal::Configuration::Validator::Env
end
def args
[ "--env-file", secrets_file, *argumentize("--env", clear) ]
end
def secrets_io
StringIO.new(Kamal::EnvFile.new(secrets).to_s)
end
def secrets
@secrets ||= secrets_keys.to_h { |key| [ key, ENV.fetch(key) ] }
end
def secrets_directory
File.dirname(secrets_file)
[ *clear_args, *secret_args ]
end
def merge(other)
self.class.new \
config: { "clear" => clear.merge(other.clear), "secret" => secrets_keys | other.secrets_keys },
secrets_file: secrets_file || other.secrets_file
config: { "clear" => clear.merge(other.clear), "secret" => secret_keys | other.secret_keys },
secrets: secrets
end
private
def clear_args
argumentize("--env", clear)
end
def secret_args
argumentize("--env", secret_keys.to_h { |key| [ key, secrets[key] ] }, sensitive: true)
end
end

View File

@@ -1,12 +1,13 @@
class Kamal::Configuration::Env::Tag
attr_reader :name, :config
attr_reader :name, :config, :secrets
def initialize(name, config:)
def initialize(name, config:, secrets:)
@name = name
@config = config
@secrets = secrets
end
def env
Kamal::Configuration::Env.new(config: config)
Kamal::Configuration::Env.new(config: config, secrets: secrets)
end
end

View File

@@ -1,10 +1,11 @@
class Kamal::Configuration::Registry
include Kamal::Configuration::Validation
attr_reader :registry_config
attr_reader :registry_config, :secrets
def initialize(config:)
@registry_config = config.raw_config.registry || {}
@secrets = config.secrets
validate! registry_config, with: Kamal::Configuration::Validator::Registry
end
@@ -23,7 +24,7 @@ class Kamal::Configuration::Registry
private
def lookup(key)
if registry_config[key].is_a?(Array)
ENV.fetch(registry_config[key].first).dup
secrets[registry_config[key].first]
else
registry_config[key]
end

View File

@@ -18,7 +18,7 @@ class Kamal::Configuration::Role
@specialized_env = Kamal::Configuration::Env.new \
config: specializations.fetch("env", {}),
secrets_file: File.join(config.host_env_directory, "roles", "#{container_prefix}.env"),
secrets: config.secrets,
context: "servers/#{name}/env"
@specialized_logging = Kamal::Configuration::Logging.new \

View File

@@ -0,0 +1,25 @@
class Kamal::Configuration::Secrets
attr_reader :secret_files
def initialize(destination: nil)
@secret_files = \
(destination ? [ ".kamal/secrets", ".kamal/secrets.#{destination}" ] : [ ".kamal/secrets" ])
.select { |file| File.exist?(file) }
end
def [](key)
@secrets ||= load
@secrets.fetch(key)
rescue KeyError
if secret_files.any?
raise Kamal::ConfigurationError, "Secret '#{key}' not found in #{secret_files.join(', ')}"
else
raise Kamal::ConfigurationError, "Secret '#{key}' not found, no secret files provided"
end
end
private
def load
secret_files.any? ? Dotenv.parse(*secret_files) : {}
end
end

View File

@@ -34,7 +34,7 @@ class Kamal::Configuration::Traefik
def env
Kamal::Configuration::Env.new \
config: traefik_config.fetch("env", {}),
secrets_file: File.join(config.host_env_directory, "traefik", "traefik.env"),
secrets: config.secrets,
context: "traefik/env"
end