Merge pull request #831 from basecamp/check-buildx-contexts
Check that we have valid contexts before building
This commit is contained in:
@@ -35,22 +35,25 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|||||||
|
|
||||||
run_locally do
|
run_locally do
|
||||||
begin
|
begin
|
||||||
KAMAL.with_verbosity(:debug) do
|
context_hosts = capture_with_info(*KAMAL.builder.context_hosts).split("\n")
|
||||||
Dir.chdir(KAMAL.config.builder.build_directory) { execute *push }
|
|
||||||
|
if context_hosts != KAMAL.builder.config_context_hosts
|
||||||
|
warn "Context hosts have changed, so re-creating builder, was: #{context_hosts.join(", ")}], now: #{KAMAL.builder.config_context_hosts.join(", ")}"
|
||||||
|
cli.remove
|
||||||
|
cli.create
|
||||||
end
|
end
|
||||||
rescue SSHKit::Command::Failed => e
|
rescue SSHKit::Command::Failed => e
|
||||||
if e.message =~ /(no builder)|(no such file or directory)/
|
warn "Missing compatible builder, so creating a new one first"
|
||||||
warn "Missing compatible builder, so creating a new one first"
|
if e.message =~ /(context not found|no builder)/
|
||||||
|
cli.create
|
||||||
if cli.create
|
|
||||||
KAMAL.with_verbosity(:debug) do
|
|
||||||
Dir.chdir(KAMAL.config.builder.build_directory) { execute *push }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
raise
|
raise
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
KAMAL.with_verbosity(:debug) do
|
||||||
|
Dir.chdir(KAMAL.config.builder.build_directory) { execute *push }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
require "active_support/core_ext/string/filters"
|
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, :context_hosts, :config_context_hosts, :validate_image,
|
||||||
|
to: :target
|
||||||
|
|
||||||
include Clone
|
include Clone
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
class Kamal::Commands::Builder::Base < Kamal::Commands::Base
|
class Kamal::Commands::Builder::Base < Kamal::Commands::Base
|
||||||
class BuilderError < StandardError; end
|
class BuilderError < StandardError; end
|
||||||
|
|
||||||
|
ENDPOINT_DOCKER_HOST_INSPECT = "'{{.Endpoints.docker.Host}}'"
|
||||||
|
|
||||||
delegate :argumentize, to: Kamal::Utils
|
delegate :argumentize, to: Kamal::Utils
|
||||||
delegate :args, :secrets, :dockerfile, :target, :local_arch, :local_host, :remote_arch, :remote_host, :cache_from, :cache_to, :ssh, to: :builder_config
|
delegate :args, :secrets, :dockerfile, :target, :local_arch, :local_host, :remote_arch, :remote_host, :cache_from, :cache_to, :ssh, to: :builder_config
|
||||||
|
|
||||||
@@ -30,6 +32,13 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def context_hosts
|
||||||
|
:true
|
||||||
|
end
|
||||||
|
|
||||||
|
def config_context_hosts
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def build_tags
|
def build_tags
|
||||||
@@ -74,4 +83,8 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
|
|||||||
def builder_config
|
def builder_config
|
||||||
config.builder
|
config.builder
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def context_host(builder_name)
|
||||||
|
docker :context, :inspect, builder_name, "--format", ENDPOINT_DOCKER_HOST_INSPECT
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ class Kamal::Commands::Builder::Multiarch < Kamal::Commands::Builder::Base
|
|||||||
build_context
|
build_context
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def context_hosts
|
||||||
|
docker :buildx, :inspect, builder_name, "> /dev/null"
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def builder_name
|
def builder_name
|
||||||
"kamal-#{config.service}-multiarch"
|
"kamal-#{config.service}-multiarch"
|
||||||
|
|||||||
@@ -12,6 +12,16 @@ class Kamal::Commands::Builder::Multiarch::Remote < Kamal::Commands::Builder::Mu
|
|||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def context_hosts
|
||||||
|
chain \
|
||||||
|
context_host(builder_name_with_arch(local_arch)),
|
||||||
|
context_host(builder_name_with_arch(remote_arch))
|
||||||
|
end
|
||||||
|
|
||||||
|
def config_context_hosts
|
||||||
|
[ local_host, remote_host ].compact
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def builder_name
|
def builder_name
|
||||||
super + "-remote"
|
super + "-remote"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
class Kamal::Commands::Builder::Native::Cached < Kamal::Commands::Builder::Native
|
class Kamal::Commands::Builder::Native::Cached < Kamal::Commands::Builder::Native
|
||||||
def create
|
def create
|
||||||
docker :buildx, :create, "--use", "--driver=docker-container"
|
docker :buildx, :create, "--name", builder_name, "--use", "--driver=docker-container"
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove
|
def remove
|
||||||
@@ -13,4 +13,13 @@ class Kamal::Commands::Builder::Native::Cached < Kamal::Commands::Builder::Nativ
|
|||||||
*build_options,
|
*build_options,
|
||||||
build_context
|
build_context
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def context_hosts
|
||||||
|
docker :buildx, :inspect, builder_name, "> /dev/null"
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def builder_name
|
||||||
|
"kamal-#{config.service}-native-cached"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -26,6 +26,14 @@ class Kamal::Commands::Builder::Native::Remote < Kamal::Commands::Builder::Nativ
|
|||||||
build_context
|
build_context
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def context_hosts
|
||||||
|
context_host(builder_name_with_arch)
|
||||||
|
end
|
||||||
|
|
||||||
|
def config_context_hosts
|
||||||
|
[ remote_host ]
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
def builder_name
|
def builder_name
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ class CliBuildTest < CliTestCase
|
|||||||
.with(:git, "-C", anything, :status, "--porcelain")
|
.with(:git, "-C", anything, :status, "--porcelain")
|
||||||
.returns("")
|
.returns("")
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
|
.with(:docker, :buildx, :inspect, "kamal-app-multiarch", "> /dev/null")
|
||||||
|
.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
|
||||||
@@ -121,11 +125,9 @@ class CliBuildTest < CliTestCase
|
|||||||
SSHKit::Backend::Abstract.any_instance.expects(: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.expects(:execute)
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
.with { |*args| args[0..1] == [ :docker, :buildx ] }
|
.with(:docker, :buildx, :inspect, "kamal-app-multiarch", "> /dev/null")
|
||||||
.raises(SSHKit::Command::Failed.new("no builder"))
|
.raises(SSHKit::Command::Failed.new("no builder"))
|
||||||
.then
|
|
||||||
.returns(true)
|
|
||||||
|
|
||||||
SSHKit::Backend::Abstract.any_instance.expects(:execute).with { |*args| args.first.start_with?("git") }
|
SSHKit::Backend::Abstract.any_instance.expects(:execute).with { |*args| args.first.start_with?("git") }
|
||||||
|
|
||||||
@@ -137,6 +139,9 @@ class CliBuildTest < CliTestCase
|
|||||||
.with(:git, "-C", anything, :status, "--porcelain")
|
.with(:git, "-C", anything, :status, "--porcelain")
|
||||||
.returns("")
|
.returns("")
|
||||||
|
|
||||||
|
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", ".")
|
||||||
|
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ class CliTestCase < ActiveSupport::TestCase
|
|||||||
.with { |arg1, arg2| arg1 == :mkdir && arg2 == ".kamal/locks/app" }
|
.with { |arg1, arg2| arg1 == :mkdir && arg2 == ".kamal/locks/app" }
|
||||||
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
|
||||||
.with { |arg1, arg2| arg1 == :rm && arg2 == ".kamal/locks/app/details" }
|
.with { |arg1, arg2| arg1 == :rm && arg2 == ".kamal/locks/app/details" }
|
||||||
|
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
|
||||||
|
.with { |*args| args[0..2] == [ :docker, :buildx, :inspect ] }
|
||||||
|
.returns("")
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_hook_ran(hook, output, version:, service_version:, hosts:, command:, subcommand: nil, runtime: false)
|
def assert_hook_ran(hook, output, version:, service_version:, hosts:, command:, subcommand: nil, runtime: false)
|
||||||
|
|||||||
@@ -116,6 +116,10 @@ class CliMainTest < CliTestCase
|
|||||||
.with(:git, "-C", anything, :status, "--porcelain")
|
.with(:git, "-C", anything, :status, "--porcelain")
|
||||||
.returns("")
|
.returns("")
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
|
.with(:docker, :buildx, :inspect, "kamal-app-multiarch", "> /dev/null")
|
||||||
|
.returns("")
|
||||||
|
|
||||||
assert_raises(Kamal::Cli::LockError) do
|
assert_raises(Kamal::Cli::LockError) do
|
||||||
run_command("deploy")
|
run_command("deploy")
|
||||||
end
|
end
|
||||||
@@ -145,6 +149,10 @@ class CliMainTest < CliTestCase
|
|||||||
.with(:git, "-C", anything, :status, "--porcelain")
|
.with(:git, "-C", anything, :status, "--porcelain")
|
||||||
.returns("")
|
.returns("")
|
||||||
|
|
||||||
|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||||
|
.with(:docker, :buildx, :inspect, "kamal-app-multiarch", "> /dev/null")
|
||||||
|
.returns("")
|
||||||
|
|
||||||
assert_raises(SSHKit::Runner::ExecuteError) do
|
assert_raises(SSHKit::Runner::ExecuteError) do
|
||||||
run_command("deploy")
|
run_command("deploy")
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -158,6 +158,48 @@ class CommandsBuilderTest < ActiveSupport::TestCase
|
|||||||
builder.push.join(" ")
|
builder.push.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "multiarch context hosts" do
|
||||||
|
command = new_builder_command
|
||||||
|
assert_equal "docker buildx inspect kamal-app-multiarch > /dev/null", command.context_hosts.join(" ")
|
||||||
|
assert_equal "", command.config_context_hosts.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "native context hosts" do
|
||||||
|
command = new_builder_command(builder: { "multiarch" => false })
|
||||||
|
assert_equal :true, command.context_hosts
|
||||||
|
assert_equal "", command.config_context_hosts.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "native cached context hosts" do
|
||||||
|
command = new_builder_command(builder: { "multiarch" => false, "cache" => { "type" => "registry" } })
|
||||||
|
assert_equal "docker buildx inspect kamal-app-native-cached > /dev/null", command.context_hosts.join(" ")
|
||||||
|
assert_equal "", command.config_context_hosts.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "native remote context hosts" do
|
||||||
|
command = new_builder_command(builder: { "remote" => { "arch" => "amd64", "host" => "ssh://host" } })
|
||||||
|
assert_equal "docker context inspect kamal-app-native-remote-amd64 --format '{{.Endpoints.docker.Host}}'", command.context_hosts.join(" ")
|
||||||
|
assert_equal [ "ssh://host" ], command.config_context_hosts
|
||||||
|
end
|
||||||
|
|
||||||
|
test "multiarch remote context hosts" do
|
||||||
|
command = new_builder_command(builder: {
|
||||||
|
"remote" => { "arch" => "amd64", "host" => "ssh://host" },
|
||||||
|
"local" => { "arch" => "arm64" }
|
||||||
|
})
|
||||||
|
assert_equal "docker context inspect kamal-app-multiarch-remote-arm64 --format '{{.Endpoints.docker.Host}}' ; docker context inspect kamal-app-multiarch-remote-amd64 --format '{{.Endpoints.docker.Host}}'", command.context_hosts.join(" ")
|
||||||
|
assert_equal [ "ssh://host" ], command.config_context_hosts
|
||||||
|
end
|
||||||
|
|
||||||
|
test "multiarch remote context hosts with local host" do
|
||||||
|
command = new_builder_command(builder: {
|
||||||
|
"remote" => { "arch" => "amd64", "host" => "ssh://host" },
|
||||||
|
"local" => { "arch" => "arm64", "host" => "unix:///var/run/docker.sock" }
|
||||||
|
})
|
||||||
|
assert_equal "docker context inspect kamal-app-multiarch-remote-arm64 --format '{{.Endpoints.docker.Host}}' ; docker context inspect kamal-app-multiarch-remote-amd64 --format '{{.Endpoints.docker.Host}}'", command.context_hosts.join(" ")
|
||||||
|
assert_equal [ "unix:///var/run/docker.sock", "ssh://host" ], command.config_context_hosts
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def new_builder_command(additional_config = {})
|
def new_builder_command(additional_config = {})
|
||||||
Kamal::Commands::Builder.new(Kamal::Configuration.new(@config.merge(additional_config), version: "123"))
|
Kamal::Commands::Builder.new(Kamal::Configuration.new(@config.merge(additional_config), version: "123"))
|
||||||
|
|||||||
Reference in New Issue
Block a user