Compare commits

..

18 Commits

Author SHA1 Message Date
Donal McBreen
46ea88a056 Bump version for 1.9.1 2024-09-30 08:49:47 +01:00
Donal McBreen
fa05270cac Merge pull request #997 from basecamp/traefik-2.11
Traefik 2.11 default to address CVE-2024-45410
2024-09-30 03:14:08 -04:00
Jeremy Daer
b058c45973 Traefik 2.11 default to address CVE-2024-45410
Fixes #968
2024-09-28 11:28:50 -04:00
Donal McBreen
9db1403721 Bump version for 1.9.0 2024-09-26 15:30:08 -04:00
Donal McBreen
bf4add9e72 Merge pull request #946 from basecamp/kamal-2.0-downgrade
Downgrade from Kamal 2 to 1.9
2024-09-18 10:27:40 +01:00
Donal McBreen
7c7785c1eb Downgrade from Kamal 2 to 1.9
Add a downgrade command, so you can reverse the upgrade process and go
back to Kamal 1.9. This replaces kamal-proxy and reboots all the
accessories.

This gives an upgrade and downgrade path:

Upgrade:
1. Upgrade config to be Kamal 2 compatible + use kamal 2.0
2. Run `kamal upgrade`

Downgrade:
1. Switch back to previous config + use kamal 1.9
2. Run `kamal downgrade`

You can set `--rolling` to downgrade one host at a time.
2024-09-18 10:11:32 +01:00
Donal McBreen
80bd46cde3 Bump version for 1.8.3 2024-09-02 15:51:11 +01:00
Donal McBreen
b449321a45 CI on push 2024-09-02 15:38:58 +01:00
Donal McBreen
24a7e94c14 Merge pull request #922 from basecamp/hybrid-build-both-arches
Build both arches with remote multarch builder
2024-09-02 15:37:28 +01:00
Donal McBreen
d269fc5d36 Build both arches with remote multarch builder
When using the remote build arch builder, build with both arches.
2024-09-02 15:22:18 +01:00
Donal McBreen
d6f5da92be Bump version for 1.8.2 2024-08-28 09:43:06 +01:00
Donal McBreen
9ccfe20b10 Fix up tests 2024-08-26 11:20:26 +01:00
Donal McBreen
e871d347d5 Merge pull request #889 from xiaohui-zhangxh/git-clone-update-submodules
git clone with --recurse-submodules
2024-08-26 11:20:05 +01:00
Donal McBreen
f48987aa03 Merge pull request #903 from basecamp/integration-test-insecure-registry
Integration test insecure registry
2024-08-01 09:57:17 +01:00
Donal McBreen
ef051eca1b Merge pull request #904 from galori/main
Fixed typo in `env.yml`: "valies" --> "values"
2024-08-01 09:57:03 +01:00
Gall Steinitz
173d44ee0a fixed typo in env.yml: valies --> values 2024-07-31 22:12:21 -07:00
Donal McBreen
4e811372f8 Integration test insecure registry
The integrations tests use their own registry so avoid hitting docker
hub rate limits.

This was using a self signed certificate but instead use
`--insecure-registry` to let the docker daemon use HTTP.
2024-07-31 16:54:00 +01:00
xiaohui
b12de87388 git clone with --recurse-submodules 2024-07-17 10:36:58 +08:00
46 changed files with 429 additions and 117 deletions

View File

@@ -3,6 +3,7 @@ on:
push:
branches:
- main
- 1-8-stable
pull_request:
jobs:
rubocop:

View File

@@ -1,7 +1,7 @@
PATH
remote: .
specs:
kamal (1.8.1)
kamal (1.9.1)
activesupport (>= 7.0)
base64 (~> 0.2)
bcrypt_pbkdf (~> 1.0)

View File

@@ -222,6 +222,25 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
end
end
desc "downgrade", "Downgrade accessories from Kamal 2 to 1.9"
option :rolling, type: :boolean, default: false, desc: "Upgrade one host at a time"
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
def downgrade(name)
confirming "This will restart all accessories" do
with_lock do
host_groups = options[:rolling] ? KAMAL.accessory_hosts : [ KAMAL.accessory_hosts ]
host_groups.each do |hosts|
host_list = Array(hosts).join(",")
KAMAL.with_specific_hosts(hosts) do
say "Downgrading #{name} accessories on #{host_list}...", :magenta
reboot name
say "Downgraded #{name} accessories on #{host_list}...", :magenta
end
end
end
end
end
private
def with_accessory(name)
if KAMAL.config.accessory(name)

View File

