Compare commits
38 Commits
return-502
...
v1.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a155b7baab | ||
|
|
175e3bc159 | ||
|
|
e3d8a2aa82 | ||
|
|
0e067fb5e1 | ||
|
|
63babecba7 | ||
|
|
79baa598fa | ||
|
|
b1dc188841 | ||
|
|
635876bdb9 | ||
|
|
11521517fa | ||
|
|
610d9de3fd | ||
|
|
bf79df0f72 | ||
|
|
a0959b5afd | ||
|
|
7472e5dfa6 | ||
|
|
887b7dd46d | ||
|
|
77a79b299a | ||
|
|
efcb855db7 | ||
|
|
7137850354 | ||
|
|
8a85840a47 | ||
|
|
80cc0c23d8 | ||
|
|
14a9129410 | ||
|
|
60187cc3a4 | ||
|
|
87cb8c1f71 | ||
|
|
ed58ce6e61 | ||
|
|
263b4a4fb8 | ||
|
|
073f745677 | ||
|
|
a9cc7c73d2 | ||
|
|
6898e8789e | ||
|
|
d0ac6507e7 | ||
|
|
628a47ad88 | ||
|
|
47f8725cf3 | ||
|
|
5fd4a28bf7 | ||
|
|
97ba6b746b | ||
|
|
da161445fa | ||
|
|
f339626667 | ||
|
|
2d86d4f7cc | ||
|
|
792aa1dbdf | ||
|
|
2d22143a24 | ||
|
|
f96d071222 |
@@ -1,7 +1,7 @@
|
|||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
kamal (1.1.0)
|
kamal (1.3.0)
|
||||||
activesupport (>= 7.0)
|
activesupport (>= 7.0)
|
||||||
bcrypt_pbkdf (~> 1.0)
|
bcrypt_pbkdf (~> 1.0)
|
||||||
concurrent-ruby (~> 1.2)
|
concurrent-ruby (~> 1.2)
|
||||||
|
|||||||
@@ -49,9 +49,12 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "reboot [NAME]", "Reboot existing accessory on host (stop container, remove container, start new container)"
|
desc "reboot [NAME]", "Reboot existing accessory on host (stop container, remove container, start new container; use NAME=all to boot all accessories)"
|
||||||
def reboot(name)
|
def reboot(name)
|
||||||
mutating do
|
mutating do
|
||||||
|
if name == "all"
|
||||||
|
KAMAL.accessory_names.each { |accessory_name| reboot(accessory_name) }
|
||||||
|
else
|
||||||
with_accessory(name) do |accessory|
|
with_accessory(name) do |accessory|
|
||||||
on(accessory.hosts) do
|
on(accessory.hosts) do
|
||||||
execute *KAMAL.registry.login
|
execute *KAMAL.registry.login
|
||||||
@@ -63,6 +66,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
desc "start [NAME]", "Start existing accessory container on host"
|
desc "start [NAME]", "Start existing accessory container on host"
|
||||||
def start(name)
|
def start(name)
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ module Kamal::Cli
|
|||||||
class_option :version, desc: "Run commands against a specific app version"
|
class_option :version, desc: "Run commands against a specific app version"
|
||||||
|
|
||||||
class_option :primary, type: :boolean, aliases: "-p", desc: "Run commands only on primary host instead of all"
|
class_option :primary, type: :boolean, aliases: "-p", desc: "Run commands only on primary host instead of all"
|
||||||
class_option :hosts, aliases: "-h", desc: "Run commands on these hosts instead of all (separate by comma)"
|
class_option :hosts, aliases: "-h", desc: "Run commands on these hosts instead of all (separate by comma, supports wildcards with *)"
|
||||||
class_option :roles, aliases: "-r", desc: "Run commands on these roles instead of all (separate by comma)"
|
class_option :roles, aliases: "-r", desc: "Run commands on these roles instead of all (separate by comma, supports wildcards with *)"
|
||||||
|
|
||||||
class_option :config_file, aliases: "-c", default: "config/deploy.yml", desc: "Path to config file"
|
class_option :config_file, aliases: "-c", default: "config/deploy.yml", desc: "Path to config file"
|
||||||
class_option :destination, aliases: "-d", desc: "Specify destination to be used for config file (staging -> deploy.staging.yml)"
|
class_option :destination, aliases: "-d", desc: "Specify destination to be used for config file (staging -> deploy.staging.yml)"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ class Kamal::Cli::Healthcheck < Kamal::Cli::Base
|
|||||||
|
|
||||||
desc "perform", "Health check current app version"
|
desc "perform", "Health check current app version"
|
||||||
def perform
|
def perform
|
||||||
|
raise "The primary host is not configured to run Traefik" unless KAMAL.config.role(KAMAL.config.primary_role).running_traefik?
|
||||||
on(KAMAL.primary_host) do
|
on(KAMAL.primary_host) do
|
||||||
begin
|
begin
|
||||||
execute *KAMAL.healthcheck.run
|
execute *KAMAL.healthcheck.run
|
||||||
|
|||||||
@@ -38,8 +38,10 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|||||||
say "Ensure Traefik is running...", :magenta
|
say "Ensure Traefik is running...", :magenta
|
||||||
invoke "kamal:cli:traefik:boot", [], invoke_options
|
invoke "kamal:cli:traefik:boot", [], invoke_options
|
||||||
|
|
||||||
|
if KAMAL.config.role(KAMAL.config.primary_role).running_traefik?
|
||||||
say "Ensure app can pass healthcheck...", :magenta
|
say "Ensure app can pass healthcheck...", :magenta
|
||||||
invoke "kamal:cli:healthcheck:perform", [], invoke_options
|
invoke "kamal:cli:healthcheck:perform", [], invoke_options
|
||||||
|
end
|
||||||
|
|
||||||
say "Detect stale containers...", :magenta
|
say "Detect stale containers...", :magenta
|
||||||
invoke "kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true)
|
invoke "kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true)
|
||||||
|
|||||||
@@ -83,3 +83,15 @@ registry:
|
|||||||
# boot:
|
# boot:
|
||||||
# limit: 10 # Can also specify as a percentage of total hosts, such as "25%"
|
# limit: 10 # Can also specify as a percentage of total hosts, such as "25%"
|
||||||
# wait: 2
|
# wait: 2
|
||||||
|
|
||||||
|
# Configure the role used to determine the primary_host. This host takes
|
||||||
|
# deploy locks, runs health checks during the deploy, and follow logs, etc.
|
||||||
|
#
|
||||||
|
# Caution: there's no support for role renaming yet, so be careful to cleanup
|
||||||
|
# the previous role on the deployed hosts.
|
||||||
|
# primary_role: web
|
||||||
|
|
||||||
|
# Controls if we abort when see a role with no hosts. Disabling this may be
|
||||||
|
# useful for more complex deploy configurations.
|
||||||
|
#
|
||||||
|
# allow_empty_roles: false
|
||||||
|
|||||||
3
lib/kamal/cli/templates/sample_hooks/post-traefik-reboot.sample
Executable file
3
lib/kamal/cli/templates/sample_hooks/post-traefik-reboot.sample
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Rebooted Traefik on $KAMAL_HOSTS"
|
||||||
@@ -32,7 +32,7 @@ fi
|
|||||||
current_branch=$(git branch --show-current)
|
current_branch=$(git branch --show-current)
|
||||||
|
|
||||||
if [ -z "$current_branch" ]; then
|
if [ -z "$current_branch" ]; then
|
||||||
echo "No git remote set, aborting..." >&2
|
echo "Not on a git branch, aborting..." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
3
lib/kamal/cli/templates/sample_hooks/pre-traefik-reboot.sample
Executable file
3
lib/kamal/cli/templates/sample_hooks/pre-traefik-reboot.sample
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Rebooting Traefik on $KAMAL_HOSTS..."
|
||||||
@@ -13,13 +13,19 @@ class Kamal::Cli::Traefik < Kamal::Cli::Base
|
|||||||
option :rolling, type: :boolean, default: false, desc: "Reboot traefik on hosts in sequence, rather than in parallel"
|
option :rolling, type: :boolean, default: false, desc: "Reboot traefik on hosts in sequence, rather than in parallel"
|
||||||
def reboot
|
def reboot
|
||||||
mutating do
|
mutating do
|
||||||
on(KAMAL.traefik_hosts, in: options[:rolling] ? :sequence : :parallel) do
|
host_groups = options[:rolling] ? KAMAL.traefik_hosts : [KAMAL.traefik_hosts]
|
||||||
|
host_groups.each do |hosts|
|
||||||
|
host_list = Array(hosts).join(",")
|
||||||
|
run_hook "pre-traefik-reboot", hosts: host_list
|
||||||
|
on(hosts) do
|
||||||
execute *KAMAL.auditor.record("Rebooted traefik"), verbosity: :debug
|
execute *KAMAL.auditor.record("Rebooted traefik"), verbosity: :debug
|
||||||
execute *KAMAL.registry.login
|
execute *KAMAL.registry.login
|
||||||
execute *KAMAL.traefik.stop
|
execute *KAMAL.traefik.stop
|
||||||
execute *KAMAL.traefik.remove_container
|
execute *KAMAL.traefik.remove_container
|
||||||
execute *KAMAL.traefik.run
|
execute *KAMAL.traefik.run
|
||||||
end
|
end
|
||||||
|
run_hook "post-traefik-reboot", hosts: host_list
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -24,19 +24,36 @@ class Kamal::Commander
|
|||||||
attr_reader :specific_roles, :specific_hosts
|
attr_reader :specific_roles, :specific_hosts
|
||||||
|
|
||||||
def specific_primary!
|
def specific_primary!
|
||||||
self.specific_hosts = [ config.primary_web_host ]
|
self.specific_hosts = [ config.primary_host ]
|
||||||
end
|
end
|
||||||
|
|
||||||
def specific_roles=(role_names)
|
def specific_roles=(role_names)
|
||||||
@specific_roles = config.roles.select { |r| role_names.include?(r.name) } if role_names.present?
|
if role_names.present?
|
||||||
|
@specific_roles = Kamal::Utils.filter_specific_items(role_names, config.roles)
|
||||||
|
|
||||||
|
if @specific_roles.empty?
|
||||||
|
raise ArgumentError, "No --roles match for #{role_names.join(',')}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@specific_roles
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def specific_hosts=(hosts)
|
def specific_hosts=(hosts)
|
||||||
@specific_hosts = config.all_hosts & hosts if hosts.present?
|
if hosts.present?
|
||||||
|
@specific_hosts = Kamal::Utils.filter_specific_items(hosts, config.all_hosts)
|
||||||
|
|
||||||
|
if @specific_hosts.empty?
|
||||||
|
raise ArgumentError, "No --hosts match for #{hosts.join(',')}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@specific_hosts
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def primary_host
|
def primary_host
|
||||||
specific_hosts&.first || specific_roles&.first&.primary_host || config.primary_web_host
|
# Given a list of specific roles, make an effort to match up with the primary_role
|
||||||
|
specific_hosts&.first || specific_roles&.detect { |role| role.name == config.primary_role }&.primary_host || specific_roles&.first&.primary_host || config.primary_host
|
||||||
end
|
end
|
||||||
|
|
||||||
def primary_role
|
def primary_role
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ module Kamal::Commands
|
|||||||
elsif config.ssh.proxy && config.ssh.proxy.is_a?(Net::SSH::Proxy::Command)
|
elsif config.ssh.proxy && config.ssh.proxy.is_a?(Net::SSH::Proxy::Command)
|
||||||
cmd << " -o ProxyCommand='#{config.ssh.proxy.command_line_template}'"
|
cmd << " -o ProxyCommand='#{config.ssh.proxy.command_line_template}'"
|
||||||
end
|
end
|
||||||
cmd << " -t #{config.ssh.user}@#{host} '#{command.join(" ")}'"
|
cmd << " -t #{config.ssh.user}@#{host} -p #{config.ssh.port} '#{command.join(" ")}'"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
class Kamal::Commands::Healthcheck < Kamal::Commands::Base
|
class Kamal::Commands::Healthcheck < Kamal::Commands::Base
|
||||||
|
|
||||||
def run
|
def run
|
||||||
web = config.role(:web)
|
primary = config.role(config.primary_role)
|
||||||
|
|
||||||
docker :run,
|
docker :run,
|
||||||
"--detach",
|
"--detach",
|
||||||
@@ -9,12 +9,12 @@ class Kamal::Commands::Healthcheck < Kamal::Commands::Base
|
|||||||
"--publish", "#{exposed_port}:#{config.healthcheck["port"]}",
|
"--publish", "#{exposed_port}:#{config.healthcheck["port"]}",
|
||||||
"--label", "service=#{config.healthcheck_service}",
|
"--label", "service=#{config.healthcheck_service}",
|
||||||
"-e", "KAMAL_CONTAINER_NAME=\"#{config.healthcheck_service}\"",
|
"-e", "KAMAL_CONTAINER_NAME=\"#{config.healthcheck_service}\"",
|
||||||
*web.env_args,
|
*primary.env_args,
|
||||||
*web.health_check_args(cord: false),
|
*primary.health_check_args(cord: false),
|
||||||
*config.volume_args,
|
*config.volume_args,
|
||||||
*web.option_args,
|
*primary.option_args,
|
||||||
config.absolute_image,
|
config.absolute_image,
|
||||||
web.cmd
|
primary.cmd
|
||||||
end
|
end
|
||||||
|
|
||||||
def status
|
def status
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ class Kamal::Configuration
|
|||||||
|
|
||||||
def load_config_file(file)
|
def load_config_file(file)
|
||||||
if file.exist?
|
if file.exist?
|
||||||
YAML.load(ERB.new(IO.read(file)).result).symbolize_keys
|
# Newer Psych doesn't load aliases by default
|
||||||
|
load_method = YAML.respond_to?(:unsafe_load) ? :unsafe_load : :load
|
||||||
|
YAML.send(load_method, ERB.new(IO.read(file)).result).symbolize_keys
|
||||||
else
|
else
|
||||||
raise "Configuration file not found in #{file}"
|
raise "Configuration file not found in #{file}"
|
||||||
end
|
end
|
||||||
@@ -89,15 +91,22 @@ class Kamal::Configuration
|
|||||||
roles.flat_map(&:hosts).uniq
|
roles.flat_map(&:hosts).uniq
|
||||||
end
|
end
|
||||||
|
|
||||||
def primary_web_host
|
def primary_host
|
||||||
role(:web).primary_host
|
role(primary_role)&.primary_host
|
||||||
|
end
|
||||||
|
|
||||||
|
def traefik_roles
|
||||||
|
roles.select(&:running_traefik?)
|
||||||
|
end
|
||||||
|
|
||||||
|
def traefik_role_names
|
||||||
|
traefik_roles.flat_map(&:name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def traefik_hosts
|
def traefik_hosts
|
||||||
roles.select(&:running_traefik?).flat_map(&:hosts).uniq
|
traefik_roles.flat_map(&:hosts).uniq
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def repository
|
def repository
|
||||||
[ raw_config.registry["server"], image ].compact.join("/")
|
[ raw_config.registry["server"], image ].compact.join("/")
|
||||||
end
|
end
|
||||||
@@ -199,6 +208,14 @@ class Kamal::Configuration
|
|||||||
raw_config.asset_path
|
raw_config.asset_path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def primary_role
|
||||||
|
raw_config.primary_role || "web"
|
||||||
|
end
|
||||||
|
|
||||||
|
def allow_empty_roles?
|
||||||
|
raw_config.allow_empty_roles
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
def valid?
|
def valid?
|
||||||
ensure_destination_if_required && ensure_required_keys_present && ensure_valid_kamal_version
|
ensure_destination_if_required && ensure_required_keys_present && ensure_valid_kamal_version
|
||||||
@@ -208,7 +225,7 @@ class Kamal::Configuration
|
|||||||
{
|
{
|
||||||
roles: role_names,
|
roles: role_names,
|
||||||
hosts: all_hosts,
|
hosts: all_hosts,
|
||||||
primary_host: primary_web_host,
|
primary_host: primary_host,
|
||||||
version: version,
|
version: version,
|
||||||
repository: repository,
|
repository: repository,
|
||||||
absolute_image: absolute_image,
|
absolute_image: absolute_image,
|
||||||
@@ -247,9 +264,19 @@ class Kamal::Configuration
|
|||||||
raise ArgumentError, "You must specify a password for the registry in config/deploy.yml (or set the ENV variable if that's used)"
|
raise ArgumentError, "You must specify a password for the registry in config/deploy.yml (or set the ENV variable if that's used)"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
unless role_names.include?(primary_role)
|
||||||
|
raise ArgumentError, "The primary_role #{primary_role} isn't defined"
|
||||||
|
end
|
||||||
|
|
||||||
|
if role(primary_role).hosts.empty?
|
||||||
|
raise ArgumentError, "No servers specified for the #{primary_role} primary_role"
|
||||||
|
end
|
||||||
|
|
||||||
|
unless allow_empty_roles?
|
||||||
roles.each do |role|
|
roles.each do |role|
|
||||||
if role.hosts.empty?
|
if role.hosts.empty?
|
||||||
raise ArgumentError, "No servers specified for the #{role.name} role"
|
raise ArgumentError, "No servers specified for the #{role.name} role. You can ignore this with allow_empty_roles: true"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -70,8 +70,8 @@ class Kamal::Configuration::Accessory
|
|||||||
|
|
||||||
def directories
|
def directories
|
||||||
specifics["directories"]&.to_h do |host_to_container_mapping|
|
specifics["directories"]&.to_h do |host_to_container_mapping|
|
||||||
host_relative_path, container_path = host_to_container_mapping.split(":")
|
host_path, container_path = host_to_container_mapping.split(":")
|
||||||
[ expand_host_path(host_relative_path), container_path ]
|
[ expand_host_path(host_path), container_path ]
|
||||||
end || {}
|
end || {}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -138,13 +138,17 @@ class Kamal::Configuration::Accessory
|
|||||||
|
|
||||||
def remote_directories_as_volumes
|
def remote_directories_as_volumes
|
||||||
specifics["directories"]&.collect do |host_to_container_mapping|
|
specifics["directories"]&.collect do |host_to_container_mapping|
|
||||||
host_relative_path, container_path = host_to_container_mapping.split(":")
|
host_path, container_path = host_to_container_mapping.split(":")
|
||||||
[ expand_host_path(host_relative_path), container_path ].join(":")
|
[ expand_host_path(host_path), container_path ].join(":")
|
||||||
end || []
|
end || []
|
||||||
end
|
end
|
||||||
|
|
||||||
def expand_host_path(host_relative_path)
|
def expand_host_path(host_path)
|
||||||
"#{service_data_directory}/#{host_relative_path}"
|
absolute_path?(host_path) ? host_path : "#{service_data_directory}/#{host_path}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def absolute_path?(path)
|
||||||
|
Pathname.new(path).absolute?
|
||||||
end
|
end
|
||||||
|
|
||||||
def service_data_directory
|
def service_data_directory
|
||||||
|
|||||||
@@ -93,7 +93,15 @@ class Kamal::Configuration::Role
|
|||||||
|
|
||||||
|
|
||||||
def running_traefik?
|
def running_traefik?
|
||||||
name.web? || specializations["traefik"]
|
if specializations["traefik"].nil?
|
||||||
|
primary?
|
||||||
|
else
|
||||||
|
specializations["traefik"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def primary?
|
||||||
|
@config.primary_role == name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ class Kamal::Configuration::Ssh
|
|||||||
config.fetch("user", "root")
|
config.fetch("user", "root")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def port
|
||||||
|
config.fetch("port", 22)
|
||||||
|
end
|
||||||
|
|
||||||
def proxy
|
def proxy
|
||||||
if (proxy = config["proxy"])
|
if (proxy = config["proxy"])
|
||||||
Net::SSH::Proxy::Jump.new(proxy.include?("@") ? proxy : "root@#{proxy}")
|
Net::SSH::Proxy::Jump.new(proxy.include?("@") ? proxy : "root@#{proxy}")
|
||||||
@@ -18,7 +22,7 @@ class Kamal::Configuration::Ssh
|
|||||||
end
|
end
|
||||||
|
|
||||||
def options
|
def options
|
||||||
{ user: user, proxy: proxy, logger: logger, keepalive: true, keepalive_interval: 30 }.compact
|
{ user: user, port: port, proxy: proxy, logger: logger, keepalive: true, keepalive_interval: 30 }.compact
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_h
|
def to_h
|
||||||
|
|||||||
@@ -58,4 +58,20 @@ module Kamal::Utils
|
|||||||
.gsub(/`/, '\\\\`')
|
.gsub(/`/, '\\\\`')
|
||||||
.gsub(DOLLAR_SIGN_WITHOUT_SHELL_EXPANSION_REGEX, '\$')
|
.gsub(DOLLAR_SIGN_WITHOUT_SHELL_EXPANSION_REGEX, '\$')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Apply a list of host or role filters, including wildcard matches
|
||||||
|
def filter_specific_items(filters, items)
|
||||||
|
matches = []
|
||||||
|
|
||||||
|
Array(filters).select do |filter|
|
||||||
|
matches += Array(items).select do |item|
|
||||||
|
# Only allow * for a wildcard
|
||||||
|
pattern = Regexp.escape(filter).gsub('\*', '.*')
|
||||||
|
# items are roles or hosts
|
||||||
|
(item.respond_to?(:name) ? item.name : item).match(/^#{pattern}$/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
matches
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
module Kamal
|
module Kamal
|
||||||
VERSION = "1.1.0"
|
VERSION = "1.3.0"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -48,6 +48,18 @@ class CliAccessoryTest < CliTestCase
|
|||||||
run_command("reboot", "mysql")
|
run_command("reboot", "mysql")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "reboot all" do
|
||||||
|
Kamal::Commands::Registry.any_instance.expects(:login).times(3)
|
||||||
|
Kamal::Cli::Accessory.any_instance.expects(:stop).with("mysql")
|
||||||
|
Kamal::Cli::Accessory.any_instance.expects(:remove_container).with("mysql")
|
||||||
|
Kamal::Cli::Accessory.any_instance.expects(:boot).with("mysql", login: false)
|
||||||
|
Kamal::Cli::Accessory.any_instance.expects(:stop).with("redis")
|
||||||
|
Kamal::Cli::Accessory.any_instance.expects(:remove_container).with("redis")
|
||||||
|
Kamal::Cli::Accessory.any_instance.expects(:boot).with("redis", login: false)
|
||||||
|
|
||||||
|
run_command("reboot", "all")
|
||||||
|
end
|
||||||
|
|
||||||
test "start" do
|
test "start" do
|
||||||
assert_match "docker container start app-mysql", run_command("start", "mysql")
|
assert_match "docker container start app-mysql", run_command("start", "mysql")
|
||||||
end
|
end
|
||||||
@@ -97,7 +109,7 @@ class CliAccessoryTest < CliTestCase
|
|||||||
|
|
||||||
test "logs with follow" do
|
test "logs with follow" do
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:exec)
|
SSHKit::Backend::Abstract.any_instance.stubs(:exec)
|
||||||
.with("ssh -t root@1.1.1.3 'docker logs app-mysql --timestamps --tail 10 --follow 2>&1'")
|
.with("ssh -t root@1.1.1.3 -p 22 'docker logs app-mysql --timestamps --tail 10 --follow 2>&1'")
|
||||||
|
|
||||||
assert_match "docker logs app-mysql --timestamps --tail 10 --follow 2>&1", run_command("logs", "mysql", "--follow")
|
assert_match "docker logs app-mysql --timestamps --tail 10 --follow 2>&1", run_command("logs", "mysql", "--follow")
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ class CliAppTest < CliTestCase
|
|||||||
|
|
||||||
test "exec interactive" do
|
test "exec interactive" do
|
||||||
SSHKit::Backend::Abstract.any_instance.expects(:exec)
|
SSHKit::Backend::Abstract.any_instance.expects(:exec)
|
||||||
.with("ssh -t root@1.1.1.1 'docker run -it --rm --env-file .kamal/env/roles/app-web.env dhh/app:latest ruby -v'")
|
.with("ssh -t root@1.1.1.1 -p 22 'docker run -it --rm --env-file .kamal/env/roles/app-web.env dhh/app:latest ruby -v'")
|
||||||
run_command("exec", "-i", "ruby -v").tap do |output|
|
run_command("exec", "-i", "ruby -v").tap do |output|
|
||||||
assert_match "Get most recent version available as an image...", output
|
assert_match "Get most recent version available as an image...", output
|
||||||
assert_match "Launching interactive command with version latest via SSH from new container on 1.1.1.1...", output
|
assert_match "Launching interactive command with version latest via SSH from new container on 1.1.1.1...", output
|
||||||
@@ -181,7 +181,7 @@ class CliAppTest < CliTestCase
|
|||||||
|
|
||||||
test "exec interactive with reuse" do
|
test "exec interactive with reuse" do
|
||||||
SSHKit::Backend::Abstract.any_instance.expects(:exec)
|
SSHKit::Backend::Abstract.any_instance.expects(:exec)
|
||||||
.with("ssh -t root@1.1.1.1 'docker exec -it app-web-999 ruby -v'")
|
.with("ssh -t root@1.1.1.1 -p 22 'docker exec -it app-web-999 ruby -v'")
|
||||||
run_command("exec", "-i", "--reuse", "ruby -v").tap do |output|
|
run_command("exec", "-i", "--reuse", "ruby -v").tap do |output|
|
||||||
assert_match "Get current version of running container...", output
|
assert_match "Get current version of running container...", output
|
||||||
assert_match "Running docker ps --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest --format \"{{.Names}}\" | while read line; do echo ${line#app-web-}; done on 1.1.1.1", output
|
assert_match "Running docker ps --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest --format \"{{.Names}}\" | while read line; do echo ${line#app-web-}; done on 1.1.1.1", output
|
||||||
@@ -210,7 +210,7 @@ class CliAppTest < CliTestCase
|
|||||||
|
|
||||||
test "logs with follow" do
|
test "logs with follow" do
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:exec)
|
SSHKit::Backend::Abstract.any_instance.stubs(:exec)
|
||||||
.with("ssh -t root@1.1.1.1 'docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker logs --timestamps --tail 10 --follow 2>&1'")
|
.with("ssh -t root@1.1.1.1 -p 22 'docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker logs --timestamps --tail 10 --follow 2>&1'")
|
||||||
|
|
||||||
assert_match "docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker logs --timestamps --tail 10 --follow 2>&1", run_command("logs", "--follow")
|
assert_match "docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest | xargs docker logs --timestamps --tail 10 --follow 2>&1", run_command("logs", "--follow")
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -65,8 +65,18 @@ class CliHealthcheckTest < CliTestCase
|
|||||||
assert_match "container not ready (unhealthy)", exception.message
|
assert_match "container not ready (unhealthy)", exception.message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "raises an exception if primary does not have traefik" do
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:execute).never
|
||||||
|
|
||||||
|
exception = assert_raises do
|
||||||
|
run_command("perform", config_file: "test/fixtures/deploy_workers_only.yml")
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal "The primary host is not configured to run Traefik", exception.message
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def run_command(*command)
|
def run_command(*command, config_file: "test/fixtures/deploy_with_accessories.yml")
|
||||||
stdouted { Kamal::Cli::Healthcheck.start([*command, "-c", "test/fixtures/deploy_with_accessories.yml"]) }
|
stdouted { Kamal::Cli::Healthcheck.start([*command, "-c", config_file]) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -123,6 +123,21 @@ class CliMainTest < CliTestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "deploy without healthcheck if primary host doesn't have traefik" do
|
||||||
|
invoke_options = { "config_file" => "test/fixtures/deploy_workers_only.yml", "version" => "999", "skip_hooks" => false }
|
||||||
|
|
||||||
|
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:healthcheck:perform", [], invoke_options).never
|
||||||
|
|
||||||
|
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:registry:login", [], invoke_options)
|
||||||
|
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:build:deliver", [], invoke_options)
|
||||||
|
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:traefik: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)
|
||||||
|
|
||||||
|
run_command("deploy", config_file: "deploy_workers_only")
|
||||||
|
end
|
||||||
|
|
||||||
test "deploy with missing secrets" do
|
test "deploy with missing secrets" do
|
||||||
invoke_options = { "config_file" => "test/fixtures/deploy_with_secrets.yml", "version" => "999", "skip_hooks" => false }
|
invoke_options = { "config_file" => "test/fixtures/deploy_with_secrets.yml", "version" => "999", "skip_hooks" => false }
|
||||||
|
|
||||||
@@ -283,6 +298,16 @@ class CliMainTest < CliTestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "config with primary web role override" do
|
||||||
|
run_command("config", config_file: "deploy_primary_web_role_override").tap do |output|
|
||||||
|
config = YAML.load(output)
|
||||||
|
|
||||||
|
assert_equal ["web_chicago", "web_tokyo"], config[:roles]
|
||||||
|
assert_equal ["1.1.1.1", "1.1.1.2", "1.1.1.3", "1.1.1.4"], config[:hosts]
|
||||||
|
assert_equal "1.1.1.3", config[:primary_host]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "config with destination" do
|
test "config with destination" do
|
||||||
run_command("config", "-d", "world", config_file: "deploy_for_dest").tap do |output|
|
run_command("config", "-d", "world", config_file: "deploy_for_dest").tap do |output|
|
||||||
config = YAML.load(output)
|
config = YAML.load(output)
|
||||||
@@ -296,6 +321,19 @@ class CliMainTest < CliTestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "config with aliases" do
|
||||||
|
run_command("config", config_file: "deploy_with_aliases").tap do |output|
|
||||||
|
config = YAML.load(output)
|
||||||
|
|
||||||
|
assert_equal ["web", "web_tokyo", "workers", "workers_tokyo"], config[:roles]
|
||||||
|
assert_equal ["1.1.1.1", "1.1.1.2", "1.1.1.3", "1.1.1.4"], config[:hosts]
|
||||||
|
assert_equal "999", config[:version]
|
||||||
|
assert_equal "registry.digitalocean.com/dhh/app", config[:repository]
|
||||||
|
assert_equal "registry.digitalocean.com/dhh/app:999", config[:absolute_image]
|
||||||
|
assert_equal "app-999", config[:service_with_version]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "init" do
|
test "init" do
|
||||||
Pathname.any_instance.expects(:exist?).returns(false).times(3)
|
Pathname.any_instance.expects(:exist?).returns(false).times(3)
|
||||||
Pathname.any_instance.stubs(:mkpath)
|
Pathname.any_instance.stubs(:mkpath)
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class CliTraefikTest < CliTestCase
|
|||||||
|
|
||||||
test "logs with follow" do
|
test "logs with follow" do
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:exec)
|
SSHKit::Backend::Abstract.any_instance.stubs(:exec)
|
||||||
.with("ssh -t root@1.1.1.1 'docker logs traefik --timestamps --tail 10 --follow 2>&1'")
|
.with("ssh -t root@1.1.1.1 -p 22 'docker logs traefik --timestamps --tail 10 --follow 2>&1'")
|
||||||
|
|
||||||
assert_match "docker logs traefik --timestamps --tail 10 --follow", run_command("logs", "--follow")
|
assert_match "docker logs traefik --timestamps --tail 10 --follow", run_command("logs", "--follow")
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -14,6 +14,20 @@ class CommanderTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
@kamal.specific_hosts = [ "1.1.1.1", "1.1.1.2" ]
|
@kamal.specific_hosts = [ "1.1.1.1", "1.1.1.2" ]
|
||||||
assert_equal [ "1.1.1.1", "1.1.1.2" ], @kamal.hosts
|
assert_equal [ "1.1.1.1", "1.1.1.2" ], @kamal.hosts
|
||||||
|
|
||||||
|
@kamal.specific_hosts = [ "1.1.1.1*" ]
|
||||||
|
assert_equal [ "1.1.1.1" ], @kamal.hosts
|
||||||
|
|
||||||
|
@kamal.specific_hosts = [ "1.1.1.*", "*.1.2.*" ]
|
||||||
|
assert_equal [ "1.1.1.1", "1.1.1.2", "1.1.1.3", "1.1.1.4" ], @kamal.hosts
|
||||||
|
|
||||||
|
@kamal.specific_hosts = [ "*" ]
|
||||||
|
assert_equal [ "1.1.1.1", "1.1.1.2", "1.1.1.3", "1.1.1.4" ], @kamal.hosts
|
||||||
|
|
||||||
|
exception = assert_raises(ArgumentError) do
|
||||||
|
@kamal.specific_hosts = [ "*miss" ]
|
||||||
|
end
|
||||||
|
assert_match /hosts match for \*miss/, exception.message
|
||||||
end
|
end
|
||||||
|
|
||||||
test "filtering hosts by filtering roles" do
|
test "filtering hosts by filtering roles" do
|
||||||
@@ -21,6 +35,11 @@ class CommanderTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
@kamal.specific_roles = [ "web" ]
|
@kamal.specific_roles = [ "web" ]
|
||||||
assert_equal [ "1.1.1.1", "1.1.1.2" ], @kamal.hosts
|
assert_equal [ "1.1.1.1", "1.1.1.2" ], @kamal.hosts
|
||||||
|
|
||||||
|
exception = assert_raises(ArgumentError) do
|
||||||
|
@kamal.specific_roles = [ "*miss" ]
|
||||||
|
end
|
||||||
|
assert_match /roles match for \*miss/, exception.message
|
||||||
end
|
end
|
||||||
|
|
||||||
test "filtering roles" do
|
test "filtering roles" do
|
||||||
@@ -28,6 +47,20 @@ class CommanderTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
@kamal.specific_roles = [ "workers" ]
|
@kamal.specific_roles = [ "workers" ]
|
||||||
assert_equal [ "workers" ], @kamal.roles.map(&:name)
|
assert_equal [ "workers" ], @kamal.roles.map(&:name)
|
||||||
|
|
||||||
|
@kamal.specific_roles = [ "w*" ]
|
||||||
|
assert_equal [ "web", "workers" ], @kamal.roles.map(&:name)
|
||||||
|
|
||||||
|
@kamal.specific_roles = [ "we*", "*orkers" ]
|
||||||
|
assert_equal [ "web", "workers" ], @kamal.roles.map(&:name)
|
||||||
|
|
||||||
|
@kamal.specific_roles = [ "*" ]
|
||||||
|
assert_equal [ "web", "workers" ], @kamal.roles.map(&:name)
|
||||||
|
|
||||||
|
exception = assert_raises(ArgumentError) do
|
||||||
|
@kamal.specific_roles = [ "*miss" ]
|
||||||
|
end
|
||||||
|
assert_match /roles match for \*miss/, exception.message
|
||||||
end
|
end
|
||||||
|
|
||||||
test "filtering roles by filtering hosts" do
|
test "filtering roles by filtering hosts" do
|
||||||
@@ -76,6 +109,15 @@ class CommanderTest < ActiveSupport::TestCase
|
|||||||
assert_equal({ in: :groups, limit: 1, wait: 2 }, @kamal.boot_strategy)
|
assert_equal({ in: :groups, limit: 1, wait: 2 }, @kamal.boot_strategy)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "try to match the primary role from a list of specific roles" do
|
||||||
|
configure_with(:deploy_primary_web_role_override)
|
||||||
|
|
||||||
|
@kamal.specific_roles = [ "web_*" ]
|
||||||
|
assert_equal [ "web_chicago", "web_tokyo" ], @kamal.roles.map(&:name)
|
||||||
|
assert_equal "web_tokyo", @kamal.primary_role
|
||||||
|
assert_equal "1.1.1.3", @kamal.primary_host
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def configure_with(variant)
|
def configure_with(variant)
|
||||||
@kamal = Kamal::Commander.new.tap do |kamal|
|
@kamal = Kamal::Commander.new.tap do |kamal|
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
test "follow logs" do
|
test "follow logs" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"ssh -t root@1.1.1.5 'docker logs app-mysql --timestamps --tail 10 --follow 2>&1'",
|
"ssh -t root@1.1.1.5 -p 22 'docker logs app-mysql --timestamps --tail 10 --follow 2>&1'",
|
||||||
new_command(:mysql).follow_logs
|
new_command(:mysql).follow_logs
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -190,32 +190,37 @@ class CommandsAppTest < ActiveSupport::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "run over ssh" do
|
test "run over ssh" do
|
||||||
assert_equal "ssh -t root@1.1.1.1 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
assert_equal "ssh -t root@1.1.1.1 -p 22 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "run over ssh with custom user" do
|
test "run over ssh with custom user" do
|
||||||
@config[:ssh] = { "user" => "app" }
|
@config[:ssh] = { "user" => "app" }
|
||||||
assert_equal "ssh -t app@1.1.1.1 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
assert_equal "ssh -t app@1.1.1.1 -p 22 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "run over ssh with custom port" do
|
||||||
|
@config[:ssh] = { "port" => "2222" }
|
||||||
|
assert_equal "ssh -t root@1.1.1.1 -p 2222 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "run over ssh with proxy" do
|
test "run over ssh with proxy" do
|
||||||
@config[:ssh] = { "proxy" => "2.2.2.2" }
|
@config[:ssh] = { "proxy" => "2.2.2.2" }
|
||||||
assert_equal "ssh -J root@2.2.2.2 -t root@1.1.1.1 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
assert_equal "ssh -J root@2.2.2.2 -t root@1.1.1.1 -p 22 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "run over ssh with proxy user" do
|
test "run over ssh with proxy user" do
|
||||||
@config[:ssh] = { "proxy" => "app@2.2.2.2" }
|
@config[:ssh] = { "proxy" => "app@2.2.2.2" }
|
||||||
assert_equal "ssh -J app@2.2.2.2 -t root@1.1.1.1 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
assert_equal "ssh -J app@2.2.2.2 -t root@1.1.1.1 -p 22 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "run over ssh with custom user with proxy" do
|
test "run over ssh with custom user with proxy" do
|
||||||
@config[:ssh] = { "user" => "app", "proxy" => "2.2.2.2" }
|
@config[:ssh] = { "user" => "app", "proxy" => "2.2.2.2" }
|
||||||
assert_equal "ssh -J root@2.2.2.2 -t app@1.1.1.1 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
assert_equal "ssh -J root@2.2.2.2 -t app@1.1.1.1 -p 22 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "run over ssh with proxy_command" do
|
test "run over ssh with proxy_command" do
|
||||||
@config[:ssh] = { "proxy_command" => "ssh -W %h:%p user@proxy-server" }
|
@config[:ssh] = { "proxy_command" => "ssh -W %h:%p user@proxy-server" }
|
||||||
assert_equal "ssh -o ProxyCommand='ssh -W %h:%p user@proxy-server' -t root@1.1.1.1 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
assert_equal "ssh -o ProxyCommand='ssh -W %h:%p user@proxy-server' -t root@1.1.1.1 -p 22 'ls'", new_command.run_over_ssh("ls", host: "1.1.1.1")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "current_running_container_id" do
|
test "current_running_container_id" do
|
||||||
|
|||||||
@@ -167,13 +167,13 @@ class CommandsTraefikTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
test "traefik follow logs" do
|
test "traefik follow logs" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"ssh -t root@1.1.1.1 'docker logs traefik --timestamps --tail 10 --follow 2>&1'",
|
"ssh -t root@1.1.1.1 -p 22 'docker logs traefik --timestamps --tail 10 --follow 2>&1'",
|
||||||
new_command.follow_logs(host: @config[:servers].first)
|
new_command.follow_logs(host: @config[:servers].first)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "traefik follow logs with grep hello!" do
|
test "traefik follow logs with grep hello!" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"ssh -t root@1.1.1.1 'docker logs traefik --timestamps --tail 10 --follow 2>&1 | grep \"hello!\"'",
|
"ssh -t root@1.1.1.1 -p 22 'docker logs traefik --timestamps --tail 10 --follow 2>&1 | grep \"hello!\"'",
|
||||||
new_command.follow_logs(host: @config[:servers].first, grep: 'hello!')
|
new_command.follow_logs(host: @config[:servers].first, grep: 'hello!')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -149,10 +149,16 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase
|
|||||||
assert_match "%", @config.accessory(:mysql).files.keys[2].read
|
assert_match "%", @config.accessory(:mysql).files.keys[2].read
|
||||||
end
|
end
|
||||||
|
|
||||||
test "directories" do
|
test "directory with a relative path" do
|
||||||
|
@deploy[:accessories]["mysql"]["directories"] = [ "data:/var/lib/mysql" ]
|
||||||
assert_equal({"$PWD/app-mysql/data"=>"/var/lib/mysql"}, @config.accessory(:mysql).directories)
|
assert_equal({"$PWD/app-mysql/data"=>"/var/lib/mysql"}, @config.accessory(:mysql).directories)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "directory with an absolute path" do
|
||||||
|
@deploy[:accessories]["mysql"]["directories"] = [ "/var/data/mysql:/var/lib/mysql" ]
|
||||||
|
assert_equal({"/var/data/mysql"=>"/var/lib/mysql"}, @config.accessory(:mysql).directories)
|
||||||
|
end
|
||||||
|
|
||||||
test "options" do
|
test "options" do
|
||||||
assert_equal ["--cpus", "\"4\"", "--memory", "\"2GB\""], @config.accessory(:redis).option_args
|
assert_equal ["--cpus", "\"4\"", "--memory", "\"2GB\""], @config.accessory(:redis).option_args
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ class ConfigurationSshTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
config = Kamal::Configuration.new(@deploy.tap { |c| c.merge!(ssh: { "log_level" => "debug" }) })
|
config = Kamal::Configuration.new(@deploy.tap { |c| c.merge!(ssh: { "log_level" => "debug" }) })
|
||||||
assert_equal 0, config.ssh.options[:logger].level
|
assert_equal 0, config.ssh.options[:logger].level
|
||||||
|
|
||||||
|
config = Kamal::Configuration.new(@deploy.tap { |c| c.merge!(ssh: { "port" => 2222 }) })
|
||||||
|
assert_equal 2222, config.ssh.options[:port]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "ssh options with proxy host" do
|
test "ssh options with proxy host" do
|
||||||
|
|||||||
@@ -58,9 +58,9 @@ class ConfigurationTest < ActiveSupport::TestCase
|
|||||||
assert_equal [ "1.1.1.1", "1.1.1.2", "1.1.1.3" ], @config_with_roles.all_hosts
|
assert_equal [ "1.1.1.1", "1.1.1.2", "1.1.1.3" ], @config_with_roles.all_hosts
|
||||||
end
|
end
|
||||||
|
|
||||||
test "primary web host" do
|
test "primary host" do
|
||||||
assert_equal "1.1.1.1", @config.primary_web_host
|
assert_equal "1.1.1.1", @config.primary_host
|
||||||
assert_equal "1.1.1.1", @config_with_roles.primary_web_host
|
assert_equal "1.1.1.1", @config_with_roles.primary_host
|
||||||
end
|
end
|
||||||
|
|
||||||
test "traefik hosts" do
|
test "traefik hosts" do
|
||||||
@@ -165,6 +165,16 @@ class ConfigurationTest < ActiveSupport::TestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "allow_empty_roles" do
|
||||||
|
assert_silent do
|
||||||
|
Kamal::Configuration.new @deploy.merge(servers: { "web" => %w[ web ], "workers" => { "hosts" => %w[ ] } }, allow_empty_roles: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_raises(ArgumentError) do
|
||||||
|
Kamal::Configuration.new @deploy.merge(servers: { "web" => %w[], "workers" => { "hosts" => %w[] } }, allow_empty_roles: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "volume_args" do
|
test "volume_args" do
|
||||||
assert_equal ["--volume", "/local/path:/container/path"], @config.volume_args
|
assert_equal ["--volume", "/local/path:/container/path"], @config.volume_args
|
||||||
end
|
end
|
||||||
@@ -227,7 +237,7 @@ class ConfigurationTest < ActiveSupport::TestCase
|
|||||||
:repository=>"dhh/app",
|
:repository=>"dhh/app",
|
||||||
:absolute_image=>"dhh/app:missing",
|
:absolute_image=>"dhh/app:missing",
|
||||||
:service_with_version=>"app-missing",
|
:service_with_version=>"app-missing",
|
||||||
:ssh_options=>{ :user=>"root", log_level: :fatal, keepalive: true, keepalive_interval: 30 },
|
:ssh_options=>{ :user=>"root", port: 22, log_level: :fatal, keepalive: true, keepalive_interval: 30 },
|
||||||
:sshkit=>{},
|
:sshkit=>{},
|
||||||
:volume_args=>["--volume", "/local/path:/container/path"],
|
:volume_args=>["--volume", "/local/path:/container/path"],
|
||||||
:builder=>{},
|
:builder=>{},
|
||||||
@@ -278,4 +288,25 @@ class ConfigurationTest < ActiveSupport::TestCase
|
|||||||
assert_nil @config.asset_path
|
assert_nil @config.asset_path
|
||||||
assert_equal "foo", Kamal::Configuration.new(@deploy.merge!(asset_path: "foo")).asset_path
|
assert_equal "foo", Kamal::Configuration.new(@deploy.merge!(asset_path: "foo")).asset_path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "primary role" do
|
||||||
|
assert_equal "web", @config.primary_role
|
||||||
|
|
||||||
|
config = Kamal::Configuration.new(@deploy_with_roles.deep_merge({
|
||||||
|
servers: { "alternate_web" => { "hosts" => [ "1.1.1.4", "1.1.1.5" ] } },
|
||||||
|
primary_role: "alternate_web" } ))
|
||||||
|
|
||||||
|
|
||||||
|
assert_equal "alternate_web", config.primary_role
|
||||||
|
assert_equal "1.1.1.4", config.primary_host
|
||||||
|
assert config.role(:alternate_web).primary?
|
||||||
|
assert config.role(:alternate_web).running_traefik?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "primary role missing" do
|
||||||
|
error = assert_raises(ArgumentError) do
|
||||||
|
Kamal::Configuration.new(@deploy.merge(primary_role: "bar"))
|
||||||
|
end
|
||||||
|
assert_match /bar isn't defined/, error.message
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
20
test/fixtures/deploy_primary_web_role_override.yml
vendored
Normal file
20
test/fixtures/deploy_primary_web_role_override.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
service: app
|
||||||
|
image: dhh/app
|
||||||
|
servers:
|
||||||
|
web_chicago:
|
||||||
|
traefik: enabled
|
||||||
|
hosts:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 1.1.1.2
|
||||||
|
web_tokyo:
|
||||||
|
traefik: enabled
|
||||||
|
hosts:
|
||||||
|
- 1.1.1.3
|
||||||
|
- 1.1.1.4
|
||||||
|
env:
|
||||||
|
REDIS_URL: redis://x/y
|
||||||
|
registry:
|
||||||
|
server: registry.digitalocean.com
|
||||||
|
username: user
|
||||||
|
password: pw
|
||||||
|
primary_role: web_tokyo
|
||||||
36
test/fixtures/deploy_with_aliases.yml
vendored
Normal file
36
test/fixtures/deploy_with_aliases.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# helper aliases
|
||||||
|
chicago_hosts: &chicago_hosts
|
||||||
|
hosts:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 1.1.1.2
|
||||||
|
tokyo_hosts: &tokyo_hosts
|
||||||
|
hosts:
|
||||||
|
- 1.1.1.3
|
||||||
|
- 1.1.1.4
|
||||||
|
web_common: &web_common
|
||||||
|
env:
|
||||||
|
ROLE: "web"
|
||||||
|
traefik: true
|
||||||
|
|
||||||
|
# actual config
|
||||||
|
service: app
|
||||||
|
image: dhh/app
|
||||||
|
servers:
|
||||||
|
web:
|
||||||
|
<<: *chicago_hosts
|
||||||
|
<<: *web_common
|
||||||
|
web_tokyo:
|
||||||
|
<<: *tokyo_hosts
|
||||||
|
<<: *web_common
|
||||||
|
workers:
|
||||||
|
cmd: bin/jobs
|
||||||
|
<<: *chicago_hosts
|
||||||
|
workers_tokyo:
|
||||||
|
cmd: bin/jobs
|
||||||
|
<<: *tokyo_hosts
|
||||||
|
env:
|
||||||
|
REDIS_URL: redis://x/y
|
||||||
|
registry:
|
||||||
|
server: registry.digitalocean.com
|
||||||
|
username: user
|
||||||
|
password: pw
|
||||||
12
test/fixtures/deploy_workers_only.yml
vendored
Normal file
12
test/fixtures/deploy_workers_only.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
service: app
|
||||||
|
image: dhh/app
|
||||||
|
servers:
|
||||||
|
workers:
|
||||||
|
traefik: false
|
||||||
|
hosts:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 1.1.1.2
|
||||||
|
primary_role: workers
|
||||||
|
registry:
|
||||||
|
username: user
|
||||||
|
password: pw
|
||||||
3
test/integration/docker/deployer/app/.kamal/hooks/post-traefik-reboot
Executable file
3
test/integration/docker/deployer/app/.kamal/hooks/post-traefik-reboot
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
echo "Rebooted Traefik on ${KAMAL_HOSTS}"
|
||||||
|
mkdir -p /tmp/${TEST_ID} && touch /tmp/${TEST_ID}/post-traefik-reboot
|
||||||
3
test/integration/docker/deployer/app/.kamal/hooks/pre-traefik-reboot
Executable file
3
test/integration/docker/deployer/app/.kamal/hooks/pre-traefik-reboot
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
echo "Rebooting Traefik on ${KAMAL_HOSTS}..."
|
||||||
|
mkdir -p /tmp/${TEST_ID} && touch /tmp/${TEST_ID}/pre-traefik-reboot
|
||||||
@@ -54,7 +54,7 @@ class MainTest < IntegrationTest
|
|||||||
assert_equal "registry:4443/app:#{version}", config[:absolute_image]
|
assert_equal "registry:4443/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", 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])
|
||||||
assert_equal({ "multiarch" => false, "args" => { "COMMIT_SHA" => version } }, config[:builder])
|
assert_equal({ "multiarch" => false, "args" => { "COMMIT_SHA" => version } }, config[:builder])
|
||||||
assert_equal [ "--log-opt", "max-size=\"10m\"" ], config[:logging]
|
assert_equal [ "--log-opt", "max-size=\"10m\"" ], config[:logging]
|
||||||
assert_equal({ "path" => "/up", "port" => 3000, "max_attempts" => 7, "exposed_port" => 3999, "cord"=>"/tmp/kamal-cord", "log_lines" => 50, "cmd"=>"wget -qO- http://localhost > /dev/null || exit 1" }, config[:healthcheck])
|
assert_equal({ "path" => "/up", "port" => 3000, "max_attempts" => 7, "exposed_port" => 3999, "cord"=>"/tmp/kamal-cord", "log_lines" => 50, "cmd"=>"wget -qO- http://localhost > /dev/null || exit 1" }, config[:healthcheck])
|
||||||
|
|||||||
@@ -7,8 +7,19 @@ class TraefikTest < IntegrationTest
|
|||||||
kamal :traefik, :boot
|
kamal :traefik, :boot
|
||||||
assert_traefik_running
|
assert_traefik_running
|
||||||
|
|
||||||
kamal :traefik, :reboot
|
output = kamal :traefik, :reboot, capture: true
|
||||||
assert_traefik_running
|
assert_traefik_running
|
||||||
|
assert_hooks_ran "pre-traefik-reboot", "post-traefik-reboot"
|
||||||
|
assert_match /Rebooting Traefik on vm1,vm2.../, output
|
||||||
|
assert_match /Rebooted Traefik on vm1,vm2/, output
|
||||||
|
|
||||||
|
output = kamal :traefik, :reboot, :"--rolling", capture: true
|
||||||
|
assert_traefik_running
|
||||||
|
assert_hooks_ran "pre-traefik-reboot", "post-traefik-reboot"
|
||||||
|
assert_match /Rebooting Traefik on vm1.../, output
|
||||||
|
assert_match /Rebooted Traefik on vm1/, output
|
||||||
|
assert_match /Rebooting Traefik on vm2.../, output
|
||||||
|
assert_match /Rebooted Traefik on vm2/, output
|
||||||
|
|
||||||
kamal :traefik, :boot
|
kamal :traefik, :boot
|
||||||
assert_traefik_running
|
assert_traefik_running
|
||||||
|
|||||||
Reference in New Issue
Block a user