/*
* 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.handler.admin;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import com.carrotsearch.randomizedtesting.rules.SystemPropertiesRestoreRule;
import org.apache.commons.io.FileUtils;
import org.apache.lucene.util.Constants;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.client.solrj.embedded.JettySolrRunner;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.request.CoreAdminRequest;
import org.apache.solr.client.solrj.request.CoreStatus;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.CoreDescriptor;
import org.apache.solr.core.SolrCore;
import org.apache.solr.response.SolrQueryResponse;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
public class CoreAdminHandlerTest extends SolrTestCaseJ4 {
@BeforeClass
public static void beforeClass() throws Exception {
initCore("solrconfig.xml", "schema.xml");
}
@Rule
public TestRule solrTestRules = RuleChain.outerRule(new SystemPropertiesRestoreRule());
public String getCoreName() { return this.getClass().getName() + "_sys_vars"; }
@Test
public void testCreateWithSysVars() throws Exception {
useFactory(null); // I require FS-based indexes for this test.
final File workDir = createTempDir(getCoreName()).toFile();
String coreName = "with_sys_vars";
File instDir = new File(workDir, coreName);
File subHome = new File(instDir, "conf");
assertTrue("Failed to make subdirectory ", subHome.mkdirs());
// Be sure we pick up sysvars when we create this
String srcDir = SolrTestCaseJ4.TEST_HOME() + "/collection1/conf";
FileUtils.copyFile(new File(srcDir, "schema-tiny.xml"), new File(subHome, "schema_ren.xml"));
FileUtils.copyFile(new File(srcDir, "solrconfig-minimal.xml"), new File(subHome, "solrconfig_ren.xml"));
FileUtils.copyFile(new File(srcDir, "solrconfig.snippet.randomindexconfig.xml"),
new File(subHome, "solrconfig.snippet.randomindexconfig.xml"));
final CoreContainer cores = h.getCoreContainer();
final CoreAdminHandler admin = new CoreAdminHandler(cores);
// create a new core (using CoreAdminHandler) w/ properties
System.setProperty("INSTDIR_TEST", instDir.getAbsolutePath());
System.setProperty("CONFIG_TEST", "solrconfig_ren.xml");
System.setProperty("SCHEMA_TEST", "schema_ren.xml");
File dataDir = new File(workDir.getAbsolutePath(), "data_diff");
System.setProperty("DATA_TEST", dataDir.getAbsolutePath());
SolrQueryResponse resp = new SolrQueryResponse();
admin.handleRequestBody
(req(CoreAdminParams.ACTION,
CoreAdminParams.CoreAdminAction.CREATE.toString(),
CoreAdminParams.NAME, getCoreName(),
CoreAdminParams.INSTANCE_DIR, "${INSTDIR_TEST}",
CoreAdminParams.CONFIG, "${CONFIG_TEST}",
CoreAdminParams.SCHEMA, "${SCHEMA_TEST}",
CoreAdminParams.DATA_DIR, "${DATA_TEST}"),
resp);
assertNull("Exception on create", resp.getException());
// Now assert that certain values are properly dereferenced in the process of creating the core, see
// SOLR-4982.
// Should NOT be a datadir named ${DATA_TEST} (literal). This is the bug after all
File badDir = new File(instDir, "${DATA_TEST}");
assertFalse("Should have substituted the sys var, found file " + badDir.getAbsolutePath(), badDir.exists());
// For the other 3 vars, we couldn't get past creating the core fi dereferencing didn't work correctly.
// Should have segments in the directory pointed to by the ${DATA_TEST}.
File test = new File(dataDir, "index");
assertTrue("Should have found index dir at " + test.getAbsolutePath(), test.exists());
}
@Test
public void testCoreAdminHandler() throws Exception {
final File workDir = createTempDir().toFile();
final CoreContainer cores = h.getCoreContainer();
final CoreAdminHandler admin = new CoreAdminHandler(cores);
Path instDir;
try (SolrCore template = cores.getCore("collection1")) {
assertNotNull(template);
instDir = template.getCoreDescriptor().getInstanceDir();
}
assertTrue("instDir doesn't exist: " + instDir, Files.exists(instDir));
final File instPropFile = new File(workDir, "instProp");
FileUtils.copyDirectory(instDir.toFile(), instPropFile);
SolrQueryResponse resp = new SolrQueryResponse();
// Sneaking in a test for using a bad core name
try {
admin.handleRequestBody
(req(CoreAdminParams.ACTION,
CoreAdminParams.CoreAdminAction.CREATE.toString(),
CoreAdminParams.INSTANCE_DIR, instPropFile.getAbsolutePath(),
CoreAdminParams.NAME, "ugly$core=name"),
resp);
} catch (SolrException se) {
assertTrue("Expected error message for bad core name.", se.toString().contains("Invalid core"));
}
CoreDescriptor cd = cores.getCoreDescriptor("ugly$core=name");
assertNull("Should NOT have added this core!", cd);
// create a new core (using CoreAdminHandler) w/ properties
admin.handleRequestBody
(req(CoreAdminParams.ACTION,
CoreAdminParams.CoreAdminAction.CREATE.toString(),
CoreAdminParams.INSTANCE_DIR, instPropFile.getAbsolutePath(),
CoreAdminParams.NAME, "props",
CoreAdminParams.PROPERTY_PREFIX + "hoss","man",
CoreAdminParams.PROPERTY_PREFIX + "foo","baz"),
resp);
assertNull("Exception on create", resp.getException());
cd = cores.getCoreDescriptor("props");
assertNotNull("Core not added!", cd);
assertEquals(cd.getCoreProperty("hoss", null), "man");
assertEquals(cd.getCoreProperty("foo", null), "baz");
// attempt to create a bogus core and confirm failure
ignoreException("Could not load config");
try {
resp = new SolrQueryResponse();
admin.handleRequestBody
(req(CoreAdminParams.ACTION,
CoreAdminParams.CoreAdminAction.CREATE.toString(),
CoreAdminParams.NAME, "bogus_dir_core",
CoreAdminParams.INSTANCE_DIR, "dir_does_not_exist_127896"),
resp);
fail("bogus collection created ok");
} catch (SolrException e) {
// :NOOP:
// :TODO: CoreAdminHandler's exception messages are terrible, otherwise we could assert something useful here
}
unIgnoreException("Could not load config");
// check specifically for status of the failed core name
resp = new SolrQueryResponse();
admin.handleRequestBody
(req(CoreAdminParams.ACTION,
CoreAdminParams.CoreAdminAction.STATUS.toString(),
CoreAdminParams.CORE, "bogus_dir_core"),
resp);
Map<String,Exception> failures =
(Map<String,Exception>) resp.getValues().get("initFailures");
assertNotNull("core failures is null", failures);
NamedList<Object> status =
(NamedList<Object>)resp.getValues().get("status");
assertNotNull("core status is null", status);
assertEquals("wrong number of core failures", 1, failures.size());
Exception fail = failures.get("bogus_dir_core");
assertNotNull("null failure for test core", fail);
assertTrue("init failure doesn't mention problem: " + fail.getCause().getMessage(),
0 < fail.getCause().getMessage().indexOf("dir_does_not_exist"));
assertEquals("bogus_dir_core status isn't empty",
0, ((NamedList)status.get("bogus_dir_core")).size());
//Try renaming the core, we should fail
// First assert that the props core exists
cd = cores.getCoreDescriptor("props");
assertNotNull("Core disappeared!", cd);
// now rename it something else just for kicks since we don't actually test this that I could find.
admin.handleRequestBody
(req(CoreAdminParams.ACTION,
CoreAdminParams.CoreAdminAction.RENAME.toString(),
CoreAdminParams.CORE, "props",
CoreAdminParams.OTHER, "rename_me"),
resp);
cd = cores.getCoreDescriptor("rename_me");
assertNotNull("Core should have been renamed!", cd);
// Rename it something bogus and see if you get an exception, the old core is still there and the bogus one isn't
try {
admin.handleRequestBody
(req(CoreAdminParams.ACTION,
CoreAdminParams.CoreAdminAction.RENAME.toString(),
CoreAdminParams.CORE, "rename_me",
CoreAdminParams.OTHER, "bad$name"),
resp);
} catch (SolrException e) { // why the heck does create return a SolrException (admittedly wrapping an IAE)
assertTrue("Expected error message for bad core name.", e.getMessage().contains("Invalid core"));
}
cd = cores.getCoreDescriptor("bad$name");
assertNull("Core should NOT exist!", cd);
cd = cores.getCoreDescriptor("rename_me");
assertNotNull("Core should have been renamed!", cd);
// :TODO: because of SOLR-3665 we can't ask for status from all cores
}
@Test
public void testDeleteInstanceDir() throws Exception {
File solrHomeDirectory = new File(initCoreDataDir, getClass().getName() + "-corex-"
+ System.nanoTime());
solrHomeDirectory.mkdirs();
copySolrHomeToTemp(solrHomeDirectory, "corex");
File corex = new File(solrHomeDirectory, "corex");
FileUtils.write(new File(corex, "core.properties"), "", StandardCharsets.UTF_8);
JettySolrRunner runner = new JettySolrRunner(solrHomeDirectory.getAbsolutePath(), buildJettyConfig("/solr"));
runner.start();
try (HttpSolrClient client = getHttpSolrClient(runner.getBaseUrl() + "/corex")) {
client.setConnectionTimeout(SolrTestCaseJ4.DEFAULT_CONNECTION_TIMEOUT);
client.setSoTimeout(SolrTestCaseJ4.DEFAULT_CONNECTION_TIMEOUT);
SolrInputDocument doc = new SolrInputDocument();
doc.addField("id", "123");
client.add(doc);
client.commit();
}
try (HttpSolrClient client = getHttpSolrClient(runner.getBaseUrl().toString())) {
client.setConnectionTimeout(SolrTestCaseJ4.DEFAULT_CONNECTION_TIMEOUT);
client.setSoTimeout(SolrTestCaseJ4.DEFAULT_CONNECTION_TIMEOUT);
CoreAdminRequest.Unload req = new CoreAdminRequest.Unload(false);
req.setDeleteInstanceDir(true);
req.setCoreName("corex");
req.process(client);
}
runner.stop();
assertFalse("Instance directory exists after core unload with deleteInstanceDir=true : " + corex,
corex.exists());
}
@Test
public void testDeleteInstanceDirAfterCreateFailure() throws Exception {
assumeFalse("Ignore test on windows because it does not delete data directory immediately after unload", Constants.WINDOWS);
File solrHomeDirectory = new File(initCoreDataDir, getClass().getName() + "-corex-"
+ System.nanoTime());
solrHomeDirectory.mkdirs();
copySolrHomeToTemp(solrHomeDirectory, "corex");
File corex = new File(solrHomeDirectory, "corex");
FileUtils.write(new File(corex, "core.properties"), "", StandardCharsets.UTF_8);
JettySolrRunner runner = new JettySolrRunner(solrHomeDirectory.getAbsolutePath(), buildJettyConfig("/solr"));
runner.start();
try (HttpSolrClient client = getHttpSolrClient(runner.getBaseUrl() + "/corex")) {
client.setConnectionTimeout(SolrTestCaseJ4.DEFAULT_CONNECTION_TIMEOUT);
client.setSoTimeout(SolrTestCaseJ4.DEFAULT_CONNECTION_TIMEOUT);
SolrInputDocument doc = new SolrInputDocument();
doc.addField("id", "123");
client.add(doc);
client.commit();
}
Path dataDir = null;
try (HttpSolrClient client = getHttpSolrClient(runner.getBaseUrl().toString())) {
CoreStatus status = CoreAdminRequest.getCoreStatus("corex", true, client);
String dataDirectory = status.getDataDirectory();
dataDir = Paths.get(dataDirectory);
assertTrue(Files.exists(dataDir));
}
File subHome = new File(solrHomeDirectory, "corex" + File.separator + "conf");
String top = SolrTestCaseJ4.TEST_HOME() + "/collection1/conf";
FileUtils.copyFile(new File(top, "bad-error-solrconfig.xml"), new File(subHome, "solrconfig.xml"));
try (HttpSolrClient client = getHttpSolrClient(runner.getBaseUrl().toString())) {
client.setConnectionTimeout(SolrTestCaseJ4.DEFAULT_CONNECTION_TIMEOUT);
client.setSoTimeout(SolrTestCaseJ4.DEFAULT_CONNECTION_TIMEOUT);
try {
CoreAdminRequest.reloadCore("corex", client);
} catch (Exception e) {
// this is expected because we put a bad solrconfig -- ignore
}
CoreAdminRequest.Unload req = new CoreAdminRequest.Unload(false);
req.setDeleteDataDir(true);
req.setDeleteInstanceDir(false); // important because the data directory is inside the instance directory
req.setCoreName("corex");
req.process(client);
}
runner.stop();
assertTrue("The data directory was not cleaned up on unload after a failed core reload", Files.notExists(dataDir));
}
@Test
public void testNonexistentCoreReload() throws Exception {
final CoreAdminHandler admin = new CoreAdminHandler(h.getCoreContainer());
SolrQueryResponse resp = new SolrQueryResponse();
try {
admin.handleRequestBody(
req(CoreAdminParams.ACTION,
CoreAdminParams.CoreAdminAction.RELOAD.toString(),
CoreAdminParams.CORE, "non-existent-core")
, resp);
fail("Was able to successfully reload non-existent-core");
} catch (Exception e) {
String e1 = e.getCause().getMessage();
assertEquals("Expected error message for non-existent core.", "No such core: non-existent-core", e.getCause().getMessage());
}
// test null core
try {
admin.handleRequestBody(
req(CoreAdminParams.ACTION,
CoreAdminParams.CoreAdminAction.RELOAD.toString())
, resp);
fail("Was able to successfully reload null core");
}
catch (Exception e) {
if (!(e instanceof SolrException)) {
fail("Expected SolrException but got " + e);
}
assertEquals("Expected error message for non-existent core.", "Missing required parameter: core", e.getMessage());
}
}
}