[Feature] Add optional accessory registry.

Add test cases to cover new option.
This commit is contained in:
Pavel Shpak
2024-12-22 02:50:53 +02:00
parent 1547089da0
commit b5aee11a40
12 changed files with 208 additions and 77 deletions

View File

@@ -14,8 +14,8 @@ class CliAccessoryTest < CliTestCase
Kamal::Cli::Accessory.any_instance.expects(:upload).with("mysql")
run_command("boot", "mysql").tap do |output|
assert_match /docker login.*on 1.1.1.3/, output
assert_match "docker run --name app-mysql --detach --restart unless-stopped --network kamal --log-opt max-size=\"10m\" --publish 3306:3306 --env MYSQL_ROOT_HOST=\"%\" --env-file .kamal/apps/app/env/accessories/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 login private.registry -u [REDACTED] -p [REDACTED] on 1.1.1.3", output
assert_match "docker run --name app-mysql --detach --restart unless-stopped --network kamal --log-opt max-size=\"10m\" --publish 3306:3306 --env MYSQL_ROOT_HOST=\"%\" --env-file .kamal/apps/app/env/accessories/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\" private.registry/mysql:5.7 on 1.1.1.3", output
end
end
@@ -24,17 +24,21 @@ class CliAccessoryTest < CliTestCase
Kamal::Cli::Accessory.any_instance.expects(:upload).with("mysql")
Kamal::Cli::Accessory.any_instance.expects(:directories).with("redis")
Kamal::Cli::Accessory.any_instance.expects(:upload).with("redis")
Kamal::Cli::Accessory.any_instance.expects(:directories).with("busybox")
Kamal::Cli::Accessory.any_instance.expects(:upload).with("busybox")
run_command("boot", "all").tap do |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.2/, output
assert_match "docker login private.registry -u [REDACTED] -p [REDACTED] on 1.1.1.3", output
assert_match "docker login private.registry -u [REDACTED] -p [REDACTED] on 1.1.1.1", output
assert_match "docker login private.registry -u [REDACTED] -p [REDACTED] on 1.1.1.2", output
assert_match "docker login other.registry -u [REDACTED] -p [REDACTED] on 1.1.1.3", output
assert_match /docker network create kamal.*on 1.1.1.1/, output
assert_match /docker network create kamal.*on 1.1.1.2/, output
assert_match /docker network create kamal.*on 1.1.1.3/, output
assert_match "docker run --name app-mysql --detach --restart unless-stopped --network kamal --log-opt max-size=\"10m\" --publish 3306:3306 --env MYSQL_ROOT_HOST=\"%\" --env-file .kamal/apps/app/env/accessories/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 --network kamal --log-opt max-size=\"10m\" --publish 3306:3306 --env MYSQL_ROOT_HOST=\"%\" --env-file .kamal/apps/app/env/accessories/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\" private.registry/mysql:5.7 on 1.1.1.3", output
assert_match "docker run --name app-redis --detach --restart unless-stopped --network kamal --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/apps/app/env/accessories/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 --network kamal --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/apps/app/env/accessories/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 custom-box --detach --restart unless-stopped --network kamal --log-opt max-size=\"10m\" --env-file .kamal/apps/app/env/accessories/busybox.env --label service=\"custom-box\" other.registry/busybox:latest on 1.1.1.3", output
end
end
@@ -60,13 +64,16 @@ class CliAccessoryTest < CliTestCase
end
test "reboot all" do
Kamal::Commands::Registry.any_instance.expects(:login).times(3)
Kamal::Commands::Registry.any_instance.expects(:login).times(4)
Kamal::Cli::Accessory.any_instance.expects(:stop).with("mysql")
Kamal::Cli::Accessory.any_instance.expects(:remove_container).with("mysql")
Kamal::Cli::Accessory.any_instance.expects(:boot).with("mysql", prepare: false)
Kamal::Cli::Accessory.any_instance.expects(:stop).with("redis")
Kamal::Cli::Accessory.any_instance.expects(:remove_container).with("redis")
Kamal::Cli::Accessory.any_instance.expects(:boot).with("redis", prepare: false)
Kamal::Cli::Accessory.any_instance.expects(:stop).with("busybox")
Kamal::Cli::Accessory.any_instance.expects(:remove_container).with("busybox")
Kamal::Cli::Accessory.any_instance.expects(:boot).with("busybox", prepare: false)
run_command("reboot", "all")
end
@@ -94,7 +101,7 @@ class CliAccessoryTest < CliTestCase
end
test "details with non-existent accessory" do
assert_equal "No accessory by the name of 'hello' (options: mysql and redis)", stderred { run_command("details", "hello") }
assert_equal "No accessory by the name of 'hello' (options: mysql, redis, and busybox)", stderred { run_command("details", "hello") }
end
test "details with all" do
@@ -180,6 +187,10 @@ class CliAccessoryTest < CliTestCase
Kamal::Cli::Accessory.any_instance.expects(:remove_container).with("redis")
Kamal::Cli::Accessory.any_instance.expects(:remove_image).with("redis")
Kamal::Cli::Accessory.any_instance.expects(:remove_service_directory).with("redis")
Kamal::Cli::Accessory.any_instance.expects(:stop).with("busybox")
Kamal::Cli::Accessory.any_instance.expects(:remove_container).with("busybox")
Kamal::Cli::Accessory.any_instance.expects(:remove_image).with("busybox")
Kamal::Cli::Accessory.any_instance.expects(:remove_service_directory).with("busybox")
run_command("remove", "all", "-y")
end
@@ -189,7 +200,7 @@ class CliAccessoryTest < CliTestCase
end
test "remove_image" do
assert_match "docker image rm --force mysql", run_command("remove_image", "mysql")
assert_match "docker image rm --force private.registry/mysql:5.7", run_command("remove_image", "mysql")
end
test "remove_service_directory" do
@@ -201,8 +212,8 @@ class CliAccessoryTest < CliTestCase
Kamal::Cli::Accessory.any_instance.expects(:upload).with("redis")
run_command("boot", "redis", "--hosts", "1.1.1.1").tap do |output|
assert_match /docker login.*on 1.1.1.1/, output
assert_no_match /docker login.*on 1.1.1.2/, output
assert_match "docker login private.registry -u [REDACTED] -p [REDACTED] on 1.1.1.1", output
assert_no_match "docker login private.registry -u [REDACTED] -p [REDACTED] on 1.1.1.2", output
assert_match "docker run --name app-redis --detach --restart unless-stopped --network kamal --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/apps/app/env/accessories/redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.1", output
assert_no_match "docker run --name app-redis --detach --restart unless-stopped --network kamal --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/apps/app/env/accessories/redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.2", output
end
@@ -213,8 +224,8 @@ class CliAccessoryTest < CliTestCase
Kamal::Cli::Accessory.any_instance.expects(:upload).with("redis")
run_command("boot", "redis", "--hosts", "1.1.1.1,1.1.1.3").tap do |output|
assert_match /docker login.*on 1.1.1.1/, output
assert_no_match /docker login.*on 1.1.1.3/, output
assert_match "docker login private.registry -u [REDACTED] -p [REDACTED] on 1.1.1.1", output
assert_no_match "docker login private.registry -u [REDACTED] -p [REDACTED] on 1.1.1.3", output
assert_match "docker run --name app-redis --detach --restart unless-stopped --network kamal --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/apps/app/env/accessories/redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.1", output
assert_no_match "docker run --name app-redis --detach --restart unless-stopped --network kamal --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/apps/app/env/accessories/redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.3", output
end
@@ -225,7 +236,7 @@ class CliAccessoryTest < CliTestCase
assert_match "Upgrading all accessories on 1.1.1.3,1.1.1.1,1.1.1.2...", output
assert_match "docker network create kamal on 1.1.1.3", output
assert_match "docker container stop app-mysql on 1.1.1.3", output
assert_match "docker run --name app-mysql --detach --restart unless-stopped --network kamal --log-opt max-size=\"10m\" --publish 3306:3306 --env MYSQL_ROOT_HOST="%" --env-file .kamal/apps/app/env/accessories/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 --network kamal --log-opt max-size=\"10m\" --publish 3306:3306 --env MYSQL_ROOT_HOST="%" --env-file .kamal/apps/app/env/accessories/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\" private.registry/mysql:5.7 on 1.1.1.3", output
assert_match "Upgraded all accessories on 1.1.1.3,1.1.1.1,1.1.1.2...", output
end
end
@@ -235,14 +246,13 @@ class CliAccessoryTest < CliTestCase
assert_match "Upgrading all accessories on 1.1.1.3...", output
assert_match "docker network create kamal on 1.1.1.3", output
assert_match "docker container stop app-mysql on 1.1.1.3", output
assert_match "docker run --name app-mysql --detach --restart unless-stopped --network kamal --log-opt max-size=\"10m\" --publish 3306:3306 --env MYSQL_ROOT_HOST="%" --env-file .kamal/apps/app/env/accessories/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 --network kamal --log-opt max-size=\"10m\" --publish 3306:3306 --env MYSQL_ROOT_HOST="%" --env-file .kamal/apps/app/env/accessories/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\" private.registry/mysql:5.7 on 1.1.1.3", output
assert_match "Upgraded all accessories on 1.1.1.3", output
end
end
private
def run_command(*command)
stdouted { Kamal::Cli::Accessory.start([ *command, "-c", "test/fixtures/deploy_with_accessories.yml" ]) }
stdouted { Kamal::Cli::Accessory.start([ *command, "-c", "test/fixtures/deploy_with_accessories_with_different_registries.yml" ]) }
end
end