@@ -22,48 +22,50 @@ module Kamal::Cli
class_option :skip_hooks, aliases: "-H", type: :boolean, default: false, desc: "Don't run hooks"
@@ran_pre_init_hook = false
class << self
def ran_pre_init_hook
@@ran_pre_init_hook
end
def ran_pre_init_hook=(value)
@@ran_pre_init_hook = value
end
end
def initialize(*)
super
@original_env = ENV.to_h.dup
run_pre_init_hook
load_env
initialize_commander(options_with_subcommand_class_options)
end
private
def reload_env
reset_env
load_env
end
def load_env
if destination = options[:destination]
if File.exist?(".kamal/.env.#{destination}") || File.exist?(".kamal/.env")
Dotenv.load(".kamal/.env.#{destination}", ".kamal/.env")
else
loading_files = [ (".env" if File.exist?(".env")), (".env.#{destination}" if File.exist?(".env.#{destination}")) ].compact
if loading_files.any?
warn "Loading #{loading_files.join(" and ")} from the project root, in future they will be loaded from .kamal/"
Dotenv.load(".env.#{destination}", ".env")
end
end
Dotenv.load(".env.#{destination}", ".env")
else
if File.exist?(".kamal/.env")
Dotenv.load(".kamal/.env")
elsif File.exist?(".env")
$stderr.puts caller
warn "Loading .env from the project root, in future it will be loaded then from .kamal/.env"
Dotenv.load(".env")
end
Dotenv.load(".env")
end
end
def reset_env
replace_env @original_env
end
def replace_env(env)
ENV.clear
ENV.update(env)
end
def with_original_env
keeping_current_env do
reset_env
yield
end
end
def keeping_current_env
current_env = ENV.to_h.dup
yield
ensure
replace_env(current_env)
end
def options_with_subcommand_class_options
options.merge(@_initializer.last[:class_options] || {})
end
@@ -160,23 +162,8 @@ module Kamal::Cli
end
end
def run_pre_init_hook
unless self.class.ran_pre_init_hook
hook = "pre-init"
if run_hook?(hook)
say "Running the #{hook} hook...", :magenta
run_locally do
execute *Kamal::Hooks.file(hook), verbosity: :debug
rescue SSHKit::Command::Failed => e
raise HookError.new("Hook `#{hook}` failed:\n#{e.message}")
end
end
self.class.ran_pre_init_hook = true
end
end
def run_hook(hook, **extra_details)
if run_hook?(hook)
if !options[:skip_hooks] && KAMAL.hook.hook_exists?(hook)
details = { hosts: KAMAL.hosts.join(","), command: command, subcommand: subcommand }
say "Running the #{hook} hook...", :magenta
@@ -188,10 +175,6 @@ module Kamal::Cli
end
end
def run_hook?(hook)
!options[:skip_hooks] && Kamal::Hooks.exists?(hook)
end
def on(*args, &block)
if !KAMAL.connected?
run_hook "pre-connect"
@@ -223,6 +206,10 @@ module Kamal::Cli
instance_variable_get("@_invocations").first
end
def reset_invocation(cli_class)
instance_variable_get("@_invocations")[cli_class].pop
end
def ensure_run_and_locks_directory
on(KAMAL.hosts) do
execute(*KAMAL.server.ensure_run_directory)

View File

@@ -10,6 +10,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
invoke "kamal:cli:server:bootstrap", [], invoke_options
say "Evaluate and push env files...", :magenta
invoke "kamal:cli:main:envify", [], invoke_options
invoke "kamal:cli:env:push", [], invoke_options
invoke "kamal:cli:accessory:boot", [ "all" ], invoke_options
@@ -178,6 +179,31 @@ class Kamal::Cli::Main < Kamal::Cli::Base
end
end
desc "envify", "Create .env by evaluating .env.erb (or .env.staging.erb -> .env.staging when using -d staging)"
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip .env file push"
def envify
if destination = options[:destination]
env_template_path = ".env.#{destination}.erb"
env_path = ".env.#{destination}"
else
env_template_path = ".env.erb"
env_path = ".env"
end
if Pathname.new(File.expand_path(env_template_path)).exist?
# Ensure existing env doesn't pollute template evaluation
content = with_original_env { ERB.new(File.read(env_template_path), trim_mode: "-").result }
File.write(env_path, content, perm: 0600)
unless options[:skip_push]
reload_env
invoke "kamal:cli:env:push", options
end
else
puts "Skipping envify (no #{env_template_path} exist)"
end
end
desc "remove", "Remove Traefik, app, accessories, and registry session from servers"
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
def remove
@@ -191,6 +217,37 @@ class Kamal::Cli::Main < Kamal::Cli::Base
end
end
desc "downgrade", "Downgrade from Kamal 2 to 1.9"
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
option :rolling, type: :boolean, default: false, desc: "Downgrade one host at a time"
def downgrade
confirming "This will replace Traefik with kamal-proxy and restart all accessories" do
with_lock do
if options[:rolling]
(KAMAL.hosts | KAMAL.accessory_hosts).each do |host|
KAMAL.with_specific_hosts(host) do
say "Downgrading #{host}...", :magenta
if KAMAL.hosts.include?(host)
invoke "kamal:cli:traefik:downgrade", [], options.merge(confirmed: true, rolling: false)
reset_invocation(Kamal::Cli::Traefik)
end
if KAMAL.accessory_hosts.include?(host)
invoke "kamal:cli:accessory:downgrade", [ "all" ], options.merge(confirmed: true, rolling: false)
reset_invocation(Kamal::Cli::Accessory)
end
say "Downgraded #{host}", :magenta
end
end
else
say "Downgrading all hosts...", :magenta
invoke "kamal:cli:traefik:downgrade", [], options.merge(confirmed: true)
invoke "kamal:cli:accessory:downgrade", [ "all" ], options.merge(confirmed: true)
say "Downgraded all hosts", :magenta
end
end
end
end
desc "version", "Show Kamal version"
def version
puts Kamal::VERSION

View File

@@ -119,4 +119,44 @@ class Kamal::Cli::Traefik < Kamal::Cli::Base
end
end
end
desc "downgrade", "Downgrade to Traefik on servers (stop container, remove container, start new container, reboot app)"
option :rolling, type: :boolean, default: false, desc: "Reboot proxy on hosts in sequence, rather than in parallel"
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
def downgrade
invoke_options = { "version" => KAMAL.config.latest_tag }.merge(options)
confirming "This will cause a brief outage on each host. Are you sure?" do
host_groups = options[:rolling] ? KAMAL.hosts : [ KAMAL.hosts ]
host_groups.each do |hosts|
host_list = Array(hosts).join(",")
say "Downgrading to Traefik on #{host_list}...", :magenta
run_hook "pre-traefik-reboot", hosts: host_list
on(hosts) do |host|
execute *KAMAL.auditor.record("Rebooted Traefik"), verbosity: :debug
execute *KAMAL.registry.login
"Stopping and removing kamal-proxy on #{host}, if running..."
execute *KAMAL.traefik.cleanup_kamal_proxy
"Stopping and removing Traefik on #{host}, if running..."
execute *KAMAL.traefik.stop, raise_on_non_zero_exit: false
execute *KAMAL.traefik.remove_container
execute *KAMAL.traefik.remove_image
end
KAMAL.with_specific_hosts(hosts) do
invoke "kamal:cli:traefik:boot", [], invoke_options
reset_invocation(Kamal::Cli::Traefik)
invoke "kamal:cli:app:boot", [], invoke_options
reset_invocation(Kamal::Cli::App)
invoke "kamal:cli:prune:all", [], invoke_options
reset_invocation(Kamal::Cli::Prune)
end
run_hook "post-traefik-reboot", hosts: host_list
say "Downgraded to Traefik on #{host_list}", :magenta
end
end
end
end

View File

@@ -56,6 +56,13 @@ class Kamal::Commander
end
end
def with_specific_hosts(hosts)
original_hosts, self.specific_hosts = specific_hosts, hosts
yield
ensure
self.specific_hosts = original_hosts
end
def accessory_names
config.accessories&.collect(&:name) || []
end

View File

@@ -23,7 +23,7 @@ class Kamal::Commander::Specifics
end
def accessory_hosts
specific_hosts || config.accessories.flat_map(&:hosts)
config.accessories.flat_map(&:hosts) & specified_hosts
end
private

View File

@@ -6,7 +6,7 @@ module Kamal::Commands::Builder::Clone
end
def clone
git :clone, Kamal::Git.root, path: clone_directory
git :clone, Kamal::Git.root, "--recurse-submodules", path: clone_directory
end
def clone_reset_steps
@@ -14,7 +14,8 @@ module Kamal::Commands::Builder::Clone
git(:remote, "set-url", :origin, Kamal::Git.root, path: build_directory),
git(:fetch, :origin, path: build_directory),
git(:reset, "--hard", Kamal::Git.revision, path: build_directory),
git(:clean, "-fdx", path: build_directory)
git(:clean, "-fdx", path: build_directory),
git(:submodule, :update, "--init", path: build_directory)
]
end

View File

@@ -58,4 +58,8 @@ class Kamal::Commands::Builder::Multiarch::Remote < Kamal::Commands::Builder::Mu
def remove_context(arch)
docker :context, :rm, builder_name_with_arch(arch)
end
def platform_names
"linux/#{local_arch},linux/#{remote_arch}"
end
end

View File

@@ -1,5 +1,14 @@
class Kamal::Commands::Hook < Kamal::Commands::Base
def run(hook, **details)
[ Kamal::Hooks.file(hook), env: tags(**details).env ]
[ hook_file(hook), env: tags(**details).env ]
end
def hook_exists?(hook)
Pathname.new(hook_file(hook)).exist?
end
private
def hook_file(hook)
File.join(config.hooks_path, hook)
end
end

View File

@@ -62,6 +62,15 @@ class Kamal::Commands::Traefik < Kamal::Commands::Base
[ :rm, "-f", env.secrets_file ]
end
def cleanup_kamal_proxy
chain \
docker(:container, :stop, "kamal-proxy"),
combine(
docker(:container, :prune, "--force", "--filter", "label=org.opencontainers.image.title=kamal-proxy"),
docker(:image, :prune, "--all", "--force", "--filter", "label=org.opencontainers.image.title=kamal-proxy")
)
end
private
def publish_args
argumentize "--publish", port if publish?

View File

@@ -7,7 +7,7 @@ require "erb"
require "net/ssh/proxy/jump"
class Kamal::Configuration
delegate :service, :image, :labels, :stop_wait_time, to: :raw_config, allow_nil: true
delegate :service, :image, :labels, :stop_wait_time, :hooks_path, to: :raw_config, allow_nil: true
delegate :argumentize, :optionize, to: Kamal::Utils
attr_reader :destination, :raw_config
@@ -208,6 +208,10 @@ class Kamal::Configuration
end
end
def hooks_path
raw_config.hooks_path || ".kamal/hooks"
end
def asset_path
raw_config.asset_path
end

View File

@@ -74,6 +74,10 @@ env:
# To configure this, set the path to the assets:
asset_path: /path/to/assets
# Path to hooks, defaults to `.kamal/hooks`
# See https://kamal-deploy.org/docs/hooks for more information
hooks_path: /user_home/kamal/hooks
# Require destinations
#
# Whether deployments require a destination to be specified, defaults to `false`

View File

@@ -24,14 +24,14 @@ env:
# KAMAL_REGISTRY_PASSWORD=pw
# DB_PASSWORD=secret123
# ```
# See https://kamal-deploy.org/docs/commands/env/ for how to use generated .env files.
# See https://kamal-deploy.org/docs/commands/envify/ for how to use generated .env files.
#
# To pass the secrets you should list them under the `secret` key. When you do this the
# other variables need to be moved under the `clear` key.
#
# Unlike clear values, secrets are not passed directly to the container,
# but are stored in an env file on the host
# The file is not updated when deploying, only when running `kamal env push`.
# The file is not updated when deploying, only when running `kamal envify` or `kamal env push`.
env:
clear:
DB_USER: app

View File

@@ -17,8 +17,8 @@ traefik:
# Image
#
# The Traefik image to use, defaults to `traefik:v2.10`
image: traefik:v2.9
# The Traefik image to use, defaults to `traefik:v2.11`
image: traefik:v2.11
# Host port
#

View File

@@ -1,5 +1,5 @@
class Kamal::Configuration::Traefik
DEFAULT_IMAGE = "traefik:v2.10"
DEFAULT_IMAGE = "traefik:v2.11"
CONTAINER_PORT = 80
DEFAULT_ARGS = {
"log.level" => "DEBUG"

View File

@@ -1,3 +1,3 @@
module Kamal
VERSION = "1.8.1"
VERSION = "1.9.1"
end

View File

@@ -209,6 +209,24 @@ class CliAccessoryTest < CliTestCase
end
end
test "downgrade" do
run_command("downgrade", "-y", "all").tap do |output|
assert_match "Downgrading all accessories on 1.1.1.3,1.1.1.1,1.1.1.2...", output
assert_match "docker container stop app-mysql on 1.1.1.3", output
assert_match "docker run --name app-mysql --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 3306:3306 --env-file .kamal/env/accessories/app-mysql.env --env MYSQL_ROOT_HOST=\"%\" --volume $PWD/app-mysql/etc/mysql/my.cnf:/etc/mysql/my.cnf --volume $PWD/app-mysql/data:/var/lib/mysql --label service=\"app-mysql\" mysql:5.7 on 1.1.1.3", output
assert_match "Downgraded all accessories on 1.1.1.3,1.1.1.1,1.1.1.2", output
end
end
test "downgrade rolling" do
run_command("downgrade", "--rolling", "-y", "all").tap do |output|
assert_match "Downgrading all accessories on 1.1.1.3...", output
assert_match "docker container stop app-mysql on 1.1.1.3", output
assert_match "docker run --name app-mysql --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 3306:3306 --env-file .kamal/env/accessories/app-mysql.env --env MYSQL_ROOT_HOST=\"%\" --volume $PWD/app-mysql/etc/mysql/my.cnf:/etc/mysql/my.cnf --volume $PWD/app-mysql/data:/var/lib/mysql --label service=\"app-mysql\" mysql:5.7 on 1.1.1.3", output
assert_match "Downgraded all accessories on 1.1.1.3", output
end
end
private
def run_command(*command)
stdouted { Kamal::Cli::Accessory.start([ *command, "-c", "test/fixtures/deploy_with_accessories.yml" ]) }

View File

@@ -10,7 +10,7 @@ class CliBuildTest < CliTestCase
test "push" do
with_build_directory do |build_directory|
Kamal::Hooks.stubs(:exists?).returns(true)
Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
hook_variables = { version: 999, service_version: "app@999", hosts: "1.1.1.1,1.1.1.2,1.1.1.3,1.1.1.4", command: "build", subcommand: "push" }
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
@@ -42,7 +42,7 @@ class CliBuildTest < CliTestCase
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:docker, "--version", "&&", :docker, :buildx, "version")
SSHKit::Backend::Abstract.any_instance.expects(:execute)
.with(:git, "-C", "#{Dir.tmpdir}/kamal-clones/app-#{pwd_sha}", :clone, Dir.pwd)
.with(:git, "-C", "#{Dir.tmpdir}/kamal-clones/app-#{pwd_sha}", :clone, Dir.pwd, "--recurse-submodules")
.raises(SSHKit::Command::Failed.new("fatal: destination path 'kamal' already exists and is not an empty directory"))
.then
.returns(true)
@@ -50,6 +50,7 @@ class CliBuildTest < CliTestCase
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:git, "-C", build_directory, :fetch, :origin)
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:git, "-C", build_directory, :reset, "--hard", Kamal::Git.revision)
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:git, "-C", build_directory, :clean, "-fdx")
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:git, "-C", build_directory, :submodule, :update, "--init")
SSHKit::Backend::Abstract.any_instance.expects(:execute)
.with(:docker, :buildx, :build, "--push", "--platform", "linux/amd64,linux/arm64", "--builder", "kamal-app-multiarch", "-t", "dhh/app:999", "-t", "dhh/app:latest", "--label", "service=\"app\"", "--file", "Dockerfile", ".")
@@ -70,7 +71,7 @@ class CliBuildTest < CliTestCase
end
test "push without clone" do
Kamal::Hooks.stubs(:exists?).returns(true)
Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
hook_variables = { version: 999, service_version: "app@999", hosts: "1.1.1.1,1.1.1.2,1.1.1.3,1.1.1.4", command: "build", subcommand: "push" }
run_command("push", "--verbose", fixture: :without_clone).tap do |output|
@@ -88,7 +89,7 @@ class CliBuildTest < CliTestCase
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:docker, "--version", "&&", :docker, :buildx, "version")
SSHKit::Backend::Abstract.any_instance.expects(:execute)
.with(:git, "-C", "#{Dir.tmpdir}/kamal-clones/app-#{pwd_sha}", :clone, Dir.pwd)
.with(:git, "-C", "#{Dir.tmpdir}/kamal-clones/app-#{pwd_sha}", :clone, Dir.pwd, "--recurse-submodules")
.raises(SSHKit::Command::Failed.new("fatal: destination path 'kamal' already exists and is not an empty directory"))
.then
.returns(true)

View File

@@ -18,7 +18,7 @@ class CliTestCase < ActiveSupport::TestCase
private
def fail_hook(hook)
@executions = []
Kamal::Hooks.stubs(:exists?).returns(true)
Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |*args| @executions << args; args != [ ".kamal/hooks/#{hook}" ] }

View File

