Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

podman save use named pipe #7073

Merged
merged 1 commit into from
Aug 12, 2020

Conversation

QiWang19
Copy link
Contributor

podman save uses named pipe as output path, not directly using /dev/stdout.
fix #7017

Signed-off-by: Qi Wang [email protected]

@QiWang19
Copy link
Contributor Author

@mtrmac I'm not sure if this works. Could you PTAL?

Copy link
Collaborator

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t quite think this works.

It’s probably worth splitting this into a separate function to keep the main flow fairly simple.

cmd/podman/images/save.go Outdated Show resolved Hide resolved
cmd/podman/images/save.go Show resolved Hide resolved
cmd/podman/images/save.go Outdated Show resolved Hide resolved
cmd/podman/images/save.go Outdated Show resolved Hide resolved
cmd/podman/images/save.go Outdated Show resolved Hide resolved
cmd/podman/images/save.go Outdated Show resolved Hide resolved
cmd/podman/images/save.go Outdated Show resolved Hide resolved
@openshift-ci-robot openshift-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 30, 2020
@openshift-ci-robot openshift-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 31, 2020
Copy link
Collaborator

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Things that should work:

  • The base case of podman save | cat > file or something similar.
  • podman save > /dev/full should reliably fail; so should podman save | read-one-byte-and-exit.
  • podman save | some-very-slow-consumer should reliably receive all of the file (i.e. the save should not terminate until all of the data has been written).

See #7017 (comment) for a broad outline of what I think is necessary to make that work. (But, to be fair, I haven’t written and tested the code.)

cmd/podman/images/save.go Outdated Show resolved Hide resolved
cmd/podman/images/save.go Outdated Show resolved Hide resolved
cmd/podman/images/save.go Outdated Show resolved Hide resolved
cmd/podman/images/save.go Outdated Show resolved Hide resolved
cmd/podman/images/save.go Outdated Show resolved Hide resolved
@QiWang19 QiWang19 force-pushed the save-stdout branch 3 times, most recently from 9549ffd to cb9dbb5 Compare August 4, 2020 20:24
@QiWang19
Copy link
Contributor Author

QiWang19 commented Aug 4, 2020

The documentation example, are you suggesting this should fail?

$ podman save > alpine-all.tar alpine

@mtrmac
Copy link
Collaborator

mtrmac commented Aug 4, 2020

The documentation exampl, are you suggesting this should fail?

$ podman save > alpine-all.tar alpine

I’m not quite sure what this refers to, but no, not in general. I was some ways to trigger failures that should be detected (note that /dev/full is a specially behaving device, not a random meaningless name).

@QiWang19
Copy link
Contributor Author

QiWang19 commented Aug 7, 2020

@mtrmac PTAL

