fix: Implement buildx fixes for general buildkit support and platform handling (#734)

* fix: Implement buildx fixes for general buildkit support and platform handling

* chore: Revert to having buildkit for non-buildx build attribute
This commit is contained in:
Martin 2025-05-24 15:52:00 +02:00 committed by GitHub
parent da2a7967c8
commit 6f1d33f6ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 78 additions and 13 deletions

View file

@ -43,7 +43,7 @@ resource "docker_image" "ubuntu" {
### Build
You can also use the resource to build an image. By default the build block is using the old legacy docker build. In order to use a buildx builder, please read the section below
You can also use the resource to build an image. If you want to use a buildx builder with all of its features, please read the section below.
-> **Note**: The default timeout for the building is 20 minutes. If you need to increase this, you can use [operation timeouts](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts).
@ -85,7 +85,7 @@ resource "docker_image" "zoo" {
-> **Note**: The buildx feature is currently in preview and may have some quirks. Known issues: Setting `ulimits` will not work.
The `build` argument uses the legacy docker builder. If you want to use a buildx builder, you need to set the `builder` argument. For the default buildx builder, you can set the `builder` argument to `default`. For a custom buildx builder, you can set the `builder` argument to the name of the builder. You can find the name of the builder by running `docker buildx ls`.
If you want to use a buildx builder, you need to set the `builder` argument. For the default buildx builder, you can set the `builder` argument to `default`. For a custom buildx builder, you can set the `builder` argument to the name of the builder. You can find the name of the builder by running `docker buildx ls`.
The single platform build result is automatically loaded to `docker images`.
@ -125,7 +125,7 @@ Optional:
- `build_args` (Map of String) Pairs for build-time variables in the form of `ENDPOINT : "https://example.com"`
- `build_id` (String) BuildID is an optional identifier that can be passed together with the build request. The same identifier can be used to gracefully cancel the build with the cancel request.
- `build_log_file` (String) Path to a file where the buildx log are written to. Only available when `builder` is set. If not set, no logs are available. The path is taken as is, so make sure to use a path that is available.
- `builder` (String) Set the name of the buildx builder to use. If not set or empty, the legacy builder will be used.
- `builder` (String) Set the name of the buildx builder to use. If not set, the legacy builder is used.
- `cache_from` (List of String) Images to consider as cache sources
- `cgroup_parent` (String) Optional parent cgroup for the container
- `cpu_period` (Number) The length of a CPU period in microseconds
@ -143,7 +143,7 @@ Optional:
- `memory_swap` (Number) Total memory (memory + swap), -1 to enable unlimited swap
- `network_mode` (String) Set the networking mode for the RUN instructions during build
- `no_cache` (Boolean) Do not use the cache when building the image
- `platform` (String) Set platform if server is multi-platform capable
- `platform` (String) Set the target platform for the build. Defaults to `GOOS/GOARCH`. For more information see the [docker documentation](https://github.com/docker/buildx/blob/master/docs/reference/buildx.md#-set-the-target-platforms-for-the-build---platform)
- `pull_parent` (Boolean) Attempt to pull the image even if an older image exists locally
- `remote_context` (String) A Git repository URI or HTTP/HTTPS context URI. Will be ignored if `builder` is set.
- `remove` (Boolean) Remove intermediate containers after a successful build. Defaults to `true`.

View file

@ -353,7 +353,7 @@ func mapBuildAttributesToBuildOptions(buildAttributes map[string]interface{}, im
options.target = target
}
if platform, ok := buildAttributes["platform"].(string); ok {
if platform, ok := buildAttributes["platform"].(string); ok && len(platform) > 0 {
options.platforms = append(options.platforms, platform)
}

View file

@ -388,7 +388,7 @@ func resourceDockerImage() *schema.Resource {
},
"platform": {
Type: schema.TypeString,
Description: "Set platform if server is multi-platform capable",
Description: "Set the target platform for the build. Defaults to `GOOS/GOARCH`. For more information see the [docker documentation](https://github.com/docker/buildx/blob/master/docs/reference/buildx.md#-set-the-target-platforms-for-the-build---platform)",
Optional: true,
ForceNew: true,
},
@ -406,7 +406,7 @@ func resourceDockerImage() *schema.Resource {
},
"builder": {
Type: schema.TypeString,
Description: "Set the name of the buildx builder to use. If not set or empty, the legacy builder will be used.",
Description: "Set the name of the buildx builder to use. If not set, the legacy builder is used.",
Optional: true,
ForceNew: true,
},

View file

@ -8,6 +8,7 @@ import (
"fmt"
"io"
"log"
"net"
"os"
"path/filepath"
"strings"
@ -16,12 +17,15 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/api/types/versions"
"github.com/docker/docker/client"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/mitchellh/go-homedir"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/secrets/secretsprovider"
"github.com/moby/go-archive"
"github.com/pkg/errors"
)
@ -56,15 +60,17 @@ func resourceDockerImageCreate(ctx context.Context, d *schema.ResourceData, meta
}
buildLogFile := rawBuild["build_log_file"].(string)
log.Printf("[DEBUG] build options %#v", options)
err = runBuild(ctx, dockerCli, options, buildLogFile)
if err != nil {
return diag.FromErr(err)
return diag.Errorf("Error running buildx build: %v", err)
}
} else {
err := buildDockerImage(ctx, rawBuild, imageName, client)
if err != nil {
return diag.FromErr(err)
return diag.Errorf("Error running legacy build: %v", err)
}
}
}
@ -356,6 +362,23 @@ func buildDockerImage(ctx context.Context, rawBuild map[string]interface{}, imag
buildContext := rawBuild["context"].(string)
buildKitSession := enableBuildKitIfSupported(ctx, client, &buildOptions)
// If Buildkit is enabled, try to parse and use secrets if present.
if buildKitSession != nil {
if secretsRaw, secretsDefined := rawBuild["secrets"]; secretsDefined {
parsedSecrets := parseBuildSecrets(secretsRaw)
store, err := secretsprovider.NewStore(parsedSecrets)
if err != nil {
return err
}
provider := secretsprovider.NewSecretProvider(store)
buildKitSession.Allow(provider)
}
}
buildCtx, relDockerfile, err := prepareBuildContext(buildContext, buildOptions.Dockerfile)
if err != nil {
return err
@ -377,6 +400,33 @@ func buildDockerImage(ctx context.Context, rawBuild map[string]interface{}, imag
return nil
}
const minBuildkitDockerVersion = "1.39"
func enableBuildKitIfSupported(
ctx context.Context,
client *client.Client,
buildOptions *types.ImageBuildOptions,
) *session.Session {
dockerClientVersion := client.ClientVersion()
log.Printf("[DEBUG] DockerClientVersion: %v, minBuildKitDockerVersion: %v\n", dockerClientVersion, minBuildkitDockerVersion)
if versions.GreaterThanOrEqualTo(dockerClientVersion, minBuildkitDockerVersion) {
log.Printf("[DEBUG] Enabling BuildKit")
s, _ := session.NewSession(ctx, "docker-provider")
dialSession := func(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) {
return client.DialHijack(ctx, "/session", proto, meta)
}
//nolint
go s.Run(ctx, dialSession)
defer s.Close() //nolint:errcheck
buildOptions.SessionID = s.ID()
buildOptions.Version = types.BuilderBuildKit
return s
} else {
buildOptions.Version = types.BuilderV1
return nil
}
}
func prepareBuildContext(specifiedContext string, specifiedDockerfile string) (io.ReadCloser, string, error) {
var (
dockerfileCtx io.ReadCloser
@ -465,3 +515,20 @@ func decodeBuildMessages(response types.ImageBuildResponse) (string, error) {
return buf.String(), buildErr
}
func parseBuildSecrets(secretsRaw interface{}) []secretsprovider.Source {
options := secretsRaw.([]interface{})
secrets := make([]secretsprovider.Source, len(options))
for i, option := range options {
secretRaw := option.(map[string]interface{})
source := secretsprovider.Source{
ID: secretRaw["id"].(string),
FilePath: secretRaw["src"].(string),
Env: secretRaw["env"].(string),
}
secrets[i] = source
}
return secrets
}

View file

@ -28,7 +28,7 @@ you need to use it in combination with `docker_registry_image` as follows:
### Build
You can also use the resource to build an image. By default the build block is using the old legacy docker build. In order to use a buildx builder, please read the section below
You can also use the resource to build an image. If you want to use a buildx builder with all of its features, please read the section below.
-> **Note**: The default timeout for the building is 20 minutes. If you need to increase this, you can use [operation timeouts](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts).
@ -46,7 +46,7 @@ You can use the `triggers` argument to specify when the image should be rebuild.
-> **Note**: The buildx feature is currently in preview and may have some quirks. Known issues: Setting `ulimits` will not work.
The `build` argument uses the legacy docker builder. If you want to use a buildx builder, you need to set the `builder` argument. For the default buildx builder, you can set the `builder` argument to `default`. For a custom buildx builder, you can set the `builder` argument to the name of the builder. You can find the name of the builder by running `docker buildx ls`.
If you want to use a buildx builder, you need to set the `builder` argument. For the default buildx builder, you can set the `builder` argument to `default`. For a custom buildx builder, you can set the `builder` argument to the name of the builder. You can find the name of the builder by running `docker buildx ls`.
The single platform build result is automatically loaded to `docker images`.

View file

@ -5,7 +5,6 @@ resource "docker_image" "test" {
dockerfile = "Dockerfile"
force_remove = true
builder = "default"
platform = "linux/amd64"
secrets {
id = "TEST_SECRET_SRC"

View file

@ -5,7 +5,6 @@ resource "docker_image" "test" {
dockerfile = "Dockerfile"
force_remove = true
builder = "default"
platform = "linux/amd64"
build_log_file = "%s"
}