From b514ed45fb8347b63f501c8291e9d63b14881a9c Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Wed, 11 Jun 2025 16:17:20 +0200 Subject: [PATCH] bake: extra-hosts support Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- bake/bake.go | 36 +++++++++++++++++++++++++++++++++++ bake/bake_test.go | 4 ++++ bake/compose.go | 11 +++++++++++ bake/compose_test.go | 4 ++++ docs/bake-reference.md | 17 +++++++++++++++++ docs/reference/buildx_bake.md | 1 + tests/bake.go | 26 +++++++++++++++++++++++++ 7 files changed, 99 insertions(+) diff --git a/bake/bake.go b/bake/bake.go index ca4cb277d..32ec402ba 100644 --- a/bake/bake.go +++ b/bake/bake.go @@ -4,6 +4,7 @@ import ( "context" "encoding" "encoding/json" + "fmt" "io" "maps" "os" @@ -726,6 +727,7 @@ type Target struct { Ulimits []string `json:"ulimits,omitempty" hcl:"ulimits,optional" cty:"ulimits"` Call *string `json:"call,omitempty" hcl:"call,optional" cty:"call"` Entitlements []string `json:"entitlements,omitempty" hcl:"entitlements,optional" cty:"entitlements"` + ExtraHosts map[string]*string `json:"extra-hosts,omitempty" hcl:"extra-hosts,optional" cty:"extra-hosts"` // IMPORTANT: if you add more fields here, do not forget to update newOverrides/AddOverrides and docs/bake-reference.md. // linked is a private field to mark a target used as a linked one @@ -764,6 +766,14 @@ func (t *Target) MarshalJSON() ([]byte, error) { } } + tgt.ExtraHosts = maps.Clone(t.ExtraHosts) + for k, v := range t.ExtraHosts { + if v != nil { + escaped := esc(*v) + tgt.ExtraHosts[k] = &escaped + } + } + return json.Marshal(tgt) } @@ -894,6 +904,15 @@ func (t *Target) Merge(t2 *Target) { if t2.Entitlements != nil { // merge t.Entitlements = append(t.Entitlements, t2.Entitlements...) } + for k, v := range t2.ExtraHosts { + if v == nil { + continue + } + if t.ExtraHosts == nil { + t.ExtraHosts = map[string]*string{} + } + t.ExtraHosts[k] = v + } t.Inherits = append(t.Inherits, t2.Inherits...) } @@ -1082,6 +1101,14 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon return errors.Errorf("invalid value %s for boolean key load", value) } t.Outputs = setLoadOverride(t.Outputs, load) + case "extra-hosts": + if len(keys) != 2 { + return errors.Errorf("invalid format for extra-hosts, expecting extra-hosts.=") + } + if t.ExtraHosts == nil { + t.ExtraHosts = map[string]*string{} + } + t.ExtraHosts[keys[1]] = &value default: return errors.Errorf("unknown key: %s", keys[0]) } @@ -1404,6 +1431,14 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) { } } + var extraHosts []string + for k, v := range t.ExtraHosts { + if v == nil { + continue + } + extraHosts = append(extraHosts, fmt.Sprintf("%s=%s", k, *v)) + } + bo := &build.Options{ Inputs: bi, Tags: t.Tags, @@ -1415,6 +1450,7 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) { NetworkMode: networkMode, Linked: t.linked, ShmSize: *shmSize, + ExtraHosts: extraHosts, } platforms, err := platformutil.Parse(t.Platforms) diff --git a/bake/bake_test.go b/bake/bake_test.go index 482b83add..ec3b7ec1a 100644 --- a/bake/bake_test.go +++ b/bake/bake_test.go @@ -27,6 +27,9 @@ target "webDEP" { no-cache = true shm-size = "128m" ulimits = ["nofile=1024:1024"] + extra-hosts = { + my_hostname = "8.8.8.8" + } } target "webapp" { @@ -64,6 +67,7 @@ target "webapp" { require.Equal(t, true, *m["webapp"].NoCache) require.Equal(t, "128m", *m["webapp"].ShmSize) require.Equal(t, []string{"nofile=1024:1024"}, m["webapp"].Ulimits) + require.Equal(t, map[string]*string{"my_hostname": ptrstr("8.8.8.8")}, m["webapp"].ExtraHosts) require.Nil(t, m["webapp"].Pull) require.Equal(t, 1, len(g)) diff --git a/bake/compose.go b/bake/compose.go index 1ca046d00..b913d560d 100644 --- a/bake/compose.go +++ b/bake/compose.go @@ -122,6 +122,16 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf } } + extraHosts := map[string]*string{} + if s.Build.ExtraHosts != nil { + for k, v := range s.Build.ExtraHosts { + for _, ip := range v { + vv := ip + extraHosts[k] = &vv + } + } + } + var ssh []*buildflags.SSH for _, bkey := range s.Build.SSH { sshkey := composeToBuildkitSSH(bkey) @@ -180,6 +190,7 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf Secrets: secrets, ShmSize: shmSize, Ulimits: ulimits, + ExtraHosts: extraHosts, } if err = t.composeExtTarget(s.Build.Extensions); err != nil { return nil, err diff --git a/bake/compose_test.go b/bake/compose_test.go index e19ff1387..6e08fdc41 100644 --- a/bake/compose_test.go +++ b/bake/compose_test.go @@ -32,6 +32,9 @@ services: - type=local,src=path/to/cache cache_to: - type=local,dest=path/to/cache + extra_hosts: + - "somehost:162.242.195.82" + - "myhostv6:::1" ssh: - key=/path/to/key - default @@ -76,6 +79,7 @@ secrets: require.Equal(t, ptrstr("123"), c.Targets[1].Args["buildno"]) require.Equal(t, []string{"type=local,src=path/to/cache"}, stringify(c.Targets[1].CacheFrom)) require.Equal(t, []string{"type=local,dest=path/to/cache"}, stringify(c.Targets[1].CacheTo)) + require.Equal(t, map[string]*string{"myhostv6": ptrstr("::1"), "somehost": ptrstr("162.242.195.82")}, c.Targets[1].ExtraHosts) require.Equal(t, "none", *c.Targets[1].NetworkMode) require.Equal(t, []string{"default", "key=/path/to/key"}, stringify(c.Targets[1].SSH)) require.Equal(t, []string{ diff --git a/docs/bake-reference.md b/docs/bake-reference.md index 5245f611d..22592ec37 100644 --- a/docs/bake-reference.md +++ b/docs/bake-reference.md @@ -227,6 +227,8 @@ The following table shows the complete list of attributes that you can assign to | [`description`](#targetdescription) | String | Description of a target | | [`dockerfile-inline`](#targetdockerfile-inline) | String | Inline Dockerfile string | | [`dockerfile`](#targetdockerfile) | String | Dockerfile location | +| [`entitlements`](#targetentitlements) | List | Permissions that the build process requires to run | +| [`extra-hosts`](#targetextra-hosts) | List | Customs host-to-IP mapping | | [`inherits`](#targetinherits) | List | Inherit attributes from other targets | | [`labels`](#targetlabels) | Map | Metadata for images | | [`matrix`](#targetmatrix) | Map | Define a set of variables that forks a target into multiple targets. | @@ -583,6 +585,20 @@ target "integration-tests" { Entitlements are enabled with a two-step process. First, a target must declare the entitlements it requires. Secondly, when invoking the `bake` command, the user must grant the entitlements by passing the `--allow` flag or confirming the entitlements when prompted in an interactive terminal. This is to ensure that the user is aware of the possibly insecure permissions they are granting to the build process. +### `target.extra-hosts` + +Use the `extra-hosts` attribute to define customs host-to-IP mapping for the +target. This has the same effect as passing a [`--add-host`][add-host] flag to +the build command. + +```hcl +target "default" { + extra-hosts = { + my_hostname = "8.8.8.8" + } +} +``` + ### `target.inherits` A target can inherit attributes from other targets. @@ -1422,6 +1438,7 @@ target "webapp-dev" { +[add-host]: https://docs.docker.com/reference/cli/docker/buildx/build/#add-host [attestations]: https://docs.docker.com/build/attestations/ [bake_stdlib]: https://github.com/docker/buildx/blob/master/bake/hclparser/stdlib.go [build-arg]: https://docs.docker.com/reference/cli/docker/image/build/#build-arg diff --git a/docs/reference/buildx_bake.md b/docs/reference/buildx_bake.md index 189866b2b..ab896703a 100644 --- a/docs/reference/buildx_bake.md +++ b/docs/reference/buildx_bake.md @@ -366,6 +366,7 @@ You can override the following fields: * `context` * `dockerfile` * `entitlements` +* `extra-hosts` * `labels` * `load` * `no-cache` diff --git a/tests/bake.go b/tests/bake.go index ca9902a47..71df5e861 100644 --- a/tests/bake.go +++ b/tests/bake.go @@ -80,6 +80,7 @@ var bakeTests = []func(t *testing.T, sb integration.Sandbox){ testBakeCallMetadata, testBakeMultiPlatform, testBakeCheckCallOutput, + testBakeExtraHosts, } func testBakePrint(t *testing.T, sb integration.Sandbox) { @@ -2162,6 +2163,31 @@ target "third" { }) } +func testBakeExtraHosts(t *testing.T, sb integration.Sandbox) { + dockerfile := []byte(` +FROM busybox +RUN cat /etc/hosts | grep myhost | grep 1.2.3.4 + `) + bakefile := []byte(` +target "default" { + extra-hosts = { + myhost = "1.2.3.4" + } +} +`) + dir := tmpdir( + t, + fstest.CreateFile("docker-bake.hcl", bakefile, 0600), + fstest.CreateFile("Dockerfile", dockerfile, 0600), + ) + + out, err := bakeCmd( + sb, + withDir(dir), + ) + require.NoError(t, err, out) +} + func writeTempPrivateKey(fp string) error { privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil {