Copilot commented on code in PR #14006:
URL: https://github.com/apache/cloudstack/pull/14006#discussion_r3887067573


##########
plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapper.java:
##########
@@ -221,23 +222,33 @@ private String mountBackupDirectory(String 
backupRepoAddress, String backupRepoT
                 mountCmd.add("-o");
                 mountCmd.add(mountOptions);
             }
-            Script.executeCommand(mountCmd.toArray(new String[0]));
+            exitValue = Script.executeCommandForExitValue(mountTimeout, 
mountCmd.toArray(new String[0]));
         } catch (Exception e) {
             logger.error("Failed to mount repository {} of type {} to the 
directory {}", backupRepoAddress, backupRepoType, mountDirectory, e);
             throw new CloudRuntimeException("Failed to mount the backup 
repository on the KVM host");
         }
+        if (exitValue != 0) {
+            logger.error("Failed to mount repository {} of type {} to the 
directory {}, mount exited with {}", backupRepoAddress,
+                    backupRepoType, mountDirectory, exitValue);
+            throw new CloudRuntimeException("Failed to mount the backup 
repository on the KVM host");
+        }
         return mountDirectory;
     }
 
     private void unmountBackupDirectory(String backupDirectory) {
+        int exitValue;
         try {
             String umountPath = Script.getExecutableAbsolutePath("umount");
             String[] umountCmd = new String[] { "sudo", umountPath, 
backupDirectory };
-            Script.executeCommand(umountCmd);
+            exitValue = Script.executeCommandForExitValue(umountCmd);
         } catch (Exception e) {

Review Comment:
   unmountBackupDirectory now uses Script.executeCommandForExitValue(String...) 
without passing an explicit timeout. That overload delegates to 
executeCommandForExitValue(0, ...) which falls back to Script's 1-hour default 
timeout; if umount blocks (common with unreachable NFS/CIFS), restores can 
still hang in the finally cleanup path far longer than the configured mount 
timeout.



##########
plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapper.java:
##########
@@ -221,23 +222,33 @@ private String mountBackupDirectory(String 
backupRepoAddress, String backupRepoT
                 mountCmd.add("-o");
                 mountCmd.add(mountOptions);
             }
-            Script.executeCommand(mountCmd.toArray(new String[0]));
+            exitValue = Script.executeCommandForExitValue(mountTimeout, 
mountCmd.toArray(new String[0]));
         } catch (Exception e) {
             logger.error("Failed to mount repository {} of type {} to the 
directory {}", backupRepoAddress, backupRepoType, mountDirectory, e);
             throw new CloudRuntimeException("Failed to mount the backup 
repository on the KVM host");
         }
+        if (exitValue != 0) {
+            logger.error("Failed to mount repository {} of type {} to the 
directory {}, mount exited with {}", backupRepoAddress,
+                    backupRepoType, mountDirectory, exitValue);
+            throw new CloudRuntimeException("Failed to mount the backup 
repository on the KVM host");
+        }

Review Comment:
   When the mount command exits non-zero, mountBackupDirectory throws but 
leaves the temporary mount directory created by Files.createTempDirectory() 
behind. Since the mount failure short-circuits before the restore methods' 
finally blocks run, this can accumulate stale /tmp/csbackup.* directories on 
repeated failures.
   
   Consider cleaning up the temporary directory on mount failure before 
throwing (best-effort, log on failure).



##########
plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapperTest.java:
##########
@@ -404,7 +404,7 @@ public void testExecuteWithRsyncFailure() throws Exception {
                         .thenAnswer(invocation -> invocation.getArgument(0));
                 scriptMock.when(() -> 
Script.executeCommand(any(String[].class)))
                         .thenReturn(null);
-                scriptMock.when(() -> 
Script.executeCommandForExitValue(any(String[].class)))
+                scriptMock.when(() -> 
Script.executeCommandForExitValue(anyLong(), any(String[].class)))
                         .thenAnswer(invocation -> {
                             if 
(Arrays.stream(invocation.getArguments()).map(String::valueOf).anyMatch("rsync"::equals))
 {
                                 return 1; // Rsync failure

Review Comment:
   The Mockito stub meant to simulate an rsync failure never matches because 
invocation.getArguments() contains (timeout, String[] cmd). Converting the 
String[] to String via String.valueOf(...) yields an array identity (e.g., 
"[Ljava.lang.String;@...") rather than the command contents, so the "rsync" 
check is always false and the test doesn't actually exercise the failure path.
   
   This issue also appears on line 603 of the same file.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to