/*
* 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.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.lucene.util.LuceneTestCase.Slow;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.common.cloud.*;
import org.apache.solr.common.util.Utils;
import org.apache.solr.core.CloudConfig;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.CoreDescriptor;
import org.apache.solr.core.SolrXmlConfig;
import org.apache.solr.core.TransientSolrCoreCache;
import org.apache.solr.handler.admin.CoreAdminHandler;
import org.apache.solr.handler.component.HttpShardHandlerFactory;
import org.apache.solr.update.UpdateShardHandler;
import org.apache.solr.update.UpdateShardHandlerConfig;
import org.apache.zookeeper.CreateMode;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
@Slow
@SolrTestCaseJ4.SuppressSSL
public class ZkControllerTest extends SolrTestCaseJ4 {
private static final String COLLECTION_NAME = "collection1";
static final int TIMEOUT = 10000;
@BeforeClass
public static void beforeClass() throws Exception {
}
@AfterClass
public static void afterClass() throws Exception {
}
public void testNodeNameUrlConversion() throws Exception {
// nodeName from parts
assertEquals("localhost:8888_solr",
ZkController.generateNodeName("localhost", "8888", "solr"));
assertEquals("localhost:8888_solr",
ZkController.generateNodeName("localhost", "8888", "/solr"));
assertEquals("localhost:8888_solr",
ZkController.generateNodeName("localhost", "8888", "/solr/"));
// root context
assertEquals("localhost:8888_",
ZkController.generateNodeName("localhost", "8888", ""));
assertEquals("localhost:8888_",
ZkController.generateNodeName("localhost", "8888", "/"));
// subdir
assertEquals("foo-bar:77_solr%2Fsub_dir",
ZkController.generateNodeName("foo-bar", "77", "solr/sub_dir"));
assertEquals("foo-bar:77_solr%2Fsub_dir",
ZkController.generateNodeName("foo-bar", "77", "/solr/sub_dir"));
assertEquals("foo-bar:77_solr%2Fsub_dir",
ZkController.generateNodeName("foo-bar", "77", "/solr/sub_dir/"));
// setup a SolrZkClient to do some getBaseUrlForNodeName testing
String zkDir = createTempDir("zkData").toFile().getAbsolutePath();
ZkTestServer server = new ZkTestServer(zkDir);
try {
server.run();
AbstractZkTestCase.tryCleanSolrZkNode(server.getZkHost());
AbstractZkTestCase.makeSolrZkNode(server.getZkHost());
try (SolrZkClient client = new SolrZkClient(server.getZkAddress(), TIMEOUT)) {
ZkController.createClusterZkNodes(client);
try (ZkStateReader zkStateReader = new ZkStateReader(client)) {
zkStateReader.createClusterStateWatchersAndUpdate();
// getBaseUrlForNodeName
assertEquals("http://zzz.xxx:1234/solr",
zkStateReader.getBaseUrlForNodeName("zzz.xxx:1234_solr"));
assertEquals("http://xxx:99",
zkStateReader.getBaseUrlForNodeName("xxx:99_"));
assertEquals("http://foo-bar.baz.org:9999/some_dir",
zkStateReader.getBaseUrlForNodeName("foo-bar.baz.org:9999_some_dir"));
assertEquals("http://foo-bar.baz.org:9999/solr/sub_dir",
zkStateReader.getBaseUrlForNodeName("foo-bar.baz.org:9999_solr%2Fsub_dir"));
// generateNodeName + getBaseUrlForNodeName
assertEquals("http://foo:9876/solr",
zkStateReader.getBaseUrlForNodeName
(ZkController.generateNodeName("foo", "9876", "solr")));
assertEquals("http://foo:9876/solr",
zkStateReader.getBaseUrlForNodeName
(ZkController.generateNodeName("foo", "9876", "/solr")));
assertEquals("http://foo:9876/solr",
zkStateReader.getBaseUrlForNodeName
(ZkController.generateNodeName("foo", "9876", "/solr/")));
assertEquals("http://foo.bar.com:9876/solr/sub_dir",
zkStateReader.getBaseUrlForNodeName
(ZkController.generateNodeName("foo.bar.com", "9876", "solr/sub_dir")));
assertEquals("http://foo.bar.com:9876/solr/sub_dir",
zkStateReader.getBaseUrlForNodeName
(ZkController.generateNodeName("foo.bar.com", "9876", "/solr/sub_dir/")));
assertEquals("http://foo-bar:9876",
zkStateReader.getBaseUrlForNodeName
(ZkController.generateNodeName("foo-bar", "9876", "")));
assertEquals("http://foo-bar:9876",
zkStateReader.getBaseUrlForNodeName
(ZkController.generateNodeName("foo-bar", "9876", "/")));
assertEquals("http://foo-bar.com:80/some_dir",
zkStateReader.getBaseUrlForNodeName
(ZkController.generateNodeName("foo-bar.com", "80", "some_dir")));
assertEquals("http://foo-bar.com:80/some_dir",
zkStateReader.getBaseUrlForNodeName
(ZkController.generateNodeName("foo-bar.com", "80", "/some_dir")));
}
ClusterProperties cp = new ClusterProperties(client);
cp.setClusterProperty("urlScheme", "https");
//Verify the URL Scheme is taken into account
try (ZkStateReader zkStateReader = new ZkStateReader(client)) {
zkStateReader.createClusterStateWatchersAndUpdate();
assertEquals("https://zzz.xxx:1234/solr",
zkStateReader.getBaseUrlForNodeName("zzz.xxx:1234_solr"));
assertEquals("https://foo-bar.com:80/some_dir",
zkStateReader.getBaseUrlForNodeName
(ZkController.generateNodeName("foo-bar.com", "80", "/some_dir")));
}
}
} finally {
server.shutdown();
}
}
@Test
public void testReadConfigName() throws Exception {
String zkDir = createTempDir("zkData").toFile().getAbsolutePath();
CoreContainer cc = null;
ZkTestServer server = new ZkTestServer(zkDir);
try {
server.run();
AbstractZkTestCase.tryCleanSolrZkNode(server.getZkHost());
AbstractZkTestCase.makeSolrZkNode(server.getZkHost());
SolrZkClient zkClient = new SolrZkClient(server.getZkAddress(), TIMEOUT);
String actualConfigName = "firstConfig";
zkClient.makePath(ZkConfigManager.CONFIGS_ZKNODE + "/" + actualConfigName, true);
Map<String,Object> props = new HashMap<>();
props.put("configName", actualConfigName);
ZkNodeProps zkProps = new ZkNodeProps(props);
zkClient.makePath(ZkStateReader.COLLECTIONS_ZKNODE + "/"
+ COLLECTION_NAME, Utils.toJSON(zkProps),
CreateMode.PERSISTENT, true);
zkClient.close();
cc = getCoreContainer();
CloudConfig cloudConfig = new CloudConfig.CloudConfigBuilder("127.0.0.1", 8983, "solr").build();
ZkController zkController = new ZkController(cc, server.getZkAddress(), TIMEOUT, cloudConfig,
new CurrentCoreDescriptorProvider() {
@Override
public List<CoreDescriptor> getCurrentDescriptors() {
// do nothing
return null;
}
});
try {
String configName = zkController.getZkStateReader().readConfigName(COLLECTION_NAME);
assertEquals(configName, actualConfigName);
} finally {
zkController.close();
}
} finally {
if (cc != null) {
cc.shutdown();
}
server.shutdown();
}
}
public void testGetHostName() throws Exception {
String zkDir = createTempDir("zkData").toFile().getAbsolutePath();
CoreContainer cc = null;
ZkTestServer server = new ZkTestServer(zkDir);
try {
server.run();
AbstractZkTestCase.tryCleanSolrZkNode(server.getZkHost());
AbstractZkTestCase.makeSolrZkNode(server.getZkHost());
cc = getCoreContainer();
ZkController zkController = null;
try {
CloudConfig cloudConfig = new CloudConfig.CloudConfigBuilder("127.0.0.1", 8983, "solr").build();
zkController = new ZkController(cc, server.getZkAddress(), TIMEOUT, cloudConfig, new CurrentCoreDescriptorProvider() {
@Override
public List<CoreDescriptor> getCurrentDescriptors() {
// do nothing
return null;
}
});
} catch (IllegalArgumentException e) {
fail("ZkController did not normalize host name correctly");
} finally {
if (zkController != null)
zkController.close();
}
} finally {
if (cc != null) {
cc.shutdown();
}
server.shutdown();
}
}
@AwaitsFix(bugUrl = "https://issues.apache.org/jira/browse/SOLR-7736")
public void testPublishAndWaitForDownStates() throws Exception {
String zkDir = createTempDir("testPublishAndWaitForDownStates").toFile().getAbsolutePath();
CoreContainer cc = null;
ZkTestServer server = new ZkTestServer(zkDir);
try {
server.run();
AbstractZkTestCase.tryCleanSolrZkNode(server.getZkHost());
AbstractZkTestCase.makeSolrZkNode(server.getZkHost());
cc = getCoreContainer();
ZkController zkController = null;
try {
CloudConfig cloudConfig = new CloudConfig.CloudConfigBuilder("127.0.0.1", 8983, "solr").build();
zkController = new ZkController(cc, server.getZkAddress(), TIMEOUT, cloudConfig, new CurrentCoreDescriptorProvider() {
@Override
public List<CoreDescriptor> getCurrentDescriptors() {
// do nothing
return null;
}
});
HashMap<String, DocCollection> collectionStates = new HashMap<>();
HashMap<String, Replica> replicas = new HashMap<>();
// add two replicas with the same core name but one of them should be on a different node
// than this ZkController instance
for (int i=1; i<=2; i++) {
Replica r = new Replica("core_node" + i,
map(ZkStateReader.STATE_PROP, i == 1 ? "active" : "down",
ZkStateReader.NODE_NAME_PROP, i == 1 ? "127.0.0.1:8983_solr" : "non_existent_host",
ZkStateReader.CORE_NAME_PROP, "collection1"));
replicas.put("core_node" + i, r);
}
HashMap<String, Object> sliceProps = new HashMap<>();
sliceProps.put("state", Slice.State.ACTIVE.toString());
Slice slice = new Slice("shard1", replicas, sliceProps);
DocCollection c = new DocCollection("testPublishAndWaitForDownStates", map("shard1", slice), Collections.emptyMap(), DocRouter.DEFAULT);
ClusterState state = new ClusterState(0, Collections.emptySet(), map("testPublishAndWaitForDownStates", c));
byte[] bytes = Utils.toJSON(state);
zkController.getZkClient().makePath(ZkStateReader.getCollectionPath("testPublishAndWaitForDownStates"), bytes, CreateMode.PERSISTENT, true);
zkController.getZkStateReader().forceUpdateCollection("testPublishAndWaitForDownStates");
assertTrue(zkController.getZkStateReader().getClusterState().hasCollection("testPublishAndWaitForDownStates"));
assertNotNull(zkController.getZkStateReader().getClusterState().getCollection("testPublishAndWaitForDownStates"));
long now = System.nanoTime();
long timeout = now + TimeUnit.NANOSECONDS.convert(ZkController.WAIT_DOWN_STATES_TIMEOUT_SECONDS, TimeUnit.SECONDS);
zkController.publishAndWaitForDownStates();
assertTrue("The ZkController.publishAndWaitForDownStates should have timed out but it didn't", System.nanoTime() >= timeout);
} finally {
if (zkController != null)
zkController.close();
}
} finally {
if (cc != null) {
cc.shutdown();
}
server.shutdown();
}
}
private CoreContainer getCoreContainer() {
return new MockCoreContainer();
}
@Override
public void tearDown() throws Exception {
super.tearDown();
}
private static class MockCoreContainer extends CoreContainer {
UpdateShardHandler updateShardHandler = new UpdateShardHandler(UpdateShardHandlerConfig.DEFAULT);
public MockCoreContainer() {
super(SolrXmlConfig.fromString(null, "<solr/>"));
this.shardHandlerFactory = new HttpShardHandlerFactory();
this.coreAdminHandler = new CoreAdminHandler();
}
@Override
public void load() {}
@Override
public UpdateShardHandler getUpdateShardHandler() {
return updateShardHandler;
}
@Override
public void shutdown() {
updateShardHandler.close();
super.shutdown();
}
@Override
public TransientSolrCoreCache getTransientCacheHandler() {
return transientSolrCoreCache;
}
}
}