diff --git a/lib/kamal/cli/accessory.rb b/lib/kamal/cli/accessory.rb index 00999b2d6..c95cbb1e8 100644 --- a/lib/kamal/cli/accessory.rb +++ b/lib/kamal/cli/accessory.rb @@ -292,7 +292,7 @@ def remove_accessory(name) def prepare(name) with_accessory(name) do |accessory, hosts| on(hosts) do - execute *KAMAL.registry.login + execute *KAMAL.registry.login(registry_config: accessory.registry) execute *KAMAL.docker.create_network rescue SSHKit::Command::Failed => e raise unless e.message.include?("already exists") diff --git a/lib/kamal/commands/accessory.rb b/lib/kamal/commands/accessory.rb index 281b87138..77ceb607b 100644 --- a/lib/kamal/commands/accessory.rb +++ b/lib/kamal/commands/accessory.rb @@ -4,11 +4,10 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base attr_reader :accessory_config delegate :service_name, :image, :hosts, :port, :files, :directories, :cmd, :network_args, :publish_args, :env_args, :volume_args, :label_args, :option_args, - :secrets_io, :secrets_path, :env_directory, :proxy, :running_proxy?, + :secrets_io, :secrets_path, :env_directory, :proxy, :running_proxy?, :registry, to: :accessory_config delegate :proxy_container_name, to: :config - def initialize(config, name:) super(config) @accessory_config = config.accessory(name) @@ -42,7 +41,6 @@ def info docker :ps, *service_filter end - def logs(timestamps: true, since: nil, lines: nil, grep: nil, grep_options: nil) pipe \ docker(:logs, service_name, (" --since #{since}" if since), (" --tail #{lines}" if lines), ("--timestamps" if timestamps), "2>&1"), @@ -56,7 +54,6 @@ def follow_logs(timestamps: true, grep: nil, grep_options: nil) (%(grep "#{grep}"#{" #{grep_options}" if grep_options}) if grep) end - def execute_in_existing_container(*command, interactive: false) docker :exec, ("-it" if interactive), @@ -87,7 +84,6 @@ def run_over_ssh(command) super command, host: hosts.first end - def ensure_local_file_present(local_file) if !local_file.is_a?(StringIO) && !Pathname.new(local_file).exist? raise "Missing file: #{local_file}" diff --git a/lib/kamal/commands/registry.rb b/lib/kamal/commands/registry.rb index 69f953608..b17fbf31e 100644 --- a/lib/kamal/commands/registry.rb +++ b/lib/kamal/commands/registry.rb @@ -1,14 +1,16 @@ class Kamal::Commands::Registry < Kamal::Commands::Base - delegate :registry, to: :config + def login(registry_config: nil) + registry_config ||= config.registry - def login docker :login, - registry.server, - "-u", sensitive(Kamal::Utils.escape_shell_value(registry.username)), - "-p", sensitive(Kamal::Utils.escape_shell_value(registry.password)) + registry_config.server, + "-u", sensitive(Kamal::Utils.escape_shell_value(registry_config.username)), + "-p", sensitive(Kamal::Utils.escape_shell_value(registry_config.password)) end - def logout - docker :logout, registry.server + def logout(registry_config: nil) + registry_config ||= config.registry + + docker :logout, registry_config.server end end diff --git a/lib/kamal/configuration.rb b/lib/kamal/configuration.rb index 52c807d7a..827e1204f 100644 --- a/lib/kamal/configuration.rb +++ b/lib/kamal/configuration.rb @@ -59,7 +59,7 @@ def initialize(raw_config, destination: nil, version: nil, validate: true) # Eager load config to validate it, these are first as they have dependencies later on @servers = Servers.new(config: self) - @registry = Registry.new(config: self) + @registry = Registry.new(config: raw_config, secrets: secrets) @accessories = @raw_config.accessories&.keys&.collect { |name| Accessory.new(name, config: self) } || [] @aliases = @raw_config.aliases&.keys&.to_h { |name| [ name, Alias.new(name, config: self) ] } || {} @@ -82,7 +82,6 @@ def initialize(raw_config, destination: nil, version: nil, validate: true) ensure_unique_hosts_for_ssl_roles end - def version=(version) @declared_version = version end @@ -106,7 +105,6 @@ def minimum_version raw_config.minimum_version end - def roles servers.roles end @@ -119,7 +117,6 @@ def accessory(name) accessories.detect { |a| a.name == name.to_s } end - def all_hosts (roles + accessories).flat_map(&:hosts).uniq end @@ -180,7 +177,6 @@ def retain_containers raw_config.retain_containers || 5 end - def volume_args if raw_config.volumes.present? argumentize "--volume", raw_config.volumes @@ -193,7 +189,6 @@ def logging_args logging.args end - def readiness_delay raw_config.readiness_delay || 7 end @@ -206,7 +201,6 @@ def drain_timeout raw_config.drain_timeout || 30 end - def run_directory ".kamal" end @@ -227,7 +221,6 @@ def assets_directory File.join app_directory, "assets" end - def hooks_path raw_config.hooks_path || ".kamal/hooks" end @@ -236,7 +229,6 @@ def asset_path raw_config.asset_path end - def env_tags @env_tags ||= if (tags = raw_config.env["tags"]) tags.collect { |name, config| Env::Tag.new(name, config: config, secrets: secrets) } @@ -277,7 +269,6 @@ def proxy_options_file File.join proxy_directory, "options" end - def to_h { roles: role_names, diff --git a/lib/kamal/configuration/accessory.rb b/lib/kamal/configuration/accessory.rb index 198e6321e..ccb845fdb 100644 --- a/lib/kamal/configuration/accessory.rb +++ b/lib/kamal/configuration/accessory.rb @@ -5,7 +5,7 @@ class Kamal::Configuration::Accessory delegate :argumentize, :optionize, to: Kamal::Utils - attr_reader :name, :accessory_config, :env, :proxy + attr_reader :name, :env, :proxy, :registry def initialize(name, config:) @name, @config, @accessory_config = name.inquiry, config, config.raw_config["accessories"][name] @@ -16,12 +16,9 @@ def initialize(name, config:) context: "accessories/#{name}", with: Kamal::Configuration::Validator::Accessory - @env = Kamal::Configuration::Env.new \ - config: accessory_config.fetch("env", {}), - secrets: config.secrets, - context: "accessories/#{name}/env" - - initialize_proxy if running_proxy? + @env = initialize_env + @proxy = initialize_proxy if running_proxy? + @registry = initialize_registry if accessory_config["registry"].present? end def service_name @@ -29,7 +26,7 @@ def service_name end def image - accessory_config["image"] + [ registry&.server, accessory_config["image"] ].compact.join("/") end def hosts @@ -109,18 +106,32 @@ def cmd end def running_proxy? - @accessory_config["proxy"].present? - end - - def initialize_proxy - @proxy = Kamal::Configuration::Proxy.new \ - config: config, - proxy_config: accessory_config["proxy"], - context: "accessories/#{name}/proxy" + accessory_config["proxy"].present? end private - attr_accessor :config + attr_reader :config, :accessory_config + + def initialize_env + Kamal::Configuration::Env.new \ + config: accessory_config.fetch("env", {}), + secrets: config.secrets, + context: "accessories/#{name}/env" + end + + def initialize_proxy + Kamal::Configuration::Proxy.new \ + config: config, + proxy_config: accessory_config["proxy"], + context: "accessories/#{name}/proxy" + end + + def initialize_registry + Kamal::Configuration::Registry.new \ + config: accessory_config, + secrets: config.secrets, + context: "accessories/#{name}/registry" + end def default_labels { "service" => service_name } diff --git a/lib/kamal/configuration/docs/accessory.yml b/lib/kamal/configuration/docs/accessory.yml index b82a476eb..571d72170 100644 --- a/lib/kamal/configuration/docs/accessory.yml +++ b/lib/kamal/configuration/docs/accessory.yml @@ -23,9 +23,27 @@ accessories: # Image # - # The Docker image to use, prefix it with a registry if not using Docker Hub: + # The Docker image to use. + # Prefix it with its server when using root level registry different from Docker Hub. + # Define registry directly or via anchors when it differs from root level registry. image: mysql:8.0 + # Registry + # + # By default accessories use Docker Hub registry. + # You can specify different registry per accessory with this option. + # Don't prefix image with this registry server. + # Use anchors if you need to set the same specific registry for several accessories. + # + # ```yml + # registry: + # <<: *specific-registry + # ``` + # + # See kamal docs registry for more information: + registry: + ... + # Accessory hosts # # Specify one of `host`, `hosts`, or `roles`: diff --git a/lib/kamal/configuration/registry.rb b/lib/kamal/configuration/registry.rb index 763cf976a..d3fba5157 100644 --- a/lib/kamal/configuration/registry.rb +++ b/lib/kamal/configuration/registry.rb @@ -1,12 +1,10 @@ class Kamal::Configuration::Registry include Kamal::Configuration::Validation - 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 + def initialize(config:, secrets:, context: "registry") + @registry_config = config["registry"] || {} + @secrets = secrets + validate! registry_config, context: context, with: Kamal::Configuration::Validator::Registry end def server @@ -22,6 +20,8 @@ def password end private + attr_reader :registry_config, :secrets + def lookup(key) if registry_config[key].is_a?(Array) secrets[registry_config[key].first] diff --git a/test/cli/accessory_test.rb b/test/cli/accessory_test.rb index 05431fcbd..cc517e595 100644 --- a/test/cli/accessory_test.rb +++ b/test/cli/accessory_test.rb @@ -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 diff --git a/test/commands/accessory_test.rb b/test/commands/accessory_test.rb index b9bcca7e3..6ff9902ea 100644 --- a/test/commands/accessory_test.rb +++ b/test/commands/accessory_test.rb @@ -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", diff --git a/test/commands/registry_test.rb b/test/commands/registry_test.rb index cf2734b72..0a71b1dab 100755 --- a/test/commands/registry_test.rb +++ b/test/commands/registry_test.rb @@ -2,14 +2,27 @@ 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 diff --git a/test/configuration/accessory_test.rb b/test/configuration/accessory_test.rb index d15a48ad3..fc01dc904 100644 --- a/test/configuration/accessory_test.rb +++ b/test/configuration/accessory_test.rb @@ -3,7 +3,9 @@ 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 diff --git a/test/fixtures/deploy_with_accessories_with_different_registries.yml b/test/fixtures/deploy_with_accessories_with_different_registries.yml new file mode 100644 index 000000000..8bd8f1d9f --- /dev/null +++ b/test/fixtures/deploy_with_accessories_with_different_registries.yml @@ -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