Skip to content

Commit 4f373a4

Browse files
dustymabejbtrystram
andcommitted
cmd-diff: extract files instead of using FUSE
This approach use more disk space but disk access for the diff will be faster. Files will also survive under after git diff returns in case manual inspection is desired. Co-authored-by: Jean-Baptiste Trystram <[email protected]>
1 parent c1f8bdc commit 4f373a4

File tree

1 file changed

+54
-57
lines changed

1 file changed

+54
-57
lines changed

src/cmd-diff

Lines changed: 54 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -531,72 +531,69 @@ def run_guestfs_mount(image_path, mount_target):
531531
# the paths to be used for analysis and then clean up once given back
532532
# control.
533533
def 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)
534+
metal_image_from = get_metal_path(diff_from)
535+
metal_image_to = get_metal_path(diff_to)
536+
537+
diff_dir_from = os.path.join(cache_dir("metal"), diff_from.id)
538+
diff_dir_to = os.path.join(cache_dir("metal"), diff_to.id)
539+
540+
for image_path, diff_dir in [(metal_image_from, diff_dir_from),
541+
(metal_image_to, diff_dir_to)]:
542+
if os.path.exists(diff_dir):
543+
# If it exists assume it's cached already and we don't
544+
# need to do anything. If it's stale for whatever reason
545+
# the user can `cosa diff --gc`.
546+
continue
547+
548+
os.makedirs(diff_dir)
549+
550+
g = None
551+
try:
552+
g = guestfs.GuestFS(python_return_dict=True)
553+
g.set_backend("direct")
554+
g.add_drive_opts(image_path, readonly=1)
555+
g.launch()
556+
557+
# Mount the disks in the guestfs VM
558+
root = g.findfs_label("root")
559+
g.mount_ro(root, "/")
560+
boot = g.findfs_label("boot")
561+
g.mount_ro(boot, "/boot")
562+
efi = g.findfs_label("EFI-SYSTEM")
563+
g.mount_ro(efi, "/boot/efi")
564+
565+
566+
with tempfile.NamedTemporaryFile(suffix=".tar", delete=True) as tmp_tar:
567+
g.tar_out("/", tmp_tar.name, xattrs=True, selinux=True, excludes=excludes)
568+
# Extract the tarball.
569+
runcmd(['tar', '-xf', tmp_tar.name, '-C', diff_dir])
570+
571+
except Exception as e:
572+
print(f"Error in guestfs process for {image_path}: {e}", file=sys.stderr)
573+
raise
574+
finally:
575+
if g:
576+
g.close()
577+
578+
# Allow the caller to operate on these values
579+
return diff_dir_from, diff_dir_to
583580

584581

585582
def 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)
583+
mount_dir_from, mount_dir_to = diff_metal_helper(diff_from, diff_to)
584+
git_diff(mount_dir_from, mount_dir_to)
588585

589586

590587
def 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)
588+
mount_dir_from, mount_dir_to = diff_metal_helper(diff_from, diff_to)
589+
cmd = ['find', '.', '-type', 'd', '-exec', 'du', '-sh', '{}', ';']
590+
diff_cmd_outputs(cmd, mount_dir_from, mount_dir_to, strategy=DiffCmdOutputStrategy.CD)
594591

595592

596593
def 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)
594+
mount_dir_from, mount_dir_to = diff_metal_helper(diff_from, diff_to)
595+
cmd = ['find', '.']
596+
diff_cmd_outputs(cmd, mount_dir_from, mount_dir_to, strategy=DiffCmdOutputStrategy.CD)
600597

601598

602599
def diff_cmd_outputs(cmd, path_from, path_to, strategy: DiffCmdOutputStrategy = DiffCmdOutputStrategy.TEMPLATE):

0 commit comments

Comments
 (0)