View File

@@ -5,7 +5,9 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
setup_test_secrets("secrets" => "MYSQL_ROOT_PASSWORD=secret123")
@config = {
service: "app", image: "dhh/app", registry: { "server" => "private.registry", "username" => "dhh", "password" => "secret" },
service: "app",
image: "dhh/app",
registry: { "server" => "private.registry", "username" => "dhh", "password" => "secret" },
servers: [ "1.1.1.1" ],
builder: { "arch" => "amd64" },
accessories: {
@@ -39,6 +41,7 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
"busybox" => {
"service" => "custom-busybox",
"image" => "busybox:latest",
"registry" => { "server" => "other.registry", "username" => "user", "password" => "pw" },
"host" => "1.1.1.7",
"proxy" => {
"host" => "busybox.example.com"
@@ -62,7 +65,7 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
new_command(:redis).run.join(" ")
assert_equal \
"docker run --name custom-busybox --detach --restart unless-stopped --network kamal --log-opt max-size=\"10m\" --env-file .kamal/apps/app/env/accessories/busybox.env --label service=\"custom-busybox\" busybox:latest",
"docker run --name custom-busybox --detach --restart unless-stopped --network kamal --log-opt max-size=\"10m\" --env-file .kamal/apps/app/env/accessories/busybox.env --label service=\"custom-busybox\" other.registry/busybox:latest",
new_command(:busybox).run.join(" ")
end
@@ -70,7 +73,7 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
@config[:logging] = { "driver" => "local", "options" => { "max-size" => "100m", "max-file" => "3" } }
assert_equal \
"docker run --name custom-busybox --detach --restart unless-stopped --network kamal --log-driver \"local\" --log-opt max-size=\"100m\" --log-opt max-file=\"3\" --env-file .kamal/apps/app/env/accessories/busybox.env --label service=\"custom-busybox\" busybox:latest",
"docker run --name custom-busybox --detach --restart unless-stopped --network kamal --log-driver \"local\" --log-opt max-size=\"100m\" --log-opt max-file=\"3\" --env-file .kamal/apps/app/env/accessories/busybox.env --label service=\"custom-busybox\" other.registry/busybox:latest",
new_command(:busybox).run.join(" ")
end
@@ -100,7 +103,6 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
new_command(:mysql).info.join(" ")
end
test "execute in new container" do
assert_equal \
"docker run --rm --network kamal --env MYSQL_ROOT_HOST=\"%\" --env-file .kamal/apps/app/env/accessories/mysql.env private.registry/mysql:8.0 mysql -u root",
@@ -127,8 +129,6 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
end
end
test "logs" do
assert_equal \
"docker logs app-mysql --timestamps 2>&1",

View File

@@ -2,14 +2,27 @@ require "test_helper"
class CommandsRegistryTest < ActiveSupport::TestCase
setup do
@config = { service: "app",
@config = {
service: "app",
image: "dhh/app",
registry: { "username" => "dhh",
registry: {
"username" => "dhh",
"password" => "secret",
"server" => "hub.docker.com"
},
builder: { "arch" => "amd64" },
servers: [ "1.1.1.1" ]
servers: [ "1.1.1.1" ],
accessories: {
"db" => {
"image" => "mysql:8.0",
"hosts" => [ "1.1.1.1" ],
"registry" => {
"username" => "user",
"password" => "pw",
"server" => "other.hub.docker.com"
}
}
}
}
end
@@ -19,13 +32,24 @@ class CommandsRegistryTest < ActiveSupport::TestCase
registry.login.join(" ")
end
test "given registry login" do
assert_equal \
"docker login other.hub.docker.com -u \"user\" -p \"pw\"",
registry.login(registry_config: accessory_registry_config).join(" ")
end
test "registry login with ENV password" do
with_test_secrets("secrets" => "KAMAL_REGISTRY_PASSWORD=more-secret") do
with_test_secrets("secrets" => "KAMAL_REGISTRY_PASSWORD=more-secret\nKAMAL_MYSQL_REGISTRY_PASSWORD=secret-pw") do
@config[:registry]["password"] = [ "KAMAL_REGISTRY_PASSWORD" ]
@config[:accessories]["db"]["registry"]["password"] = [ "KAMAL_MYSQL_REGISTRY_PASSWORD" ]
assert_equal \
"docker login hub.docker.com -u \"dhh\" -p \"more-secret\"",
registry.login.join(" ")
assert_equal \
"docker login other.hub.docker.com -u \"user\" -p \"secret-pw\"",
registry.login(registry_config: accessory_registry_config).join(" ")
end
end
@@ -55,8 +79,22 @@ class CommandsRegistryTest < ActiveSupport::TestCase
registry.logout.join(" ")
end
test "given registry logout" do
assert_equal \
"docker logout other.hub.docker.com",
registry.logout(registry_config: accessory_registry_config).join(" ")
end
private
def registry
Kamal::Commands::Registry.new Kamal::Configuration.new(@config)
Kamal::Commands::Registry.new main_config
end
def main_config
Kamal::Configuration.new(@config)
end
def accessory_registry_config
main_config.accessory("db").registry
end
end

View File

@@ -3,7 +3,9 @@ require "test_helper"
class ConfigurationAccessoryTest < ActiveSupport::TestCase
setup do
@deploy = {
service: "app", image: "dhh/app", registry: { "username" => "dhh", "password" => "secret" },
service: "app",
image: "dhh/app",
registry: { "username" => "dhh", "password" => "secret" },
servers: {
"web" => [ "1.1.1.1", "1.1.1.2" ],
"workers" => [ "1.1.1.3", "1.1.1.4" ]
@@ -12,7 +14,7 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase
env: { "REDIS_URL" => "redis://x/y" },
accessories: {
"mysql" => {
"image" => "mysql:8.0",
"image" => "public.registry/mysql:8.0",
"host" => "1.1.1.5",
"port" => "3306",
"env" => {
@@ -52,6 +54,7 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase
"monitoring" => {
"service" => "custom-monitoring",
"image" => "monitoring:latest",
"registry" => { "server" => "other.registry", "username" => "user", "password" => "pw" },
"roles" => [ "web" ],
"port" => "4321:4321",
"labels" => {
@@ -80,6 +83,21 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase
assert_equal "custom-monitoring", @config.accessory(:monitoring).service_name
end
test "image" do
assert_equal "public.registry/mysql:8.0", @config.accessory(:mysql).image
assert_equal "redis:latest", @config.accessory(:redis).image
assert_equal "other.registry/monitoring:latest", @config.accessory(:monitoring).image
end
test "registry" do
assert_nil @config.accessory(:mysql).registry
assert_nil @config.accessory(:redis).registry
monitoring_registry = @config.accessory(:monitoring).registry
assert_equal "other.registry", monitoring_registry.server
assert_equal "user", monitoring_registry.username
assert_equal "pw", monitoring_registry.password
end
test "port" do
assert_equal "3306:3306", @config.accessory(:mysql).port
assert_equal "6379:6379", @config.accessory(:redis).port

View File

@@ -0,0 +1,47 @@
service: app
image: dhh/app
servers:
web:
- "1.1.1.1"
- "1.1.1.2"
workers:
- "1.1.1.3"
- "1.1.1.4"
registry:
server: private.registry
username: user
password: pw
builder:
arch: amd64
accessories:
mysql:
image: private.registry/mysql:5.7
host: 1.1.1.3
port: 3306
env:
clear:
MYSQL_ROOT_HOST: '%'
secret:
- MYSQL_ROOT_PASSWORD
files:
- test/fixtures/files/my.cnf:/etc/mysql/my.cnf
directories:
- data:/var/lib/mysql
redis:
image: redis:latest
roles:
- web
port: 6379
directories:
- data:/data
busybox:
service: custom-box
image: busybox:latest
host: 1.1.1.3
registry:
server: other.registry
username: other_user
password: other_pw
readiness_delay: 0