Merge pull request #732 from basecamp/always-send-clear-env
Always send the clear env to the container
This commit is contained in:
@@ -9,20 +9,20 @@ class Kamal::Cli::Env < Kamal::Cli::Base
|
|||||||
|
|
||||||
KAMAL.roles_on(host).each do |role|
|
KAMAL.roles_on(host).each do |role|
|
||||||
execute *KAMAL.app(role: role).make_env_directory
|
execute *KAMAL.app(role: role).make_env_directory
|
||||||
upload! StringIO.new(role.env_file), role.host_env_file_path, mode: 400
|
upload! role.env.secrets_io, role.env.secrets_file, mode: 400
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
on(KAMAL.traefik_hosts) do
|
on(KAMAL.traefik_hosts) do
|
||||||
execute *KAMAL.traefik.make_env_directory
|
execute *KAMAL.traefik.make_env_directory
|
||||||
upload! StringIO.new(KAMAL.traefik.env_file), KAMAL.traefik.host_env_file_path, mode: 400
|
upload! KAMAL.traefik.env.secrets_io, KAMAL.traefik.env.secrets_file, mode: 400
|
||||||
end
|
end
|
||||||
|
|
||||||
on(KAMAL.accessory_hosts) do
|
on(KAMAL.accessory_hosts) do
|
||||||
KAMAL.accessories_on(host).each do |accessory|
|
KAMAL.accessories_on(host).each do |accessory|
|
||||||
accessory_config = KAMAL.config.accessory(accessory)
|
accessory_config = KAMAL.config.accessory(accessory)
|
||||||
execute *KAMAL.accessory(accessory).make_env_directory
|
execute *KAMAL.accessory(accessory).make_env_directory
|
||||||
upload! StringIO.new(accessory_config.env_file), accessory_config.host_env_file_path, mode: 400
|
upload! accessory_config.env.secrets_io, accessory_config.env.secrets_file, mode: 400
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -99,11 +99,11 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def make_env_directory
|
def make_env_directory
|
||||||
make_directory accessory_config.host_env_directory
|
make_directory accessory_config.env.secrets_directory
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_env_file
|
def remove_env_file
|
||||||
[ :rm, "-f", accessory_config.host_env_file_path ]
|
[ :rm, "-f", accessory_config.env.secrets_file ]
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -68,11 +68,11 @@ class Kamal::Commands::App < Kamal::Commands::Base
|
|||||||
|
|
||||||
|
|
||||||
def make_env_directory
|
def make_env_directory
|
||||||
make_directory role.host_env_directory
|
make_directory role.env.secrets_directory
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_env_file
|
def remove_env_file
|
||||||
[ :rm, "-f", role.host_env_file_path ]
|
[ :rm, "-f", role.env.secrets_file ]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -71,20 +71,18 @@ class Kamal::Commands::Traefik < Kamal::Commands::Base
|
|||||||
"#{host_port}:#{CONTAINER_PORT}"
|
"#{host_port}:#{CONTAINER_PORT}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def env_file
|
def env
|
||||||
Kamal::EnvFile.new(config.traefik.fetch("env", {}))
|
Kamal::Configuration::Env.from_config \
|
||||||
end
|
config: config.traefik.fetch("env", {}),
|
||||||
|
secrets_file: File.join(config.host_env_directory, "traefik", "traefik.env")
|
||||||
def host_env_file_path
|
|
||||||
File.join host_env_directory, "traefik.env"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_env_directory
|
def make_env_directory
|
||||||
make_directory(host_env_directory)
|
make_directory(env.secrets_directory)
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_env_file
|
def remove_env_file
|
||||||
[ :rm, "-f", host_env_file_path ]
|
[ :rm, "-f", env.secrets_file ]
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@@ -97,11 +95,7 @@ class Kamal::Commands::Traefik < Kamal::Commands::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def env_args
|
def env_args
|
||||||
argumentize "--env-file", host_env_file_path
|
env.args
|
||||||
end
|
|
||||||
|
|
||||||
def host_env_directory
|
|
||||||
File.join config.host_env_directory, "traefik"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def labels
|
def labels
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ require "erb"
|
|||||||
require "net/ssh/proxy/jump"
|
require "net/ssh/proxy/jump"
|
||||||
|
|
||||||
class Kamal::Configuration
|
class Kamal::Configuration
|
||||||
delegate :service, :image, :servers, :env, :labels, :registry, :stop_wait_time, :hooks_path, :logging, to: :raw_config, allow_nil: true
|
delegate :service, :image, :servers, :labels, :registry, :stop_wait_time, :hooks_path, :logging, to: :raw_config, allow_nil: true
|
||||||
delegate :argumentize, :optionize, to: Kamal::Utils
|
delegate :argumentize, :optionize, to: Kamal::Utils
|
||||||
|
|
||||||
attr_reader :destination, :raw_config
|
attr_reader :destination, :raw_config
|
||||||
@@ -216,12 +216,17 @@ class Kamal::Configuration
|
|||||||
raw_config.hooks_path || ".kamal/hooks"
|
raw_config.hooks_path || ".kamal/hooks"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def asset_path
|
||||||
|
raw_config.asset_path
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
def host_env_directory
|
def host_env_directory
|
||||||
"#{run_directory}/env"
|
"#{run_directory}/env"
|
||||||
end
|
end
|
||||||
|
|
||||||
def asset_path
|
def env
|
||||||
raw_config.asset_path
|
raw_config.env || {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -42,23 +42,13 @@ class Kamal::Configuration::Accessory
|
|||||||
end
|
end
|
||||||
|
|
||||||
def env
|
def env
|
||||||
specifics["env"] || {}
|
Kamal::Configuration::Env.from_config \
|
||||||
end
|
config: specifics.fetch("env", {}),
|
||||||
|
secrets_file: File.join(config.host_env_directory, "accessories", "#{service_name}.env")
|
||||||
def env_file
|
|
||||||
Kamal::EnvFile.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"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def env_args
|
def env_args
|
||||||
argumentize "--env-file", host_env_file_path
|
env.args
|
||||||
end
|
end
|
||||||
|
|
||||||
def files
|
def files
|
||||||
@@ -111,10 +101,10 @@ class Kamal::Configuration::Accessory
|
|||||||
end
|
end
|
||||||
|
|
||||||
def with_clear_env_loaded
|
def with_clear_env_loaded
|
||||||
(env["clear"] || env).each { |k, v| ENV[k] = v }
|
env.clear.each { |k, v| ENV[k] = v }
|
||||||
yield
|
yield
|
||||||
ensure
|
ensure
|
||||||
(env["clear"] || env).each { |k, v| ENV.delete(k) }
|
env.clear.each { |k, v| ENV.delete(k) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_dynamic_file(local_file)
|
def read_dynamic_file(local_file)
|
||||||
|
|||||||
40
lib/kamal/configuration/env.rb
Normal file
40
lib/kamal/configuration/env.rb
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
class Kamal::Configuration::Env
|
||||||
|
attr_reader :secrets_keys, :clear, :secrets_file
|
||||||
|
delegate :argumentize, to: Kamal::Utils
|
||||||
|
|
||||||
|
def self.from_config(config:, secrets_file: nil)
|
||||||
|
secrets_keys = config.fetch("secret", [])
|
||||||
|
clear = config.fetch("clear", config.key?("secret") ? {} : config)
|
||||||
|
|
||||||
|
new clear: clear, secrets_keys: secrets_keys, secrets_file: secrets_file
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(clear:, secrets_keys:, secrets_file:)
|
||||||
|
@clear = clear
|
||||||
|
@secrets_keys = secrets_keys
|
||||||
|
@secrets_file = secrets_file
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
|
||||||
|
def merge(other)
|
||||||
|
self.class.new \
|
||||||
|
clear: @clear.merge(other.clear),
|
||||||
|
secrets_keys: @secrets_keys | other.secrets_keys,
|
||||||
|
secrets_file: secrets_file
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -51,27 +51,11 @@ class Kamal::Configuration::Role
|
|||||||
|
|
||||||
|
|
||||||
def env
|
def env
|
||||||
if config.env && config.env["secret"]
|
@env ||= base_env.merge(specialized_env)
|
||||||
merged_env_with_secrets
|
|
||||||
else
|
|
||||||
merged_env
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def env_file
|
|
||||||
Kamal::EnvFile.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"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def env_args
|
def env_args
|
||||||
argumentize "--env-file", host_env_file_path
|
env.args
|
||||||
end
|
end
|
||||||
|
|
||||||
def asset_volume_args
|
def asset_volume_args
|
||||||
@@ -217,7 +201,7 @@ class Kamal::Configuration::Role
|
|||||||
end
|
end
|
||||||
|
|
||||||
def traefik_service
|
def traefik_service
|
||||||
[ config.service, name, config.destination ].compact.join("-")
|
container_prefix
|
||||||
end
|
end
|
||||||
|
|
||||||
def custom_labels
|
def custom_labels
|
||||||
@@ -236,24 +220,14 @@ class Kamal::Configuration::Role
|
|||||||
end
|
end
|
||||||
|
|
||||||
def specialized_env
|
def specialized_env
|
||||||
specializations["env"] || {}
|
Kamal::Configuration::Env.from_config config: specializations.fetch("env", {})
|
||||||
end
|
|
||||||
|
|
||||||
def merged_env
|
|
||||||
config.env&.merge(specialized_env) || {}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Secrets are stored in an array, which won't merge by default, so have to do it by hand.
|
# Secrets are stored in an array, which won't merge by default, so have to do it by hand.
|
||||||
def merged_env_with_secrets
|
def base_env
|
||||||
merged_env.tap do |new_env|
|
Kamal::Configuration::Env.from_config \
|
||||||
new_env["secret"] = Array(config.env["secret"]) + Array(specialized_env["secret"])
|
config: config.env,
|
||||||
|
secrets_file: File.join(config.host_env_directory, "roles", "#{container_prefix}.env")
|
||||||
# If there's no secret/clear split, everything is clear
|
|
||||||
clear_app_env = config.env["secret"] ? Array(config.env["clear"]) : Array(config.env["clear"] || config.env)
|
|
||||||
clear_role_env = specialized_env["secret"] ? Array(specialized_env["clear"]) : Array(specialized_env["clear"] || specialized_env)
|
|
||||||
|
|
||||||
new_env["clear"] = clear_app_env.to_h.merge(clear_role_env.to_h)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def http_health_check(port:, path:)
|
def http_health_check(port:, path:)
|
||||||
|
|||||||
@@ -6,18 +6,8 @@ class Kamal::EnvFile
|
|||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
env_file = StringIO.new.tap do |contents|
|
env_file = StringIO.new.tap do |contents|
|
||||||
if (secrets = @env["secret"]).present?
|
@env.each do |key, value|
|
||||||
@env.fetch("secret", @env)&.each do |key|
|
contents << docker_env_file_line(key, value)
|
||||||
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
|
||||||
end.string
|
end.string
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ class CliAccessoryTest < CliTestCase
|
|||||||
|
|
||||||
run_command("boot", "mysql").tap do |output|
|
run_command("boot", "mysql").tap do |output|
|
||||||
assert_match /docker login.*on 1.1.1.3/, output
|
assert_match /docker login.*on 1.1.1.3/, output
|
||||||
assert_match "docker run --name app-mysql --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 3306:3306 --env-file .kamal/env/accessories/app-mysql.env --volume $PWD/app-mysql/etc/mysql/my.cnf:/etc/mysql/my.cnf --volume $PWD/app-mysql/data:/var/lib/mysql --label service=\"app-mysql\" mysql:5.7 on 1.1.1.3", output
|
assert_match "docker run --name app-mysql --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 3306:3306 --env-file .kamal/env/accessories/app-mysql.env --env MYSQL_ROOT_HOST=\"%\" --volume $PWD/app-mysql/etc/mysql/my.cnf:/etc/mysql/my.cnf --volume $PWD/app-mysql/data:/var/lib/mysql --label service=\"app-mysql\" mysql:5.7 on 1.1.1.3", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ class CliAccessoryTest < CliTestCase
|
|||||||
assert_match /docker login.*on 1.1.1.3/, output
|
assert_match /docker login.*on 1.1.1.3/, output
|
||||||
assert_match /docker login.*on 1.1.1.1/, output
|
assert_match /docker login.*on 1.1.1.1/, output
|
||||||
assert_match /docker login.*on 1.1.1.2/, output
|
assert_match /docker login.*on 1.1.1.2/, output
|
||||||
assert_match "docker run --name app-mysql --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 3306:3306 --env-file .kamal/env/accessories/app-mysql.env --volume $PWD/app-mysql/etc/mysql/my.cnf:/etc/mysql/my.cnf --volume $PWD/app-mysql/data:/var/lib/mysql --label service=\"app-mysql\" mysql:5.7 on 1.1.1.3", output
|
assert_match "docker run --name app-mysql --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 3306:3306 --env-file .kamal/env/accessories/app-mysql.env --env MYSQL_ROOT_HOST=\"%\" --volume $PWD/app-mysql/etc/mysql/my.cnf:/etc/mysql/my.cnf --volume $PWD/app-mysql/data:/var/lib/mysql --label service=\"app-mysql\" mysql:5.7 on 1.1.1.3", output
|
||||||
assert_match "docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/env/accessories/app-redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.1", output
|
assert_match "docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/env/accessories/app-redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.1", output
|
||||||
assert_match "docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/env/accessories/app-redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.2", output
|
assert_match "docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/env/accessories/app-redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.2", output
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -50,11 +50,11 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
test "run" do
|
test "run" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name app-mysql --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 3306:3306 --env-file .kamal/env/accessories/app-mysql.env --label service=\"app-mysql\" private.registry/mysql:8.0",
|
"docker run --name app-mysql --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 3306:3306 --env-file .kamal/env/accessories/app-mysql.env --env MYSQL_ROOT_HOST=\"%\" --label service=\"app-mysql\" private.registry/mysql:8.0",
|
||||||
new_command(:mysql).run.join(" ")
|
new_command(:mysql).run.join(" ")
|
||||||
|
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/env/accessories/app-redis.env --volume /var/lib/redis:/data --label service=\"app-redis\" --label cache=\"true\" redis:latest",
|
"docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/env/accessories/app-redis.env --env SOMETHING=\"else\" --volume /var/lib/redis:/data --label service=\"app-redis\" --label cache=\"true\" redis:latest",
|
||||||
new_command(:redis).run.join(" ")
|
new_command(:redis).run.join(" ")
|
||||||
|
|
||||||
assert_equal \
|
assert_equal \
|
||||||
@@ -91,7 +91,7 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
test "execute in new container" do
|
test "execute in new container" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --rm --env-file .kamal/env/accessories/app-mysql.env private.registry/mysql:8.0 mysql -u root",
|
"docker run --rm --env-file .kamal/env/accessories/app-mysql.env --env MYSQL_ROOT_HOST=\"%\" private.registry/mysql:8.0 mysql -u root",
|
||||||
new_command(:mysql).execute_in_new_container("mysql", "-u", "root").join(" ")
|
new_command(:mysql).execute_in_new_container("mysql", "-u", "root").join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
test "execute in new container over ssh" do
|
test "execute in new container over ssh" do
|
||||||
new_command(:mysql).stub(:run_over_ssh, ->(cmd) { cmd.join(" ") }) do
|
new_command(:mysql).stub(:run_over_ssh, ->(cmd) { cmd.join(" ") }) do
|
||||||
assert_match %r{docker run -it --rm --env-file .kamal/env/accessories/app-mysql.env private.registry/mysql:8.0 mysql -u root},
|
assert_match %r{docker run -it --rm --env-file .kamal/env/accessories/app-mysql.env --env MYSQL_ROOT_HOST=\"%\" private.registry/mysql:8.0 mysql -u root},
|
||||||
new_command(:mysql).execute_in_new_container_over_ssh("mysql", "-u", "root")
|
new_command(:mysql).execute_in_new_container_over_ssh("mysql", "-u", "root")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -177,14 +177,10 @@ class CommandsTraefikTest < ActiveSupport::TestCase
|
|||||||
new_command.follow_logs(host: @config[:servers].first, grep: "hello!")
|
new_command.follow_logs(host: @config[:servers].first, grep: "hello!")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "env_file" do
|
test "secrets io" do
|
||||||
@config[:traefik]["env"] = { "secret" => %w[EXAMPLE_API_KEY] }
|
@config[:traefik]["env"] = { "secret" => %w[EXAMPLE_API_KEY] }
|
||||||
|
|
||||||
assert_equal "EXAMPLE_API_KEY=456\n", new_command.env_file.to_s
|
assert_equal "EXAMPLE_API_KEY=456\n", new_command.env.secrets_io.string
|
||||||
end
|
|
||||||
|
|
||||||
test "host_env_file_path" do
|
|
||||||
assert_equal ".kamal/env/traefik/traefik.env", new_command.host_env_file_path
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "make_env_directory" do
|
test "make_env_directory" do
|
||||||
|
|||||||
@@ -113,29 +113,25 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "env args" do
|
test "env args" do
|
||||||
assert_equal [ "--env-file", ".kamal/env/accessories/app-mysql.env" ], @config.accessory(:mysql).env_args
|
assert_equal [ "--env-file", ".kamal/env/accessories/app-mysql.env", "--env", "MYSQL_ROOT_HOST=\"%\"" ], @config.accessory(:mysql).env_args
|
||||||
assert_equal [ "--env-file", ".kamal/env/accessories/app-redis.env" ], @config.accessory(:redis).env_args
|
assert_equal [ "--env-file", ".kamal/env/accessories/app-redis.env", "--env", "SOMETHING=\"else\"" ], @config.accessory(:redis).env_args
|
||||||
end
|
end
|
||||||
|
|
||||||
test "env file with secret" do
|
test "env with secrets" do
|
||||||
ENV["MYSQL_ROOT_PASSWORD"] = "secret123"
|
ENV["MYSQL_ROOT_PASSWORD"] = "secret123"
|
||||||
|
|
||||||
expected = <<~ENV
|
expected_secrets_file = <<~ENV
|
||||||
MYSQL_ROOT_PASSWORD=secret123
|
MYSQL_ROOT_PASSWORD=secret123
|
||||||
MYSQL_ROOT_HOST=%
|
|
||||||
ENV
|
ENV
|
||||||
|
|
||||||
assert_equal expected, @config.accessory(:mysql).env_file.to_s
|
assert_equal expected_secrets_file, @config.accessory(:mysql).env.secrets_io.string
|
||||||
|
assert_equal [ "--env-file", ".kamal/env/accessories/app-mysql.env", "--env", "MYSQL_ROOT_HOST=\"%\"" ], @config.accessory(:mysql).env_args
|
||||||
ensure
|
ensure
|
||||||
ENV["MYSQL_ROOT_PASSWORD"] = nil
|
ENV["MYSQL_ROOT_PASSWORD"] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
test "host_env_directory" do
|
test "env secrets path" do
|
||||||
assert_equal ".kamal/env/accessories", @config.accessory(:mysql).host_env_directory
|
assert_equal ".kamal/env/accessories/app-mysql.env", @config.accessory(:mysql).env.secrets_file
|
||||||
end
|
|
||||||
|
|
||||||
test "host_env_file_path" do
|
|
||||||
assert_equal ".kamal/env/accessories/app-mysql.env", @config.accessory(:mysql).host_env_file_path
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "volume args" do
|
test "volume args" do
|
||||||
|
|||||||
74
test/configuration/env_test.rb
Normal file
74
test/configuration/env_test.rb
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class ConfigurationEnvTest < ActiveSupport::TestCase
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
test "simple" do
|
||||||
|
assert_config \
|
||||||
|
config: { "foo" => "bar", "baz" => "haz" },
|
||||||
|
clear: { "foo" => "bar", "baz" => "haz" },
|
||||||
|
secrets: {}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "clear" do
|
||||||
|
assert_config \
|
||||||
|
config: { "clear" => { "foo" => "bar", "baz" => "haz" } },
|
||||||
|
clear: { "foo" => "bar", "baz" => "haz" },
|
||||||
|
secrets: {}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "secret" do
|
||||||
|
ENV["PASSWORD"] = "hello"
|
||||||
|
env = Kamal::Configuration::Env.from_config config: { "secret" => [ "PASSWORD" ] }
|
||||||
|
|
||||||
|
assert_config \
|
||||||
|
config: { "secret" => [ "PASSWORD" ] },
|
||||||
|
clear: {},
|
||||||
|
secrets: { "PASSWORD" => "hello" }
|
||||||
|
ensure
|
||||||
|
ENV.delete "PASSWORD"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "missing secret" do
|
||||||
|
env = {
|
||||||
|
"secret" => [ "PASSWORD" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_raises(KeyError) { Kamal::Configuration::Env.from_config(config: { "secret" => [ "PASSWORD" ] }).secrets }
|
||||||
|
end
|
||||||
|
|
||||||
|
test "secret and clear" do
|
||||||
|
ENV["PASSWORD"] = "hello"
|
||||||
|
config = {
|
||||||
|
"secret" => [ "PASSWORD" ],
|
||||||
|
"clear" => {
|
||||||
|
"foo" => "bar",
|
||||||
|
"baz" => "haz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_config \
|
||||||
|
config: config,
|
||||||
|
clear: { "foo" => "bar", "baz" => "haz" },
|
||||||
|
secrets: { "PASSWORD" => "hello" }
|
||||||
|
ensure
|
||||||
|
ENV.delete "PASSWORD"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "stringIO conversion" do
|
||||||
|
env = {
|
||||||
|
"foo" => "bar",
|
||||||
|
"baz" => "haz"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_equal "foo=bar\nbaz=haz\n", \
|
||||||
|
StringIO.new(Kamal::EnvFile.new(env)).read
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def assert_config(config:, clear:, secrets:)
|
||||||
|
env = Kamal::Configuration::Env.from_config config: config
|
||||||
|
assert_equal clear, env.clear
|
||||||
|
assert_equal secrets, env.secrets
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -70,14 +70,10 @@ class ConfigurationRoleTest < ActiveSupport::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "env overwritten by role" do
|
test "env overwritten by role" do
|
||||||
assert_equal "redis://a/b", @config_with_roles.role(:workers).env["REDIS_URL"]
|
assert_equal "redis://a/b", @config_with_roles.role(:workers).env.clear["REDIS_URL"]
|
||||||
|
|
||||||
expected_env = <<~ENV
|
assert_equal "\n", @config_with_roles.role(:workers).env.secrets_io.string
|
||||||
REDIS_URL=redis://a/b
|
assert_equal [ "--env-file", ".kamal/env/roles/app-workers.env", "--env", "REDIS_URL=\"redis://a/b\"", "--env", "WEB_CONCURRENCY=\"4\"" ], @config_with_roles.role(:workers).env_args
|
||||||
WEB_CONCURRENCY=4
|
|
||||||
ENV
|
|
||||||
|
|
||||||
assert_equal expected_env, @config_with_roles.role(:workers).env_file.to_s
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "container name" do
|
test "container name" do
|
||||||
@@ -90,7 +86,7 @@ class ConfigurationRoleTest < ActiveSupport::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "env args" do
|
test "env args" do
|
||||||
assert_equal [ "--env-file", ".kamal/env/roles/app-workers.env" ], @config_with_roles.role(:workers).env_args
|
assert_equal [ "--env-file", ".kamal/env/roles/app-workers.env", "--env", "REDIS_URL=\"redis://a/b\"", "--env", "WEB_CONCURRENCY=\"4\"" ], @config_with_roles.role(:workers).env_args
|
||||||
end
|
end
|
||||||
|
|
||||||
test "env secret overwritten by role" do
|
test "env secret overwritten by role" do
|
||||||
@@ -116,14 +112,13 @@ class ConfigurationRoleTest < ActiveSupport::TestCase
|
|||||||
ENV["REDIS_PASSWORD"] = "secret456"
|
ENV["REDIS_PASSWORD"] = "secret456"
|
||||||
ENV["DB_PASSWORD"] = "secret&\"123"
|
ENV["DB_PASSWORD"] = "secret&\"123"
|
||||||
|
|
||||||
expected = <<~ENV
|
expected_secrets_file = <<~ENV
|
||||||
REDIS_PASSWORD=secret456
|
REDIS_PASSWORD=secret456
|
||||||
DB_PASSWORD=secret&\"123
|
DB_PASSWORD=secret&\"123
|
||||||
REDIS_URL=redis://a/b
|
|
||||||
WEB_CONCURRENCY=4
|
|
||||||
ENV
|
ENV
|
||||||
|
|
||||||
assert_equal expected, @config_with_roles.role(:workers).env_file.to_s
|
assert_equal expected_secrets_file, @config_with_roles.role(:workers).env.secrets_io.string
|
||||||
|
assert_equal [ "--env-file", ".kamal/env/roles/app-workers.env", "--env", "REDIS_URL=\"redis://a/b\"", "--env", "WEB_CONCURRENCY=\"4\"" ], @config_with_roles.role(:workers).env_args
|
||||||
ensure
|
ensure
|
||||||
ENV["REDIS_PASSWORD"] = nil
|
ENV["REDIS_PASSWORD"] = nil
|
||||||
ENV["DB_PASSWORD"] = nil
|
ENV["DB_PASSWORD"] = nil
|
||||||
@@ -142,13 +137,12 @@ class ConfigurationRoleTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
ENV["DB_PASSWORD"] = "secret123"
|
ENV["DB_PASSWORD"] = "secret123"
|
||||||
|
|
||||||
expected = <<~ENV
|
expected_secrets_file = <<~ENV
|
||||||
DB_PASSWORD=secret123
|
DB_PASSWORD=secret123
|
||||||
REDIS_URL=redis://a/b
|
|
||||||
WEB_CONCURRENCY=4
|
|
||||||
ENV
|
ENV
|
||||||
|
|
||||||
assert_equal expected, @config_with_roles.role(:workers).env_file.to_s
|
assert_equal expected_secrets_file, @config_with_roles.role(:workers).env.secrets_io.string
|
||||||
|
assert_equal [ "--env-file", ".kamal/env/roles/app-workers.env", "--env", "REDIS_URL=\"redis://a/b\"", "--env", "WEB_CONCURRENCY=\"4\"" ], @config_with_roles.role(:workers).env_args
|
||||||
ensure
|
ensure
|
||||||
ENV["DB_PASSWORD"] = nil
|
ENV["DB_PASSWORD"] = nil
|
||||||
end
|
end
|
||||||
@@ -165,13 +159,12 @@ class ConfigurationRoleTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
ENV["REDIS_PASSWORD"] = "secret456"
|
ENV["REDIS_PASSWORD"] = "secret456"
|
||||||
|
|
||||||
expected = <<~ENV
|
expected_secrets_file = <<~ENV
|
||||||
REDIS_PASSWORD=secret456
|
REDIS_PASSWORD=secret456
|
||||||
REDIS_URL=redis://a/b
|
|
||||||
WEB_CONCURRENCY=4
|
|
||||||
ENV
|
ENV
|
||||||
|
|
||||||
assert_equal expected, @config_with_roles.role(:workers).env_file.to_s
|
assert_equal expected_secrets_file, @config_with_roles.role(:workers).env.secrets_io.string
|
||||||
|
assert_equal [ "--env-file", ".kamal/env/roles/app-workers.env", "--env", "REDIS_URL=\"redis://a/b\"", "--env", "WEB_CONCURRENCY=\"4\"" ], @config_with_roles.role(:workers).env_args
|
||||||
ensure
|
ensure
|
||||||
ENV["REDIS_PASSWORD"] = nil
|
ENV["REDIS_PASSWORD"] = nil
|
||||||
end
|
end
|
||||||
@@ -194,22 +187,18 @@ class ConfigurationRoleTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
ENV["REDIS_PASSWORD"] = "secret456"
|
ENV["REDIS_PASSWORD"] = "secret456"
|
||||||
|
|
||||||
expected = <<~ENV
|
expected_secrets_file = <<~ENV
|
||||||
REDIS_PASSWORD=secret456
|
REDIS_PASSWORD=secret456
|
||||||
REDIS_URL=redis://c/d
|
|
||||||
ENV
|
ENV
|
||||||
|
|
||||||
assert_equal expected, @config_with_roles.role(:workers).env_file.to_s
|
assert_equal expected_secrets_file, @config_with_roles.role(:workers).env.secrets_io.string
|
||||||
|
assert_equal [ "--env-file", ".kamal/env/roles/app-workers.env", "--env", "REDIS_URL=\"redis://c/d\"" ], @config_with_roles.role(:workers).env_args
|
||||||
ensure
|
ensure
|
||||||
ENV["REDIS_PASSWORD"] = nil
|
ENV["REDIS_PASSWORD"] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
test "host_env_directory" do
|
test "env secrets_file" do
|
||||||
assert_equal ".kamal/env/roles", @config_with_roles.role(:workers).host_env_directory
|
assert_equal ".kamal/env/roles/app-workers.env", @config_with_roles.role(:workers).env.secrets_file
|
||||||
end
|
|
||||||
|
|
||||||
test "host_env_file_path" do
|
|
||||||
assert_equal ".kamal/env/roles/app-workers.env", @config_with_roles.role(:workers).host_env_file_path
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "uses cord" do
|
test "uses cord" do
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
require "test_helper"
|
require "test_helper"
|
||||||
|
|
||||||
class EnvFileTest < ActiveSupport::TestCase
|
class EnvFileTest < ActiveSupport::TestCase
|
||||||
test "env file simple" do
|
test "to_s" do
|
||||||
env = {
|
env = {
|
||||||
"foo" => "bar",
|
"foo" => "bar",
|
||||||
"baz" => "haz"
|
"baz" => "haz"
|
||||||
@@ -11,80 +11,27 @@ class EnvFileTest < ActiveSupport::TestCase
|
|||||||
Kamal::EnvFile.new(env).to_s
|
Kamal::EnvFile.new(env).to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
test "env file clear" do
|
test "to_s empty" do
|
||||||
env = {
|
|
||||||
"clear" => {
|
|
||||||
"foo" => "bar",
|
|
||||||
"baz" => "haz"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_equal "foo=bar\nbaz=haz\n", \
|
|
||||||
Kamal::EnvFile.new(env).to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
test "env file empty" do
|
|
||||||
assert_equal "\n", Kamal::EnvFile.new({}).to_s
|
assert_equal "\n", Kamal::EnvFile.new({}).to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
test "env file secret" do
|
test "to_s escaped newline" do
|
||||||
ENV["PASSWORD"] = "hello"
|
|
||||||
env = {
|
env = {
|
||||||
"secret" => [ "PASSWORD" ]
|
"foo" => "hello\\nthere"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_equal "PASSWORD=hello\n", \
|
assert_equal "foo=hello\\\\nthere\n", \
|
||||||
Kamal::EnvFile.new(env).to_s
|
Kamal::EnvFile.new(env).to_s
|
||||||
ensure
|
ensure
|
||||||
ENV.delete "PASSWORD"
|
ENV.delete "PASSWORD"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "env file secret escaped newline" do
|
test "to_s newline" do
|
||||||
ENV["PASSWORD"] = "hello\\nthere"
|
|
||||||
env = {
|
env = {
|
||||||
"secret" => [ "PASSWORD" ]
|
"foo" => "hello\nthere"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_equal "PASSWORD=hello\\\\nthere\n", \
|
assert_equal "foo=hello\\nthere\n", \
|
||||||
Kamal::EnvFile.new(env).to_s
|
|
||||||
ensure
|
|
||||||
ENV.delete "PASSWORD"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "env file secret newline" do
|
|
||||||
ENV["PASSWORD"] = "hello\nthere"
|
|
||||||
env = {
|
|
||||||
"secret" => [ "PASSWORD" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_equal "PASSWORD=hello\\nthere\n", \
|
|
||||||
Kamal::EnvFile.new(env).to_s
|
|
||||||
ensure
|
|
||||||
ENV.delete "PASSWORD"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "env file missing secret" do
|
|
||||||
env = {
|
|
||||||
"secret" => [ "PASSWORD" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_raises(KeyError) { Kamal::EnvFile.new(env).to_s }
|
|
||||||
|
|
||||||
ensure
|
|
||||||
ENV.delete "PASSWORD"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "env file secret and clear" do
|
|
||||||
ENV["PASSWORD"] = "hello"
|
|
||||||
env = {
|
|
||||||
"secret" => [ "PASSWORD" ],
|
|
||||||
"clear" => {
|
|
||||||
"foo" => "bar",
|
|
||||||
"baz" => "haz"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_equal "PASSWORD=hello\nfoo=bar\nbaz=haz\n", \
|
|
||||||
Kamal::EnvFile.new(env).to_s
|
Kamal::EnvFile.new(env).to_s
|
||||||
ensure
|
ensure
|
||||||
ENV.delete "PASSWORD"
|
ENV.delete "PASSWORD"
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ servers:
|
|||||||
- vm2
|
- vm2
|
||||||
env:
|
env:
|
||||||
clear:
|
clear:
|
||||||
CLEAR_TOKEN: '4321'
|
CLEAR_TOKEN: 4321
|
||||||
|
HOST_TOKEN: "${HOST_TOKEN}"
|
||||||
secret:
|
secret:
|
||||||
- SECRET_TOKEN
|
- SECRET_TOKEN
|
||||||
asset_path: /usr/share/nginx/html/versions
|
asset_path: /usr/share/nginx/html/versions
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ RUN apt-get update --fix-missing && apt-get -y install openssh-client openssh-se
|
|||||||
RUN mkdir /root/.ssh && ln -s /shared/ssh/id_rsa.pub /root/.ssh/authorized_keys
|
RUN mkdir /root/.ssh && ln -s /shared/ssh/id_rsa.pub /root/.ssh/authorized_keys
|
||||||
RUN mkdir -p /etc/docker/certs.d/registry:4443 && ln -s /shared/certs/domain.crt /etc/docker/certs.d/registry:4443/ca.crt
|
RUN mkdir -p /etc/docker/certs.d/registry:4443 && ln -s /shared/certs/domain.crt /etc/docker/certs.d/registry:4443/ca.crt
|
||||||
|
|
||||||
|
RUN echo "HOST_TOKEN=abcd" >> /etc/environment
|
||||||
|
|
||||||
COPY boot.sh .
|
COPY boot.sh .
|
||||||
|
|
||||||
HEALTHCHECK --interval=1s CMD pgrep dockerd
|
HEALTHCHECK --interval=1s CMD pgrep dockerd
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ class MainTest < IntegrationTest
|
|||||||
test "envify, deploy, redeploy, rollback, details and audit" do
|
test "envify, deploy, redeploy, rollback, details and audit" do
|
||||||
kamal :envify
|
kamal :envify
|
||||||
assert_local_env_file "SECRET_TOKEN=1234"
|
assert_local_env_file "SECRET_TOKEN=1234"
|
||||||
assert_remote_env_file "SECRET_TOKEN=1234\nCLEAR_TOKEN=4321"
|
assert_remote_env_file "SECRET_TOKEN=1234"
|
||||||
remove_local_env_file
|
remove_local_env_file
|
||||||
|
|
||||||
first_version = latest_app_version
|
first_version = latest_app_version
|
||||||
@@ -14,6 +14,9 @@ class MainTest < IntegrationTest
|
|||||||
kamal :deploy
|
kamal :deploy
|
||||||
assert_app_is_up version: first_version
|
assert_app_is_up version: first_version
|
||||||
assert_hooks_ran "pre-connect", "pre-build", "pre-deploy", "post-deploy"
|
assert_hooks_ran "pre-connect", "pre-build", "pre-deploy", "post-deploy"
|
||||||
|
assert_env :CLEAR_TOKEN, "4321", version: first_version
|
||||||
|
assert_env :HOST_TOKEN, "abcd", version: first_version
|
||||||
|
assert_env :SECRET_TOKEN, "1234", version: first_version
|
||||||
|
|
||||||
second_version = update_app_rev
|
second_version = update_app_rev
|
||||||
|
|
||||||
@@ -94,6 +97,10 @@ class MainTest < IntegrationTest
|
|||||||
assert_equal contents, deployer_exec("cat .env", capture: true)
|
assert_equal contents, deployer_exec("cat .env", capture: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def assert_env(key, value, version:)
|
||||||
|
assert_equal "#{key}=#{value}", docker_compose("exec vm1 docker exec app-web-#{version} env | grep #{key}", capture: true)
|
||||||
|
end
|
||||||
|
|
||||||
def remove_local_env_file
|
def remove_local_env_file
|
||||||
deployer_exec("rm .env")
|
deployer_exec("rm .env")
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user