Split env into separate secrets/clear envs

Split each env file in two on the deploy hosts, one for secrets and
one for clear values. This will allow us to update them independently.
This commit is contained in:
Donal McBreen
2024-03-05 15:49:55 +00:00
parent 6563393d9a
commit 1fa25200cc
20 changed files with 241 additions and 165 deletions

View File

@@ -1,7 +1,7 @@
require "tempfile"
class Kamal::Cli::Env < Kamal::Cli::Base
desc "push", "Push the env file to the remote hosts"
desc "push", "Push the env files to the remote hosts"
def push
mutating do
on(KAMAL.hosts) do
@@ -10,26 +10,29 @@ class Kamal::Cli::Env < Kamal::Cli::Base
KAMAL.roles_on(host).each do |role|
role_config = KAMAL.config.role(role)
execute *KAMAL.app(role: role).make_env_directory
upload! StringIO.new(role_config.env_file), role_config.host_env_file_path, mode: 400
upload! StringIO.new(role_config.env_file.secret), role_config.host_secret_env_file_path, mode: 400
upload! StringIO.new(role_config.env_file.clear), role_config.host_clear_env_file_path, mode: 400
end
end
on(KAMAL.traefik_hosts) do
execute *KAMAL.traefik.make_env_directory
upload! StringIO.new(KAMAL.traefik.env_file), KAMAL.traefik.host_env_file_path, mode: 400
upload! StringIO.new(KAMAL.traefik.env_file.secret), KAMAL.traefik.host_secret_env_file_path, mode: 400
upload! StringIO.new(KAMAL.traefik.env_file.clear), KAMAL.traefik.host_clear_env_file_path, mode: 400
end
on(KAMAL.accessory_hosts) do
KAMAL.accessories_on(host).each do |accessory|
accessory_config = KAMAL.config.accessory(accessory)
execute *KAMAL.accessory(accessory).make_env_directory
upload! StringIO.new(accessory_config.env_file), accessory_config.host_env_file_path, mode: 400
upload! StringIO.new(accessory_config.env_file.secret), accessory_config.host_secret_env_file_path, mode: 400
upload! StringIO.new(accessory_config.env_file.clear), accessory_config.host_clear_env_file_path, mode: 400
end
end
end
end
desc "delete", "Delete the env file from the remote hosts"
desc "delete", "Delete the env files from the remote hosts"
def delete
mutating do
on(KAMAL.hosts) do
@@ -37,18 +40,18 @@ class Kamal::Cli::Env < Kamal::Cli::Base
KAMAL.roles_on(host).each do |role|
role_config = KAMAL.config.role(role)
execute *KAMAL.app(role: role).remove_env_file
execute *KAMAL.app(role: role).remove_env_files
end
end
on(KAMAL.traefik_hosts) do
execute *KAMAL.traefik.remove_env_file
execute *KAMAL.traefik.remove_env_files
end
on(KAMAL.accessory_hosts) do
KAMAL.accessories_on(host).each do |accessory|
accessory_config = KAMAL.config.accessory(accessory)
execute *KAMAL.accessory(accessory).remove_env_file
execute *KAMAL.accessory(accessory).remove_env_files
end
end
end

View File

@@ -102,8 +102,8 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base
make_directory accessory_config.host_env_directory
end
def remove_env_file
[:rm, "-f", accessory_config.host_env_file_path]
def remove_env_files
[:rm, "-f", File.join(accessory_config.host_env_directory, "#{accessory_config.service_name}*.env")]
end
private

View File

@@ -72,8 +72,8 @@ class Kamal::Commands::App < Kamal::Commands::Base
make_directory role_config.host_env_directory
end
def remove_env_file
[ :rm, "-f", role_config.host_env_file_path ]
def remove_env_files
[ :rm, "-f", File.join(role_config.host_env_directory, "#{role_config.container_prefix}*.env") ]
end

View File

@@ -72,19 +72,23 @@ class Kamal::Commands::Traefik < Kamal::Commands::Base
end
def env_file
Kamal::EnvFile.new(config.traefik.fetch("env", {}))
Kamal::EnvFiles.new(config.traefik.fetch("env", {}))
end
def host_env_file_path
File.join host_env_directory, "traefik.env"
def host_clear_env_file_path
File.join host_env_directory, "traefik-clear.env"
end
def host_secret_env_file_path
File.join host_env_directory, "traefik-secret.env"
end
def make_env_directory
make_directory(host_env_directory)
end
def remove_env_file
[:rm, "-f", host_env_file_path]
def remove_env_files
[:rm, "-f", File.join(host_env_directory, "traefik*.env")]
end
private
@@ -97,7 +101,10 @@ class Kamal::Commands::Traefik < Kamal::Commands::Base
end
def env_args
argumentize "--env-file", host_env_file_path
[
*argumentize("--env-file", host_secret_env_file_path),
*argumentize("--env-file", host_clear_env_file_path)
]
end
def host_env_directory

View File

@@ -46,19 +46,26 @@ class Kamal::Configuration::Accessory
end
def env_file
Kamal::EnvFile.new(env)
Kamal::EnvFiles.new(env)
end
def host_env_directory
File.join config.host_env_directory, "accessories"
end
def host_env_file_path
File.join host_env_directory, "#{service_name}.env"
def host_clear_env_file_path
File.join host_env_directory, "#{service_name}-clear.env"
end
def host_secret_env_file_path
File.join host_env_directory, "#{service_name}-secret.env"
end
def env_args
argumentize "--env-file", host_env_file_path
[
*argumentize("--env-file", host_secret_env_file_path),
*argumentize("--env-file", host_clear_env_file_path)
]
end
def files

View File

@@ -46,19 +46,26 @@ class Kamal::Configuration::Role
end
def env_file
Kamal::EnvFile.new(env)
Kamal::EnvFiles.new(env)
end
def host_env_directory
File.join config.host_env_directory, "roles"
end
def host_env_file_path
File.join host_env_directory, "#{[config.service, name, config.destination].compact.join("-")}.env"
def host_clear_env_file_path
host_env_file_path(:clear)
end
def host_secret_env_file_path
host_env_file_path(:secret)
end
def env_args
argumentize "--env-file", host_env_file_path
[
*argumentize("--env-file", host_secret_env_file_path),
*argumentize("--env-file", host_clear_env_file_path)
]
end
def asset_volume_args
@@ -243,6 +250,10 @@ class Kamal::Configuration::Role
end
end
def host_env_file_path(env_type)
File.join host_env_directory, "#{[container_prefix, env_type].compact.join("-")}.env"
end
def http_health_check(port:, path:)
"curl -f #{URI.join("http://localhost:#{port}", path)} || exit 1" if path.present? || port.present?
end

View File

@@ -1,32 +1,25 @@
# Encode an env hash as a string where secret values have been looked up and all values escaped for Docker.
class Kamal::EnvFile
class Kamal::EnvFiles
def initialize(env)
@env = env
end
def to_s
env_file = StringIO.new.tap do |contents|
if (secrets = @env["secret"]).present?
@env.fetch("secret", @env)&.each do |key|
contents << docker_env_file_line(key, ENV.fetch(key))
end
@env["clear"]&.each do |key, value|
contents << docker_env_file_line(key, value)
end
else
@env.fetch("clear", @env)&.each do |key, value|
contents << docker_env_file_line(key, value)
end
end
end.string
# Ensure the file has some contents to avoid the SSHKIT empty file warning
env_file.presence || "\n"
def secret
env_file do
@env["secret"]&.to_h { |key| [ key, ENV.fetch(key) ] }
end
end
def clear
env_file do
if (secrets = @env["secret"]).present?
@env["clear"]
else
@env.fetch("clear", @env)
end
end
end
alias to_str to_s
private
def docker_env_file_line(key, value)
"#{key.to_s}=#{escape_docker_env_file_value(value)}\n"
@@ -38,4 +31,14 @@ class Kamal::EnvFile
# so remove leading and trailing ones and unescape any others
value.to_s.dump[1..-2].gsub(/\\"/, "\"")
end
def env_file(&block)
StringIO.new.tap do |contents|
block.call&.each do |key, value|
contents << docker_env_file_line(key, value)
end
# Ensure the file has some contents to avoid the SSHKit empty file warning
contents << "\n" if contents.length == 0
end.string
end
end