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

Added the "--out" parameter and fixed an issue with "--noout" which prevented stdout from being written to. #18657

Merged
merged 1 commit into from
Jun 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions cmd/podman/containers/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,8 @@ var (
)

var (
runOpts = entities.ContainerRunOptions{
OutputStream: os.Stdout,
InputStream: os.Stdin,
ErrorStream: os.Stderr,
}
runRmi bool
runOpts entities.ContainerRunOptions
runRmi bool
)

func runFlags(cmd *cobra.Command) {
Expand Down Expand Up @@ -154,6 +150,11 @@ func run(cmd *cobra.Command, args []string) error {
}
}

// First set the default streams before they get modified by any flags.
runOpts.OutputStream = os.Stdout
runOpts.InputStream = os.Stdin
runOpts.ErrorStream = os.Stderr

// If -i is not set, clear stdin
if !cliVals.Interactive {
runOpts.InputStream = nil
Expand Down
43 changes: 35 additions & 8 deletions cmd/podman/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,24 @@ var (

useSyslog bool
requireCleanup = true
noOut = false

// Defaults for capturing/redirecting the command output since (the) cobra is
// global-hungry and doesn't allow you to attach anything that allows us to
// transform the noStdout BoolVar to a string that we can assign to useStdout.
noStdout = false
useStdout = ""
)

func init() {
// Hooks are called before PersistentPreRunE()
// Hooks are called before PersistentPreRunE(). These hooks affect global
// state and are executed after processing the command-line, but before
// actually running the command.
cobra.OnInitialize(
stdOutHook, // Caution, this hook redirects stdout and output from any following hooks may be affected.
loggingHook,
syslogHook,
earlyInitHook,
configHook,
noOutHook,
)

rootFlags(rootCmd, registry.PodmanConfig())
Expand Down Expand Up @@ -376,10 +383,23 @@ func loggingHook() {
}
}

func noOutHook() {
if noOut {
null, _ := os.Open(os.DevNull)
os.Stdout = null
// used for capturing podman's formatted output to some file as per the -out and -noout flags.
func stdOutHook() {
// if noStdOut was specified, then assign /dev/null as the standard file for output.
if noStdout {
useStdout = os.DevNull
}
// if we were given a filename for output, then open that and use it. we end up leaking
// the file since it's intended to be in scope as long as our process is running.
if useStdout != "" {
if fd, err := os.OpenFile(useStdout, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm); err == nil {
os.Stdout = fd

// if we couldn't open the file for write, then just bail with an error.
} else {
fmt.Fprintf(os.Stderr, "unable to open file for standard output: %s\n", err.Error())
os.Exit(1)
}
}
}

Expand Down Expand Up @@ -415,7 +435,14 @@ func rootFlags(cmd *cobra.Command, podmanConfig *entities.PodmanConfig) {
lFlags.StringVar(&podmanConfig.Identity, identityFlagName, ident, "path to SSH identity file, (CONTAINER_SSHKEY)")
_ = cmd.RegisterFlagCompletionFunc(identityFlagName, completion.AutocompleteDefault)

lFlags.BoolVar(&noOut, "noout", false, "do not output to stdout")
// Flags that control or influence any kind of output.
outFlagName := "out"
lFlags.StringVar(&useStdout, outFlagName, "", "Send output (stdout) from podman to a file")
_ = cmd.RegisterFlagCompletionFunc(outFlagName, completion.AutocompleteDefault)

lFlags.BoolVar(&noStdout, "noout", false, "do not output to stdout")
_ = lFlags.MarkHidden("noout") // Superseded by --out

lFlags.BoolVarP(&podmanConfig.Remote, "remote", "r", registry.IsRemote(), "Access remote Podman service")
pFlags := cmd.PersistentFlags()
if registry.IsRemote() {
Expand Down
5 changes: 2 additions & 3 deletions docs/source/markdown/podman.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,8 @@ and "$graphroot/networks" as rootless.
For the CNI backend the default is "/etc/cni/net.d" as root
and "$HOME/.config/cni/net.d" as rootless. CNI is deprecated from Podman in the future, use netavark.

#### **--noout**

Redirect stdout to /dev/null. This command prevents all stdout from the Podman command. The **--noout** option is not block stderr or stdout from containers.
#### **--out**=*path*
Redirect the output of podman to the specified path without affecting the container output or its logs. This parameter can be used to capture the output from any of podman's commands directly into a file and enable suppression of podman's output by specifying /dev/null as the path. To explicitly disable the container logging, the **--log-driver** option should be used.

#### **--remote**, **-r**
When true, access to the Podman service is remote. Defaults to false.
Expand Down
30 changes: 30 additions & 0 deletions test/system/001-basic.bats
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,36 @@ run_podman --noout system connection ls
is "$output" "" "output should be empty"
}

# Tests --noout to ensure that the output fd can be written to.
@test "podman --noout is actually writing to /dev/null" {
skip_if_remote "unshare only works locally"
skip_if_not_rootless "unshare requires rootless"
run_podman --noout unshare ls
is "$output" "" "output should be empty"
}

@test "podman version --out writes matching version to a json" {
run_podman version

# copypasta from version check. we're doing this to extract the version.
if expr "${lines[0]}" : "Client: *" >/dev/null; then
lines=("${lines[@]:1}")
fi

# get the version number so that we have something to compare with.
IFS=: read version_key version_number <<<"${lines[0]}"
is "$version_key" "Version" "Version line"

# now we can output everything as some json. we can't use PODMAN_TMPDIR since basic_setup
# isn't being used in setup() due to being unable to trust podman-images or podman-rm.
outfile=$(mktemp -p ${BATS_TEST_TMPDIR} veroutXXXXXXXX)
run_podman --out $outfile version -f json

# extract the version from the file.
run jq -r --arg field "$version_key" '.Client | .[$field]' $outfile
is "$output" ${version_number} "Version matches"
}

@test "podman - shutdown engines" {
run_podman --log-level=debug run --rm $IMAGE true
is "$output" ".*Shutting down engines.*"
Expand Down
31 changes: 31 additions & 0 deletions test/system/030-run.bats
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,37 @@ json-file | f
is "$output" "" "output should be empty"
}

@test "podman --out run should save the container id" {
outfile=${PODMAN_TMPDIR}/out-results

# first we'll need to run something, write its output to a file, and then read its contents.
run_podman --out $outfile run -d --name test $IMAGE echo hola
is "$output" "" "output should be redirected"
run_podman wait test

# compare the container id against the one in the file
run_podman container inspect --format '{{.Id}}' test
is "$output" "$(<$outfile)" "container id should match"

run_podman --out /dev/null rm test
is "$output" "" "output should be empty"
}

@test "podman --out create should save the container id" {
outfile=${PODMAN_TMPDIR}/out-results

# first we'll need to run something, write its output to a file, and then read its contents.
run_podman --out $outfile create --name test $IMAGE echo hola
is "$output" "" "output should be redirected"

# compare the container id against the one in the file
run_podman container inspect --format '{{.Id}}' test
is "$output" "$(<$outfile)" "container id should match"

run_podman --out /dev/null rm test
is "$output" "" "output should be empty"
}

# Regression test for issue #8082
@test "podman run : look up correct image name" {
# Create a 2nd tag for the local image. Force to lower case, and apply it.
Expand Down