Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
Tonis Tiigi authored on 2017/04/11 07:27:42... | ... |
@@ -56,6 +56,7 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui |
56 | 56 |
options.ExtraHosts = r.Form["extrahosts"] |
57 | 57 |
options.SecurityOpt = r.Form["securityopt"] |
58 | 58 |
options.Squash = httputils.BoolValue(r, "squash") |
59 |
+ options.Target = r.FormValue("target") |
|
59 | 60 |
|
60 | 61 |
if r.Form.Get("shmsize") != "" { |
61 | 62 |
shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64) |
... | ... |
@@ -16,6 +16,7 @@ import ( |
16 | 16 |
"github.com/docker/docker/api/types/backend" |
17 | 17 |
"github.com/docker/docker/api/types/container" |
18 | 18 |
"github.com/docker/docker/builder" |
19 |
+ "github.com/docker/docker/builder/dockerfile/command" |
|
19 | 20 |
"github.com/docker/docker/builder/dockerfile/parser" |
20 | 21 |
"github.com/docker/docker/image" |
21 | 22 |
"github.com/docker/docker/pkg/stringid" |
... | ... |
@@ -253,6 +254,10 @@ func (b *Builder) build(stdout io.Writer, stderr io.Writer, out io.Writer) (stri |
253 | 253 |
// Not cancelled yet, keep going... |
254 | 254 |
} |
255 | 255 |
|
256 |
+ if command.From == n.Value && b.imageContexts.isCurrentTarget(b.options.Target) { |
|
257 |
+ break |
|
258 |
+ } |
|
259 |
+ |
|
256 | 260 |
if err := b.dispatch(i, total, n); err != nil { |
257 | 261 |
if b.options.ForceRemove { |
258 | 262 |
b.clearTmp() |
... | ... |
@@ -267,6 +272,10 @@ func (b *Builder) build(stdout io.Writer, stderr io.Writer, out io.Writer) (stri |
267 | 267 |
} |
268 | 268 |
} |
269 | 269 |
|
270 |
+ if b.options.Target != "" && !b.imageContexts.isCurrentTarget(b.options.Target) { |
|
271 |
+ return "", perrors.Errorf("failed to reach build target %s in Dockerfile", b.options.Target) |
|
272 |
+ } |
|
273 |
+ |
|
270 | 274 |
b.warnOnUnusedBuildArgs() |
271 | 275 |
|
272 | 276 |
if b.image == "" { |
... | ... |
@@ -15,10 +15,11 @@ import ( |
15 | 15 |
// imageContexts is a helper for stacking up built image rootfs and reusing |
16 | 16 |
// them as contexts |
17 | 17 |
type imageContexts struct { |
18 |
- b *Builder |
|
19 |
- list []*imageMount |
|
20 |
- byName map[string]*imageMount |
|
21 |
- cache *pathCache |
|
18 |
+ b *Builder |
|
19 |
+ list []*imageMount |
|
20 |
+ byName map[string]*imageMount |
|
21 |
+ cache *pathCache |
|
22 |
+ currentName string |
|
22 | 23 |
} |
23 | 24 |
|
24 | 25 |
func (ic *imageContexts) new(name string, increment bool) (*imageMount, error) { |
... | ... |
@@ -35,6 +36,7 @@ func (ic *imageContexts) new(name string, increment bool) (*imageMount, error) { |
35 | 35 |
if increment { |
36 | 36 |
ic.list = append(ic.list, im) |
37 | 37 |
} |
38 |
+ ic.currentName = name |
|
38 | 39 |
return im, nil |
39 | 40 |
} |
40 | 41 |
|
... | ... |
@@ -88,6 +90,13 @@ func (ic *imageContexts) unmount() (retErr error) { |
88 | 88 |
return |
89 | 89 |
} |
90 | 90 |
|
91 |
+func (ic *imageContexts) isCurrentTarget(target string) bool { |
|
92 |
+ if target == "" { |
|
93 |
+ return false |
|
94 |
+ } |
|
95 |
+ return strings.EqualFold(ic.currentName, target) |
|
96 |
+} |
|
97 |
+ |
|
91 | 98 |
func (ic *imageContexts) getCache(id, path string) (interface{}, bool) { |
92 | 99 |
if ic.cache != nil { |
93 | 100 |
if id == "" { |
... | ... |
@@ -64,6 +64,7 @@ type buildOptions struct { |
64 | 64 |
securityOpt []string |
65 | 65 |
networkMode string |
66 | 66 |
squash bool |
67 |
+ target string |
|
67 | 68 |
} |
68 | 69 |
|
69 | 70 |
// NewBuildCommand creates a new `docker build` command |
... | ... |
@@ -115,6 +116,7 @@ func NewBuildCommand(dockerCli *command.DockerCli) *cobra.Command { |
115 | 115 |
flags.StringVar(&options.networkMode, "network", "default", "Set the networking mode for the RUN instructions during build") |
116 | 116 |
flags.SetAnnotation("network", "version", []string{"1.25"}) |
117 | 117 |
flags.Var(&options.extraHosts, "add-host", "Add a custom host-to-IP mapping (host:ip)") |
118 |
+ flags.StringVar(&options.target, "target", "", "Set the target build stage to build.") |
|
118 | 119 |
|
119 | 120 |
command.AddTrustVerificationFlags(flags) |
120 | 121 |
|
... | ... |
@@ -302,6 +304,7 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error { |
302 | 302 |
NetworkMode: options.networkMode, |
303 | 303 |
Squash: options.squash, |
304 | 304 |
ExtraHosts: options.extraHosts.GetAll(), |
305 |
+ Target: options.target, |
|
305 | 306 |
} |
306 | 307 |
|
307 | 308 |
response, err := dockerCli.Client().ImageBuild(ctx, body, buildOptions) |
... | ... |
@@ -95,6 +95,7 @@ func (cli *Client) imageBuildOptionsToQuery(options types.ImageBuildOptions) (ur |
95 | 95 |
query.Set("cgroupparent", options.CgroupParent) |
96 | 96 |
query.Set("shmsize", strconv.FormatInt(options.ShmSize, 10)) |
97 | 97 |
query.Set("dockerfile", options.Dockerfile) |
98 |
+ query.Set("target", options.Target) |
|
98 | 99 |
|
99 | 100 |
ulimitsJSON, err := json.Marshal(options.Ulimits) |
100 | 101 |
if err != nil { |
... | ... |
@@ -6210,6 +6210,33 @@ func (s *DockerSuite) TestBuildCopyFromWindowsIsCaseInsensitive(c *check.C) { |
6210 | 6210 |
result.Assert(c, exp) |
6211 | 6211 |
} |
6212 | 6212 |
|
6213 |
+func (s *DockerSuite) TestBuildIntermediateTarget(c *check.C) { |
|
6214 |
+ dockerfile := ` |
|
6215 |
+ FROM busybox AS build-env |
|
6216 |
+ CMD ["/dev"] |
|
6217 |
+ FROM busybox |
|
6218 |
+ CMD ["/dist"] |
|
6219 |
+ ` |
|
6220 |
+ ctx := fakeContext(c, dockerfile, map[string]string{ |
|
6221 |
+ "Dockerfile": dockerfile, |
|
6222 |
+ }) |
|
6223 |
+ defer ctx.Close() |
|
6224 |
+ |
|
6225 |
+ result := buildImage("build1", withExternalBuildContext(ctx), |
|
6226 |
+ cli.WithFlags("--target", "build-env")) |
|
6227 |
+ result.Assert(c, icmd.Success) |
|
6228 |
+ |
|
6229 |
+ res := inspectFieldJSON(c, "build1", "Config.Cmd") |
|
6230 |
+ c.Assert(res, checker.Equals, `["/dev"]`) |
|
6231 |
+ |
|
6232 |
+ result = buildImage("build1", withExternalBuildContext(ctx), |
|
6233 |
+ cli.WithFlags("--target", "nosuchtarget")) |
|
6234 |
+ result.Assert(c, icmd.Expected{ |
|
6235 |
+ ExitCode: 1, |
|
6236 |
+ Err: "failed to reach build target", |
|
6237 |
+ }) |
|
6238 |
+} |
|
6239 |
+ |
|
6213 | 6240 |
// TestBuildOpaqueDirectory tests that a build succeeds which |
6214 | 6241 |
// creates opaque directories. |
6215 | 6242 |
// See https://github.com/docker/docker/issues/25244 |