From b420b2613d202f4a8588d3949646c9ef6e4997af Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Tue, 14 Jan 2025 10:17:56 -0700 Subject: [PATCH] Adds support for Docker Build Cloud --- lib/kamal/commands/builder.rb | 8 ++++- lib/kamal/commands/builder/cloud.rb | 22 ++++++++++++ lib/kamal/configuration/builder.rb | 4 +++ lib/kamal/configuration/docs/builder.yml | 3 ++ test/cli/build_test.rb | 12 +++++++ test/commands/builder_test.rb | 8 +++++ test/fixtures/deploy_with_cloud_builder.yml | 40 +++++++++++++++++++++ 7 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 lib/kamal/commands/builder/cloud.rb create mode 100644 test/fixtures/deploy_with_cloud_builder.yml diff --git a/lib/kamal/commands/builder.rb b/lib/kamal/commands/builder.rb index cd2980fbf..f784b6f98 100644 --- a/lib/kamal/commands/builder.rb +++ b/lib/kamal/commands/builder.rb @@ -2,7 +2,7 @@ class Kamal::Commands::Builder < Kamal::Commands::Base delegate :create, :remove, :push, :clean, :pull, :info, :inspect_builder, :validate_image, :first_mirror, to: :target - delegate :local?, :remote?, to: "config.builder" + delegate :local?, :remote?, :cloud?, to: "config.builder" include Clone @@ -17,6 +17,8 @@ def target else remote end + elsif cloud? + cloud else local end @@ -34,6 +36,10 @@ def hybrid @hybrid ||= Kamal::Commands::Builder::Hybrid.new(config) end + def cloud + @cloud ||= Kamal::Commands::Builder::Cloud.new(config) + end + def ensure_local_dependencies_installed if name.native? diff --git a/lib/kamal/commands/builder/cloud.rb b/lib/kamal/commands/builder/cloud.rb new file mode 100644 index 000000000..f7b97c501 --- /dev/null +++ b/lib/kamal/commands/builder/cloud.rb @@ -0,0 +1,22 @@ +class Kamal::Commands::Builder::Cloud < Kamal::Commands::Builder::Base + # Expects `driver` to be of format "cloud docker-org-name/builder-name" + + def create + docker :buildx, :create, "--driver", driver + end + + def remove + docker :buildx, :rm, builder_name + end + + private + def builder_name + driver.gsub(/[ \/]/, "-") + end + + def inspect_buildx + pipe \ + docker(:buildx, :inspect, builder_name), + grep("-q", "Endpoint:.*cloud://.*") + end +end diff --git a/lib/kamal/configuration/builder.rb b/lib/kamal/configuration/builder.rb index 970c47d18..6821725d3 100644 --- a/lib/kamal/configuration/builder.rb +++ b/lib/kamal/configuration/builder.rb @@ -53,6 +53,10 @@ def local? !local_disabled? && (arches.empty? || local_arches.any?) end + def cloud? + driver.start_with? "cloud" + end + def cached? !!builder_config["cache"] end diff --git a/lib/kamal/configuration/docs/builder.yml b/lib/kamal/configuration/docs/builder.yml index 230b39eef..ec215a6a9 100644 --- a/lib/kamal/configuration/docs/builder.yml +++ b/lib/kamal/configuration/docs/builder.yml @@ -102,6 +102,9 @@ builder: # # The build driver to use, defaults to `docker-container`: driver: docker + # + # If you want to use Docker Build Cloud (https://www.docker.com/products/build-cloud/), you can set the driver to: + driver: cloud org-name/builder-name # Provenance # diff --git a/test/cli/build_test.rb b/test/cli/build_test.rb index 4259fa5bb..9ad988ee3 100644 --- a/test/cli/build_test.rb +++ b/test/cli/build_test.rb @@ -235,6 +235,12 @@ class CliBuildTest < CliTestCase end end + test "create cloud" do + run_command("create", fixture: :with_cloud_builder).tap do |output| + assert_match /docker buildx create --driver cloud example_org\/cloud_builder/, output + end + end + test "create with error" do stub_setup SSHKit::Backend::Abstract.any_instance.stubs(:execute) @@ -252,6 +258,12 @@ class CliBuildTest < CliTestCase end end + test "remove cloud" do + run_command("remove", fixture: :with_cloud_builder).tap do |output| + assert_match /docker buildx rm cloud-example_org-cloud_builder/, output + end + end + test "details" do SSHKit::Backend::Abstract.any_instance.stubs(:capture) .with(:docker, :context, :ls, "&&", :docker, :buildx, :ls) diff --git a/test/commands/builder_test.rb b/test/commands/builder_test.rb index 85703f546..c5e23ea84 100644 --- a/test/commands/builder_test.rb +++ b/test/commands/builder_test.rb @@ -61,6 +61,14 @@ class CommandsBuilderTest < ActiveSupport::TestCase builder.push.join(" ") end + test "cloud builder" do + builder = new_builder_command(builder: { "arch" => [ "#{local_arch}" ], "driver" => "cloud docker-org-name/builder-name" }) + assert_equal "cloud", builder.name + assert_equal \ + "docker buildx build --push --platform linux/#{local_arch} --builder cloud-docker-org-name-builder-name -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --file Dockerfile .", + builder.push.join(" ") + end + test "build args" do builder = new_builder_command(builder: { "args" => { "a" => 1, "b" => 2 } }) assert_equal \ diff --git a/test/fixtures/deploy_with_cloud_builder.yml b/test/fixtures/deploy_with_cloud_builder.yml new file mode 100644 index 000000000..420ff2053 --- /dev/null +++ b/test/fixtures/deploy_with_cloud_builder.yml @@ -0,0 +1,40 @@ +service: app +image: dhh/app +servers: + web: + - "1.1.1.1" + - "1.1.1.2" + workers: + - "1.1.1.3" + - "1.1.1.4" +registry: + username: user + password: pw + +accessories: + mysql: + image: mysql:5.7 + host: 1.1.1.3 + port: 3306 + env: + clear: + MYSQL_ROOT_HOST: '%' + secret: + - MYSQL_ROOT_PASSWORD + files: + - test/fixtures/files/my.cnf:/etc/mysql/my.cnf + directories: + - data:/var/lib/mysql + redis: + image: redis:latest + roles: + - web + port: 6379 + directories: + - data:/data + +readiness_delay: 0 + +builder: + arch: <%= Kamal::Utils.docker_arch == "arm64" ? "amd64" : "arm64" %> + driver: cloud example_org/cloud_builder