Merge pull request #449 from basecamp/asset-path

Asset paths
This commit is contained in:
Donal McBreen
2023-09-12 08:26:07 +01:00
committed by GitHub
14 changed files with 304 additions and 28 deletions

View File

@@ -55,8 +55,6 @@ class CliAppTest < CliTestCase
end
test "boot errors leave lock in place" do
invoke_options = { "config_file" => "test/fixtures/deploy_simple.yml", "version" => "999" }
Kamal::Cli::App.any_instance.expects(:using_version).raises(RuntimeError)
assert !KAMAL.holding_lock?
@@ -66,6 +64,34 @@ class CliAppTest < CliTestCase
assert KAMAL.holding_lock?
end
test "boot with assets" do
Object.any_instance.stubs(:sleep)
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-latest$", "--quiet", raise_on_non_zero_exit: false)
.returns("12345678") # running version
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-latest$", "--quiet", "|", :xargs, :docker, :inspect, "--format", "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'")
.returns("running") # health check
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :ps, "--filter", "label=service=app", "--filter", "label=role=web", "--filter", "status=running", "--filter", "status=restarting", "--latest", "--format", "\"{{.Names}}\"", "|", "while read line; do echo ${line#app-web-}; done", raise_on_non_zero_exit: false)
.returns("123").twice # old version
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :inspect, "-f '{{ range .Mounts }}{{printf \"%s %s\n\" .Source .Destination}}{{ end }}'", "app-web-123", "|", :awk, "'$2 == \"/tmp/kamal-cord\" {print $1}'", :raise_on_non_zero_exit => false)
.returns("") # old version
run_command("boot", config: :with_assets).tap do |output|
assert_match "docker tag dhh/app:latest dhh/app:latest", output
assert_match "/usr/bin/env mkdir -p .kamal/assets/volumes/app-web-latest ; cp -rn .kamal/assets/extracted/app-web-latest/* .kamal/assets/volumes/app-web-latest ; cp -rn .kamal/assets/extracted/app-web-latest/* .kamal/assets/volumes/app-web-123 || true ; cp -rn .kamal/assets/extracted/app-web-123/* .kamal/assets/volumes/app-web-latest || true", output
assert_match "/usr/bin/env mkdir -p .kamal/assets/extracted/app-web-latest && docker stop -t 1 app-web-assets 2> /dev/null || true && docker run --name app-web-assets --detach --rm dhh/app:latest sleep 1000000 && docker cp -L app-web-assets:/public/assets/. .kamal/assets/extracted/app-web-latest && docker stop -t 1 app-web-assets", output
assert_match /docker run --detach --restart unless-stopped --name app-web-latest --hostname 1.1.1.1-[0-9a-f]{12} /, output
assert_match "docker container ls --all --filter name=^app-web-123$ --quiet | xargs docker stop", output
assert_match "/usr/bin/env find .kamal/assets/extracted -maxdepth 1 -name 'app-web-*' ! -name app-web-latest -exec rm -rf \"{}\" + ; find .kamal/assets/volumes -maxdepth 1 -name 'app-web-*' ! -name app-web-latest -exec rm -rf \"{}\" +", output
end
end
test "start" do
run_command("start").tap do |output|
assert_match "docker start app-web-999", output

View File

