Compare commits
9 Commits
v1.2.0
...
rollback-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b694a0814 | ||
|
|
635876bdb9 | ||
|
|
11521517fa | ||
|
|
610d9de3fd | ||
|
|
7472e5dfa6 | ||
|
|
887b7dd46d | ||
|
|
87cb8c1f71 | ||
|
|
2d22143a24 | ||
|
|
f96d071222 |
@@ -49,17 +49,21 @@ 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
|
||||||
with_accessory(name) do |accessory|
|
if name == "all"
|
||||||
on(accessory.hosts) do
|
KAMAL.accessory_names.each { |accessory_name| reboot(accessory_name) }
|
||||||
execute *KAMAL.registry.login
|
else
|
||||||
end
|
with_accessory(name) do |accessory|
|
||||||
|
on(accessory.hosts) do
|
||||||
|
execute *KAMAL.registry.login
|
||||||
|
end
|
||||||
|
|
||||||
stop(name)
|
stop(name)
|
||||||
remove_container(name)
|
remove_container(name)
|
||||||
boot(name, login: false)
|
boot(name, login: false)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
say "Ensure app can pass healthcheck...", :magenta
|
if KAMAL.config.role(KAMAL.config.primary_role).running_traefik?
|
||||||
invoke "kamal:cli:healthcheck:perform", [], invoke_options
|
say "Ensure app can pass healthcheck...", :magenta
|
||||||
|
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)
|
||||||
@@ -91,7 +93,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|||||||
mutating do
|
mutating do
|
||||||
invoke_options = deploy_options
|
invoke_options = deploy_options
|
||||||
|
|
||||||
KAMAL.config.version = version
|
KAMAL.config.version = Kamal::Git.used? ? Kamal::Git.resolve_revision(version) : version
|
||||||
old_version = nil
|
old_version = nil
|
||||||
|
|
||||||
if container_available?(version)
|
if container_available?(version)
|
||||||
|
|||||||
@@ -84,13 +84,12 @@ registry:
|
|||||||
# 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_web_host. This host takes
|
# Configure the role used to determine the primary_host. This host takes
|
||||||
# deploy locks, runs health checks during the deploy, and follow logs, etc.
|
# deploy locks, runs health checks during the deploy, and follow logs, etc.
|
||||||
# This role should have traefik enabled.
|
|
||||||
#
|
#
|
||||||
# Caution: there's no support for role renaming yet, so be careful to cleanup
|
# Caution: there's no support for role renaming yet, so be careful to cleanup
|
||||||
# the previous role on the deployed hosts.
|
# the previous role on the deployed hosts.
|
||||||
# primary_web_role: web
|
# primary_role: web
|
||||||
|
|
||||||
# Controls if we abort when see a role with no hosts. Disabling this may be
|
# Controls if we abort when see a role with no hosts. Disabling this may be
|
||||||
# useful for more complex deploy configurations.
|
# useful for more complex deploy configurations.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ 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)
|
||||||
@@ -36,7 +36,7 @@ class Kamal::Commander
|
|||||||
end
|
end
|
||||||
|
|
||||||
def primary_host
|
def primary_host
|
||||||
specific_hosts&.first || specific_roles&.first&.primary_host || config.primary_web_host
|
specific_hosts&.first || specific_roles&.first&.primary_host || config.primary_host
|
||||||
end
|
end
|
||||||
|
|
||||||
def primary_role
|
def primary_role
|
||||||
|
|||||||
@@ -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(config.primary_web_role)
|
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
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ class Kamal::Configuration
|
|||||||
roles.flat_map(&:hosts).uniq
|
roles.flat_map(&:hosts).uniq
|
||||||
end
|
end
|
||||||
|
|
||||||
def primary_web_host
|
def primary_host
|
||||||
role(primary_web_role)&.primary_host
|
role(primary_role)&.primary_host
|
||||||
end
|
end
|
||||||
|
|
||||||
def traefik_roles
|
def traefik_roles
|
||||||
@@ -208,8 +208,8 @@ class Kamal::Configuration
|
|||||||
raw_config.asset_path
|
raw_config.asset_path
|
||||||
end
|
end
|
||||||
|
|
||||||
def primary_web_role
|
def primary_role
|
||||||
raw_config.primary_web_role || "web"
|
raw_config.primary_role || "web"
|
||||||
end
|
end
|
||||||
|
|
||||||
def allow_empty_roles?
|
def allow_empty_roles?
|
||||||
@@ -225,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,
|
||||||
@@ -264,16 +264,12 @@ 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_web_role)
|
unless role_names.include?(primary_role)
|
||||||
raise ArgumentError, "The primary_web_role #{primary_web_role} isn't defined"
|
raise ArgumentError, "The primary_role #{primary_role} isn't defined"
|
||||||
end
|
end
|
||||||
|
|
||||||
unless traefik_role_names.include?(primary_web_role)
|
if role(primary_role).hosts.empty?
|
||||||
raise ArgumentError, "Role #{primary_web_role} needs to have traefik enabled"
|
raise ArgumentError, "No servers specified for the #{primary_role} primary_role"
|
||||||
end
|
|
||||||
|
|
||||||
if role(primary_web_role).hosts.empty?
|
|
||||||
raise ArgumentError, "No servers specified for the #{primary_web_role} primary_web_role"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
unless allow_empty_roles?
|
unless allow_empty_roles?
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,16 @@ module Kamal::Git
|
|||||||
`git rev-parse HEAD`.strip
|
`git rev-parse HEAD`.strip
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Attempt to convert a short -> long git sha, or return the original
|
||||||
|
def resolve_revision(revision)
|
||||||
|
resolved_rev = `git rev-parse -q --verify #{revision}`.strip
|
||||||
|
if resolved_rev.empty?
|
||||||
|
revision
|
||||||
|
else
|
||||||
|
resolved_rev
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def uncommitted_changes
|
def uncommitted_changes
|
||||||
`git status --porcelain`.strip
|
`git status --porcelain`.strip
|
||||||
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
|
||||||
|
|||||||
@@ -64,9 +64,19 @@ class CliHealthcheckTest < CliTestCase
|
|||||||
end
|
end
|
||||||
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
|
||||||
|
|||||||
@@ -122,6 +122,21 @@ class CliMainTest < CliTestCase
|
|||||||
refute_match /Running the post-deploy hook.../, output
|
refute_match /Running the post-deploy hook.../, output
|
||||||
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 }
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -289,27 +289,23 @@ class ConfigurationTest < ActiveSupport::TestCase
|
|||||||
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 web role" do
|
test "primary role" do
|
||||||
assert_equal "web", @config.primary_web_role
|
assert_equal "web", @config.primary_role
|
||||||
|
|
||||||
config = Kamal::Configuration.new(@deploy_with_roles.deep_merge({
|
config = Kamal::Configuration.new(@deploy_with_roles.deep_merge({
|
||||||
servers: { "alternate_web" => { "hosts" => [ "1.1.1.4", "1.1.1.5" ] , "traefik" => true } },
|
servers: { "alternate_web" => { "hosts" => [ "1.1.1.4", "1.1.1.5" ] } },
|
||||||
primary_web_role: "alternate_web" } ))
|
primary_role: "alternate_web" } ))
|
||||||
|
|
||||||
assert_equal "alternate_web", config.primary_web_role
|
|
||||||
assert_equal "1.1.1.4", config.primary_web_host
|
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
|
end
|
||||||
|
|
||||||
test "primary web role no traefik" do
|
test "primary role missing" do
|
||||||
error = assert_raises(ArgumentError) do
|
error = assert_raises(ArgumentError) do
|
||||||
Kamal::Configuration.new(@deploy_with_roles.merge(primary_web_role: "workers"))
|
Kamal::Configuration.new(@deploy.merge(primary_role: "bar"))
|
||||||
end
|
|
||||||
assert_match /workers needs to have traefik enabled/, error.message
|
|
||||||
end
|
|
||||||
|
|
||||||
test "primary web role missing" do
|
|
||||||
error = assert_raises(ArgumentError) do
|
|
||||||
Kamal::Configuration.new(@deploy.merge(primary_web_role: "bar"))
|
|
||||||
end
|
end
|
||||||
assert_match /bar isn't defined/, error.message
|
assert_match /bar isn't defined/, error.message
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -17,4 +17,4 @@ registry:
|
|||||||
server: registry.digitalocean.com
|
server: registry.digitalocean.com
|
||||||
username: user
|
username: user
|
||||||
password: pw
|
password: pw
|
||||||
primary_web_role: web_tokyo
|
primary_role: web_tokyo
|
||||||
|
|||||||
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
|
||||||
Reference in New Issue
Block a user