builder: add an option for specifying build target

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi 2017-04-10 15:27:42 -07:00
parent 778e32a2fa
commit 33e07f41ad
7 changed files with 55 additions and 4 deletions

View file

@ -56,6 +56,7 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
options.ExtraHosts = r.Form["extrahosts"]
options.SecurityOpt = r.Form["securityopt"]
options.Squash = httputils.BoolValue(r, "squash")
options.Target = r.FormValue("target")
if r.Form.Get("shmsize") != "" {
shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64)

View file

@ -176,6 +176,7 @@ type ImageBuildOptions struct {
CacheFrom []string
SecurityOpt []string
ExtraHosts []string // List of extra hosts
Target string
}
// ImageBuildResponse holds information

View file

@ -16,6 +16,7 @@ import (
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/builder"
"github.com/docker/docker/builder/dockerfile/command"
"github.com/docker/docker/builder/dockerfile/parser"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/stringid"
@ -253,6 +254,10 @@ func (b *Builder) build(stdout io.Writer, stderr io.Writer, out io.Writer) (stri
// Not cancelled yet, keep going...
}
if command.From == n.Value && b.imageContexts.isCurrentTarget(b.options.Target) {
break
}
if err := b.dispatch(i, total, n); err != nil {
if b.options.ForceRemove {
b.clearTmp()
@ -267,6 +272,10 @@ func (b *Builder) build(stdout io.Writer, stderr io.Writer, out io.Writer) (stri
}
}
if b.options.Target != "" && !b.imageContexts.isCurrentTarget(b.options.Target) {
return "", perrors.Errorf("failed to reach build target %s in Dockerfile", b.options.Target)
}
b.warnOnUnusedBuildArgs()
if b.image == "" {

View file

@ -19,6 +19,7 @@ type imageContexts struct {
list []*imageMount
byName map[string]*imageMount
cache *pathCache
currentName string
}
func (ic *imageContexts) new(name string, increment bool) (*imageMount, error) {
@ -35,6 +36,7 @@ func (ic *imageContexts) new(name string, increment bool) (*imageMount, error) {
if increment {
ic.list = append(ic.list, im)
}
ic.currentName = name
return im, nil
}
@ -88,6 +90,13 @@ func (ic *imageContexts) unmount() (retErr error) {
return
}
func (ic *imageContexts) isCurrentTarget(target string) bool {
if target == "" {
return false
}
return strings.EqualFold(ic.currentName, target)
}
func (ic *imageContexts) getCache(id, path string) (interface{}, bool) {
if ic.cache != nil {
if id == "" {

View file

@ -64,6 +64,7 @@ type buildOptions struct {
securityOpt []string
networkMode string
squash bool
target string
}
// NewBuildCommand creates a new `docker build` command
@ -115,6 +116,7 @@ func NewBuildCommand(dockerCli *command.DockerCli) *cobra.Command {
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)")
flags.StringVar(&options.target, "target", "", "Set the target build stage to build.")
command.AddTrustVerificationFlags(flags)
@ -302,6 +304,7 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
NetworkMode: options.networkMode,
Squash: options.squash,
ExtraHosts: options.extraHosts.GetAll(),
Target: options.target,
}
response, err := dockerCli.Client().ImageBuild(ctx, body, buildOptions)

View file

@ -95,6 +95,7 @@ func (cli *Client) imageBuildOptionsToQuery(options types.ImageBuildOptions) (ur
query.Set("cgroupparent", options.CgroupParent)
query.Set("shmsize", strconv.FormatInt(options.ShmSize, 10))
query.Set("dockerfile", options.Dockerfile)
query.Set("target", options.Target)
ulimitsJSON, err := json.Marshal(options.Ulimits)
if err != nil {

View file

@ -6210,6 +6210,33 @@ func (s *DockerSuite) TestBuildCopyFromWindowsIsCaseInsensitive(c *check.C) {
result.Assert(c, exp)
}
func (s *DockerSuite) TestBuildIntermediateTarget(c *check.C) {
dockerfile := `
FROM busybox AS build-env
CMD ["/dev"]
FROM busybox
CMD ["/dist"]
`
ctx := fakeContext(c, dockerfile, map[string]string{
"Dockerfile": dockerfile,
})
defer ctx.Close()
result := buildImage("build1", withExternalBuildContext(ctx),
cli.WithFlags("--target", "build-env"))
result.Assert(c, icmd.Success)
res := inspectFieldJSON(c, "build1", "Config.Cmd")
c.Assert(res, checker.Equals, `["/dev"]`)
result = buildImage("build1", withExternalBuildContext(ctx),
cli.WithFlags("--target", "nosuchtarget"))
result.Assert(c, icmd.Expected{
ExitCode: 1,
Err: "failed to reach build target",
})
}
// TestBuildOpaqueDirectory tests that a build succeeds which
// creates opaque directories.
// See https://github.com/docker/docker/issues/25244