@@ -345,8 +345,39 @@ class CommandsAppTest < ActiveSupport::TestCase
assert_equal "rm -r corddir", new_command.cut_cord("corddir").join(" ")
end
test "extract assets" do
assert_equal [
:mkdir, "-p", ".kamal/assets/extracted/app-web-999", "&&",
:docker, :stop, "-t 1", "app-web-assets", "2> /dev/null", "|| true", "&&",
:docker, :run, "--name", "app-web-assets", "--detach", "--rm", "dhh/app:latest", "sleep 1000000", "&&",
:docker, :cp, "-L", "app-web-assets:/public/assets/.", ".kamal/assets/extracted/app-web-999", "&&",
:docker, :stop, "-t 1", "app-web-assets"
], new_command(asset_path: "/public/assets").extract_assets
end
test "sync asset volumes" do
assert_equal [
:mkdir, "-p", ".kamal/assets/volumes/app-web-999", ";",
:cp, "-rn", ".kamal/assets/extracted/app-web-999/*", ".kamal/assets/volumes/app-web-999"
], new_command(asset_path: "/public/assets").sync_asset_volumes
assert_equal [
:mkdir, "-p", ".kamal/assets/volumes/app-web-999", ";",
:cp, "-rn", ".kamal/assets/extracted/app-web-999/*", ".kamal/assets/volumes/app-web-999", ";",
:cp, "-rn", ".kamal/assets/extracted/app-web-999/*", ".kamal/assets/volumes/app-web-998", "|| true", ";",
:cp, "-rn", ".kamal/assets/extracted/app-web-998/*", ".kamal/assets/volumes/app-web-999", "|| true",
], new_command(asset_path: "/public/assets").sync_asset_volumes(old_version: 998)
end
test "clean up assets" do
assert_equal [
:find, ".kamal/assets/extracted", "-maxdepth 1", "-name", "'app-web-*'", "!", "-name", "app-web-999", "-exec rm -rf \"{}\" +", ";",
:find, ".kamal/assets/volumes", "-maxdepth 1", "-name", "'app-web-*'", "!", "-name", "app-web-999", "-exec rm -rf \"{}\" +"
], new_command(asset_path: "/public/assets").clean_up_assets
end
private
def new_command(role: "web")
Kamal::Commands::App.new(Kamal::Configuration.new(@config, destination: @destination, version: "999"), role: role)
def new_command(role: "web", **additional_config)
Kamal::Commands::App.new(Kamal::Configuration.new(@config.merge(additional_config), destination: @destination, version: "999"), role: role)
end
end

View File

@@ -80,6 +80,15 @@ class ConfigurationRoleTest < ActiveSupport::TestCase
assert_equal expected_env, @config_with_roles.role(:workers).env_file
end
test "container name" do
ENV["VERSION"] = "12345"
assert_equal "app-workers-12345", @config_with_roles.role(:workers).container_name
assert_equal "app-web-12345", @config_with_roles.role(:web).container_name
ensure
ENV.delete("VERSION")
end
test "env args" do
assert_equal ["--env-file", ".kamal/env/roles/app-workers.env"], @config_with_roles.role(:workers).env_args
end
@@ -180,19 +189,67 @@ class ConfigurationRoleTest < ActiveSupport::TestCase
assert !@config_with_roles.role(:workers).uses_cord?
end
test "cord host directory" do
assert_match %r{\$\(pwd\)/.kamal/cords/app-web-[0-9a-f]{32}}, @config_with_roles.role(:web).cord_host_directory
end
test "cord host file" do
assert_match %r{\$\(pwd\)/.kamal/cords/app-web-[0-9a-f]{32}/cord}, @config_with_roles.role(:web).cord_host_file
assert_match %r{.kamal/cords/app-web-[0-9a-f]{32}/cord}, @config_with_roles.role(:web).cord_host_file
end
test "cord container directory" do
assert_equal "/tmp/kamal-cord", @config_with_roles.role(:web).cord_container_directory
test "cord volume" do
assert_equal "/tmp/kamal-cord", @config_with_roles.role(:web).cord_volume.container_path
assert_match %r{.kamal/cords/app-web-[0-9a-f]{32}}, @config_with_roles.role(:web).cord_volume.host_path
assert_equal "--volume", @config_with_roles.role(:web).cord_volume.docker_args[0]
assert_match %r{\$\(pwd\)/.kamal/cords/app-web-[0-9a-f]{32}:/tmp/kamal-cord}, @config_with_roles.role(:web).cord_volume.docker_args[1]
end
test "cord container file" do
assert_equal "/tmp/kamal-cord/cord", @config_with_roles.role(:web).cord_container_file
end
test "asset path and volume args" do
ENV["VERSION"] = "12345"
assert_nil @config_with_roles.role(:web).asset_volume_args
assert_nil @config_with_roles.role(:workers).asset_volume_args
assert_nil @config_with_roles.role(:web).asset_path
assert_nil @config_with_roles.role(:workers).asset_path
assert !@config_with_roles.role(:web).assets?
assert !@config_with_roles.role(:workers).assets?
config_with_assets = Kamal::Configuration.new(@deploy_with_roles.dup.tap { |c|
c[:asset_path] = "foo"
})
assert_equal "foo", config_with_assets.role(:web).asset_path
assert_equal "foo", config_with_assets.role(:workers).asset_path
assert_equal ["--volume", "$(pwd)/.kamal/assets/volumes/app-web-12345:foo"], config_with_assets.role(:web).asset_volume_args
assert_nil config_with_assets.role(:workers).asset_volume_args
assert config_with_assets.role(:web).assets?
assert !config_with_assets.role(:workers).assets?
config_with_assets = Kamal::Configuration.new(@deploy_with_roles.dup.tap { |c|
c[:servers]["web"] = { "hosts" => [ "1.1.1.1", "1.1.1.2" ], "asset_path" => "bar" }
})
assert_equal "bar", config_with_assets.role(:web).asset_path
assert_nil config_with_assets.role(:workers).asset_path
assert_equal ["--volume", "$(pwd)/.kamal/assets/volumes/app-web-12345:bar"], config_with_assets.role(:web).asset_volume_args
assert_nil config_with_assets.role(:workers).asset_volume_args
assert config_with_assets.role(:web).assets?
assert !config_with_assets.role(:workers).assets?
ensure
ENV.delete("VERSION")
end
test "asset extracted path" do
ENV["VERSION"] = "12345"
assert_equal ".kamal/assets/extracted/app-web-12345", @config_with_roles.role(:web).asset_extracted_path
assert_equal ".kamal/assets/extracted/app-workers-12345", @config_with_roles.role(:workers).asset_extracted_path
ensure
ENV.delete("VERSION")
end
test "asset volume path" do
ENV["VERSION"] = "12345"
assert_equal ".kamal/assets/volumes/app-web-12345", @config_with_roles.role(:web).asset_volume_path
assert_equal ".kamal/assets/volumes/app-workers-12345", @config_with_roles.role(:workers).asset_volume_path
ensure
ENV.delete("VERSION")
end
end

