@@ -7,9 +7,7 @@ import json
77import subprocess
88import sys
99import tempfile
10- import time
1110import rpm
12- from multiprocessing import Process
1311
1412from dataclasses import dataclass
1513from enum import IntEnum
@@ -531,72 +529,68 @@ def run_guestfs_mount(image_path, mount_target):
531529# the paths to be used for analysis and then clean up once given back
532530# control.
533531def diff_metal_helper (diff_from , diff_to ):
534- metal_from = get_metal_path (diff_from )
535- metal_to = get_metal_path (diff_to )
536-
537- mount_dir_from = os .path .join (cache_dir ("metal" ), diff_from .id )
538- mount_dir_to = os .path .join (cache_dir ("metal" ), diff_to .id )
539-
540- for d in [mount_dir_from , mount_dir_to ]:
541- if os .path .exists (d ):
542- shutil .rmtree (d )
543- os .makedirs (d )
544-
545- # As the libreguest mount call is blocking until unmounted, let's
546- # do that in a separate thread
547- p_from = Process (target = run_guestfs_mount , args = (metal_from , mount_dir_from ))
548- p_to = Process (target = run_guestfs_mount , args = (metal_to , mount_dir_to ))
549-
550- try :
551- p_from .start ()
552- p_to .start ()
553- # Wait for the FUSE mounts to be ready. We'll check for a known file.
554- for i , d in enumerate ([mount_dir_from , mount_dir_to ]):
555- p = p_from if i == 0 else p_to
556- timeout = 60 # seconds
557- start_time = time .time ()
558- check_file = os .path .join (d , 'ostree' )
559- while not os .path .exists (check_file ):
560- time .sleep (1 )
561- if time .time () - start_time > timeout :
562- raise Exception (f"Timeout waiting for mount in { d } " )
563- if not p .is_alive ():
564- raise Exception (f"A guestfs process for { os .path .basename (d )} died unexpectedly." )
565-
566- # Allow the caller to operate on these values
567- yield mount_dir_from , mount_dir_to
568-
569- finally :
570- # Unmount the FUSE binds, this will make the guestfs mount calls return
571- runcmd (['fusermount' , '-u' , mount_dir_from ], check = False )
572- runcmd (['fusermount' , '-u' , mount_dir_to ], check = False )
573-
574- # Ensure the background processes are terminated
575- def shutdown_process (process ):
576- process .join (timeout = 5 )
577- if process .is_alive ():
578- process .terminate ()
579- process .join ()
580-
581- shutdown_process (p_from )
582- shutdown_process (p_to )
532+ metal_image_from = get_metal_path (diff_from )
533+ metal_image_to = get_metal_path (diff_to )
534+
535+ diff_dir_from = os .path .join (cache_dir ("metal" ), diff_from .id )
536+ diff_dir_to = os .path .join (cache_dir ("metal" ), diff_to .id )
537+
538+ for image_path , diff_dir in [(metal_image_from , diff_dir_from ),
539+ (metal_image_to , diff_dir_to )]:
540+ if os .path .exists (diff_dir ):
541+ # If it exists assume it's cached already and we don't
542+ # need to do anything. If it's stale for whatever reason
543+ # the user can `cosa diff --gc`.
544+ continue
545+
546+ os .makedirs (diff_dir )
547+
548+ g = None
549+ try :
550+ g = guestfs .GuestFS (python_return_dict = True )
551+ g .set_backend ("direct" )
552+ g .add_drive_opts (image_path , readonly = 1 )
553+ g .launch ()
554+
555+ # Mount the disks in the guestfs VM
556+ root = g .findfs_label ("root" )
557+ g .mount_ro (root , "/" )
558+ boot = g .findfs_label ("boot" )
559+ g .mount_ro (boot , "/boot" )
560+ efi = g .findfs_label ("EFI-SYSTEM" )
561+ g .mount_ro (efi , "/boot/efi" )
562+
563+ with tempfile .NamedTemporaryFile (suffix = ".tar" , delete = True ) as tmp_tar :
564+ g .tar_out ("/" , tmp_tar .name , xattrs = True , selinux = True , excludes = excludes )
565+ # Extract the tarball.
566+ runcmd (['tar' , '-xf' , tmp_tar .name , '-C' , diff_dir ])
567+
568+ except Exception as e :
569+ print (f"Error in guestfs process for { image_path } : { e } " , file = sys .stderr )
570+ raise
571+ finally :
572+ if g :
573+ g .close ()
574+
575+ # Allow the caller to operate on these values
576+ return diff_dir_from , diff_dir_to
583577
584578
585579def diff_metal (diff_from , diff_to ):
586- for mount_dir_from , mount_dir_to in diff_metal_helper (diff_from , diff_to ):
587- git_diff (mount_dir_from , mount_dir_to )
580+ mount_dir_from , mount_dir_to = diff_metal_helper (diff_from , diff_to )
581+ git_diff (mount_dir_from , mount_dir_to )
588582
589583
590584def diff_metal_du (diff_from , diff_to ):
591- for mount_dir_from , mount_dir_to in diff_metal_helper (diff_from , diff_to ):
592- cmd = ['find' , '.' , '-type' , 'd' , '-exec' , 'du' , '-sh' , '{}' , ';' ]
593- diff_cmd_outputs (cmd , mount_dir_from , mount_dir_to , strategy = DiffCmdOutputStrategy .CD )
585+ mount_dir_from , mount_dir_to = diff_metal_helper (diff_from , diff_to )
586+ cmd = ['find' , '.' , '-type' , 'd' , '-exec' , 'du' , '-sh' , '{}' , ';' ]
587+ diff_cmd_outputs (cmd , mount_dir_from , mount_dir_to , strategy = DiffCmdOutputStrategy .CD )
594588
595589
596590def diff_metal_ls (diff_from , diff_to ):
597- for mount_dir_from , mount_dir_to in diff_metal_helper (diff_from , diff_to ):
598- cmd = ['find' , '.' ]
599- diff_cmd_outputs (cmd , mount_dir_from , mount_dir_to , strategy = DiffCmdOutputStrategy .CD )
591+ mount_dir_from , mount_dir_to = diff_metal_helper (diff_from , diff_to )
592+ cmd = ['find' , '.' ]
593+ diff_cmd_outputs (cmd , mount_dir_from , mount_dir_to , strategy = DiffCmdOutputStrategy .CD )
600594
601595
602596def diff_cmd_outputs (cmd , path_from , path_to , strategy : DiffCmdOutputStrategy = DiffCmdOutputStrategy .TEMPLATE ):
0 commit comments