Skip to content

Commit 70d5af3

Browse files
committed
Increase compatibility for terminal/no-terminal options
Increase compatibility with docker compose for exec and run commands: - Add support for long option `--no-tty` besides `-T`. - Add support for hidden options `-t` and `--tty`. - Make default for tty auto-detected. - For the `exec` command: run `podman exec` without `--interactive` when no-tty is specified (the `run` command already does this). - For `run`, fix adding --rm option when detached. The default for terminal/no-terminal is based on terminal detection, mimicking docker compose code for [run](https://github.com/docker/compose/blob/3371227794f5f3645f4f19829c60a741635ed329/cmd/compose/run.go#L178) and [exec](https://github.com/docker/compose/blob/3371227794f5f3645f4f19829c60a741635ed329/cmd/compose/exec.go#L72). Hidden options `-t` and `--tty` are mimicking docker compose code for [run](https://github.com/docker/compose/blob/3371227794f5f3645f4f19829c60a741635ed329/cmd/compose/run.go#L195-L196) and [exec](https://github.com/docker/compose/blob/3371227794f5f3645f4f19829c60a741635ed329/cmd/compose/exec.go#L77-L78). NOTE: some of the added integration tests require the `script` command to emulate a TTY. This command is available in most unix environments, but might not be present in a minimal docker images. When `script` is not available, these tests pass without doing anything. Fixes #900. Signed-off-by: Erik van Oosten <e.vanoosten@grons.nl>
1 parent 262b4ed commit 70d5af3

13 files changed

Lines changed: 254 additions & 21 deletions

completion/bash/podman-compose

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ _completeUpArgs() {
7373

7474
# complete the arguments for `podman-compose exec` and return 0
7575
_completeExecArgs() {
76-
exec_opts="${help_opts} -d --detach --privileged -u --user -T --index -e --env -w --workdir"
76+
exec_opts="${help_opts} -d --detach --privileged -u --user -T --no-tty --index -e --env -w --workdir"
7777
if [[ ${prev} == "-u" || ${prev} == "--user" || ${prev} == "--index" || ${prev} == "-e" || ${prev} == "--env" || ${prev} == "-w" || ${prev} == "--workdir" ]]; then
7878
return 0
7979
elif [[ ${cur} == -* ]]; then
@@ -237,7 +237,7 @@ _completeStartArgs() {
237237

238238
# complete the arguments for `podman-compose run` and return 0
239239
_completeRunArgs() {
240-
run_opts="${help_opts} -d --detach --privileged -u --user -T --index -e --env -w --workdir"
240+
run_opts="${help_opts} -d --detach --privileged -u --user -T --no-tty --index -e --env -w --workdir"
241241
if [[ ${prev} == "-u" || ${prev} == "--user" || ${prev} == "--index" || ${prev} == "-e" || ${prev} == "--env" || ${prev} == "-w" || ${prev} == "--workdir" ]]; then
242242
return 0
243243
elif [[ ${cur} == -* ]]; then
@@ -267,7 +267,7 @@ _podmanCompose() {
267267
help_opts="-h --help"
268268

269269
# global options that don't take additional arguments
270-
basic_global_opts="${help_opts} -v --no-ansi --no-cleanup --dry-run"
270+
basic_global_opts="${help_opts} -v --no-ansi --no-cleanup --dry-run"
271271

272272
# global options that take paths as arguments
273273
path_arg_global_opts="-f --file --podman-path"
@@ -291,7 +291,7 @@ _podmanCompose() {
291291
if [[ $? -eq 0 ]]; then
292292
return 0
293293
fi
294-
294+
295295
# computing comp_cword_adj, which thruthfully tells us how deep in the subcommands tree we are
296296
# additionally, set the chosen_root_command if possible
297297
comp_cword_adj=${COMP_CWORD}
@@ -309,7 +309,7 @@ _podmanCompose() {
309309
fi
310310
if [[ ${el} == -* && ${el} != ${cur} ]]; then
311311
let "comp_cword_adj--"
312-
312+
313313
for opt in ${arg_global_opts_array[@]}; do
314314
if [[ ${el} == ${opt} ]]; then
315315
skip_next="yes"
@@ -320,7 +320,7 @@ _podmanCompose() {
320320
fi
321321
done
322322
fi
323-
323+
324324
if [[ ${comp_cword_adj} -eq 1 ]]; then
325325
_completeRoot
326326

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Increased compatibility with exec/run parameters `-T`, `--no-tty`, `-t`, and `-tty`, default is now detected

podman_compose.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1428,7 +1428,7 @@ async def container_to_args(
14281428
podman_args.extend(["--hostname", cnt["hostname"]])
14291429
if cnt.get("shm_size"):
14301430
podman_args.extend(["--shm-size", str(cnt["shm_size"])])
1431-
if cnt.get("stdin_open"):
1431+
if cnt.get("tty") and cnt.get("stdin_open"):
14321432
podman_args.append("-i")
14331433
if cnt.get("stop_signal"):
14341434
podman_args.extend(["--stop-signal", cnt["stop_signal"]])
@@ -4078,10 +4078,10 @@ async def compose_run(compose: PodmanCompose, args: argparse.Namespace) -> None:
40784078
compose_run_update_container_from_args(compose, cnt, args)
40794079
# run podman
40804080
podman_args = await container_to_args(compose, cnt, args.detach, args.no_deps)
4081-
if not args.detach:
4081+
if args.tty and not args.detach:
40824082
podman_args.insert(1, "-i")
4083-
if args.rm:
4084-
podman_args.insert(1, "--rm")
4083+
if args.rm:
4084+
podman_args.insert(1, "--rm")
40854085
p = await compose.podman.run([], "run", podman_args)
40864086
sys.exit(p)
40874087

@@ -4118,7 +4118,7 @@ def compose_run_update_container_from_args(
41184118
volumes = clone(cnt.get("volumes", []))
41194119
volumes.extend(args.volume)
41204120
cnt["volumes"] = volumes
4121-
cnt["tty"] = not args.T
4121+
cnt["tty"] = args.tty
41224122
if args.cnt_command is not None and len(args.cnt_command) > 0:
41234123
cnt["command"] = args.cnt_command
41244124
# can't restart and --rm
@@ -4138,14 +4138,15 @@ async def compose_exec(compose: PodmanCompose, args: argparse.Namespace) -> None
41384138

41394139

41404140
def compose_exec_args(cnt: dict, container_name: str, args: argparse.Namespace) -> list[str]:
4141-
podman_args = ["--interactive"]
4141+
podman_args = []
41424142
if args.privileged:
41434143
podman_args += ["--privileged"]
41444144
if args.user:
41454145
podman_args += ["--user", args.user]
41464146
if args.workdir:
41474147
podman_args += ["--workdir", args.workdir]
4148-
if not args.T:
4148+
if args.tty:
4149+
podman_args += ["--interactive"]
41494150
podman_args += ["--tty"]
41504151
env = dict(cnt.get("environment", {}))
41514152
if args.env:
@@ -4607,11 +4608,16 @@ def compose_run_parse(parser: argparse.ArgumentParser) -> None:
46074608
action="append",
46084609
help="Bind mount a volume (can be used multiple times)",
46094610
)
4610-
parser.add_argument(
4611+
tty_parser = parser.add_mutually_exclusive_group(required=False)
4612+
tty_parser.add_argument(
46114613
"-T",
4612-
action="store_true",
4613-
help="Disable pseudo-tty allocation. By default `podman-compose run` allocates a TTY.",
4614+
"--no-tty",
4615+
dest="tty",
4616+
action="store_false",
4617+
help="Disable pseudo-TTY allocation (default: auto-detected)",
46144618
)
4619+
tty_parser.add_argument("-t", "--tty", dest="tty", action="store_true", help=argparse.SUPPRESS)
4620+
parser.set_defaults(tty=sys.stdout.isatty())
46154621
parser.add_argument(
46164622
"-w",
46174623
"--workdir",
@@ -4645,11 +4651,16 @@ def compose_exec_parse(parser: argparse.ArgumentParser) -> None:
46454651
parser.add_argument(
46464652
"-u", "--user", type=str, default=None, help="Run as specified username or uid"
46474653
)
4648-
parser.add_argument(
4654+
tty_parser = parser.add_mutually_exclusive_group(required=False)
4655+
tty_parser.add_argument(
46494656
"-T",
4650-
action="store_true",
4651-
help="Disable pseudo-tty allocation. By default `podman-compose run` allocates a TTY.",
4657+
"--no-tty",
4658+
dest="tty",
4659+
action="store_false",
4660+
help="Disable pseudo-TTY allocation (default: auto-detected)",
46524661
)
4662+
tty_parser.add_argument("-t", "--tty", dest="tty", action="store_true", help=argparse.SUPPRESS)
4663+
parser.set_defaults(tty=sys.stdout.isatty())
46534664
parser.add_argument(
46544665
"--index",
46554666
type=int,

tests/integration/exec_tty/__init__.py

Whitespace-only changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FROM nopush/podman-compose-test
2+
3+
CMD ["sleep", "120"]
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
version: "3"
2+
services:
3+
web1:
4+
build:
5+
context: ./context
6+
dockerfile: Dockerfile
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
3+
import os
4+
import subprocess
5+
import unittest
6+
7+
from tests.integration.test_utils import RunSubprocessMixin
8+
from tests.integration.test_utils import podman_compose_path
9+
from tests.integration.test_utils import test_path
10+
11+
12+
def compose_yaml_path() -> str:
13+
"""Returns the path to the compose file used for this test module"""
14+
base_path = os.path.join(test_path(), "exec_tty")
15+
return os.path.join(base_path, "docker-compose.yml")
16+
17+
18+
class TestComposeExtraHosts(unittest.TestCase, RunSubprocessMixin):
19+
def test_exec(self) -> None:
20+
script_installed = (
21+
subprocess.run(["script", "--version"], stdout=subprocess.DEVNULL).returncode == 0
22+
)
23+
24+
try:
25+
self.run_subprocess_assert_returncode([
26+
podman_compose_path(),
27+
"-f",
28+
compose_yaml_path(),
29+
"build",
30+
"--no-cache",
31+
])
32+
33+
self.run_subprocess_assert_returncode([
34+
podman_compose_path(),
35+
"-f",
36+
compose_yaml_path(),
37+
"up",
38+
"-d",
39+
])
40+
41+
# TTY auto detected (no tty)
42+
self.run_subprocess_assert_returncode(
43+
[
44+
podman_compose_path(),
45+
"-f",
46+
compose_yaml_path(),
47+
"exec",
48+
"web1",
49+
"tty",
50+
"-s",
51+
],
52+
expected_returncode=1, # exit code 1 == no tty
53+
)
54+
55+
# TTY auto detected (tty emulated by 'script' command)
56+
if script_installed:
57+
self.run_subprocess_assert_returncode(
58+
[
59+
"script",
60+
"--return",
61+
"--quiet",
62+
"--log-out",
63+
"/dev/null",
64+
"--command",
65+
f"{podman_compose_path()} -f {compose_yaml_path()} exec web1 tty -s",
66+
],
67+
expected_returncode=0, # exit code 0 == tty
68+
)
69+
70+
# TTY disabled (even though an emulated tty is available through the 'script' command)
71+
if script_installed:
72+
self.run_subprocess_assert_returncode(
73+
[
74+
"script",
75+
"--return",
76+
"--log-out",
77+
"/dev/null",
78+
"--quiet",
79+
"--command",
80+
f"{podman_compose_path()} "
81+
f"-f {compose_yaml_path()} "
82+
"exec --no-tty web1 tty -s",
83+
],
84+
expected_returncode=1, # exit code 1 == no tty
85+
)
86+
87+
# TTY enabled (tty emulated by 'script' command)
88+
if script_installed:
89+
self.run_subprocess_assert_returncode(
90+
[
91+
"script",
92+
"--return",
93+
"--log-out",
94+
"/dev/null",
95+
"--quiet",
96+
"--command",
97+
f"{podman_compose_path()} -f {compose_yaml_path()} exec --tty web1 tty -s",
98+
],
99+
expected_returncode=0, # exit code 0 == tty
100+
)
101+
102+
finally:
103+
self.run_subprocess_assert_returncode([
104+
podman_compose_path(),
105+
"-f",
106+
compose_yaml_path(),
107+
"down",
108+
])

tests/integration/run_tty/__init__.py

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
FROM nopush/podman-compose-test
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
version: "3"
2+
services:
3+
web1:
4+
build:
5+
context: ./context
6+
dockerfile: Dockerfile

0 commit comments

Comments
 (0)