1414import hashlib
1515import itertools
1616import json
17+ import logging
1718import os
1819import random
1920import re
@@ -78,16 +79,7 @@ def try_float(i, fallback=None):
7879 return fallback
7980
8081
81- def log (* msgs , sep = " " , end = "\n " ):
82- try :
83- current_task = asyncio .current_task ()
84- except RuntimeError :
85- current_task = None
86- line = (sep .join ([str (msg ) for msg in msgs ])) + end
87- if current_task and not current_task .get_name ().startswith ("Task" ):
88- line = f"[{ current_task .get_name ()} ] " + line
89- sys .stderr .write (line )
90- sys .stderr .flush ()
82+ log = logging .getLogger (__name__ )
9183
9284
9385dir_re = re .compile (r"^[~/\.]" )
@@ -387,7 +379,7 @@ async def assert_volume(compose, mount_dict):
387379 proj_name = compose .project_name
388380 vol_name = vol ["name" ]
389381 is_ext = vol .get ("external" , None )
390- log (f"podman volume inspect { vol_name } || podman volume create { vol_name } " )
382+ log . debug (f"podman volume inspect { vol_name } || podman volume create { vol_name } " )
391383 # TODO: might move to using "volume list"
392384 # podman volume list --format '{{.Name}}\t{{.MountPoint}}' \
393385 # -f 'label=io.podman.compose.project=HERE'
@@ -563,7 +555,7 @@ def get_secret_args(compose, cnt, secret):
563555 volume_ref = ["--volume" , f"{ source_file } :{ dest_file } :ro,rprivate,rbind" ]
564556 if uid or gid or mode :
565557 sec = target if target else secret_name
566- log (
558+ log . warn (
567559 f'WARNING: Service { cnt ["_service" ]} uses secret "{ sec } " with uid, gid, or mode.'
568560 + " These fields are not supported by this implementation of the Compose file"
569561 )
@@ -594,7 +586,7 @@ def get_secret_args(compose, cnt, secret):
594586 if target and target != secret_name :
595587 raise ValueError (err_str .format (target , secret_name ))
596588 if target :
597- log (
589+ log . warn (
598590 'WARNING: Service "{}" uses target: "{}" for secret: "{}".' .format (
599591 cnt ["_service" ], target , secret_name
600592 )
@@ -778,7 +770,7 @@ def get_net_args(compose, cnt):
778770 elif net .startswith ("bridge" ):
779771 is_bridge = True
780772 else :
781- print (f"unknown network_mode [{ net } ]" )
773+ log . fatal (f"unknown network_mode [{ net } ]" )
782774 sys .exit (1 )
783775 else :
784776 is_bridge = True
@@ -1154,7 +1146,7 @@ async def output(self, podman_args, cmd="", cmd_args=None):
11541146 cmd_args = cmd_args or []
11551147 xargs = self .compose .get_podman_args (cmd ) if cmd else []
11561148 cmd_ls = [self .podman_path , * podman_args , cmd ] + xargs + cmd_args
1157- log ( cmd_ls )
1149+ log . info ( str ( cmd_ls ) )
11581150 p = await asyncio .subprocess .create_subprocess_exec (
11591151 * cmd_ls , stdout = asyncio .subprocess .PIPE , stderr = asyncio .subprocess .PIPE
11601152 )
@@ -1174,7 +1166,7 @@ def exec(
11741166 cmd_args = list (map (str , cmd_args or []))
11751167 xargs = self .compose .get_podman_args (cmd ) if cmd else []
11761168 cmd_ls = [self .podman_path , * podman_args , cmd ] + xargs + cmd_args
1177- log (" " .join ([str (i ) for i in cmd_ls ]))
1169+ log . info (" " .join ([str (i ) for i in cmd_ls ]))
11781170 os .execlp (self .podman_path , * cmd_ls )
11791171
11801172 async def run (
@@ -1191,7 +1183,7 @@ async def run(
11911183 cmd_args = list (map (str , cmd_args or []))
11921184 xargs = self .compose .get_podman_args (cmd ) if cmd else []
11931185 cmd_ls = [self .podman_path , * podman_args , cmd ] + xargs + cmd_args
1194- log (" " .join ([str (i ) for i in cmd_ls ]))
1186+ log . info (" " .join ([str (i ) for i in cmd_ls ]))
11951187 if self .dry_run :
11961188 return None
11971189
@@ -1225,16 +1217,16 @@ async def format_out(stdout):
12251217 try :
12261218 exit_code = await p .wait ()
12271219 except asyncio .CancelledError :
1228- log ("Sending termination signal" )
1220+ log . info ("Sending termination signal" )
12291221 p .terminate ()
12301222 try :
12311223 exit_code = await wait_with_timeout (p .wait (), 10 )
12321224 except TimeoutError :
1233- log ("container did not shut down after 10 seconds, killing" )
1225+ log . warning ("container did not shut down after 10 seconds, killing" )
12341226 p .kill ()
12351227 exit_code = await p .wait ()
12361228
1237- log (f"exit code: { exit_code } " )
1229+ log . info (f"exit code: { exit_code } " )
12381230 return exit_code
12391231
12401232 async def volume_ls (self , proj = None ):
@@ -1482,7 +1474,7 @@ def assert_services(self, services):
14821474 missing = given - self .all_services
14831475 if missing :
14841476 missing_csv = "," .join (missing )
1485- log (f"missing services [{ missing_csv } ]" )
1477+ log . warn (f"missing services [{ missing_csv } ]" )
14861478 sys .exit (1 )
14871479
14881480 def get_podman_args (self , cmd ):
@@ -1496,7 +1488,7 @@ def get_podman_args(self, cmd):
14961488 return xargs
14971489
14981490 async def run (self ):
1499- log ("podman-compose version: " + __version__ )
1491+ log . info ("podman-compose version: " + __version__ )
15001492 args = self ._parse_args ()
15011493 podman_path = args .podman_path
15021494 if podman_path != "podman" :
@@ -1505,7 +1497,7 @@ async def run(self):
15051497 else :
15061498 # this also works if podman hasn't been installed now
15071499 if args .dry_run is False :
1508- log (f"Binary { podman_path } has not been found." )
1500+ log . fatal (f"Binary { podman_path } has not been found." )
15091501 sys .exit (1 )
15101502 self .podman = Podman (self , podman_path , args .dry_run , asyncio .Semaphore (args .parallel ))
15111503
@@ -1519,9 +1511,9 @@ async def run(self):
15191511 except subprocess .CalledProcessError :
15201512 self .podman_version = None
15211513 if not self .podman_version :
1522- log ("it seems that you do not have `podman` installed" )
1514+ log . fatal ("it seems that you do not have `podman` installed" )
15231515 sys .exit (1 )
1524- log ("using podman version: " + self .podman_version )
1516+ log . info ("using podman version: " + self .podman_version )
15251517 cmd_name = args .command
15261518 compose_required = cmd_name != "version" and (
15271519 cmd_name != "systemd" or args .action != "create-unit"
@@ -1549,15 +1541,15 @@ def _parse_compose_file(self):
15491541 args .file = list (filter (os .path .exists , default_ls ))
15501542 files = args .file
15511543 if not files :
1552- log (
1544+ log . fatal (
15531545 "no compose.yaml, docker-compose.yml or container-compose.yml file found, "
15541546 "pass files with -f"
15551547 )
15561548 sys .exit (- 1 )
15571549 ex = map (os .path .exists , files )
15581550 missing = [fn0 for ex0 , fn0 in zip (ex , files ) if not ex0 ]
15591551 if missing :
1560- log ("missing files: " , missing )
1552+ log . fatal ("missing files: %s " , missing )
15611553 sys .exit (1 )
15621554 # make absolute
15631555 relative_files = files
@@ -1635,7 +1627,7 @@ def _parse_compose_file(self):
16351627 compose ["_dirname" ] = dirname
16361628 # debug mode
16371629 if len (files ) > 1 :
1638- log (" ** merged:\n " , json .dumps (compose , indent = 2 ))
1630+ log . debug (" ** merged:\n %s " , json .dumps (compose , indent = 2 ))
16391631 # ver = compose.get('version', None)
16401632
16411633 if not project_name :
@@ -1656,7 +1648,7 @@ def _parse_compose_file(self):
16561648 services = compose .get ("services" , None )
16571649 if services is None :
16581650 services = {}
1659- log ("WARNING: No services defined" )
1651+ log . warn ("WARNING: No services defined" )
16601652 # include services with no profile defined or the selected profiles
16611653 services = self ._resolve_profiles (services , set (args .profile ))
16621654
@@ -1689,7 +1681,7 @@ def _parse_compose_file(self):
16891681 unused_nets = given_nets - allnets - set (["default" ])
16901682 if len (unused_nets ):
16911683 unused_nets_str = "," .join (unused_nets )
1692- log (f"WARNING: unused networks: { unused_nets_str } " )
1684+ log . warn (f"WARNING: unused networks: { unused_nets_str } " )
16931685 if len (missing_nets ):
16941686 missing_nets_str = "," .join (missing_nets )
16951687 raise RuntimeError (f"missing networks: { missing_nets_str } " )
@@ -1800,6 +1792,8 @@ def _parse_args(self):
18001792 if not self .global_args .command or self .global_args .command == "help" :
18011793 parser .print_help ()
18021794 sys .exit (- 1 )
1795+
1796+ logging .basicConfig (level = ('DEBUG' if self .global_args .verbose else 'WARN' ))
18031797 return self .global_args
18041798
18051799 @staticmethod
@@ -1887,6 +1881,11 @@ def _init_global_parser(parser):
18871881 parser .add_argument (
18881882 "--parallel" , type = int , default = os .environ .get ("COMPOSE_PARALLEL_LIMIT" , sys .maxsize )
18891883 )
1884+ parser .add_argument (
1885+ "--verbose" ,
1886+ help = "Print debugging output" ,
1887+ action = "store_true" ,
1888+ )
18901889
18911890
18921891podman_compose = PodmanCompose ()
@@ -1982,15 +1981,15 @@ async def compose_systemd(compose, args):
19821981 proj_name = compose .project_name
19831982 fn = os .path .expanduser (f"~/{ stacks_dir } /{ proj_name } .env" )
19841983 os .makedirs (os .path .dirname (fn ), exist_ok = True )
1985- print (f"writing [{ fn } ]: ..." )
1984+ log . debug (f"writing [{ fn } ]: ..." )
19861985 with open (fn , "w" , encoding = "utf-8" ) as f :
19871986 for k , v in compose .environ .items ():
19881987 if k .startswith ("COMPOSE_" ) or k .startswith ("PODMAN_" ):
19891988 f .write (f"{ k } ={ v } \n " )
1990- print (f"writing [{ fn } ]: done." )
1991- print ("\n \n creating the pod without starting it: ...\n \n " )
1989+ log . debug (f"writing [{ fn } ]: done." )
1990+ log . info ("\n \n creating the pod without starting it: ...\n \n " )
19921991 process = await asyncio .subprocess .create_subprocess_exec (script , ["up" , "--no-start" ])
1993- print ("\n final exit code is " , process )
1992+ log . info ("\n final exit code is " , process )
19941993 username = getpass .getuser ()
19951994 print (
19961995 f"""
@@ -2037,18 +2036,18 @@ async def compose_systemd(compose, args):
20372036WantedBy=default.target
20382037"""
20392038 if os .access (os .path .dirname (fn ), os .W_OK ):
2040- print (f"writing [{ fn } ]: ..." )
2039+ log . debug (f"writing [{ fn } ]: ..." )
20412040 with open (fn , "w" , encoding = "utf-8" ) as f :
20422041 f .write (out )
2043- print (f"writing [{ fn } ]: done." )
2042+ log . debug (f"writing [{ fn } ]: done." )
20442043 print (
20452044 """
20462045while in your project type `podman-compose systemd -a register`
20472046"""
20482047 )
20492048 else :
20502049 print (out )
2051- log (f"Could not write to [{ fn } ], use 'sudo'" )
2050+ log . warn (f"Could not write to [{ fn } ], use 'sudo'" )
20522051
20532052
20542053@cmd_run (podman_compose , "pull" , "pull stack images" )
@@ -2188,7 +2187,7 @@ def get_excluded(compose, args):
21882187 for service in args .services :
21892188 excluded -= compose .services [service ]["_deps" ]
21902189 excluded .discard (service )
2191- log ("** excluding: " , excluded )
2190+ log . debug ("** excluding: %s " , excluded )
21922191 return excluded
21932192
21942193
@@ -2221,18 +2220,18 @@ async def compose_up(compose: PodmanCompose, args):
22212220 )
22222221 diff_hashes = [i for i in hashes if i and i != compose .yaml_hash ]
22232222 if args .force_recreate or len (diff_hashes ):
2224- log ("recreating: ..." )
2223+ log . info ("recreating: ..." )
22252224 down_args = argparse .Namespace (** dict (args .__dict__ , volumes = False ))
22262225 await compose .commands ["down" ](compose , down_args )
2227- log ("recreating: done\n \n " )
2226+ log . info ("recreating: done\n \n " )
22282227 # args.no_recreate disables check for changes (which is not implemented)
22292228
22302229 podman_command = "run" if args .detach and not args .no_start else "create"
22312230
22322231 await create_pods (compose , args )
22332232 for cnt in compose .containers :
22342233 if cnt ["_service" ] in excluded :
2235- log ("** skipping: " , cnt ["name" ])
2234+ log . debug ("** skipping: %s " , cnt ["name" ])
22362235 continue
22372236 podman_args = await container_to_args (compose , cnt , detached = args .detach )
22382237 subproc = await compose .podman .run ([], podman_command , podman_args )
@@ -2264,7 +2263,7 @@ async def compose_up(compose: PodmanCompose, args):
22642263 space_suffix = " " * (max_service_length - len (cnt ["_service" ]) + 1 )
22652264 log_formatter = "{}[{}]{}|\x1b [0m" .format (color , cnt ["_service" ], space_suffix )
22662265 if cnt ["_service" ] in excluded :
2267- log ("** skipping: " , cnt ["name" ])
2266+ log . debug ("** skipping: %s " , cnt ["name" ])
22682267 continue
22692268
22702269 tasks .add (
@@ -2368,7 +2367,7 @@ async def compose_down(compose, args):
23682367 if cnt ["_service" ] not in excluded :
23692368 continue
23702369 vol_names_to_keep .update (get_volume_names (compose , cnt ))
2371- log ("keep" , vol_names_to_keep )
2370+ log . debug ("keep" , vol_names_to_keep )
23722371 for volume_name in await compose .podman .volume_ls ():
23732372 if volume_name in vol_names_to_keep :
23742373 continue
@@ -2643,7 +2642,7 @@ async def compose_unpause(compose, args):
26432642async def compose_kill (compose , args ):
26442643 # to ensure that the user did not execute the command by mistake
26452644 if not args .services and not args .all :
2646- print (
2645+ log . fatal (
26472646 "Error: you must provide at least one service name or use (--all) to kill all services"
26482647 )
26492648 sys .exit ()
0 commit comments