From d26c9ad7f5a20a161e9d5d7df7d46c06c69ac565 Mon Sep 17 00:00:00 2001 From: Ulysses Souza Date: Tue, 26 Nov 2019 17:14:55 +0100 Subject: [PATCH] Create a global variable to hold output file With a function scoped `os.File`, next time the GC passes the instance is collected, calling the finalizer and triggering the invalidation of the FD, that cannot be used anymore. Signed-off-by: Ulysses Souza --- internal/commands/build/build.go | 35 +++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/internal/commands/build/build.go b/internal/commands/build/build.go index ac2fd7429..c6fade7a6 100644 --- a/internal/commands/build/build.go +++ b/internal/commands/build/build.go @@ -23,6 +23,7 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" compose "github.com/docker/cli/cli/compose/types" + "github.com/docker/cli/cli/streams" "github.com/docker/cnab-to-oci/remotes" "github.com/docker/distribution/reference" "github.com/moby/buildkit/client" @@ -75,6 +76,30 @@ func Cmd(dockerCli command.Cli) *cobra.Command { return cmd } +// FIXME: DO NOT SET THIS VARIABLE DIRECTLY! Use `getOutputFile` +// This global var prevents the file to be garbage collected and by that invalidated +// A an alternative fix for this would be writing the output to a bytes buffer and flushing to stdout. +// The impossibility here is that os.File is not an interface that a buffer can implement. +// Maybe `progress.NewPrinter` should implement an "os.File-like" interface just for its needs. +// See https://github.com/golang/go/issues/14106 +var _outputFile *os.File + +func getOutputFile(realOut *streams.Out, quiet bool) (*os.File, error) { + if _outputFile != nil { + return _outputFile, nil + } + if quiet { + var err error + _outputFile, err = os.Create(os.DevNull) + if err != nil { + return nil, err + } + return _outputFile, nil + } + _outputFile = os.NewFile(realOut.FD(), os.Stdout.Name()) + return _outputFile, nil +} + func runBuild(dockerCli command.Cli, contextPath string, opt buildOptions) error { err := checkMinimalEngineVersion(dockerCli) if err != nil { @@ -160,13 +185,9 @@ func buildImageUsingBuildx(app *types.App, contextPath string, opt buildOptions, }, } - var out *os.File - if opt.quiet { - if out, err = os.Create(os.DevNull); err != nil { - return nil, err - } - } else { - out = os.NewFile(dockerCli.Out().FD(), "/dev/stdout") + out, err := getOutputFile(dockerCli.Out(), opt.quiet) + if err != nil { + return nil, err } pw := progress.NewPrinter(ctx, out, opt.progress)