Merge branch 'main' into valid_service_name
This commit is contained in:
@@ -5,11 +5,11 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|||||||
if name == "all"
|
if name == "all"
|
||||||
KAMAL.accessory_names.each { |accessory_name| boot(accessory_name) }
|
KAMAL.accessory_names.each { |accessory_name| boot(accessory_name) }
|
||||||
else
|
else
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory, hosts|
|
||||||
directories(name)
|
directories(name)
|
||||||
upload(name)
|
upload(name)
|
||||||
|
|
||||||
on(accessory.hosts) do
|
on(hosts) do
|
||||||
execute *KAMAL.registry.login if login
|
execute *KAMAL.registry.login if login
|
||||||
execute *KAMAL.auditor.record("Booted #{name} accessory"), verbosity: :debug
|
execute *KAMAL.auditor.record("Booted #{name} accessory"), verbosity: :debug
|
||||||
execute *accessory.run
|
execute *accessory.run
|
||||||
@@ -22,8 +22,8 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|||||||
desc "upload [NAME]", "Upload accessory files to host", hide: true
|
desc "upload [NAME]", "Upload accessory files to host", hide: true
|
||||||
def upload(name)
|
def upload(name)
|
||||||
mutating do
|
mutating do
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory, hosts|
|
||||||
on(accessory.hosts) do
|
on(hosts) do
|
||||||
accessory.files.each do |(local, remote)|
|
accessory.files.each do |(local, remote)|
|
||||||
accessory.ensure_local_file_present(local)
|
accessory.ensure_local_file_present(local)
|
||||||
|
|
||||||
@@ -39,8 +39,8 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|||||||
desc "directories [NAME]", "Create accessory directories on host", hide: true
|
desc "directories [NAME]", "Create accessory directories on host", hide: true
|
||||||
def directories(name)
|
def directories(name)
|
||||||
mutating do
|
mutating do
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory, hosts|
|
||||||
on(accessory.hosts) do
|
on(hosts) do
|
||||||
accessory.directories.keys.each do |host_path|
|
accessory.directories.keys.each do |host_path|
|
||||||
execute *accessory.make_directory(host_path)
|
execute *accessory.make_directory(host_path)
|
||||||
end
|
end
|
||||||
@@ -55,8 +55,8 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|||||||
if name == "all"
|
if name == "all"
|
||||||
KAMAL.accessory_names.each { |accessory_name| reboot(accessory_name) }
|
KAMAL.accessory_names.each { |accessory_name| reboot(accessory_name) }
|
||||||
else
|
else
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory, hosts|
|
||||||
on(accessory.hosts) do
|
on(hosts) do
|
||||||
execute *KAMAL.registry.login
|
execute *KAMAL.registry.login
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -71,8 +71,8 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|||||||
desc "start [NAME]", "Start existing accessory container on host"
|
desc "start [NAME]", "Start existing accessory container on host"
|
||||||
def start(name)
|
def start(name)
|
||||||
mutating do
|
mutating do
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory, hosts|
|
||||||
on(accessory.hosts) do
|
on(hosts) do
|
||||||
execute *KAMAL.auditor.record("Started #{name} accessory"), verbosity: :debug
|
execute *KAMAL.auditor.record("Started #{name} accessory"), verbosity: :debug
|
||||||
execute *accessory.start
|
execute *accessory.start
|
||||||
end
|
end
|
||||||
@@ -83,8 +83,8 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|||||||
desc "stop [NAME]", "Stop existing accessory container on host"
|
desc "stop [NAME]", "Stop existing accessory container on host"
|
||||||
def stop(name)
|
def stop(name)
|
||||||
mutating do
|
mutating do
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory, hosts|
|
||||||
on(accessory.hosts) do
|
on(hosts) do
|
||||||
execute *KAMAL.auditor.record("Stopped #{name} accessory"), verbosity: :debug
|
execute *KAMAL.auditor.record("Stopped #{name} accessory"), verbosity: :debug
|
||||||
execute *accessory.stop, raise_on_non_zero_exit: false
|
execute *accessory.stop, raise_on_non_zero_exit: false
|
||||||
end
|
end
|
||||||
@@ -107,8 +107,8 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|||||||
if name == "all"
|
if name == "all"
|
||||||
KAMAL.accessory_names.each { |accessory_name| details(accessory_name) }
|
KAMAL.accessory_names.each { |accessory_name| details(accessory_name) }
|
||||||
else
|
else
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory, hosts|
|
||||||
on(accessory.hosts) { puts capture_with_info(*accessory.info) }
|
on(hosts) { puts capture_with_info(*accessory.info) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -117,7 +117,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|||||||
option :interactive, aliases: "-i", type: :boolean, default: false, desc: "Execute command over ssh for an interactive shell (use for console/bash)"
|
option :interactive, aliases: "-i", type: :boolean, default: false, desc: "Execute command over ssh for an interactive shell (use for console/bash)"
|
||||||
option :reuse, type: :boolean, default: false, desc: "Reuse currently running container instead of starting a new one"
|
option :reuse, type: :boolean, default: false, desc: "Reuse currently running container instead of starting a new one"
|
||||||
def exec(name, cmd)
|
def exec(name, cmd)
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory, hosts|
|
||||||
case
|
case
|
||||||
when options[:interactive] && options[:reuse]
|
when options[:interactive] && options[:reuse]
|
||||||
say "Launching interactive command with via SSH from existing container...", :magenta
|
say "Launching interactive command with via SSH from existing container...", :magenta
|
||||||
@@ -129,14 +129,14 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|||||||
|
|
||||||
when options[:reuse]
|
when options[:reuse]
|
||||||
say "Launching command from existing container...", :magenta
|
say "Launching command from existing container...", :magenta
|
||||||
on(accessory.hosts) do
|
on(hosts) do
|
||||||
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on #{name} accessory"), verbosity: :debug
|
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on #{name} accessory"), verbosity: :debug
|
||||||
capture_with_info(*accessory.execute_in_existing_container(cmd))
|
capture_with_info(*accessory.execute_in_existing_container(cmd))
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
say "Launching command from new container...", :magenta
|
say "Launching command from new container...", :magenta
|
||||||
on(accessory.hosts) do
|
on(hosts) do
|
||||||
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on #{name} accessory"), verbosity: :debug
|
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on #{name} accessory"), verbosity: :debug
|
||||||
capture_with_info(*accessory.execute_in_new_container(cmd))
|
capture_with_info(*accessory.execute_in_new_container(cmd))
|
||||||
end
|
end
|
||||||
@@ -150,12 +150,12 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|||||||
option :grep, aliases: "-g", desc: "Show lines with grep match only (use this to fetch specific requests by id)"
|
option :grep, aliases: "-g", desc: "Show lines with grep match only (use this to fetch specific requests by id)"
|
||||||
option :follow, aliases: "-f", desc: "Follow logs on primary server (or specific host set by --hosts)"
|
option :follow, aliases: "-f", desc: "Follow logs on primary server (or specific host set by --hosts)"
|
||||||
def logs(name)
|
def logs(name)
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory, hosts|
|
||||||
grep = options[:grep]
|
grep = options[:grep]
|
||||||
|
|
||||||
if options[:follow]
|
if options[:follow]
|
||||||
run_locally do
|
run_locally do
|
||||||
info "Following logs on #{accessory.hosts}..."
|
info "Following logs on #{hosts}..."
|
||||||
info accessory.follow_logs(grep: grep)
|
info accessory.follow_logs(grep: grep)
|
||||||
exec accessory.follow_logs(grep: grep)
|
exec accessory.follow_logs(grep: grep)
|
||||||
end
|
end
|
||||||
@@ -163,7 +163,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|||||||
since = options[:since]
|
since = options[:since]
|
||||||
lines = options[:lines].presence || ((since || grep) ? nil : 100) # Default to 100 lines if since or grep isn't set
|
lines = options[:lines].presence || ((since || grep) ? nil : 100) # Default to 100 lines if since or grep isn't set
|
||||||
|
|
||||||
on(accessory.hosts) do
|
on(hosts) do
|
||||||
puts capture_with_info(*accessory.logs(since: since, lines: lines, grep: grep))
|
puts capture_with_info(*accessory.logs(since: since, lines: lines, grep: grep))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -192,8 +192,8 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|||||||
desc "remove_container [NAME]", "Remove accessory container from host", hide: true
|
desc "remove_container [NAME]", "Remove accessory container from host", hide: true
|
||||||
def remove_container(name)
|
def remove_container(name)
|
||||||
mutating do
|
mutating do
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory, hosts|
|
||||||
on(accessory.hosts) do
|
on(hosts) do
|
||||||
execute *KAMAL.auditor.record("Remove #{name} accessory container"), verbosity: :debug
|
execute *KAMAL.auditor.record("Remove #{name} accessory container"), verbosity: :debug
|
||||||
execute *accessory.remove_container
|
execute *accessory.remove_container
|
||||||
end
|
end
|
||||||
@@ -204,8 +204,8 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|||||||
desc "remove_image [NAME]", "Remove accessory image from host", hide: true
|
desc "remove_image [NAME]", "Remove accessory image from host", hide: true
|
||||||
def remove_image(name)
|
def remove_image(name)
|
||||||
mutating do
|
mutating do
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory, hosts|
|
||||||
on(accessory.hosts) do
|
on(hosts) do
|
||||||
execute *KAMAL.auditor.record("Removed #{name} accessory image"), verbosity: :debug
|
execute *KAMAL.auditor.record("Removed #{name} accessory image"), verbosity: :debug
|
||||||
execute *accessory.remove_image
|
execute *accessory.remove_image
|
||||||
end
|
end
|
||||||
@@ -216,8 +216,8 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|||||||
desc "remove_service_directory [NAME]", "Remove accessory directory used for uploaded files and data directories from host", hide: true
|
desc "remove_service_directory [NAME]", "Remove accessory directory used for uploaded files and data directories from host", hide: true
|
||||||
def remove_service_directory(name)
|
def remove_service_directory(name)
|
||||||
mutating do
|
mutating do
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory, hosts|
|
||||||
on(accessory.hosts) do
|
on(hosts) do
|
||||||
execute *accessory.remove_service_directory
|
execute *accessory.remove_service_directory
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -227,7 +227,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|||||||
private
|
private
|
||||||
def with_accessory(name)
|
def with_accessory(name)
|
||||||
if accessory = KAMAL.accessory(name)
|
if accessory = KAMAL.accessory(name)
|
||||||
yield accessory
|
yield accessory, accessory_hosts(accessory)
|
||||||
else
|
else
|
||||||
error_on_missing_accessory(name)
|
error_on_missing_accessory(name)
|
||||||
end
|
end
|
||||||
@@ -240,4 +240,12 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|||||||
"No accessory by the name of '#{name}'" +
|
"No accessory by the name of '#{name}'" +
|
||||||
(options ? " (options: #{options.to_sentence})" : "")
|
(options ? " (options: #{options.to_sentence})" : "")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def accessory_hosts(accessory)
|
||||||
|
if KAMAL.specific_hosts&.any?
|
||||||
|
KAMAL.specific_hosts & accessory.hosts
|
||||||
|
else
|
||||||
|
accessory.hosts
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -18,12 +18,16 @@ class Kamal::Cli::Prune < Kamal::Cli::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "containers", "Prune all stopped containers, except the last 5"
|
desc "containers", "Prune all stopped containers, except the last n (default 5)"
|
||||||
|
option :retain, type: :numeric, default: nil, desc: "Number of containers to retain"
|
||||||
def containers
|
def containers
|
||||||
|
retain = options.fetch(:retain, KAMAL.config.retain_containers)
|
||||||
|
raise "retain must be at least 1" if retain < 1
|
||||||
|
|
||||||
mutating do
|
mutating do
|
||||||
on(KAMAL.hosts) do
|
on(KAMAL.hosts) do
|
||||||
execute *KAMAL.auditor.record("Pruned containers"), verbosity: :debug
|
execute *KAMAL.auditor.record("Pruned containers"), verbosity: :debug
|
||||||
execute *KAMAL.prune.app_containers
|
execute *KAMAL.prune.app_containers(retain: retain)
|
||||||
execute *KAMAL.prune.healthcheck_containers
|
execute *KAMAL.prune.healthcheck_containers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -77,6 +77,10 @@ registry:
|
|||||||
# Bridge fingerprinted assets, like JS and CSS, between versions to avoid
|
# Bridge fingerprinted assets, like JS and CSS, between versions to avoid
|
||||||
# hitting 404 on in-flight requests. Combines all files from new and old
|
# hitting 404 on in-flight requests. Combines all files from new and old
|
||||||
# version inside the asset_path.
|
# version inside the asset_path.
|
||||||
|
#
|
||||||
|
# If your app is using the Sprockets gem, ensure it sets `config.assets.manifest`.
|
||||||
|
# See https://github.com/basecamp/kamal/issues/626 for details
|
||||||
|
#
|
||||||
# asset_path: /rails/public/assets
|
# asset_path: /rails/public/assets
|
||||||
|
|
||||||
# Configure rolling deploys by setting a wait time between batches of restarts.
|
# Configure rolling deploys by setting a wait time between batches of restarts.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
|
|||||||
class BuilderError < StandardError; end
|
class BuilderError < StandardError; end
|
||||||
|
|
||||||
delegate :argumentize, to: Kamal::Utils
|
delegate :argumentize, to: Kamal::Utils
|
||||||
delegate :args, :secrets, :dockerfile, :local_arch, :local_host, :remote_arch, :remote_host, :cache_from, :cache_to, to: :builder_config
|
delegate :args, :secrets, :dockerfile, :local_arch, :local_host, :remote_arch, :remote_host, :cache_from, :cache_to, :ssh, to: :builder_config
|
||||||
|
|
||||||
def clean
|
def clean
|
||||||
docker :image, :rm, "--force", config.absolute_image
|
docker :image, :rm, "--force", config.absolute_image
|
||||||
@@ -14,7 +14,7 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def build_options
|
def build_options
|
||||||
[ *build_tags, *build_cache, *build_labels, *build_args, *build_secrets, *build_dockerfile ]
|
[ *build_tags, *build_cache, *build_labels, *build_args, *build_secrets, *build_dockerfile, *build_ssh ]
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_context
|
def build_context
|
||||||
@@ -60,6 +60,10 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def build_ssh
|
||||||
|
argumentize "--ssh", ssh if ssh.present?
|
||||||
|
end
|
||||||
|
|
||||||
def builder_config
|
def builder_config
|
||||||
config.builder
|
config.builder
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ class Kamal::Commands::Prune < Kamal::Commands::Base
|
|||||||
"while read image tag; do docker rmi $tag; done"
|
"while read image tag; do docker rmi $tag; done"
|
||||||
end
|
end
|
||||||
|
|
||||||
def app_containers(keep_last: 5)
|
def app_containers(retain:)
|
||||||
pipe \
|
pipe \
|
||||||
docker(:ps, "-q", "-a", *service_filter, *stopped_containers_filters),
|
docker(:ps, "-q", "-a", *service_filter, *stopped_containers_filters),
|
||||||
"tail -n +#{keep_last + 1}",
|
"tail -n +#{retain + 1}",
|
||||||
"while read container_id; do docker rm $container_id; done"
|
"while read container_id; do docker rm $container_id; done"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
class Kamal::Commands::Traefik < Kamal::Commands::Base
|
class Kamal::Commands::Traefik < Kamal::Commands::Base
|
||||||
delegate :argumentize, :optionize, to: Kamal::Utils
|
delegate :argumentize, :optionize, to: Kamal::Utils
|
||||||
|
|
||||||
DEFAULT_IMAGE = "traefik:v2.9"
|
DEFAULT_IMAGE = "traefik:v2.10"
|
||||||
CONTAINER_PORT = 80
|
CONTAINER_PORT = 80
|
||||||
DEFAULT_ARGS = {
|
DEFAULT_ARGS = {
|
||||||
'log.level' => 'DEBUG'
|
'log.level' => 'DEBUG'
|
||||||
|
|||||||
@@ -127,6 +127,10 @@ class Kamal::Configuration
|
|||||||
raw_config.require_destination
|
raw_config.require_destination
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def retain_containers
|
||||||
|
raw_config.retain_containers || 5
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
def volume_args
|
def volume_args
|
||||||
if raw_config.volumes.present?
|
if raw_config.volumes.present?
|
||||||
@@ -218,7 +222,7 @@ class Kamal::Configuration
|
|||||||
|
|
||||||
|
|
||||||
def valid?
|
def valid?
|
||||||
ensure_destination_if_required && ensure_required_keys_present && ensure_valid_kamal_version && ensure_valid_service_name
|
ensure_destination_if_required && ensure_required_keys_present && ensure_valid_kamal_version && ensure_retain_containers_valid && ensure_valid_service_name
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_h
|
def to_h
|
||||||
@@ -297,6 +301,12 @@ class Kamal::Configuration
|
|||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ensure_retain_containers_valid
|
||||||
|
raise ArgumentError, "Must retain at least 1 container" if retain_containers < 1
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
def role_names
|
def role_names
|
||||||
raw_config.servers.is_a?(Array) ? [ "web" ] : raw_config.servers.keys.sort
|
raw_config.servers.is_a?(Array) ? [ "web" ] : raw_config.servers.keys.sort
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class Kamal::Configuration::Accessory
|
|||||||
end
|
end
|
||||||
|
|
||||||
def service_name
|
def service_name
|
||||||
"#{config.service}-#{name}"
|
specifics["service"] || "#{config.service}-#{name}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def image
|
def image
|
||||||
|
|||||||
@@ -81,6 +81,10 @@ class Kamal::Configuration::Builder
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ssh
|
||||||
|
@options["ssh"]
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def valid?
|
def valid?
|
||||||
if @options["cache"] && @options["cache"]["type"]
|
if @options["cache"] && @options["cache"]["type"]
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
require "sshkit"
|
require "sshkit"
|
||||||
require "sshkit/dsl"
|
require "sshkit/dsl"
|
||||||
|
require "net/scp"
|
||||||
require "active_support/core_ext/hash/deep_merge"
|
require "active_support/core_ext/hash/deep_merge"
|
||||||
require "json"
|
require "json"
|
||||||
|
|
||||||
|
|||||||
@@ -148,6 +148,30 @@ class CliAccessoryTest < CliTestCase
|
|||||||
assert_match "rm -rf app-mysql", run_command("remove_service_directory", "mysql")
|
assert_match "rm -rf app-mysql", run_command("remove_service_directory", "mysql")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "hosts param respected" do
|
||||||
|
Kamal::Cli::Accessory.any_instance.expects(:directories).with("redis")
|
||||||
|
Kamal::Cli::Accessory.any_instance.expects(:upload).with("redis")
|
||||||
|
|
||||||
|
run_command("boot", "redis", "--hosts", "1.1.1.1").tap do |output|
|
||||||
|
assert_match /docker login.*on 1.1.1.1/, output
|
||||||
|
refute_match /docker login.*on 1.1.1.2/, output
|
||||||
|
assert_match "docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/env/accessories/app-redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.1", output
|
||||||
|
refute_match "docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/env/accessories/app-redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.2", output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "hosts param intersected with configuration" do
|
||||||
|
Kamal::Cli::Accessory.any_instance.expects(:directories).with("redis")
|
||||||
|
Kamal::Cli::Accessory.any_instance.expects(:upload).with("redis")
|
||||||
|
|
||||||
|
run_command("boot", "redis", "--hosts", "1.1.1.1,1.1.1.3").tap do |output|
|
||||||
|
assert_match /docker login.*on 1.1.1.1/, output
|
||||||
|
refute_match /docker login.*on 1.1.1.3/, output
|
||||||
|
assert_match "docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/env/accessories/app-redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.1", output
|
||||||
|
refute_match "docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/env/accessories/app-redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.3", output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def run_command(*command)
|
def run_command(*command)
|
||||||
stdouted { Kamal::Cli::Accessory.start([*command, "-c", "test/fixtures/deploy_with_accessories.yml"]) }
|
stdouted { Kamal::Cli::Accessory.start([*command, "-c", "test/fixtures/deploy_with_accessories.yml"]) }
|
||||||
|
|||||||
@@ -20,6 +20,15 @@ class CliPruneTest < CliTestCase
|
|||||||
assert_match /docker ps -q -a --filter label=service=app --filter status=created --filter status=exited --filter status=dead | tail -n +6 | while read container_id; do docker rm $container_id; done on 1.1.1.\d/, output
|
assert_match /docker ps -q -a --filter label=service=app --filter status=created --filter status=exited --filter status=dead | tail -n +6 | while read container_id; do docker rm $container_id; done on 1.1.1.\d/, output
|
||||||
assert_match /docker container prune --force --filter label=service=healthcheck-app on 1.1.1.\d/, output
|
assert_match /docker container prune --force --filter label=service=healthcheck-app on 1.1.1.\d/, output
|
||||||
end
|
end
|
||||||
|
|
||||||
|
run_command("containers", "--retain", "10").tap do |output|
|
||||||
|
assert_match /docker ps -q -a --filter label=service=app --filter status=created --filter status=exited --filter status=dead | tail -n +11 | while read container_id; do docker rm $container_id; done on 1.1.1.\d/, output
|
||||||
|
assert_match /docker container prune --force --filter label=service=healthcheck-app on 1.1.1.\d/, output
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_raises(RuntimeError, "retain must be at least 1") do
|
||||||
|
run_command("containers", "--retain", "0")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"busybox" => {
|
"busybox" => {
|
||||||
|
"service" => "custom-busybox",
|
||||||
"image" => "busybox:latest",
|
"image" => "busybox:latest",
|
||||||
"host" => "1.1.1.7"
|
"host" => "1.1.1.7"
|
||||||
}
|
}
|
||||||
@@ -57,7 +58,7 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
|
|||||||
new_command(:redis).run.join(" ")
|
new_command(:redis).run.join(" ")
|
||||||
|
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name app-busybox --detach --restart unless-stopped --log-opt max-size=\"10m\" --env-file .kamal/env/accessories/app-busybox.env --label service=\"app-busybox\" busybox:latest",
|
"docker run --name custom-busybox --detach --restart unless-stopped --log-opt max-size=\"10m\" --env-file .kamal/env/accessories/custom-busybox.env --label service=\"custom-busybox\" busybox:latest",
|
||||||
new_command(:busybox).run.join(" ")
|
new_command(:busybox).run.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
|
|||||||
@config[:logging] = { "driver" => "local", "options" => { "max-size" => "100m", "max-file" => "3" } }
|
@config[:logging] = { "driver" => "local", "options" => { "max-size" => "100m", "max-file" => "3" } }
|
||||||
|
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name app-busybox --detach --restart unless-stopped --log-driver \"local\" --log-opt max-size=\"100m\" --log-opt max-file=\"3\" --env-file .kamal/env/accessories/app-busybox.env --label service=\"app-busybox\" busybox:latest",
|
"docker run --name custom-busybox --detach --restart unless-stopped --log-driver \"local\" --log-opt max-size=\"100m\" --log-opt max-file=\"3\" --env-file .kamal/env/accessories/custom-busybox.env --label service=\"custom-busybox\" busybox:latest",
|
||||||
new_command(:busybox).run.join(" ")
|
new_command(:busybox).run.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -111,6 +111,14 @@ class CommandsBuilderTest < ActiveSupport::TestCase
|
|||||||
builder.push.join(" ")
|
builder.push.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "build with ssh agent socket" do
|
||||||
|
builder = new_builder_command(builder: { "ssh" => 'default=$SSH_AUTH_SOCK' })
|
||||||
|
|
||||||
|
assert_equal \
|
||||||
|
"-t dhh/app:123 -t dhh/app:latest --label service=\"app\" --file Dockerfile --ssh default=$SSH_AUTH_SOCK",
|
||||||
|
builder.target.build_options.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
test "validate image" do
|
test "validate image" do
|
||||||
assert_equal "docker inspect -f '{{ .Config.Labels.service }}' dhh/app:123 | grep -x app || (echo \"Image dhh/app:123 is missing the `service` label\" && exit 1)", new_builder_command.validate_image.join(" ")
|
assert_equal "docker inspect -f '{{ .Config.Labels.service }}' dhh/app:123 | grep -x app || (echo \"Image dhh/app:123 is missing the `service` label\" && exit 1)", new_builder_command.validate_image.join(" ")
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -23,7 +23,11 @@ class CommandsPruneTest < ActiveSupport::TestCase
|
|||||||
test "app containers" do
|
test "app containers" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker ps -q -a --filter label=service=app --filter status=created --filter status=exited --filter status=dead | tail -n +6 | while read container_id; do docker rm $container_id; done",
|
"docker ps -q -a --filter label=service=app --filter status=created --filter status=exited --filter status=dead | tail -n +6 | while read container_id; do docker rm $container_id; done",
|
||||||
new_command.app_containers.join(" ")
|
new_command.app_containers(retain: 5).join(" ")
|
||||||
|
|
||||||
|
assert_equal \
|
||||||
|
"docker ps -q -a --filter label=service=app --filter status=created --filter status=exited --filter status=dead | tail -n +4 | while read container_id; do docker rm $container_id; done",
|
||||||
|
new_command.app_containers(retain: 3).join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "healthcheck containers" do
|
test "healthcheck containers" do
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"monitoring" => {
|
"monitoring" => {
|
||||||
|
"service" => "custom-monitoring",
|
||||||
"image" => "monitoring:latest",
|
"image" => "monitoring:latest",
|
||||||
"roles" => [ "web" ],
|
"roles" => [ "web" ],
|
||||||
"port" => "4321:4321",
|
"port" => "4321:4321",
|
||||||
@@ -72,6 +73,7 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase
|
|||||||
test "service name" do
|
test "service name" do
|
||||||
assert_equal "app-mysql", @config.accessory(:mysql).service_name
|
assert_equal "app-mysql", @config.accessory(:mysql).service_name
|
||||||
assert_equal "app-redis", @config.accessory(:redis).service_name
|
assert_equal "app-redis", @config.accessory(:redis).service_name
|
||||||
|
assert_equal "custom-monitoring", @config.accessory(:monitoring).service_name
|
||||||
end
|
end
|
||||||
|
|
||||||
test "port" do
|
test "port" do
|
||||||
|
|||||||
@@ -148,4 +148,14 @@ class ConfigurationBuilderTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
assert_equal "..", @config_with_builder_option.builder.context
|
assert_equal "..", @config_with_builder_option.builder.context
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "ssh" do
|
||||||
|
assert_nil @config.builder.ssh
|
||||||
|
end
|
||||||
|
|
||||||
|
test "setting ssh params" do
|
||||||
|
@deploy_with_builder_option[:builder] = { "ssh" => 'default=$SSH_AUTH_SOCK' }
|
||||||
|
|
||||||
|
assert_equal 'default=$SSH_AUTH_SOCK', @config_with_builder_option.builder.ssh
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -319,4 +319,12 @@ class ConfigurationTest < ActiveSupport::TestCase
|
|||||||
end
|
end
|
||||||
assert_match /bar isn't defined/, error.message
|
assert_match /bar isn't defined/, error.message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "retain_containers" do
|
||||||
|
assert_equal 5, @config.retain_containers
|
||||||
|
config = Kamal::Configuration.new(@deploy_with_roles.merge(retain_containers: 2))
|
||||||
|
assert_equal 2, config.retain_containers
|
||||||
|
|
||||||
|
assert_raises(ArgumentError) { Kamal::Configuration.new(@deploy_with_roles.merge(retain_containers: 0)) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -24,9 +24,10 @@ traefik:
|
|||||||
args:
|
args:
|
||||||
accesslog: true
|
accesslog: true
|
||||||
accesslog.format: json
|
accesslog.format: json
|
||||||
image: registry:4443/traefik:v2.9
|
image: registry:4443/traefik:v2.10
|
||||||
accessories:
|
accessories:
|
||||||
busybox:
|
busybox:
|
||||||
|
service: custom-busybox
|
||||||
image: registry:4443/busybox:1.36.0
|
image: registry:4443/busybox:1.36.0
|
||||||
cmd: sh -c 'echo "Starting busybox..."; trap exit term; while true; do sleep 1; done'
|
cmd: sh -c 'echo "Starting busybox..."; trap exit term; while true; do sleep 1; done'
|
||||||
roles:
|
roles:
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ push_image_to_registry_4443() {
|
|||||||
|
|
||||||
install_kamal
|
install_kamal
|
||||||
push_image_to_registry_4443 nginx 1-alpine-slim
|
push_image_to_registry_4443 nginx 1-alpine-slim
|
||||||
push_image_to_registry_4443 traefik v2.9
|
push_image_to_registry_4443 traefik v2.10
|
||||||
push_image_to_registry_4443 busybox 1.36.0
|
push_image_to_registry_4443 busybox 1.36.0
|
||||||
|
|
||||||
# .ssh is on a shared volume that persists between runs. Clean it up as the
|
# .ssh is on a shared volume that persists between runs. Clean it up as the
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class MainTest < IntegrationTest
|
|||||||
assert_match /Traefik Host: vm2/, details
|
assert_match /Traefik Host: vm2/, details
|
||||||
assert_match /App Host: vm1/, details
|
assert_match /App Host: vm1/, details
|
||||||
assert_match /App Host: vm2/, details
|
assert_match /App Host: vm2/, details
|
||||||
assert_match /traefik:v2.9/, details
|
assert_match /traefik:v2.10/, details
|
||||||
assert_match /registry:4443\/app:#{first_version}/, details
|
assert_match /registry:4443\/app:#{first_version}/, details
|
||||||
|
|
||||||
audit = kamal :audit, capture: true
|
audit = kamal :audit, capture: true
|
||||||
|
|||||||
@@ -52,11 +52,11 @@ class TraefikTest < IntegrationTest
|
|||||||
|
|
||||||
private
|
private
|
||||||
def assert_traefik_running
|
def assert_traefik_running
|
||||||
assert_match /traefik:v2.9 "\/entrypoint.sh/, traefik_details
|
assert_match /traefik:v2.10 "\/entrypoint.sh/, traefik_details
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_traefik_not_running
|
def assert_traefik_not_running
|
||||||
refute_match /traefik:v2.9 "\/entrypoint.sh/, traefik_details
|
refute_match /traefik:v2.10 "\/entrypoint.sh/, traefik_details
|
||||||
end
|
end
|
||||||
|
|
||||||
def traefik_details
|
def traefik_details
|
||||||
|
|||||||
Reference in New Issue
Block a user