blob: f0ce5d7a9617fce74fb2f097e51ccbc7f131c150 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.solr.cloud;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.solr.common.cloud.ZkMaintenanceUtils;
import org.apache.solr.util.SolrCLI;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.data.Stat;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
public class SolrCLIZkUtilsTest extends SolrCloudTestCase {
@BeforeClass
public static void setupCluster() throws Exception {
configureCluster(1)
.addConfig("conf1", TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
.configure();
zkAddr = cluster.getZkServer().getZkAddress();
zkClient = new SolrZkClient(zkAddr, 30000);
}
@AfterClass
public static void closeConn() {
if (null != zkClient) {
zkClient.close();
zkClient = null;
}
zkAddr = null;
}
private static String zkAddr;
private static SolrZkClient zkClient;
@Test
public void testUpconfig() throws Exception {
// Use a full, explicit path for configset.
Path configSet = TEST_PATH().resolve("configsets");
Path srcPathCheck = configSet.resolve("cloud-subdirs").resolve("conf");
AbstractDistribZkTestBase.copyConfigUp(configSet, "cloud-subdirs", "upconfig1", zkAddr);
// Now do we have that config up on ZK?
verifyZkLocalPathsMatch(srcPathCheck, "/configs/upconfig1");
// Now just use a name in the configsets directory, do we find it?
configSet = TEST_PATH().resolve("configsets");
String[] args = new String[]{
"-confname", "upconfig2",
"-confdir", "cloud-subdirs",
"-zkHost", zkAddr,
"-configsetsDir", configSet.toAbsolutePath().toString(),
};
SolrCLI.ConfigSetUploadTool tool = new SolrCLI.ConfigSetUploadTool();
int res = tool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(tool.getOptions()), args));
assertEquals("tool should have returned 0 for success ", 0, res);
// Now do we have that config up on ZK?
verifyZkLocalPathsMatch(srcPathCheck, "/configs/upconfig2");
// do we barf on a bogus path?
args = new String[]{
"-confname", "upconfig3",
"-confdir", "nothinghere",
"-zkHost", zkAddr,
"-configsetsDir", configSet.toAbsolutePath().toString(),
};
res = tool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(tool.getOptions()), args));
assertTrue("tool should have returned non-zero for failure ", 0 != res);
String content = new String(zkClient.getData("/configs/upconfig2/schema.xml", null, null, true), StandardCharsets.UTF_8);
assertTrue("There should be content in the node! ", content.contains("Apache Software Foundation"));
}
@Test
public void testDownconfig() throws Exception {
Path tmp = Paths.get(createTempDir("downConfigNewPlace").toAbsolutePath().toString(), "myconfset");
// First we need a configset on ZK to bring down.
Path configSet = TEST_PATH().resolve("configsets");
Path srcPathCheck = configSet.resolve("cloud-subdirs").resolve("conf");
AbstractDistribZkTestBase.copyConfigUp(configSet, "cloud-subdirs", "downconfig1", zkAddr);
// Now do we have that config up on ZK?
verifyZkLocalPathsMatch(srcPathCheck, "/configs/downconfig1");
String[] args = new String[]{
"-confname", "downconfig1",
"-confdir", tmp.toAbsolutePath().toString(),
"-zkHost", zkAddr,
};
SolrCLI.ConfigSetDownloadTool downTool = new SolrCLI.ConfigSetDownloadTool();
int 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/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
AbstractDistribZkTestBase.copyConfigUp(tmp.getParent(), "myconfset", "downconfig2", zkAddr);
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
public void testCp() throws Exception {
// First get something up on ZK
Path configSet = TEST_PATH().resolve("configsets");
Path srcPathCheck = configSet.resolve("cloud-subdirs").resolve("conf");
AbstractDistribZkTestBase.copyConfigUp(configSet, "cloud-subdirs", "cp1", zkAddr);
// Now copy it somewhere else on ZK.
String[] args = new String[]{
"-src", "zk:/configs/cp1",
"-dst", "zk:/cp2",
"-recurse", "true",
"-zkHost", zkAddr,
};
SolrCLI.ZkCpTool cpTool = new SolrCLI.ZkCpTool();
int res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args));
assertEquals("Copy from zk -> zk should have succeeded.", 0, res);
verifyZnodesMatch("/configs/cp1", "/cp2");
// try with zk->local
Path tmp = createTempDir("tmpNewPlace2");
args = new String[]{
"-src", "zk:/configs/cp1",
"-dst", "file:" + tmp.toAbsolutePath().toString(),
"-recurse", "true",
"-zkHost", zkAddr,
};
res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args));
assertEquals("Copy should have succeeded.", 0, res);
verifyZkLocalPathsMatch(tmp, "/configs/cp1");
// try with zk->local no file: prefix
tmp = createTempDir("tmpNewPlace3");
args = new String[]{
"-src", "zk:/configs/cp1",
"-dst", tmp.toAbsolutePath().toString(),
"-recurse", "true",
"-zkHost", zkAddr,
};
res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args));
assertEquals("Copy should have succeeded.", 0, res);
verifyZkLocalPathsMatch(tmp, "/configs/cp1");
// try with local->zk
args = new String[]{
"-src", srcPathCheck.toAbsolutePath().toString(),
"-dst", "zk:/cp3",
"-recurse", "true",
"-zkHost", zkAddr,
};
res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args));
assertEquals("Copy should have succeeded.", 0, res);
verifyZkLocalPathsMatch(srcPathCheck, "/cp3");
// try with local->zk, file: specified
args = new String[]{
"-src", "file:" + srcPathCheck.toAbsolutePath().toString(),
"-dst", "zk:/cp4",
"-recurse", "true",
"-zkHost", zkAddr,
};
res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args));
assertEquals("Copy should have succeeded.", 0, res);
verifyZkLocalPathsMatch(srcPathCheck, "/cp4");
// try with recurse not specified
args = new String[]{
"-src", "file:" + srcPathCheck.toAbsolutePath().toString(),
"-dst", "zk:/cp5Fail",
"-zkHost", zkAddr,
};
res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args));
assertTrue("Copy should NOT have succeeded, recurse not specified.", 0 != res);
// try with recurse = false
args = new String[]{
"-src", "file:" + srcPathCheck.toAbsolutePath().toString(),
"-dst", "zk:/cp6Fail",
"-recurse", "false",
"-zkHost", zkAddr,
};
res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args));
assertTrue("Copy should NOT have succeeded, recurse set to false.", 0 != res);
// 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.
// copy to local ending in separator
//src and cp3 and cp4 are valid
String localSlash = tmp.normalize() + File.separator +"cpToLocal" + File.separator;
args = new String[]{
"-src", "zk:/cp3/schema.xml",
"-dst", localSlash,
"-recurse", "false",
"-zkHost", zkAddr,
};
res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args));
assertEquals("Copy should nave created intermediate directory locally.", 0, res);
assertTrue("File should have been copied to a directory successfully", Files.exists(Paths.get(localSlash, "schema.xml")));
// copy to ZK ending in '/'.
//src and cp3 are valid
args = new String[]{
"-src", "file:" + srcPathCheck.normalize().toAbsolutePath().toString() + File.separator + "solrconfig.xml",
"-dst", "zk:/powerup/",
"-recurse", "false",
"-zkHost", zkAddr,
};
res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args));
assertEquals("Copy up to intermediate file should have succeeded.", 0, res);
assertTrue("Should have created an intermediate node on ZK", zkClient.exists("/powerup/solrconfig.xml", true));
// copy individual file up
//src and cp3 are valid
args = new String[]{
"-src", "file:" + srcPathCheck.normalize().toAbsolutePath().toString() + File.separator + "solrconfig.xml",
"-dst", "zk:/copyUpFile.xml",
"-recurse", "false",
"-zkHost", zkAddr,
};
res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args));
assertEquals("Copy up to named file should have succeeded.", 0, res);
assertTrue("Should NOT have created an intermediate node on ZK", zkClient.exists("/copyUpFile.xml", true));
// copy individual file down
//src and cp3 are valid
String localNamed = tmp.normalize().toString() + File.separator + "localnamed" + File.separator + "renamed.txt";
args = new String[]{
"-src", "zk:/cp4/solrconfig.xml",
"-dst", "file:" + localNamed,
"-recurse", "false",
"-zkHost", zkAddr,
};
res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args));
assertEquals("Copy to local named file should have succeeded.", 0, res);
Path locPath = Paths.get(localNamed);
assertTrue("Should have found file: " + localNamed, Files.exists(locPath));
assertTrue("Should be an individual file", Files.isRegularFile(locPath));
assertTrue("File should have some data", Files.size(locPath) > 100);
boolean foundApache = false;
for (String line : Files.readAllLines(locPath, Charset.forName("UTF-8"))) {
if (line.contains("Apache Software Foundation")) {
foundApache = true;
break;
}
}
assertTrue("Should have found Apache Software Foundation in the file! ", foundApache);
// Test copy from somwehere in ZK to the root of ZK.
args = new String[]{
"-src", "zk:/cp4/solrconfig.xml",
"-dst", "zk:/",
"-recurse", "false",
"-zkHost", zkAddr,
};
res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args));
assertEquals("Copy from somewhere in ZK to ZK root should have succeeded.", 0, res);
assertTrue("Should have found znode /solrconfig.xml: ", zkClient.exists("/solrconfig.xml", true));
// Check that the form path/ works for copying files up. Should append the last bit of the source path to the dst
args = new String[]{
"-src", "file:" + srcPathCheck.toAbsolutePath().toString(),
"-dst", "zk:/cp7/",
"-recurse", "true",
"-zkHost", zkAddr,
};
res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args));
assertEquals("Copy should have succeeded.", 0, res);
verifyZkLocalPathsMatch(srcPathCheck, "/cp7/" + srcPathCheck.getFileName().toString());
// Check for an intermediate ZNODE having content. You know cp7/stopwords is a parent node.
tmp = createTempDir("dirdata");
Path file = Paths.get(tmp.toAbsolutePath().toString(), "zknode.data");
List<String> lines = new ArrayList<>();
lines.add("{Some Arbitrary Data}");
Files.write(file, lines, Charset.forName("UTF-8"));
// First, just copy the data up the cp7 since it's a directory.
args = new String[]{
"-src", "file:" + file.toAbsolutePath().toString(),
"-dst", "zk:/cp7/conf/stopwords/",
"-recurse", "false",
"-zkHost", zkAddr,
};
res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args));
assertEquals("Copy should have succeeded.", 0, res);
String content = new String(zkClient.getData("/cp7/conf/stopwords", null, null, true), StandardCharsets.UTF_8);
assertTrue("There should be content in the node! ", content.contains("{Some Arbitrary Data}"));
res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args));
assertEquals("Copy should have succeeded.", 0, res);
tmp = createTempDir("cp8");
args = new String[]{
"-src", "zk:/cp7",
"-dst", "file:" + tmp.toAbsolutePath().toString(),
"-recurse", "true",
"-zkHost", zkAddr,
};
res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args));
assertEquals("Copy should have succeeded.", 0, res);
// Next, copy cp7 down and verify that zknode.data exists for cp7
Path zData = Paths.get(tmp.toAbsolutePath().toString(), "conf/stopwords/zknode.data");
assertTrue("znode.data should have been copied down", zData.toFile().exists());
// Finally, copy up to cp8 and verify that the data is up there.
args = new String[]{
"-src", "file:" + tmp.toAbsolutePath().toString(),
"-dst", "zk:/cp9",
"-recurse", "true",
"-zkHost", zkAddr,
};
res = cpTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(cpTool.getOptions()), args));
assertEquals("Copy should have succeeded.", 0, res);
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
public void testMv() throws Exception {
// First get something up on ZK
Path configSet = TEST_PATH().resolve("configsets");
Path srcPathCheck = configSet.resolve("cloud-subdirs").resolve("conf");
AbstractDistribZkTestBase.copyConfigUp(configSet, "cloud-subdirs", "mv1", zkAddr);
// Now move it somewhere else.
String[] args = new String[]{
"-src", "zk:/configs/mv1",
"-dst", "zk:/mv2",
"-zkHost", zkAddr,
};
SolrCLI.ZkMvTool mvTool = new SolrCLI.ZkMvTool();
int res = mvTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(mvTool.getOptions()), args));
assertEquals("Move should have succeeded.", 0, res);
// Now does the moved directory match the original on disk?
verifyZkLocalPathsMatch(srcPathCheck, "/mv2");
// And are we sure the old path is gone?
assertFalse("/configs/mv1 Znode should not be there: ", zkClient.exists("/configs/mv1", true));
// Files are in mv2
// Now fail if we specify "file:". Everything should still be in /mv2
args = new String[]{
"-src", "file:" + File.separator + "mv2",
"-dst", "/mv3",
"-zkHost", zkAddr,
};
// Still in mv2
res = mvTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(mvTool.getOptions()), args));
assertTrue("Move should NOT have succeeded with file: specified.", 0 != res);
// Let's move it to yet another place with no zk: prefix.
args = new String[]{
"-src", "/mv2",
"-dst", "/mv4",
"-zkHost", zkAddr,
};
res = mvTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(mvTool.getOptions()), args));
assertEquals("Move should have succeeded.", 0, res);
assertFalse("Znode /mv3 really should be gone", zkClient.exists("/mv3", true));
// Now does the moved directory match the original on disk?
verifyZkLocalPathsMatch(srcPathCheck, "/mv4");
args = new String[]{
"-src", "/mv4/solrconfig.xml",
"-dst", "/testmvsingle/solrconfig.xml",
"-zkHost", zkAddr,
};
res = mvTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(mvTool.getOptions()), args));
assertEquals("Move should have succeeded.", 0, res);
assertTrue("Should be able to move a single file", zkClient.exists("/testmvsingle/solrconfig.xml", true));
zkClient.makePath("/parentNode", true);
// what happens if the destination ends with a slash?
args = new String[]{
"-src", "/mv4/schema.xml",
"-dst", "/parentnode/",
"-zkHost", zkAddr,
};
res = mvTool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(mvTool.getOptions()), args));
assertEquals("Move should have succeeded.", 0, res);
assertTrue("Should be able to move a single file to a parent znode", zkClient.exists("/parentnode/schema.xml", true));
String content = new String(zkClient.getData("/parentnode/schema.xml", null, null, true), StandardCharsets.UTF_8);
assertTrue("There should be content in the node! ", content.contains("Apache Software Foundation"));
}
@Test
public void testLs() throws Exception {
Path configSet = TEST_PATH().resolve("configsets");
AbstractDistribZkTestBase.copyConfigUp(configSet, "cloud-subdirs", "lister", zkAddr);
// Should only find a single level.
String[] args = new String[]{
"-path", "/configs",
"-zkHost", zkAddr,
};
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos, false, StandardCharsets.UTF_8.name());
SolrCLI.ZkLsTool tool = new SolrCLI.ZkLsTool(ps);
int res = tool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(tool.getOptions()), args));
String content = new String(baos.toByteArray(), StandardCharsets.UTF_8);
assertEquals("List should have succeeded", res, 0);
assertTrue("Return should contain the conf directory", content.contains("lister"));
assertFalse("Return should NOT contain a child node", content.contains("solrconfig.xml"));
// simple ls recurse=false
args = new String[]{
"-path", "/configs",
"-recurse", "false",
"-zkHost", zkAddr,
};
res = tool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(tool.getOptions()), args));
content = new String(baos.toByteArray(), StandardCharsets.UTF_8);
assertEquals("List should have succeeded", res, 0);
assertTrue("Return should contain the conf directory", content.contains("lister"));
assertFalse("Return should NOT contain a child node", content.contains("solrconfig.xml"));
// recurse=true
args = new String[]{
"-path", "/configs",
"-recurse", "true",
"-zkHost", zkAddr,
};
res = tool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(tool.getOptions()), args));
content = new String(baos.toByteArray(), StandardCharsets.UTF_8);
assertEquals("List should have succeeded", res, 0);
assertTrue("Return should contain the conf directory", content.contains("lister"));
assertTrue("Return should contain a child node", content.contains("solrconfig.xml"));
// Saw a case where going from root foo'd, so test it.
args = new String[]{
"-path", "/",
"-recurse", "true",
"-zkHost", zkAddr,
};
res = tool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(tool.getOptions()), args));
content = new String(baos.toByteArray(), StandardCharsets.UTF_8);
assertEquals("List should have succeeded", res, 0);
assertTrue("Return should contain the conf directory", content.contains("lister"));
assertTrue("Return should contain a child node", content.contains("solrconfig.xml"));
args = new String[]{
"-path", "/",
"-zkHost", zkAddr,
};
res = tool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(tool.getOptions()), args));
content = new String(baos.toByteArray(), StandardCharsets.UTF_8);
assertEquals("List should have succeeded", res, 0);
assertFalse("Return should not contain /zookeeper", content.contains("/zookeeper"));
// Saw a case where ending in slash foo'd, so test it.
args = new String[]{
"-path", "/configs/",
"-recurse", "true",
"-zkHost", zkAddr,
};
res = tool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(tool.getOptions()), args));
content = new String(baos.toByteArray(), StandardCharsets.UTF_8);
assertEquals("List should have succeeded", res, 0);
assertTrue("Return should contain the conf directory", content.contains("lister"));
assertTrue("Return should contain a child node", content.contains("solrconfig.xml"));
}
@Test
public void testRm() throws Exception {
Path configSet = TEST_PATH().resolve("configsets");
Path srcPathCheck = configSet.resolve("cloud-subdirs").resolve("conf");
AbstractDistribZkTestBase.copyConfigUp(configSet, "cloud-subdirs", "rm1", zkAddr);
AbstractDistribZkTestBase.copyConfigUp(configSet, "cloud-subdirs", "rm2", zkAddr);
// Should fail if recurse not set.
String[] args = new String[]{
"-path", "/configs/rm1",
"-zkHost", zkAddr,
};
SolrCLI.ZkRmTool tool = new SolrCLI.ZkRmTool();
int res = tool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(tool.getOptions()), args));
assertTrue("Should have failed to remove node with children unless -recurse is set to true", res != 0);
// Are we sure all the znodes are still there?
verifyZkLocalPathsMatch(srcPathCheck, "/configs/rm1");
args = new String[]{
"-path", "zk:/configs/rm1",
"-recurse", "false",
"-zkHost", zkAddr,
};
res = tool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(tool.getOptions()), args));
assertTrue("Should have failed to remove node with children if -recurse is set to false", res != 0);
args = new String[]{
"-path", "/configs/rm1",
"-recurse", "true",
"-zkHost", zkAddr,
};
res = tool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(tool.getOptions()), args));
assertEquals("Should have removed node /configs/rm1", res, 0);
assertFalse("Znode /configs/toremove really should be gone", zkClient.exists("/configs/rm1", true));
// Check that zk prefix also works.
args = new String[]{
"-path", "zk:/configs/rm2",
"-recurse", "true",
"-zkHost", zkAddr,
};
res = tool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(tool.getOptions()), args));
assertEquals("Should have removed node /configs/rm2", res, 0);
assertFalse("Znode /configs/toremove2 really should be gone", zkClient.exists("/configs/rm2", true));
// This should silently just refuse to do anything to the / or /zookeeper
args = new String[]{
"-path", "zk:/",
"-recurse", "true",
"-zkHost", zkAddr,
};
AbstractDistribZkTestBase.copyConfigUp(configSet, "cloud-subdirs", "rm3", zkAddr);
res = tool.runTool(SolrCLI.processCommandLineArgs(SolrCLI.joinCommonAndToolOptions(tool.getOptions()), args));
assertFalse("Should fail when trying to remove /.", res == 0);
}
// Check that all children of fileRoot are children of zkRoot and vice-versa
private void verifyZkLocalPathsMatch(Path fileRoot, String zkRoot) throws IOException, KeeperException, InterruptedException {
verifyAllFilesAreZNodes(fileRoot, zkRoot);
verifyAllZNodesAreFiles(fileRoot, zkRoot);
}
private static boolean isEphemeral(String zkPath) throws KeeperException, InterruptedException {
Stat znodeStat = zkClient.exists(zkPath, null, true);
return znodeStat.getEphemeralOwner() != 0;
}
void verifyAllZNodesAreFiles(Path fileRoot, String zkRoot) throws KeeperException, InterruptedException {
for (String child : zkClient.getChildren(zkRoot, null, true)) {
// Skip ephemeral nodes
if (zkRoot.endsWith("/") == false) zkRoot += "/";
if (isEphemeral(zkRoot + child)) continue;
Path thisPath = Paths.get(fileRoot.toAbsolutePath().toString(), child);
assertTrue("Znode " + child + " should have been found on disk at " + fileRoot.toAbsolutePath().toString(),
Files.exists(thisPath));
verifyAllZNodesAreFiles(thisPath, zkRoot + child);
}
}
void verifyAllFilesAreZNodes(Path fileRoot, String zkRoot) throws IOException {
Files.walkFileTree(fileRoot, new SimpleFileVisitor<Path>() {
void checkPathOnZk(Path path) {
String znode = ZkMaintenanceUtils.createZkNodeName(zkRoot, fileRoot, path);
try { // It's easier to catch this exception and fail than catch it everywher eles.
assertTrue("Should have found " + znode + " on Zookeeper", zkClient.exists(znode, true));
} catch (Exception e) {
fail("Caught unexpected exception " + e.getMessage() + " Znode we were checking " + znode);
}
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
assertTrue("Path should start at proper place!", file.startsWith(fileRoot));
checkPathOnZk(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
checkPathOnZk(dir);
return FileVisitResult.CONTINUE;
}
});
}
// Insure that all znodes in first are in second and vice-versa
private void verifyZnodesMatch(String first, String second) throws KeeperException, InterruptedException {
verifyFirstZNodesInSecond(first, second);
verifyFirstZNodesInSecond(second, first);
}
// Note, no folderol here with Windows path names.
private void verifyFirstZNodesInSecond(String first, String second) throws KeeperException, InterruptedException {
for (String node : zkClient.getChildren(first, null, true)) {
String fNode = first + "/" + node;
String sNode = second + "/" + node;
assertTrue("Node " + sNode + " not found. Exists on " + fNode, zkClient.exists(sNode, true));
verifyFirstZNodesInSecond(fNode, sNode);
}
}
}