Configurable Kamal directory

To avoid polluting the default SSH directory with lots of Kamal config,
we'll default to putting them in a `kamal` sub directory.

But also make the directory configurable with the `run_directory` key,
so for example you can set it as `/var/run/kamal/`

The directory is created during bootstrap or before any command that
will need to access a file.
This commit is contained in:
Donal McBreen
2023-08-28 16:12:56 +01:00
parent 9363b6a464
commit bcfa1d83e8
17 changed files with 101 additions and 23 deletions

View File

@@ -20,7 +20,7 @@ class CliBuildTest < CliTestCase
end
test "push without builder" do
stub_locking
stub_setup
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with(:docker, "--version", "&&", :docker, :buildx, "version")
@@ -36,7 +36,7 @@ class CliBuildTest < CliTestCase
end
test "push with no buildx plugin" do
stub_locking
stub_setup
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with(:docker, "--version", "&&", :docker, :buildx, "version")
.raises(SSHKit::Command::Failed.new("no buildx"))
@@ -67,7 +67,7 @@ class CliBuildTest < CliTestCase
end
test "create with error" do
stub_locking
stub_setup
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |arg| arg == :docker }
.raises(SSHKit::Command::Failed.new("stderr=error"))

View File

@@ -27,11 +27,13 @@ class CliTestCase < ActiveSupport::TestCase
.raises(SSHKit::Command::Failed.new("failed"))
end
def stub_locking
def stub_setup
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |arg1, arg2| arg1 == :mkdir && arg2 == "kamal_lock-app" }
.with { |*args| args == [ :mkdir, "-p", "kamal" ] }
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |arg1, arg2| arg1 == :rm && arg2 == "kamal_lock-app/details" }
.with { |arg1, arg2| arg1 == :mkdir && arg2 == "kamal/lock-app" }
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |arg1, arg2| arg1 == :rm && arg2 == "kamal/lock-app/details" }
end
def assert_hook_ran(hook, output, version:, service_version:, hosts:, command:, subcommand: nil, runtime: nil)

View File

@@ -63,11 +63,14 @@ class CliMainTest < CliTestCase
Thread.report_on_exception = false
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |*arg| arg[0..1] == [:mkdir, 'kamal_lock-app'] }
.with { |*args| args == [ :mkdir, "-p", "kamal" ] }
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |*arg| arg[0..1] == [:mkdir, 'kamal/lock-app'] }
.raises(RuntimeError, "mkdir: cannot create directory kamal_lock-app: File exists")
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_debug)
.with(:stat, 'kamal_lock-app', ">", "/dev/null", "&&", :cat, "kamal_lock-app/details", "|", :base64, "-d")
.with(:stat, 'kamal/lock-app', ">", "/dev/null", "&&", :cat, "kamal/lock-app/details", "|", :base64, "-d")
assert_raises(Kamal::Cli::LockError) do
run_command("deploy")
@@ -78,7 +81,10 @@ class CliMainTest < CliTestCase
Thread.report_on_exception = false
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |*arg| arg[0..1] == [:mkdir, 'kamal_lock-app'] }
.with { |*args| args == [ :mkdir, "-p", "kamal" ] }
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |*arg| arg[0..1] == [:mkdir, 'kamal/lock-app'] }
.raises(SocketError, "getaddrinfo: nodename nor servname provided, or not known")
assert_raises(SSHKit::Runner::ExecuteError) do
@@ -230,7 +236,7 @@ class CliMainTest < CliTestCase
test "audit" do
run_command("audit").tap do |output|
assert_match /tail -n 50 kamal-app-audit.log on 1.1.1.1/, output
assert_match %r{tail -n 50 kamal/app-audit.log on 1.1.1.1}, output
assert_match /App Host: 1.1.1.1/, output
end
end

View File

@@ -3,6 +3,7 @@ require_relative "cli_test_case"
class CliServerTest < CliTestCase
test "bootstrap already installed" do
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:docker, "-v", raise_on_non_zero_exit: false).returns(true).at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:mkdir, "-p", "kamal").returns("").at_least_once
assert_equal "", run_command("bootstrap")
end
@@ -10,6 +11,7 @@ class CliServerTest < CliTestCase
test "bootstrap install as non-root user" do
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:docker, "-v", raise_on_non_zero_exit: false).returns(false).at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:execute).with('[ "${EUID:-$(id -u)}" -eq 0 ]', raise_on_non_zero_exit: false).returns(false).at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:mkdir, "-p", "kamal").returns("").at_least_once
assert_raise RuntimeError, "Docker is not installed on 1.1.1.1, 1.1.1.3, 1.1.1.4, 1.1.1.2 and can't be automatically installed without having root access and the `curl` command available. Install Docker manually: https://docs.docker.com/engine/install/" do
run_command("bootstrap")
@@ -20,6 +22,7 @@ class CliServerTest < CliTestCase
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:docker, "-v", raise_on_non_zero_exit: false).returns(false).at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:execute).with('[ "${EUID:-$(id -u)}" -eq 0 ]', raise_on_non_zero_exit: false).returns(true).at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:curl, "-fsSL", "https://get.docker.com", "|", :sh).at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:mkdir, "-p", "kamal").returns("").at_least_once
run_command("bootstrap").tap do |output|
("1.1.1.1".."1.1.1.4").map do |host|

