Compare commits
4 Commits
parachute
...
kamal-prox
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
015c5a6f90 | ||
|
|
6568cef868 | ||
|
|
90ecb6a12a | ||
|
|
2c2053558a |
@@ -1,6 +1,6 @@
|
|||||||
# Kamal: Deploy web apps anywhere
|
# Kamal: Deploy web apps anywhere
|
||||||
|
|
||||||
From bare metal to cloud VMs, deploy web apps anywhere with zero downtime. Kamal uses parachute for zero-downtime deployments. Works seamlessly across multiple hosts, using SSHKit to execute commands. Originally built for Rails apps, Kamal will work with any type of web app that can be containerized with Docker.
|
From bare metal to cloud VMs, deploy web apps anywhere with zero downtime. Kamal uses a [custom proxy](https://github.com/basecamp/kamal-proxy) for zero-downtime deployments. Works seamlessly across multiple hosts, using SSHKit to execute commands. Originally built for Rails apps, Kamal will work with any type of web app that can be containerized with Docker.
|
||||||
|
|
||||||
➡️ See [kamal-deploy.org](https://kamal-deploy.org) for documentation on [installation](https://kamal-deploy.org/docs/installation), [configuration](https://kamal-deploy.org/docs/configuration), and [commands](https://kamal-deploy.org/docs/commands).
|
➡️ See [kamal-deploy.org](https://kamal-deploy.org) for documentation on [installation](https://kamal-deploy.org/docs/installation), [configuration](https://kamal-deploy.org/docs/configuration), and [commands](https://kamal-deploy.org/docs/commands).
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|||||||
say "Building from a local git clone, so ignoring these uncommitted changes:\n #{uncommitted_changes}", :yellow
|
say "Building from a local git clone, so ignoring these uncommitted changes:\n #{uncommitted_changes}", :yellow
|
||||||
end
|
end
|
||||||
|
|
||||||
prepare_clone
|
run_locally do
|
||||||
|
Clone.new(self).prepare
|
||||||
|
end
|
||||||
elsif uncommitted_changes.present?
|
elsif uncommitted_changes.present?
|
||||||
say "Building with uncommitted changes:\n #{uncommitted_changes}", :yellow
|
say "Building with uncommitted changes:\n #{uncommitted_changes}", :yellow
|
||||||
end
|
end
|
||||||
@@ -126,23 +128,4 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def prepare_clone
|
|
||||||
run_locally do
|
|
||||||
begin
|
|
||||||
info "Cloning repo into build directory `#{KAMAL.config.builder.build_directory}`..."
|
|
||||||
|
|
||||||
execute *KAMAL.builder.create_clone_directory
|
|
||||||
execute *KAMAL.builder.clone
|
|
||||||
rescue SSHKit::Command::Failed => e
|
|
||||||
if e.message =~ /already exists and is not an empty directory/
|
|
||||||
info "Resetting local clone as `#{KAMAL.config.builder.build_directory}` already exists..."
|
|
||||||
|
|
||||||
KAMAL.builder.clone_reset_steps.each { |step| execute *step }
|
|
||||||
else
|
|
||||||
raise
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
61
lib/kamal/cli/build/clone.rb
Normal file
61
lib/kamal/cli/build/clone.rb
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
require "uri"
|
||||||
|
|
||||||
|
class Kamal::Cli::Build::Clone
|
||||||
|
attr_reader :sshkit
|
||||||
|
delegate :info, :error, :execute, :capture_with_info, to: :sshkit
|
||||||
|
|
||||||
|
def initialize(sshkit)
|
||||||
|
@sshkit = sshkit
|
||||||
|
end
|
||||||
|
|
||||||
|
def prepare
|
||||||
|
begin
|
||||||
|
clone_repo
|
||||||
|
rescue SSHKit::Command::Failed => e
|
||||||
|
if e.message =~ /already exists and is not an empty directory/
|
||||||
|
reset
|
||||||
|
else
|
||||||
|
raise Kamal::Cli::Build::BuildError, "Failed to clone repo: #{e.message}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
validate!
|
||||||
|
rescue Kamal::Cli::Build::BuildError => e
|
||||||
|
error "Error preparing clone: #{e.message}, deleting and retrying..."
|
||||||
|
|
||||||
|
FileUtils.rm_rf KAMAL.config.builder.clone_directory
|
||||||
|
clone_repo
|
||||||
|
validate!
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def clone_repo
|
||||||
|
info "Cloning repo into build directory `#{KAMAL.config.builder.build_directory}`..."
|
||||||
|
|
||||||
|
FileUtils.mkdir_p KAMAL.config.builder.clone_directory
|
||||||
|
execute *KAMAL.builder.clone
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset
|
||||||
|
info "Resetting local clone as `#{KAMAL.config.builder.build_directory}` already exists..."
|
||||||
|
|
||||||
|
KAMAL.builder.clone_reset_steps.each { |step| execute *step }
|
||||||
|
rescue SSHKit::Command::Failed => e
|
||||||
|
raise Kamal::Cli::Build::BuildError, "Failed to clone repo: #{e.message}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate!
|
||||||
|
status = capture_with_info(*KAMAL.builder.clone_status).strip
|
||||||
|
|
||||||
|
unless status.empty?
|
||||||
|
raise Kamal::Cli::Build::BuildError, "Clone in #{KAMAL.config.builder.build_directory} is dirty, #{status}"
|
||||||
|
end
|
||||||
|
|
||||||
|
revision = capture_with_info(*KAMAL.builder.clone_revision).strip
|
||||||
|
if revision != Kamal::Git.revision
|
||||||
|
raise Kamal::Cli::Build::BuildError, "Clone in #{KAMAL.config.builder.build_directory} is not on the correct revision, expected `#{Kamal::Git.revision}` but got `#{revision}`"
|
||||||
|
end
|
||||||
|
rescue SSHKit::Command::Failed => e
|
||||||
|
raise Kamal::Cli::Build::BuildError, "Failed to validate clone: #{e.message}"
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -60,6 +60,50 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
desc "update", "Update from Traefik to kamal-proxy, for when moving from Kamal v1 to Kamal v2"
|
||||||
|
option :rolling, type: :boolean, default: false, desc: "Reboot proxy on hosts in sequence, rather than in parallel"
|
||||||
|
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
||||||
|
def update
|
||||||
|
confirming "This will cause a brief outage on each host. Are you sure?" do
|
||||||
|
with_lock do
|
||||||
|
host_groups = options[:rolling] ? KAMAL.proxy_hosts : [ KAMAL.proxy_hosts ]
|
||||||
|
host_groups.each do |hosts|
|
||||||
|
host_list = Array(hosts).join(",")
|
||||||
|
run_hook "pre-proxy-reboot", hosts: host_list
|
||||||
|
on(hosts) do
|
||||||
|
info "Updating proxy from Traefik to kamal-proxy on #{host}..."
|
||||||
|
execute *KAMAL.auditor.record("Updated proxy from Traefik to kamal-proxy"), verbosity: :debug
|
||||||
|
execute *KAMAL.registry.login
|
||||||
|
|
||||||
|
info "Stopping and removing Traefik on #{host}..."
|
||||||
|
execute *KAMAL.proxy.stop(name: "traefik"), raise_on_non_zero_exit: false
|
||||||
|
execute *KAMAL.proxy.remove_container(filter: "label=org.opencontainers.image.title=traefik")
|
||||||
|
execute *KAMAL.proxy.remove_image(filter: "label=org.opencontainers.image.title=traefik")
|
||||||
|
|
||||||
|
info "Stopping and removing kamal-proxy on #{host}, if running..."
|
||||||
|
execute *KAMAL.proxy.stop, raise_on_non_zero_exit: false
|
||||||
|
execute *KAMAL.proxy.remove_container
|
||||||
|
|
||||||
|
info "Starting kamal-proxy on #{host}..."
|
||||||
|
execute *KAMAL.proxy.run
|
||||||
|
|
||||||
|
KAMAL.roles_on(host).select(&:running_proxy?).each do |role|
|
||||||
|
app = KAMAL.app(role: role, host: host)
|
||||||
|
|
||||||
|
version = capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip
|
||||||
|
endpoint = capture_with_info(*app.container_endpoint(version: version)).strip
|
||||||
|
raise Kamal::Cli::BootError, "Failed to get endpoint for #{role} on #{host}, is the app container running?" if endpoint.empty?
|
||||||
|
|
||||||
|
info "Deploying #{endpoint} for role `#{role}` on #{host}..."
|
||||||
|
execute *KAMAL.proxy.deploy(role.container_prefix, target: endpoint)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
run_hook "post-proxy-reboot", hosts: host_list
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
desc "details", "Show details about proxy container from servers"
|
desc "details", "Show details about proxy container from servers"
|
||||||
def details
|
def details
|
||||||
on(KAMAL.proxy_hosts) { |host| puts_by_host host, capture_with_info(*KAMAL.proxy.info), type: "Proxy" }
|
on(KAMAL.proxy_hosts) { |host| puts_by_host host, capture_with_info(*KAMAL.proxy.info), type: "Proxy" }
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ require "active_support/core_ext/string/filters"
|
|||||||
|
|
||||||
class Kamal::Commands::Builder < Kamal::Commands::Base
|
class Kamal::Commands::Builder < Kamal::Commands::Base
|
||||||
delegate :create, :remove, :push, :clean, :pull, :info, :validate_image, to: :target
|
delegate :create, :remove, :push, :clean, :pull, :info, :validate_image, to: :target
|
||||||
delegate :clone_directory, :build_directory, to: :"config.builder"
|
|
||||||
|
include Clone
|
||||||
|
|
||||||
def name
|
def name
|
||||||
target.class.to_s.remove("Kamal::Commands::Builder::").underscore.inquiry
|
target.class.to_s.remove("Kamal::Commands::Builder::").underscore.inquiry
|
||||||
@@ -54,23 +55,6 @@ class Kamal::Commands::Builder < Kamal::Commands::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_clone_directory
|
|
||||||
make_directory clone_directory
|
|
||||||
end
|
|
||||||
|
|
||||||
def clone
|
|
||||||
git :clone, Kamal::Git.root, path: clone_directory
|
|
||||||
end
|
|
||||||
|
|
||||||
def clone_reset_steps
|
|
||||||
[
|
|
||||||
git(:remote, "set-url", :origin, Kamal::Git.root, path: build_directory),
|
|
||||||
git(:fetch, :origin, path: build_directory),
|
|
||||||
git(:reset, "--hard", Kamal::Git.revision, path: build_directory),
|
|
||||||
git(:clean, "-fdx", path: build_directory)
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
def ensure_local_docker_installed
|
def ensure_local_docker_installed
|
||||||
docker "--version"
|
docker "--version"
|
||||||
|
|||||||
28
lib/kamal/commands/builder/clone.rb
Normal file
28
lib/kamal/commands/builder/clone.rb
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
module Kamal::Commands::Builder::Clone
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
delegate :clone_directory, :build_directory, to: :"config.builder"
|
||||||
|
end
|
||||||
|
|
||||||
|
def clone
|
||||||
|
git :clone, Kamal::Git.root, path: clone_directory
|
||||||
|
end
|
||||||
|
|
||||||
|
def clone_reset_steps
|
||||||
|
[
|
||||||
|
git(:remote, "set-url", :origin, Kamal::Git.root, path: build_directory),
|
||||||
|
git(:fetch, :origin, path: build_directory),
|
||||||
|
git(:reset, "--hard", Kamal::Git.revision, path: build_directory),
|
||||||
|
git(:clean, "-fdx", path: build_directory)
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def clone_status
|
||||||
|
git :status, "--porcelain", path: build_directory
|
||||||
|
end
|
||||||
|
|
||||||
|
def clone_revision
|
||||||
|
git :"rev-parse", :HEAD, path: build_directory
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -16,7 +16,7 @@ class Kamal::Commands::Proxy < Kamal::Commands::Base
|
|||||||
"--restart", "unless-stopped",
|
"--restart", "unless-stopped",
|
||||||
*proxy_config.publish_args,
|
*proxy_config.publish_args,
|
||||||
"--volume", "/var/run/docker.sock:/var/run/docker.sock",
|
"--volume", "/var/run/docker.sock:/var/run/docker.sock",
|
||||||
"--volume", "#{container_name}:/root/.config/parachute",
|
"--volume", "#{container_name}:/root/.config/kamal-proxy",
|
||||||
*config.logging_args,
|
*config.logging_args,
|
||||||
*proxy_config.docker_options_args,
|
*proxy_config.docker_options_args,
|
||||||
proxy_config.image
|
proxy_config.image
|
||||||
@@ -26,8 +26,8 @@ class Kamal::Commands::Proxy < Kamal::Commands::Base
|
|||||||
docker :container, :start, container_name
|
docker :container, :start, container_name
|
||||||
end
|
end
|
||||||
|
|
||||||
def stop
|
def stop(name: container_name)
|
||||||
docker :container, :stop, container_name
|
docker :container, :stop, name
|
||||||
end
|
end
|
||||||
|
|
||||||
def start_or_run
|
def start_or_run
|
||||||
@@ -36,11 +36,11 @@ class Kamal::Commands::Proxy < Kamal::Commands::Base
|
|||||||
|
|
||||||
def deploy(service, target:)
|
def deploy(service, target:)
|
||||||
optionize({ target: target })
|
optionize({ target: target })
|
||||||
docker :exec, container_name, :parachute, :deploy, service, *optionize({ target: target }), *proxy_config.deploy_command_args
|
docker :exec, container_name, "kamal-proxy", :deploy, service, *optionize({ target: target }), *proxy_config.deploy_command_args
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove(service, target:)
|
def remove(service, target:)
|
||||||
docker :exec, container_name, :parachute, :remove, service, *optionize({ target: target })
|
docker :exec, container_name, "kamal-proxy", :remove, service, *optionize({ target: target })
|
||||||
end
|
end
|
||||||
|
|
||||||
def info
|
def info
|
||||||
@@ -60,20 +60,20 @@ class Kamal::Commands::Proxy < Kamal::Commands::Base
|
|||||||
).join(" "), host: host
|
).join(" "), host: host
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_container
|
def remove_container(filter: container_filter)
|
||||||
docker :container, :prune, "--force", "--filter", container_filter
|
docker :container, :prune, "--force", "--filter", filter
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_image
|
def remove_image(filter: image_filter)
|
||||||
docker :image, :prune, "--all", "--force", "--filter", image_filter
|
docker :image, :prune, "--all", "--force", "--filter", filter
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def container_filter
|
def container_filter
|
||||||
"label=org.opencontainers.image.title=parachute"
|
"label=org.opencontainers.image.title=kamal-proxy"
|
||||||
end
|
end
|
||||||
|
|
||||||
def image_filter
|
def image_filter
|
||||||
"label=org.opencontainers.image.title=parachute"
|
"label=org.opencontainers.image.title=kamal-proxy"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
class Kamal::Configuration::Proxy
|
class Kamal::Configuration::Proxy
|
||||||
DEFAULT_HTTP_PORT = 80
|
DEFAULT_HTTP_PORT = 80
|
||||||
DEFAULT_HTTPS_PORT = 443
|
DEFAULT_HTTPS_PORT = 443
|
||||||
DEFAULT_IMAGE = "basecamp/parachute:latest"
|
DEFAULT_IMAGE = "basecamp/kamal-proxy:latest"
|
||||||
|
|
||||||
delegate :argumentize, :optionize, to: Kamal::Utils
|
delegate :argumentize, :optionize, to: Kamal::Utils
|
||||||
|
|
||||||
@@ -18,23 +18,15 @@ class Kamal::Configuration::Proxy
|
|||||||
end
|
end
|
||||||
|
|
||||||
def http_port
|
def http_port
|
||||||
if options.key?(:http_port)
|
options.fetch(:http_port, DEFAULT_HTTP_PORT)
|
||||||
options[:http_port]
|
|
||||||
elsif !automatic_tls?
|
|
||||||
DEFAULT_HTTP_PORT
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def https_port
|
def https_port
|
||||||
if options.key?(:http_port)
|
options.fetch(:http_port, DEFAULT_HTTPS_PORT)
|
||||||
options[:http_port]
|
|
||||||
elsif automatic_tls?
|
|
||||||
DEFAULT_HTTPS_PORT
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def container_name
|
def container_name
|
||||||
"parachute_#{http_port}_#{https_port}"
|
"kamal-proxy"
|
||||||
end
|
end
|
||||||
|
|
||||||
def docker_options_args
|
def docker_options_args
|
||||||
@@ -42,7 +34,7 @@ class Kamal::Configuration::Proxy
|
|||||||
end
|
end
|
||||||
|
|
||||||
def publish_args
|
def publish_args
|
||||||
argumentize "--publish", *("#{http_port}:80" if http_port), *("#{https_port}:80" if https_port)
|
argumentize "--publish", [ *("#{http_port}:#{DEFAULT_HTTP_PORT}" if http_port), *("#{https_port}:#{DEFAULT_HTTPS_PORT}" if https_port) ]
|
||||||
end
|
end
|
||||||
|
|
||||||
def deploy_options
|
def deploy_options
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ class CliAppTest < CliTestCase
|
|||||||
|
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute).returns("")
|
SSHKit::Backend::Abstract.any_instance.stubs(:execute).returns("")
|
||||||
|
|
||||||
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:docker, :exec, "parachute_80_", :parachute, :deploy, "app-web", "--target", "\"172.1.0.2:80\"").raises(SSHKit::Command::Failed, "Deploy failed").at_least_once
|
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:docker, :exec, "kamal-proxy", "kamal-proxy", :deploy, "app-web", "--target", "\"172.1.0.2:80\"").raises(SSHKit::Command::Failed, "Deploy failed").at_least_once
|
||||||
|
|
||||||
stderred do
|
stderred do
|
||||||
run_command("boot", config: :with_roles, host: nil, allowed_error_message: "Deploy failed").tap do |output|
|
run_command("boot", config: :with_roles, host: nil, allowed_error_message: "Deploy failed").tap do |output|
|
||||||
|
|||||||
@@ -9,10 +9,18 @@ class CliBuildTest < CliTestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "push" do
|
test "push" do
|
||||||
with_build_directory do
|
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)
|
||||||
hook_variables = { version: 999, service_version: "app@999", hosts: "1.1.1.1,1.1.1.2,1.1.1.3,1.1.1.4", command: "build", subcommand: "push" }
|
hook_variables = { version: 999, service_version: "app@999", hosts: "1.1.1.1,1.1.1.2,1.1.1.3,1.1.1.4", command: "build", subcommand: "push" }
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
|
.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").tap do |output|
|
run_command("push", "--verbose").tap do |output|
|
||||||
assert_hook_ran "pre-build", output, **hook_variables
|
assert_hook_ran "pre-build", output, **hook_variables
|
||||||
assert_match /Cloning repo into build directory/, output
|
assert_match /Cloning repo into build directory/, output
|
||||||
@@ -23,28 +31,33 @@ class CliBuildTest < CliTestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "push reseting clone" do
|
test "push resetting clone" do
|
||||||
with_build_directory do
|
with_build_directory do |build_directory|
|
||||||
stub_setup
|
stub_setup
|
||||||
build_dir = "#{Dir.tmpdir}/kamal-clones/app-#{pwd_sha}/kamal/"
|
|
||||||
|
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute).with(:docker, "--version", "&&", :docker, :buildx, "version")
|
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:docker, "--version", "&&", :docker, :buildx, "version")
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute).with(:docker, :buildx, :create, "--use", "--name", "kamal-app-multiarch")
|
|
||||||
|
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute).with(:mkdir, "-p", "#{Dir.tmpdir}/kamal-clones/app-#{pwd_sha}")
|
SSHKit::Backend::Abstract.any_instance.expects(:execute)
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
|
||||||
.with(:git, "-C", "#{Dir.tmpdir}/kamal-clones/app-#{pwd_sha}", :clone, Dir.pwd)
|
.with(:git, "-C", "#{Dir.tmpdir}/kamal-clones/app-#{pwd_sha}", :clone, Dir.pwd)
|
||||||
.raises(SSHKit::Command::Failed.new("fatal: destination path 'kamal' already exists and is not an empty directory"))
|
.raises(SSHKit::Command::Failed.new("fatal: destination path 'kamal' already exists and is not an empty directory"))
|
||||||
.then
|
.then
|
||||||
.returns(true)
|
.returns(true)
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute).with(:git, "-C", build_dir, :remote, "set-url", :origin, Dir.pwd)
|
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:git, "-C", build_directory, :remote, "set-url", :origin, Dir.pwd)
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute).with(:git, "-C", build_dir, :fetch, :origin)
|
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:git, "-C", build_directory, :fetch, :origin)
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute).with(:git, "-C", build_dir, :reset, "--hard", Kamal::Git.revision)
|
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:git, "-C", build_directory, :reset, "--hard", Kamal::Git.revision)
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute).with(:git, "-C", build_dir, :clean, "-fdx")
|
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:git, "-C", build_directory, :clean, "-fdx")
|
||||||
|
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
SSHKit::Backend::Abstract.any_instance.expects(:execute)
|
||||||
.with(:docker, :buildx, :build, "--push", "--platform", "linux/amd64,linux/arm64", "--builder", "kamal-app-multiarch", "-t", "dhh/app:999", "-t", "dhh/app:latest", "--label", "service=\"app\"", "--file", "Dockerfile", ".")
|
.with(:docker, :buildx, :build, "--push", "--platform", "linux/amd64,linux/arm64", "--builder", "kamal-app-multiarch", "-t", "dhh/app:999", "-t", "dhh/app:latest", "--label", "service=\"app\"", "--file", "Dockerfile", ".")
|
||||||
|
|
||||||
|
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").tap do |output|
|
run_command("push", "--verbose").tap do |output|
|
||||||
assert_match /Cloning repo into build directory/, output
|
assert_match /Cloning repo into build directory/, output
|
||||||
assert_match /Resetting local clone/, output
|
assert_match /Resetting local clone/, output
|
||||||
@@ -64,25 +77,65 @@ class CliBuildTest < CliTestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "push without builder" do
|
test "push with corrupt clone" do
|
||||||
with_build_directory do
|
with_build_directory do |build_directory|
|
||||||
stub_setup
|
stub_setup
|
||||||
|
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:docker, "--version", "&&", :docker, :buildx, "version")
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:execute)
|
||||||
|
.with(:git, "-C", "#{Dir.tmpdir}/kamal-clones/app-#{pwd_sha}", :clone, Dir.pwd)
|
||||||
|
.raises(SSHKit::Command::Failed.new("fatal: destination path 'kamal' already exists and is not an empty directory"))
|
||||||
|
.then
|
||||||
|
.returns(true)
|
||||||
|
.twice
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:git, "-C", build_directory, :remote, "set-url", :origin, Dir.pwd)
|
||||||
|
.raises(SSHKit::Command::Failed.new("fatal: not a git repository"))
|
||||||
|
|
||||||
|
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("")
|
||||||
|
|
||||||
|
Dir.stubs(:chdir)
|
||||||
|
|
||||||
|
run_command("push", "--verbose") do |output|
|
||||||
|
assert_match /Cloning repo into build directory `#{build_directory}`\.\.\..*Cloning repo into build directory `#{build_directory}`\.\.\./, output
|
||||||
|
assert_match "Resetting local clone as `#{build_directory}` already exists...", output
|
||||||
|
assert_match "Error preparing clone: Failed to clone repo: fatal: not a git repository, deleting and retrying...", output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "push without builder" do
|
||||||
|
with_build_directory do |build_directory|
|
||||||
|
stub_setup
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:execute)
|
||||||
.with(:docker, "--version", "&&", :docker, :buildx, "version")
|
.with(:docker, "--version", "&&", :docker, :buildx, "version")
|
||||||
|
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
SSHKit::Backend::Abstract.any_instance.expects(:execute)
|
||||||
.with(:docker, :buildx, :create, "--use", "--name", "kamal-app-multiarch")
|
.with(:docker, :buildx, :create, "--use", "--name", "kamal-app-multiarch")
|
||||||
|
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
SSHKit::Backend::Abstract.any_instance.expects(:execute)
|
||||||
.with { |*args| args[0..1] == [ :docker, :buildx ] }
|
.with { |*args| args[0..1] == [ :docker, :buildx ] }
|
||||||
.raises(SSHKit::Command::Failed.new("no builder"))
|
.raises(SSHKit::Command::Failed.new("no builder"))
|
||||||
.then
|
.then
|
||||||
.returns(true)
|
.returns(true)
|
||||||
|
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute).with { |*args| args.first.start_with?("git") }
|
SSHKit::Backend::Abstract.any_instance.expects(:execute).with { |*args| args.first.start_with?("git") }
|
||||||
|
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute).with(:mkdir, "-p", "#{Dir.tmpdir}/kamal-clones/app-#{pwd_sha}")
|
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").tap do |output|
|
run_command("push").tap do |output|
|
||||||
assert_match /WARN Missing compatible builder, so creating a new one first/, output
|
assert_match /WARN Missing compatible builder, so creating a new one first/, output
|
||||||
@@ -183,7 +236,7 @@ class CliBuildTest < CliTestCase
|
|||||||
build_directory = File.join Dir.tmpdir, "kamal-clones", "app-#{pwd_sha}", "kamal"
|
build_directory = File.join Dir.tmpdir, "kamal-clones", "app-#{pwd_sha}", "kamal"
|
||||||
FileUtils.mkdir_p build_directory
|
FileUtils.mkdir_p build_directory
|
||||||
FileUtils.touch File.join build_directory, "Dockerfile"
|
FileUtils.touch File.join build_directory, "Dockerfile"
|
||||||
yield
|
yield build_directory + "/"
|
||||||
ensure
|
ensure
|
||||||
FileUtils.rm_rf build_directory
|
FileUtils.rm_rf build_directory
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -108,6 +108,14 @@ class CliMainTest < CliTestCase
|
|||||||
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_debug)
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_debug)
|
||||||
.with(:stat, ".kamal/locks/app", ">", "/dev/null", "&&", :cat, ".kamal/locks/app/details", "|", :base64, "-d")
|
.with(:stat, ".kamal/locks/app", ">", "/dev/null", "&&", :cat, ".kamal/locks/app/details", "|", :base64, "-d")
|
||||||
|
|
||||||
|
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("")
|
||||||
|
|
||||||
assert_raises(Kamal::Cli::LockError) do
|
assert_raises(Kamal::Cli::LockError) do
|
||||||
run_command("deploy")
|
run_command("deploy")
|
||||||
end
|
end
|
||||||
@@ -129,6 +137,14 @@ class CliMainTest < CliTestCase
|
|||||||
.with { |*arg| arg[0..1] == [ :mkdir, ".kamal/locks/app" ] }
|
.with { |*arg| arg[0..1] == [ :mkdir, ".kamal/locks/app" ] }
|
||||||
.raises(SocketError, "getaddrinfo: nodename nor servname provided, or not known")
|
.raises(SocketError, "getaddrinfo: nodename nor servname provided, or not known")
|
||||||
|
|
||||||
|
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("")
|
||||||
|
|
||||||
assert_raises(SSHKit::Runner::ExecuteError) do
|
assert_raises(SSHKit::Runner::ExecuteError) do
|
||||||
run_command("deploy")
|
run_command("deploy")
|
||||||
end
|
end
|
||||||
@@ -437,9 +453,9 @@ class CliMainTest < CliTestCase
|
|||||||
|
|
||||||
test "remove with confirmation" do
|
test "remove with confirmation" do
|
||||||
run_command("remove", "-y", config_file: "deploy_with_accessories").tap do |output|
|
run_command("remove", "-y", config_file: "deploy_with_accessories").tap do |output|
|
||||||
assert_match /docker container stop parachute/, output
|
assert_match /docker container stop kamal-proxy/, output
|
||||||
assert_match /docker container prune --force --filter label=org.opencontainers.image.title=parachute/, output
|
assert_match /docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy/, output
|
||||||
assert_match /docker image prune --all --force --filter label=org.opencontainers.image.title=parachute/, output
|
assert_match /docker image prune --all --force --filter label=org.opencontainers.image.title=kamal-proxy/, output
|
||||||
|
|
||||||
assert_match /docker ps --quiet --filter label=service=app | xargs docker stop/, output
|
assert_match /docker ps --quiet --filter label=service=app | xargs docker stop/, output
|
||||||
assert_match /docker container prune --force --filter label=service=app/, output
|
assert_match /docker container prune --force --filter label=service=app/, output
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ class CliProxyTest < CliTestCase
|
|||||||
test "boot" do
|
test "boot" do
|
||||||
run_command("boot").tap do |output|
|
run_command("boot").tap do |output|
|
||||||
assert_match "docker login", output
|
assert_match "docker login", output
|
||||||
assert_match "docker run --name parachute_80_ --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --volume parachute_80_:/root/.config/parachute --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}", output
|
assert_match "docker run --name kamal-proxy --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume kamal-proxy:/root/.config/kamal-proxy --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -12,27 +12,27 @@ class CliProxyTest < CliTestCase
|
|||||||
Kamal::Commands::Registry.any_instance.expects(:login).twice
|
Kamal::Commands::Registry.any_instance.expects(:login).twice
|
||||||
|
|
||||||
run_command("reboot", "-y").tap do |output|
|
run_command("reboot", "-y").tap do |output|
|
||||||
assert_match "docker container stop parachute", output
|
assert_match "docker container stop kamal-proxy", output
|
||||||
assert_match "docker container prune --force --filter label=org.opencontainers.image.title=parachute", output
|
assert_match "docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy", output
|
||||||
assert_match "docker run --name parachute_80_ --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --volume parachute_80_:/root/.config/parachute --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}", output
|
assert_match "docker run --name kamal-proxy --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume kamal-proxy:/root/.config/kamal-proxy --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "reboot --rolling" do
|
test "reboot --rolling" do
|
||||||
run_command("reboot", "--rolling", "-y").tap do |output|
|
run_command("reboot", "--rolling", "-y").tap do |output|
|
||||||
assert_match "Running docker container prune --force --filter label=org.opencontainers.image.title=parachute on 1.1.1.1", output
|
assert_match "Running docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy on 1.1.1.1", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "start" do
|
test "start" do
|
||||||
run_command("start").tap do |output|
|
run_command("start").tap do |output|
|
||||||
assert_match "docker container start parachute", output
|
assert_match "docker container start kamal-proxy", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "stop" do
|
test "stop" do
|
||||||
run_command("stop").tap do |output|
|
run_command("stop").tap do |output|
|
||||||
assert_match "docker container stop parachute", output
|
assert_match "docker container stop kamal-proxy", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -45,13 +45,13 @@ class CliProxyTest < CliTestCase
|
|||||||
|
|
||||||
test "details" do
|
test "details" do
|
||||||
run_command("details").tap do |output|
|
run_command("details").tap do |output|
|
||||||
assert_match "docker ps --filter name=^parachute_80_$", output
|
assert_match "docker ps --filter name=^kamal-proxy$", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "logs" do
|
test "logs" do
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:capture)
|
SSHKit::Backend::Abstract.any_instance.stubs(:capture)
|
||||||
.with(:docker, :logs, "parachute_80_", " --tail 100", "--timestamps", "2>&1")
|
.with(:docker, :logs, "kamal-proxy", " --tail 100", "--timestamps", "2>&1")
|
||||||
.returns("Log entry")
|
.returns("Log entry")
|
||||||
|
|
||||||
run_command("logs").tap do |output|
|
run_command("logs").tap do |output|
|
||||||
@@ -62,9 +62,9 @@ class CliProxyTest < 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 -p 22 'docker logs parachute_80_ --timestamps --tail 10 --follow 2>&1'")
|
.with("ssh -t root@1.1.1.1 -p 22 'docker logs kamal-proxy --timestamps --tail 10 --follow 2>&1'")
|
||||||
|
|
||||||
assert_match "docker logs parachute_80_ --timestamps --tail 10 --follow", run_command("logs", "--follow")
|
assert_match "docker logs kamal-proxy --timestamps --tail 10 --follow", run_command("logs", "--follow")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "remove" do
|
test "remove" do
|
||||||
@@ -77,13 +77,35 @@ class CliProxyTest < CliTestCase
|
|||||||
|
|
||||||
test "remove_container" do
|
test "remove_container" do
|
||||||
run_command("remove_container").tap do |output|
|
run_command("remove_container").tap do |output|
|
||||||
assert_match "docker container prune --force --filter label=org.opencontainers.image.title=parachute", output
|
assert_match "docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "remove_image" do
|
test "remove_image" do
|
||||||
run_command("remove_image").tap do |output|
|
run_command("remove_image").tap do |output|
|
||||||
assert_match "docker image prune --all --force --filter label=org.opencontainers.image.title=parachute", output
|
assert_match "docker image prune --all --force --filter label=org.opencontainers.image.title=kamal-proxy", output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "update" do
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
|
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-123$", "--quiet", "|", :xargs, :docker, :inspect, "--format", "'{{.NetworkSettings.IPAddress}}{{range $k, $v := .NetworkSettings.Ports}}{{printf \":%s\" $k}}{{break}}{{end}}'", "|", :sed, "-e", "'s/\\/tcp$//'")
|
||||||
|
.returns("172.1.0.2:80")
|
||||||
|
.at_least_once
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
|
.with { |*args| args[0..1] == [ :sh, "-c" ] }
|
||||||
|
.returns("123")
|
||||||
|
.at_least_once
|
||||||
|
|
||||||
|
run_command("update", "-y").tap do |output|
|
||||||
|
assert_match "docker container stop traefik", output
|
||||||
|
assert_match "docker container prune --force --filter label=org.opencontainers.image.title=traefik", output
|
||||||
|
assert_match "docker image prune --all --force --filter label=org.opencontainers.image.title=traefik", output
|
||||||
|
assert_match "docker container stop kamal-proxy", output
|
||||||
|
assert_match "docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy", output
|
||||||
|
assert_match "docker run --name kamal-proxy --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume kamal-proxy:/root/.config/kamal-proxy --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}", output
|
||||||
|
assert_match "docker exec kamal-proxy kamal-proxy deploy app-web --target \"172.1.0.2:80\"", output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ class CommandsProxyTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
test "run" do
|
test "run" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name parachute_80_ --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --volume parachute_80_:/root/.config/parachute --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}",
|
"docker run --name kamal-proxy --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume kamal-proxy:/root/.config/kamal-proxy --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "run with ports configured" do
|
test "run with ports configured" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name parachute_80_ --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --volume parachute_80_:/root/.config/parachute --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}",
|
"docker run --name kamal-proxy --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume kamal-proxy:/root/.config/kamal-proxy --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ class CommandsProxyTest < ActiveSupport::TestCase
|
|||||||
@config.delete(:proxy)
|
@config.delete(:proxy)
|
||||||
|
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name parachute_80_ --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --volume parachute_80_:/root/.config/parachute --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}",
|
"docker run --name kamal-proxy --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume kamal-proxy:/root/.config/kamal-proxy --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -37,85 +37,85 @@ class CommandsProxyTest < ActiveSupport::TestCase
|
|||||||
@config[:logging] = { "driver" => "local", "options" => { "max-size" => "100m", "max-file" => "3" } }
|
@config[:logging] = { "driver" => "local", "options" => { "max-size" => "100m", "max-file" => "3" } }
|
||||||
|
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker run --name parachute_80_ --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --volume parachute_80_:/root/.config/parachute --log-driver \"local\" --log-opt max-size=\"100m\" --log-opt max-file=\"3\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}",
|
"docker run --name kamal-proxy --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume kamal-proxy:/root/.config/kamal-proxy --log-driver \"local\" --log-opt max-size=\"100m\" --log-opt max-file=\"3\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}",
|
||||||
new_command.run.join(" ")
|
new_command.run.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "proxy start" do
|
test "proxy start" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker container start parachute_80_",
|
"docker container start kamal-proxy",
|
||||||
new_command.start.join(" ")
|
new_command.start.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "proxy stop" do
|
test "proxy stop" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker container stop parachute_80_",
|
"docker container stop kamal-proxy",
|
||||||
new_command.stop.join(" ")
|
new_command.stop.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "proxy info" do
|
test "proxy info" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker ps --filter name=^parachute_80_$",
|
"docker ps --filter name=^kamal-proxy$",
|
||||||
new_command.info.join(" ")
|
new_command.info.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "proxy logs" do
|
test "proxy logs" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker logs parachute_80_ --timestamps 2>&1",
|
"docker logs kamal-proxy --timestamps 2>&1",
|
||||||
new_command.logs.join(" ")
|
new_command.logs.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "proxy logs since 2h" do
|
test "proxy logs since 2h" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker logs parachute_80_ --since 2h --timestamps 2>&1",
|
"docker logs kamal-proxy --since 2h --timestamps 2>&1",
|
||||||
new_command.logs(since: "2h").join(" ")
|
new_command.logs(since: "2h").join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "proxy logs last 10 lines" do
|
test "proxy logs last 10 lines" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker logs parachute_80_ --tail 10 --timestamps 2>&1",
|
"docker logs kamal-proxy --tail 10 --timestamps 2>&1",
|
||||||
new_command.logs(lines: 10).join(" ")
|
new_command.logs(lines: 10).join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "proxy logs with grep hello!" do
|
test "proxy logs with grep hello!" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker logs parachute_80_ --timestamps 2>&1 | grep 'hello!'",
|
"docker logs kamal-proxy --timestamps 2>&1 | grep 'hello!'",
|
||||||
new_command.logs(grep: "hello!").join(" ")
|
new_command.logs(grep: "hello!").join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "proxy remove container" do
|
test "proxy remove container" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker container prune --force --filter label=org.opencontainers.image.title=parachute",
|
"docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy",
|
||||||
new_command.remove_container.join(" ")
|
new_command.remove_container.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "proxy remove image" do
|
test "proxy remove image" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker image prune --all --force --filter label=org.opencontainers.image.title=parachute",
|
"docker image prune --all --force --filter label=org.opencontainers.image.title=kamal-proxy",
|
||||||
new_command.remove_image.join(" ")
|
new_command.remove_image.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "proxy follow logs" do
|
test "proxy follow logs" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"ssh -t root@1.1.1.1 -p 22 'docker logs parachute_80_ --timestamps --tail 10 --follow 2>&1'",
|
"ssh -t root@1.1.1.1 -p 22 'docker logs kamal-proxy --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 "proxy follow logs with grep hello!" do
|
test "proxy follow logs with grep hello!" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"ssh -t root@1.1.1.1 -p 22 'docker logs parachute_80_ --timestamps --tail 10 --follow 2>&1 | grep \"hello!\"'",
|
"ssh -t root@1.1.1.1 -p 22 'docker logs kamal-proxy --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
|
||||||
|
|
||||||
test "deploy" do
|
test "deploy" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker exec parachute_80_ parachute deploy service --target \"172.1.0.2:80\"",
|
"docker exec kamal-proxy kamal-proxy deploy service --target \"172.1.0.2:80\"",
|
||||||
new_command.deploy("service", target: "172.1.0.2:80").join(" ")
|
new_command.deploy("service", target: "172.1.0.2:80").join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "remove" do
|
test "remove" do
|
||||||
assert_equal \
|
assert_equal \
|
||||||
"docker exec parachute_80_ parachute remove service --target \"172.1.0.2:80\"",
|
"docker exec kamal-proxy kamal-proxy remove service --target \"172.1.0.2:80\"",
|
||||||
new_command.remove("service", target: "172.1.0.2:80").join(" ")
|
new_command.remove("service", target: "172.1.0.2:80").join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ builder:
|
|||||||
args:
|
args:
|
||||||
COMMIT_SHA: <%= `git rev-parse HEAD` %>
|
COMMIT_SHA: <%= `git rev-parse HEAD` %>
|
||||||
proxy:
|
proxy:
|
||||||
image: registry:4443/basecamp/parachute:latest
|
image: registry:4443/basecamp/kamal-proxy:latest
|
||||||
http_port: 80
|
http_port: 80
|
||||||
https_port: 443
|
https_port: 443
|
||||||
debug: true
|
debug: true
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ builder:
|
|||||||
args:
|
args:
|
||||||
COMMIT_SHA: <%= `git rev-parse HEAD` %>
|
COMMIT_SHA: <%= `git rev-parse HEAD` %>
|
||||||
proxy:
|
proxy:
|
||||||
image: registry:4443/basecamp/parachute:latest
|
image: registry:4443/basecamp/kamal-proxy:latest
|
||||||
accessories:
|
accessories:
|
||||||
busybox:
|
busybox:
|
||||||
service: custom-busybox
|
service: custom-busybox
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ push_image_to_registry_4443() {
|
|||||||
|
|
||||||
install_kamal
|
install_kamal
|
||||||
push_image_to_registry_4443 nginx 1-alpine-slim
|
push_image_to_registry_4443 nginx 1-alpine-slim
|
||||||
push_image_to_registry_4443 basecamp/parachute latest
|
push_image_to_registry_4443 basecamp/kamal-proxy latest
|
||||||
push_image_to_registry_4443 busybox 1.36.0
|
push_image_to_registry_4443 busybox 1.36.0
|
||||||
|
|
||||||
# .ssh is on a shared volume that persists between runs. Clean it up as the
|
# .ssh is on a shared volume that persists between runs. Clean it up as the
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class IntegrationMainTest < IntegrationTest
|
|||||||
assert_match /Proxy Host: vm2/, details
|
assert_match /Proxy Host: vm2/, details
|
||||||
assert_match /App Host: vm1/, details
|
assert_match /App Host: vm1/, details
|
||||||
assert_match /App Host: vm2/, details
|
assert_match /App Host: vm2/, details
|
||||||
assert_match /basecamp\/parachute:latest/, details
|
assert_match /basecamp\/kamal-proxy:latest/, details
|
||||||
assert_match /registry:4443\/app:#{first_version}/, details
|
assert_match /registry:4443\/app:#{first_version}/, details
|
||||||
|
|
||||||
audit = kamal :audit, capture: true
|
audit = kamal :audit, capture: true
|
||||||
|
|||||||
@@ -53,11 +53,11 @@ class IntegrationProxyTest < IntegrationTest
|
|||||||
|
|
||||||
private
|
private
|
||||||
def assert_proxy_running
|
def assert_proxy_running
|
||||||
assert_match %r{registry:4443/basecamp/parachute:latest "parachute run"}, proxy_details
|
assert_match %r{registry:4443/basecamp/kamal-proxy:latest "kamal-proxy run"}, proxy_details
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_proxy_not_running
|
def assert_proxy_not_running
|
||||||
assert_no_match %r{registry:4443/basecamp/parachute:latest "parachute run"}, proxy_details
|
assert_no_match %r{registry:4443/basecamp/kamal-proxy:latest "kamal-proxy run"}, proxy_details
|
||||||
end
|
end
|
||||||
|
|
||||||
def proxy_details
|
def proxy_details
|
||||||
|
|||||||
Reference in New Issue
Block a user