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

@@ -10,12 +10,21 @@ class Kamal::Cli::App < Kamal::Cli::Base
on(KAMAL.hosts) do
execute *KAMAL.auditor.record("Tagging #{KAMAL.config.absolute_image} as the latest image"), verbosity: :debug
execute *KAMAL.app.tag_current_as_latest
KAMAL.roles_on(host).each do |role|
app = KAMAL.app(role: role)
role_config = KAMAL.config.role(role)
if role_config.assets?
execute *app.extract_assets
old_version = capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip
execute *app.sync_asset_volumes(old_version: old_version)
end
end
end
on(KAMAL.hosts, **KAMAL.boot_strategy) do |host|
roles = KAMAL.roles_on(host)
roles.each do |role|
KAMAL.roles_on(host).each do |role|
app = KAMAL.app(role: role)
auditor = KAMAL.auditor(role: role)
role_config = KAMAL.config.role(role)
@@ -27,13 +36,11 @@ class Kamal::Cli::App < Kamal::Cli::Base
execute *app.rename_container(version: version, new_version: tmp_version)
end
execute *auditor.record("Booted app version #{version}"), verbosity: :debug
old_version = capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip
if role_config.uses_cord?
execute *app.tie_cord(role_config.cord_host_file)
end
execute *app.tie_cord(role_config.cord_host_file) if role_config.uses_cord?
execute *auditor.record("Booted app version #{version}"), verbosity: :debug
execute *app.run(hostname: "#{host}-#{SecureRandom.hex(6)}")
@@ -47,7 +54,10 @@ class Kamal::Cli::App < Kamal::Cli::Base
Kamal::Utils::HealthcheckPoller.wait_for_unhealthy(pause_after_ready: true) { capture_with_info(*app.status(version: old_version)) }
end
end
execute *app.stop(version: old_version), raise_on_non_zero_exit: false
execute *app.clean_up_assets if role_config.assets?
end
end
end

View File

@@ -20,6 +20,7 @@ class Kamal::Commands::App < Kamal::Commands::Base
*role_config.health_check_args,
*config.logging_args,
*config.volume_args,
*role_config.asset_volume_args,
*role_config.label_args,
*role_config.option_args,
config.absolute_image,
@@ -105,7 +106,7 @@ class Kamal::Commands::App < Kamal::Commands::Base
def list_versions(*docker_args, statuses: nil)
pipe \
docker(:ps, *filter_args(statuses: statuses), *docker_args, "--format", '"{{.Names}}"'),
%(while read line; do echo ${line##{role_config.full_name}-}; done) # Extract SHA from "service-role-dest-SHA"
%(while read line; do echo ${line##{role_config.container_prefix}-}; done) # Extract SHA from "service-role-dest-SHA"
end
def list_containers
@@ -153,7 +154,7 @@ class Kamal::Commands::App < Kamal::Commands::Base
def cord(version:)
pipe \
docker(:inspect, "-f '{{ range .Mounts }}{{printf \"%s %s\n\" .Source .Destination}}{{ end }}'", container_name(version)),
[:awk, "'$2 == \"#{role_config.cord_container_directory}\" {print $1}'"]
[:awk, "'$2 == \"#{role_config.cord_volume.container_path}\" {print $1}'"]
end
def tie_cord(cord)
@@ -164,9 +165,43 @@ class Kamal::Commands::App < Kamal::Commands::Base
remove_directory(cord)
end
def extract_assets
asset_container = "#{role_config.container_prefix}-assets"
combine \
make_directory(role_config.asset_extracted_path),
[*docker(:stop, "-t 1", asset_container, "2> /dev/null"), "|| true"],
docker(:run, "--name", asset_container, "--detach", "--rm", config.latest_image, "sleep 1000000"),
docker(:cp, "-L", "#{asset_container}:#{role_config.asset_path}/.", role_config.asset_extracted_path),
docker(:stop, "-t 1", asset_container),
by: "&&"
end
def sync_asset_volumes(old_version: nil)
new_extracted_path, new_volume_path = role_config.asset_extracted_path(config.version), role_config.asset_volume.host_path
if old_version.present?
old_extracted_path, old_volume_path = role_config.asset_extracted_path(old_version), role_config.asset_volume(old_version).host_path
end
commands = [make_directory(new_volume_path), copy_contents(new_extracted_path, new_volume_path)]
if old_version.present?
commands << copy_contents(new_extracted_path, old_volume_path, continue_on_error: true)
commands << copy_contents(old_extracted_path, new_volume_path, continue_on_error: true)
end
chain *commands
end
def clean_up_assets
chain \
find_and_remove_older_siblings(role_config.asset_extracted_path),
find_and_remove_older_siblings(role_config.asset_volume_path)
end
private
def container_name(version = nil)
[ role_config.full_name, version || config.version ].compact.join("-")
[ role_config.container_prefix, version || config.version ].compact.join("-")
end
def filter_args(statuses: nil)
@@ -186,4 +221,19 @@ class Kamal::Commands::App < Kamal::Commands::Base
end
end
end
def find_and_remove_older_siblings(path)
[
:find,
Pathname.new(path).dirname.to_s,
"-maxdepth 1",
"-name", "'#{role_config.container_prefix}-*'",
"!", "-name", Pathname.new(path).basename.to_s,
"-exec rm -rf \"{}\" +"
]
end
def copy_contents(source, destination, continue_on_error: false)
[ :cp, "-rn", "#{source}/*", destination, *("|| true" if continue_on_error)]
end
end

View File

@@ -215,6 +215,10 @@ class Kamal::Configuration
@run_id ||= SecureRandom.hex(16)
end
def asset_path
raw_config.asset_path
end
private
# Will raise ArgumentError if any required config keys are missing
def ensure_required_keys_present

View File

@@ -48,11 +48,15 @@ class Kamal::Configuration::Role
argumentize "--env-file", host_env_file_path
end
def asset_volume_args
asset_volume&.docker_args
end
def health_check_args(cord: true)
if health_check_cmd.present?
if cord && uses_cord?
optionize({ "health-cmd" => health_check_cmd_with_cord, "health-interval" => health_check_interval })
.concat(["--volume", "#{cord_host_directory}:#{cord_container_directory}"])
.concat(cord_volume.docker_args)
else
optionize({ "health-cmd" => health_check_cmd, "health-interval" => health_check_interval })
end
@@ -74,15 +78,23 @@ class Kamal::Configuration::Role
end
def uses_cord?
running_traefik? && cord_container_directory.present? && health_check_cmd.present?
running_traefik? && cord_volume && health_check_cmd.present?
end
def cord_host_directory
File.join config.run_directory_as_docker_volume, "cords", [full_name, config.run_id].join("-")
File.join config.run_directory_as_docker_volume, "cords", [container_prefix, config.run_id].join("-")
end
def cord_volume
if (cord = health_check_options["cord"])
@cord_volume ||= Kamal::Configuration::Volume.new \
host_path: File.join(config.run_directory, "cords", [container_prefix, config.run_id].join("-")),
container_path: cord
end
end
def cord_host_file
File.join cord_host_directory, CORD_FILE
File.join cord_volume.host_path, CORD_FILE
end
def cord_container_directory
@@ -90,7 +102,7 @@ class Kamal::Configuration::Role
end
def cord_container_file
File.join cord_container_directory, CORD_FILE
File.join cord_volume.container_path, CORD_FILE
end
@@ -110,10 +122,37 @@ class Kamal::Configuration::Role
name.web? || specializations["traefik"]
end
def full_name
def container_name(version = nil)
[ container_prefix, version || config.version ].compact.join("-")
end
def container_prefix
[ config.service, name, config.destination ].compact.join("-")
end
def asset_path
specializations["asset_path"] || config.asset_path
end
def assets?
asset_path.present? && running_traefik?
end
def asset_volume(version = nil)
if assets?
Kamal::Configuration::Volume.new \
host_path: asset_volume_path(version), container_path: asset_path
end
end
def asset_extracted_path(version = nil)
File.join config.run_directory, "assets", "extracted", container_name(version)
end
def asset_volume_path(version = nil)
File.join config.run_directory, "assets", "volumes", container_name(version)
end
private
attr_accessor :config

View File

@@ -0,0 +1,22 @@
class Kamal::Configuration::Volume
attr_reader :host_path, :container_path
delegate :argumentize, to: Kamal::Utils
def initialize(host_path:, container_path:)
@host_path = host_path
@container_path = container_path
end
def docker_args
argumentize "--volume", "#{host_path_for_docker_volume}:#{container_path}"
end
private
def host_path_for_docker_volume
if Pathname.new(host_path).absolute?
host_path
else
File.join "$(pwd)", host_path
end
end
end