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>

Reply via email to