Skip to content

Commit daf754e

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 e743450 commit daf754e

File tree

1 file changed

+53
-59
lines changed

1 file changed

+53
-59
lines changed

src/cmd-diff

Lines changed: 53 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ import json
77
import subprocess
88
import sys
99
import tempfile
10-
import time
1110
import rpm
12-
from multiprocessing import Process
1311

1412
from dataclasses import dataclass
1513
from 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.
533531
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)
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

585579
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)
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

590584
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)
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

596590
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)
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

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

0 commit comments

Comments
 (0)