diff --git a/cmd/buildah/build.go b/cmd/buildah/build.go index 4157df001..3e155f44a 100644 --- a/cmd/buildah/build.go +++ b/cmd/buildah/build.go @@ -100,6 +100,7 @@ func getContainerfiles(files []string) []string { func buildCmd(c *cobra.Command, inputArgs []string, iopts buildOptions) error { output := "" + cleanTmpFile := false tags := []string{} if c.Flag("tag").Changed { tags = iopts.Tag @@ -111,6 +112,10 @@ func buildCmd(c *cobra.Command, inputArgs []string, iopts buildOptions) error { if err := auth.CheckAuthFile(iopts.BudResults.Authfile); err != nil { return err } + iopts.BudResults.Authfile, cleanTmpFile = buildahutil.MirrorToTempFileIfPathIsDescriptor(iopts.BudResults.Authfile) + if cleanTmpFile { + defer os.Remove(iopts.BudResults.Authfile) + } pullPolicy := define.PullIfMissing if iopts.Pull { diff --git a/pkg/util/util.go b/pkg/util/util.go index 8410d3984..209ad9544 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -1,12 +1,44 @@ package util import ( + "io/ioutil" "os" "path/filepath" + "strings" "github.com/pkg/errors" ) +// Mirrors path to a tmpfile if path points to a +// file descriptor instead of actual file on filesystem +// reason: operations with file descriptors are can lead +// to edge cases where content on FD is not in a consumable +// state after first consumption. +// returns path as string and bool to confirm if temp file +// was created and needs to be cleaned up. +func MirrorToTempFileIfPathIsDescriptor(file string) (string, bool) { + // one use-case is discussed here + // https://github.com/containers/buildah/issues/3070 + if !strings.HasPrefix(file, "/dev/fd") { + return file, false + } + b, err := ioutil.ReadFile(file) + if err != nil { + // if anything goes wrong return original path + return file, false + } + tmpfile, err := ioutil.TempFile(os.TempDir(), "buildah-temp-file") + if err != nil { + return file, false + } + if _, err := tmpfile.Write(b); err != nil { + // if anything goes wrong return original path + return file, false + } + + return tmpfile.Name(), true +} + // DiscoverContainerfile tries to find a Containerfile or a Dockerfile within the provided `path`. func DiscoverContainerfile(path string) (foundCtrFile string, err error) { // Test for existence of the file diff --git a/tests/bud.bats b/tests/bud.bats index aa9a59c5b..7b9adc43e 100644 --- a/tests/bud.bats +++ b/tests/bud.bats @@ -2261,6 +2261,14 @@ _EOF run_buildah 125 build --authfile /tmp/nonexistent --signature-policy ${TESTSDIR}/policy.json -t ${target} ${TESTSDIR}/bud/containerfile } + +@test "bud for multi-stage Containerfile with invalid registry and --authfile as a fd, should fail with no such host" { + target=alpine-multi-stage-image + run_buildah 125 build --authfile=<(echo "{ \"auths\": { \"myrepository.example\": { \"auth\": \"$(echo 'username:password' | base64 --wrap=0)\" } } }") -t ${target} --file ${TESTSDIR}/bud/from-invalid-registry/Containerfile + # Should fail with `no such host` instead of: error reading JSON file "/dev/fd/x" + expect_output --substring "no such host" +} + @test "bud COPY with URL should fail" { mkdir ${TESTSDIR}/bud/copy FILE=${TESTSDIR}/bud/copy/Dockerfile.url diff --git a/tests/bud/from-invalid-registry/Containerfile b/tests/bud/from-invalid-registry/Containerfile new file mode 100644 index 000000000..00dc3081f --- /dev/null +++ b/tests/bud/from-invalid-registry/Containerfile @@ -0,0 +1,3 @@ +FROM alpine as build +# Invalid registry and image +FROM myrepository.example/image:tag