mirror of https://github.com/apache/lucene.git
SOLR-10387: zkTransfer normalizes destination path incorrectly if source is a windows directory
This commit is contained in:
parent
efdb04d06c
commit
edcdc3052b
|
@ -148,6 +148,9 @@ Bug Fixes
|
||||||
|
|
||||||
* SOLR-10369: bin\solr.cmd delete and healthcheck now works again; fixed continuation chars ^ (Luis Goes via janhoy)
|
* SOLR-10369: bin\solr.cmd delete and healthcheck now works again; fixed continuation chars ^ (Luis Goes via janhoy)
|
||||||
|
|
||||||
|
* SOLR-10387: zkTransfer normalizes destination path incorrectly if source is a windows directory
|
||||||
|
(gopikannan venugopalsamy, Erick Erickson)
|
||||||
|
|
||||||
Other Changes
|
Other Changes
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
package org.apache.solr.cloud;
|
package org.apache.solr.cloud;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
@ -229,9 +230,9 @@ public class SolrCLIZkUtilsTest extends SolrCloudTestCase {
|
||||||
// NOTE: really can't test copying to '.' because the test framework doesn't allow altering the source tree
|
// NOTE: really can't test copying to '.' because the test framework doesn't allow altering the source tree
|
||||||
// and at least IntelliJ's CWD is in the source tree.
|
// and at least IntelliJ's CWD is in the source tree.
|
||||||
|
|
||||||
// copy to local ending in '/'
|
// copy to local ending in separator
|
||||||
//src and cp3 and cp4 are valid
|
//src and cp3 and cp4 are valid
|
||||||
String localSlash = tmp.normalize() + "/cpToLocal/";
|
String localSlash = tmp.normalize() + File.separator +"cpToLocal" + File.separator;
|
||||||
args = new String[]{
|
args = new String[]{
|
||||||
"-src", "zk:/cp3/schema.xml",
|
"-src", "zk:/cp3/schema.xml",
|
||||||
"-dst", localSlash,
|
"-dst", localSlash,
|
||||||
|
@ -246,7 +247,7 @@ public class SolrCLIZkUtilsTest extends SolrCloudTestCase {
|
||||||
// copy to ZK ending in '/'.
|
// copy to ZK ending in '/'.
|
||||||
//src and cp3 are valid
|
//src and cp3 are valid
|
||||||
args = new String[]{
|
args = new String[]{
|
||||||
"-src", "file:" + srcPathCheck.normalize().toAbsolutePath().toString() + "/solrconfig.xml",
|
"-src", "file:" + srcPathCheck.normalize().toAbsolutePath().toString() + File.separator + "solrconfig.xml",
|
||||||
"-dst", "zk:/powerup/",
|
"-dst", "zk:/powerup/",
|
||||||
"-recurse", "false",
|
"-recurse", "false",
|
||||||
"-zkHost", zkAddr,
|
"-zkHost", zkAddr,
|
||||||
|
@ -259,7 +260,7 @@ public class SolrCLIZkUtilsTest extends SolrCloudTestCase {
|
||||||
// copy individual file up
|
// copy individual file up
|
||||||
//src and cp3 are valid
|
//src and cp3 are valid
|
||||||
args = new String[]{
|
args = new String[]{
|
||||||
"-src", "file:" + srcPathCheck.normalize().toAbsolutePath().toString() + "/solrconfig.xml",
|
"-src", "file:" + srcPathCheck.normalize().toAbsolutePath().toString() + File.separator + "solrconfig.xml",
|
||||||
"-dst", "zk:/copyUpFile.xml",
|
"-dst", "zk:/copyUpFile.xml",
|
||||||
"-recurse", "false",
|
"-recurse", "false",
|
||||||
"-zkHost", zkAddr,
|
"-zkHost", zkAddr,
|
||||||
|
@ -272,7 +273,7 @@ public class SolrCLIZkUtilsTest extends SolrCloudTestCase {
|
||||||
// copy individual file down
|
// copy individual file down
|
||||||
//src and cp3 are valid
|
//src and cp3 are valid
|
||||||
|
|
||||||
String localNamed = tmp.normalize().toString() + "/localnamed/renamed.txt";
|
String localNamed = tmp.normalize().toString() + File.separator + "localnamed" + File.separator + "renamed.txt";
|
||||||
args = new String[]{
|
args = new String[]{
|
||||||
"-src", "zk:/cp4/solrconfig.xml",
|
"-src", "zk:/cp4/solrconfig.xml",
|
||||||
"-dst", "file:" + localNamed,
|
"-dst", "file:" + localNamed,
|
||||||
|
@ -404,7 +405,7 @@ public class SolrCLIZkUtilsTest extends SolrCloudTestCase {
|
||||||
// Files are in mv2
|
// Files are in mv2
|
||||||
// Now fail if we specify "file:". Everything should still be in /mv2
|
// Now fail if we specify "file:". Everything should still be in /mv2
|
||||||
args = new String[]{
|
args = new String[]{
|
||||||
"-src", "file:/mv2",
|
"-src", "file:" + File.separator + "mv2",
|
||||||
"-dst", "/mv3",
|
"-dst", "/mv3",
|
||||||
"-zkHost", zkAddr,
|
"-zkHost", zkAddr,
|
||||||
};
|
};
|
||||||
|
|
|
@ -119,33 +119,36 @@ public class ZkMaintenanceUtils {
|
||||||
throw new SolrServerException("Local path " + Paths.get(src).toAbsolutePath() + " is a directory and recurse is false");
|
throw new SolrServerException("Local path " + Paths.get(src).toAbsolutePath() + " is a directory and recurse is false");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (srcIsZk == false && dstIsZk == false) {
|
|
||||||
throw new SolrServerException("At least one of the source and dest parameters must be prefixed with 'zk:' ");
|
|
||||||
}
|
|
||||||
if (dstIsZk && dst.length() == 0) {
|
if (dstIsZk && dst.length() == 0) {
|
||||||
dst = "/"; // for consistency, one can copy from zk: and send to zk:/
|
dst = "/"; // for consistency, one can copy from zk: and send to zk:/
|
||||||
}
|
}
|
||||||
dst = normalizeDest(src, dst);
|
dst = normalizeDest(src, dst, srcIsZk, dstIsZk);
|
||||||
|
|
||||||
|
// ZK -> ZK copy.
|
||||||
if (srcIsZk && dstIsZk) {
|
if (srcIsZk && dstIsZk) {
|
||||||
traverseZkTree(zkClient, src, VISIT_ORDER.VISIT_PRE, new ZkCopier(zkClient, src, dst));
|
traverseZkTree(zkClient, src, VISIT_ORDER.VISIT_PRE, new ZkCopier(zkClient, src, dst));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//local -> ZK copy
|
||||||
if (dstIsZk) {
|
if (dstIsZk) {
|
||||||
uploadToZK(zkClient, Paths.get(src), dst, null);
|
uploadToZK(zkClient, Paths.get(src), dst, null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copying individual files from ZK requires special handling since downloadFromZK assumes it's a directory.
|
// Copying individual files from ZK requires special handling since downloadFromZK assumes the node has children.
|
||||||
// This is kind of a weak test for the notion of "directory" on Zookeeper.
|
// This is kind of a weak test for the notion of "directory" on Zookeeper.
|
||||||
|
// ZK -> local copy where ZK is a parent node
|
||||||
if (zkClient.getChildren(src, null, true).size() > 0) {
|
if (zkClient.getChildren(src, null, true).size() > 0) {
|
||||||
downloadFromZK(zkClient, src, Paths.get(dst));
|
downloadFromZK(zkClient, src, Paths.get(dst));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Single file ZK -> local copy where ZK is a leaf node
|
||||||
if (Files.isDirectory(Paths.get(dst))) {
|
if (Files.isDirectory(Paths.get(dst))) {
|
||||||
if (dst.endsWith("/") == false) dst += "/";
|
if (dst.endsWith(File.separator) == false) dst += File.separator;
|
||||||
dst = normalizeDest(src, dst);
|
dst = normalizeDest(src, dst, srcIsZk, dstIsZk);
|
||||||
}
|
}
|
||||||
byte[] data = zkClient.getData(src, null, null, true);
|
byte[] data = zkClient.getData(src, null, null, true);
|
||||||
Path filename = Paths.get(dst);
|
Path filename = Paths.get(dst);
|
||||||
|
@ -154,18 +157,19 @@ public class ZkMaintenanceUtils {
|
||||||
Files.write(filename, data);
|
Files.write(filename, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the dest ends with a separator, it's a directory or non-leaf znode, so return the
|
||||||
private static String normalizeDest(String srcName, String dstName) {
|
// last element of the src to appended to the dstName.
|
||||||
|
private static String normalizeDest(String srcName, String dstName, boolean srcIsZk, boolean dstIsZk) {
|
||||||
// Special handling for "."
|
// Special handling for "."
|
||||||
if (dstName.equals(".")) {
|
if (dstName.equals(".")) {
|
||||||
return Paths.get(".").normalize().toAbsolutePath().toString();
|
return Paths.get(".").normalize().toAbsolutePath().toString();
|
||||||
}
|
}
|
||||||
// Pull the last element of the src path and add it to the dst if the src does NOT end in a slash
|
|
||||||
|
|
||||||
// If the source ends in a slash, do not append the last segment to the dest
|
String dstSeparator = (dstIsZk) ? "/" : File.separator;
|
||||||
|
String srcSeparator = (srcIsZk) ? "/" : File.separator;
|
||||||
|
|
||||||
if (dstName.endsWith("/")) { // Dest is a directory.
|
if (dstName.endsWith(dstSeparator)) { // Dest is a directory or non-leaf znode, append last element of the src path.
|
||||||
int pos = srcName.lastIndexOf("/");
|
int pos = srcName.lastIndexOf(srcSeparator);
|
||||||
if (pos < 0) {
|
if (pos < 0) {
|
||||||
dstName += srcName;
|
dstName += srcName;
|
||||||
} else {
|
} else {
|
||||||
|
@ -178,7 +182,7 @@ public class ZkMaintenanceUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void moveZnode(SolrZkClient zkClient, String src, String dst) throws SolrServerException, KeeperException, InterruptedException {
|
public static void moveZnode(SolrZkClient zkClient, String src, String dst) throws SolrServerException, KeeperException, InterruptedException {
|
||||||
String destName = normalizeDest(src, dst);
|
String destName = normalizeDest(src, dst, true, true);
|
||||||
|
|
||||||
// Special handling if the source has no children, i.e. copying just a single file.
|
// Special handling if the source has no children, i.e. copying just a single file.
|
||||||
if (zkClient.getChildren(src, null, true).size() == 0) {
|
if (zkClient.getChildren(src, null, true).size() == 0) {
|
||||||
|
@ -384,12 +388,11 @@ public class ZkMaintenanceUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Take into account Windows file separaters when making a Znode's name.
|
// Take into account Windows file separators when making a Znode's name.
|
||||||
public static String createZkNodeName(String zkRoot, Path root, Path file) {
|
public static String createZkNodeName(String zkRoot, Path root, Path file) {
|
||||||
String relativePath = root.relativize(file).toString();
|
String relativePath = root.relativize(file).toString();
|
||||||
// Windows shenanigans
|
// Windows shenanigans
|
||||||
String separator = root.getFileSystem().getSeparator();
|
if ("\\".equals(File.separator))
|
||||||
if ("\\".equals(separator))
|
|
||||||
relativePath = relativePath.replaceAll("\\\\", "/");
|
relativePath = relativePath.replaceAll("\\\\", "/");
|
||||||
// It's possible that the relative path and file are the same, in which case
|
// It's possible that the relative path and file are the same, in which case
|
||||||
// adding the bare slash is A Bad Idea unless it's a non-leaf data node
|
// adding the bare slash is A Bad Idea unless it's a non-leaf data node
|
||||||
|
|
Loading…
Reference in New Issue