Combine multiarch and native/cache builders

Combine the two builders, as they are almost identical. The only
difference was whether the platforms were set.

The native cached builder wasn't using the context it created, so now
we do.

We'll set the driver to `docker-container` - it seems to be the default
but the Docker docs claim it is `docker`.
This commit is contained in:
Donal McBreen
2024-06-06 09:43:58 +01:00
parent f48987aa03
commit 2a4a8ac859
8 changed files with 76 additions and 82 deletions

View File

@@ -11,22 +11,17 @@ class Kamal::Commands::Builder < Kamal::Commands::Base
end end
def target def target
if config.builder.multiarch? case
if config.builder.remote? when !config.builder.multiarch? && !config.builder.cached?
if config.builder.local? native
multiarch_remote when !config.builder.multiarch? && config.builder.cached?
else local
native_remote when config.builder.local? && config.builder.remote?
end multiarch_remote
else when config.builder.remote?
multiarch native_remote
end
else else
if config.builder.cached? local
native_cached
else
native
end
end end
end end
@@ -34,16 +29,12 @@ class Kamal::Commands::Builder < Kamal::Commands::Base
@native ||= Kamal::Commands::Builder::Native.new(config) @native ||= Kamal::Commands::Builder::Native.new(config)
end end
def native_cached
@native ||= Kamal::Commands::Builder::Native::Cached.new(config)
end
def native_remote def native_remote
@native ||= Kamal::Commands::Builder::Native::Remote.new(config) @native ||= Kamal::Commands::Builder::Native::Remote.new(config)
end end
def multiarch def local
@multiarch ||= Kamal::Commands::Builder::Multiarch.new(config) @local ||= Kamal::Commands::Builder::Local.new(config)
end end
def multiarch_remote def multiarch_remote

View File

@@ -5,7 +5,10 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
ENDPOINT_DOCKER_HOST_INSPECT = "'{{.Endpoints.docker.Host}}'" 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, :multiarch?, :ssh,
to: :builder_config
def clean def clean
docker :image, :rm, "--force", config.absolute_image docker :image, :rm, "--force", config.absolute_image

View File

@@ -1,6 +1,6 @@
class Kamal::Commands::Builder::Multiarch < Kamal::Commands::Builder::Base class Kamal::Commands::Builder::Local < Kamal::Commands::Builder::Base
def create def create
docker :buildx, :create, "--use", "--name", builder_name docker :buildx, :create, "--name", builder_name, "--driver=docker-container"
end end
def remove def remove
@@ -16,7 +16,7 @@ class Kamal::Commands::Builder::Multiarch < Kamal::Commands::Builder::Base
def push def push
docker :buildx, :build, docker :buildx, :build,
"--push", "--push",
"--platform", platform_names, *platform_options,
"--builder", builder_name, "--builder", builder_name,
*build_options, *build_options,
build_context build_context
@@ -28,14 +28,16 @@ class Kamal::Commands::Builder::Multiarch < Kamal::Commands::Builder::Base
private private
def builder_name def builder_name
"kamal-#{config.service}-multiarch" "kamal-local"
end end
def platform_names def platform_options
if local_arch if multiarch?
"linux/#{local_arch}" if local_arch
else [ "--platform", "linux/#{local_arch}" ]
"linux/amd64,linux/arm64" else
[ "--platform", "linux/amd64,linux/arm64" ]
end
end end
end end
end end

View File