@QiWang19 QiWang19 force-pushed the save-stdout branch 2 times, most recently from 6890d2c to f626be2 Compare August 7, 2020 16:01
cmd/podman/images/save.go Outdated Show resolved Hide resolved
cmd/podman/images/save.go Outdated Show resolved Hide resolved
cmd/podman/images/utils_linux.go Outdated Show resolved Hide resolved
cmd/podman/images/utils_linux.go Show resolved Hide resolved
cmd/podman/images/save.go Outdated Show resolved Hide resolved
@QiWang19 QiWang19 force-pushed the save-stdout branch 2 times, most recently from 78eaf92 to d2578ea Compare August 8, 2020 01:01
return err
}
defer func() {
if err = cleanup(); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICS cleanup() blocks until the pipe is opened and closed, which may never happen on some of the error paths, and this would just hang.

Maybe use context.Context to terminate that wait, or just expose errc and have this function inject a fallback “wait canceled” error that is then ignored in favor of the real cause.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mtrmac PTAL

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how to use context.Context to terminate?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way the cleanup function now returns a channel, a separate termination context might not be necessary:

func save(…) (finalErr error) {
succeeded := falseif /* use pipe */ {
    pipePath, cleanup, err := setupPipe()
    …
    if cleanup != nil {
        defer func() {
            channel := cleanup()
            if succeeded {
                writeErr := <- channel
                if writeErr != nil && finalErr == nil { finalErr = writeErr }
            }
        }
   }
}
…
err := registry.ImageEngine().Save(context.Background(), args[0], tags, saveOpts)
if err == nil { succeeded = true }
return err
}

@QiWang19 QiWang19 force-pushed the save-stdout branch 3 times, most recently from 17c383f to 0e50185 Compare August 10, 2020 20:56
@rhatdan
Copy link
Member

rhatdan commented Aug 11, 2020

/approve

@openshift-ci-robot openshift-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 11, 2020
@@ -6,6 +6,8 @@ import (
"strings"

"github.com/containers/podman/v2/libpod/define"
"github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh/terminal"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you eliminate this empty line.

}
defer func() {
errc := cleanup()
if err := <-errc; err != nil && err.Error() != "wait canceled" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you make "wait canceled" into a constant error, rather then a string. Call it Success.

"io/ioutil"
"os"
"path/filepath"
"syscall"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use golang.org/x/sys/unix instead of syscall

@openshift-ci-robot
Copy link
Collaborator

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: QiWang19, rhatdan

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@rhatdan
Copy link
Member

rhatdan commented Aug 11, 2020

Can you add some tests?

@QiWang19
Copy link
Contributor Author

@mtrmac PTAL, will this avoid the hangs?

@QiWang19
Copy link
Contributor Author

Can you add some tests?

The command needs test is podman save --format oci-archive alpine | cat> test.tar
I haven't foundd ways to test the pipe using ginkgo or in system tests

Copy link
Collaborator

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think this works. Test by replacing the parse.ValidateFileName call with a forced err = "this failed" — the command should not hang, it should remove the temporary directory, and it should return the “this failed” error string.

return
}
fpipe.Close()
errc <- errWaitCancel
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this reporting any error at all when the goroutine has succeeded?

cmd/podman/images/utils_linux.go Show resolved Hide resolved
cmd/podman/images/save.go Outdated Show resolved Hide resolved
@mtrmac
Copy link
Collaborator

mtrmac commented Aug 11, 2020

Can you add some tests?

The command needs test is podman save --format oci-archive alpine | cat> test.tar
I haven't foundd ways to test the pipe using ginkgo or in system tests

run_podman help "$@" |\
seems to suggest it should be possible.

In Go, It might be possible to use os.Pipe + cmd.Exec to run the second command; the PodmanAsUserBase call stack would have to be extended with a stdout parameter. That might not be worth the effort.

@edsantiago
Copy link
Member

seems to suggest it should be possible

Without going into too much gory detail... it's not.The BATS run function redirects stdout and stderr, no choice about it. run_podman, as a courtesy for debugging test failures, echos the captured result to stdout, and that can (with caveats) be used in some cases.

That cannot work for this PR because the critical point is that the podman command itself have the stdout filehandle as a pipe. I have given @QiWang19 a one-time permission to run $PODMAN directly in a new test, without going through run_podman, as long as there is a big scary DO NOT EVER DO THIS warning.

@mtrmac
Copy link
Collaborator

mtrmac commented Aug 11, 2020

seems to suggest it should be possible

Without going into too much gory detail... it's not.

Thanks!

Copy link
Collaborator

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

(I’ll leave the aesthetic nits and tests, if any, up to Podman experts.)

cmd/podman/images/save.go Outdated Show resolved Hide resolved

// setupPipe() for fixing https://github.com/containers/podman/issues/7017
// uses named pipe since containers/image EvalSymlinks fails with /dev/stdout
func setupPipe() (string, func() <-chan error, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: The semantics of the cleanup callback is now complex enough that it might be worth documenting. (OTOH it’s not that much code and it has a single user.)

podman save uses named pipe as output path, not directly using /dev/stdout.
fix containers#7017

Signed-off-by: Qi Wang <[email protected]>
@QiWang19
Copy link
Contributor Author

@TomSweeneyRedHat
Copy link
Member

LGTM
thanks for the work on this @QiWang19 and @mtrmac !

@rhatdan
Copy link
Member

rhatdan commented Aug 12, 2020

/lgtm

@openshift-ci-robot openshift-ci-robot added the lgtm Indicates that a PR is ready to be merged. label Aug 12, 2020
@openshift-merge-robot openshift-merge-robot merged commit d777a7b into containers:master Aug 12, 2020
@github-actions github-actions bot added the locked - please file new issue/PR Assist humans wanting to comment on an old issue or PR with locked comments. label Sep 24, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 24, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged. locked - please file new issue/PR Assist humans wanting to comment on an old issue or PR with locked comments.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

podman save --format=oci-archive fails to write to a pipe
8 participants