Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion lib/kamal/cli/main.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,25 @@ def deploy(boot_accessories: false)
say "Ensure kamal-proxy is running...", :magenta
invoke "kamal:cli:proxy:boot", [], invoke_options

invoke "kamal:cli:accessory:boot", [ "all" ], invoke_options if boot_accessories
# Boot non-proxied accessories first to avoid error pages directory validation issues on fresh servers.
# Proxied accessories will be booted after app:boot to ensure error pages are available.
# Each accessory boots in a separate modify(lock:true) block; locks are released between groups.
Comment thread
TotyLabs marked this conversation as resolved.
Outdated
if boot_accessories
non_proxied_accessories = KAMAL.accessory_names.filter { |name| !KAMAL.accessory(name).running_proxy? }
non_proxied_accessories.each { |name| invoke "kamal:cli:accessory:boot", [ name ], invoke_options }
end

say "Detect stale containers...", :magenta
invoke "kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true)

invoke "kamal:cli:app:boot", [], invoke_options

# Boot proxied accessories after app:boot to ensure error pages directory exists
if boot_accessories
proxied_accessories = KAMAL.accessory_names.filter { |name| KAMAL.accessory(name).running_proxy? }
proxied_accessories.each { |name| invoke "kamal:cli:accessory:boot", [ name ], invoke_options }
end

say "Prune old containers and images...", :magenta
invoke "kamal:cli:prune:all", [], invoke_options
end
Expand Down
29 changes: 25 additions & 4 deletions test/cli/main_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ class CliMainTest < CliTestCase
invoke_options = base_invoke_options

Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:server:bootstrap", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:accessory:boot", [ "all" ], invoke_options)
# deploy
# deploy (deploy_simple.yml has no accessories, so no accessory:boot invocations expected)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:build:pull", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:proxy:boot", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true))
Expand Down Expand Up @@ -67,8 +66,7 @@ class CliMainTest < CliTestCase
invoke_options = base_invoke_options(no_cache: true)

Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:server:bootstrap", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:accessory:boot", [ "all" ], invoke_options)
# deploy
# deploy (deploy_simple.yml has no accessories, so no accessory:boot invocations expected)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:build:deliver", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:proxy:boot", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true))
Expand All @@ -85,6 +83,29 @@ class CliMainTest < CliTestCase
end
end

test "setup with error pages and proxied accessory boots accessories in correct order" do
invoke_options = base_invoke_options(config_file: "deploy_with_error_pages_and_accessories.yml")

Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:server:bootstrap", [], invoke_options)
# Non-proxied accessory (mysql) should be booted first
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:accessory:boot", [ "mysql" ], invoke_options)
# deploy
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:build:deliver", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:proxy:boot", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true))
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:boot", [], invoke_options)
# Proxied accessory (cache) should be booted after app:boot to ensure error pages directory exists
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:accessory:boot", [ "cache" ], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:prune:all", [], invoke_options)
Comment thread
TotyLabs marked this conversation as resolved.

run_command("setup", config_file: "deploy_with_error_pages_and_accessories.yml").tap do |output|
assert_match /Ensure Docker is installed.../, output
assert_match /Build and push app image/, output
assert_match /Ensure kamal-proxy is running/, output
assert_match /Prune old containers and images/, output
end
end

test "deploy" do
with_test_secrets("secrets" => "DB_PASSWORD=secret") do
invoke_options = base_invoke_options(verbose: true)
Expand Down
25 changes: 25 additions & 0 deletions test/fixtures/deploy_with_error_pages_and_accessories.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
service: app
image: dhh/app
servers:
- "1.1.1.1"
registry:
username: user
password: pw
builder:
arch: amd64

accessories:
mysql:
image: mysql:5.7
host: 1.1.1.1
port: 3306
cache:
image: redis:7-alpine
host: 1.1.1.1
port: 6379:6379
proxy:
host: cache.example.com
path_prefix: "/cache"
app_port: 6379

error_pages_path: public