View File

@@ -0,0 +1,13 @@
require "test_helper"
class ConfigurationVolumeTest < ActiveSupport::TestCase
test "docker args absolute" do
volume = Kamal::Configuration::Volume.new(host_path: "/root/foo/bar", container_path: "/assets")
assert_equal ["--volume", "/root/foo/bar:/assets"], volume.docker_args
end
test "docker args relative" do
volume = Kamal::Configuration::Volume.new(host_path: "foo/bar", container_path: "/assets")
assert_equal ["--volume", "$(pwd)/foo/bar:/assets"], volume.docker_args
end
end

View File

@@ -269,4 +269,9 @@ class ConfigurationTest < ActiveSupport::TestCase
SecureRandom.expects(:hex).with(16).returns("09876543211234567890098765432112")
assert_equal "09876543211234567890098765432112", @config.run_id
end
test "asset path" do
assert_nil @config.asset_path
assert_equal "foo", Kamal::Configuration.new(@deploy.merge!(asset_path: "foo")).asset_path
end
end

9
test/fixtures/deploy_with_assets.yml vendored Normal file
View File

@@ -0,0 +1,9 @@
service: app
image: dhh/app
servers:
- "1.1.1.1"
- "1.1.1.2"
registry:
username: user
password: pw
asset_path: /public/assets

View File

@@ -4,4 +4,5 @@ COPY default.conf /etc/nginx/conf.d/default.conf
ARG COMMIT_SHA
RUN echo $COMMIT_SHA > /usr/share/nginx/html/version
RUN mkdir -p /usr/share/nginx/html/versions && echo "version" > /usr/share/nginx/html/versions/$COMMIT_SHA

View File

@@ -8,6 +8,7 @@ env:
CLEAR_TOKEN: '4321'
secret:
- SECRET_TOKEN
asset_path: /usr/share/nginx/html/versions
registry:
server: registry:4443

View File

@@ -20,6 +20,8 @@ class MainTest < IntegrationTest
assert_app_is_up version: second_version
assert_hooks_ran "pre-connect", "pre-build", "pre-deploy", "post-deploy"
assert_accumulated_assets first_version, second_version
kamal :rollback, first_version
assert_hooks_ran "pre-connect", "pre-deploy", "post-deploy"
assert_app_is_up version: first_version
@@ -69,4 +71,10 @@ class MainTest < IntegrationTest
def assert_no_remote_env_file
assert_equal "nofile", docker_compose("exec vm1 stat /root/.kamal/env/roles/app-web.env 2> /dev/null || echo nofile", capture: true)
end
def assert_accumulated_assets(*versions)
versions.each do |version|
assert_equal "200", Net::HTTP.get_response(URI.parse("http://localhost:12345/versions/#{version}")).code
end
end
end