blob: bea5d0c1104386ddcd95c70a72d3395158b7fa45 [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.pkg;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.lucene.analysis.core.WhitespaceTokenizerFactory;
import org.apache.lucene.analysis.pattern.PatternReplaceCharFilterFactory;
import org.apache.lucene.util.ResourceLoader;
import org.apache.lucene.util.ResourceLoaderAware;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.embedded.JettySolrRunner;
import org.apache.solr.client.solrj.impl.BaseHttpSolrClient;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.request.GenericSolrRequest;
import org.apache.solr.client.solrj.request.RequestWriter;
import org.apache.solr.client.solrj.request.UpdateRequest;
import org.apache.solr.client.solrj.request.V2Request;
import org.apache.solr.client.solrj.request.beans.Package;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.util.ClientUtils;
import org.apache.solr.cloud.MiniSolrCloudCluster;
import org.apache.solr.cloud.SolrCloudTestCase;
import org.apache.solr.common.MapWriterMap;
import org.apache.solr.common.NavigableObject;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.annotation.JsonProperty;
import org.apache.solr.common.params.MapSolrParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.ReflectMapWriter;
import org.apache.solr.common.util.Utils;
import org.apache.solr.core.SolrCore;
import org.apache.solr.filestore.PackageStoreAPI;
import org.apache.solr.filestore.TestDistribPackageStore;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.search.QParser;
import org.apache.solr.search.QParserPlugin;
import org.apache.solr.util.LogLevel;
import org.apache.solr.util.SimplePostTool;
import org.apache.solr.util.plugin.SolrCoreAware;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.apache.solr.common.cloud.ZkStateReader.SOLR_PKGS_PATH;
import static org.apache.solr.common.params.CommonParams.JAVABIN;
import static org.apache.solr.common.params.CommonParams.WT;
import static org.apache.solr.core.TestSolrConfigHandler.getFileContent;
import static org.apache.solr.filestore.TestDistribPackageStore.checkAllNodesForFile;
import static org.apache.solr.filestore.TestDistribPackageStore.readFile;
import static org.apache.solr.filestore.TestDistribPackageStore.uploadKey;
@LogLevel("org.apache.solr.pkg.PackageLoader=DEBUG;org.apache.solr.pkg.PackageAPI=DEBUG")
public class TestPackages extends SolrCloudTestCase {
@Before
public void setup() {
System.setProperty("enable.packages", "true");
}
@After
public void teardown() {
System.clearProperty("enable.packages");
}
public static class ConfigPlugin implements ReflectMapWriter {
@JsonProperty
public String name;
@JsonProperty("class")
public String klass;
}
@Test
@SuppressWarnings({"unchecked"})
public void testPluginLoading() throws Exception {
MiniSolrCloudCluster cluster =
configureCluster(4)
.withJettyConfig(jetty -> jetty.enableV2(true))
.addConfig("conf", configset("cloud-minimal"))
.configure();
try {
String FILE1 = "/mypkg/runtimelibs.jar";
String FILE2 = "/mypkg/runtimelibs_v2.jar";
String FILE3 = "/mypkg/runtimelibs_v3.jar";
String URP1 = "/mypkg/testurpv1.jar";
String URP2 = "/mypkg/testurpv2.jar";
String EXPR1 = "/mypkg/expressible.jar";
String COLLECTION_NAME = "testPluginLoadingColl";
byte[] derFile = readFile("cryptokeys/pub_key512.der");
uploadKey(derFile, PackageStoreAPI.KEYS_DIR+"/pub_key512.der", cluster);
postFileAndWait(cluster, "runtimecode/runtimelibs.jar.bin", FILE1,
"L3q/qIGs4NaF6JiO0ZkMUFa88j0OmYc+I6O7BOdNuMct/xoZ4h73aZHZGc0+nmI1f/U3bOlMPINlSOM6LK3JpQ==");
postFileAndWait(cluster, "runtimecode/testurp_v1.jar.bin", URP1,
"h6UmMzuPqu4hQFGLBMJh/6kDSEXpJlgLsQDXx0KuxXWkV5giilRP57K3towiJRh2J+rqihqIghNCi3YgzgUnWQ==");
postFileAndWait(cluster, "runtimecode/expressible.jar.bin", EXPR1,
"ZOT11arAiPmPZYOHzqodiNnxO9pRyRozWZEBX8XGjU1/HJptFnZK+DI7eXnUtbNaMcbXE2Ze8hh4M/eGyhY8BQ==");
Package.AddVersion add = new Package.AddVersion();
add.version = "1.0";
add.pkg = "mypkg";
add.files = Arrays.asList(new String[]{FILE1, URP1, EXPR1});
V2Request req = new V2Request.Builder("/cluster/package")
.forceV2(true)
.withMethod(SolrRequest.METHOD.POST)
.withPayload(Collections.singletonMap("add", add))
.build();
req.process(cluster.getSolrClient());
CollectionAdminRequest
.createCollection(COLLECTION_NAME, "conf", 2, 2)
.process(cluster.getSolrClient());
cluster.waitForActiveCollection(COLLECTION_NAME, 2, 4);
TestDistribPackageStore.assertResponseValues(10,
() -> new V2Request.Builder("/cluster/package").
withMethod(SolrRequest.METHOD.GET)
.build().process(cluster.getSolrClient()),
Utils.makeMap(
":result:packages:mypkg[0]:version", "1.0",
":result:packages:mypkg[0]:files[0]", FILE1
));
Map<String,ConfigPlugin> plugins = new LinkedHashMap<>();
ConfigPlugin p = new ConfigPlugin();
p.klass = "mypkg:org.apache.solr.core.RuntimeLibReqHandler";
p.name = "/runtime";
plugins.put("create-requesthandler", p);
p = new ConfigPlugin();
p.klass = "mypkg:org.apache.solr.core.RuntimeLibSearchComponent";
p.name = "get";
plugins.put("create-searchcomponent", p);
p = new ConfigPlugin();
p.klass = "mypkg:org.apache.solr.core.RuntimeLibResponseWriter";
p.name = "json1";
plugins.put("create-queryResponseWriter", p);
p = new ConfigPlugin();
p.klass = "mypkg:org.apache.solr.update.TestVersionedURP";
p.name = "myurp";
plugins.put("create-updateProcessor", p);
p = new ConfigPlugin();
p.klass = "mypkg:org.apache.solr.client.solrj.io.stream.metrics.MinCopyMetric";
p.name = "mincopy";
plugins.put("create-expressible", p);
V2Request v2r = new V2Request.Builder( "/c/"+COLLECTION_NAME+ "/config")
.withMethod(SolrRequest.METHOD.POST)
.withPayload(plugins)
.forceV2(true)
.build();
cluster.getSolrClient().request(v2r);
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "queryResponseWriter", "json1",
"mypkg", "1.0" );
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "searchComponent", "get",
"mypkg", "1.0" );
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "requestHandler", "/runtime",
"mypkg", "1.0" );
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "updateProcessor", "myurp",
"mypkg", "1.0" );
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "expressible", "mincopy",
"mypkg", "1.0" );
TestDistribPackageStore.assertResponseValues(10,
cluster.getSolrClient() ,
new GenericSolrRequest(SolrRequest.METHOD.GET,
"/stream", new MapSolrParams((Map) Utils.makeMap("collection", COLLECTION_NAME,
WT, JAVABIN,
"action", "plugins"
))), Utils.makeMap(
":plugins:mincopy", "org.apache.solr.client.solrj.io.stream.metrics.MinCopyMetric"
));
UpdateRequest ur = new UpdateRequest();
ur.add(new SolrInputDocument("id", "1"));
ur.setParam("processor", "myurp");
ur.process(cluster.getSolrClient(), COLLECTION_NAME);
cluster.getSolrClient().commit(COLLECTION_NAME, true, true);
QueryResponse result = cluster.getSolrClient()
.query(COLLECTION_NAME, new SolrQuery( "id:1"));
assertEquals("Version 1", result.getResults().get(0).getFieldValue("TestVersionedURP.Ver_s"));
executeReq( "/" + COLLECTION_NAME + "/runtime?wt=javabin", cluster.getRandomJetty(random()),
Utils.JAVABINCONSUMER,
Utils.makeMap("class", "org.apache.solr.core.RuntimeLibReqHandler"));
executeReq( "/" + COLLECTION_NAME + "/get?wt=json", cluster.getRandomJetty(random()),
Utils.JSONCONSUMER,
Utils.makeMap("class", "org.apache.solr.core.RuntimeLibSearchComponent",
"Version","1"));
executeReq( "/" + COLLECTION_NAME + "/runtime?wt=json1", cluster.getRandomJetty(random()),
Utils.JSONCONSUMER,
Utils.makeMap("wt", "org.apache.solr.core.RuntimeLibResponseWriter"));
//now upload the second jar
postFileAndWait(cluster, "runtimecode/runtimelibs_v2.jar.bin", FILE2,
"j+Rflxi64tXdqosIhbusqi6GTwZq8znunC/dzwcWW0/dHlFGKDurOaE1Nz9FSPJuXbHkVLj638yZ0Lp1ssnoYA==");
postFileAndWait(cluster, "runtimecode/testurp_v2.jar.bin", URP2,
"P/ptFXRvQMd4oKPvadSpd+A9ffwY3gcex5GVFVRy3df0/OF8XT5my8rQz7FZva+2ORbWxdXS8NKwNrbPVHLGXw==");
//add the version using package API
add.version = "1.1";
add.files = Arrays.asList(new String[]{FILE2,URP2, EXPR1});
req.process(cluster.getSolrClient());
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "queryResponseWriter", "json1",
"mypkg", "1.1" );
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "searchComponent", "get",
"mypkg", "1.1" );
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "requestHandler", "/runtime",
"mypkg", "1.1" );
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "updateProcessor", "myurp",
"mypkg", "1.1" );
executeReq( "/" + COLLECTION_NAME + "/get?wt=json", cluster.getRandomJetty(random()),
Utils.JSONCONSUMER,
Utils.makeMap( "Version","2"));
//now upload the third jar
postFileAndWait(cluster, "runtimecode/runtimelibs_v3.jar.bin", FILE3,
"a400n4T7FT+2gM0SC6+MfSOExjud8MkhTSFylhvwNjtWwUgKdPFn434Wv7Qc4QEqDVLhQoL3WqYtQmLPti0G4Q==");
add.version = "2.1";
add.files = Arrays.asList(new String[]{FILE3, URP2, EXPR1});
req.process(cluster.getSolrClient());
//now let's verify that the classes are updated
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "queryResponseWriter", "json1",
"mypkg", "2.1" );
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "searchComponent", "get",
"mypkg", "2.1" );
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "requestHandler", "/runtime",
"mypkg", "2.1" );
executeReq( "/" + COLLECTION_NAME + "/runtime?wt=json", cluster.getRandomJetty(random()),
Utils.JSONCONSUMER,
Utils.makeMap("Version","2"));
//insert a doc with urp
ur = new UpdateRequest();
ur.add(new SolrInputDocument("id", "2"));
ur.setParam("processor", "myurp");
ur.process(cluster.getSolrClient(), COLLECTION_NAME);
cluster.getSolrClient().commit(COLLECTION_NAME, true, true);
result = cluster.getSolrClient()
.query(COLLECTION_NAME, new SolrQuery( "id:2"));
assertEquals("Version 2", result.getResults().get(0).getFieldValue("TestVersionedURP.Ver_s"));
Package.DelVersion delVersion = new Package.DelVersion();
delVersion.pkg = "mypkg";
delVersion.version = "1.0";
V2Request delete = new V2Request.Builder("/cluster/package")
.withMethod(SolrRequest.METHOD.POST)
.forceV2(true)
.withPayload(Collections.singletonMap("delete", delVersion))
.build();
delete.process(cluster.getSolrClient());
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "queryResponseWriter", "json1",
"mypkg", "2.1" );
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "searchComponent", "get",
"mypkg", "2.1" );
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "requestHandler", "/runtime",
"mypkg", "2.1" );
// now remove the hughest version. So, it will roll back to the next highest one
delVersion.version = "2.1";
delete.process(cluster.getSolrClient());
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "queryResponseWriter", "json1",
"mypkg", "1.1" );
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "searchComponent", "get",
"mypkg", "1.1" );
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "requestHandler", "/runtime",
"mypkg", "1.1" );
ModifiableSolrParams params = new ModifiableSolrParams();
params.add("collection", COLLECTION_NAME);
new GenericSolrRequest(SolrRequest.METHOD.POST, "/config/params", params ){
@Override
public RequestWriter.ContentWriter getContentWriter(String expectedType) {
return new RequestWriter.StringPayloadContentWriter("{set:{PKG_VERSIONS:{mypkg : '1.1'}}}",
ClientUtils.TEXT_JSON);
}
}.process(cluster.getSolrClient()) ;
add.version = "2.1";
add.files = Arrays.asList(new String[]{FILE3, URP2, EXPR1});
req.process(cluster.getSolrClient());
//the collections mypkg is set to use version 1.1
//so no upgrade
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "queryResponseWriter", "json1",
"mypkg", "1.1" );
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "searchComponent", "get",
"mypkg", "1.1" );
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "requestHandler", "/runtime",
"mypkg", "1.1" );
new GenericSolrRequest(SolrRequest.METHOD.POST, "/config/params", params ){
@Override
public RequestWriter.ContentWriter getContentWriter(String expectedType) {
return new RequestWriter.StringPayloadContentWriter("{set:{PKG_VERSIONS:{mypkg : '2.1'}}}",
ClientUtils.TEXT_JSON);
}
}.process(cluster.getSolrClient()) ;
//now, let's force every collection using 'mypkg' to refresh
//so that it uses version 2.1
new V2Request.Builder("/cluster/package")
.withMethod(SolrRequest.METHOD.POST)
.withPayload("{refresh : mypkg}")
.forceV2(true)
.build()
.process(cluster.getSolrClient());
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "queryResponseWriter", "json1",
"mypkg", "2.1" );
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "searchComponent", "get",
"mypkg", "2.1" );
verifyComponent(cluster.getSolrClient(),
COLLECTION_NAME, "requestHandler", "/runtime",
"mypkg", "2.1" );
plugins.clear();
p = new ConfigPlugin();
p.name = "/rt_2";
p.klass = "mypkg:"+ C.class.getName();
plugins.put("create-requesthandler", p);
p = new ConfigPlugin();
p.name = "qp1";
p.klass = "mypkg:"+ C2.class.getName();
plugins.put("create-queryparser", p);
v2r = new V2Request.Builder( "/c/"+COLLECTION_NAME+ "/config")
.withMethod(SolrRequest.METHOD.POST)
.withPayload(plugins)
.forceV2(true)
.build();
cluster.getSolrClient().request(v2r);
assertTrue(C.informCalled);
assertTrue(C2.informCalled);
//we create a new node. This node does not have the packages. But it should download it from another node
JettySolrRunner jetty = cluster.startJettySolrRunner();
//create a new replica for this collection. it should end up
CollectionAdminRequest.addReplicaToShard(COLLECTION_NAME, "shard1")
.setNrtReplicas(1)
.setNode(jetty.getNodeName())
.process(cluster.getSolrClient());
cluster.waitForActiveCollection(COLLECTION_NAME, 2, 5);
checkAllNodesForFile(cluster,FILE3,
Utils.makeMap(":files:" + FILE3 + ":name", "runtimelibs_v3.jar"),
false);
} finally {
cluster.shutdown();
}
}
@SuppressWarnings({"unchecked","rawtypes"})
private void executeReq(String uri, JettySolrRunner jetty, Utils.InputStreamConsumer parser, Map expected) throws Exception {
try(HttpSolrClient client = (HttpSolrClient) jetty.newClient()){
TestDistribPackageStore.assertResponseValues(10,
() -> {
Object o = Utils.executeGET(client.getHttpClient(),
jetty.getBaseUrl() + uri, parser);
if(o instanceof NavigableObject) return (NavigableObject) o;
if(o instanceof Map) return new MapWriterMap((Map) o);
throw new RuntimeException("Unknown response");
}, expected);
}
}
private void verifyComponent(SolrClient client, String COLLECTION_NAME,
String componentType, String componentName, String pkg, String version) throws Exception {
@SuppressWarnings({"unchecked"})
SolrParams params = new MapSolrParams((Map) Utils.makeMap("collection", COLLECTION_NAME,
WT, JAVABIN,
"componentName", componentName,
"meta", "true"));
GenericSolrRequest req1 = new GenericSolrRequest(SolrRequest.METHOD.GET,
"/config/" + componentType, params);
TestDistribPackageStore.assertResponseValues(10,
client,
req1, Utils.makeMap(
":config:" + componentType + ":" + componentName + ":_packageinfo_:package", pkg,
":config:" + componentType + ":" + componentName + ":_packageinfo_:version", version
));
}
@Test
public void testAPI() throws Exception {
System.setProperty("enable.packages", "true");
MiniSolrCloudCluster cluster =
configureCluster(4)
.withJettyConfig(jetty -> jetty.enableV2(true))
.addConfig("conf", configset("cloud-minimal"))
.configure();
try {
String errPath = "/error/details[0]/errorMessages[0]";
String FILE1 = "/mypkg/v.0.12/jar_a.jar";
String FILE2 = "/mypkg/v.0.12/jar_b.jar";
String FILE3 = "/mypkg/v.0.13/jar_a.jar";
Package.AddVersion add = new Package.AddVersion();
add.version = "0.12";
add.pkg = "test_pkg";
add.files = Arrays.asList(new String[]{FILE1, FILE2});
V2Request req = new V2Request.Builder("/cluster/package")
.forceV2(true)
.withMethod(SolrRequest.METHOD.POST)
.withPayload(Collections.singletonMap("add", add))
.build();
//the files is not yet there. The command should fail with error saying "No such file"
expectError(req, cluster.getSolrClient(), errPath, "No such file:");
//post the jar file. No signature is sent
postFileAndWait(cluster, "runtimecode/runtimelibs.jar.bin", FILE1, null);
add.files = Arrays.asList(new String[]{FILE1});
expectError(req, cluster.getSolrClient(), errPath,
FILE1 + " has no signature");
//now we upload the keys
byte[] derFile = readFile("cryptokeys/pub_key512.der");
uploadKey(derFile, PackageStoreAPI.KEYS_DIR+"/pub_key512.der", cluster);
//and upload the same file with a different name but it has proper signature
postFileAndWait(cluster, "runtimecode/runtimelibs.jar.bin", FILE2,
"L3q/qIGs4NaF6JiO0ZkMUFa88j0OmYc+I6O7BOdNuMct/xoZ4h73aZHZGc0+nmI1f/U3bOlMPINlSOM6LK3JpQ==");
// with correct signature
//after uploading the file, let's delete the keys to see if we get proper error message
add.files = Arrays.asList(new String[]{FILE2});
/*expectError(req, cluster.getSolrClient(), errPath,
"ZooKeeper does not have any public keys");*/
//Now lets' put the keys back
//this time we have a file with proper signature, public keys are in ZK
// so the add {} command should succeed
req.process(cluster.getSolrClient());
//Now verify the data in ZK
TestDistribPackageStore.assertResponseValues(1,
() -> new MapWriterMap((Map) Utils.fromJSON(cluster.getZkClient().getData(SOLR_PKGS_PATH,
null, new Stat(), true))),
Utils.makeMap(
":packages:test_pkg[0]:version", "0.12",
":packages:test_pkg[0]:files[0]", FILE1
));
//post a new jar with a proper signature
postFileAndWait(cluster, "runtimecode/runtimelibs_v2.jar.bin", FILE3,
"j+Rflxi64tXdqosIhbusqi6GTwZq8znunC/dzwcWW0/dHlFGKDurOaE1Nz9FSPJuXbHkVLj638yZ0Lp1ssnoYA==");
//this time we are adding the second version of the package (0.13)
add.version = "0.13";
add.pkg = "test_pkg";
add.files = Arrays.asList(new String[]{FILE3});
//this request should succeed
req.process(cluster.getSolrClient());
//no verify the data (/packages.json) in ZK
TestDistribPackageStore.assertResponseValues(1,
() -> new MapWriterMap((Map) Utils.fromJSON(cluster.getZkClient().getData(SOLR_PKGS_PATH,
null, new Stat(), true))),
Utils.makeMap(
":packages:test_pkg[1]:version", "0.13",
":packages:test_pkg[1]:files[0]", FILE3
));
//Now we will just delete one version
Package.DelVersion delVersion = new Package.DelVersion();
delVersion.version = "0.1";//this version does not exist
delVersion.pkg = "test_pkg";
req = new V2Request.Builder("/cluster/package")
.forceV2(true)
.withMethod(SolrRequest.METHOD.POST)
.withPayload(Collections.singletonMap("delete", delVersion))
.build();
//we are expecting an error
expectError(req, cluster.getSolrClient(), errPath, "No such version:");
delVersion.version = "0.12";//correct version. Should succeed
req.process(cluster.getSolrClient());
//Verify with ZK that the data is correct
TestDistribPackageStore.assertResponseValues(1,
() -> new MapWriterMap((Map) Utils.fromJSON(cluster.getZkClient().getData(SOLR_PKGS_PATH,
null, new Stat(), true))),
Utils.makeMap(
":packages:test_pkg[0]:version", "0.13",
":packages:test_pkg[0]:files[0]", FILE2
));
//So far we have been verifying the details with ZK directly
//use the package read API to verify with each node that it has the correct data
for (JettySolrRunner jetty : cluster.getJettySolrRunners()) {
String path = jetty.getBaseUrl().toString().replace("/solr", "/api") + "/cluster/package?wt=javabin";
TestDistribPackageStore.assertResponseValues(10, new Callable<NavigableObject>() {
@Override
public NavigableObject call() throws Exception {
try (HttpSolrClient solrClient = (HttpSolrClient) jetty.newClient()) {
return (NavigableObject) Utils.executeGET(solrClient.getHttpClient(), path, Utils.JAVABINCONSUMER);
}
}
}, Utils.makeMap(
":result:packages:test_pkg[0]:version", "0.13",
":result:packages:test_pkg[0]:files[0]", FILE3
));
}
} finally {
cluster.shutdown();
}
}
public static class C extends RequestHandlerBase implements SolrCoreAware {
static boolean informCalled = false;
@Override
public void inform(SolrCore core) {
informCalled = true;
}
@Override
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) {
}
@Override
public String getDescription() {
return "test";
}
}
public static class C2 extends QParserPlugin implements ResourceLoaderAware {
static boolean informCalled = false;
@Override
public void inform(ResourceLoader loader) throws IOException {
informCalled = true;
}
@Override
public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
return null;
}
}
public void testSchemaPlugins() throws Exception {
String COLLECTION_NAME = "testSchemaLoadingColl";
System.setProperty("managed.schema.mutable", "true");
MiniSolrCloudCluster cluster =
configureCluster(4)
.withJettyConfig(jetty -> jetty.enableV2(true))
.addConfig("conf1", configset("schema-package"))
.configure();
try {
String FILE1 = "/schemapkg/schema-plugins.jar";
byte[] derFile = readFile("cryptokeys/pub_key512.der");
uploadKey(derFile, PackageStoreAPI.KEYS_DIR+"/pub_key512.der", cluster);
postFileAndWait(cluster, "runtimecode/schema-plugins.jar.bin", FILE1,
"U+AdO/jgY3DtMpeFRGoTQk72iA5g/qjPvdQYPGBaXB5+ggcTZk4FoIWiueB0bwGJ8Mg3V/elxOqEbD2JR8R0tA==");
String FILE2 = "/schemapkg/payload-component.jar";
postFileAndWait(cluster, "runtimecode/payload-component.jar.bin", FILE2,
"gI6vYUDmSXSXmpNEeK1cwqrp4qTeVQgizGQkd8A4Prx2K8k7c5QlXbcs4lxFAAbbdXz9F4esBqTCiLMjVDHJ5Q==");
Package.AddVersion add = new Package.AddVersion();
add.version = "1.0";
add.pkg = "schemapkg";
add.files = Arrays.asList(FILE1,FILE2);
V2Request req = new V2Request.Builder("/cluster/package")
.forceV2(true)
.withMethod(SolrRequest.METHOD.POST)
.withPayload(Collections.singletonMap("add", add))
.build();
req.process(cluster.getSolrClient());
TestDistribPackageStore.assertResponseValues(10,
() -> new V2Request.Builder("/cluster/package").
withMethod(SolrRequest.METHOD.GET)
.build().process(cluster.getSolrClient()),
Utils.makeMap(
":result:packages:schemapkg[0]:version", "1.0",
":result:packages:schemapkg[0]:files[0]", FILE1
));
CollectionAdminRequest
.createCollection(COLLECTION_NAME, "conf1", 2, 2)
.process(cluster.getSolrClient());
cluster.waitForActiveCollection(COLLECTION_NAME, 2, 4);
verifySchemaComponent(cluster.getSolrClient(), COLLECTION_NAME, "/schema/fieldtypes/myNewTextFieldWithAnalyzerClass",
Utils.makeMap(":fieldType:analyzer:charFilters[0]:_packageinfo_:version" ,"1.0",
":fieldType:analyzer:tokenizer:_packageinfo_:version","1.0",
":fieldType:_packageinfo_:version","1.0"));
add = new Package.AddVersion();
add.version = "2.0";
add.pkg = "schemapkg";
add.files = Arrays.asList(FILE1);
req = new V2Request.Builder("/cluster/package")
.forceV2(true)
.withMethod(SolrRequest.METHOD.POST)
.withPayload(Collections.singletonMap("add", add))
.build();
req.process(cluster.getSolrClient());
TestDistribPackageStore.assertResponseValues(10,
() -> new V2Request.Builder("/cluster/package").
withMethod(SolrRequest.METHOD.GET)
.build().process(cluster.getSolrClient()),
Utils.makeMap(
":result:packages:schemapkg[0]:version", "2.0",
":result:packages:schemapkg[0]:files[0]", FILE1
));
verifySchemaComponent(cluster.getSolrClient(), COLLECTION_NAME, "/schema/fieldtypes/myNewTextFieldWithAnalyzerClass",
Utils.makeMap(":fieldType:analyzer:charFilters[0]:_packageinfo_:version" ,"2.0",
":fieldType:analyzer:tokenizer:_packageinfo_:version","2.0",
":fieldType:_packageinfo_:version","2.0"));
} finally {
cluster.shutdown();
}
}
@SuppressWarnings({"rawtypes","unchecked"})
private void verifySchemaComponent(SolrClient client, String COLLECTION_NAME, String path,
Map expected) throws Exception {
SolrParams params = new MapSolrParams((Map) Utils.makeMap("collection", COLLECTION_NAME,
WT, JAVABIN,
"meta", "true"));
GenericSolrRequest req = new GenericSolrRequest(SolrRequest.METHOD.GET,path
, params);
TestDistribPackageStore.assertResponseValues(10,
client,
req, expected);
}
public static void postFileAndWait(MiniSolrCloudCluster cluster, String fname, String path, String sig) throws Exception {
ByteBuffer fileContent = getFileContent(fname);
String sha512 = DigestUtils.sha512Hex(fileContent.array());
TestDistribPackageStore.postFile(cluster.getSolrClient(),
fileContent,
path, sig);// has file, but no signature
TestDistribPackageStore.checkAllNodesForFile(cluster, path, Utils.makeMap(
":files:" + path + ":sha512",
sha512
), false);
}
private void expectError(V2Request req, SolrClient client, String errPath, String expectErrorMsg) throws IOException, SolrServerException {
try {
req.process(client);
fail("should have failed with message : " + expectErrorMsg);
} catch (BaseHttpSolrClient.RemoteExecutionException e) {
String msg = e.getMetaData()._getStr(errPath, "");
assertTrue("should have failed with message: " + expectErrorMsg + "actual message : " + msg,
msg.contains(expectErrorMsg)
);
}
}
public static class BasePatternReplaceCharFilterFactory extends PatternReplaceCharFilterFactory {
public BasePatternReplaceCharFilterFactory(Map<String, String> args) {
super(args);
}
}
public static class BaseWhitespaceTokenizerFactory extends WhitespaceTokenizerFactory {
public BaseWhitespaceTokenizerFactory(Map<String, String> args) {
super(args);
}
}
/*
//copy the jav files to a package and then run the main method
public static void main(String[] args) throws Exception {
persistZip("/tmp/x.jar", MyPatternReplaceCharFilterFactory.class, MyTextField.class, MyWhitespaceTokenizerFactory.class);
}*/
public static ByteBuffer persistZip(String loc,
@SuppressWarnings({"rawtypes"}) Class... classes) throws IOException {
ByteBuffer jar = generateZip(classes);
try (FileOutputStream fos = new FileOutputStream(loc)) {
fos.write(jar.array(), 0, jar.limit());
fos.flush();
}
return jar;
}
public static ByteBuffer generateZip(@SuppressWarnings({"rawtypes"}) Class... classes) throws IOException {
SimplePostTool.BAOS bos = new SimplePostTool.BAOS();
try (ZipOutputStream zipOut = new ZipOutputStream(bos)) {
zipOut.setLevel(ZipOutputStream.DEFLATED);
for (@SuppressWarnings({"rawtypes"}) Class c : classes) {
String path = c.getName().replace('.', '/').concat(".class");
ZipEntry entry = new ZipEntry(path);
ByteBuffer b = SimplePostTool.inputStreamToByteArray(c.getClassLoader().getResourceAsStream(path));
zipOut.putNextEntry(entry);
zipOut.write(b.array(), 0, b.limit());
zipOut.closeEntry();
}
}
return bos.getByteBuffer();
}
}