View File

@@ -20,7 +20,7 @@ class CliTraefikTest < CliTestCase
test "reboot --rolling" do
run_command("reboot", "--rolling").tap do |output|
assert_match "Running docker container prune --force --filter label=org.opencontainers.image.title=Traefik on 1.1.1.1", output.lines[3]
assert_match "Running docker container prune --force --filter label=org.opencontainers.image.title=Traefik on 1.1.1.1", output
end
end

View File

@@ -21,7 +21,7 @@ class CommandsAuditorTest < ActiveSupport::TestCase
:echo,
"[#{@recorded_at}] [#{@performer}]",
"app removed container",
">>", "kamal-app-audit.log"
">>", "kamal/app-audit.log"
], @auditor.record("app removed container")
end
@@ -31,7 +31,7 @@ class CommandsAuditorTest < ActiveSupport::TestCase
:echo,
"[#{@recorded_at}] [#{@performer}] [staging]",
"app removed container",
">>", "kamal-app-staging-audit.log"
">>", "kamal/app-staging-audit.log"
], auditor.record("app removed container")
end
end
@@ -42,7 +42,7 @@ class CommandsAuditorTest < ActiveSupport::TestCase
:echo,
"[#{@recorded_at}] [#{@performer}] [web]",
"app removed container",
">>", "kamal-app-audit.log"
">>", "kamal/app-audit.log"
], auditor.record("app removed container")
end
end
@@ -52,7 +52,7 @@ class CommandsAuditorTest < ActiveSupport::TestCase
:echo,
"[#{@recorded_at}] [#{@performer}] [value]",
"app removed container",
">>", "kamal-app-audit.log"
">>", "kamal/app-audit.log"
], @auditor.record("app removed container", detail: "value")
end

View File

@@ -10,19 +10,19 @@ class CommandsLockTest < ActiveSupport::TestCase
test "status" do
assert_equal \
"stat kamal_lock-app > /dev/null && cat kamal_lock-app/details | base64 -d",
"stat kamal/lock-app > /dev/null && cat kamal/lock-app/details | base64 -d",
new_command.status.join(" ")
end
test "acquire" do
assert_match \
/mkdir kamal_lock-app && echo ".*" > kamal_lock-app\/details/m,
%r{mkdir kamal/lock-app && echo ".*" > kamal/lock-app/details}m,
new_command.acquire("Hello", "123").join(" ")
end
test "release" do
assert_match \
"rm kamal_lock-app/details && rm -r kamal_lock-app",
"rm kamal/lock-app/details && rm -r kamal/lock-app",
new_command.release.join(" ")
end

View File

@@ -0,0 +1,23 @@
require "test_helper"
class CommandsServerTest < ActiveSupport::TestCase
setup do
@config = {
service: "app", image: "dhh/app", registry: { "username" => "dhh", "password" => "secret" }, servers: [ "1.1.1.1" ],
traefik: { "args" => { "accesslog.format" => "json", "metrics.prometheus.buckets" => "0.1,0.3,1.2,5.0" } }
}
end
test "ensure run directory" do
assert_equal "mkdir -p kamal", new_command.ensure_run_directory.join(" ")
end
test "ensure non default run directory" do
assert_equal "mkdir -p /var/run/kamal", new_command(run_directory: "/var/run/kamal").ensure_run_directory.join(" ")
end
private
def new_command(extra_config = {})
Kamal::Commands::Server.new(Kamal::Configuration.new(@config.merge(extra_config)))
end
end

View File

@@ -283,4 +283,12 @@ class ConfigurationTest < ActiveSupport::TestCase
Kamal::Configuration.new(@deploy.tap { |c| c.merge!(minimum_version: "10000.0.0") })
end
end
test "run directory" do
config = Kamal::Configuration.new(@deploy)
assert_equal "kamal", config.run_directory
config = Kamal::Configuration.new(@deploy.merge!(run_directory: "/root/kamal"))
assert_equal "/root/kamal", config.run_directory
end
end