diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index fcff490a26..f6c0424488 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -765,6 +765,8 @@ when using one of Exact*StatsCache (Mikhail Khludnev) * SOLR-10908: CloudSolrStream.toExpression incorrectly handles fq clauses (Rohit Singh via Erick Erickson) +* SOLR-11198: downconfig downloads empty file as folder (Erick Erickson) + Optimizations ---------------------- * SOLR-10634: JSON Facet API: When a field/terms facet will retrieve all buckets (i.e. limit:-1) diff --git a/solr/core/src/test/org/apache/solr/cloud/SolrCLIZkUtilsTest.java b/solr/core/src/test/org/apache/solr/cloud/SolrCLIZkUtilsTest.java index 776075e7b6..579e93d749 100644 --- a/solr/core/src/test/org/apache/solr/cloud/SolrCLIZkUtilsTest.java +++ b/solr/core/src/test/org/apache/solr/cloud/SolrCLIZkUtilsTest.java @@ -106,7 +106,7 @@ public class SolrCLIZkUtilsTest extends SolrCloudTestCase { @Test public void testDownconfig() throws Exception { - Path tmp = createTempDir("downConfigNewPlace"); + Path tmp = Paths.get(createTempDir("downConfigNewPlace").toAbsolutePath().toString(), "myconfset"); // First we need a configset on ZK to bring down. @@ -127,6 +127,28 @@ public class SolrCLIZkUtilsTest extends SolrCloudTestCase { assertEquals("Download should have succeeded.", 0, res); verifyZkLocalPathsMatch(Paths.get(tmp.toAbsolutePath().toString(), "conf"), "/configs/downconfig1"); + // Insure that empty files don't become directories (SOLR-11198) + + Path emptyFile = Paths.get(tmp.toAbsolutePath().toString(), "conf", "stopwords", "emptyfile"); + Files.createFile(emptyFile); + + // Now copy it up and back and insure it's still a file in the new place + copyConfigUp(tmp.getParent(), "myconfset", "downconfig2"); + Path tmp2 = createTempDir("downConfigNewPlace2"); + downTool = new SolrCLI.ConfigSetDownloadTool(); + args = new String[]{ + "-confname", "downconfig2", + "-confdir", tmp2.toAbsolutePath().toString(), + "-zkHost", zkAddr, + }; + + res = downTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(downTool.getOptions()), args)); + assertEquals("Download should have succeeded.", 0, res); + verifyZkLocalPathsMatch(Paths.get(tmp.toAbsolutePath().toString(), "conf"), "/configs/downconfig2"); + // And insure the empty file is a text file + Path destEmpty = Paths.get(tmp2.toAbsolutePath().toString(), "conf", "stopwords", "emptyfile"); + assertTrue("Empty files should NOT be copied down as directories", destEmpty.toFile().isFile()); + } @Test @@ -373,6 +395,58 @@ public class SolrCLIZkUtilsTest extends SolrCloudTestCase { content = new String(zkClient.getData("/cp9/conf/stopwords", null, null, true), StandardCharsets.UTF_8); assertTrue("There should be content in the node! ", content.contains("{Some Arbitrary Data}")); + // Copy an individual empty file up and back down and insure it's still a file + Path emptyFile = Paths.get(tmp.toAbsolutePath().toString(), "conf", "stopwords", "emptyfile"); + Files.createFile(emptyFile); + + args = new String[]{ + "-src", "file:" + emptyFile.toAbsolutePath().toString(), + "-dst", "zk:/cp7/conf/stopwords/emptyFile", + "-recurse", "false", + "-zkHost", zkAddr, + }; + + res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args)); + assertEquals("Copy should have succeeded.", 0, res); + + Path tmp2 = createTempDir("cp9"); + Path emptyDest = Paths.get(tmp2.toAbsolutePath().toString(), "emptyFile"); + args = new String[]{ + "-src", "zk:/cp7/conf/stopwords/emptyFile", + "-dst", "file:" + emptyDest.toAbsolutePath().toString(), + "-recurse", "false", + "-zkHost", zkAddr, + }; + res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args)); + assertEquals("Copy should have succeeded.", 0, res); + + assertTrue("Empty files should NOT be copied down as directories", emptyDest.toFile().isFile()); + + // Now with recursive copy + + args = new String[]{ + "-src", "file:" + emptyFile.getParent().getParent().toString(), + "-dst", "zk:/cp10", + "-recurse", "true", + "-zkHost", zkAddr, + }; + + res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args)); + assertEquals("Copy should have succeeded.", 0, res); + + // Now copy it all back and make sure empty file is still a file when recursively copying. + tmp2 = createTempDir("cp10"); + args = new String[]{ + "-src", "zk:/cp10", + "-dst", "file:" + tmp2.toAbsolutePath().toString(), + "-recurse", "true", + "-zkHost", zkAddr, + }; + res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args)); + assertEquals("Copy should have succeeded.", 0, res); + + Path locEmpty = Paths.get(tmp2.toAbsolutePath().toString(), "stopwords", "emptyFile"); + assertTrue("Empty files should NOT be copied down as directories", locEmpty.toFile().isFile()); } @Test diff --git a/solr/solrj/src/java/org/apache/solr/common/cloud/ZkMaintenanceUtils.java b/solr/solrj/src/java/org/apache/solr/common/cloud/ZkMaintenanceUtils.java index dfdc8cf2fd..6843480ab8 100644 --- a/solr/solrj/src/java/org/apache/solr/common/cloud/ZkMaintenanceUtils.java +++ b/solr/solrj/src/java/org/apache/solr/common/cloud/ZkMaintenanceUtils.java @@ -308,13 +308,13 @@ public class ZkMaintenanceUtils { public static void downloadFromZK(SolrZkClient zkClient, String zkPath, Path file) throws IOException { try { List children = zkClient.getChildren(zkPath, null, true); - // If it has no children, it's a leaf node, write the assoicated data from the ZNode. + // If it has no children, it's a leaf node, write the associated data from the ZNode. // Otherwise, continue recursing, but write the associated data to a special file if any if (children.size() == 0) { // If we didn't copy data down, then we also didn't create the file. But we still need a marker on the local - // disk so create a dir. + // disk so create an empty file. if (copyDataDown(zkClient, zkPath, file.toFile()) == 0) { - Files.createDirectories(file); + Files.createFile(file); } } else { Files.createDirectories(file); // Make parent dir.