mirror of
https://github.com/containers/podman-compose.git
synced 2025-05-13 18:55:00 +02:00
Output: use logging, remove garbage output by default
Fixes #489. Introduces a --verbose flag if you want to see all the noise that was previously printed by default. Signed-off-by: James O'Beirne <james.obeirne@pm.me> Signed-off-by: Florian Maurer <f.maurer@outlook.de>
This commit is contained in:
parent
dcb6cdb55a
commit
1e9e2ee776
@ -14,6 +14,7 @@ import glob
|
|||||||
import hashlib
|
import hashlib
|
||||||
import itertools
|
import itertools
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
@ -78,16 +79,7 @@ def try_float(i, fallback=None):
|
|||||||
return fallback
|
return fallback
|
||||||
|
|
||||||
|
|
||||||
def log(*msgs, sep=" ", end="\n"):
|
log = logging.getLogger(__name__)
|
||||||
try:
|
|
||||||
current_task = asyncio.current_task()
|
|
||||||
except RuntimeError:
|
|
||||||
current_task = None
|
|
||||||
line = (sep.join([str(msg) for msg in msgs])) + end
|
|
||||||
if current_task and not current_task.get_name().startswith("Task"):
|
|
||||||
line = f"[{current_task.get_name()}] " + line
|
|
||||||
sys.stderr.write(line)
|
|
||||||
sys.stderr.flush()
|
|
||||||
|
|
||||||
|
|
||||||
dir_re = re.compile(r"^[~/\.]")
|
dir_re = re.compile(r"^[~/\.]")
|
||||||
@ -387,7 +379,7 @@ async def assert_volume(compose, mount_dict):
|
|||||||
proj_name = compose.project_name
|
proj_name = compose.project_name
|
||||||
vol_name = vol["name"]
|
vol_name = vol["name"]
|
||||||
is_ext = vol.get("external", None)
|
is_ext = vol.get("external", None)
|
||||||
log(f"podman volume inspect {vol_name} || podman volume create {vol_name}")
|
log.debug(f"podman volume inspect {vol_name} || podman volume create {vol_name}")
|
||||||
# TODO: might move to using "volume list"
|
# TODO: might move to using "volume list"
|
||||||
# podman volume list --format '{{.Name}}\t{{.MountPoint}}' \
|
# podman volume list --format '{{.Name}}\t{{.MountPoint}}' \
|
||||||
# -f 'label=io.podman.compose.project=HERE'
|
# -f 'label=io.podman.compose.project=HERE'
|
||||||
@ -563,7 +555,7 @@ def get_secret_args(compose, cnt, secret):
|
|||||||
volume_ref = ["--volume", f"{source_file}:{dest_file}:ro,rprivate,rbind"]
|
volume_ref = ["--volume", f"{source_file}:{dest_file}:ro,rprivate,rbind"]
|
||||||
if uid or gid or mode:
|
if uid or gid or mode:
|
||||||
sec = target if target else secret_name
|
sec = target if target else secret_name
|
||||||
log(
|
log.warn(
|
||||||
f'WARNING: Service {cnt["_service"]} uses secret "{sec}" with uid, gid, or mode.'
|
f'WARNING: Service {cnt["_service"]} uses secret "{sec}" with uid, gid, or mode.'
|
||||||
+ " These fields are not supported by this implementation of the Compose file"
|
+ " These fields are not supported by this implementation of the Compose file"
|
||||||
)
|
)
|
||||||
@ -594,7 +586,7 @@ def get_secret_args(compose, cnt, secret):
|
|||||||
if target and target != secret_name:
|
if target and target != secret_name:
|
||||||
raise ValueError(err_str.format(target, secret_name))
|
raise ValueError(err_str.format(target, secret_name))
|
||||||
if target:
|
if target:
|
||||||
log(
|
log.warn(
|
||||||
'WARNING: Service "{}" uses target: "{}" for secret: "{}".'.format(
|
'WARNING: Service "{}" uses target: "{}" for secret: "{}".'.format(
|
||||||
cnt["_service"], target, secret_name
|
cnt["_service"], target, secret_name
|
||||||
)
|
)
|
||||||
@ -778,7 +770,7 @@ def get_net_args(compose, cnt):
|
|||||||
elif net.startswith("bridge"):
|
elif net.startswith("bridge"):
|
||||||
is_bridge = True
|
is_bridge = True
|
||||||
else:
|
else:
|
||||||
print(f"unknown network_mode [{net}]")
|
log.fatal(f"unknown network_mode [{net}]")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
is_bridge = True
|
is_bridge = True
|
||||||
@ -1154,7 +1146,7 @@ class Podman:
|
|||||||
cmd_args = cmd_args or []
|
cmd_args = cmd_args or []
|
||||||
xargs = self.compose.get_podman_args(cmd) if cmd else []
|
xargs = self.compose.get_podman_args(cmd) if cmd else []
|
||||||
cmd_ls = [self.podman_path, *podman_args, cmd] + xargs + cmd_args
|
cmd_ls = [self.podman_path, *podman_args, cmd] + xargs + cmd_args
|
||||||
log(cmd_ls)
|
log.info(str(cmd_ls))
|
||||||
p = await asyncio.subprocess.create_subprocess_exec(
|
p = await asyncio.subprocess.create_subprocess_exec(
|
||||||
*cmd_ls, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
*cmd_ls, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
||||||
)
|
)
|
||||||
@ -1174,7 +1166,7 @@ class Podman:
|
|||||||
cmd_args = list(map(str, cmd_args or []))
|
cmd_args = list(map(str, cmd_args or []))
|
||||||
xargs = self.compose.get_podman_args(cmd) if cmd else []
|
xargs = self.compose.get_podman_args(cmd) if cmd else []
|
||||||
cmd_ls = [self.podman_path, *podman_args, cmd] + xargs + cmd_args
|
cmd_ls = [self.podman_path, *podman_args, cmd] + xargs + cmd_args
|
||||||
log(" ".join([str(i) for i in cmd_ls]))
|
log.info(" ".join([str(i) for i in cmd_ls]))
|
||||||
os.execlp(self.podman_path, *cmd_ls)
|
os.execlp(self.podman_path, *cmd_ls)
|
||||||
|
|
||||||
async def run(
|
async def run(
|
||||||
@ -1191,7 +1183,7 @@ class Podman:
|
|||||||
cmd_args = list(map(str, cmd_args or []))
|
cmd_args = list(map(str, cmd_args or []))
|
||||||
xargs = self.compose.get_podman_args(cmd) if cmd else []
|
xargs = self.compose.get_podman_args(cmd) if cmd else []
|
||||||
cmd_ls = [self.podman_path, *podman_args, cmd] + xargs + cmd_args
|
cmd_ls = [self.podman_path, *podman_args, cmd] + xargs + cmd_args
|
||||||
log(" ".join([str(i) for i in cmd_ls]))
|
log.info(" ".join([str(i) for i in cmd_ls]))
|
||||||
if self.dry_run:
|
if self.dry_run:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -1225,16 +1217,16 @@ class Podman:
|
|||||||
try:
|
try:
|
||||||
exit_code = await p.wait()
|
exit_code = await p.wait()
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
log("Sending termination signal")
|
log.info("Sending termination signal")
|
||||||
p.terminate()
|
p.terminate()
|
||||||
try:
|
try:
|
||||||
exit_code = await wait_with_timeout(p.wait(), 10)
|
exit_code = await wait_with_timeout(p.wait(), 10)
|
||||||
except TimeoutError:
|
except TimeoutError:
|
||||||
log("container did not shut down after 10 seconds, killing")
|
log.warning("container did not shut down after 10 seconds, killing")
|
||||||
p.kill()
|
p.kill()
|
||||||
exit_code = await p.wait()
|
exit_code = await p.wait()
|
||||||
|
|
||||||
log(f"exit code: {exit_code}")
|
log.info(f"exit code: {exit_code}")
|
||||||
return exit_code
|
return exit_code
|
||||||
|
|
||||||
async def volume_ls(self, proj=None):
|
async def volume_ls(self, proj=None):
|
||||||
@ -1482,7 +1474,7 @@ class PodmanCompose:
|
|||||||
missing = given - self.all_services
|
missing = given - self.all_services
|
||||||
if missing:
|
if missing:
|
||||||
missing_csv = ",".join(missing)
|
missing_csv = ",".join(missing)
|
||||||
log(f"missing services [{missing_csv}]")
|
log.warn(f"missing services [{missing_csv}]")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def get_podman_args(self, cmd):
|
def get_podman_args(self, cmd):
|
||||||
@ -1496,7 +1488,7 @@ class PodmanCompose:
|
|||||||
return xargs
|
return xargs
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
log("podman-compose version: " + __version__)
|
log.info("podman-compose version: " + __version__)
|
||||||
args = self._parse_args()
|
args = self._parse_args()
|
||||||
podman_path = args.podman_path
|
podman_path = args.podman_path
|
||||||
if podman_path != "podman":
|
if podman_path != "podman":
|
||||||
@ -1505,7 +1497,7 @@ class PodmanCompose:
|
|||||||
else:
|
else:
|
||||||
# this also works if podman hasn't been installed now
|
# this also works if podman hasn't been installed now
|
||||||
if args.dry_run is False:
|
if args.dry_run is False:
|
||||||
log(f"Binary {podman_path} has not been found.")
|
log.fatal(f"Binary {podman_path} has not been found.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
self.podman = Podman(self, podman_path, args.dry_run, asyncio.Semaphore(args.parallel))
|
self.podman = Podman(self, podman_path, args.dry_run, asyncio.Semaphore(args.parallel))
|
||||||
|
|
||||||
@ -1519,9 +1511,9 @@ class PodmanCompose:
|
|||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
self.podman_version = None
|
self.podman_version = None
|
||||||
if not self.podman_version:
|
if not self.podman_version:
|
||||||
log("it seems that you do not have `podman` installed")
|
log.fatal("it seems that you do not have `podman` installed")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
log("using podman version: " + self.podman_version)
|
log.info("using podman version: " + self.podman_version)
|
||||||
cmd_name = args.command
|
cmd_name = args.command
|
||||||
compose_required = cmd_name != "version" and (
|
compose_required = cmd_name != "version" and (
|
||||||
cmd_name != "systemd" or args.action != "create-unit"
|
cmd_name != "systemd" or args.action != "create-unit"
|
||||||
@ -1549,7 +1541,7 @@ class PodmanCompose:
|
|||||||
args.file = list(filter(os.path.exists, default_ls))
|
args.file = list(filter(os.path.exists, default_ls))
|
||||||
files = args.file
|
files = args.file
|
||||||
if not files:
|
if not files:
|
||||||
log(
|
log.fatal(
|
||||||
"no compose.yaml, docker-compose.yml or container-compose.yml file found, "
|
"no compose.yaml, docker-compose.yml or container-compose.yml file found, "
|
||||||
"pass files with -f"
|
"pass files with -f"
|
||||||
)
|
)
|
||||||
@ -1557,7 +1549,7 @@ class PodmanCompose:
|
|||||||
ex = map(os.path.exists, files)
|
ex = map(os.path.exists, files)
|
||||||
missing = [fn0 for ex0, fn0 in zip(ex, files) if not ex0]
|
missing = [fn0 for ex0, fn0 in zip(ex, files) if not ex0]
|
||||||
if missing:
|
if missing:
|
||||||
log("missing files: ", missing)
|
log.fatal("missing files: %s", missing)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
# make absolute
|
# make absolute
|
||||||
relative_files = files
|
relative_files = files
|
||||||
@ -1635,7 +1627,7 @@ class PodmanCompose:
|
|||||||
compose["_dirname"] = dirname
|
compose["_dirname"] = dirname
|
||||||
# debug mode
|
# debug mode
|
||||||
if len(files) > 1:
|
if len(files) > 1:
|
||||||
log(" ** merged:\n", json.dumps(compose, indent=2))
|
log.debug(" ** merged:\n%s", json.dumps(compose, indent=2))
|
||||||
# ver = compose.get('version', None)
|
# ver = compose.get('version', None)
|
||||||
|
|
||||||
if not project_name:
|
if not project_name:
|
||||||
@ -1656,7 +1648,7 @@ class PodmanCompose:
|
|||||||
services = compose.get("services", None)
|
services = compose.get("services", None)
|
||||||
if services is None:
|
if services is None:
|
||||||
services = {}
|
services = {}
|
||||||
log("WARNING: No services defined")
|
log.warn("WARNING: No services defined")
|
||||||
# include services with no profile defined or the selected profiles
|
# include services with no profile defined or the selected profiles
|
||||||
services = self._resolve_profiles(services, set(args.profile))
|
services = self._resolve_profiles(services, set(args.profile))
|
||||||
|
|
||||||
@ -1689,7 +1681,7 @@ class PodmanCompose:
|
|||||||
unused_nets = given_nets - allnets - set(["default"])
|
unused_nets = given_nets - allnets - set(["default"])
|
||||||
if len(unused_nets):
|
if len(unused_nets):
|
||||||
unused_nets_str = ",".join(unused_nets)
|
unused_nets_str = ",".join(unused_nets)
|
||||||
log(f"WARNING: unused networks: {unused_nets_str}")
|
log.warn(f"WARNING: unused networks: {unused_nets_str}")
|
||||||
if len(missing_nets):
|
if len(missing_nets):
|
||||||
missing_nets_str = ",".join(missing_nets)
|
missing_nets_str = ",".join(missing_nets)
|
||||||
raise RuntimeError(f"missing networks: {missing_nets_str}")
|
raise RuntimeError(f"missing networks: {missing_nets_str}")
|
||||||
@ -1800,6 +1792,8 @@ class PodmanCompose:
|
|||||||
if not self.global_args.command or self.global_args.command == "help":
|
if not self.global_args.command or self.global_args.command == "help":
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
|
logging.basicConfig(level=('DEBUG' if self.global_args.verbose else 'WARN'))
|
||||||
return self.global_args
|
return self.global_args
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -1887,6 +1881,11 @@ class PodmanCompose:
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--parallel", type=int, default=os.environ.get("COMPOSE_PARALLEL_LIMIT", sys.maxsize)
|
"--parallel", type=int, default=os.environ.get("COMPOSE_PARALLEL_LIMIT", sys.maxsize)
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--verbose",
|
||||||
|
help="Print debugging output",
|
||||||
|
action="store_true",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
podman_compose = PodmanCompose()
|
podman_compose = PodmanCompose()
|
||||||
@ -1982,15 +1981,15 @@ async def compose_systemd(compose, args):
|
|||||||
proj_name = compose.project_name
|
proj_name = compose.project_name
|
||||||
fn = os.path.expanduser(f"~/{stacks_dir}/{proj_name}.env")
|
fn = os.path.expanduser(f"~/{stacks_dir}/{proj_name}.env")
|
||||||
os.makedirs(os.path.dirname(fn), exist_ok=True)
|
os.makedirs(os.path.dirname(fn), exist_ok=True)
|
||||||
print(f"writing [{fn}]: ...")
|
log.debug(f"writing [{fn}]: ...")
|
||||||
with open(fn, "w", encoding="utf-8") as f:
|
with open(fn, "w", encoding="utf-8") as f:
|
||||||
for k, v in compose.environ.items():
|
for k, v in compose.environ.items():
|
||||||
if k.startswith("COMPOSE_") or k.startswith("PODMAN_"):
|
if k.startswith("COMPOSE_") or k.startswith("PODMAN_"):
|
||||||
f.write(f"{k}={v}\n")
|
f.write(f"{k}={v}\n")
|
||||||
print(f"writing [{fn}]: done.")
|
log.debug(f"writing [{fn}]: done.")
|
||||||
print("\n\ncreating the pod without starting it: ...\n\n")
|
log.info("\n\ncreating the pod without starting it: ...\n\n")
|
||||||
process = await asyncio.subprocess.create_subprocess_exec(script, ["up", "--no-start"])
|
process = await asyncio.subprocess.create_subprocess_exec(script, ["up", "--no-start"])
|
||||||
print("\nfinal exit code is ", process)
|
log.info("\nfinal exit code is ", process)
|
||||||
username = getpass.getuser()
|
username = getpass.getuser()
|
||||||
print(
|
print(
|
||||||
f"""
|
f"""
|
||||||
@ -2037,10 +2036,10 @@ ExecStop=/usr/bin/podman pod stop pod_%i
|
|||||||
WantedBy=default.target
|
WantedBy=default.target
|
||||||
"""
|
"""
|
||||||
if os.access(os.path.dirname(fn), os.W_OK):
|
if os.access(os.path.dirname(fn), os.W_OK):
|
||||||
print(f"writing [{fn}]: ...")
|
log.debug(f"writing [{fn}]: ...")
|
||||||
with open(fn, "w", encoding="utf-8") as f:
|
with open(fn, "w", encoding="utf-8") as f:
|
||||||
f.write(out)
|
f.write(out)
|
||||||
print(f"writing [{fn}]: done.")
|
log.debug(f"writing [{fn}]: done.")
|
||||||
print(
|
print(
|
||||||
"""
|
"""
|
||||||
while in your project type `podman-compose systemd -a register`
|
while in your project type `podman-compose systemd -a register`
|
||||||
@ -2048,7 +2047,7 @@ while in your project type `podman-compose systemd -a register`
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
print(out)
|
print(out)
|
||||||
log(f"Could not write to [{fn}], use 'sudo'")
|
log.warn(f"Could not write to [{fn}], use 'sudo'")
|
||||||
|
|
||||||
|
|
||||||
@cmd_run(podman_compose, "pull", "pull stack images")
|
@cmd_run(podman_compose, "pull", "pull stack images")
|
||||||
@ -2188,7 +2187,7 @@ def get_excluded(compose, args):
|
|||||||
for service in args.services:
|
for service in args.services:
|
||||||
excluded -= compose.services[service]["_deps"]
|
excluded -= compose.services[service]["_deps"]
|
||||||
excluded.discard(service)
|
excluded.discard(service)
|
||||||
log("** excluding: ", excluded)
|
log.debug("** excluding: %s", excluded)
|
||||||
return excluded
|
return excluded
|
||||||
|
|
||||||
|
|
||||||
@ -2221,10 +2220,10 @@ async def compose_up(compose: PodmanCompose, args):
|
|||||||
)
|
)
|
||||||
diff_hashes = [i for i in hashes if i and i != compose.yaml_hash]
|
diff_hashes = [i for i in hashes if i and i != compose.yaml_hash]
|
||||||
if args.force_recreate or len(diff_hashes):
|
if args.force_recreate or len(diff_hashes):
|
||||||
log("recreating: ...")
|
log.info("recreating: ...")
|
||||||
down_args = argparse.Namespace(**dict(args.__dict__, volumes=False))
|
down_args = argparse.Namespace(**dict(args.__dict__, volumes=False))
|
||||||
await compose.commands["down"](compose, down_args)
|
await compose.commands["down"](compose, down_args)
|
||||||
log("recreating: done\n\n")
|
log.info("recreating: done\n\n")
|
||||||
# args.no_recreate disables check for changes (which is not implemented)
|
# args.no_recreate disables check for changes (which is not implemented)
|
||||||
|
|
||||||
podman_command = "run" if args.detach and not args.no_start else "create"
|
podman_command = "run" if args.detach and not args.no_start else "create"
|
||||||
@ -2232,7 +2231,7 @@ async def compose_up(compose: PodmanCompose, args):
|
|||||||
await create_pods(compose, args)
|
await create_pods(compose, args)
|
||||||
for cnt in compose.containers:
|
for cnt in compose.containers:
|
||||||
if cnt["_service"] in excluded:
|
if cnt["_service"] in excluded:
|
||||||
log("** skipping: ", cnt["name"])
|
log.debug("** skipping: %s", cnt["name"])
|
||||||
continue
|
continue
|
||||||
podman_args = await container_to_args(compose, cnt, detached=args.detach)
|
podman_args = await container_to_args(compose, cnt, detached=args.detach)
|
||||||
subproc = await compose.podman.run([], podman_command, podman_args)
|
subproc = await compose.podman.run([], podman_command, podman_args)
|
||||||
@ -2264,7 +2263,7 @@ async def compose_up(compose: PodmanCompose, args):
|
|||||||
space_suffix = " " * (max_service_length - len(cnt["_service"]) + 1)
|
space_suffix = " " * (max_service_length - len(cnt["_service"]) + 1)
|
||||||
log_formatter = "{}[{}]{}|\x1b[0m".format(color, cnt["_service"], space_suffix)
|
log_formatter = "{}[{}]{}|\x1b[0m".format(color, cnt["_service"], space_suffix)
|
||||||
if cnt["_service"] in excluded:
|
if cnt["_service"] in excluded:
|
||||||
log("** skipping: ", cnt["name"])
|
log.debug("** skipping: %s", cnt["name"])
|
||||||
continue
|
continue
|
||||||
|
|
||||||
tasks.add(
|
tasks.add(
|
||||||
@ -2368,7 +2367,7 @@ async def compose_down(compose, args):
|
|||||||
if cnt["_service"] not in excluded:
|
if cnt["_service"] not in excluded:
|
||||||
continue
|
continue
|
||||||
vol_names_to_keep.update(get_volume_names(compose, cnt))
|
vol_names_to_keep.update(get_volume_names(compose, cnt))
|
||||||
log("keep", vol_names_to_keep)
|
log.debug("keep", vol_names_to_keep)
|
||||||
for volume_name in await compose.podman.volume_ls():
|
for volume_name in await compose.podman.volume_ls():
|
||||||
if volume_name in vol_names_to_keep:
|
if volume_name in vol_names_to_keep:
|
||||||
continue
|
continue
|
||||||
@ -2643,7 +2642,7 @@ async def compose_unpause(compose, args):
|
|||||||
async def compose_kill(compose, args):
|
async def compose_kill(compose, args):
|
||||||
# to ensure that the user did not execute the command by mistake
|
# to ensure that the user did not execute the command by mistake
|
||||||
if not args.services and not args.all:
|
if not args.services and not args.all:
|
||||||
print(
|
log.fatal(
|
||||||
"Error: you must provide at least one service name or use (--all) to kill all services"
|
"Error: you must provide at least one service name or use (--all) to kill all services"
|
||||||
)
|
)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
Loading…
Reference in New Issue
Block a user