blob: 7a9decf63702bb1edf8b1d197fad58bf4990deec [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.api.collections;
import com.google.common.collect.Lists;
import org.apache.solr.SolrTestCaseUtil;
import org.apache.solr.SolrTestUtil;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.BaseHttpSolrClient;
import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.client.solrj.request.V2Request;
import org.apache.solr.client.solrj.response.CollectionAdminResponse;
import org.apache.solr.cloud.AbstractDistribZkTestBase;
import org.apache.solr.cloud.ZkTestServer;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.common.cloud.DocCollection;
import org.apache.solr.common.cloud.Replica;
import org.apache.solr.common.cloud.Slice;
import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.solr.common.cloud.ZkConfigManager;
import org.apache.solr.common.params.CollectionParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.ShardParams;
import org.apache.solr.common.util.NamedList;
import org.apache.zookeeper.KeeperException;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import static org.apache.solr.common.params.CommonAdminParams.WAIT_FOR_FINAL_STATE;
//@LuceneTestCase.Nightly
public class TestCollectionAPI extends ReplicaPropertiesBase {
public static final String COLLECTION_NAME = "testcollection";
public static final String COLLECTION_NAME1 = "testcollection1";
private static final String SHARD2 = "s2";
@BeforeClass
public static void beforeTestCollectionAPI() throws Exception {
System.setProperty("solr.default.collection_op_timeout", "20000");
System.setProperty("solr.skipCommitOnClose", "false");
System.setProperty("solr.suppressDefaultConfigBootstrap", "false");
}
public TestCollectionAPI() throws Exception {
useFactory(null);
sliceCount = 2;
numJettys = 2;
}
@Test
// @ShardsFixed(num = 2)
public void test() throws Exception {
try (CloudHttp2SolrClient client = createCloudClient(null)) {
CollectionAdminRequest.Create req;
if (useTlogReplicas()) {
req = CollectionAdminRequest.createCollection(COLLECTION_NAME, "_default",2, 0, 1, 0); // MRM-TEST TODO: 1 pull replica each
} else {
req = CollectionAdminRequest.createCollection(COLLECTION_NAME, "_default",2, 1, 0, 0);
}
setV2(req);
client.request(req);
assertV2CallsCount();
createCollection(null, COLLECTION_NAME1, 1, 1, client, null, "_default");
}
// list needs to see it
cluster.waitForActiveCollection(COLLECTION_NAME, 2, 2);
cluster.waitForActiveCollection(COLLECTION_NAME1, 1, 1);
listCollection();
clusterStatusNoCollection();
clusterStatusWithCollection();
clusterStatusWithCollectionAndShard();
clusterStatusWithCollectionAndMultipleShards();
clusterStatusWithRouteKey();
clusterStatusAliasTest();
// clusterStatusRolesTest();
// MRM TODO: debug
// replicaPropTest();
// clusterStatusZNodeVersion(); maybe this relies on stateformat=1?
// testClusterStateMigration(); same
testCollectionCreationCollectionNameValidation();
testReplicationFactorValidaton();
testCollectionCreationShardNameValidation();
testAliasCreationNameValidation();
testShardCreationNameValidation();
testNoConfigset();
testModifyCollection(); // deletes replicationFactor property from collections, be careful adding new tests after this one!
clusterStatusBadCollectionTest();
}
private void testCollectionCreationTooManyShards() throws Exception {
try (CloudHttp2SolrClient client = createCloudClient(null)) {
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CREATE.toString());
params.set("name", "collection_too_many");
params.set("router.name", "implicit");
params.set("numShards", "10");
params.set("maxShardsPerNode", 1);
params.set("shards", "b0,b1,b2,b3,b4,b5,b6,b7,b8,b9");
SolrRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
try {
client.request(request);
fail("A collection creation request with too many shards than allowed by maxShardsPerNode should not have succeeded");
} catch (BaseHttpSolrClient.RemoteSolrException e) {
final String errorMessage = e.getMessage();
assertTrue(errorMessage.contains("Cannot create collection"));
assertTrue(errorMessage.contains("This requires 10 shards to be created (higher than the allowed number)"));
assertMissingCollection(client, "collection_too_many");
}
}
}
private void assertMissingCollection(CloudHttp2SolrClient client, String collectionName) throws Exception {
ClusterState clusterState = client.getZkStateReader().getClusterState();
assertNull(clusterState.getCollectionOrNull(collectionName));
}
private void testModifyCollection() throws Exception {
try (CloudHttp2SolrClient client = createCloudClient(null)) {
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.MODIFYCOLLECTION.toString());
params.set("collection", COLLECTION_NAME);
params.set("non_existent_property", "");
QueryRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
try {
client.request(request);
fail("Trying to unset an unknown property should have failed");
} catch (BaseHttpSolrClient.RemoteSolrException e) {
// expected
assertTrue(e.getMessage().contains("no supported values provided"));
}
}
}
private void testReplicationFactorValidaton() throws Exception {
try (CloudHttp2SolrClient client = createCloudClient(null)) {
//Test that you can't specify both replicationFactor and nrtReplicas
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CREATE.toString());
params.set("name", "test_repFactorColl");
params.set("numShards", "1");
params.set("replicationFactor", "1");
params.set("nrtReplicas", "2");
SolrRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
try {
client.request(request);
fail();
} catch (BaseHttpSolrClient.RemoteSolrException e) {
final String errorMessage = e.getMessage();
assertTrue(errorMessage.contains("Cannot specify both replicationFactor and nrtReplicas as they mean the same thing"));
}
//Create it again correctly
CollectionAdminRequest.Create req = CollectionAdminRequest.createCollection("test_repFactorColl", "_default", 1, 3, 0, 0);
client.request(req);
//Assert that replicationFactor has also been set to 3
assertCountsForRepFactorAndNrtReplicas(client, "test_repFactorColl");
params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.MODIFYCOLLECTION.toString());
params.set("collection", "test_repFactorColl");
params.set("readOnly", "false");
request = new QueryRequest(params);
request.setPath("/admin/collections");
client.request(request);
assertCountsForRepFactorAndNrtReplicas(client, "test_repFactorColl");
}
}
// See SOLR-12013. We should report something back if the configset has mysteriously disappeared.
private void testNoConfigset() throws Exception {
String configSet = "delete_config";
final String collection = "deleted_collection";
try (CloudHttp2SolrClient client = createCloudClient(null)) {
AbstractDistribZkTestBase.copyConfigUp(SolrTestUtil.TEST_PATH().resolve("configsets"), "cloud-minimal", configSet, client.getZkHost());
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CREATE.toString());
params.set("name", collection);
params.set("numShards", "1");
params.set("replicationFactor", "1");
params.set("collection.configName", configSet);
SolrRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
client.request(request);
// Now try deleting the configset and doing a clusterstatus.
String parent = ZkConfigManager.CONFIGS_ZKNODE + "/" + configSet;
deleteThemAll(client.getZkStateReader().getZkClient(), parent);
final CollectionAdminRequest.ClusterStatus req = CollectionAdminRequest.getClusterStatus();
NamedList<Object> rsp = client.request(req);
NamedList<Object> cluster = (NamedList<Object>) rsp.get("cluster");
assertNotNull("Cluster state should not be null", cluster);
NamedList<Object> collections = (NamedList<Object>) cluster.get("collections");
assertNotNull("Collections should not be null in cluster state", collections);
assertNotNull("Testing to insure collections are returned", collections.get(COLLECTION_NAME1));
}
}
private void deleteThemAll(SolrZkClient zkClient, String node) throws KeeperException, InterruptedException {
List<String> kids = zkClient.getChildren(node, null, true);
for (String kid : kids) {
deleteThemAll(zkClient, node + "/" + kid);
}
zkClient.delete(node, -1);
}
private void assertCountsForRepFactorAndNrtReplicas(CloudHttp2SolrClient client, String collectionName) throws Exception {
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CLUSTERSTATUS.toString());
params.set("collection", collectionName);
QueryRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
NamedList<Object> rsp = client.request(request);
NamedList<Object> cluster = (NamedList<Object>) rsp.get("cluster");
assertNotNull("Cluster state should not be null", cluster);
NamedList<Object> collections = (NamedList<Object>) cluster.get("collections");
assertNotNull("Collections should not be null in cluster state", collections);
assertEquals(1, collections.size());
Map<String, Object> collection = (Map<String, Object>) collections.get(collectionName);
assertNotNull(collection);
assertEquals(collection.get("replicationFactor"), collection.get("nrtReplicas"));
}
private void assertReadOnly(CloudHttp2SolrClient client, String collectionName) throws Exception {
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CLUSTERSTATUS.toString());
params.set("collection", collectionName);
QueryRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
NamedList<Object> rsp = client.request(request);
NamedList<Object> cluster = (NamedList<Object>) rsp.get("cluster");
assertNotNull("Cluster state should not be null", cluster);
NamedList<Object> collections = (NamedList<Object>) cluster.get("collections");
assertNotNull("Collections should not be null in cluster state", collections);
assertEquals(1, collections.size());
Map<String, Object> collection = (Map<String, Object>) collections.get(collectionName);
assertNotNull(collection);
assertEquals(collection.get("readOnly"), collection.get("false"));
}
private void clusterStatusWithCollectionAndShard() throws IOException, SolrServerException {
try (CloudHttp2SolrClient client = createCloudClient(null)) {
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CLUSTERSTATUS.toString());
params.set("collection", COLLECTION_NAME);
params.set("shard", SHARD1);
SolrRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
NamedList<Object> rsp = client.request(request);
NamedList<Object> cluster = (NamedList<Object>) rsp.get("cluster");
assertNotNull("Cluster state should not be null", cluster);
NamedList<Object> collections = (NamedList<Object>) cluster.get("collections");
assertNotNull("Collections should not be null in cluster state", collections);
assertNotNull(collections.get(COLLECTION_NAME));
assertEquals(1, collections.size());
Map<String, Object> collection = (Map<String, Object>) collections.get(COLLECTION_NAME);
assertNotNull(collections.toString(), collection);
Map<String, Object> shardStatus = (Map<String,Object>) collection.get("shards");
assertNotNull(collection.toString(), shardStatus);
assertEquals(collection.toString(), 1, shardStatus.size());
Map<String, Object> selectedShardStatus = (Map<String, Object>) shardStatus.get(SHARD1);
assertNotNull(selectedShardStatus);
}
}
private void clusterStatusWithCollectionAndMultipleShards() throws IOException, SolrServerException {
try (CloudHttp2SolrClient client = createCloudClient(null)) {
final CollectionAdminRequest.ClusterStatus request = new CollectionAdminRequest.ClusterStatus();
request.setCollectionName(COLLECTION_NAME);
request.setShardName(SHARD1 + "," + SHARD2);
NamedList<Object> rsp = request.process(client).getResponse();
NamedList<Object> cluster = (NamedList<Object>) rsp.get("cluster");
assertNotNull("Cluster state should not be null", cluster);
NamedList<Object> collections = (NamedList<Object>) cluster.get("collections");
assertNotNull("Collections should not be null in cluster state", collections);
assertNotNull(collections.get(COLLECTION_NAME));
assertEquals(1, collections.size());
Map<String, Object> collection = (Map<String, Object>) collections.get(COLLECTION_NAME);
Map<String, Object> shardStatus = (Map<String, Object>) collection.get("shards");
assertEquals(2, shardStatus.size());
Map<String, Object> firstSelectedShardStatus = (Map<String, Object>) shardStatus.get(SHARD1);
assertNotNull(firstSelectedShardStatus);
Map<String, Object> secondSelectedShardStatus = (Map<String, Object>) shardStatus.get(SHARD2);
assertNotNull(secondSelectedShardStatus);
}
}
private void listCollection() throws IOException, SolrServerException {
try (CloudHttp2SolrClient client = createCloudClient(null)) {
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.LIST.toString());
SolrRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
NamedList<Object> rsp = client.request(request);
List<String> collections = (List<String>) rsp.get("collections");
assertTrue(COLLECTION + " was not found in list " + collections, collections.contains(COLLECTION));
assertTrue(COLLECTION_NAME + " was not found in list " + collections, collections.contains(COLLECTION_NAME));
assertTrue(COLLECTION_NAME1 + " was not found in list " + collections, collections.contains(COLLECTION_NAME1));
}
}
private void clusterStatusNoCollection() throws Exception {
try (CloudHttp2SolrClient client = createCloudClient(null)) {
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CLUSTERSTATUS.toString());
SolrRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
NamedList<Object> rsp = client.request(request);
NamedList<Object> cluster = (NamedList<Object>) rsp.get("cluster");
assertNotNull("Cluster state should not be null", cluster);
NamedList<Object> collections = (NamedList<Object>) cluster.get("collections");
assertNotNull("Collections should not be null in cluster state", collections);
assertNotNull(collections.get(COLLECTION_NAME1));
assertEquals(3, collections.size());
List<String> liveNodes = (List<String>) cluster.get("live_nodes");
assertNotNull("Live nodes should not be null", liveNodes);
assertFalse(liveNodes.isEmpty());
}
}
private void clusterStatusWithCollection() throws IOException, SolrServerException {
try (CloudHttp2SolrClient client = createCloudClient(null)) {
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CLUSTERSTATUS.toString());
params.set("collection", COLLECTION_NAME);
SolrRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
NamedList<Object> rsp = client.request(request);
NamedList<Object> cluster = (NamedList<Object>) rsp.get("cluster");
assertNotNull("Cluster state should not be null", cluster);
NamedList<Object> collections = (NamedList<Object>) cluster.get("collections");
assertNotNull("Collections should not be null in cluster state", collections);
assertEquals(1, collections.size());
Map<String, Object> collection = (Map<String, Object>) collections.get(COLLECTION_NAME);
assertNotNull(collection);
assertEquals("_default", collection.get("configName"));
// assertEquals("1", collection.get("nrtReplicas"));
}
}
private void clusterStatusZNodeVersion() throws Exception {
String cname = "clusterStatusZNodeVersion";
try (CloudHttp2SolrClient client = createCloudClient(null)) {
setV2(CollectionAdminRequest.createCollection(cname, "_default", 1, 1).setMaxShardsPerNode(1)).process(client);
assertV2CallsCount();
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CLUSTERSTATUS.toString());
params.set("collection", cname);
SolrRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
NamedList<Object> rsp = client.request(request);
NamedList<Object> cluster = (NamedList<Object>) rsp.get("cluster");
assertNotNull("Cluster state should not be null", cluster);
NamedList<Object> collections = (NamedList<Object>) cluster.get("collections");
assertNotNull("Collections should not be null in cluster state", collections);
assertEquals(1, collections.size());
Map<String, Object> collection = (Map<String, Object>) collections.get(cname);
assertNotNull(collection);
assertEquals("_default", collection.get("configName"));
Integer znodeVersion = (Integer) collection.get("znodeVersion");
assertNotNull(znodeVersion);
CollectionAdminRequest.AddReplica addReplica = CollectionAdminRequest.addReplicaToShard(cname, "shard1");
setV2(addReplica);
addReplica.process(client);
assertV2CallsCount();
rsp = client.request(request);
cluster = (NamedList<Object>) rsp.get("cluster");
collections = (NamedList<Object>) cluster.get("collections");
collection = (Map<String, Object>) collections.get(cname);
Integer newVersion = (Integer) collection.get("znodeVersion");
assertNotNull(newVersion);
assertTrue(newVersion > znodeVersion);
}
}
private static long totalexpectedV2Calls;
public static SolrRequest setV2(SolrRequest req) {
if (V2Request.v2Calls.get() == null) V2Request.v2Calls.set(new AtomicLong());
totalexpectedV2Calls = V2Request.v2Calls.get().get();
if (random().nextBoolean()) {
req.setUseV2(true);
req.setUseBinaryV2(random().nextBoolean());
totalexpectedV2Calls++;
}
return req;
}
public static void assertV2CallsCount() {
assertEquals(totalexpectedV2Calls, V2Request.v2Calls.get().get());
}
private void clusterStatusWithRouteKey() throws IOException, SolrServerException {
try (CloudHttp2SolrClient client = createCloudClient(COLLECTION)) {
SolrInputDocument doc = new SolrInputDocument();
doc.addField("id", "a!123"); // goes to shard2. see ShardRoutingTest for details
client.add(doc);
client.commit();
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CLUSTERSTATUS.toString());
params.set("collection", COLLECTION);
params.set(ShardParams._ROUTE_, "a!");
SolrRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
NamedList<Object> rsp = client.request(request);
NamedList<Object> cluster = (NamedList<Object>) rsp.get("cluster");
assertNotNull("Cluster state should not be null", cluster);
NamedList<Object> collections = (NamedList<Object>) cluster.get("collections");
assertNotNull("Collections should not be null in cluster state", collections);
assertNotNull(collections.get(COLLECTION));
assertEquals(1, collections.size());
Map<String, Object> collection = (Map<String, Object>) collections.get(COLLECTION);
assertEquals("_default", collection.get("configName"));
Map<String, Object> shardStatus = (Map<String, Object>) collection.get("shards");
assertEquals(1, shardStatus.size());
Map<String, Object> selectedShardStatus = (Map<String, Object>) shardStatus.get(SHARD2);
assertNotNull(selectedShardStatus);
}
}
private void clusterStatusAliasTest() throws Exception {
try (CloudHttp2SolrClient client = createCloudClient(null)) {
// create an alias named myalias
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CREATEALIAS.toString());
params.set("name", "myalias");
params.set("collections", COLLECTION + "," + COLLECTION_NAME);
SolrRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
client.request(request);
// request a collection that's part of an alias
params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CLUSTERSTATUS.toString());
params.set("collection", COLLECTION);
request = new QueryRequest(params);
request.setPath("/admin/collections");
NamedList<Object> rsp = client.request(request);
NamedList<Object> cluster = (NamedList<Object>) rsp.get("cluster");
assertNotNull("Cluster state should not be null", cluster);
Map<String, String> aliases = (Map<String, String>) cluster.get("aliases");
assertNotNull("Aliases should not be null", aliases);
assertEquals("Alias: myalias not found in cluster status",
COLLECTION + "," + COLLECTION_NAME, aliases.get("myalias"));
NamedList<Object> collections = (NamedList<Object>) cluster.get("collections");
assertNotNull("Collections should not be null in cluster state", collections);
assertNotNull(collections.get(COLLECTION));
Map<String, Object> collection = (Map<String, Object>) collections.get(COLLECTION);
assertEquals("_default", collection.get("configName"));
List<String> collAlias = (List<String>) collection.get("aliases");
assertEquals("Aliases not found", Lists.newArrayList("myalias"), collAlias);
// status request on the alias itself
params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CLUSTERSTATUS.toString());
params.set("collection", "myalias");
request = new QueryRequest(params);
request.setPath("/admin/collections");
// SOLR-12938 - this should NOT cause an exception
rsp = client.request(request);
cluster = (NamedList<Object>) rsp.get("cluster");
assertNotNull("Cluster state should not be null", cluster);
collections = (NamedList<Object>) cluster.get("collections");
assertNotNull("Collections should not be null in cluster state", collections);
assertNotNull(collections.get(COLLECTION));
assertNotNull(collections.get(COLLECTION_NAME));
// status request on something neither an alias nor a collection itself
params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CLUSTERSTATUS.toString());
params.set("collection", "notAnAliasOrCollection");
request = new QueryRequest(params);
request.setPath("/admin/collections");
// SOLR-12938 - this should still cause an exception
try {
client.request(request);
fail("requesting status for 'notAnAliasOrCollection' should cause an exception from CLUSTERSTATUS" );
} catch (RuntimeException e) {
// success
}
}
}
private void clusterStatusRolesTest() throws Exception {
try (CloudHttp2SolrClient client = createCloudClient(null)) {
client.connect();
Replica replica = client.getZkStateReader().getLeaderRetry(COLLECTION, SHARD1);
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.ADDROLE.toString());
params.set("node", replica.getNodeName());
params.set("role", "overseer");
SolrRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
client.request(request);
params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CLUSTERSTATUS.toString());
params.set("collection", COLLECTION);
request = new QueryRequest(params);
request.setPath("/admin/collections");
NamedList<Object> rsp = client.request(request);
NamedList<Object> cluster = (NamedList<Object>) rsp.get("cluster");
assertNotNull("Cluster state should not be null", cluster);
Map<String, Object> roles = (Map<String, Object>) cluster.get("roles");
assertNotNull("Role information should not be null", roles);
List<String> overseer = (List<String>) roles.get("overseer");
assertNotNull(overseer);
assertEquals(1, overseer.size());
assertTrue(overseer.contains(replica.getNodeName()));
}
}
private void clusterStatusBadCollectionTest() throws Exception {
try (CloudHttp2SolrClient client = createCloudClient(null)) {
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CLUSTERSTATUS.toString());
params.set("collection", "bad_collection_name");
SolrRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
try {
client.request(request);
fail("Collection does not exist. An exception should be thrown");
} catch (SolrException e) {
//expected
assertTrue(e.getMessage().contains("Collection: bad_collection_name not found"));
}
}
}
private void replicaPropTest() throws Exception {
try (CloudHttp2SolrClient client = createCloudClient(null)) {
client.connect();
Map<String, Slice> slices = client.getZkStateReader().getClusterState().getCollection(COLLECTION_NAME).getSlicesMap();
List<String> sliceList = new ArrayList<>(slices.keySet());
String c1_s1 = sliceList.get(0);
List<String> replicasList = new ArrayList<>(slices.get(c1_s1).getReplicasMap().keySet());
String c1_s1_r1 = replicasList.get(0);
String c1_s1_r2 = replicasList.get(1);
String c1_s2 = sliceList.get(1);
replicasList = new ArrayList<>(slices.get(c1_s2).getReplicasMap().keySet());
String c1_s2_r1 = replicasList.get(0);
String c1_s2_r2 = replicasList.get(1);
slices = client.getZkStateReader().getClusterState().getCollection(COLLECTION_NAME1).getSlicesMap();
sliceList = new ArrayList<>(slices.keySet());
String c2_s1 = sliceList.get(0);
replicasList = new ArrayList<>(slices.get(c2_s1).getReplicasMap().keySet());
String c2_s1_r1 = replicasList.get(0);
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.ADDREPLICAPROP.toString());
// Insure we get error returns when omitting required parameters
missingParamsError(client, params);
params.set("collection", COLLECTION_NAME);
missingParamsError(client, params);
params.set("shard", c1_s1);
missingParamsError(client, params);
params.set("replica", c1_s1_r1);
missingParamsError(client, params);
params.set("property", "preferredLeader");
missingParamsError(client, params);
params.set("property.value", "true");
SolrRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
client.request(request);
// The above should have set exactly one preferredleader...
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r1, "preferredleader", "true");
verifyUniquePropertyWithinCollection(client, COLLECTION_NAME, "preferredLeader");
doPropertyAction(client,
"action", CollectionParams.CollectionAction.ADDREPLICAPROP.toString(),
"collection", COLLECTION_NAME,
"shard", c1_s1,
"replica", c1_s1_r2,
"property", "preferredLeader",
"property.value", "true");
// The preferred leader property for shard1 should have switched to the other replica.
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r2, "preferredleader", "true");
verifyUniquePropertyWithinCollection(client, COLLECTION_NAME, "preferredLeader");
doPropertyAction(client,
"action", CollectionParams.CollectionAction.ADDREPLICAPROP.toString(),
"collection", COLLECTION_NAME,
"shard", c1_s2,
"replica", c1_s2_r1,
"property", "preferredLeader",
"property.value", "true");
// Now we should have a preferred leader in both shards...
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r2, "preferredleader", "true");
verifyPropertyVal(client, COLLECTION_NAME, c1_s2_r1, "preferredleader", "true");
verifyUniquePropertyWithinCollection(client, COLLECTION_NAME, "preferredLeader");
doPropertyAction(client,
"action", CollectionParams.CollectionAction.ADDREPLICAPROP.toString(),
"collection", COLLECTION_NAME1,
"shard", c2_s1,
"replica", c2_s1_r1,
"property", "preferredLeader",
"property.value", "true");
// Now we should have three preferred leaders.
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r2, "preferredleader", "true");
verifyPropertyVal(client, COLLECTION_NAME, c1_s2_r1, "preferredleader", "true");
verifyPropertyVal(client, COLLECTION_NAME1, c2_s1_r1, "preferredleader", "true");
verifyUniquePropertyWithinCollection(client, COLLECTION_NAME, "preferredLeader");
verifyUniquePropertyWithinCollection(client, COLLECTION_NAME1, "preferredLeader");
doPropertyAction(client,
"action", CollectionParams.CollectionAction.DELETEREPLICAPROP.toString(),
"collection", COLLECTION_NAME1,
"shard", c2_s1,
"replica", c2_s1_r1,
"property", "preferredLeader");
// Now we should have two preferred leaders.
// But first we have to wait for the overseer to finish the action
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r2, "preferredleader", "true");
verifyPropertyVal(client, COLLECTION_NAME, c1_s2_r1, "preferredleader", "true");
verifyUniquePropertyWithinCollection(client, COLLECTION_NAME, "preferredLeader");
verifyUniquePropertyWithinCollection(client, COLLECTION_NAME1, "preferredLeader");
// Try adding an arbitrary property to one that has the leader property
doPropertyAction(client,
"action", CollectionParams.CollectionAction.ADDREPLICAPROP.toString(),
"collection", COLLECTION_NAME,
"shard", c1_s1,
"replica", c1_s1_r1,
"property", "testprop",
"property.value", "true");
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r2, "preferredleader", "true");
verifyPropertyVal(client, COLLECTION_NAME, c1_s2_r1, "preferredleader", "true");
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r1, "testprop", "true");
verifyUniquePropertyWithinCollection(client, COLLECTION_NAME, "preferredLeader");
verifyUniquePropertyWithinCollection(client, COLLECTION_NAME1, "preferredLeader");
doPropertyAction(client,
"action", CollectionParams.CollectionAction.ADDREPLICAPROP.toString(),
"collection", COLLECTION_NAME,
"shard", c1_s1,
"replica", c1_s1_r2,
"property", "prop",
"property.value", "silly");
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r2, "preferredleader", "true");
verifyPropertyVal(client, COLLECTION_NAME, c1_s2_r1, "preferredleader", "true");
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r1, "testprop", "true");
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r2, "prop", "silly");
verifyUniquePropertyWithinCollection(client, COLLECTION_NAME, "preferredLeader");
verifyUniquePropertyWithinCollection(client, COLLECTION_NAME1, "preferredLeader");
doPropertyAction(client,
"action", CollectionParams.CollectionAction.ADDREPLICAPROP.toLower(),
"collection", COLLECTION_NAME,
"shard", c1_s1,
"replica", c1_s1_r1,
"property", "testprop",
"property.value", "nonsense",
OverseerCollectionMessageHandler.SHARD_UNIQUE, "true");
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r2, "preferredleader", "true");
verifyPropertyVal(client, COLLECTION_NAME, c1_s2_r1, "preferredleader", "true");
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r1, "testprop", "nonsense");
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r2, "prop", "silly");
verifyUniquePropertyWithinCollection(client, COLLECTION_NAME, "preferredLeader");
verifyUniquePropertyWithinCollection(client, COLLECTION_NAME1, "preferredLeader");
doPropertyAction(client,
"action", CollectionParams.CollectionAction.ADDREPLICAPROP.toLower(),
"collection", COLLECTION_NAME,
"shard", c1_s1,
"replica", c1_s1_r1,
"property", "property.testprop",
"property.value", "true",
OverseerCollectionMessageHandler.SHARD_UNIQUE, "false");
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r2, "preferredleader", "true");
verifyPropertyVal(client, COLLECTION_NAME, c1_s2_r1, "preferredleader", "true");
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r1, "testprop", "true");
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r2, "prop", "silly");
verifyUniquePropertyWithinCollection(client, COLLECTION_NAME, "preferredLeader");
verifyUniquePropertyWithinCollection(client, COLLECTION_NAME1, "preferredLeader");
doPropertyAction(client,
"action", CollectionParams.CollectionAction.DELETEREPLICAPROP.toLower(),
"collection", COLLECTION_NAME,
"shard", c1_s1,
"replica", c1_s1_r1,
"property", "property.testprop");
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r2, "preferredleader", "true");
verifyPropertyVal(client, COLLECTION_NAME, c1_s2_r1, "preferredleader", "true");
verifyPropertyNotPresent(client, COLLECTION_NAME, c1_s1_r1, "testprop");
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r2, "prop", "silly");
verifyUniquePropertyWithinCollection(client, COLLECTION_NAME, "preferredLeader");
verifyUniquePropertyWithinCollection(client, COLLECTION_NAME1, "preferredLeader");
try {
doPropertyAction(client,
"action", CollectionParams.CollectionAction.ADDREPLICAPROP.toString(),
"collection", COLLECTION_NAME,
"shard", c1_s1,
"replica", c1_s1_r1,
"property", "preferredLeader",
"property.value", "true",
OverseerCollectionMessageHandler.SHARD_UNIQUE, "false");
fail("Should have thrown an exception, setting shardUnique=false is not allowed for 'preferredLeader'.");
} catch (SolrException se) {
assertTrue("Should have received a specific error message",
se.getMessage().contains("with the shardUnique parameter set to something other than 'true'"));
}
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r2, "preferredleader", "true");
verifyPropertyVal(client, COLLECTION_NAME, c1_s2_r1, "preferredleader", "true");
verifyPropertyNotPresent(client, COLLECTION_NAME, c1_s1_r1, "testprop");
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r2, "prop", "silly");
verifyUniquePropertyWithinCollection(client, COLLECTION_NAME, "preferredLeader");
verifyUniquePropertyWithinCollection(client, COLLECTION_NAME1, "preferredLeader");
Map<String, String> origProps = getProps(client, COLLECTION_NAME, c1_s1_r1,
"state", "core", "node_name", "base_url");
doPropertyAction(client,
"action", CollectionParams.CollectionAction.ADDREPLICAPROP.toLower(),
"collection", COLLECTION_NAME,
"shard", c1_s1,
"replica", c1_s1_r1,
"property", "state",
"property.value", "state_bad");
doPropertyAction(client,
"action", CollectionParams.CollectionAction.ADDREPLICAPROP.toLower(),
"collection", COLLECTION_NAME,
"shard", c1_s1,
"replica", c1_s1_r1,
"property", "core",
"property.value", "core_bad");
doPropertyAction(client,
"action", CollectionParams.CollectionAction.ADDREPLICAPROP.toLower(),
"collection", COLLECTION_NAME,
"shard", c1_s1,
"replica", c1_s1_r1,
"property", "node_name",
"property.value", "node_name_bad");
doPropertyAction(client,
"action", CollectionParams.CollectionAction.ADDREPLICAPROP.toLower(),
"collection", COLLECTION_NAME,
"shard", c1_s1,
"replica", c1_s1_r1,
"property", "base_url",
"property.value", "base_url_bad");
// The above should be on new proeprties.
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r1, "state", "state_bad");
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r1, "core", "core_bad");
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r1, "node_name", "node_name_bad");
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r1, "base_url", "base_url_bad");
doPropertyAction(client,
"action", CollectionParams.CollectionAction.DELETEREPLICAPROP.toLower(),
"collection", COLLECTION_NAME,
"shard", c1_s1,
"replica", c1_s1_r1,
"property", "state");
doPropertyAction(client,
"action", CollectionParams.CollectionAction.DELETEREPLICAPROP.toLower(),
"collection", COLLECTION_NAME,
"shard", c1_s1,
"replica", c1_s1_r1,
"property", "core");
doPropertyAction(client,
"action", CollectionParams.CollectionAction.DELETEREPLICAPROP.toLower(),
"collection", COLLECTION_NAME,
"shard", c1_s1,
"replica", c1_s1_r1,
"property", "node_name");
doPropertyAction(client,
"action", CollectionParams.CollectionAction.DELETEREPLICAPROP.toLower(),
"collection", COLLECTION_NAME,
"shard", c1_s1,
"replica", c1_s1_r1,
"property", "base_url");
// They better not have been changed!
for (Map.Entry<String, String> ent : origProps.entrySet()) {
verifyPropertyVal(client, COLLECTION_NAME, c1_s1_r1, ent.getKey(), ent.getValue());
}
verifyPropertyNotPresent(client, COLLECTION_NAME, c1_s1_r1, "state");
verifyPropertyNotPresent(client, COLLECTION_NAME, c1_s1_r1, "core");
verifyPropertyNotPresent(client, COLLECTION_NAME, c1_s1_r1, "node_name");
verifyPropertyNotPresent(client, COLLECTION_NAME, c1_s1_r1, "base_url");
}
}
private void testCollectionCreationCollectionNameValidation() throws Exception {
try (CloudHttp2SolrClient client = createCloudClient(null)) {
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CREATE.toString());
params.set("name", "invalid@name#with$weird%characters");
SolrRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
try {
client.request(request);
fail();
} catch (BaseHttpSolrClient.RemoteSolrException e) {
final String errorMessage = e.getMessage();
assertTrue(errorMessage.contains("Invalid collection"));
assertTrue(errorMessage.contains("invalid@name#with$weird%characters"));
assertTrue(errorMessage.contains("collection names must consist entirely of"));
}
}
}
private void testCollectionCreationShardNameValidation() throws Exception {
try (CloudHttp2SolrClient client = createCloudClient(null)) {
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CREATE.toString());
params.set("name", "valid_collection_name");
params.set("router.name", "implicit");
params.set("numShards", "1");
params.set("shards", "invalid@name#with$weird%characters");
params.set(WAIT_FOR_FINAL_STATE, "true");
SolrRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
try {
client.request(request);
fail();
} catch (BaseHttpSolrClient.RemoteSolrException e) {
final String errorMessage = e.getMessage();
assertTrue(errorMessage.contains("Invalid shard"));
assertTrue(errorMessage.contains("invalid@name#with$weird%characters"));
assertTrue(errorMessage.contains("shard names must consist entirely of"));
}
}
}
private void testAliasCreationNameValidation() throws Exception{
try (CloudHttp2SolrClient client = createCloudClient(null)) {
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CREATEALIAS.toString());
params.set("name", "invalid@name#with$weird%characters");
params.set("collections", COLLECTION_NAME);
SolrRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
try {
client.request(request);
fail();
} catch (BaseHttpSolrClient.RemoteSolrException e) {
final String errorMessage = e.getMessage();
assertTrue(errorMessage.contains("Invalid alias"));
assertTrue(errorMessage.contains("invalid@name#with$weird%characters"));
assertTrue(errorMessage.contains("alias names must consist entirely of"));
}
}
}
private void testShardCreationNameValidation() throws Exception {
try (CloudHttp2SolrClient client = createCloudClient(null)) {
client.connect();
// Create a collection w/ implicit router
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CREATE.toString());
params.set("name", "valid_collection_name");
params.set("shards", "a");
params.set("router.name", "implicit");
SolrRequest request = new QueryRequest(params);
request.setPath("/admin/collections");
client.request(request);
params = new ModifiableSolrParams();
params.set("action", CollectionParams.CollectionAction.CREATESHARD.toString());
params.set("collection", "valid_collection_name");
params.set("shard", "invalid@name#with$weird%characters");
request = new QueryRequest(params);
request.setPath("/admin/collections");
try {
client.request(request);
fail();
} catch (BaseHttpSolrClient.RemoteSolrException e) {
final String errorMessage = e.getMessage();
assertTrue(errorMessage, errorMessage.contains("Invalid shard"));
assertTrue(errorMessage.contains("invalid@name#with$weird%characters"));
assertTrue(errorMessage.contains("shard names must consist entirely of"));
}
}
}
// Expects the map will have keys, but blank values.
private Map<String, String> getProps(CloudHttp2SolrClient client, String collectionName, String replicaName, String... props)
throws KeeperException, InterruptedException {
ClusterState clusterState = client.getZkStateReader().getClusterState();
final DocCollection docCollection = clusterState.getCollectionOrNull(collectionName);
if (docCollection == null || docCollection.getReplica(replicaName) == null) {
fail("Could not find collection/replica pair! " + collectionName + "/" + replicaName);
}
Replica replica = docCollection.getReplica(replicaName);
Map<String, String> propMap = new HashMap<>();
for (String prop : props) {
propMap.put(prop, replica.getProperty(prop));
}
return propMap;
}
private void missingParamsError(CloudHttp2SolrClient client, ModifiableSolrParams origParams)
throws IOException, SolrServerException {
SolrRequest request;
try {
request = new QueryRequest(origParams);
request.setPath("/admin/collections");
client.request(request);
fail("Should have thrown a SolrException due to lack of a required parameter.");
} catch (SolrException se) {
assertTrue("Should have gotten a specific message back mentioning 'missing required parameter'. Got: " + se.getMessage(),
se.getMessage().toLowerCase(Locale.ROOT).contains("missing required parameter:"));
}
}
/**
* After a failed attempt to create a collection (due to bad configs), assert that
* the collection can be created with a good collection.
*/
@Test
public void testRecreateCollectionAfterFailure() throws Exception {
// Upload a bad configset
SolrZkClient zkClient = new SolrZkClient(cluster.getZkServer().getZkHost(), ZkTestServer.TIMEOUT,
ZkTestServer.TIMEOUT, null);
zkClient.start();
ZkTestServer.putConfig("badconf", zkClient, "/solr", ZkTestServer.SOLRHOME, "bad-error-solrconfig.xml", "solrconfig.xml");
ZkTestServer.putConfig("badconf", zkClient, "/solr", ZkTestServer.SOLRHOME, "schema-minimal.xml", "schema.xml");
zkClient.close();
try (CloudHttp2SolrClient client = createCloudClient(null)) {
// first, try creating a collection with badconf
// MRM TODO: this should fail with the wait for final state
BaseHttpSolrClient.RemoteSolrException rse = SolrTestCaseUtil.expectThrows(BaseHttpSolrClient.RemoteSolrException.class, () -> {
CollectionAdminResponse rsp = CollectionAdminRequest.createCollection("testcollection", "badconf", 1, 2).process(client);
});
assertNotNull(rse.getMessage());
assertNotSame(0, rse.code());
CollectionAdminResponse rsp = CollectionAdminRequest.createCollection
("testcollection", "_default", 1, 2).process(client);
assertNull(rsp.getErrorMessages());
assertSame(0, rsp.getStatus());
}
}
}