func (b *Builder) processLabels() error { if len(b.options.Labels) == 0 { return nil } var labels []string for k, v := range b.options.Labels { labels = append(labels, fmt.Sprintf("%q='%s'", k, v)) } // Sort the label to have a repeatable order sort.Strings(labels) line := "LABEL " + strings.Join(labels, " ") _, node, err := parser.ParseLine(line, &b.directive, false) if err != nil { return err } b.dockerfile.Children = append(b.dockerfile.Children, node) return nil }
// build runs the Dockerfile builder from a context and a docker object that allows to make calls // to Docker. // // This will (barring errors): // // * read the dockerfile from context // * parse the dockerfile if not already parsed // * walk the AST and execute it by dispatching to handlers. If Remove // or ForceRemove is set, additional cleanup around containers happens after // processing. // * Tag image, if applicable. // * Print a happy message and return the image ID. // func (b *Builder) build(stdout io.Writer, stderr io.Writer, out io.Writer) (string, error) { b.Stdout = stdout b.Stderr = stderr b.Output = out // If Dockerfile was not parsed yet, extract it from the Context if b.dockerfile == nil { if err := b.readDockerfile(); err != nil { return "", err } } repoAndTags, err := sanitizeRepoAndTags(b.options.Tags) if err != nil { return "", err } if len(b.options.Labels) > 0 { line := "LABEL " for k, v := range b.options.Labels { line += fmt.Sprintf("%q='%s' ", k, v) } _, node, err := parser.ParseLine(line, &b.directive, false) if err != nil { return "", err } b.dockerfile.Children = append(b.dockerfile.Children, node) } var shortImgID string total := len(b.dockerfile.Children) for _, n := range b.dockerfile.Children { if err := b.checkDispatch(n, false); err != nil { return "", err } } for i, n := range b.dockerfile.Children { select { case <-b.clientCtx.Done(): logrus.Debug("Builder: build cancelled!") fmt.Fprintf(b.Stdout, "Build cancelled") return "", fmt.Errorf("Build cancelled") default: // Not cancelled yet, keep going... } if err := b.dispatch(i, total, n); err != nil { if b.options.ForceRemove { b.clearTmp() } return "", err } shortImgID = stringid.TruncateID(b.image) fmt.Fprintf(b.Stdout, " ---> %s\n", shortImgID) if b.options.Remove { b.clearTmp() } } // check if there are any leftover build-args that were passed but not // consumed during build. Return a warning, if there are any. leftoverArgs := []string{} for arg := range b.options.BuildArgs { if !b.isBuildArgAllowed(arg) { leftoverArgs = append(leftoverArgs, arg) } } if len(leftoverArgs) > 0 { fmt.Fprintf(b.Stderr, "[Warning] One or more build-args %v were not consumed\n", leftoverArgs) } if b.image == "" { return "", fmt.Errorf("No image was generated. Is your Dockerfile empty?") } if b.options.Squash { var fromID string if b.from != nil { fromID = b.from.ImageID() } b.image, err = b.docker.SquashImage(b.image, fromID) if err != nil { return "", perrors.Wrap(err, "error squashing image") } } imageID := image.ID(b.image) for _, rt := range repoAndTags { if err := b.docker.TagImageWithReference(imageID, rt); err != nil { return "", err } } fmt.Fprintf(b.Stdout, "Successfully built %s\n", shortImgID) return b.image, nil }
// build runs the Dockerfile builder from a context and a docker object that allows to make calls // to Docker. // // This will (barring errors): // // * read the dockerfile from context // * parse the dockerfile if not already parsed // * walk the AST and execute it by dispatching to handlers. If Remove // or ForceRemove is set, additional cleanup around containers happens after // processing. // * Tag image, if applicable. // * Print a happy message and return the image ID. // func (b *Builder) build(config *types.ImageBuildOptions, context builder.Context, stdout io.Writer, stderr io.Writer, out io.Writer, clientGone <-chan bool) (string, error) { b.options = config b.context = context b.Stdout = stdout b.Stderr = stderr b.Output = out // If Dockerfile was not parsed yet, extract it from the Context if b.dockerfile == nil { if err := b.readDockerfile(); err != nil { return "", err } } finished := make(chan struct{}) defer close(finished) go func() { select { case <-finished: case <-clientGone: b.cancelOnce.Do(func() { close(b.cancelled) }) } }() repoAndTags, err := sanitizeRepoAndTags(config.Tags) if err != nil { return "", err } if len(b.options.Labels) > 0 { line := "LABEL " for k, v := range b.options.Labels { line += fmt.Sprintf("%q=%q ", k, v) } _, node, err := parser.ParseLine(line) if err != nil { return "", err } b.dockerfile.Children = append(b.dockerfile.Children, node) } var shortImgID string for i, n := range b.dockerfile.Children { select { case <-b.cancelled: logrus.Debug("Builder: build cancelled!") fmt.Fprintf(b.Stdout, "Build cancelled") return "", fmt.Errorf("Build cancelled") default: // Not cancelled yet, keep going... } if err := b.dispatch(i, n); err != nil { if b.options.ForceRemove { b.clearTmp() } return "", err } shortImgID = stringid.TruncateID(b.image) fmt.Fprintf(b.Stdout, " ---> %s\n", shortImgID) if b.options.Remove { b.clearTmp() } } // check if there are any leftover build-args that were passed but not // consumed during build. Return an error, if there are any. leftoverArgs := []string{} for arg := range b.options.BuildArgs { if !b.isBuildArgAllowed(arg) { leftoverArgs = append(leftoverArgs, arg) } } if len(leftoverArgs) > 0 { return "", fmt.Errorf("One or more build-args %v were not consumed, failing build.", leftoverArgs) } if b.image == "" { return "", fmt.Errorf("No image was generated. Is your Dockerfile empty?") } for _, rt := range repoAndTags { if err := b.docker.TagImage(rt, b.image); err != nil { return "", err } } fmt.Fprintf(b.Stdout, "Successfully built %s\n", shortImgID) return b.image, nil }