@@ -8,6 +8,7 @@ class CliMainTest < CliTestCase
invoke_options = { "config_file" => "test/fixtures/deploy_simple.yml", "version" => "999", "skip_hooks" => false }
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:server:bootstrap", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:main:envify", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:env:push", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:accessory:boot", [ "all" ], invoke_options)
Kamal::Cli::Main.any_instance.expects(:deploy)
@@ -23,6 +24,7 @@ class CliMainTest < CliTestCase
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:server:bootstrap", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:env:push", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:main:envify", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:accessory:boot", [ "all" ], invoke_options)
# deploy
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:registry:login", [], invoke_options.merge(skip_local: true))
@@ -56,11 +58,10 @@ class CliMainTest < CliTestCase
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:boot", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:prune:all", [], invoke_options)
Kamal::Hooks.stubs(:exists?).returns(true)
Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
hook_variables = { version: 999, service_version: "app@999", hosts: "1.1.1.1,1.1.1.2", command: "deploy" }
run_command("deploy", "--verbose").tap do |output|
assert_match "Running the pre-init hook...", output
assert_hook_ran "pre-connect", output, **hook_variables
assert_match /Log into image registry/, output
assert_match /Build and push app image/, output
@@ -236,7 +237,7 @@ class CliMainTest < CliTestCase
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true))
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:boot", [], invoke_options)
Kamal::Hooks.stubs(:exists?).returns(true)
Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
hook_variables = { version: 999, service_version: "app@999", hosts: "1.1.1.1,1.1.1.2", command: "redeploy" }
@@ -297,7 +298,7 @@ class CliMainTest < CliTestCase
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-version-to-rollback$", "--quiet", "|", :xargs, :docker, :inspect, "--format", "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'")
.returns("unhealthy").at_least_once # health check
Kamal::Hooks.stubs(:exists?).returns(true)
Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
hook_variables = { version: 123, service_version: "app@123", hosts: "1.1.1.1,1.1.1.2,1.1.1.3,1.1.1.4", command: "rollback" }
run_command("rollback", "--verbose", "123", config_file: "deploy_with_accessories").tap do |output|
@@ -395,7 +396,7 @@ class CliMainTest < CliTestCase
end
test "init" do
Pathname.any_instance.expects(:exist?).returns(false).times(4)
Pathname.any_instance.expects(:exist?).returns(false).times(3)
Pathname.any_instance.stubs(:mkpath)
FileUtils.stubs(:mkdir_p)
FileUtils.stubs(:cp_r)
@@ -408,7 +409,7 @@ class CliMainTest < CliTestCase
end
test "init with existing config" do
Pathname.any_instance.expects(:exist?).returns(true).times(4)
Pathname.any_instance.expects(:exist?).returns(true).times(3)
run_command("init").tap do |output|
assert_match /Config file already exists in config\/deploy.yml \(remove first to create a new one\)/, output
@@ -416,7 +417,7 @@ class CliMainTest < CliTestCase
end
test "init with bundle option" do
Pathname.any_instance.expects(:exist?).returns(false).times(5)
Pathname.any_instance.expects(:exist?).returns(false).times(4)
Pathname.any_instance.stubs(:mkpath)
FileUtils.stubs(:mkdir_p)
FileUtils.stubs(:cp_r)
@@ -433,7 +434,7 @@ class CliMainTest < CliTestCase
end
test "init with bundle option and existing binstub" do
Pathname.any_instance.expects(:exist?).returns(true).times(5)
Pathname.any_instance.expects(:exist?).returns(true).times(4)
Pathname.any_instance.stubs(:mkpath)
FileUtils.stubs(:mkdir_p)
FileUtils.stubs(:cp_r)
@@ -445,6 +446,50 @@ class CliMainTest < CliTestCase
end
end
test "envify" do
with_test_dotenv(".env.erb": "HELLO=<%= 'world' %>") do
run_command("envify")
assert_equal("HELLO=world", File.read(".env"))
end
end
test "envify with blank line trimming" do
file = <<~EOF
HELLO=<%= 'world' %>
<% if true -%>
KEY=value
<% end -%>
EOF
with_test_dotenv(".env.erb": file) do
run_command("envify")
assert_equal("HELLO=world\nKEY=value\n", File.read(".env"))
end
end
test "envify with destination" do
with_test_dotenv(".env.world.erb": "HELLO=<%= 'world' %>") do
run_command("envify", "-d", "world", config_file: "deploy_for_dest")
assert_equal "HELLO=world", File.read(".env.world")
end
end
test "envify with skip_push" do
Pathname.any_instance.expects(:exist?).returns(true).times(1)
File.expects(:read).with(".env.erb").returns("HELLO=<%= 'world' %>")
File.expects(:write).with(".env", "HELLO=world", perm: 0600)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:env:push").never
run_command("envify", "--skip-push")
end
test "envify with clean env" do
with_test_dotenv(".env": "HELLO=already", ".env.erb": "HELLO=<%= ENV.fetch 'HELLO', 'never' %>") do
run_command("envify", "--skip-push")
assert_equal "HELLO=never", File.read(".env")
end
end
test "remove with confirmation" do
run_command("remove", "-y", config_file: "deploy_with_accessories").tap do |output|
assert_match /docker container stop traefik/, output
@@ -492,6 +537,34 @@ class CliMainTest < CliTestCase
assert_equal Kamal::VERSION, version
end
test "downgrade" do
invoke_options = { "config_file" => "test/fixtures/deploy_with_accessories.yml", "skip_hooks" => false, "confirmed" => true, "rolling" => false }
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:traefik:downgrade", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:accessory:downgrade", [ "all" ], invoke_options)
run_command("downgrade", "-y", config_file: "deploy_with_accessories").tap do |output|
assert_match "Downgrading all hosts...", output
assert_match "Downgraded all hosts", output
end
end
test "downgrade rolling" do
invoke_options = { "config_file" => "test/fixtures/deploy_with_accessories.yml", "skip_hooks" => false, "confirmed" => true, "rolling" => false }
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:traefik:downgrade", [], invoke_options).times(4)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:accessory:downgrade", [ "all" ], invoke_options).times(3)
run_command("downgrade", "--rolling", "-y", config_file: "deploy_with_accessories").tap do |output|
assert_match "Downgrading 1.1.1.1...", output
assert_match "Downgraded 1.1.1.1", output
assert_match "Downgrading 1.1.1.2...", output
assert_match "Downgraded 1.1.1.2", output
assert_match "Downgrading 1.1.1.3...", output
assert_match "Downgraded 1.1.1.3", output
assert_match "Downgrading 1.1.1.4...", output
assert_match "Downgraded 1.1.1.4", output
end
end
private
def run_command(*command, config_file: "deploy_simple")
stdouted { Kamal::Cli::Main.start([ *command, "-c", "test/fixtures/#{config_file}.yml" ]) }
@@ -504,11 +577,8 @@ class CliMainTest < CliTestCase
FileUtils.cp_r("test/fixtures/", fixtures_dup)
Dir.chdir(dir) do
FileUtils.mkdir_p(".kamal")
Dir.chdir(".kamal") do
files.each do |filename, contents|
File.binwrite(filename.to_s, contents)
end
files.each do |filename, contents|
File.binwrite(filename.to_s, contents)
end
yield
end

