This is an automated email from the ASF dual-hosted git repository.

lhotari pushed a commit to branch branch-3.0
in repository https://gitbox.apache.org/repos/asf/pulsar.git

commit a17a262fd54c54d8993dbd576d2aea525c10c53d
Author: Nikhil Erigila <[email protected]>
AuthorDate: Thu Sep 19 14:27:36 2024 +0530

    [fix][broker] Fix incomplete NAR file extraction which prevents broker from 
starting (#23274)
    
    (cherry picked from commit 03330b3f7ca7dc06114d34fe34679eef73f821e7)
---
 .../org/apache/pulsar/common/nar/NarUnpacker.java  | 29 ++++++++++++++++------
 .../apache/pulsar/common/nar/NarUnpackerTest.java  | 11 ++++++++
 2 files changed, 33 insertions(+), 7 deletions(-)

diff --git 
a/pulsar-common/src/main/java/org/apache/pulsar/common/nar/NarUnpacker.java 
b/pulsar-common/src/main/java/org/apache/pulsar/common/nar/NarUnpacker.java
index 1e34c3e4fe7..e08d1f0241b 100644
--- a/pulsar-common/src/main/java/org/apache/pulsar/common/nar/NarUnpacker.java
+++ b/pulsar-common/src/main/java/org/apache/pulsar/common/nar/NarUnpacker.java
@@ -32,7 +32,9 @@ import java.io.InputStream;
 import java.io.RandomAccessFile;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
+import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.Base64;
@@ -86,19 +88,32 @@ public class NarUnpacker {
             try (FileChannel channel = new RandomAccessFile(lockFile, 
"rw").getChannel();
                  FileLock lock = channel.lock()) {
                 File narWorkingDirectory = new File(parentDirectory, md5Sum);
-                if (narWorkingDirectory.mkdir()) {
+                if (!narWorkingDirectory.exists()) {
+                    File narExtractionTempDirectory = new 
File(parentDirectory, md5Sum + ".tmp");
+                    if (narExtractionTempDirectory.exists()) {
+                        FileUtils.deleteFile(narExtractionTempDirectory, true);
+                    }
+                    if (!narExtractionTempDirectory.mkdir()) {
+                        throw new IOException("Cannot create " + 
narExtractionTempDirectory);
+                    }
                     try {
-                        log.info("Extracting {} to {}", nar, 
narWorkingDirectory);
+                        log.info("Extracting {} to {}", nar, 
narExtractionTempDirectory);
                         if (extractCallback != null) {
                             extractCallback.run();
                         }
-                        unpack(nar, narWorkingDirectory);
+                        unpack(nar, narExtractionTempDirectory);
                     } catch (IOException e) {
                         log.error("There was a problem extracting the nar 
file. Deleting {} to clean up state.",
-                                narWorkingDirectory, e);
-                        FileUtils.deleteFile(narWorkingDirectory, true);
+                                narExtractionTempDirectory, e);
+                        try {
+                            FileUtils.deleteFile(narExtractionTempDirectory, 
true);
+                        } catch (IOException e2) {
+                            log.error("Failed to delete temporary directory 
{}", narExtractionTempDirectory, e2);
+                        }
                         throw e;
                     }
+                    Files.move(narExtractionTempDirectory.toPath(), 
narWorkingDirectory.toPath(),
+                            StandardCopyOption.ATOMIC_MOVE);
                 }
                 return narWorkingDirectory;
             }
@@ -166,7 +181,7 @@ public class NarUnpacker {
      * @throws IOException
      *             if cannot read file
      */
-    private static byte[] calculateMd5sum(final File file) throws IOException {
+    protected static byte[] calculateMd5sum(final File file) throws 
IOException {
         try (final FileInputStream inputStream = new FileInputStream(file)) {
             final MessageDigest md5 = MessageDigest.getInstance("md5");
 
@@ -183,4 +198,4 @@ public class NarUnpacker {
             throw new IllegalArgumentException(nsae);
         }
     }
-}
+}
\ No newline at end of file
diff --git 
a/pulsar-common/src/test/java/org/apache/pulsar/common/nar/NarUnpackerTest.java 
b/pulsar-common/src/test/java/org/apache/pulsar/common/nar/NarUnpackerTest.java
index f93afac2ff9..f57871ca689 100644
--- 
a/pulsar-common/src/test/java/org/apache/pulsar/common/nar/NarUnpackerTest.java
+++ 
b/pulsar-common/src/test/java/org/apache/pulsar/common/nar/NarUnpackerTest.java
@@ -117,6 +117,17 @@ public class NarUnpackerTest {
         }
     }
 
+    @Test
+    void shouldReExtractWhenUnpackedDirectoryIsMissing() throws IOException {
+        AtomicInteger extractCounter = new AtomicInteger();
+
+        File narWorkingDirectory = NarUnpacker.doUnpackNar(sampleZipFile, 
extractDirectory, extractCounter::incrementAndGet);
+        FileUtils.deleteFile(narWorkingDirectory, true);
+        NarUnpacker.doUnpackNar(sampleZipFile, extractDirectory, 
extractCounter::incrementAndGet);
+
+        assertEquals(extractCounter.get(), 2);
+    }
+
     @Test
     void shouldExtractFilesOnceInDifferentProcess() throws 
InterruptedException {
         int processes = 5;

Reply via email to