This is an automated email from the ASF dual-hosted git repository.
ab pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr-sandbox.git
The following commit(s) were added to refs/heads/main by this push:
new 713d634 Add support for mirroring ConfigSet requests. (#69)
713d634 is described below
commit 713d634718db5a2a8548a4ce9064b884e70dd8c9
Author: Andrzej BiaĆecki <[email protected]>
AuthorDate: Tue Oct 17 10:25:27 2023 +0200
Add support for mirroring ConfigSet requests. (#69)
---
.../solr/crossdc/common/MirroredSolrRequest.java | 88 +++++++++++-
.../common/MirroredSolrRequestSerializer.java | 39 ++++++
.../handler/admin/MirroringConfigSetsHandler.java | 115 ++++++++++++++++
.../admin/MirroringCollectionsHandlerTest.java | 2 +
.../admin/MirroringConfigSetsHandlerTest.java | 151 +++++++++++++++++++++
.../src/test/resources/configs/cloud-minimal.zip | Bin 0 -> 3000 bytes
.../src/test/resources/mirroring-solr.xml | 1 +
7 files changed, 395 insertions(+), 1 deletion(-)
diff --git
a/crossdc-commons/src/main/java/org/apache/solr/crossdc/common/MirroredSolrRequest.java
b/crossdc-commons/src/main/java/org/apache/solr/crossdc/common/MirroredSolrRequest.java
index b32ad5e..b85616b 100644
---
a/crossdc-commons/src/main/java/org/apache/solr/crossdc/common/MirroredSolrRequest.java
+++
b/crossdc-commons/src/main/java/org/apache/solr/crossdc/common/MirroredSolrRequest.java
@@ -19,16 +19,22 @@ package org.apache.solr.crossdc.common;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.request.ConfigSetAdminRequest;
import org.apache.solr.client.solrj.request.UpdateRequest;
import org.apache.solr.client.solrj.response.CollectionAdminResponse;
+import org.apache.solr.client.solrj.response.ConfigSetAdminResponse;
import org.apache.solr.common.params.CollectionParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.ContentStream;
+import org.apache.solr.common.util.ContentStreamBase;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.*;
import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
/**
* Class to encapsulate a mirrored Solr request.
@@ -39,6 +45,7 @@ public class MirroredSolrRequest {
public enum Type {
UPDATE,
ADMIN,
+ CONFIGSET,
UNKNOWN;
public static final Type get(String s) {
@@ -72,6 +79,85 @@ public class MirroredSolrRequest {
}
}
+ public static class MirroredConfigSetRequest extends
ConfigSetAdminRequest<MirroredConfigSetRequest, ConfigSetAdminResponse> {
+ private Collection<ContentStream> contentStreams;
+ private ModifiableSolrParams params;
+
+ public MirroredConfigSetRequest(METHOD method, SolrParams params,
Collection<ContentStream> contentStreams) {
+ this.setMethod(method);
+ this.params = ModifiableSolrParams.of(params);
+ this.contentStreams = contentStreams;
+ if (method == METHOD.POST && (contentStreams == null ||
contentStreams.isEmpty())) {
+ throw new RuntimeException("Invalid request - POST requires at
least 1 content stream");
+ }
+ }
+
+ @Override
+ protected MirroredConfigSetRequest getThis() {
+ return this;
+ }
+
+ @Override
+ public SolrParams getParams() {
+ return params;
+ }
+
+ public void setParams(ModifiableSolrParams params) {
+ this.params = params;
+ }
+
+ @Override
+ public Collection<ContentStream> getContentStreams() {
+ return contentStreams;
+ }
+
+ @Override
+ protected ConfigSetAdminResponse createResponse(SolrClient client) {
+ return new ConfigSetAdminResponse();
+ }
+ }
+
+ public static class ExposedByteArrayContentStream extends
ContentStreamBase {
+ private final byte[] bytes;
+
+ public ExposedByteArrayContentStream(byte[] bytes, String source,
String contentType) {
+ this.bytes = bytes;
+
+ this.contentType = contentType;
+ name = source;
+ size = (long) bytes.length;
+ sourceInfo = source;
+ }
+
+ public byte[] byteArray() {
+ return bytes;
+ }
+
+ @Override
+ public InputStream getStream() throws IOException {
+ return new ByteArrayInputStream(bytes);
+ }
+
+ @Override
+ public String toString() {
+ return "contentType=" + contentType +
+ ", name=" + name +
+ ", sourceInfo=" + sourceInfo +
+ ", size=" + size;
+ }
+
+ public static ExposedByteArrayContentStream of(ContentStream cs)
throws IOException {
+ if (cs instanceof ExposedByteArrayContentStream) {
+ return (ExposedByteArrayContentStream) cs;
+ }
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ cs.getStream().transferTo(baos);
+ ExposedByteArrayContentStream res = new
ExposedByteArrayContentStream(baos.toByteArray(), cs.getSourceInfo(),
cs.getContentType());
+ res.setName(cs.getName());
+ return res;
+ }
+ }
+
private final SolrRequest solrRequest;
private final Type type;
diff --git
a/crossdc-commons/src/main/java/org/apache/solr/crossdc/common/MirroredSolrRequestSerializer.java
b/crossdc-commons/src/main/java/org/apache/solr/crossdc/common/MirroredSolrRequestSerializer.java
index 86832ad..ceca073 100644
---
a/crossdc-commons/src/main/java/org/apache/solr/crossdc/common/MirroredSolrRequestSerializer.java
+++
b/crossdc-commons/src/main/java/org/apache/solr/crossdc/common/MirroredSolrRequestSerializer.java
@@ -24,6 +24,7 @@ import org.apache.solr.common.params.CollectionParams;
import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.common.params.MapSolrParams;
import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.util.ContentStream;
import org.apache.solr.common.util.JavaBinCodec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,6 +33,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -114,6 +116,27 @@ public class MirroredSolrRequestSerializer implements
Serializer<MirroredSolrReq
}
request = new MirroredSolrRequest.MirroredAdminRequest(action,
params);
log.debug("-- admin req={}",
((MirroredSolrRequest.MirroredAdminRequest) request).jsonStr());
+ } else if (type == MirroredSolrRequest.Type.CONFIGSET) {
+ List<ContentStream> contentStreams = null;
+ String m = (String) requestMap.get("method");
+ SolrRequest.METHOD method = SolrRequest.METHOD.valueOf(m);
+ List<String> csNames = new ArrayList<>();
+ List<Map<String, Object>> streamsList = (List<Map<String,
Object>>) requestMap.get("contentStreams");
+ if (streamsList != null) {
+ contentStreams = new ArrayList<>();
+ for (Map<String, Object> streamMap : streamsList) {
+ String contentType = (String) streamMap.get("contentType");
+ String name = (String) streamMap.get("name");
+ csNames.add(name);
+ String sourceInfo = (String) streamMap.get("sourceInfo");
+ byte[] content = (byte[]) streamMap.get("content");
+ MirroredSolrRequest.ExposedByteArrayContentStream ecs =
new MirroredSolrRequest.ExposedByteArrayContentStream(content, sourceInfo,
contentType);
+ ecs.setName(name);
+ contentStreams.add(ecs);
+ }
+ }
+ request = new MirroredSolrRequest.MirroredConfigSetRequest(method,
params, contentStreams);
+ log.debug("-- configSet method={}, req={}, streams={}",
request.getMethod(), request.getParams(), csNames);
} else {
throw new RuntimeException("Unknown request type: " + requestMap);
}
@@ -150,6 +173,22 @@ public class MirroredSolrRequestSerializer implements
Serializer<MirroredSolrReq
map.put("docs", update.getDocuments());
map.put("deletes", update.getDeleteById());
map.put("deleteQuery", update.getDeleteQuery());
+ } else if (solrRequest instanceof
MirroredSolrRequest.MirroredConfigSetRequest) {
+ MirroredSolrRequest.MirroredConfigSetRequest config =
(MirroredSolrRequest.MirroredConfigSetRequest) solrRequest;
+ map.put("method", config.getMethod().toString());
+ if (config.getContentStreams() != null) {
+ List<Map<String, Object>> streamsList = new ArrayList<>();
+ for (ContentStream cs : config.getContentStreams()) {
+ Map<String, Object> streamMap = new HashMap<>();
+ streamMap.put("name", cs.getName());
+ streamMap.put("contentType", cs.getContentType());
+ streamMap.put("sourceInfo", cs.getSourceInfo());
+ MirroredSolrRequest.ExposedByteArrayContentStream ecs
= MirroredSolrRequest.ExposedByteArrayContentStream.of(cs);
+ streamMap.put("content", ecs.byteArray());
+ streamsList.add(streamMap);
+ }
+ map.put("contentStreams", streamsList);
+ }
}
codec.marshal(map, baos);
diff --git
a/crossdc-producer/src/main/java/org/apache/solr/handler/admin/MirroringConfigSetsHandler.java
b/crossdc-producer/src/main/java/org/apache/solr/handler/admin/MirroringConfigSetsHandler.java
new file mode 100644
index 0000000..be766f5
--- /dev/null
+++
b/crossdc-producer/src/main/java/org/apache/solr/handler/admin/MirroringConfigSetsHandler.java
@@ -0,0 +1,115 @@
+/*
+ * 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 com.google.common.annotations.VisibleForTesting;
+import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.common.cloud.SolrZkClient;
+import org.apache.solr.common.util.ContentStream;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.crossdc.common.ConfUtil;
+import org.apache.solr.crossdc.common.CrossDcConstants;
+import org.apache.solr.crossdc.common.KafkaCrossDcConf;
+import org.apache.solr.crossdc.common.KafkaMirroringSink;
+import org.apache.solr.crossdc.common.MirroredSolrRequest;
+import org.apache.solr.request.LocalSolrQueryRequest;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+public class MirroringConfigSetsHandler extends ConfigSetsHandler {
+ private static final Logger log =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ private KafkaMirroringSink sink;
+
+ /**
+ * Overloaded ctor to inject CoreContainer into the handler.
+ *
+ * @param coreContainer Core Container of the solr webapp installed.
+ */
+ public MirroringConfigSetsHandler(CoreContainer coreContainer) {
+ this(coreContainer, null);
+ }
+
+ public MirroringConfigSetsHandler(CoreContainer coreContainer,
KafkaMirroringSink sink) {
+ super(coreContainer);
+ log.info("Using MirroringCollectionsHandler.");
+ if (sink == null) {
+ Map<String, Object> properties = new HashMap<>();
+ try {
+ SolrZkClient zkClient = null;
+ if (coreContainer.getZkController() != null) {
+ zkClient = coreContainer.getZkController().getZkClient();
+ }
+ ConfUtil.fillProperties(zkClient, properties);
+ KafkaCrossDcConf conf = new KafkaCrossDcConf(properties);
+ this.sink = new KafkaMirroringSink(conf);
+ } catch (Exception e) {
+ log.error("Exception configuring Kafka sink - mirroring disabled!", e);
+ this.sink = null;
+ }
+ } else {
+ this.sink = sink;
+ }
+ }
+
+ @Override
+ public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp)
throws Exception {
+ boolean doMirroring =
req.getParams().getBool(CrossDcConstants.SHOULD_MIRROR, true);
+ if (!doMirroring) {
+ log.info(" -- doMirroring=false, skipping...");
+ super.handleRequestBody(req, rsp);
+ return;
+ }
+ // fully read all streams and re-package them so they are re-readable
+ LocalSolrQueryRequest localReq = new LocalSolrQueryRequest(req.getCore(),
req.getParams());
+ List<ContentStream> contentStreams = null;
+ if (req.getContentStreams() != null) {
+ contentStreams = new ArrayList<>();
+ for (ContentStream cs : req.getContentStreams()) {
+ MirroredSolrRequest.ExposedByteArrayContentStream stream =
MirroredSolrRequest.ExposedByteArrayContentStream.of(cs);
+ contentStreams.add(stream);
+ }
+ localReq.setContentStreams(contentStreams);
+ }
+ // throw any errors before mirroring
+ baseHandleRequestBody(localReq, rsp);
+
+ if (rsp.getException() != null) {
+ return;
+ }
+ if (sink == null) {
+ return;
+ }
+ SolrRequest.METHOD method =
SolrRequest.METHOD.valueOf(req.getHttpMethod().toUpperCase(Locale.ROOT));
+ MirroredSolrRequest.MirroredConfigSetRequest configSetRequest = new
MirroredSolrRequest.MirroredConfigSetRequest(method, req.getParams(),
contentStreams);
+ sink.submit(new MirroredSolrRequest(MirroredSolrRequest.Type.CONFIGSET,
configSetRequest));
+ }
+
+ @VisibleForTesting
+ public void baseHandleRequestBody(SolrQueryRequest req, SolrQueryResponse
rsp) throws Exception {
+ super.handleRequestBody(req, rsp);
+ }
+}
diff --git
a/crossdc-producer/src/test/java/org/apache/solr/handler/admin/MirroringCollectionsHandlerTest.java
b/crossdc-producer/src/test/java/org/apache/solr/handler/admin/MirroringCollectionsHandlerTest.java
index 9773007..4bedc51 100644
---
a/crossdc-producer/src/test/java/org/apache/solr/handler/admin/MirroringCollectionsHandlerTest.java
+++
b/crossdc-producer/src/test/java/org/apache/solr/handler/admin/MirroringCollectionsHandlerTest.java
@@ -1,5 +1,6 @@
package org.apache.solr.handler.admin;
+import org.apache.commons.io.IOUtils;
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters;
import org.apache.commons.io.IOUtils;
import org.apache.lucene.util.QuickPatchThreadsFilter;
@@ -13,6 +14,7 @@ import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.SolrXmlConfig;
+import org.apache.solr.crossdc.common.CrossDcConf;
import org.apache.solr.crossdc.SolrKafkaTestsIgnoredThreadsFilter;
import org.apache.solr.crossdc.common.KafkaCrossDcConf;
import org.apache.solr.crossdc.common.KafkaMirroringSink;
diff --git
a/crossdc-producer/src/test/java/org/apache/solr/handler/admin/MirroringConfigSetsHandlerTest.java
b/crossdc-producer/src/test/java/org/apache/solr/handler/admin/MirroringConfigSetsHandlerTest.java
new file mode 100644
index 0000000..d2aa0a5
--- /dev/null
+++
b/crossdc-producer/src/test/java/org/apache/solr/handler/admin/MirroringConfigSetsHandlerTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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 org.apache.commons.io.IOUtils;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.cloud.ZkController;
+import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.common.cloud.SolrZkClient;
+import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.common.params.ConfigSetParams;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.ContentStream;
+import org.apache.solr.common.util.ContentStreamBase;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.core.SolrXmlConfig;
+import org.apache.solr.crossdc.common.CrossDcConf;
+import org.apache.solr.crossdc.common.KafkaMirroringSink;
+import org.apache.solr.crossdc.common.MirroredSolrRequest;
+import org.apache.solr.request.LocalSolrQueryRequest;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+public class MirroringConfigSetsHandlerTest extends SolrTestCaseJ4 {
+
+ private KafkaMirroringSink sink = Mockito.mock(KafkaMirroringSink.class);
+ private CoreContainer coreContainer = Mockito.mock(CoreContainer.class);
+ private ZkController zkController = Mockito.mock(ZkController.class);
+ private SolrZkClient solrZkClient = Mockito.mock(SolrZkClient.class);
+ private ArgumentCaptor<MirroredSolrRequest> captor;
+ private SolrCore solrCore = Mockito.mock(SolrCore.class);
+
+ private static SolrQueryRequest createRequest(SolrCore solrCore,
ConfigSetParams.ConfigSetAction action, String configSetName, String
zipResource) throws Exception {
+ ModifiableSolrParams params = new ModifiableSolrParams();
+ LocalSolrQueryRequest req = new LocalSolrQueryRequest(solrCore,
params);
+ params.set(ConfigSetParams.ACTION, action.toLower());
+ String method = "GET";
+ if (action == ConfigSetParams.ConfigSetAction.UPLOAD) {
+ method = "POST";
+ byte[] content = IOUtils.resourceToByteArray(zipResource);
+ params.set(CommonParams.NAME, configSetName);
+ List<ContentStream> streams = List.of(new
ContentStreamBase.ByteArrayStream(content, configSetName, "application/zip"));
+ req.setContentStreams(streams);
+ }
+ req.getContext().put("httpMethod", method);
+ return req;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ Mockito.when(coreContainer.isZooKeeperAware()).thenReturn(true);
+ Mockito.when(coreContainer.getZkController()).thenReturn(zkController);
+ Mockito.when(zkController.getZkClient()).thenReturn(solrZkClient);
+ Mockito.doAnswer(inv ->
null).when(solrZkClient).getData(Mockito.anyString(), Mockito.any(),
Mockito.any(), Mockito.anyBoolean());
+ captor = ArgumentCaptor.forClass(MirroredSolrRequest.class);
+ Mockito.doNothing().when(sink).submit(captor.capture());
+ }
+
+ @Test
+ public void testList() throws Exception {
+ SolrQueryRequest req = createRequest(solrCore,
ConfigSetParams.ConfigSetAction.LIST, null, null);
+ runCommand(req, false, true);
+ }
+
+ @Test
+ public void testUpload() throws Exception {
+ SolrQueryRequest req = createRequest(solrCore,
ConfigSetParams.ConfigSetAction.UPLOAD, "testConf",
"/configs/cloud-minimal.zip");
+ runCommand(req, true, true);
+ }
+
+ private void runCommand(SolrQueryRequest req, boolean expectStreams,
boolean expectResult) throws Exception {
+ int initialMirroredCount = captor.getAllValues().size();
+ MirroringConfigSetsHandler handler = Mockito.spy(new
MirroringConfigSetsHandler(coreContainer, sink));
+ Mockito.doNothing().when(handler).baseHandleRequestBody(Mockito.any(),
Mockito.any());
+ SolrQueryResponse rsp = new SolrQueryResponse();
+ handler.handleRequestBody(req, rsp);
+ if (expectResult) {
+ assertEquals("should capture additional mirrored value",
initialMirroredCount + 1, captor.getAllValues().size());
+ MirroredSolrRequest mirroredSolrRequest = captor.getValue();
+ assertNotNull("missing mirrored request", mirroredSolrRequest);
+ assertEquals(MirroredSolrRequest.Type.CONFIGSET,
mirroredSolrRequest.getType());
+ SolrRequest solrRequest = mirroredSolrRequest.getSolrRequest();
+ SolrParams mirroredParams = solrRequest.getParams();
+ req.getParams().forEach(entry -> {
+ assertEquals(entry.getValue(),
mirroredParams.getParams(entry.getKey()));
+ });
+ assertEquals("HTTP method", req.getHttpMethod(),
solrRequest.getMethod().toString());
+ if (expectStreams) {
+ List<ContentStream> sourceStreams = (List<ContentStream>)
req.getContentStreams();
+ assertNotNull("source streams missing", sourceStreams);
+ List<ContentStream> mirroredStreams = (List<ContentStream>)
solrRequest.getContentStreams();
+ assertNotNull("mirrored streams missing", mirroredStreams);
+ assertEquals("number of streams", sourceStreams.size(),
mirroredStreams.size());
+ for (int i = 0; i < sourceStreams.size(); i++) {
+ ContentStream source = sourceStreams.get(i);
+ ContentStream mirrored = mirroredStreams.get(i);
+ assertEquals("name", source.getName(), mirrored.getName());
+ assertEquals("contentType", source.getContentType(),
mirrored.getContentType());
+ byte[] sourceContent =
MirroredSolrRequest.ExposedByteArrayContentStream.of(source).byteArray();
+ byte[] mirroredContent =
MirroredSolrRequest.ExposedByteArrayContentStream.of(mirrored).byteArray();
+ assertTrue("different content",
Arrays.equals(sourceContent, mirroredContent));
+ }
+ }
+ } else {
+ assertEquals(initialMirroredCount, captor.getAllValues().size());
+ }
+ }
+
+ @Test
+ public void testCoreContainerInit() throws Exception {
+ Path home = createTempDir();
+ String solrXml = IOUtils.resourceToString("/mirroring-solr.xml",
StandardCharsets.UTF_8);
+ CoreContainer cores = new CoreContainer(SolrXmlConfig.fromString(home,
solrXml));
+ try {
+ cores.load();
+ assertTrue(cores.getConfigSetsHandler() instanceof
MirroringConfigSetsHandler);
+ } finally {
+ cores.shutdown();
+ }
+ }
+}
diff --git a/crossdc-producer/src/test/resources/configs/cloud-minimal.zip
b/crossdc-producer/src/test/resources/configs/cloud-minimal.zip
new file mode 100644
index 0000000..f0c8269
Binary files /dev/null and
b/crossdc-producer/src/test/resources/configs/cloud-minimal.zip differ
diff --git a/crossdc-producer/src/test/resources/mirroring-solr.xml
b/crossdc-producer/src/test/resources/mirroring-solr.xml
index 09b0dbd..6e710ba 100644
--- a/crossdc-producer/src/test/resources/mirroring-solr.xml
+++ b/crossdc-producer/src/test/resources/mirroring-solr.xml
@@ -17,6 +17,7 @@
-->
<solr>
<str
name="collectionsHandler">org.apache.solr.handler.admin.MirroringCollectionsHandler</str>
+ <str
name="configSetsHandler">org.apache.solr.handler.admin.MirroringConfigSetsHandler</str>
<solrcloud>
<str name="host">${host:}</str>
<int name="hostPort">${hostPort:0}</int>