Compare commits
12 Commits
v2.7.0
...
7da03fd94c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7da03fd94c | ||
|
|
2eed47d464 | ||
|
|
95cbc62ef1 | ||
|
|
18f1bbbeac | ||
|
|
5dd8eba182 | ||
|
|
75754e4b7b | ||
|
|
8e470ed051 | ||
|
|
4b88852aea | ||
|
|
cfaa4fb0db | ||
|
|
2bcb313590 | ||
|
|
3cf510bc8f | ||
|
|
e61d96d154 |
12
Gemfile.lock
12
Gemfile.lock
@@ -82,15 +82,15 @@ GEM
|
|||||||
net-sftp (4.0.0)
|
net-sftp (4.0.0)
|
||||||
net-ssh (>= 5.0.0, < 8.0.0)
|
net-ssh (>= 5.0.0, < 8.0.0)
|
||||||
net-ssh (7.3.0)
|
net-ssh (7.3.0)
|
||||||
nokogiri (1.18.8-aarch64-linux-musl)
|
nokogiri (1.18.9-aarch64-linux-musl)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.18.8-arm64-darwin)
|
nokogiri (1.18.9-arm64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.18.8-x86_64-darwin)
|
nokogiri (1.18.9-x86_64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.18.8-x86_64-linux-gnu)
|
nokogiri (1.18.9-x86_64-linux-gnu)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.18.8-x86_64-linux-musl)
|
nokogiri (1.18.9-x86_64-linux-musl)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
ostruct (0.6.1)
|
ostruct (0.6.1)
|
||||||
parallel (1.26.3)
|
parallel (1.26.3)
|
||||||
@@ -169,7 +169,7 @@ GEM
|
|||||||
net-ssh (>= 2.8.0)
|
net-ssh (>= 2.8.0)
|
||||||
ostruct
|
ostruct
|
||||||
stringio (3.1.2)
|
stringio (3.1.2)
|
||||||
thor (1.3.2)
|
thor (1.4.0)
|
||||||
tzinfo (2.0.6)
|
tzinfo (2.0.6)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
unicode-display_width (3.1.2)
|
unicode-display_width (3.1.2)
|
||||||
|
|||||||
@@ -67,10 +67,11 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|||||||
|
|
||||||
desc "pull", "Pull app image from registry onto servers"
|
desc "pull", "Pull app image from registry onto servers"
|
||||||
def pull
|
def pull
|
||||||
login_to_registry_remotely
|
login_to_registry_remotely unless KAMAL.registry.local?
|
||||||
|
|
||||||
|
forward_local_registry_port do
|
||||||
if (first_hosts = mirror_hosts).any?
|
if (first_hosts = mirror_hosts).any?
|
||||||
# Pull on a single host per mirror first to seed them
|
# Pull on a single host per mirror first to seed them
|
||||||
say "Pulling image on #{first_hosts.join(", ")} to seed the #{"mirror".pluralize(first_hosts.count)}...", :magenta
|
say "Pulling image on #{first_hosts.join(", ")} to seed the #{"mirror".pluralize(first_hosts.count)}...", :magenta
|
||||||
pull_on_hosts(first_hosts)
|
pull_on_hosts(first_hosts)
|
||||||
say "Pulling image on remaining hosts...", :magenta
|
say "Pulling image on remaining hosts...", :magenta
|
||||||
@@ -79,6 +80,7 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|||||||
pull_on_hosts(KAMAL.app_hosts)
|
pull_on_hosts(KAMAL.app_hosts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
desc "create", "Create a build setup"
|
desc "create", "Create a build setup"
|
||||||
def create
|
def create
|
||||||
@@ -192,13 +194,27 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|||||||
|
|
||||||
def login_to_registry_locally
|
def login_to_registry_locally
|
||||||
run_locally do
|
run_locally do
|
||||||
|
if KAMAL.registry.local?
|
||||||
|
execute *KAMAL.registry.setup
|
||||||
|
else
|
||||||
execute *KAMAL.registry.login
|
execute *KAMAL.registry.login
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def login_to_registry_remotely
|
def login_to_registry_remotely
|
||||||
on(KAMAL.app_hosts) do
|
on(KAMAL.app_hosts) do
|
||||||
execute *KAMAL.registry.login
|
execute *KAMAL.registry.login
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def forward_local_registry_port(&block)
|
||||||
|
if KAMAL.config.registry.local?
|
||||||
|
Kamal::Cli::PortForwarding.
|
||||||
|
new(KAMAL.hosts, KAMAL.config.registry.local_port).
|
||||||
|
forward(&block)
|
||||||
|
else
|
||||||
|
yield
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|||||||
invoke "kamal:cli:app:remove", [], options.without(:confirmed)
|
invoke "kamal:cli:app:remove", [], options.without(:confirmed)
|
||||||
invoke "kamal:cli:proxy:remove", [], options.without(:confirmed)
|
invoke "kamal:cli:proxy:remove", [], options.without(:confirmed)
|
||||||
invoke "kamal:cli:accessory:remove", [ "all" ], options
|
invoke "kamal:cli:accessory:remove", [ "all" ], options
|
||||||
invoke "kamal:cli:registry:logout", [], options.without(:confirmed).merge(skip_local: true)
|
invoke "kamal:cli:registry:remove", [], options.without(:confirmed).merge(skip_local: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
42
lib/kamal/cli/port_forwarding.rb
Normal file
42
lib/kamal/cli/port_forwarding.rb
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
class Kamal::Cli::PortForwarding
|
||||||
|
attr_reader :hosts, :port
|
||||||
|
|
||||||
|
def initialize(hosts, port)
|
||||||
|
@hosts = hosts
|
||||||
|
@port = port
|
||||||
|
end
|
||||||
|
|
||||||
|
def forward
|
||||||
|
@done = false
|
||||||
|
forward_ports
|
||||||
|
|
||||||
|
yield
|
||||||
|
ensure
|
||||||
|
stop
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def stop
|
||||||
|
@done = true
|
||||||
|
@threads.to_a.each(&:join)
|
||||||
|
end
|
||||||
|
|
||||||
|
def forward_ports
|
||||||
|
@threads = hosts.map do |host|
|
||||||
|
Thread.new do
|
||||||
|
Net::SSH.start(host, KAMAL.config.ssh.user) do |ssh|
|
||||||
|
ssh.forward.remote(port, "127.0.0.1", port)
|
||||||
|
ssh.loop(0.1) do
|
||||||
|
if @done
|
||||||
|
ssh.forward.cancel_remote(port)
|
||||||
|
break
|
||||||
|
else
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,19 +1,27 @@
|
|||||||
class Kamal::Cli::Registry < Kamal::Cli::Base
|
class Kamal::Cli::Registry < Kamal::Cli::Base
|
||||||
desc "login", "Log in to registry locally and remotely"
|
desc "setup", "Setup local registry or log in to remote registry locally and remotely"
|
||||||
option :skip_local, aliases: "-L", type: :boolean, default: false, desc: "Skip local login"
|
option :skip_local, aliases: "-L", type: :boolean, default: false, desc: "Skip local login"
|
||||||
option :skip_remote, aliases: "-R", type: :boolean, default: false, desc: "Skip remote login"
|
option :skip_remote, aliases: "-R", type: :boolean, default: false, desc: "Skip remote login"
|
||||||
def login
|
def setup
|
||||||
ensure_docker_installed unless options[:skip_local]
|
ensure_docker_installed unless options[:skip_local]
|
||||||
|
|
||||||
|
if KAMAL.registry.local?
|
||||||
|
run_locally { execute *KAMAL.registry.setup } unless options[:skip_local]
|
||||||
|
else
|
||||||
run_locally { execute *KAMAL.registry.login } unless options[:skip_local]
|
run_locally { execute *KAMAL.registry.login } unless options[:skip_local]
|
||||||
on(KAMAL.hosts) { execute *KAMAL.registry.login } unless options[:skip_remote]
|
on(KAMAL.hosts) { execute *KAMAL.registry.login } unless options[:skip_remote]
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
desc "logout", "Log out of registry locally and remotely"
|
desc "remove", "Remove local registry or log out of remote registry locally and remotely"
|
||||||
option :skip_local, aliases: "-L", type: :boolean, default: false, desc: "Skip local login"
|
option :skip_local, aliases: "-L", type: :boolean, default: false, desc: "Skip local login"
|
||||||
option :skip_remote, aliases: "-R", type: :boolean, default: false, desc: "Skip remote login"
|
option :skip_remote, aliases: "-R", type: :boolean, default: false, desc: "Skip remote login"
|
||||||
def logout
|
def remove
|
||||||
|
if KAMAL.registry.local?
|
||||||
|
run_locally { execute *KAMAL.registry.remove, raise_on_non_zero_exit: false } unless options[:skip_local]
|
||||||
|
else
|
||||||
run_locally { execute *KAMAL.registry.logout } unless options[:skip_local]
|
run_locally { execute *KAMAL.registry.logout } unless options[:skip_local]
|
||||||
on(KAMAL.hosts) { execute *KAMAL.registry.logout } unless options[:skip_remote]
|
on(KAMAL.hosts) { execute *KAMAL.registry.logout } unless options[:skip_remote]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|||||||
@@ -25,13 +25,14 @@ proxy:
|
|||||||
|
|
||||||
# Credentials for your image host.
|
# Credentials for your image host.
|
||||||
registry:
|
registry:
|
||||||
|
server: localhost:5555
|
||||||
# Specify the registry server, if you're not using Docker Hub
|
# Specify the registry server, if you're not using Docker Hub
|
||||||
# server: registry.digitalocean.com / ghcr.io / ...
|
# server: registry.digitalocean.com / ghcr.io / ...
|
||||||
username: my-user
|
# username: my-user
|
||||||
|
|
||||||
# Always use an access token rather than real password (pulled from .kamal/secrets).
|
# Always use an access token rather than real password (pulled from .kamal/secrets).
|
||||||
password:
|
# password:
|
||||||
- KAMAL_REGISTRY_PASSWORD
|
# - KAMAL_REGISTRY_PASSWORD
|
||||||
|
|
||||||
# Configure builder setup.
|
# Configure builder setup.
|
||||||
builder:
|
builder:
|
||||||
@@ -39,7 +40,6 @@ builder:
|
|||||||
# Pass in additional build args needed for your Dockerfile.
|
# Pass in additional build args needed for your Dockerfile.
|
||||||
# args:
|
# args:
|
||||||
# RUBY_VERSION: <%= ENV["RBENV_VERSION"] || ENV["rvm_ruby_string"] || "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}" %>
|
# RUBY_VERSION: <%= ENV["RBENV_VERSION"] || ENV["rvm_ruby_string"] || "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}" %>
|
||||||
|
|
||||||
# Inject ENV variables into containers (secrets come from .kamal/secrets).
|
# Inject ENV variables into containers (secrets come from .kamal/secrets).
|
||||||
#
|
#
|
||||||
# env:
|
# env:
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git.
|
# password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git.
|
||||||
|
|
||||||
# Option 1: Read secrets from the environment
|
# Option 1: Read secrets from the environment
|
||||||
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
|
# KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
|
||||||
|
|
||||||
# Option 2: Read secrets via a command
|
# Option 2: Read secrets via a command
|
||||||
# RAILS_MASTER_KEY=$(cat config/master.key)
|
# RAILS_MASTER_KEY=$(cat config/master.key)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class Kamal::Commander
|
|||||||
end
|
end
|
||||||
|
|
||||||
def config
|
def config
|
||||||
@config ||= Kamal::Configuration.create_from(**@config_kwargs).tap do |config|
|
@config ||= Kamal::Configuration.create_from(**@config_kwargs.to_h).tap do |config|
|
||||||
@config_kwargs = nil
|
@config_kwargs = nil
|
||||||
configure_sshkit_with(config)
|
configure_sshkit_with(config)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
require "shellwords"
|
||||||
|
|
||||||
class Kamal::Commands::Builder::Base < Kamal::Commands::Base
|
class Kamal::Commands::Builder::Base < Kamal::Commands::Base
|
||||||
class BuilderError < StandardError; end
|
class BuilderError < StandardError; end
|
||||||
|
|
||||||
@@ -44,7 +46,7 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def build_context
|
def build_context
|
||||||
config.builder.context
|
Shellwords.escape(config.builder.context)
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_image
|
def validate_image
|
||||||
@@ -92,7 +94,7 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
|
|||||||
|
|
||||||
def build_dockerfile
|
def build_dockerfile
|
||||||
if Pathname.new(File.expand_path(dockerfile)).exist?
|
if Pathname.new(File.expand_path(dockerfile)).exist?
|
||||||
argumentize "--file", dockerfile
|
argumentize "--file", Shellwords.escape(dockerfile)
|
||||||
else
|
else
|
||||||
raise BuilderError, "Missing #{dockerfile}"
|
raise BuilderError, "Missing #{dockerfile}"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
class Kamal::Commands::Builder::Local < Kamal::Commands::Builder::Base
|
class Kamal::Commands::Builder::Local < Kamal::Commands::Builder::Base
|
||||||
def create
|
def create
|
||||||
docker :buildx, :create, "--name", builder_name, "--driver=#{driver}" unless docker_driver?
|
return if docker_driver?
|
||||||
|
|
||||||
|
options =
|
||||||
|
if KAMAL.registry.local?
|
||||||
|
"--driver=#{driver} --driver-opt network=host"
|
||||||
|
else
|
||||||
|
"--driver=#{driver}"
|
||||||
|
end
|
||||||
|
|
||||||
|
docker :buildx, :create, "--name", builder_name, options
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove
|
def remove
|
||||||
@@ -9,6 +18,10 @@ class Kamal::Commands::Builder::Local < Kamal::Commands::Builder::Base
|
|||||||
|
|
||||||
private
|
private
|
||||||
def builder_name
|
def builder_name
|
||||||
|
if KAMAL.registry.local?
|
||||||
|
"kamal-local-registry-#{driver}"
|
||||||
|
else
|
||||||
"kamal-local-#{driver}"
|
"kamal-local-#{driver}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class Kamal::Commands::Builder::Remote < Kamal::Commands::Builder::Base
|
|||||||
|
|
||||||
def inspect_builder
|
def inspect_builder
|
||||||
combine \
|
combine \
|
||||||
combine inspect_buildx, inspect_remote_context,
|
combine(inspect_buildx, inspect_remote_context),
|
||||||
[ "(echo no compatible builder && exit 1)" ],
|
[ "(echo no compatible builder && exit 1)" ],
|
||||||
by: "||"
|
by: "||"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ class Kamal::Commands::Registry < Kamal::Commands::Base
|
|||||||
def login(registry_config: nil)
|
def login(registry_config: nil)
|
||||||
registry_config ||= config.registry
|
registry_config ||= config.registry
|
||||||
|
|
||||||
|
return if registry_config.local?
|
||||||
|
|
||||||
docker :login,
|
docker :login,
|
||||||
registry_config.server,
|
registry_config.server,
|
||||||
"-u", sensitive(Kamal::Utils.escape_shell_value(registry_config.username)),
|
"-u", sensitive(Kamal::Utils.escape_shell_value(registry_config.username)),
|
||||||
@@ -13,4 +15,24 @@ class Kamal::Commands::Registry < Kamal::Commands::Base
|
|||||||
|
|
||||||
docker :logout, registry_config.server
|
docker :logout, registry_config.server
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def setup(registry_config: nil)
|
||||||
|
registry_config ||= config.registry
|
||||||
|
|
||||||
|
combine \
|
||||||
|
docker(:start, "kamal-docker-registry"),
|
||||||
|
docker(:run, "--detach", "-p", "127.0.0.1:#{registry_config.local_port}:5000", "--name", "kamal-docker-registry", "registry:3"),
|
||||||
|
by: "||"
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove
|
||||||
|
combine \
|
||||||
|
docker(:stop, "kamal-docker-registry"),
|
||||||
|
docker(:rm, "kamal-docker-registry"),
|
||||||
|
by: "&&"
|
||||||
|
end
|
||||||
|
|
||||||
|
def local?
|
||||||
|
config.registry.local?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ require "erb"
|
|||||||
require "net/ssh/proxy/jump"
|
require "net/ssh/proxy/jump"
|
||||||
|
|
||||||
class Kamal::Configuration
|
class Kamal::Configuration
|
||||||
delegate :service, :image, :labels, :hooks_path, to: :raw_config, allow_nil: true
|
delegate :service, :labels, :hooks_path, to: :raw_config, allow_nil: true
|
||||||
delegate :argumentize, :optionize, to: Kamal::Utils
|
delegate :argumentize, :optionize, to: Kamal::Utils
|
||||||
|
|
||||||
attr_reader :destination, :raw_config, :secrets
|
attr_reader :destination, :raw_config, :secrets
|
||||||
@@ -157,6 +157,13 @@ class Kamal::Configuration
|
|||||||
(proxy_roles.flat_map(&:hosts) + proxy_accessories.flat_map(&:hosts)).uniq
|
(proxy_roles.flat_map(&:hosts) + proxy_accessories.flat_map(&:hosts)).uniq
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def image
|
||||||
|
name = raw_config&.image.presence
|
||||||
|
name ||= raw_config&.service if registry.local?
|
||||||
|
|
||||||
|
name
|
||||||
|
end
|
||||||
|
|
||||||
def repository
|
def repository
|
||||||
[ registry.server, image ].compact.join("/")
|
[ registry.server, image ].compact.join("/")
|
||||||
end
|
end
|
||||||
@@ -282,10 +289,12 @@ class Kamal::Configuration
|
|||||||
end
|
end
|
||||||
|
|
||||||
def ensure_required_keys_present
|
def ensure_required_keys_present
|
||||||
%i[ service image registry ].each do |key|
|
%i[ service registry ].each do |key|
|
||||||
raise Kamal::ConfigurationError, "Missing required configuration for #{key}" unless raw_config[key].present?
|
raise Kamal::ConfigurationError, "Missing required configuration for #{key}" unless raw_config[key].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
raise Kamal::ConfigurationError, "Missing required configuration for image" if image.blank?
|
||||||
|
|
||||||
if raw_config.servers.nil?
|
if raw_config.servers.nil?
|
||||||
raise Kamal::ConfigurationError, "No servers or accessories specified" unless raw_config.accessories.present?
|
raise Kamal::ConfigurationError, "No servers or accessories specified" unless raw_config.accessories.present?
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -45,27 +45,23 @@ proxy:
|
|||||||
# unless you explicitly set `forward_headers: true`
|
# unless you explicitly set `forward_headers: true`
|
||||||
#
|
#
|
||||||
# Defaults to `false`:
|
# Defaults to `false`:
|
||||||
ssl: ...
|
ssl: true
|
||||||
|
|
||||||
# Custom SSL certificate
|
# Custom SSL certificate
|
||||||
#
|
#
|
||||||
# In some cases, using Let's Encrypt for automatic certificate management is not an
|
# In some cases, using Let's Encrypt for automatic certificate management is not an
|
||||||
# option, for example if you are running from host than one host. Or you may already
|
# option, for example if you are running from more than one host.
|
||||||
# have SSL certificates issued by a different Certificate Authority (CA).
|
|
||||||
# Kamal supports loading custom SSL certificates
|
|
||||||
# directly from secrets.
|
|
||||||
#
|
#
|
||||||
# Examples:
|
# Or you may already have SSL certificates issued by a different Certificate Authority (CA).
|
||||||
# ssl: true # Enable SSL with Let's Encrypt
|
|
||||||
# ssl: false # Disable SSL
|
|
||||||
# ssl: # Enable custom SSL
|
|
||||||
# certificate_pem: CERTIFICATE_PEM
|
|
||||||
# private_key_pem: PRIVATE_KEY_PEM
|
|
||||||
#
|
#
|
||||||
|
# Kamal supports loading custom SSL certificates directly from secrets. You should
|
||||||
|
# pass a hash mapping the `certificate_pem` and `private_key_pem` to the secret names.
|
||||||
|
ssl:
|
||||||
|
certificate_pem: CERTIFICATE_PEM
|
||||||
|
private_key_pem: PRIVATE_KEY_PEM
|
||||||
# ### Notes
|
# ### Notes
|
||||||
# - If the certificate or key is missing or invalid, kamal-proxy will fail to start.
|
# - If the certificate or key is missing or invalid, deployments will fail.
|
||||||
# - Always handle SSL certificates and private keys securely. Avoid hard-coding them in deploy.yml files or source control.
|
# - Always handle SSL certificates and private keys securely. Avoid hard-coding them in source control.
|
||||||
# - For automated certificate management, consider using the built-in Let's Encrypt integration instead.
|
|
||||||
|
|
||||||
# SSL redirect
|
# SSL redirect
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -19,6 +19,14 @@ class Kamal::Configuration::Registry
|
|||||||
lookup("password")
|
lookup("password")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def local?
|
||||||
|
server.to_s.match?("^localhost[:$]")
|
||||||
|
end
|
||||||
|
|
||||||
|
def local_port
|
||||||
|
local? ? (server.split(":").last.to_i || 80) : nil
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
attr_reader :registry_config, :secrets
|
attr_reader :registry_config, :secrets
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ class Kamal::Configuration::Validator
|
|||||||
example_value = example[key]
|
example_value = example[key]
|
||||||
|
|
||||||
if example_value == "..."
|
if example_value == "..."
|
||||||
if key.to_s == "ssl"
|
unless key.to_s == "proxy" && boolean?(value.class)
|
||||||
validate_type! value, TrueClass, FalseClass, Hash
|
|
||||||
elsif key.to_s != "proxy" || !boolean?(value.class)
|
|
||||||
validate_type! value, *(Array if key == :servers), Hash
|
validate_type! value, *(Array if key == :servers), Hash
|
||||||
end
|
end
|
||||||
|
elsif key.to_s == "ssl"
|
||||||
|
validate_type! value, TrueClass, FalseClass, Hash
|
||||||
elsif key == "hosts"
|
elsif key == "hosts"
|
||||||
validate_servers! value
|
validate_servers! value
|
||||||
elsif example_value.is_a?(Array)
|
elsif example_value.is_a?(Array)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class Kamal::Configuration::Validator::Registry < Kamal::Configuration::Validato
|
|||||||
with_context(key) do
|
with_context(key) do
|
||||||
value = config[key]
|
value = config[key]
|
||||||
|
|
||||||
|
unless config["server"]&.match?("^localhost[:$]")
|
||||||
error "is required" unless value.present?
|
error "is required" unless value.present?
|
||||||
|
|
||||||
unless value.is_a?(String) || (value.is_a?(Array) && value.size == 1 && value.first.is_a?(String))
|
unless value.is_a?(String) || (value.is_a?(Array) && value.size == 1 && value.first.is_a?(String))
|
||||||
@@ -23,3 +24,4 @@ class Kamal::Configuration::Validator::Registry < Kamal::Configuration::Validato
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
require "tempfile"
|
require "tempfile"
|
||||||
require "open3"
|
require "open3"
|
||||||
|
require "shellwords"
|
||||||
|
|
||||||
module Kamal::Docker
|
module Kamal::Docker
|
||||||
extend self
|
extend self
|
||||||
@@ -15,7 +16,7 @@ module Kamal::Docker
|
|||||||
DOCKERFILE
|
DOCKERFILE
|
||||||
dockerfile.close
|
dockerfile.close
|
||||||
|
|
||||||
cmd = "docker buildx build -t=#{BUILD_CHECK_TAG} -f=#{dockerfile.path} ."
|
cmd = "docker buildx build -t=#{BUILD_CHECK_TAG} -f=#{Shellwords.escape(dockerfile.path)} ."
|
||||||
system(cmd) || raise("failed to build check image")
|
system(cmd) || raise("failed to build check image")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class Kamal::Secrets::Adapters::OnePassword < Kamal::Secrets::Adapters::Base
|
|||||||
|
|
||||||
def fetch_secrets(secrets, from:, account:, session:)
|
def fetch_secrets(secrets, from:, account:, session:)
|
||||||
if secrets.blank?
|
if secrets.blank?
|
||||||
fetch_all_secrets(from: from, account: account, session: session) if secrets.blank?
|
fetch_all_secrets(from: from, account: account, session: session)
|
||||||
else
|
else
|
||||||
fetch_specified_secrets(secrets, from: from, account: account, session: session)
|
fetch_specified_secrets(secrets, from: from, account: account, session: session)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -31,6 +31,24 @@ class CliBuildTest < CliTestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "push with remote builder checks both the builder and the remote context" do
|
||||||
|
with_build_directory do |build_directory|
|
||||||
|
Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
|
.with(:git, "-C", anything, :"rev-parse", :HEAD)
|
||||||
|
.returns(Kamal::Git.revision)
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
|
.with(:git, "-C", anything, :status, "--porcelain")
|
||||||
|
.returns("")
|
||||||
|
|
||||||
|
run_command("push", "--verbose", fixture: :with_remote_builder).tap do |output|
|
||||||
|
assert_match "docker buildx inspect kamal-remote-ssh---app-1-1-1-5 | grep -q Endpoint:.*kamal-remote-ssh---app-1-1-1-5-context && docker context inspect kamal-remote-ssh---app-1-1-1-5-context --format '{{.Endpoints.docker.Host}}' | grep -xq ssh://app@1.1.1.5 || (echo no compatible builder && exit 1)", output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "push --output=docker" do
|
test "push --output=docker" do
|
||||||
with_build_directory do |build_directory|
|
with_build_directory do |build_directory|
|
||||||
Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
|
Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
|
||||||
@@ -135,6 +153,48 @@ class CliBuildTest < CliTestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "push without builder for local registry" do
|
||||||
|
with_build_directory do |build_directory|
|
||||||
|
stub_setup
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:execute)
|
||||||
|
.with(:docker, "--version", "&&", :docker, :buildx, "version")
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
||||||
|
.with { |*args| args[0..1] == [ :docker, :login ] }
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:execute)
|
||||||
|
.with(:docker, :start, "kamal-docker-registry", "||", :docker, :run, "--detach", "-p", "127.0.0.1:5000:5000", "--name", "kamal-docker-registry", "registry:3")
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:execute)
|
||||||
|
.with(:docker, :buildx, :rm, "kamal-local-registry-docker-container")
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:execute)
|
||||||
|
.with(:docker, :buildx, :create, "--name", "kamal-local-registry-docker-container", "--driver=docker-container --driver-opt network=host")
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:execute)
|
||||||
|
.with(:docker, :buildx, :inspect, "kamal-local-registry-docker-container")
|
||||||
|
.raises(SSHKit::Command::Failed.new("no builder"))
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:execute).with { |*args| args.first.to_s.start_with?("git") }
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
|
.with(:git, "-C", anything, :"rev-parse", :HEAD)
|
||||||
|
.returns(Kamal::Git.revision)
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
|
.with(:git, "-C", anything, :status, "--porcelain")
|
||||||
|
.returns("")
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:execute)
|
||||||
|
.with(:docker, :buildx, :build, "--output=type=registry", "--platform", "linux/amd64", "--builder", "kamal-local-registry-docker-container", "-t", "localhost:5000/dhh/app:999", "-t", "localhost:5000/dhh/app:latest", "--label", "service=\"app\"", "--file", "Dockerfile", ".", "2>&1")
|
||||||
|
|
||||||
|
run_command("push", fixture: :with_local_registry_and_accessories).tap do |output|
|
||||||
|
assert_match /WARN Missing compatible builder, so creating a new one first/, output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "push without builder" do
|
test "push without builder" do
|
||||||
with_build_directory do |build_directory|
|
with_build_directory do |build_directory|
|
||||||
stub_setup
|
stub_setup
|
||||||
|
|||||||
@@ -39,6 +39,30 @@ class CliMainTest < CliTestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "deploy with local registry" do
|
||||||
|
with_test_secrets("secrets" => "DB_PASSWORD=secret") do
|
||||||
|
invoke_options = { "config_file" => "test/fixtures/deploy_with_local_registry.yml", "version" => "999", "skip_hooks" => false, "verbose" => true }
|
||||||
|
|
||||||
|
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:build:deliver", [], invoke_options)
|
||||||
|
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:proxy:boot", [], invoke_options)
|
||||||
|
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::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:prune:all", [], invoke_options)
|
||||||
|
|
||||||
|
Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
|
||||||
|
|
||||||
|
run_command("deploy", "--verbose", config_file: "deploy_with_local_registry").tap do |output|
|
||||||
|
assert_hook_ran "pre-connect", output
|
||||||
|
assert_match /Build and push app image/, output
|
||||||
|
assert_hook_ran "pre-deploy", output
|
||||||
|
assert_match /Ensure kamal-proxy is running/, output
|
||||||
|
assert_match /Detect stale containers/, output
|
||||||
|
assert_match /Prune old containers and images/, output
|
||||||
|
assert_hook_ran "post-deploy", output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "deploy" do
|
test "deploy" do
|
||||||
with_test_secrets("secrets" => "DB_PASSWORD=secret") do
|
with_test_secrets("secrets" => "DB_PASSWORD=secret") do
|
||||||
invoke_options = { "config_file" => "test/fixtures/deploy_simple.yml", "version" => "999", "skip_hooks" => false, "verbose" => true }
|
invoke_options = { "config_file" => "test/fixtures/deploy_simple.yml", "version" => "999", "skip_hooks" => false, "verbose" => true }
|
||||||
@@ -302,6 +326,16 @@ class CliMainTest < CliTestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "remove" do
|
||||||
|
options = { "config_file" => "test/fixtures/deploy_simple.yml", "skip_hooks" => false, "confirmed" => true }
|
||||||
|
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:proxy:remove", [], options)
|
||||||
|
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:remove", [], options)
|
||||||
|
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:accessory:remove", [ "all" ], options)
|
||||||
|
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:registry:remove", [], options.merge(skip_local: true))
|
||||||
|
|
||||||
|
run_command("remove", "-y")
|
||||||
|
end
|
||||||
|
|
||||||
test "details" do
|
test "details" do
|
||||||
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:proxy:details")
|
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:proxy:details")
|
||||||
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:details")
|
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:details")
|
||||||
|
|||||||
@@ -1,55 +1,55 @@
|
|||||||
require_relative "cli_test_case"
|
require_relative "cli_test_case"
|
||||||
|
|
||||||
class CliRegistryTest < CliTestCase
|
class CliRegistryTest < CliTestCase
|
||||||
test "login" do
|
test "setup" do
|
||||||
run_command("login").tap do |output|
|
run_command("setup").tap do |output|
|
||||||
assert_match /docker login -u \[REDACTED\] -p \[REDACTED\] as .*@localhost/, output
|
assert_match /docker login -u \[REDACTED\] -p \[REDACTED\] as .*@localhost/, output
|
||||||
assert_match /docker login -u \[REDACTED\] -p \[REDACTED\] on 1.1.1.\d/, output
|
assert_match /docker login -u \[REDACTED\] -p \[REDACTED\] on 1.1.1.\d/, output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "login skip local" do
|
test "setup skip local" do
|
||||||
run_command("login", "-L").tap do |output|
|
run_command("setup", "-L").tap do |output|
|
||||||
assert_no_match /docker login -u \[REDACTED\] -p \[REDACTED\] as .*@localhost/, output
|
assert_no_match /docker login -u \[REDACTED\] -p \[REDACTED\] as .*@localhost/, output
|
||||||
assert_match /docker login -u \[REDACTED\] -p \[REDACTED\] on 1.1.1.\d/, output
|
assert_match /docker login -u \[REDACTED\] -p \[REDACTED\] on 1.1.1.\d/, output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "login skip remote" do
|
test "setup skip remote" do
|
||||||
run_command("login", "-R").tap do |output|
|
run_command("setup", "-R").tap do |output|
|
||||||
assert_match /docker login -u \[REDACTED\] -p \[REDACTED\] as .*@localhost/, output
|
assert_match /docker login -u \[REDACTED\] -p \[REDACTED\] as .*@localhost/, output
|
||||||
assert_no_match /docker login -u \[REDACTED\] -p \[REDACTED\] on 1.1.1.\d/, output
|
assert_no_match /docker login -u \[REDACTED\] -p \[REDACTED\] on 1.1.1.\d/, output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "logout" do
|
test "remove" do
|
||||||
run_command("logout").tap do |output|
|
run_command("remove").tap do |output|
|
||||||
assert_match /docker logout as .*@localhost/, output
|
assert_match /docker logout as .*@localhost/, output
|
||||||
assert_match /docker logout on 1.1.1.\d/, output
|
assert_match /docker logout on 1.1.1.\d/, output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "logout skip local" do
|
test "remove skip local" do
|
||||||
run_command("logout", "-L").tap do |output|
|
run_command("remove", "-L").tap do |output|
|
||||||
assert_no_match /docker logout as .*@localhost/, output
|
assert_no_match /docker logout as .*@localhost/, output
|
||||||
assert_match /docker logout on 1.1.1.\d/, output
|
assert_match /docker logout on 1.1.1.\d/, output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "logout skip remote" do
|
test "remove skip remote" do
|
||||||
run_command("logout", "-R").tap do |output|
|
run_command("remove", "-R").tap do |output|
|
||||||
assert_match /docker logout as .*@localhost/, output
|
assert_match /docker logout as .*@localhost/, output
|
||||||
assert_no_match /docker logout on 1.1.1.\d/, output
|
assert_no_match /docker logout on 1.1.1.\d/, output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "login with no docker" do
|
test "setup with no docker" do
|
||||||
stub_setup
|
stub_setup
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
||||||
.with(:docker, "--version", "&&", :docker, :buildx, "version")
|
.with(:docker, "--version", "&&", :docker, :buildx, "version")
|
||||||
.raises(SSHKit::Command::Failed.new("command not found"))
|
.raises(SSHKit::Command::Failed.new("command not found"))
|
||||||
|
|
||||||
assert_raises(Kamal::Cli::DependencyError) { run_command("login") }
|
assert_raises(Kamal::Cli::DependencyError) { run_command("setup") }
|
||||||
end
|
end
|
||||||
|
|
||||||
test "allow remote login with no docker" do
|
test "allow remote login with no docker" do
|
||||||
@@ -61,12 +61,23 @@ class CliRegistryTest < CliTestCase
|
|||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
||||||
.with { |*args| args[0..1] == [ :docker, :login ] }
|
.with { |*args| args[0..1] == [ :docker, :login ] }
|
||||||
|
|
||||||
assert_nothing_raised { run_command("login", "--skip-local") }
|
assert_nothing_raised { run_command("setup", "--skip-local") }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "setup local registry" do
|
||||||
|
run_command("setup", fixture: :with_local_registry).tap do |output|
|
||||||
|
assert_match /docker start kamal-docker-registry || docker run --detach -p 127.0.0.1:5000:5000 --name kamal-docker-registry registry:2 as .*@localhost/, output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "remove local registry" do
|
||||||
|
run_command("remove", fixture: :with_local_registry).tap do |output|
|
||||||
|
assert_match /docker stop kamal-docker-registry && docker rm kamal-docker-registry as .*@localhost/, output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def run_command(*command)
|
def run_command(*command, fixture: :with_accessories)
|
||||||
stdouted { Kamal::Cli::Registry.start([ *command, "-c", "test/fixtures/deploy_with_accessories.yml" ]) }
|
stdouted { Kamal::Cli::Registry.start([ *command, "-c", "test/fixtures/deploy_#{fixture}.yml" ]) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -228,7 +228,11 @@ class CommandsBuilderTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
private
|
private
|
||||||
def new_builder_command(additional_config = {})
|
def new_builder_command(additional_config = {})
|
||||||
Kamal::Commands::Builder.new(Kamal::Configuration.new(@config.deep_merge(additional_config), version: "123"))
|
Kamal::Configuration.new(@config.deep_merge(additional_config), version: "123").then do |config|
|
||||||
|
KAMAL.reset
|
||||||
|
KAMAL.stubs(:config).returns(config)
|
||||||
|
Kamal::Commands::Builder.new(config)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def local_arch
|
def local_arch
|
||||||
|
|||||||
@@ -85,6 +85,15 @@ class CommandsRegistryTest < ActiveSupport::TestCase
|
|||||||
registry.logout(registry_config: accessory_registry_config).join(" ")
|
registry.logout(registry_config: accessory_registry_config).join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "registry setup" do
|
||||||
|
@config[:registry] = { "server" => "localhost:5000" }
|
||||||
|
assert_equal "docker start kamal-docker-registry || docker run --detach -p 127.0.0.1:5000:5000 --name kamal-docker-registry registry:3", registry.setup.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "registry remove" do
|
||||||
|
assert_equal "docker stop kamal-docker-registry && docker rm kamal-docker-registry", registry.remove.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def registry
|
def registry
|
||||||
Kamal::Commands::Registry.new main_config
|
Kamal::Commands::Registry.new main_config
|
||||||
|
|||||||
@@ -43,6 +43,19 @@ class ConfigurationTest < ActiveSupport::TestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "image uses service name if registry is local" do
|
||||||
|
assert_equal "app", Kamal::Configuration.new(@deploy.tap {
|
||||||
|
_1[:registry] = { "server" => "localhost:5000" }
|
||||||
|
_1.delete(:image)
|
||||||
|
}).image
|
||||||
|
end
|
||||||
|
|
||||||
|
test "image uses image if registry is local" do
|
||||||
|
assert_equal "dhh/app", Kamal::Configuration.new(@deploy.tap {
|
||||||
|
_1[:registry] = { "server" => "localhost:5000" }
|
||||||
|
}).image
|
||||||
|
end
|
||||||
|
|
||||||
test "service name valid" do
|
test "service name valid" do
|
||||||
assert_nothing_raised do
|
assert_nothing_raised do
|
||||||
Kamal::Configuration.new(@deploy.tap { |config| config[:service] = "hey-app1_primary" })
|
Kamal::Configuration.new(@deploy.tap { |config| config[:service] = "hey-app1_primary" })
|
||||||
|
|||||||
10
test/fixtures/deploy_with_local_registry.yml
vendored
Normal file
10
test/fixtures/deploy_with_local_registry.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
service: app
|
||||||
|
image: dhh/app
|
||||||
|
servers:
|
||||||
|
web:
|
||||||
|
- '1.1.1.1'
|
||||||
|
- '1.1.1.2'
|
||||||
|
registry:
|
||||||
|
server: localhost:5000
|
||||||
|
builder:
|
||||||
|
arch: amd64
|
||||||
45
test/fixtures/deploy_with_local_registry_and_accessories.yml
vendored
Normal file
45
test/fixtures/deploy_with_local_registry_and_accessories.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
service: app
|
||||||
|
image: dhh/app
|
||||||
|
servers:
|
||||||
|
web:
|
||||||
|
- '1.1.1.1'
|
||||||
|
- '1.1.1.2'
|
||||||
|
workers:
|
||||||
|
- '1.1.1.3'
|
||||||
|
- '1.1.1.4'
|
||||||
|
registry:
|
||||||
|
server: localhost:5000
|
||||||
|
builder:
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
accessories:
|
||||||
|
mysql:
|
||||||
|
image: mysql:5.7
|
||||||
|
host: 1.1.1.3
|
||||||
|
port: 3306
|
||||||
|
env:
|
||||||
|
clear:
|
||||||
|
MYSQL_ROOT_HOST: '%'
|
||||||
|
secret:
|
||||||
|
- MYSQL_ROOT_PASSWORD
|
||||||
|
files:
|
||||||
|
- test/fixtures/files/my.cnf:/etc/mysql/my.cnf
|
||||||
|
directories:
|
||||||
|
- data:/var/lib/mysql
|
||||||
|
redis:
|
||||||
|
image: redis:latest
|
||||||
|
roles:
|
||||||
|
- web
|
||||||
|
port: 6379
|
||||||
|
directories:
|
||||||
|
- data:/data
|
||||||
|
busybox:
|
||||||
|
service: custom-box
|
||||||
|
image: busybox:latest
|
||||||
|
host: 1.1.1.3
|
||||||
|
registry:
|
||||||
|
server: other.registry
|
||||||
|
username: other_user
|
||||||
|
password: other_pw
|
||||||
|
|
||||||
|
readiness_delay: 0
|
||||||
@@ -29,14 +29,14 @@ class AppTest < IntegrationTest
|
|||||||
images = kamal :app, :images, capture: true
|
images = kamal :app, :images, capture: true
|
||||||
assert_match "App Host: vm1", images
|
assert_match "App Host: vm1", images
|
||||||
assert_match "App Host: vm2", images
|
assert_match "App Host: vm2", images
|
||||||
assert_match /registry:4443\/app\s+#{latest_app_version}/, images
|
assert_match /localhost:5000\/app\s+#{latest_app_version}/, images
|
||||||
assert_match /registry:4443\/app\s+latest/, images
|
assert_match /localhost:5000\/app\s+latest/, images
|
||||||
|
|
||||||
containers = kamal :app, :containers, capture: true
|
containers = kamal :app, :containers, capture: true
|
||||||
assert_match "App Host: vm1", containers
|
assert_match "App Host: vm1", containers
|
||||||
assert_match "App Host: vm2", containers
|
assert_match "App Host: vm2", containers
|
||||||
assert_match "registry:4443/app:#{latest_app_version}", containers
|
assert_match "localhost:5000/app:#{latest_app_version}", containers
|
||||||
assert_match "registry:4443/app:latest", containers
|
assert_match "localhost:5000/app:latest", containers
|
||||||
|
|
||||||
exec_output = kamal :app, :exec, :ps, capture: true
|
exec_output = kamal :app, :exec, :ps, capture: true
|
||||||
assert_match "App Host: vm1", exec_output
|
assert_match "App Host: vm1", exec_output
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ servers:
|
|||||||
env:
|
env:
|
||||||
clear:
|
clear:
|
||||||
CLEAR_TOKEN: 4321
|
CLEAR_TOKEN: 4321
|
||||||
CLEAR_TAG: ""
|
CLEAR_TAG: ''
|
||||||
HOST_TOKEN: "${HOST_TOKEN}"
|
HOST_TOKEN: '${HOST_TOKEN}'
|
||||||
secret:
|
secret:
|
||||||
- SECRET_TOKEN
|
- SECRET_TOKEN
|
||||||
- INTERPOLATED_SECRET1
|
- INTERPOLATED_SECRET1
|
||||||
@@ -26,9 +26,7 @@ readiness_delay: 0
|
|||||||
proxy:
|
proxy:
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
registry:
|
registry:
|
||||||
server: registry:4443
|
server: localhost:5000
|
||||||
username: root
|
|
||||||
password: root
|
|
||||||
builder:
|
builder:
|
||||||
driver: docker
|
driver: docker
|
||||||
arch: <%= Kamal::Utils.docker_arch %>
|
arch: <%= Kamal::Utils.docker_arch %>
|
||||||
|
|||||||
@@ -131,7 +131,9 @@ class IntegrationTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
def wait_for_healthy(timeout: 30)
|
def wait_for_healthy(timeout: 30)
|
||||||
timeout_at = Time.now + timeout
|
timeout_at = Time.now + timeout
|
||||||
while docker_compose("ps -a | tail -n +2 | grep -v '(healthy)' | wc -l", capture: true) != "0"
|
loop do
|
||||||
|
result = docker_compose("ps -a | tail -n +2 | grep -v '(healthy)' | wc -l", capture: true)
|
||||||
|
break if result.split.last == "0" || result == "0"
|
||||||
if timeout_at < Time.now
|
if timeout_at < Time.now
|
||||||
docker_compose("ps -a | tail -n +2 | grep -v '(healthy)'")
|
docker_compose("ps -a | tail -n +2 | grep -v '(healthy)'")
|
||||||
raise "Container not healthy after #{timeout} seconds" if timeout_at < Time.now
|
raise "Container not healthy after #{timeout} seconds" if timeout_at < Time.now
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class MainTest < IntegrationTest
|
|||||||
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 /basecamp\/kamal-proxy:#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}/, details
|
assert_match /basecamp\/kamal-proxy:#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}/, details
|
||||||
assert_match /registry:4443\/app:#{first_version}/, details
|
assert_match /localhost:5000\/app:#{first_version}/, details
|
||||||
|
|
||||||
audit = kamal :audit, capture: true
|
audit = kamal :audit, capture: true
|
||||||
assert_match /Booted app version #{first_version}.*Booted app version #{second_version}.*Booted app version #{first_version}.*/m, audit
|
assert_match /Booted app version #{first_version}.*Booted app version #{second_version}.*Booted app version #{first_version}.*/m, audit
|
||||||
@@ -67,8 +67,8 @@ class MainTest < IntegrationTest
|
|||||||
assert_equal [ "vm1", "vm2", "vm3" ], config[:hosts]
|
assert_equal [ "vm1", "vm2", "vm3" ], config[:hosts]
|
||||||
assert_equal "vm1", config[:primary_host]
|
assert_equal "vm1", config[:primary_host]
|
||||||
assert_equal version, config[:version]
|
assert_equal version, config[:version]
|
||||||
assert_equal "registry:4443/app", config[:repository]
|
assert_equal "localhost:5000/app", config[:repository]
|
||||||
assert_equal "registry:4443/app:#{version}", config[:absolute_image]
|
assert_equal "localhost:5000/app:#{version}", config[:absolute_image]
|
||||||
assert_equal "app-#{version}", config[:service_with_version]
|
assert_equal "app-#{version}", config[:service_with_version]
|
||||||
assert_equal [], config[:volume_args]
|
assert_equal [], config[:volume_args]
|
||||||
assert_equal({ user: "root", port: 22, keepalive: true, keepalive_interval: 30, log_level: :fatal }, config[:ssh_options])
|
assert_equal({ user: "root", port: 22, keepalive: true, keepalive_interval: 30, log_level: :fatal }, config[:ssh_options])
|
||||||
|
|||||||
Reference in New Issue
Block a user