View File

@@ -40,8 +40,7 @@ class CliServerTest < CliTestCase
SSHKit::Backend::Abstract.any_instance.expects(:execute).with('[ "${EUID:-$(id -u)}" -eq 0 ] || command -v sudo >/dev/null || command -v su >/dev/null', raise_on_non_zero_exit: false).returns(true).at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:sh, "-c", "'curl -fsSL https://get.docker.com || wget -O - https://get.docker.com || echo \"exit 1\"'", "|", :sh).at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:mkdir, "-p", ".kamal").returns("").at_least_once
Kamal::Hooks.stubs(:exists?).returns(true)
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(".kamal/hooks/pre-init", anything).at_least_once
Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(".kamal/hooks/pre-connect", anything).at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(".kamal/hooks/docker-setup", anything).at_least_once

View File

@@ -103,6 +103,90 @@ class CliTraefikTest < CliTestCase
end
end
test "downgrade" do
Object.any_instance.stubs(:sleep)
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-latest$", "--quiet", raise_on_non_zero_exit: false)
.returns("12345678")
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-workers-latest$", "--quiet", raise_on_non_zero_exit: false)
.returns("12345678")
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with { |*args| args[0..1] == [ :sh, "-c" ] }
.returns("123") # old version
SSHKit::Backend::Abstract.any_instance.stubs(: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.stubs(:capture_with_info)
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-workers-latest$", "--quiet", "|", :xargs, :docker, :inspect, "--format", "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'")
.returns("running").at_least_once # workers health check
SSHKit::Backend::Abstract.any_instance.stubs(: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("downgrade", "-y").tap do |output|
assert_match "Downgrading to Traefik on 1.1.1.1,1.1.1.2,1.1.1.3,1.1.1.4...", output
assert_match "docker login -u [REDACTED] -p [REDACTED]", output
assert_match "docker container stop kamal-proxy ; docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy && docker image prune --all --force --filter label=org.opencontainers.image.title=kamal-proxy", output
assert_match "docker container stop traefik", output
assert_match "docker container prune --force --filter label=org.opencontainers.image.title=Traefik", output
assert_match "docker image prune --all --force --filter label=org.opencontainers.image.title=Traefik", output
assert_match "/usr/bin/env mkdir -p .kamal", output
assert_match "docker login -u [REDACTED] -p [REDACTED]", output
assert_match "docker container start traefik || docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --env-file .kamal/env/traefik/traefik.env --log-opt max-size=\"10m\" --label traefik.http.routers.catchall.entryPoints=\"http\" --label traefik.http.routers.catchall.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.routers.catchall.service=\"unavailable\" --label traefik.http.routers.catchall.priority=\"1\" --label traefik.http.services.unavailable.loadbalancer.server.port=\"0\" traefik:v2.11 --providers.docker --log.level=\"DEBUG\"", output
assert_match "/usr/bin/env mkdir -p .kamal", output
assert_match %r{docker rename app-web-latest app-web-latest_replaced_.*}, output
assert_match %r{docker run --detach --restart unless-stopped --name app-web-latest --hostname 1.1.1.1-.* -e KAMAL_CONTAINER_NAME="app-web-latest" -e KAMAL_VERSION="latest" --env-file .kamal/env/roles/app-web.env --health-cmd}, output
assert_match "docker tag dhh/app:latest dhh/app:latest", output
assert_match "/usr/bin/env mkdir -p .kamal", 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", output
assert_match "docker image prune --force --filter label=service=app", output
assert_match "Downgraded to Traefik on 1.1.1.1,1.1.1.2,1.1.1.3,1.1.1.4", output
end
end
test "downgrade rolling" do
Object.any_instance.stubs(:sleep)
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-latest$", "--quiet", raise_on_non_zero_exit: false)
.returns("12345678")
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-workers-latest$", "--quiet", raise_on_non_zero_exit: false)
.returns("12345678")
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with { |*args| args[0..1] == [ :sh, "-c" ] }
.returns("123") # old version
SSHKit::Backend::Abstract.any_instance.stubs(: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.stubs(:capture_with_info)
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-workers-latest$", "--quiet", "|", :xargs, :docker, :inspect, "--format", "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'")
.returns("running").at_least_once # workers health check
SSHKit::Backend::Abstract.any_instance.stubs(: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("downgrade", "--rolling", "-y",).tap do |output|
%w[1.1.1.1 1.1.1.2 1.1.1.3 1.1.1.4].each do |host|
assert_match "Downgrading to Traefik on #{host}...", output
assert_match "docker container stop kamal-proxy ; docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy && docker image prune --all --force --filter label=org.opencontainers.image.title=kamal-proxy", output
assert_match "Downgraded to Traefik on #{host}", output
end
end
end
private
def run_command(*command)
stdouted { Kamal::Cli::Traefik.start([ *command, "-c", "test/fixtures/deploy_with_accessories.yml" ]) }

View File

@@ -30,10 +30,10 @@ class CommandsBuilderTest < ActiveSupport::TestCase
end
test "target multiarch remote when local and remote is set" do
builder = new_builder_command(builder: { "local" => {}, "remote" => {}, "cache" => { "type" => "gha" } })
builder = new_builder_command(builder: { "local" => { "arch" => "arm64" }, "remote" => { "arch" => "amd64" }, "cache" => { "type" => "gha" } })
assert_equal "multiarch/remote", builder.name
assert_equal \
"docker buildx build --push --platform linux/amd64,linux/arm64 --builder kamal-app-multiarch-remote -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .",
"docker buildx build --push --platform linux/arm64,linux/amd64 --builder kamal-app-multiarch-remote -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .",
builder.push.join(" ")
end

View File

@@ -28,7 +28,6 @@ class CommandsHookTest < ActiveSupport::TestCase
end
test "run with custom hooks_path" do
ENV["KAMAL_HOOKS_PATH"] = "custom/hooks/path"
assert_equal [
"custom/hooks/path/foo",
{ env: {
@@ -37,9 +36,7 @@ class CommandsHookTest < ActiveSupport::TestCase
"KAMAL_VERSION" => "123",
"KAMAL_SERVICE_VERSION" => "app@123",
"KAMAL_SERVICE" => "app" } }
], new_command.run("foo")
ensure
ENV.delete("KAMAL_HOOKS_PATH")
], new_command(hooks_path: "custom/hooks/path").run("foo")
end
private

View File

@@ -6,7 +6,7 @@ class ConfigurationValidationTest < ActiveSupport::TestCase
end
test "wrong root types" do
[ :service, :image, :asset_path, :primary_role, :minimum_version, :run_directory ].each do |key|
[ :service, :image, :asset_path, :hooks_path, :primary_role, :minimum_version, :run_directory ].each do |key|
assert_error "#{key}: should be a string", **{ key => [] }
end

View File

@@ -2,6 +2,8 @@ require_relative "integration_test"
class AccessoryTest < IntegrationTest
test "boot, stop, start, restart, logs, remove" do
kamal :envify
kamal :accessory, :boot, :busybox
assert_accessory_running :busybox

View File

@@ -2,6 +2,8 @@ require_relative "integration_test"
class AppTest < IntegrationTest
test "stop, start, boot, logs, images, containers, exec, remove" do
kamal :envify
kamal :deploy
assert_app_is_up

View File

@@ -4,6 +4,8 @@ class BrokenDeployTest < IntegrationTest
test "deploying a bad image" do
@app = "app_with_roles"
kamal :envify
first_version = latest_app_version
kamal :deploy

View File

@@ -29,8 +29,6 @@ services:
context: docker/registry
environment:
- REGISTRY_HTTP_ADDR=0.0.0.0:4443
- REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt
- REGISTRY_HTTP_TLS_KEY=/certs/domain.key
volumes:
- shared:/shared
- registry:/var/lib/registry/

View File

@@ -22,7 +22,6 @@ COPY app_with_roles/ app_with_roles/
RUN rm -rf /root/.ssh
RUN ln -s /shared/ssh /root/.ssh
RUN mkdir -p /etc/docker/certs.d/registry:4443 && ln -s /shared/certs/domain.crt /etc/docker/certs.d/registry:4443/ca.crt
RUN git config --global user.email "deployer@example.com"
RUN git config --global user.name "Deployer"

View File

@@ -0,0 +1,2 @@
SECRET_TOKEN='1234 with "中文"'
SECRET_TAG='TAGME'

View File

@@ -33,7 +33,7 @@ traefik:
args:
accesslog: true
accesslog.format: json
image: registry:4443/traefik:v2.10
image: registry:4443/traefik:v2.11
accessories:
busybox:
service: custom-busybox

View File

@@ -0,0 +1 @@
SECRET_TOKEN='1234 with "中文"'

View File

@@ -27,7 +27,7 @@ traefik:
args:
accesslog: true
accesslog.format: json
image: registry:4443/traefik:v2.10
image: registry:4443/traefik:v2.11
accessories:
busybox:
service: custom-busybox

View File

@@ -1,5 +1,5 @@
#!/bin/bash
dockerd --max-concurrent-downloads 1 &
dockerd --max-concurrent-downloads 1 --insecure-registry registry:4443 &
exec sleep infinity

View File

@@ -19,7 +19,7 @@ push_image_to_registry_4443() {
install_kamal
push_image_to_registry_4443 nginx 1-alpine-slim
push_image_to_registry_4443 traefik v2.10
push_image_to_registry_4443 traefik v2.11
push_image_to_registry_4443 busybox 1.36.0
# .ssh is on a shared volume that persists between runs. Clean it up as the

View File

@@ -1,5 +1,3 @@
#!/bin/sh
while [ ! -f /certs/domain.crt ]; do sleep 1; done
exec /entrypoint.sh /etc/docker/registry/config.yml

View File

@@ -10,8 +10,6 @@ RUN mkdir ssh && \
COPY registry-dns.conf .
COPY boot.sh .
RUN mkdir certs && openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/domain.key -x509 -days 365 -out certs/domain.crt -subj '/CN=registry' -extensions EXT -config registry-dns.conf
HEALTHCHECK --interval=1s CMD pgrep sleep
CMD ["./boot.sh"]

View File

@@ -5,7 +5,6 @@ WORKDIR /work
RUN apt-get update --fix-missing && apt-get -y install openssh-client openssh-server docker.io
RUN mkdir /root/.ssh && ln -s /shared/ssh/id_rsa.pub /root/.ssh/authorized_keys
RUN mkdir -p /etc/docker/certs.d/registry:4443 && ln -s /shared/certs/domain.crt /etc/docker/certs.d/registry:4443/ca.crt
RUN echo "HOST_TOKEN=abcd" >> /etc/environment

View File

@@ -4,6 +4,6 @@ while [ ! -f /root/.ssh/authorized_keys ]; do echo "Waiting for ssh keys"; sleep
service ssh restart
dockerd --max-concurrent-downloads 1 &
dockerd --max-concurrent-downloads 1 --insecure-registry registry:4443 &
exec sleep infinity

View File

@@ -2,6 +2,8 @@ require_relative "integration_test"
class LockTest < IntegrationTest
test "acquire, release, status" do
kamal :envify
kamal :lock, :acquire, "-m 'Integration Tests'"
status = kamal :lock, :status, capture: true

View File

@@ -1,8 +1,8 @@
require_relative "integration_test"
class MainTest < IntegrationTest
test "env push, deploy, redeploy, rollback, details and audit" do
kamal :env, :push
test "envify, deploy, redeploy, rollback, details and audit" do
kamal :envify
assert_env_files
remove_local_env_file
@@ -12,19 +12,19 @@ class MainTest < IntegrationTest
kamal :deploy
assert_app_is_up version: first_version
assert_hooks_ran "pre-init", "pre-connect", "pre-build", "pre-deploy", "post-deploy"
assert_hooks_ran "pre-connect", "pre-build", "pre-deploy", "post-deploy"
assert_envs version: first_version
second_version = update_app_rev
kamal :redeploy
assert_app_is_up version: second_version
assert_hooks_ran "pre-init", "pre-connect", "pre-build", "pre-deploy", "post-deploy"
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-init", "pre-connect", "pre-deploy", "post-deploy"
assert_hooks_ran "pre-connect", "pre-deploy", "post-deploy"
assert_app_is_up version: first_version
details = kamal :details, capture: true
@@ -32,7 +32,7 @@ class MainTest < IntegrationTest
assert_match /Traefik Host: vm2/, details
assert_match /App Host: vm1/, details
assert_match /App Host: vm2/, details
assert_match /traefik:v2.10/, details
assert_match /traefik:v2.11/, details
assert_match /registry:4443\/app:#{first_version}/, details
audit = kamal :audit, capture: true
@@ -45,7 +45,7 @@ class MainTest < IntegrationTest
test "app with roles" do
@app = "app_with_roles"
kamal :env, :push
kamal :envify
version = latest_app_version
@@ -54,7 +54,7 @@ class MainTest < IntegrationTest
kamal :deploy
assert_app_is_up version: version
assert_hooks_ran "pre-init", "pre-connect", "pre-build", "pre-deploy", "post-deploy"
assert_hooks_ran "pre-connect", "pre-build", "pre-deploy", "post-deploy"
assert_container_running host: :vm3, name: "app-workers-#{version}"
second_version = update_app_rev
@@ -87,7 +87,7 @@ class MainTest < IntegrationTest
kamal :remove, "-y"
assert_no_images_or_containers
kamal :env, :push
kamal :envify
kamal :setup
assert_images_and_containers
@@ -97,7 +97,7 @@ class MainTest < IntegrationTest
private
def assert_local_env_file(contents)
assert_equal contents, deployer_exec("cat .kamal/.env", capture: true)
assert_equal contents, deployer_exec("cat .env", capture: true)
end
def assert_envs(version:)
@@ -127,7 +127,7 @@ class MainTest < IntegrationTest
end
def remove_local_env_file
deployer_exec("rm .kamal/.env")
deployer_exec("rm .env")
end
def assert_remote_env_file(contents, vm:)

View File

@@ -2,6 +2,8 @@ require_relative "integration_test"
class TraefikTest < IntegrationTest
test "boot, reboot, stop, start, restart, logs, remove" do
kamal :envify
kamal :traefik, :boot
assert_traefik_running
@@ -50,11 +52,11 @@ class TraefikTest < IntegrationTest
private
def assert_traefik_running
assert_match /traefik:v2.10 "\/entrypoint.sh/, traefik_details
assert_match /traefik:v2.11 "\/entrypoint.sh/, traefik_details
end
def assert_traefik_not_running
assert_no_match /traefik:v2.10 "\/entrypoint.sh/, traefik_details
assert_no_match /traefik:v2.11 "\/entrypoint.sh/, traefik_details
end
def traefik_details

View File

@@ -26,10 +26,6 @@ end
class ActiveSupport::TestCase
include ActiveSupport::Testing::Stream
setup do
Kamal::Cli::Base.ran_pre_init_hook = false
end
private
def stdouted
capture(:stdout) { yield }.strip