@@ -1,4 +1,4 @@
class Kamal::Commands::Builder::Multiarch::Remote < Kamal::Commands::Builder::Multiarch class Kamal::Commands::Builder::Multiarch::Remote < Kamal::Commands::Builder::Base
def create def create
combine \ combine \
create_contexts, create_contexts,
@@ -12,6 +12,21 @@ class Kamal::Commands::Builder::Multiarch::Remote < Kamal::Commands::Builder::Mu
super super
end end
def info
combine \
docker(:context, :ls),
docker(:buildx, :ls)
end
def push
docker :buildx, :build,
"--push",
*platform_options,
"--builder", builder_name,
*build_options,
build_context
end
def context_hosts def context_hosts
chain \ chain \
context_host(builder_name_with_arch(local_arch)), context_host(builder_name_with_arch(local_arch)),
@@ -24,7 +39,7 @@ class Kamal::Commands::Builder::Multiarch::Remote < Kamal::Commands::Builder::Mu
private private
def builder_name def builder_name
super + "-remote" "kamal-#{config.service}-multiarch-remote"
end end
def builder_name_with_arch(arch) def builder_name_with_arch(arch)
@@ -58,4 +73,12 @@ class Kamal::Commands::Builder::Multiarch::Remote < Kamal::Commands::Builder::Mu
def remove_context(arch) def remove_context(arch)
docker :context, :rm, builder_name_with_arch(arch) docker :context, :rm, builder_name_with_arch(arch)
end end
def platform_options
if local_arch
[ "--platform", "linux/#{local_arch}" ]
else
[ "--platform", "linux/amd64,linux/arm64" ]
end
end
end end

View File

@@ -1,25 +0,0 @@
class Kamal::Commands::Builder::Native::Cached < Kamal::Commands::Builder::Native
def create
docker :buildx, :create, "--name", builder_name, "--use", "--driver=docker-container"
end
def remove
docker :buildx, :rm, builder_name
end
def push
docker :buildx, :build,
"--push",
*build_options,
build_context
end
def context_hosts
docker :buildx, :inspect, builder_name, "> /dev/null"
end
private
def builder_name
"kamal-#{config.service}-native-cached"
end
end

View File

@@ -22,7 +22,7 @@ class CliBuildTest < CliTestCase
.returns("") .returns("")
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :buildx, :inspect, "kamal-app-multiarch", "> /dev/null") .with(:docker, :buildx, :inspect, "kamal-local", "> /dev/null")
.returns("") .returns("")
run_command("push", "--verbose").tap do |output| run_command("push", "--verbose").tap do |output|
@@ -30,7 +30,7 @@ class CliBuildTest < CliTestCase
assert_match /Cloning repo into build directory/, output assert_match /Cloning repo into build directory/, output
assert_match /git -C #{Dir.tmpdir}\/kamal-clones\/app-#{pwd_sha} clone #{Dir.pwd}/, output assert_match /git -C #{Dir.tmpdir}\/kamal-clones\/app-#{pwd_sha} clone #{Dir.pwd}/, output
assert_match /docker --version && docker buildx version/, output assert_match /docker --version && docker buildx version/, output
assert_match /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 \. as .*@localhost/, output assert_match /docker buildx build --push --platform linux\/amd64,linux\/arm64 --builder kamal-local -t dhh\/app:999 -t dhh\/app:latest --label service="app" --file Dockerfile \. as .*@localhost/, output
end end
end end
end end
@@ -52,7 +52,7 @@ class CliBuildTest < CliTestCase
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:git, "-C", build_directory, :clean, "-fdx") SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:git, "-C", build_directory, :clean, "-fdx")
SSHKit::Backend::Abstract.any_instance.expects(: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-local", "-t", "dhh/app:999", "-t", "dhh/app:latest", "--label", "service=\"app\"", "--file", "Dockerfile", ".")
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:git, "-C", anything, :"rev-parse", :HEAD) .with(:git, "-C", anything, :"rev-parse", :HEAD)
@@ -77,7 +77,7 @@ class CliBuildTest < CliTestCase
assert_no_match /Cloning repo into build directory/, output assert_no_match /Cloning repo into build directory/, output
assert_hook_ran "pre-build", output, **hook_variables assert_hook_ran "pre-build", output, **hook_variables
assert_match /docker --version && docker buildx version/, output assert_match /docker --version && docker buildx version/, output
assert_match /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 . as .*@localhost/, output assert_match /docker buildx build --push --platform linux\/amd64,linux\/arm64 --builder kamal-local -t dhh\/app:999 -t dhh\/app:latest --label service="app" --file Dockerfile . as .*@localhost/, output
end end
end end
@@ -123,10 +123,10 @@ class CliBuildTest < CliTestCase
.with(:docker, "--version", "&&", :docker, :buildx, "version") .with(:docker, "--version", "&&", :docker, :buildx, "version")
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, "--name", "kamal-local", "--driver=docker-container")
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :buildx, :inspect, "kamal-app-multiarch", "> /dev/null") .with(:docker, :buildx, :inspect, "kamal-local", "> /dev/null")
.raises(SSHKit::Command::Failed.new("no builder")) .raises(SSHKit::Command::Failed.new("no builder"))
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") }
@@ -140,7 +140,7 @@ class CliBuildTest < CliTestCase
.returns("") .returns("")
SSHKit::Backend::Abstract.any_instance.expects(: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-local", "-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
@@ -206,7 +206,7 @@ class CliBuildTest < CliTestCase
test "create" do test "create" do
run_command("create").tap do |output| run_command("create").tap do |output|
assert_match /docker buildx create --use --name kamal-app-multiarch/, output assert_match /docker buildx create --name kamal-local --driver=docker-container/, output
end end
end end
@@ -239,7 +239,7 @@ class CliBuildTest < CliTestCase
test "remove" do test "remove" do
run_command("remove").tap do |output| run_command("remove").tap do |output|
assert_match /docker buildx rm kamal-app-multiarch/, output assert_match /docker buildx rm kamal-local/, output
end end
end end
@@ -249,7 +249,7 @@ class CliBuildTest < CliTestCase
.returns("docker builder info") .returns("docker builder info")
run_command("details").tap do |output| run_command("details").tap do |output|
assert_match /Builder: multiarch/, output assert_match /Builder: local/, output
assert_match /docker builder info/, output assert_match /docker builder info/, output
end end
end end

View File

@@ -122,7 +122,7 @@ class CliMainTest < CliTestCase
.returns("") .returns("")
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :buildx, :inspect, "kamal-app-multiarch", "> /dev/null") .with(:docker, :buildx, :inspect, "kamal-local", "> /dev/null")
.returns("") .returns("")
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
@@ -160,7 +160,7 @@ class CliMainTest < CliTestCase
.returns("") .returns("")
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :buildx, :inspect, "kamal-app-multiarch", "> /dev/null") .with(:docker, :buildx, :inspect, "kamal-local", "> /dev/null")
.returns("") .returns("")
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)

