diff --git a/components/engine/api/server/router/build/build_routes.go b/components/engine/api/server/router/build/build_routes.go index 75425b19fb..6d4835dd10 100644 --- a/components/engine/api/server/router/build/build_routes.go +++ b/components/engine/api/server/router/build/build_routes.go @@ -53,6 +53,7 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui options.CgroupParent = r.FormValue("cgroupparent") options.NetworkMode = r.FormValue("networkmode") options.Tags = r.Form["t"] + options.ExtraHosts = r.Form["extrahosts"] options.SecurityOpt = r.Form["securityopt"] options.Squash = httputils.BoolValue(r, "squash") diff --git a/components/engine/api/swagger.yaml b/components/engine/api/swagger.yaml index 90ca79162d..171f4c0f0b 100644 --- a/components/engine/api/swagger.yaml +++ b/components/engine/api/swagger.yaml @@ -4365,6 +4365,10 @@ paths: in: "query" description: "A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters." type: "string" + - name: "extrahosts" + in: "query" + description: "Extra hosts to add to /etc/hosts" + type: "string" - name: "remote" in: "query" description: "A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball." diff --git a/components/engine/api/types/client.go b/components/engine/api/types/client.go index 998cf68c5c..9eb64b6db3 100644 --- a/components/engine/api/types/client.go +++ b/components/engine/api/types/client.go @@ -175,6 +175,7 @@ type ImageBuildOptions struct { // specified here do not need to have a valid parent chain to match cache. CacheFrom []string SecurityOpt []string + ExtraHosts []string // List of extra hosts } // ImageBuildResponse holds information diff --git a/components/engine/builder/dockerfile/internals.go b/components/engine/builder/dockerfile/internals.go index 29e569dce7..a3ec20b438 100644 --- a/components/engine/builder/dockerfile/internals.go +++ b/components/engine/builder/dockerfile/internals.go @@ -498,7 +498,8 @@ func (b *Builder) create() (string, error) { Resources: resources, NetworkMode: container.NetworkMode(b.options.NetworkMode), // Set a log config to override any default value set on the daemon - LogConfig: defaultLogConfig, + LogConfig: defaultLogConfig, + ExtraHosts: b.options.ExtraHosts, } config := *b.runConfig diff --git a/components/engine/cli/command/image/build.go b/components/engine/cli/command/image/build.go index 34c231d63e..4639833a9e 100644 --- a/components/engine/cli/command/image/build.go +++ b/components/engine/cli/command/image/build.go @@ -38,6 +38,7 @@ type buildOptions struct { tags opts.ListOpts labels opts.ListOpts buildArgs opts.ListOpts + extraHosts opts.ListOpts ulimits *opts.UlimitOpt memory string memorySwap string @@ -65,10 +66,11 @@ type buildOptions struct { func NewBuildCommand(dockerCli *command.DockerCli) *cobra.Command { ulimits := make(map[string]*units.Ulimit) options := buildOptions{ - tags: opts.NewListOpts(validateTag), - buildArgs: opts.NewListOpts(opts.ValidateEnv), - ulimits: opts.NewUlimitOpt(&ulimits), - labels: opts.NewListOpts(opts.ValidateEnv), + tags: opts.NewListOpts(validateTag), + buildArgs: opts.NewListOpts(opts.ValidateEnv), + ulimits: opts.NewUlimitOpt(&ulimits), + labels: opts.NewListOpts(opts.ValidateEnv), + extraHosts: opts.NewListOpts(opts.ValidateExtraHost), } cmd := &cobra.Command{ @@ -108,6 +110,7 @@ func NewBuildCommand(dockerCli *command.DockerCli) *cobra.Command { flags.StringSliceVar(&options.securityOpt, "security-opt", []string{}, "Security options") flags.StringVar(&options.networkMode, "network", "default", "Set the networking mode for the RUN instructions during build") flags.SetAnnotation("network", "version", []string{"1.25"}) + flags.Var(&options.extraHosts, "add-host", "Add a custom host-to-IP mapping (host:ip)") command.AddTrustVerificationFlags(flags) @@ -301,6 +304,7 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error { SecurityOpt: options.securityOpt, NetworkMode: options.networkMode, Squash: options.squash, + ExtraHosts: options.extraHosts.GetAll(), } response, err := dockerCli.Client().ImageBuild(ctx, body, buildOptions) diff --git a/components/engine/client/image_build.go b/components/engine/client/image_build.go index 411d5493ea..cc5a71c2a1 100644 --- a/components/engine/client/image_build.go +++ b/components/engine/client/image_build.go @@ -48,6 +48,7 @@ func (cli *Client) imageBuildOptionsToQuery(options types.ImageBuildOptions) (ur query := url.Values{ "t": options.Tags, "securityopt": options.SecurityOpt, + "extrahosts": options.ExtraHosts, } if options.SuppressOutput { query.Set("q", "1") diff --git a/components/engine/docs/api/version-history.md b/components/engine/docs/api/version-history.md index 58167823b3..51e438e6c8 100644 --- a/components/engine/docs/api/version-history.md +++ b/components/engine/docs/api/version-history.md @@ -21,6 +21,7 @@ keywords: "API, Docker, rcli, REST, documentation" * `GET /networks` is optimised only to return list of all networks and network specific information. List of all containers attached to a specific network is removed from this API and is only available using the network specific `GET /networks/{network-id}. * `GET /containers/json` now supports `publish` and `expose` filters to filter containers that expose or publish certain ports. * `POST /services/create` and `POST /services/(id or name)/update` now accept the `ReadOnly` parameter, which mounts the container's root filesystem as read only. +* `POST /build` now accepts `extrahosts` parameter to specify a host to ip mapping to use during the build. ## v1.26 API changes diff --git a/components/engine/docs/reference/commandline/build.md b/components/engine/docs/reference/commandline/build.md index fc8183f1fa..962393dd5a 100644 --- a/components/engine/docs/reference/commandline/build.md +++ b/components/engine/docs/reference/commandline/build.md @@ -21,6 +21,7 @@ Usage: docker build [OPTIONS] PATH | URL | - Build an image from a Dockerfile Options: + --add-host value Add a custom host-to-IP mapping (host:ip) (default []) --build-arg value Set build-time variables (default []) --cache-from value Images to consider as cache sources (default []) --cgroup-parent string Optional parent cgroup for the container @@ -435,6 +436,13 @@ Linux namespaces. On Microsoft Windows, you can specify these values: Specifying the `--isolation` flag without a value is the same as setting `--isolation="default"`. +### Add entries to container hosts file (--add-host) + +You can add other hosts into a container's `/etc/hosts` file by using one or +more `--add-host` flags. This example adds a static address for a host named +`docker`: + + $ docker build --add-host=docker:10.180.0.1 . ### Squash an image's layers (--squash) **Experimental Only** @@ -451,3 +459,4 @@ space. **Note**: using this option you may see significantly more space used due to storing two copies of the image, one for the build cache with all the cache layers in tact, and one for the squashed version. + diff --git a/components/engine/integration-cli/docker_cli_build_test.go b/components/engine/integration-cli/docker_cli_build_test.go index b4484247c1..c9b575c910 100644 --- a/components/engine/integration-cli/docker_cli_build_test.go +++ b/components/engine/integration-cli/docker_cli_build_test.go @@ -5546,6 +5546,49 @@ func (s *DockerSuite) TestBuildNetContainer(c *check.C) { c.Assert(strings.TrimSpace(host), check.Equals, "foobar") } +func (s *DockerSuite) TestBuildWithExtraHost(c *check.C) { + testRequires(c, DaemonIsLinux) + + name := "testbuildwithextrahost" + buildImageSuccessfully(c, name, + withBuildFlags( + "--add-host", "foo:127.0.0.1", + "--add-host", "bar:127.0.0.1", + ), + withDockerfile(` + FROM busybox + RUN ping -c 1 foo + RUN ping -c 1 bar + `)) +} + +func (s *DockerSuite) TestBuildWithExtraHostInvalidFormat(c *check.C) { + testRequires(c, DaemonIsLinux) + dockerfile := ` + FROM busybox + RUN ping -c 1 foo` + + testCases := []struct { + testName string + dockerfile string + buildFlag string + }{ + {"extra_host_missing_ip", dockerfile, "--add-host=foo"}, + {"extra_host_missing_ip_with_delimeter", dockerfile, "--add-host=foo:"}, + {"extra_host_missing_hostname", dockerfile, "--add-host=:127.0.0.1"}, + {"extra_host_invalid_ipv4", dockerfile, "--add-host=foo:101.10.2"}, + {"extra_host_invalid_ipv6", dockerfile, "--add-host=foo:2001::1::3F"}, + } + + for _, tc := range testCases { + result := buildImage(tc.testName, withBuildFlags(tc.buildFlag), withDockerfile(tc.dockerfile)) + result.Assert(c, icmd.Expected{ + ExitCode: 125, + }) + } + +} + func (s *DockerSuite) TestBuildSquashParent(c *check.C) { testRequires(c, ExperimentalDaemon) dockerFile := ` diff --git a/components/engine/man/docker-build.1.md b/components/engine/man/docker-build.1.md index 5676cb80bd..b650fc3aa2 100644 --- a/components/engine/man/docker-build.1.md +++ b/components/engine/man/docker-build.1.md @@ -6,6 +6,7 @@ docker-build - Build an image from a Dockerfile # SYNOPSIS **docker build** +[**--add-host**[=*[]*]] [**--build-arg**[=*[]*]] [**--cpu-shares**[=*0*]] [**--cgroup-parent**[=*CGROUP-PARENT*]] @@ -74,6 +75,12 @@ set as the **URL**, the repository is cloned locally and then sent as the contex storing two copies of the image, one for the build cache with all the cache layers in tact, and one for the squashed version. +**--add-host**=[] + Add a custom host-to-IP mapping (host:ip) + + Add a line to /etc/hosts. The format is hostname:ip. The **--add-host** +option can be set multiple times. + **--build-arg**=*variable* name and value of a **buildarg**.