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 ( "podman volume inspect %s || podman volume create %s" , vol_name , 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,9 +555,11 @@ 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 (
567- f'WARNING: Service { cnt ["_service" ]} uses secret "{ sec } " with uid, gid, or mode.'
568- + " These fields are not supported by this implementation of the Compose file"
558+ log .warning (
559+ "WARNING: Service %s uses secret %s with uid, gid, or mode."
560+ + " These fields are not supported by this implementation of the Compose file" ,
561+ cnt ["_service" ],
562+ sec ,
569563 )
570564 return volume_ref
571565 # v3.5 and up added external flag, earlier the spec
@@ -594,11 +588,12 @@ def get_secret_args(compose, cnt, secret):
594588 if target and target != secret_name :
595589 raise ValueError (err_str .format (target , secret_name ))
596590 if target :
597- log (
598- 'WARNING: Service "{}" uses target: "{}" for secret: "{}".' .format (
599- cnt ["_service" ], target , secret_name
600- )
601- + " That is un-supported and a no-op and is ignored."
591+ log .warning (
592+ 'WARNING: Service "%s" uses target: "%s" for secret: "%s".'
593+ + " That is un-supported and a no-op and is ignored." ,
594+ cnt ["_service" ],
595+ target ,
596+ secret_name ,
602597 )
603598 return ["--secret" , "{}{}" .format (secret_name , secret_opts )]
604599
@@ -778,7 +773,7 @@ def get_net_args(compose, cnt):
778773 elif net .startswith ("bridge" ):
779774 is_bridge = True
780775 else :
781- print ( f "unknown network_mode [{ net } ]" )
776+ log . fatal ( "unknown network_mode [%s]" , net )
782777 sys .exit (1 )
783778 else :
784779 is_bridge = True
@@ -921,10 +916,10 @@ async def container_to_args(compose, cnt, detached=True):
921916 await assert_cnt_nets (compose , cnt )
922917 podman_args .extend (get_net_args (compose , cnt ))
923918
924- logging = cnt .get ("logging" , None )
925- if logging is not None :
926- podman_args .append (f'--log-driver={ logging .get ("driver" , "k8s-file" )} ' )
927- log_opts = logging .get ("options" ) or {}
919+ log_config = cnt .get ("logging" , None )
920+ if log_config is not None :
921+ podman_args .append (f'--log-driver={ log_config .get ("driver" , "k8s-file" )} ' )
922+ log_opts = log_config .get ("options" ) or {}
928923 podman_args += [f"--log-opt={ name } ={ value } " for name , value in log_opts .items ()]
929924 for secret in cnt .get ("secrets" , []):
930925 podman_args .extend (get_secret_args (compose , cnt , secret ))
@@ -1154,7 +1149,7 @@ async def output(self, podman_args, cmd="", cmd_args=None):
11541149 cmd_args = cmd_args or []
11551150 xargs = self .compose .get_podman_args (cmd ) if cmd else []
11561151 cmd_ls = [self .podman_path , * podman_args , cmd ] + xargs + cmd_args
1157- log ( cmd_ls )
1152+ log . info ( str ( cmd_ls ) )
11581153 p = await asyncio .subprocess .create_subprocess_exec (
11591154 * cmd_ls , stdout = asyncio .subprocess .PIPE , stderr = asyncio .subprocess .PIPE
11601155 )
@@ -1174,7 +1169,7 @@ def exec(
11741169 cmd_args = list (map (str , cmd_args or []))
11751170 xargs = self .compose .get_podman_args (cmd ) if cmd else []
11761171 cmd_ls = [self .podman_path , * podman_args , cmd ] + xargs + cmd_args
1177- log (" " .join ([str (i ) for i in cmd_ls ]))
1172+ log . info (" " .join ([str (i ) for i in cmd_ls ]))
11781173 os .execlp (self .podman_path , * cmd_ls )
11791174
11801175 async def run (
@@ -1191,7 +1186,7 @@ async def run(
11911186 cmd_args = list (map (str , cmd_args or []))
11921187 xargs = self .compose .get_podman_args (cmd ) if cmd else []
11931188 cmd_ls = [self .podman_path , * podman_args , cmd ] + xargs + cmd_args
1194- log (" " .join ([str (i ) for i in cmd_ls ]))
1189+ log . info (" " .join ([str (i ) for i in cmd_ls ]))
11951190 if self .dry_run :
11961191 return None
11971192
@@ -1225,16 +1220,16 @@ async def format_out(stdout):
12251220 try :
12261221 exit_code = await p .wait ()
12271222 except asyncio .CancelledError :
1228- log ("Sending termination signal" )
1223+ log . info ("Sending termination signal" )
12291224 p .terminate ()
12301225 try :
12311226 exit_code = await wait_with_timeout (p .wait (), 10 )
12321227 except TimeoutError :
1233- log ("container did not shut down after 10 seconds, killing" )
1228+ log . warning ("container did not shut down after 10 seconds, killing" )
12341229 p .kill ()
12351230 exit_code = await p .wait ()
12361231
1237- log ( f "exit code: { exit_code } " )
1232+ log . info ( "exit code: %s" , exit_code )
12381233 return exit_code
12391234
12401235 async def volume_ls (self , proj = None ):
@@ -1482,7 +1477,7 @@ def assert_services(self, services):
14821477 missing = given - self .all_services
14831478 if missing :
14841479 missing_csv = "," .join (missing )
1485- log ( f "missing services [{ missing_csv } ]" )
1480+ log . warning ( "missing services [%s]" , missing_csv )
14861481 sys .exit (1 )
14871482
14881483 def get_podman_args (self , cmd ):
@@ -1496,7 +1491,7 @@ def get_podman_args(self, cmd):
14961491 return xargs
14971492
14981493 async def run (self ):
1499- log ("podman-compose version: " + __version__ )
1494+ log . info ("podman-compose version: %s" , __version__ )
15001495 args = self ._parse_args ()
15011496 podman_path = args .podman_path
15021497 if podman_path != "podman" :
@@ -1505,7 +1500,7 @@ async def run(self):
15051500 else :
15061501 # this also works if podman hasn't been installed now
15071502 if args .dry_run is False :
1508- log ( f "Binary { podman_path } has not been found." )
1503+ log . fatal ( "Binary %s has not been found." , podman_path )
15091504 sys .exit (1 )
15101505 self .podman = Podman (self , podman_path , args .dry_run , asyncio .Semaphore (args .parallel ))
15111506
@@ -1519,9 +1514,9 @@ async def run(self):
15191514 except subprocess .CalledProcessError :
15201515 self .podman_version = None
15211516 if not self .podman_version :
1522- log ("it seems that you do not have `podman` installed" )
1517+ log . fatal ("it seems that you do not have `podman` installed" )
15231518 sys .exit (1 )
1524- log ("using podman version: " + self .podman_version )
1519+ log . info ("using podman version: %s" , self .podman_version )
15251520 cmd_name = args .command
15261521 compose_required = cmd_name != "version" and (
15271522 cmd_name != "systemd" or args .action != "create-unit"
@@ -1549,15 +1544,15 @@ def _parse_compose_file(self):
15491544 args .file = list (filter (os .path .exists , default_ls ))
15501545 files = args .file
15511546 if not files :
1552- log (
1547+ log . fatal (
15531548 "no compose.yaml, docker-compose.yml or container-compose.yml file found, "
15541549 "pass files with -f"
15551550 )
15561551 sys .exit (- 1 )
15571552 ex = map (os .path .exists , files )
15581553 missing = [fn0 for ex0 , fn0 in zip (ex , files ) if not ex0 ]
15591554 if missing :
1560- log ("missing files: " , missing )
1555+ log . fatal ("missing files: %s " , missing )
15611556 sys .exit (1 )
15621557 # make absolute
15631558 relative_files = files
@@ -1635,7 +1630,7 @@ def _parse_compose_file(self):
16351630 compose ["_dirname" ] = dirname
16361631 # debug mode
16371632 if len (files ) > 1 :
1638- log (" ** merged:\n " , json .dumps (compose , indent = 2 ))
1633+ log . debug (" ** merged:\n %s " , json .dumps (compose , indent = 2 ))
16391634 # ver = compose.get('version', None)
16401635
16411636 if not project_name :
@@ -1656,7 +1651,7 @@ def _parse_compose_file(self):
16561651 services = compose .get ("services" , None )
16571652 if services is None :
16581653 services = {}
1659- log ("WARNING: No services defined" )
1654+ log . warning ("WARNING: No services defined" )
16601655 # include services with no profile defined or the selected profiles
16611656 services = self ._resolve_profiles (services , set (args .profile ))
16621657
@@ -1689,7 +1684,7 @@ def _parse_compose_file(self):
16891684 unused_nets = given_nets - allnets - set (["default" ])
16901685 if len (unused_nets ):
16911686 unused_nets_str = "," .join (unused_nets )
1692- log ( f "WARNING: unused networks: { unused_nets_str } " )
1687+ log . warning ( "WARNING: unused networks: %s" , unused_nets_str )
16931688 if len (missing_nets ):
16941689 missing_nets_str = "," .join (missing_nets )
16951690 raise RuntimeError (f"missing networks: { missing_nets_str } " )
@@ -1800,6 +1795,8 @@ def _parse_args(self):
18001795 if not self .global_args .command or self .global_args .command == "help" :
18011796 parser .print_help ()
18021797 sys .exit (- 1 )
1798+
1799+ logging .basicConfig (level = ("DEBUG" if self .global_args .verbose else "WARN" ))
18031800 return self .global_args
18041801
18051802 @staticmethod
@@ -1887,6 +1884,11 @@ def _init_global_parser(parser):
18871884 parser .add_argument (
18881885 "--parallel" , type = int , default = os .environ .get ("COMPOSE_PARALLEL_LIMIT" , sys .maxsize )
18891886 )
1887+ parser .add_argument (
1888+ "--verbose" ,
1889+ help = "Print debugging output" ,
1890+ action = "store_true" ,
1891+ )
18901892
18911893
18921894podman_compose = PodmanCompose ()
@@ -1982,15 +1984,15 @@ async def compose_systemd(compose, args):
19821984 proj_name = compose .project_name
19831985 fn = os .path .expanduser (f"~/{ stacks_dir } /{ proj_name } .env" )
19841986 os .makedirs (os .path .dirname (fn ), exist_ok = True )
1985- print ( f "writing [{ fn } ]: ..." )
1987+ log . debug ( "writing [%s ]: ..." , fn )
19861988 with open (fn , "w" , encoding = "utf-8" ) as f :
19871989 for k , v in compose .environ .items ():
19881990 if k .startswith ("COMPOSE_" ) or k .startswith ("PODMAN_" ):
19891991 f .write (f"{ k } ={ v } \n " )
1990- print ( f "writing [{ fn } ]: done." )
1991- print ("\n \n creating the pod without starting it: ...\n \n " )
1992+ log . debug ( "writing [%s ]: done." , fn )
1993+ log . info ("\n \n creating the pod without starting it: ...\n \n " )
19921994 process = await asyncio .subprocess .create_subprocess_exec (script , ["up" , "--no-start" ])
1993- print ("\n final exit code is " , process )
1995+ log . info ("\n final exit code is " , process )
19941996 username = getpass .getuser ()
19951997 print (
19961998 f"""
@@ -2037,18 +2039,18 @@ async def compose_systemd(compose, args):
20372039WantedBy=default.target
20382040"""
20392041 if os .access (os .path .dirname (fn ), os .W_OK ):
2040- print ( f "writing [{ fn } ]: ..." )
2042+ log . debug ( "writing [%s ]: ..." , fn )
20412043 with open (fn , "w" , encoding = "utf-8" ) as f :
20422044 f .write (out )
2043- print ( f "writing [{ fn } ]: done." )
2045+ log . debug ( "writing [%s ]: done." , fn )
20442046 print (
20452047 """
20462048while in your project type `podman-compose systemd -a register`
20472049"""
20482050 )
20492051 else :
20502052 print (out )
2051- log ( f "Could not write to [{ fn } ], use 'sudo'" )
2053+ log . warning ( "Could not write to [%s ], use 'sudo'" , fn )
20522054
20532055
20542056@cmd_run (podman_compose , "pull" , "pull stack images" )
@@ -2188,7 +2190,7 @@ def get_excluded(compose, args):
21882190 for service in args .services :
21892191 excluded -= compose .services [service ]["_deps" ]
21902192 excluded .discard (service )
2191- log ("** excluding: " , excluded )
2193+ log . debug ("** excluding: %s " , excluded )
21922194 return excluded
21932195
21942196
@@ -2221,18 +2223,18 @@ async def compose_up(compose: PodmanCompose, args):
22212223 )
22222224 diff_hashes = [i for i in hashes if i and i != compose .yaml_hash ]
22232225 if args .force_recreate or len (diff_hashes ):
2224- log ("recreating: ..." )
2226+ log . info ("recreating: ..." )
22252227 down_args = argparse .Namespace (** dict (args .__dict__ , volumes = False ))
22262228 await compose .commands ["down" ](compose , down_args )
2227- log ("recreating: done\n \n " )
2229+ log . info ("recreating: done\n \n " )
22282230 # args.no_recreate disables check for changes (which is not implemented)
22292231
22302232 podman_command = "run" if args .detach and not args .no_start else "create"
22312233
22322234 await create_pods (compose , args )
22332235 for cnt in compose .containers :
22342236 if cnt ["_service" ] in excluded :
2235- log ("** skipping: " , cnt ["name" ])
2237+ log . debug ("** skipping: %s " , cnt ["name" ])
22362238 continue
22372239 podman_args = await container_to_args (compose , cnt , detached = args .detach )
22382240 subproc = await compose .podman .run ([], podman_command , podman_args )
@@ -2264,7 +2266,7 @@ async def compose_up(compose: PodmanCompose, args):
22642266 space_suffix = " " * (max_service_length - len (cnt ["_service" ]) + 1 )
22652267 log_formatter = "{}[{}]{}|\x1b [0m" .format (color , cnt ["_service" ], space_suffix )
22662268 if cnt ["_service" ] in excluded :
2267- log ("** skipping: " , cnt ["name" ])
2269+ log . debug ("** skipping: %s " , cnt ["name" ])
22682270 continue
22692271
22702272 tasks .add (
@@ -2368,7 +2370,7 @@ async def compose_down(compose, args):
23682370 if cnt ["_service" ] not in excluded :
23692371 continue
23702372 vol_names_to_keep .update (get_volume_names (compose , cnt ))
2371- log ("keep" , vol_names_to_keep )
2373+ log . debug ("keep %s " , vol_names_to_keep )
23722374 for volume_name in await compose .podman .volume_ls ():
23732375 if volume_name in vol_names_to_keep :
23742376 continue
@@ -2643,7 +2645,7 @@ async def compose_unpause(compose, args):
26432645async def compose_kill (compose , args ):
26442646 # to ensure that the user did not execute the command by mistake
26452647 if not args .services and not args .all :
2646- print (
2648+ log . fatal (
26472649 "Error: you must provide at least one service name or use (--all) to kill all services"
26482650 )
26492651 sys .exit ()
0 commit comments