View File

@@ -7,9 +7,9 @@ class CommandsBuilderTest < ActiveSupport::TestCase
test "target multiarch by default" do test "target multiarch by default" do
builder = new_builder_command(builder: { "cache" => { "type" => "gha" } }) builder = new_builder_command(builder: { "cache" => { "type" => "gha" } })
assert_equal "multiarch", builder.name assert_equal "local", builder.name
assert_equal \ assert_equal \
"docker buildx build --push --platform linux/amd64,linux/arm64 --builder kamal-app-multiarch -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .", "docker buildx build --push --platform linux/amd64,linux/arm64 --builder kamal-local -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .",
builder.push.join(" ") builder.push.join(" ")
end end
@@ -23,9 +23,9 @@ class CommandsBuilderTest < ActiveSupport::TestCase
test "target native cached when multiarch is off and cache is set" do test "target native cached when multiarch is off and cache is set" do
builder = new_builder_command(builder: { "multiarch" => false, "cache" => { "type" => "gha" } }) builder = new_builder_command(builder: { "multiarch" => false, "cache" => { "type" => "gha" } })
assert_equal "native/cached", builder.name assert_equal "local", builder.name
assert_equal \ assert_equal \
"docker buildx build --push -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .", "docker buildx build --push --builder kamal-local -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .",
builder.push.join(" ") builder.push.join(" ")
end end
@@ -39,9 +39,9 @@ class CommandsBuilderTest < ActiveSupport::TestCase
test "target multiarch local when arch is set" do test "target multiarch local when arch is set" do
builder = new_builder_command(builder: { "local" => { "arch" => "amd64" } }) builder = new_builder_command(builder: { "local" => { "arch" => "amd64" } })
assert_equal "multiarch", builder.name assert_equal "local", builder.name
assert_equal \ assert_equal \
"docker buildx build --push --platform linux/amd64 --builder kamal-app-multiarch -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --file Dockerfile .", "docker buildx build --push --platform linux/amd64 --builder kamal-local -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --file Dockerfile .",
builder.push.join(" ") builder.push.join(" ")
end end
@@ -93,7 +93,7 @@ class CommandsBuilderTest < ActiveSupport::TestCase
test "build context" do test "build context" do
builder = new_builder_command(builder: { "context" => ".." }) builder = new_builder_command(builder: { "context" => ".." })
assert_equal \ assert_equal \
"docker buildx build --push --platform linux/amd64,linux/arm64 --builder kamal-app-multiarch -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --file Dockerfile ..", "docker buildx build --push --platform linux/amd64,linux/arm64 --builder kamal-local -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --file Dockerfile ..",
builder.push.join(" ") builder.push.join(" ")
end end
@@ -107,7 +107,7 @@ class CommandsBuilderTest < ActiveSupport::TestCase
test "multiarch push with build args" do test "multiarch push with build args" do
builder = new_builder_command(builder: { "args" => { "a" => 1, "b" => 2 } }) builder = new_builder_command(builder: { "args" => { "a" => 1, "b" => 2 } })
assert_equal \ assert_equal \
"docker buildx build --push --platform linux/amd64,linux/arm64 --builder kamal-app-multiarch -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --build-arg a=\"1\" --build-arg b=\"2\" --file Dockerfile .", "docker buildx build --push --platform linux/amd64,linux/arm64 --builder kamal-local -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --build-arg a=\"1\" --build-arg b=\"2\" --file Dockerfile .",
builder.push.join(" ") builder.push.join(" ")
end end
@@ -133,7 +133,7 @@ class CommandsBuilderTest < ActiveSupport::TestCase
test "multiarch context build" do test "multiarch context build" do
builder = new_builder_command(builder: { "context" => "./foo" }) builder = new_builder_command(builder: { "context" => "./foo" })
assert_equal \ assert_equal \
"docker buildx build --push --platform linux/amd64,linux/arm64 --builder kamal-app-multiarch -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --file Dockerfile ./foo", "docker buildx build --push --platform linux/amd64,linux/arm64 --builder kamal-local -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --file Dockerfile ./foo",
builder.push.join(" ") builder.push.join(" ")
end end
@@ -147,7 +147,7 @@ class CommandsBuilderTest < ActiveSupport::TestCase
test "cached context build" do test "cached context build" do
builder = new_builder_command(builder: { "multiarch" => false, "context" => "./foo", "cache" => { "type" => "gha" } }) builder = new_builder_command(builder: { "multiarch" => false, "context" => "./foo", "cache" => { "type" => "gha" } })
assert_equal \ assert_equal \
"docker buildx build --push -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile ./foo", "docker buildx build --push --builder kamal-local -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile ./foo",
builder.push.join(" ") builder.push.join(" ")
end end
@@ -160,7 +160,7 @@ class CommandsBuilderTest < ActiveSupport::TestCase
test "multiarch context hosts" do test "multiarch context hosts" do
command = new_builder_command command = new_builder_command
assert_equal "docker buildx inspect kamal-app-multiarch > /dev/null", command.context_hosts.join(" ") assert_equal "docker buildx inspect kamal-local > /dev/null", command.context_hosts.join(" ")
assert_equal "", command.config_context_hosts.join(" ") assert_equal "", command.config_context_hosts.join(" ")
end end
@@ -172,7 +172,7 @@ class CommandsBuilderTest < ActiveSupport::TestCase
test "native cached context hosts" do test "native cached context hosts" do
command = new_builder_command(builder: { "multiarch" => false, "cache" => { "type" => "registry" } }) 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 "docker buildx inspect kamal-local > /dev/null", command.context_hosts.join(" ")
assert_equal "", command.config_context_hosts.join(" ") assert_equal "", command.config_context_hosts.join(" ")
end end