blob: 5731d755b91c28f85cdb87b756d91f978b2d5096 [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 javax.script.ScriptEngineManager;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.invoke.MethodHandles;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.io.FileUtils;
import org.apache.http.client.HttpClient;
import org.apache.lucene.util.TestUtil;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.SolrTestCaseUtil;
import org.apache.solr.SolrTestUtil;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.embedded.JettySolrRunner;
import org.apache.solr.client.solrj.impl.BaseHttpSolrClient;
import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
import org.apache.solr.client.solrj.impl.Http2SolrClient;
import org.apache.solr.client.solrj.impl.HttpClientUtil;
import org.apache.solr.client.solrj.request.ConfigSetAdminRequest;
import org.apache.solr.client.solrj.request.ConfigSetAdminRequest.Create;
import org.apache.solr.client.solrj.request.ConfigSetAdminRequest.Delete;
import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.client.solrj.response.CollectionAdminResponse;
import org.apache.solr.client.solrj.response.ConfigSetAdminResponse;
import org.apache.solr.common.ParWork;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.solr.common.cloud.ZkConfigManager;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.params.CollectionParams.CollectionAction;
import org.apache.solr.common.params.ConfigSetParams;
import org.apache.solr.common.params.ConfigSetParams.ConfigSetAction;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.Base64;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.Utils;
import org.apache.solr.core.ConfigSetProperties;
import org.apache.solr.core.TestSolrConfigHandler;
import org.apache.solr.security.BasicAuthIntegrationTest;
import org.apache.solr.servlet.SolrDispatchFilter;
import org.apache.solr.util.ExternalPaths;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.junit.*;
import org.noggit.JSONParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.solr.common.params.CommonParams.NAME;
import static org.apache.solr.core.ConfigSetProperties.DEFAULT_FILENAME;
import static org.junit.matchers.JUnitMatchers.containsString;
/**
* Simple ConfigSets API tests on user errors and simple success cases.
*/
public class TestConfigSetsAPI extends SolrTestCaseJ4 {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private MiniSolrCloudCluster solrCluster;
@BeforeClass
public static void beforTestConfigSetsAPI() throws Exception {
System.setProperty("solr.suppressDefaultConfigBootstrap", "false");
System.setProperty("solr.enablePublicKeyHandler", "true");
disableReuseOfCryptoKeys();
useFactory(null);
}
@Override
@Before
public void setUp() throws Exception {
super.setUp();
solrCluster = new MiniSolrCloudCluster(1, SolrTestUtil.createTempDir(), buildJettyConfig("/solr"));
}
@Override
@After
public void tearDown() throws Exception {
if (null != solrCluster) {
solrCluster.shutdown();
solrCluster = null;
}
super.tearDown();
}
@Test
public void testCreateErrors() throws Exception {
final String baseUrl = solrCluster.getJettySolrRunners().get(0).getBaseUrl().toString();
final SolrClient solrClient = getHttpSolrClient(baseUrl);
solrCluster.uploadConfigSet(SolrTestUtil.configset("configset-2"), "configSet");
// no action
CreateNoErrorChecking createNoAction = new CreateNoErrorChecking();
createNoAction.setAction(null);
verifyException(solrClient, createNoAction, "action");
// no ConfigSet name
CreateNoErrorChecking create = new CreateNoErrorChecking();
verifyException(solrClient, create, NAME);
// set ConfigSet
create.setConfigSetName("configSetName");
// ConfigSet already exists
Create alreadyExists = new Create();
alreadyExists.setConfigSetName("configSet").setBaseConfigSetName("baseConfigSet");
verifyException(solrClient, alreadyExists, "ConfigSet already exists");
// Base ConfigSet does not exist
Create baseConfigNoExists = new Create();
baseConfigNoExists.setConfigSetName("newConfigSet").setBaseConfigSetName("baseConfigSet");
verifyException(solrClient, baseConfigNoExists, "Base ConfigSet does not exist");
solrClient.close();
}
@Test
public void testCreate() throws Exception {
// no old, no new
verifyCreate(null, "configSet1", null, null);
// no old, new
verifyCreate("baseConfigSet2", "configSet2",
null, ImmutableMap.<String, String>of("immutable", "true", "key1", "value1"));
// old, no new
verifyCreate("baseConfigSet3", "configSet3",
ImmutableMap.<String, String>of("immutable", "false", "key2", "value2"), null);
// old, new
verifyCreate("baseConfigSet4", "configSet4",
ImmutableMap.<String, String>of("immutable", "true", "onlyOld", "onlyOldValue"),
ImmutableMap.<String, String>of("immutable", "false", "onlyNew", "onlyNewValue"));
}
private void setupBaseConfigSet(String baseConfigSetName, Map<String, String> oldProps) throws Exception {
final File configDir = SolrTestUtil.getFile("solr").toPath().resolve("configsets/configset-2/conf").toFile();
final File tmpConfigDir = SolrTestUtil.createTempDir().toFile();
tmpConfigDir.deleteOnExit();
FileUtils.copyDirectory(configDir, tmpConfigDir);
if (oldProps != null && oldProps.size() > 0) {
FileUtils.write(new File(tmpConfigDir, ConfigSetProperties.DEFAULT_FILENAME),
getConfigSetProps(oldProps), StandardCharsets.UTF_8);
}
ZkConfigManager configManager = new ZkConfigManager(zkClient());
configManager.uploadConfigDir(tmpConfigDir.toPath(), baseConfigSetName);
}
private void verifyCreate(String baseConfigSetName, String configSetName,
Map<String, String> oldProps, Map<String, String> newProps) throws Exception {
final String baseUrl = solrCluster.getJettySolrRunners().get(0).getBaseUrl().toString();
final SolrClient solrClient = getHttpSolrClient(baseUrl);
if (baseConfigSetName != null) {
setupBaseConfigSet(baseConfigSetName, oldProps);
}
SolrZkClient zkClient = zkClient();
ZkConfigManager configManager = new ZkConfigManager(zkClient);
assertFalse(configManager.configExists(configSetName));
Create create = new Create();
create.setBaseConfigSetName(baseConfigSetName).setConfigSetName(configSetName);
if (newProps != null) {
Properties p = new Properties();
p.putAll(newProps);
create.setNewConfigSetProperties(p);
}
ConfigSetAdminResponse response = create.process(solrClient);
assertNotNull(response.getResponse());
assertTrue(configManager.configExists(configSetName));
verifyProperties(configSetName, oldProps, newProps, zkClient);
solrClient.close();
}
private NamedList getConfigSetPropertiesFromZk(
SolrZkClient zkClient, String path) throws Exception {
byte [] oldPropsData = null;
try {
oldPropsData = zkClient.getData(path, null, null);
} catch (KeeperException.NoNodeException e) {
// okay, properties just don't exist
}
if (oldPropsData != null) {
InputStreamReader reader = new InputStreamReader(new ByteArrayInputStream(oldPropsData), StandardCharsets.UTF_8);
try {
return ConfigSetProperties.readFromInputStream(reader);
} finally {
reader.close();
}
}
return null;
}
private void verifyProperties(String configSetName, Map<String, String> oldProps,
Map<String, String> newProps, SolrZkClient zkClient) throws Exception {
NamedList properties = getConfigSetPropertiesFromZk(zkClient,
ZkConfigManager.CONFIGS_ZKNODE + "/" + configSetName + "/" + DEFAULT_FILENAME);
// let's check without merging the maps, since that's what the MessageHandler does
// (since we'd probably repeat any bug in the MessageHandler here)
if (oldProps == null && newProps == null) {
assertTrue(properties == null || properties.size() == 0);
return;
}
assertNotNull(properties);
// check all oldProps are in props
if (oldProps != null) {
for (Map.Entry<String, String> entry : oldProps.entrySet()) {
assertNotNull("Could not find " + entry.getKey() + " in " + properties, properties.get(entry.getKey()));
}
}
// check all newProps are in props
if (newProps != null) {
for (Map.Entry<String, String> entry : newProps.entrySet()) {
assertNotNull(properties.get(entry.getKey()));
}
}
// check the value in properties are correct
Iterator<Map.Entry<String, Object>> it = properties.iterator();
while (it.hasNext()) {
Map.Entry<String, Object> entry = it.next();
String newValue = newProps != null ? newProps.get(entry.getKey()) : null;
String oldValue = oldProps != null ? oldProps.get(entry.getKey()) : null;
if (newValue != null) {
assertTrue(newValue.equals(entry.getValue()));
} else if (oldValue != null) {
assertTrue(oldValue.equals(entry.getValue()));
} else {
// not in either
assert(false);
}
}
}
@Test
public void testUploadErrors() throws Exception {
ByteBuffer emptyData = ByteBuffer.allocate(0);
// Checking error when no configuration name is specified in request
Map map = postDataAndGetResponse(solrCluster.getSolrClient(), solrCluster.getJettySolrRunners().get(0).getBaseUrl().toString() + "/admin/configs?action=UPLOAD", emptyData, null, null);
assertNotNull(map);
long statusCode = (long) getObjectByPath(map, false, Arrays.asList("responseHeader", "status"));
assertEquals(400l, statusCode);
SolrZkClient zkClient = zkClient();
// Create dummy config files in zookeeper
zkClient.mkdir("/configs/myconf");
zkClient.create("/configs/myconf/firstDummyFile", "first dummy content".getBytes(StandardCharsets.UTF_8), CreateMode.PERSISTENT, true);
zkClient.create("/configs/myconf/anotherDummyFile", "second dummy content".getBytes(StandardCharsets.UTF_8), CreateMode.PERSISTENT, true);
// Checking error when configuration name specified already exists
map = postDataAndGetResponse(solrCluster.getSolrClient(), solrCluster.getJettySolrRunners().get(0).getBaseUrl().toString() + "/admin/configs?action=UPLOAD&name=myconf", emptyData, null, null);
assertNotNull(map);
statusCode = (long) getObjectByPath(map, false, Arrays.asList("responseHeader", "status"));
assertEquals(400l, statusCode);
assertTrue("Expected file doesnt exist in zk. It's possibly overwritten", zkClient.exists("/configs/myconf/firstDummyFile"));
assertTrue("Expected file doesnt exist in zk. It's possibly overwritten", zkClient.exists("/configs/myconf/anotherDummyFile"));
}
@Test
public void testUploadDisabled() throws Exception {
SolrZkClient zkClient = zkClient();
for (boolean enabled : new boolean[]{true, false}) {
System.setProperty("configset.upload.enabled", String.valueOf(enabled));
try {
long statusCode = uploadConfigSet("regular", "test-enabled-is-" + enabled, null, null, zkClient);
assertEquals("ConfigSet upload enabling/disabling not working as expected for enabled=" + enabled + ".",
enabled ? 0l : 400l, statusCode);
} finally {
System.clearProperty("configset.upload.enabled");
}
}
}
@Test
public void testUpload() throws Exception {
String suffix = "-untrusted";
uploadConfigSetWithAssertions("regular", suffix, null, null);
// try to create a collection with the uploaded configset
createCollection("newcollection", "regular" + suffix, 1, 1, solrCluster.getSolrClient());
}
@Test
@Ignore // MRM TODO: debug
public void testUploadWithScriptUpdateProcessor() throws Exception {
Assume.assumeNotNull((new ScriptEngineManager()).getEngineByExtension("js"));
Assume.assumeNotNull((new ScriptEngineManager()).getEngineByName("JavaScript"));
// Authorization off
// unprotectConfigsHandler(); // TODO Enable this back when testUploadWithLibDirective() is re-enabled
final String untrustedSuffix = "-untrusted";
uploadConfigSetWithAssertions("with-script-processor", untrustedSuffix, null, null);
// try to create a collection with the uploaded configset
Throwable thrown = SolrTestCaseUtil.expectThrows(BaseHttpSolrClient.RemoteSolrException.class, () -> {
createCollection("newcollection2", "with-script-processor" + untrustedSuffix, 1, 1, solrCluster.getSolrClient());
});
assertThat(thrown.getMessage(), containsString("Underlying core creation failed"));
// Authorization on
final String trustedSuffix = "-trusted";
protectConfigsHandler();
uploadConfigSetWithAssertions("with-script-processor", trustedSuffix, "solr", "SolrRocks");
// try to create a collection with the uploaded configset
CollectionAdminResponse resp = createCollection("newcollection2", "with-script-processor" + trustedSuffix,
1, 1, solrCluster.getSolrClient());
scriptRequest("newcollection2");
}
@Test
@Ignore // MRM-TEST TODO: flakey
public void testUploadWithLibDirective() throws Exception {
// Authorization off
unprotectConfigsHandler();
final String untrustedSuffix = "-untrusted";
uploadConfigSetWithAssertions("with-lib-directive", untrustedSuffix, null, null);
// try to create a collection with the uploaded configset
Throwable thrown = SolrTestCaseUtil.expectThrows(BaseHttpSolrClient.RemoteSolrException.class, () -> {
createCollection("newcollection3", "with-lib-directive" + untrustedSuffix, 1, 1, solrCluster.getSolrClient());
});
assertThat(thrown.getMessage(), containsString("Underlying core creation failed"));
// MRM TODO: - work out using newcollection3 and dealing with it finding the collection after first create fails - instead of using newCollection4
// Authorization on
final String trustedSuffix = "-trusted";
protectConfigsHandler();
uploadConfigSetWithAssertions("with-lib-directive", trustedSuffix, "solr", "SolrRocks");
// try to create a collection with the uploaded configset
CollectionAdminResponse resp = createCollection("newcollection4", "with-lib-directive" + trustedSuffix, 1, 1, solrCluster.getSolrClient());
SolrInputDocument doc = sdoc("id", "4055", "subject", "Solr");
solrCluster.getSolrClient().add("newcollection4", doc);
solrCluster.getSolrClient().commit("newcollection4");
assertEquals("4055", solrCluster.getSolrClient().query("newcollection4", params("q", "*:*")).getResults().get(0).get("id"));
}
protected SolrZkClient zkClient() {
ZkStateReader reader = solrCluster.getSolrClient().getZkStateReader();
if (reader == null)
solrCluster.getSolrClient().connect();
return solrCluster.getSolrClient().getZkStateReader().getZkClient();
}
private void unprotectConfigsHandler() throws Exception {
HttpClient cl = null;
try {
cl = HttpClientUtil.createClient(null);
zkClient().setData("/security.json", "{}".getBytes(UTF_8), true);
} finally {
if (cl != null) {
HttpClientUtil.close(cl);
}
}
}
private void protectConfigsHandler() throws Exception {
String authcPrefix = "/admin/authentication";
String authzPrefix = "/admin/authorization";
String securityJson = "{\n" +
" 'authentication':{\n" +
" 'class':'solr.BasicAuthPlugin',\n" +
" 'blockUnknown': false,\n" +
" 'credentials':{'solr':'orwp2Ghgj39lmnrZOTm7Qtre1VqHFDfwAEzr0ApbN3Y= Ju5osoAqOX8iafhWpPP01E5P+sg8tK8tHON7rCYZRRw='}},\n" +
" 'authorization':{\n" +
" 'class':'solr.RuleBasedAuthorizationPlugin',\n" +
" 'user-role':{'solr':'admin'},\n" +
" 'permissions':[{'name':'security-edit','role':'admin'}, {'name':'config-edit','role':'admin'}]}}";
HttpClient cl = null;
try {
cl = HttpClientUtil.createClient(null);
JettySolrRunner randomJetty = solrCluster.getRandomJetty(random());
String baseUrl = randomJetty.getBaseUrl().toString();
zkClient().setData("/security.json", securityJson.replaceAll("'", "\"").getBytes(UTF_8), true);
BasicAuthIntegrationTest.verifySecurityStatus(cl, baseUrl + authcPrefix, "authentication/class", "solr.BasicAuthPlugin", 100);
BasicAuthIntegrationTest.verifySecurityStatus(cl, baseUrl + authzPrefix, "authorization/class", "solr.RuleBasedAuthorizationPlugin", 100);
} finally {
if (cl != null) {
HttpClientUtil.close(cl);
}
}
}
private void uploadConfigSetWithAssertions(String configSetName, String suffix, String username, String password) throws Exception {
SolrZkClient zkClient = zkClient();
long statusCode = uploadConfigSet(configSetName, suffix, username, password, zkClient);
assertEquals(0l, statusCode);
assertTrue("managed-schema file should have been uploaded",
zkClient.exists("/configs/" + configSetName + suffix + "/managed-schema"));
assertTrue("managed-schema file contents on zookeeper are not exactly same as that of the file uploaded in config",
Arrays.equals(zkClient.getData("/configs/" + configSetName + suffix + "/managed-schema", null, null),
readFile("solr/configsets/upload/" + configSetName + "/managed-schema")));
assertTrue("solrconfig.xml file should have been uploaded",
zkClient.exists("/configs/" + configSetName + suffix + "/solrconfig.xml"));
byte data[] = zkClient.getData("/configs/" + configSetName + suffix, null, null);
//assertEquals("{\"trusted\": false}", new String(data, StandardCharsets.UTF_8));
assertTrue("solrconfig.xml file contents on zookeeper are not exactly same as that of the file uploaded in config",
Arrays.equals(zkClient.getData("/configs/" + configSetName + suffix + "/solrconfig.xml", null, null),
readFile("solr/configsets/upload/" + configSetName + "/solrconfig.xml")));
}
private long uploadConfigSet(String configSetName, String suffix, String username, String password,
SolrZkClient zkClient) throws IOException {
// Read zipped sample config
ByteBuffer sampleZippedConfig = TestSolrConfigHandler
.getFileContent(
createTempZipFile("solr/configsets/upload/"+configSetName), false);
ZkConfigManager configManager = new ZkConfigManager(zkClient);
assertFalse(configManager.configExists(configSetName+suffix));
Map map = postDataAndGetResponse(solrCluster.getSolrClient(),
solrCluster.getJettySolrRunners().get(0).getBaseUrl().toString() + "/admin/configs?action=UPLOAD&name="+configSetName+suffix,
sampleZippedConfig, username, password);
assertNotNull(map);
long statusCode = (long) getObjectByPath(map, false, Arrays.asList("responseHeader", "status"));
return statusCode;
}
/**
* Create a zip file (in the temp directory) containing all the files within the specified directory
* and return the path for the zip file.
*/
private String createTempZipFile(String directoryPath) {
File zipFile = new File(solrCluster.getBaseDir().toFile().getAbsolutePath() +
File.separator + TestUtil.randomSimpleString(random(), 6, 8) + ".zip");
File directory = SolrTestUtil.getFile(directoryPath);
if (log.isInfoEnabled()) {
log.info("Directory: {}", directory.getAbsolutePath());
}
try {
zip (directory, zipFile);
if (log.isInfoEnabled()) {
log.info("Zipfile: {}", zipFile.getAbsolutePath());
}
return zipFile.getAbsolutePath();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static void zip(File directory, File zipfile) throws IOException {
URI base = directory.toURI();
Deque<File> queue = new LinkedList<File>();
queue.push(directory);
OutputStream out = new FileOutputStream(zipfile);
ZipOutputStream zout = new ZipOutputStream(out);
try {
while (!queue.isEmpty()) {
directory = queue.pop();
for (File kid : directory.listFiles()) {
String name = base.relativize(kid.toURI()).getPath();
if (kid.isDirectory()) {
queue.push(kid);
name = name.endsWith("/") ? name : name + "/";
zout.putNextEntry(new ZipEntry(name));
} else {
zout.putNextEntry(new ZipEntry(name));
InputStream in = new FileInputStream(kid);
try {
byte[] buffer = new byte[1024];
while (true) {
int readCount = in.read(buffer);
if (readCount < 0) {
break;
}
zout.write(buffer, 0, readCount);
}
} finally {
in.close();
}
zout.closeEntry();
}
}
}
} finally {
zout.close();
}
}
public void scriptRequest(String collection) throws SolrServerException, IOException {
SolrClient client = solrCluster.getSolrClient();
SolrInputDocument doc = sdoc("id", "4055", "subject", "Solr");
client.add(collection, doc);
client.commit(collection);
assertEquals("42", client.query(collection, params("q", "*:*")).getResults().get(0).get("script_added_i"));
}
protected CollectionAdminResponse createCollection(String collectionName, String confSetName, int numShards,
int replicationFactor, SolrClient client) throws SolrServerException, IOException {
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionAction.CREATE.toString());
params.set("collection.configName", confSetName);
params.set("name", collectionName);
params.set("numShards", numShards);
params.set("replicationFactor", replicationFactor);
SolrRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
CollectionAdminResponse res = new CollectionAdminResponse();
res.setResponse(client.request(request));
return res;
}
public static Map postDataAndGetResponse(CloudHttp2SolrClient cloudClient,
String uri, ByteBuffer bytarr, String username, String password) throws IOException {
log.info("postDataAndGetResponse {}", uri);
Map m = null;
try {
Map<String, String> headers = new HashMap<>(1);
if (username != null) {
String userPass = username + ":" + password;
String encoded = Base64.byteArrayToBase64(userPass.getBytes(UTF_8));
headers.put("Authorization", "Basic " + encoded);
}
Http2SolrClient.SimpleResponse resp = Http2SolrClient.POST(uri, cloudClient.getHttpClient(), bytarr, "application/octet-stream", headers);
try {
m = (Map) Utils.fromJSONString(resp.asString);
} catch (JSONParser.ParseException e) {
System.err.println("err response: " + resp);
throw new AssertionError(e);
}
} catch (InterruptedException e) {
ParWork.propagateInterrupt(e);
} catch (ExecutionException e) {
log.error("", e);
} catch (TimeoutException e) {
log.error("", e);
}
return m;
}
private static Object getObjectByPath(Map root, boolean onlyPrimitive, java.util.List<String> hierarchy) {
Map obj = root;
for (int i = 0; i < hierarchy.size(); i++) {
String s = hierarchy.get(i);
if (i < hierarchy.size() - 1) {
if (!(obj.get(s) instanceof Map)) return null;
obj = (Map) obj.get(s);
if (obj == null) return null;
} else {
Object val = obj.get(s);
if (onlyPrimitive && val instanceof Map) {
return null;
}
return val;
}
}
return false;
}
private byte[] readFile(String fname) throws IOException {
byte[] buf = null;
try (FileInputStream fis = new FileInputStream(SolrTestUtil.getFile(fname))) {
buf = new byte[fis.available()];
fis.read(buf);
}
return buf;
}
@Test
public void testDeleteErrors() throws Exception {
final String baseUrl = solrCluster.getJettySolrRunners().get(0).getBaseUrl().toString();
final SolrClient solrClient = getHttpSolrClient(baseUrl);
final File configDir = SolrTestUtil.getFile("solr").toPath().resolve("configsets/configset-2/conf").toFile();
final File tmpConfigDir = SolrTestUtil.createTempDir().toFile();
tmpConfigDir.deleteOnExit();
// Ensure ConfigSet is immutable
FileUtils.copyDirectory(configDir, tmpConfigDir);
FileUtils.write(new File(tmpConfigDir, "configsetprops.json"),
getConfigSetProps(ImmutableMap.<String, String>of("immutable", "true")), StandardCharsets.UTF_8);
solrCluster.uploadConfigSet(tmpConfigDir.toPath(), "configSet");
// no ConfigSet name
DeleteNoErrorChecking delete = new DeleteNoErrorChecking();
verifyException(solrClient, delete, NAME);
// ConfigSet doesn't exist
delete.setConfigSetName("configSetBogus");
verifyException(solrClient, delete, "ConfigSet does not exist");
// ConfigSet is immutable
delete.setConfigSetName("configSet");
verifyException(solrClient, delete, "Requested delete of immutable ConfigSet");
solrClient.close();
}
private void verifyException(SolrClient solrClient, ConfigSetAdminRequest request,
String errorContains) throws Exception {
Exception e = SolrTestCaseUtil.expectThrows(Exception.class, () -> solrClient.request(request));
assertTrue("Expected exception message to contain: " + errorContains
+ " got: " + e.getMessage(), e.getMessage().contains(errorContains));
}
@Test
public void testDelete() throws Exception {
final String baseUrl = solrCluster.getJettySolrRunners().get(0).getBaseUrl().toString();
final SolrClient solrClient = getHttpSolrClient(baseUrl);
final String configSet = "configSet";
solrCluster.uploadConfigSet(SolrTestUtil.configset("configset-2"), configSet);
SolrZkClient zkClient = zkClient();
ZkConfigManager configManager = new ZkConfigManager(zkClient);
assertTrue(configManager.configExists(configSet));
Delete delete = new Delete();
delete.setConfigSetName(configSet);
ConfigSetAdminResponse response = delete.process(solrClient);
assertNotNull(response.getResponse());
assertFalse(configManager.configExists(configSet));
solrClient.close();
}
@Test
public void testList() throws Exception {
final String baseUrl = solrCluster.getJettySolrRunners().get(0).getBaseUrl().toString();
final SolrClient solrClient = getHttpSolrClient(baseUrl);
// test empty
ConfigSetAdminRequest.List list = new ConfigSetAdminRequest.List();
ConfigSetAdminResponse.List response = list.process(solrClient);
Collection<String> actualConfigSets = response.getConfigSets();
assertEquals(1, actualConfigSets.size()); // _default configset suppressed
// test multiple
Set<String> configSets = new HashSet<String>();
for (int i = 0; i < 5; ++i) {
String configSet = "configSet" + i;
solrCluster.uploadConfigSet(SolrTestUtil.configset("configset-2"), configSet);
configSets.add(configSet);
}
response = list.process(solrClient);
actualConfigSets = response.getConfigSets();
assertEquals(configSets.size() + 1, actualConfigSets.size());
assertTrue(actualConfigSets.containsAll(configSets));
solrClient.close();
}
/**
* A simple sanity check that the test-framework hueristic logic for setting
* {@link ExternalPaths#DEFAULT_CONFIGSET} is working as it should
* in the current test env, and finding the real directory which matches what {@link ZkController}
* finds and uses to bootstrap ZK in cloud based tests.
*
* <p>
* This assumes the {@link SolrDispatchFilter#SOLR_DEFAULT_CONFDIR_ATTRIBUTE} system property
* has not been externally set in the environment where this test is being run -- which should
* <b>never</b> be the case, since it would prevent the test-framework from using
* {@link ExternalPaths#DEFAULT_CONFIGSET}
*
* @see SolrDispatchFilter#SOLR_DEFAULT_CONFDIR_ATTRIBUTE
* @see ZkController#getDefaultConfigDirPath
*/
@Test
public void testUserAndTestDefaultConfigsetsAreSame() throws IOException {
final File extPath = new File(ExternalPaths.DEFAULT_CONFIGSET);
assertTrue("_default dir doesn't exist: " + ExternalPaths.DEFAULT_CONFIGSET, extPath.exists());
assertTrue("_default dir isn't a dir: " + ExternalPaths.DEFAULT_CONFIGSET, extPath.isDirectory());
final String zkBootStrap = ZkController.getDefaultConfigDirPath();
assertEquals("extPath _default configset dir vs zk bootstrap path",
ExternalPaths.DEFAULT_CONFIGSET, zkBootStrap);
}
private StringBuilder getConfigSetProps(Map<String, String> map) {
return new StringBuilder(new String(Utils.toJSON(map), StandardCharsets.UTF_8));
}
public static class CreateNoErrorChecking extends ConfigSetAdminRequest.Create {
public ConfigSetAdminRequest setAction(ConfigSetAction action) {
return super.setAction(action);
}
@Override
public SolrParams getParams() {
ModifiableSolrParams params = new ModifiableSolrParams();
if (action != null) params.set(ConfigSetParams.ACTION, action.toString());
if (configSetName != null) params.set(NAME, configSetName);
if (baseConfigSetName != null) params.set("baseConfigSet", baseConfigSetName);
return params;
}
}
public static class DeleteNoErrorChecking extends ConfigSetAdminRequest.Delete {
public ConfigSetAdminRequest setAction(ConfigSetAction action) {
return super.setAction(action);
}
@Override
public SolrParams getParams() {
ModifiableSolrParams params = new ModifiableSolrParams();
if (action != null) params.set(ConfigSetParams.ACTION, action.toString());
if (configSetName != null) params.set(NAME, configSetName);
return params;
}
}
}