diff --git a/lib/kamal/cli/app.rb b/lib/kamal/cli/app.rb index 2a02ed65..d70d066b 100644 --- a/lib/kamal/cli/app.rb +++ b/lib/kamal/cli/app.rb @@ -47,7 +47,7 @@ class Kamal::Cli::App < Kamal::Cli::Base endpoint = capture_with_info(*app.container_id_for_version(version)).strip raise Kamal::Cli::BootError, "Failed to get endpoint for #{role} on #{host}, did the container boot?" if endpoint.empty? - execute *KAMAL.proxy.deploy(role.container_prefix, target: endpoint) + execute *app.deploy(target: endpoint) end end end @@ -68,7 +68,7 @@ class Kamal::Cli::App < Kamal::Cli::Base version = capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip endpoint = capture_with_info(*app.container_id_for_version(version)).strip if endpoint.present? - execute *KAMAL.proxy.remove(role.container_prefix, target: endpoint), raise_on_non_zero_exit: false + execute *app.remove(target: endpoint), raise_on_non_zero_exit: false end end diff --git a/lib/kamal/cli/app/boot.rb b/lib/kamal/cli/app/boot.rb index 6939825a..fd330c71 100644 --- a/lib/kamal/cli/app/boot.rb +++ b/lib/kamal/cli/app/boot.rb @@ -54,7 +54,7 @@ class Kamal::Cli::App::Boot if running_proxy? endpoint = capture_with_info(*app.container_id_for_version(version)).strip raise Kamal::Cli::BootError, "Failed to get endpoint for #{role} on #{host}, did the container boot?" if endpoint.empty? - execute *KAMAL.proxy.deploy(role.container_prefix, target: endpoint) + execute *app.deploy(target: endpoint) else Kamal::Cli::Healthcheck::Poller.wait_for_healthy(pause_after_ready: true) { capture_with_info(*app.status(version: version)) } end diff --git a/lib/kamal/cli/proxy.rb b/lib/kamal/cli/proxy.rb index 12d4c6b0..f48faf0a 100644 --- a/lib/kamal/cli/proxy.rb +++ b/lib/kamal/cli/proxy.rb @@ -13,8 +13,8 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base version = capture_with_info(*KAMAL.proxy.version).strip.presence - if version && Kamal::Utils.older_version?(version, Kamal::Configuration::Proxy::MINIMUM_VERSION) - raise "kamal-proxy version #{version} is too old, please reboot to update to at least #{Kamal::Configuration::Proxy::MINIMUM_VERSION}" + if version && Kamal::Utils.older_version?(version, Kamal::Configuration::PROXY_MINIMUM_VERSION) + raise "kamal-proxy version #{version} is too old, please reboot to update to at least #{Kamal::Configuration::PROXY_MINIMUM_VERSION}" end execute *KAMAL.proxy.start_or_run end @@ -52,7 +52,7 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base if endpoint.present? info "Deploying #{endpoint} for role `#{role}` on #{host}..." - execute *KAMAL.proxy.deploy(role.container_prefix, target: endpoint) + execute *app.deploy(target: endpoint) end end end diff --git a/lib/kamal/commands/app.rb b/lib/kamal/commands/app.rb index b6e35d66..6d8f44c6 100644 --- a/lib/kamal/commands/app.rb +++ b/lib/kamal/commands/app.rb @@ -1,5 +1,5 @@ class Kamal::Commands::App < Kamal::Commands::Base - include Assets, Containers, Execution, Images, Logging + include Assets, Containers, Execution, Images, Logging, Proxy ACTIVE_DOCKER_STATUSES = [ :running, :restarting ] diff --git a/lib/kamal/commands/app/proxy.rb b/lib/kamal/commands/app/proxy.rb new file mode 100644 index 00000000..550e2e3b --- /dev/null +++ b/lib/kamal/commands/app/proxy.rb @@ -0,0 +1,16 @@ +module Kamal::Commands::App::Proxy + delegate :proxy_container_name, to: :config + + def deploy(target:) + proxy_exec :deploy, role.container_prefix, *role.proxy.deploy_command_args(target: target) + end + + def remove(target:) + proxy_exec :remove, role.container_prefix, *role.proxy.remove_command_args(target: target) + end + + private + def proxy_exec(*command) + docker :exec, proxy_container_name, "kamal-proxy", *command + end +end diff --git a/lib/kamal/commands/proxy.rb b/lib/kamal/commands/proxy.rb index 354493db..aa347e2a 100644 --- a/lib/kamal/commands/proxy.rb +++ b/lib/kamal/commands/proxy.rb @@ -1,13 +1,5 @@ class Kamal::Commands::Proxy < Kamal::Commands::Base delegate :argumentize, :optionize, to: Kamal::Utils - delegate :container_name, :app_port, to: :proxy_config - - attr_reader :proxy_config - - def initialize(config) - super - @proxy_config = config.proxy - end def run docker :run, @@ -15,11 +7,11 @@ class Kamal::Commands::Proxy < Kamal::Commands::Base "--network", "kamal", "--detach", "--restart", "unless-stopped", - *proxy_config.publish_args, + *config.proxy_publish_args, "--volume", "/var/run/docker.sock:/var/run/docker.sock", - *proxy_config.config_volume.docker_args, + *config.proxy_config_volume.docker_args, *config.logging_args, - proxy_config.image + config.proxy_image end def start @@ -34,14 +26,6 @@ class Kamal::Commands::Proxy < Kamal::Commands::Base combine start, run, by: "||" end - def deploy(service, target:) - docker :exec, container_name, "kamal-proxy", :deploy, service, *optionize({ target: "#{target}:#{app_port}" }), *proxy_config.deploy_command_args - end - - def remove(service, target:) - docker :exec, container_name, "kamal-proxy", :remove, service, *optionize({ target: "#{target}:#{app_port}" }) - end - def info docker :ps, "--filter", "name=^#{container_name}$" end @@ -85,4 +69,9 @@ class Kamal::Commands::Proxy < Kamal::Commands::Base docker(:image, :prune, "--all", "--force", "--filter", "label=org.opencontainers.image.title=Traefik") ) end + + private + def container_name + config.proxy_container_name + end end diff --git a/lib/kamal/configuration.rb b/lib/kamal/configuration.rb index f6051ca7..b28152bc 100644 --- a/lib/kamal/configuration.rb +++ b/lib/kamal/configuration.rb @@ -14,6 +14,10 @@ class Kamal::Configuration include Validation + PROXY_MINIMUM_VERSION = "v0.3.0" + PROXY_HTTP_PORT = 80 + PROXY_HTTPS_PORT = 443 + class << self def create_from(config_file:, destination: nil, version: nil) raw_config = load_config_files(config_file, *destination_config_file(config_file, destination)) @@ -61,7 +65,7 @@ class Kamal::Configuration @env = Env.new(config: @raw_config.env || {}, secrets: secrets) @logging = Logging.new(logging_config: @raw_config.logging) - @proxy = Proxy.new(config: self) + @proxy = Proxy.new(config: self, proxy_config: @raw_config.proxy || {}) @ssh = Ssh.new(config: self) @sshkit = Sshkit.new(config: self) @@ -244,6 +248,24 @@ class Kamal::Configuration env_tags.detect { |t| t.name == name.to_s } end + def proxy_publish_args + argumentize "--publish", [ "#{PROXY_HTTP_PORT}:#{PROXY_HTTP_PORT}", "#{PROXY_HTTPS_PORT}:#{PROXY_HTTPS_PORT}" ] + end + + def proxy_image + "basecamp/kamal-proxy:#{PROXY_MINIMUM_VERSION}" + end + + def proxy_container_name + "kamal-proxy" + end + + def proxy_config_volume + Kamal::Configuration::Volume.new \ + host_path: File.join(proxy_directory, "config"), + container_path: "/home/kamal-proxy/.config/kamal-proxy" + end + def to_h { diff --git a/lib/kamal/configuration/docs/proxy.yml b/lib/kamal/configuration/docs/proxy.yml index 85870d14..332a2c28 100644 --- a/lib/kamal/configuration/docs/proxy.yml +++ b/lib/kamal/configuration/docs/proxy.yml @@ -9,6 +9,12 @@ # # They are application specific, so are not shared when multiple applications # run on the same proxy. +# +# The proxy is enabled by default on the primary role, but can be disabled by +# setting `proxy: false`. +# +# It is disabled by default on all other roles, but can be enabled by setting +# `proxy: true`, or providing a proxy configuration. proxy: # Host diff --git a/lib/kamal/configuration/docs/role.yml b/lib/kamal/configuration/docs/role.yml index 0f75c550..fbc8a813 100644 --- a/lib/kamal/configuration/docs/role.yml +++ b/lib/kamal/configuration/docs/role.yml @@ -35,13 +35,14 @@ servers: hosts: - 172.1.0.3 - 172.1.0.4: experiment1 - proxy: true cmd: "bin/jobs" options: memory: 2g cpus: 4 logging: ... + proxy: + ... labels: my-label: workers env: diff --git a/lib/kamal/configuration/proxy.rb b/lib/kamal/configuration/proxy.rb index 973442ab..551601c4 100644 --- a/lib/kamal/configuration/proxy.rb +++ b/lib/kamal/configuration/proxy.rb @@ -1,36 +1,23 @@ class Kamal::Configuration::Proxy include Kamal::Configuration::Validation - MINIMUM_VERSION = "v0.3.0" - DEFAULT_HTTP_PORT = 80 - DEFAULT_HTTPS_PORT = 443 - DEFAULT_IMAGE = "basecamp/kamal-proxy:#{MINIMUM_VERSION}" DEFAULT_LOG_REQUEST_HEADERS = [ "Cache-Control", "Last-Modified", "User-Agent" ] + CONTAINER_NAME = "kamal-proxy" delegate :argumentize, :optionize, to: Kamal::Utils - def initialize(config:) + attr_reader :config, :proxy_config + + def initialize(config:, proxy_config:, context: "proxy") @config = config - @proxy_config = config.raw_config.proxy || {} - validate! proxy_config, with: Kamal::Configuration::Validator::Proxy + @proxy_config = proxy_config + validate! @proxy_config, with: Kamal::Configuration::Validator::Proxy, context: context end def app_port proxy_config.fetch("app_port", 80) end - def image - proxy_config.fetch("image", DEFAULT_IMAGE) - end - - def container_name - "kamal-proxy" - end - - def publish_args - argumentize "--publish", [ "#{DEFAULT_HTTP_PORT}:#{DEFAULT_HTTP_PORT}", "#{DEFAULT_HTTPS_PORT}:#{DEFAULT_HTTPS_PORT}" ] - end - def ssl? proxy_config.fetch("ssl", false) end @@ -56,19 +43,19 @@ class Kamal::Configuration::Proxy }.compact end - def deploy_command_args - optionize deploy_options + def deploy_command_args(target:) + optionize ({ target: "#{target}:#{app_port}" }).merge(deploy_options) end - def config_volume - Kamal::Configuration::Volume.new \ - host_path: File.join(config.proxy_directory, "config"), - container_path: "/home/kamal-proxy/.config/kamal-proxy" + def remove_command_args(target:) + optionize({ target: "#{target}:#{app_port}" }) + end + + def merge(other) + self.class.new config: config, proxy_config: proxy_config.deep_merge(other.proxy_config) end private - attr_reader :config, :proxy_config - def seconds_duration(value) value ? "#{value}s" : nil end diff --git a/lib/kamal/configuration/role.rb b/lib/kamal/configuration/role.rb index 9d559317..76305312 100644 --- a/lib/kamal/configuration/role.rb +++ b/lib/kamal/configuration/role.rb @@ -3,7 +3,7 @@ class Kamal::Configuration::Role delegate :argumentize, :optionize, to: Kamal::Utils - attr_reader :name, :config, :specialized_env, :specialized_logging + attr_reader :name, :config, :specialized_env, :specialized_logging, :specialized_proxy alias to_s name @@ -23,6 +23,8 @@ class Kamal::Configuration::Role @specialized_logging = Kamal::Configuration::Logging.new \ logging_config: specializations.fetch("logging", {}), context: "servers/#{name}/logging" + + initialize_specialized_proxy end def primary_host @@ -65,6 +67,14 @@ class Kamal::Configuration::Role @logging ||= config.logging.merge(specialized_logging) end + def proxy + @proxy ||= config.proxy.merge(specialized_proxy) if running_proxy? + end + + def running_proxy? + @running_proxy + end + def stop_args # When deploying with the proxy, kamal-proxy will drain request before returning so we don't need to wait. timeout = running_proxy? ? nil : config.drain_timeout @@ -98,16 +108,8 @@ class Kamal::Configuration::Role end - def running_proxy? - if specializations["proxy"].nil? - primary? - else - specializations["proxy"] - end - end - def primary? - self == @config.primary_role + name == @config.primary_role_name end @@ -144,6 +146,27 @@ class Kamal::Configuration::Role end private + def initialize_specialized_proxy + proxy_specializations = specializations["proxy"] + + if primary? + # only false means no proxy for non-primary roles + @running_proxy = proxy_specializations != false + else + # false and nil both mean no proxy for non-primary roles + @running_proxy = !!proxy_specializations + end + + if running_proxy? + proxy_config = proxy_specializations == true || proxy_specializations.nil? ? {} : proxy_specializations + + @specialized_proxy = Kamal::Configuration::Proxy.new \ + config: config, + proxy_config: proxy_config, + context: "servers/#{name}/proxy" + end + end + def tagged_hosts {}.tap do |tagged_hosts| extract_hosts_from_config.map do |host_config| diff --git a/lib/kamal/configuration/validator.rb b/lib/kamal/configuration/validator.rb index 2ac8482d..0d46e4ae 100644 --- a/lib/kamal/configuration/validator.rb +++ b/lib/kamal/configuration/validator.rb @@ -24,7 +24,9 @@ class Kamal::Configuration::Validator example_value = example[key] if example_value == "..." - validate_type! value, *(Array if key == :servers), Hash + unless key.to_s == "proxy" && boolean?(value.class) + validate_type! value, *(Array if key == :servers), Hash + end elsif key == "hosts" validate_servers! value elsif example_value.is_a?(Array) diff --git a/lib/kamal/configuration/validator/proxy.rb b/lib/kamal/configuration/validator/proxy.rb index a4ee19bf..bf2e5e9e 100644 --- a/lib/kamal/configuration/validator/proxy.rb +++ b/lib/kamal/configuration/validator/proxy.rb @@ -1,9 +1,11 @@ class Kamal::Configuration::Validator::Proxy < Kamal::Configuration::Validator def validate! - super + unless config.nil? + super - if config["host"].blank? && config["ssl"] - error "Must set a host to enable automatic SSL" + if config["host"].blank? && config["ssl"] + error "Must set a host to enable automatic SSL" + end end end end diff --git a/test/cli/app_test.rb b/test/cli/app_test.rb index 5fdf61c5..3cb61bf0 100644 --- a/test/cli/app_test.rb +++ b/test/cli/app_test.rb @@ -388,6 +388,15 @@ class CliAppTest < CliTestCase end end + test "boot proxy with role specific config" do + SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info).returns("123") # old version + + run_command("boot", config: :with_proxy_roles, host: nil).tap do |output| + assert_match "docker exec kamal-proxy kamal-proxy deploy app-web --target \"123:80\" --deploy-timeout \"6s\" --drain-timeout \"30s\" --target-timeout \"10s\" --buffer-requests --buffer-responses --log-request-header \"Cache-Control\" --log-request-header \"Last-Modified\" --log-request-header \"User-Agent\"", output + assert_match "docker exec kamal-proxy kamal-proxy deploy app-web2 --target \"123:80\" --deploy-timeout \"6s\" --drain-timeout \"30s\" --target-timeout \"15s\" --buffer-requests --buffer-responses --log-request-header \"Cache-Control\" --log-request-header \"Last-Modified\" --log-request-header \"User-Agent\"", output + end + end + private def run_command(*command, config: :with_accessories, host: "1.1.1.1", allow_execute_error: false) stdouted do diff --git a/test/cli/proxy_test.rb b/test/cli/proxy_test.rb index 0296723b..dfc3aff9 100644 --- a/test/cli/proxy_test.rb +++ b/test/cli/proxy_test.rb @@ -4,7 +4,7 @@ class CliProxyTest < CliTestCase test "boot" do run_command("boot").tap do |output| assert_match "docker login", output - assert_match "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}", output + assert_match "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-opt max-size=\"10m\" #{KAMAL.config.proxy_image}", output end end @@ -18,11 +18,11 @@ class CliProxyTest < CliTestCase exception = assert_raises do run_command("boot").tap do |output| assert_match "docker login", output - assert_match "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}", output + assert_match "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-opt max-size=\"10m\" #{KAMAL.config.proxy_image}", output end end - assert_includes exception.message, "kamal-proxy version v0.0.1 is too old, please reboot to update to at least #{Kamal::Configuration::Proxy::MINIMUM_VERSION}" + assert_includes exception.message, "kamal-proxy version v0.0.1 is too old, please reboot to update to at least #{Kamal::Configuration::PROXY_MINIMUM_VERSION}" ensure Thread.report_on_exception = false end @@ -31,12 +31,12 @@ class CliProxyTest < CliTestCase Thread.report_on_exception = false SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) .with(:docker, :inspect, "kamal-proxy", "--format '{{.Config.Image}}'", "|", :cut, "-d:", "-f2") - .returns(Kamal::Configuration::Proxy::MINIMUM_VERSION) + .returns(Kamal::Configuration::PROXY_MINIMUM_VERSION) .at_least_once run_command("boot").tap do |output| assert_match "docker login", output - assert_match "docker container start kamal-proxy || docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}", output + assert_match "docker container start kamal-proxy || docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-opt max-size=\"10m\" #{KAMAL.config.proxy_image}", output end ensure Thread.report_on_exception = false @@ -57,13 +57,13 @@ class CliProxyTest < CliTestCase assert_match "docker container stop kamal-proxy on 1.1.1.1", output assert_match "Running docker container stop traefik ; docker container prune --force --filter label=org.opencontainers.image.title=Traefik && docker image prune --all --force --filter label=org.opencontainers.image.title=Traefik on 1.1.1.1", output assert_match "docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy on 1.1.1.1", output - assert_match "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE} on 1.1.1.1", output + assert_match "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-opt max-size=\"10m\" #{KAMAL.config.proxy_image} on 1.1.1.1", output assert_match "docker exec kamal-proxy kamal-proxy deploy app-web --target \"abcdefabcdef:80\" --deploy-timeout \"6s\" --drain-timeout \"30s\" --buffer-requests --buffer-responses --log-request-header \"Cache-Control\" --log-request-header \"Last-Modified\" --log-request-header \"User-Agent\" on 1.1.1.1", output assert_match "docker container stop kamal-proxy on 1.1.1.2", output assert_match "Running docker container stop traefik ; docker container prune --force --filter label=org.opencontainers.image.title=Traefik && docker image prune --all --force --filter label=org.opencontainers.image.title=Traefik on 1.1.1.2", output assert_match "docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy on 1.1.1.2", output - assert_match "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE} on 1.1.1.2", output + assert_match "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-opt max-size=\"10m\" #{KAMAL.config.proxy_image} on 1.1.1.2", output assert_match "docker exec kamal-proxy kamal-proxy deploy app-web --target \"abcdefabcdef:80\" --deploy-timeout \"6s\" --drain-timeout \"30s\" --buffer-requests --buffer-responses --log-request-header \"Cache-Control\" --log-request-header \"Last-Modified\" --log-request-header \"User-Agent\" on 1.1.1.2", output end end @@ -189,7 +189,7 @@ class CliProxyTest < CliTestCase SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) .with(:docker, :inspect, "kamal-proxy", "--format '{{.Config.Image}}'", "|", :cut, "-d:", "-f2") - .returns(Kamal::Configuration::Proxy::MINIMUM_VERSION) + .returns(Kamal::Configuration::PROXY_MINIMUM_VERSION) SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) .with(:docker, :container, :ls, "--all", "--filter", "name=^app-workers-latest$", "--quiet", "|", :xargs, :docker, :inspect, "--format", "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'") @@ -205,7 +205,7 @@ class CliProxyTest < CliTestCase assert_match "/usr/bin/env mkdir -p .kamal", output assert_match "docker network create kamal", output assert_match "docker login -u [REDACTED] -p [REDACTED]", output - assert_match "docker container start kamal-proxy || docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-opt max-size=\"10m\" basecamp/kamal-proxy:#{Kamal::Configuration::Proxy::MINIMUM_VERSION}", output + assert_match "docker container start kamal-proxy || docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-opt max-size=\"10m\" basecamp/kamal-proxy:#{Kamal::Configuration::PROXY_MINIMUM_VERSION}", output assert_match "/usr/bin/env mkdir -p .kamal", output assert_match %r{docker rename app-web-latest app-web-latest_replaced_.*}, output assert_match "/usr/bin/env mkdir -p .kamal/apps/app/env/roles", output @@ -228,7 +228,7 @@ class CliProxyTest < CliTestCase SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) .with(:docker, :inspect, "kamal-proxy", "--format '{{.Config.Image}}'", "|", :cut, "-d:", "-f2") - .returns(Kamal::Configuration::Proxy::MINIMUM_VERSION) + .returns(Kamal::Configuration::PROXY_MINIMUM_VERSION) SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) .with(:docker, :container, :ls, "--all", "--filter", "name=^app-workers-latest$", "--quiet", "|", :xargs, :docker, :inspect, "--format", "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'") diff --git a/test/commands/app_test.rb b/test/commands/app_test.rb index bb78d8fa..67810a6e 100644 --- a/test/commands/app_test.rb +++ b/test/commands/app_test.rb @@ -113,6 +113,19 @@ class CommandsAppTest < ActiveSupport::TestCase new_command.info.join(" ") end + test "deploy" do + assert_equal \ + "docker exec kamal-proxy kamal-proxy deploy app-web --target \"172.1.0.2:80\" --deploy-timeout \"30s\" --drain-timeout \"30s\" --buffer-requests --buffer-responses --log-request-header \"Cache-Control\" --log-request-header \"Last-Modified\" --log-request-header \"User-Agent\"", + new_command.deploy(target: "172.1.0.2").join(" ") + end + + test "remove" do + assert_equal \ + "docker exec kamal-proxy kamal-proxy remove app-web --target \"172.1.0.2:80\"", + new_command.remove(target: "172.1.0.2").join(" ") + end + + test "logs" do assert_equal \ diff --git a/test/commands/proxy_test.rb b/test/commands/proxy_test.rb index 6f331719..f0be198a 100644 --- a/test/commands/proxy_test.rb +++ b/test/commands/proxy_test.rb @@ -15,13 +15,13 @@ class CommandsProxyTest < ActiveSupport::TestCase test "run" do assert_equal \ - "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}", + "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-opt max-size=\"10m\" #{KAMAL.config.proxy_image}", new_command.run.join(" ") end test "run with ports configured" do assert_equal \ - "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}", + "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-opt max-size=\"10m\" #{KAMAL.config.proxy_image}", new_command.run.join(" ") end @@ -29,7 +29,7 @@ class CommandsProxyTest < ActiveSupport::TestCase @config.delete(:proxy) assert_equal \ - "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}", + "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-opt max-size=\"10m\" #{KAMAL.config.proxy_image}", new_command.run.join(" ") end @@ -37,7 +37,7 @@ class CommandsProxyTest < ActiveSupport::TestCase @config[:logging] = { "driver" => "local", "options" => { "max-size" => "100m", "max-file" => "3" } } assert_equal \ - "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-driver \"local\" --log-opt max-size=\"100m\" --log-opt max-file=\"3\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}", + "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/proxy/config:/home/kamal-proxy/.config/kamal-proxy --log-driver \"local\" --log-opt max-size=\"100m\" --log-opt max-file=\"3\" #{KAMAL.config.proxy_image}", new_command.run.join(" ") end @@ -107,18 +107,6 @@ class CommandsProxyTest < ActiveSupport::TestCase new_command.follow_logs(host: @config[:servers].first, grep: "hello!") end - test "deploy" do - assert_equal \ - "docker exec kamal-proxy kamal-proxy deploy service --target \"172.1.0.2:80\" --deploy-timeout \"30s\" --drain-timeout \"30s\" --buffer-requests --buffer-responses --log-request-header \"Cache-Control\" --log-request-header \"Last-Modified\" --log-request-header \"User-Agent\"", - new_command.deploy("service", target: "172.1.0.2").join(" ") - end - - test "remove" do - assert_equal \ - "docker exec kamal-proxy kamal-proxy remove service --target \"172.1.0.2:80\"", - new_command.remove("service", target: "172.1.0.2").join(" ") - end - test "version" do assert_equal \ "docker inspect kamal-proxy --format '{{.Config.Image}}' | cut -d: -f2", diff --git a/test/configuration/role_test.rb b/test/configuration/role_test.rb index a15fed7d..c7a94de0 100644 --- a/test/configuration/role_test.rb +++ b/test/configuration/role_test.rb @@ -250,6 +250,14 @@ class ConfigurationRoleTest < ActiveSupport::TestCase assert_equal [ "-t", 30 ], config_with_roles.role(:workers).stop_args end + test "role specific proxy config" do + @deploy_with_roles[:proxy] = { "response_timeout" => 15 } + @deploy_with_roles[:servers]["workers"]["proxy"] = { "response_timeout" => 18 } + + assert_equal "15s", config_with_roles.role(:web).proxy.deploy_options[:"target-timeout"] + assert_equal "18s", config_with_roles.role(:workers).proxy.deploy_options[:"target-timeout"] + end + private def config Kamal::Configuration.new(@deploy) diff --git a/test/fixtures/deploy_primary_web_role_override.yml b/test/fixtures/deploy_primary_web_role_override.yml index 6bc2f837..4d90ce58 100644 --- a/test/fixtures/deploy_primary_web_role_override.yml +++ b/test/fixtures/deploy_primary_web_role_override.yml @@ -2,12 +2,12 @@ service: app image: dhh/app servers: web_chicago: - proxy: true + proxy: {} hosts: - 1.1.1.1 - 1.1.1.2 web_tokyo: - proxy: true + proxy: {} hosts: - 1.1.1.3 - 1.1.1.4 diff --git a/test/fixtures/deploy_with_extensions.yml b/test/fixtures/deploy_with_extensions.yml index c45104de..cbbc2e78 100644 --- a/test/fixtures/deploy_with_extensions.yml +++ b/test/fixtures/deploy_with_extensions.yml @@ -1,6 +1,6 @@ x-web: &web - proxy: true + proxy: {} service: app image: dhh/app diff --git a/test/fixtures/deploy_with_proxy_roles.yml b/test/fixtures/deploy_with_proxy_roles.yml new file mode 100644 index 00000000..73211213 --- /dev/null +++ b/test/fixtures/deploy_with_proxy_roles.yml @@ -0,0 +1,46 @@ +service: app +image: dhh/app +servers: + web: + hosts: + - "1.1.1.1" + - "1.1.1.2" + web2: + hosts: + - "1.1.1.3" + - "1.1.1.4" + proxy: + response_timeout: 15 +registry: + username: user + password: pw +builder: + arch: amd64 + +proxy: + response_timeout: 10 + +accessories: + mysql: + image: 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 + +readiness_delay: 0 +deploy_timeout: 6