builder: add an option for specifying build target
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
parent
778e32a2fa
commit
33e07f41ad
7 changed files with 55 additions and 4 deletions
|
@ -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)
|
||||
|
|
|
@ -176,6 +176,7 @@ type ImageBuildOptions struct {
|
|||
CacheFrom []string
|
||||
SecurityOpt []string
|
||||
ExtraHosts []string // List of extra hosts
|
||||
Target string
|
||||
}
|
||||
|
||||
// ImageBuildResponse holds information
|
||||
|
|
|
@ -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 == "" {
|
||||
|
|
|
@ -15,10 +15,11 @@ import (
|
|||
// imageContexts is a helper for stacking up built image rootfs and reusing
|
||||
// them as contexts
|
||||
type imageContexts struct {
|
||||
b *Builder
|
||||
list []*imageMount
|
||||
byName map[string]*imageMount
|
||||
cache *pathCache
|
||||
b *Builder
|
||||
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 == "" {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue