@@ -9,9 +9,6 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|||||||
|
|
||||||
# Assets are prepared in a separate step to ensure they are on all hosts before booting
|
# Assets are prepared in a separate step to ensure they are on all hosts before booting
|
||||||
on(KAMAL.hosts) do
|
on(KAMAL.hosts) do
|
||||||
execute *KAMAL.auditor.record("Tagging #{KAMAL.config.absolute_image} as the latest image"), verbosity: :debug
|
|
||||||
execute *KAMAL.app.tag_current_image_as_latest
|
|
||||||
|
|
||||||
KAMAL.roles_on(host).each do |role|
|
KAMAL.roles_on(host).each do |role|
|
||||||
Kamal::Cli::App::PrepareAssets.new(host, role, self).run
|
Kamal::Cli::App::PrepareAssets.new(host, role, self).run
|
||||||
end
|
end
|
||||||
@@ -22,6 +19,11 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|||||||
Kamal::Cli::App::Boot.new(host, role, version, self).run
|
Kamal::Cli::App::Boot.new(host, role, version, self).run
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
on(KAMAL.hosts) do |host|
|
||||||
|
execute *KAMAL.auditor.record("Tagging #{KAMAL.config.absolute_image} as the latest image"), verbosity: :debug
|
||||||
|
execute *KAMAL.app.tag_latest_image
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -138,7 +140,10 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|||||||
roles = KAMAL.roles_on(host)
|
roles = KAMAL.roles_on(host)
|
||||||
|
|
||||||
roles.each do |role|
|
roles.each do |role|
|
||||||
cli.send(:stale_versions, host: host, role: role).each do |version|
|
versions = capture_with_info(*KAMAL.app(role: role).list_versions, raise_on_non_zero_exit: false).split("\n")
|
||||||
|
versions -= [ capture_with_info(*KAMAL.app(role: role).current_running_version, raise_on_non_zero_exit: false).strip ]
|
||||||
|
|
||||||
|
versions.each do |version|
|
||||||
if stop
|
if stop
|
||||||
puts_by_host host, "Stopping stale container for role #{role} with version #{version}"
|
puts_by_host host, "Stopping stale container for role #{role} with version #{version}"
|
||||||
execute *KAMAL.app(role: role).stop(version: version), raise_on_non_zero_exit: false
|
execute *KAMAL.app(role: role).stop(version: version), raise_on_non_zero_exit: false
|
||||||
@@ -274,18 +279,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|||||||
version.presence
|
version.presence
|
||||||
end
|
end
|
||||||
|
|
||||||
def stale_versions(host:, role:)
|
|
||||||
versions = nil
|
|
||||||
on(host) do
|
|
||||||
versions = \
|
|
||||||
capture_with_info(*KAMAL.app(role: role).list_versions, raise_on_non_zero_exit: false)
|
|
||||||
.split("\n")
|
|
||||||
.drop(1)
|
|
||||||
end
|
|
||||||
versions
|
|
||||||
end
|
|
||||||
|
|
||||||
def version_or_latest
|
def version_or_latest
|
||||||
options[:version] || "latest"
|
options[:version] || KAMAL.config.latest_tag
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class Kamal::Commands::App < Kamal::Commands::Base
|
|||||||
|
|
||||||
|
|
||||||
def current_running_container_id
|
def current_running_container_id
|
||||||
docker :ps, "--quiet", *filter_args(statuses: ACTIVE_DOCKER_STATUSES), "--latest"
|
current_running_container(format: "--quiet")
|
||||||
end
|
end
|
||||||
|
|
||||||
def container_id_for_version(version, only_running: false)
|
def container_id_for_version(version, only_running: false)
|
||||||
@@ -57,13 +57,15 @@ class Kamal::Commands::App < Kamal::Commands::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def current_running_version
|
def current_running_version
|
||||||
list_versions("--latest", statuses: ACTIVE_DOCKER_STATUSES)
|
pipe \
|
||||||
|
current_running_container(format: "--format '{{.Names}}'"),
|
||||||
|
extract_version_from_name
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_versions(*docker_args, statuses: nil)
|
def list_versions(*docker_args, statuses: nil)
|
||||||
pipe \
|
pipe \
|
||||||
docker(:ps, *filter_args(statuses: statuses), *docker_args, "--format", '"{{.Names}}"'),
|
docker(:ps, *filter_args(statuses: statuses), *docker_args, "--format", '"{{.Names}}"'),
|
||||||
%(while read line; do echo ${line##{role.container_prefix}-}; done) # Extract SHA from "service-role-dest-SHA"
|
extract_version_from_name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -81,10 +83,33 @@ class Kamal::Commands::App < Kamal::Commands::Base
|
|||||||
[ role.container_prefix, version || config.version ].compact.join("-")
|
[ role.container_prefix, version || config.version ].compact.join("-")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def latest_image_id
|
||||||
|
docker :image, :ls, *argumentize("--filter", "reference=#{config.latest_image}"), "--format", "'{{.ID}}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_running_container(format:)
|
||||||
|
pipe \
|
||||||
|
shell(chain(latest_image_container(format: format), latest_container(format: format))),
|
||||||
|
[ :head, "-1" ]
|
||||||
|
end
|
||||||
|
|
||||||
|
def latest_image_container(format:)
|
||||||
|
latest_container format: format, filters: [ "ancestor=$(#{latest_image_id.join(" ")})" ]
|
||||||
|
end
|
||||||
|
|
||||||
|
def latest_container(format:, filters: nil)
|
||||||
|
docker :ps, "--latest", *format, *filter_args(statuses: ACTIVE_DOCKER_STATUSES), argumentize("--filter", filters)
|
||||||
|
end
|
||||||
|
|
||||||
def filter_args(statuses: nil)
|
def filter_args(statuses: nil)
|
||||||
argumentize "--filter", filters(statuses: statuses)
|
argumentize "--filter", filters(statuses: statuses)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def extract_version_from_name
|
||||||
|
# Extract SHA from "service-role-dest-SHA"
|
||||||
|
%(while read line; do echo ${line##{role.container_prefix}-}; done)
|
||||||
|
end
|
||||||
|
|
||||||
def filters(statuses: nil)
|
def filters(statuses: nil)
|
||||||
[ "label=service=#{config.service}" ].tap do |filters|
|
[ "label=service=#{config.service}" ].tap do |filters|
|
||||||
filters << "label=destination=#{config.destination}" if config.destination
|
filters << "label=destination=#{config.destination}" if config.destination
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ module Kamal::Commands::App::Assets
|
|||||||
combine \
|
combine \
|
||||||
make_directory(role.asset_extracted_path),
|
make_directory(role.asset_extracted_path),
|
||||||
[ *docker(:stop, "-t 1", asset_container, "2> /dev/null"), "|| true" ],
|
[ *docker(:stop, "-t 1", asset_container, "2> /dev/null"), "|| true" ],
|
||||||
docker(:run, "--name", asset_container, "--detach", "--rm", config.latest_image, "sleep 1000000"),
|
docker(:run, "--name", asset_container, "--detach", "--rm", config.absolute_image, "sleep 1000000"),
|
||||||
docker(:cp, "-L", "#{asset_container}:#{role.asset_path}/.", role.asset_extracted_path),
|
docker(:cp, "-L", "#{asset_container}:#{role.asset_path}/.", role.asset_extracted_path),
|
||||||
docker(:stop, "-t 1", asset_container),
|
docker(:stop, "-t 1", asset_container),
|
||||||
by: "&&"
|
by: "&&"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ module Kamal::Commands::App::Images
|
|||||||
docker :image, :prune, "--all", "--force", *filter_args
|
docker :image, :prune, "--all", "--force", *filter_args
|
||||||
end
|
end
|
||||||
|
|
||||||
def tag_current_image_as_latest
|
def tag_latest_image
|
||||||
docker :tag, config.absolute_image, config.latest_image
|
docker :tag, config.absolute_image, config.latest_image
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ module Kamal::Commands
|
|||||||
end
|
end
|
||||||
|
|
||||||
def shell(command)
|
def shell(command)
|
||||||
[ :sh, "-c", "'#{command.flatten.join(" ").gsub("'", "'\\''")}'" ]
|
[ :sh, "-c", "'#{command.flatten.join(" ").gsub("'", "'\\\\''")}'" ]
|
||||||
end
|
end
|
||||||
|
|
||||||
def docker(*args)
|
def docker(*args)
|
||||||
|
|||||||
@@ -128,7 +128,11 @@ class Kamal::Configuration
|
|||||||
end
|
end
|
||||||
|
|
||||||
def latest_image
|
def latest_image
|
||||||
"#{repository}:latest"
|
"#{repository}:#{latest_tag}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def latest_tag
|
||||||
|
[ "latest", *destination ].join("-")
|
||||||
end
|
end
|
||||||
|
|
||||||
def service_with_version
|
def service_with_version
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class CliAppTest < CliTestCase
|
|||||||
.returns("running") # health check
|
.returns("running") # health check
|
||||||
|
|
||||||
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
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)
|
.with(:sh, "-c", "'docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting'", "|", :head, "-1", "|", "while read line; do echo ${line#app-web-}; done", raise_on_non_zero_exit: false)
|
||||||
.returns("123") # old version
|
.returns("123") # old version
|
||||||
|
|
||||||
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
@@ -75,7 +75,7 @@ class CliAppTest < CliTestCase
|
|||||||
.returns("running") # health check
|
.returns("running") # health check
|
||||||
|
|
||||||
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
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)
|
.with(:sh, "-c", "'docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting'", "|", :head, "-1", "|", "while read line; do echo ${line#app-web-}; done", raise_on_non_zero_exit: false)
|
||||||
.returns("123").twice # old version
|
.returns("123").twice # old version
|
||||||
|
|
||||||
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
@@ -100,14 +100,18 @@ class CliAppTest < CliTestCase
|
|||||||
|
|
||||||
test "stop" do
|
test "stop" do
|
||||||
run_command("stop").tap do |output|
|
run_command("stop").tap do |output|
|
||||||
assert_match "docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker stop", output
|
assert_match "sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | xargs docker stop", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "stale_containers" do
|
test "stale_containers" do
|
||||||
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
.with(:docker, :ps, "--filter", "label=service=app", "--filter", "label=role=web", "--format", "\"{{.Names}}\"", "|", "while read line; do echo ${line#app-web-}; done", raise_on_non_zero_exit: false)
|
.with(:docker, :ps, "--filter", "label=service=app", "--filter", "label=role=web", "--format", "\"{{.Names}}\"", "|", "while read line; do echo ${line#app-web-}; done", raise_on_non_zero_exit: false)
|
||||||
.returns("12345678\n87654321")
|
.returns("12345678\n87654321\n")
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
|
.with(:sh, "-c", "'docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting'", "|", :head, "-1", "|", "while read line; do echo ${line#app-web-}; done", raise_on_non_zero_exit: false)
|
||||||
|
.returns("12345678\n")
|
||||||
|
|
||||||
run_command("stale_containers").tap do |output|
|
run_command("stale_containers").tap do |output|
|
||||||
assert_match /Detected stale container for role web with version 87654321/, output
|
assert_match /Detected stale container for role web with version 87654321/, output
|
||||||
@@ -117,7 +121,11 @@ class CliAppTest < CliTestCase
|
|||||||
test "stop stale_containers" do
|
test "stop stale_containers" do
|
||||||
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
.with(:docker, :ps, "--filter", "label=service=app", "--filter", "label=role=web", "--format", "\"{{.Names}}\"", "|", "while read line; do echo ${line#app-web-}; done", raise_on_non_zero_exit: false)
|
.with(:docker, :ps, "--filter", "label=service=app", "--filter", "label=role=web", "--format", "\"{{.Names}}\"", "|", "while read line; do echo ${line#app-web-}; done", raise_on_non_zero_exit: false)
|
||||||
.returns("12345678\n87654321")
|
.returns("12345678\n87654321\n")
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
|
.with(:sh, "-c", "'docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting'", "|", :head, "-1", "|", "while read line; do echo ${line#app-web-}; done", raise_on_non_zero_exit: false)
|
||||||
|
.returns("12345678\n")
|
||||||
|
|
||||||
run_command("stale_containers", "--stop").tap do |output|
|
run_command("stale_containers", "--stop").tap do |output|
|
||||||
assert_match /Stopping stale container for role web with version 87654321/, output
|
assert_match /Stopping stale container for role web with version 87654321/, output
|
||||||
@@ -133,7 +141,7 @@ class CliAppTest < CliTestCase
|
|||||||
|
|
||||||
test "remove" do
|
test "remove" do
|
||||||
run_command("remove").tap do |output|
|
run_command("remove").tap do |output|
|
||||||
assert_match /#{Regexp.escape("docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker stop")}/, output
|
assert_match /#{Regexp.escape("sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | xargs docker stop")}/, output
|
||||||
assert_match /#{Regexp.escape("docker container prune --force --filter label=service=app")}/, output
|
assert_match /#{Regexp.escape("docker container prune --force --filter label=service=app")}/, output
|
||||||
assert_match /#{Regexp.escape("docker image prune --all --force --filter label=service=app")}/, output
|
assert_match /#{Regexp.escape("docker image prune --all --force --filter label=service=app")}/, output
|
||||||
end
|
end
|
||||||
@@ -165,7 +173,7 @@ class CliAppTest < CliTestCase
|
|||||||
|
|
||||||
test "exec with reuse" do
|
test "exec with reuse" do
|
||||||
run_command("exec", "--reuse", "ruby -v").tap do |output|
|
run_command("exec", "--reuse", "ruby -v").tap do |output|
|
||||||
assert_match "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", output # Get current version
|
assert_match "sh -c 'docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | while read line; do echo ${line#app-web-}; done", output # Get current version
|
||||||
assert_match "docker exec app-web-999 ruby -v", output
|
assert_match "docker exec app-web-999 ruby -v", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -184,7 +192,7 @@ class CliAppTest < CliTestCase
|
|||||||
.with("ssh -t root@1.1.1.1 -p 22 'docker exec -it app-web-999 ruby -v'")
|
.with("ssh -t root@1.1.1.1 -p 22 'docker exec -it app-web-999 ruby -v'")
|
||||||
run_command("exec", "-i", "--reuse", "ruby -v").tap do |output|
|
run_command("exec", "-i", "--reuse", "ruby -v").tap do |output|
|
||||||
assert_match "Get current version of running container...", output
|
assert_match "Get current version of running container...", output
|
||||||
assert_match "Running 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 on 1.1.1.1", output
|
assert_match "Running /usr/bin/env sh -c 'docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | while read line; do echo ${line#app-web-}; done on 1.1.1.1", output
|
||||||
assert_match "Launching interactive command with version 999 via SSH from existing container on 1.1.1.1...", output
|
assert_match "Launching interactive command with version 999 via SSH from existing container on 1.1.1.1...", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -203,28 +211,28 @@ class CliAppTest < CliTestCase
|
|||||||
|
|
||||||
test "logs" do
|
test "logs" do
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:exec)
|
SSHKit::Backend::Abstract.any_instance.stubs(:exec)
|
||||||
.with("ssh -t root@1.1.1.1 'docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest| xargs docker logs --timestamps --tail 10 2>&1'")
|
.with("ssh -t root@1.1.1.1 'sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1| xargs docker logs --timestamps --tail 10 2>&1'")
|
||||||
|
|
||||||
assert_match "docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker logs --tail 100 2>&1", run_command("logs")
|
assert_match "sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | xargs docker logs --tail 100 2>&1", run_command("logs")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "logs with follow" do
|
test "logs with follow" do
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:exec)
|
SSHKit::Backend::Abstract.any_instance.stubs(:exec)
|
||||||
.with("ssh -t root@1.1.1.1 -p 22 'docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker logs --timestamps --tail 10 --follow 2>&1'")
|
.with("ssh -t root@1.1.1.1 -p 22 'sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | xargs docker logs --timestamps --tail 10 --follow 2>&1'")
|
||||||
|
|
||||||
assert_match "docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker logs --timestamps --tail 10 --follow 2>&1", run_command("logs", "--follow")
|
assert_match "sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | xargs docker logs --timestamps --tail 10 --follow 2>&1", run_command("logs", "--follow")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "version" do
|
test "version" do
|
||||||
run_command("version").tap do |output|
|
run_command("version").tap do |output|
|
||||||
assert_match "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", output
|
assert_match "sh -c 'docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | while read line; do echo ${line#app-web-}; done", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
test "version through main" do
|
test "version through main" do
|
||||||
stdouted { Kamal::Cli::Main.start([ "app", "version", "-c", "test/fixtures/deploy_with_accessories.yml", "--hosts", "1.1.1.1" ]) }.tap do |output|
|
stdouted { Kamal::Cli::Main.start([ "app", "version", "-c", "test/fixtures/deploy_with_accessories.yml", "--hosts", "1.1.1.1" ]) }.tap do |output|
|
||||||
assert_match "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", output
|
assert_match "sh -c 'docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | while read line; do echo ${line#app-web-}; done", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ class CliMainTest < CliTestCase
|
|||||||
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-#{role}-123$", "--quiet")
|
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-#{role}-123$", "--quiet")
|
||||||
.returns("version-to-rollback\n").at_least_once
|
.returns("version-to-rollback\n").at_least_once
|
||||||
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
.with(:docker, :ps, "--filter", "label=service=app", "--filter", "label=role=#{role}", "--filter", "status=running", "--filter", "status=restarting", "--latest", "--format", "\"{{.Names}}\"", "|", "while read line; do echo ${line#app-#{role}-}; done", raise_on_non_zero_exit: false)
|
.with(:sh, "-c", "'docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=#{role} --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=#{role} --filter status=running --filter status=restarting'", "|", :head, "-1", "|", "while read line; do echo ${line#app-#{role}-}; done", raise_on_non_zero_exit: false)
|
||||||
.returns("version-to-rollback\n").at_least_once
|
.returns("version-to-rollback\n").at_least_once
|
||||||
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-#{role}-123$", "--quiet", "|", :xargs, :docker, :inspect, "--format", "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'")
|
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-#{role}-123$", "--quiet", "|", :xargs, :docker, :inspect, "--format", "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'")
|
||||||
@@ -286,7 +286,7 @@ class CliMainTest < CliTestCase
|
|||||||
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-123$", "--quiet", raise_on_non_zero_exit: false)
|
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-123$", "--quiet", raise_on_non_zero_exit: false)
|
||||||
.returns("").at_least_once
|
.returns("").at_least_once
|
||||||
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
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)
|
.with(:sh, "-c", "'docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting'", "|", :head, "-1", "|", "while read line; do echo ${line#app-web-}; done", raise_on_non_zero_exit: false)
|
||||||
.returns("").at_least_once
|
.returns("").at_least_once
|
||||||
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-123$", "--quiet", "|", :xargs, :docker, :inspect, "--format", "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'")
|
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-123$", "--quiet", "|", :xargs, :docker, :inspect, "--format", "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'")
|
||||||
|
|||||||
@@ -95,14 +95,14 @@ class CommandsAppTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
test "stop" do
|
test "stop" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker stop",
|
"sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | xargs docker stop",
|
||||||
new_command.stop.join(" ")
|
new_command.stop.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "stop with custom stop wait time" do
|
test "stop with custom stop wait time" do
|
||||||
@config[:stop_wait_time] = 30
|
@config[:stop_wait_time] = 30
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker stop -t 30",
|
"sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | xargs docker stop -t 30",
|
||||||
new_command.stop.join(" ")
|
new_command.stop.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -128,45 +128,45 @@ class CommandsAppTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
test "logs" do
|
test "logs" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker logs 2>&1",
|
"sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | xargs docker logs 2>&1",
|
||||||
new_command.logs.join(" ")
|
new_command.logs.join(" ")
|
||||||
|
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker logs --since 5m 2>&1",
|
"sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | xargs docker logs --since 5m 2>&1",
|
||||||
new_command.logs(since: "5m").join(" ")
|
new_command.logs(since: "5m").join(" ")
|
||||||
|
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker logs --tail 100 2>&1",
|
"sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | xargs docker logs --tail 100 2>&1",
|
||||||
new_command.logs(lines: "100").join(" ")
|
new_command.logs(lines: "100").join(" ")
|
||||||
|
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker logs --since 5m --tail 100 2>&1",
|
"sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | xargs docker logs --since 5m --tail 100 2>&1",
|
||||||
new_command.logs(since: "5m", lines: "100").join(" ")
|
new_command.logs(since: "5m", lines: "100").join(" ")
|
||||||
|
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker logs 2>&1 | grep 'my-id'",
|
"sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | xargs docker logs 2>&1 | grep 'my-id'",
|
||||||
new_command.logs(grep: "my-id").join(" ")
|
new_command.logs(grep: "my-id").join(" ")
|
||||||
|
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker logs --since 5m 2>&1 | grep 'my-id'",
|
"sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | xargs docker logs --since 5m 2>&1 | grep 'my-id'",
|
||||||
new_command.logs(since: "5m", grep: "my-id").join(" ")
|
new_command.logs(since: "5m", grep: "my-id").join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "follow logs" do
|
test "follow logs" do
|
||||||
assert_match \
|
assert_equal \
|
||||||
"docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker logs --timestamps --follow 2>&1",
|
"ssh -t root@app-1 -p 22 'sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | xargs docker logs --timestamps --follow 2>&1'",
|
||||||
new_command.follow_logs(host: "app-1")
|
new_command.follow_logs(host: "app-1")
|
||||||
|
|
||||||
assert_match \
|
assert_equal \
|
||||||
"docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker logs --timestamps --follow 2>&1 | grep \"Completed\"",
|
"ssh -t root@app-1 -p 22 'sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | xargs docker logs --timestamps --follow 2>&1 | grep \"Completed\"'",
|
||||||
new_command.follow_logs(host: "app-1", grep: "Completed")
|
new_command.follow_logs(host: "app-1", grep: "Completed")
|
||||||
|
|
||||||
assert_match \
|
assert_equal \
|
||||||
"docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker logs --timestamps --tail 123 --follow 2>&1",
|
"ssh -t root@app-1 -p 22 'sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | xargs docker logs --timestamps --tail 123 --follow 2>&1'",
|
||||||
new_command.follow_logs(host: "app-1", lines: 123)
|
new_command.follow_logs(host: "app-1", lines: 123)
|
||||||
|
|
||||||
assert_match \
|
assert_equal \
|
||||||
"docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker logs --timestamps --tail 123 --follow 2>&1 | grep \"Completed\"",
|
"ssh -t root@app-1 -p 22 'sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | xargs docker logs --timestamps --tail 123 --follow 2>&1 | grep \"Completed\"'",
|
||||||
new_command.follow_logs(host: "app-1", lines: 123, grep: "Completed")
|
new_command.follow_logs(host: "app-1", lines: 123, grep: "Completed")
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -254,14 +254,14 @@ class CommandsAppTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
test "current_running_container_id" do
|
test "current_running_container_id" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest",
|
"sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1",
|
||||||
new_command.current_running_container_id.join(" ")
|
new_command.current_running_container_id.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "current_running_container_id with destination" do
|
test "current_running_container_id with destination" do
|
||||||
@destination = "staging"
|
@destination = "staging"
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker ps --quiet --filter label=service=app --filter label=destination=staging --filter label=role=web --filter status=running --filter status=restarting --latest",
|
"sh -c 'docker ps --latest --quiet --filter label=service=app --filter label=destination=staging --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest-staging --format '\\''{{.ID}}'\\'') ; docker ps --latest --quiet --filter label=service=app --filter label=destination=staging --filter label=role=web --filter status=running --filter status=restarting' | head -1",
|
||||||
new_command.current_running_container_id.join(" ")
|
new_command.current_running_container_id.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -273,7 +273,7 @@ class CommandsAppTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
test "current_running_version" do
|
test "current_running_version" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"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",
|
"sh -c 'docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --filter ancestor=$(docker image ls --filter reference=dhh/app:latest --format '\\''{{.ID}}'\\'') ; docker ps --latest --format '\\''{{.Names}}'\\'' --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting' | head -1 | while read line; do echo ${line#app-web-}; done",
|
||||||
new_command.current_running_version.join(" ")
|
new_command.current_running_version.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -351,10 +351,17 @@ class CommandsAppTest < ActiveSupport::TestCase
|
|||||||
new_command.remove_images.join(" ")
|
new_command.remove_images.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "tag_current_image_as_latest" do
|
test "tag_latest_image" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker tag dhh/app:999 dhh/app:latest",
|
"docker tag dhh/app:999 dhh/app:latest",
|
||||||
new_command.tag_current_image_as_latest.join(" ")
|
new_command.tag_latest_image.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "tag_latest_image with destination" do
|
||||||
|
@destination = "staging"
|
||||||
|
assert_equal \
|
||||||
|
"docker tag dhh/app:999 dhh/app:latest-staging",
|
||||||
|
new_command.tag_latest_image.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "make_env_directory" do
|
test "make_env_directory" do
|
||||||
@@ -383,7 +390,7 @@ class CommandsAppTest < ActiveSupport::TestCase
|
|||||||
assert_equal [
|
assert_equal [
|
||||||
:mkdir, "-p", ".kamal/assets/extracted/app-web-999", "&&",
|
:mkdir, "-p", ".kamal/assets/extracted/app-web-999", "&&",
|
||||||
:docker, :stop, "-t 1", "app-web-assets", "2> /dev/null", "|| true", "&&",
|
: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, :run, "--name", "app-web-assets", "--detach", "--rm", "dhh/app:999", "sleep 1000000", "&&",
|
||||||
:docker, :cp, "-L", "app-web-assets:/public/assets/.", ".kamal/assets/extracted/app-web-999", "&&",
|
:docker, :cp, "-L", "app-web-assets:/public/assets/.", ".kamal/assets/extracted/app-web-999", "&&",
|
||||||
:docker, :stop, "-t 1", "app-web-assets"
|
:docker, :stop, "-t 1", "app-web-assets"
|
||||||
], new_command(asset_path: "/public/assets").extract_assets
|
], new_command(asset_path: "/public/assets").extract_assets
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: docker/deployer
|
context: docker/deployer
|
||||||
environment:
|
environment:
|
||||||
- TEST_ID=${TEST_ID}
|
- TEST_ID=${TEST_ID:-}
|
||||||
volumes:
|
volumes:
|
||||||
- ../..:/kamal
|
- ../..:/kamal
|
||||||
- shared:/shared
|
- shared:/shared
|
||||||
|
|||||||
Reference in New Issue
Block a user