mirror of https://github.com/apache/lucene.git
SOLR-6736: Adding support for uploading zipped configsets using ConfigSets API
This commit is contained in:
parent
1d550b8b35
commit
f3379a4cf3
|
@ -129,6 +129,9 @@ New Features
|
||||||
|
|
||||||
* SOLR-10076: Hide keystore and truststore passwords from /admin/info/* outputs. (Mano Kovacs via Mark Miller)
|
* SOLR-10076: Hide keystore and truststore passwords from /admin/info/* outputs. (Mano Kovacs via Mark Miller)
|
||||||
|
|
||||||
|
* SOLR-6736: Adding support for uploading zipped configsets using ConfigSets API (Varun Rajput, Ishan Chattopadhyaya,
|
||||||
|
Noble Paul, Anshum Gupta, Gregory Chanan)
|
||||||
|
|
||||||
Optimizations
|
Optimizations
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
package org.apache.solr.handler.dataimport;
|
package org.apache.solr.handler.dataimport;
|
||||||
|
|
||||||
import org.apache.solr.common.SolrException;
|
import org.apache.solr.common.SolrException;
|
||||||
|
import org.apache.solr.common.SolrException.ErrorCode;
|
||||||
|
import org.apache.solr.core.SolrCore;
|
||||||
import org.apache.solr.handler.dataimport.config.ConfigNameConstants;
|
import org.apache.solr.handler.dataimport.config.ConfigNameConstants;
|
||||||
import org.apache.solr.handler.dataimport.config.Entity;
|
import org.apache.solr.handler.dataimport.config.Entity;
|
||||||
|
|
||||||
|
@ -98,6 +100,9 @@ public class EntityProcessorWrapper extends EntityProcessor {
|
||||||
for (String aTransArr : transArr) {
|
for (String aTransArr : transArr) {
|
||||||
String trans = aTransArr.trim();
|
String trans = aTransArr.trim();
|
||||||
if (trans.startsWith("script:")) {
|
if (trans.startsWith("script:")) {
|
||||||
|
// The script transformer is a potential vulnerability, esp. when the script is
|
||||||
|
// provided from an untrusted source. Check and don't proceed if source is untrusted.
|
||||||
|
checkIfTrusted(trans);
|
||||||
String functionName = trans.substring("script:".length());
|
String functionName = trans.substring("script:".length());
|
||||||
ScriptTransformer scriptTransformer = new ScriptTransformer();
|
ScriptTransformer scriptTransformer = new ScriptTransformer();
|
||||||
scriptTransformer.setFunctionName(functionName);
|
scriptTransformer.setFunctionName(functionName);
|
||||||
|
@ -126,6 +131,24 @@ public class EntityProcessorWrapper extends EntityProcessor {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkIfTrusted(String trans) {
|
||||||
|
if (docBuilder != null) {
|
||||||
|
SolrCore core = docBuilder.dataImporter.getCore();
|
||||||
|
boolean trusted = (core != null)? core.getCoreDescriptor().isConfigSetTrusted(): true;
|
||||||
|
if (!trusted) {
|
||||||
|
Exception ex = new SolrException(ErrorCode.UNAUTHORIZED, "The configset for this collection was uploaded "
|
||||||
|
+ "without any authentication in place,"
|
||||||
|
+ " and this transformer is not available for collections with untrusted configsets. To use this transformer,"
|
||||||
|
+ " re-upload the configset after enabling authentication and authorization.");
|
||||||
|
String msg = "Transformer: "
|
||||||
|
+ trans
|
||||||
|
+ ". " + ex.getMessage();
|
||||||
|
log.error(msg);
|
||||||
|
wrapAndThrow(SEVERE, ex, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
static class ReflectionTransformer extends Transformer {
|
static class ReflectionTransformer extends Transformer {
|
||||||
final Method meth;
|
final Method meth;
|
||||||
|
|
|
@ -81,7 +81,7 @@ public class ZkSolrResourceLoader extends SolrResourceLoader {
|
||||||
@Override
|
@Override
|
||||||
public InputStream openResource(String resource) throws IOException {
|
public InputStream openResource(String resource) throws IOException {
|
||||||
InputStream is;
|
InputStream is;
|
||||||
String file = configSetZkPath + "/" + resource;
|
String file = (".".equals(resource)) ? configSetZkPath : configSetZkPath + "/" + resource;
|
||||||
int maxTries = 10;
|
int maxTries = 10;
|
||||||
Exception exception = null;
|
Exception exception = null;
|
||||||
while (maxTries -- > 0) {
|
while (maxTries -- > 0) {
|
||||||
|
|
|
@ -32,11 +32,15 @@ public class ConfigSet {
|
||||||
|
|
||||||
private final NamedList properties;
|
private final NamedList properties;
|
||||||
|
|
||||||
public ConfigSet(String name, SolrConfig solrConfig, IndexSchema indexSchema, NamedList properties) {
|
private final boolean trusted;
|
||||||
|
|
||||||
|
public ConfigSet(String name, SolrConfig solrConfig, IndexSchema indexSchema,
|
||||||
|
NamedList properties, boolean trusted) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.solrconfig = solrConfig;
|
this.solrconfig = solrConfig;
|
||||||
this.indexSchema = indexSchema;
|
this.indexSchema = indexSchema;
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
|
this.trusted = trusted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
@ -54,4 +58,8 @@ public class ConfigSet {
|
||||||
public NamedList getProperties() {
|
public NamedList getProperties() {
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isTrusted() {
|
||||||
|
return trusted;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import com.google.common.cache.Cache;
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
import org.apache.solr.cloud.CloudConfigSetService;
|
import org.apache.solr.cloud.CloudConfigSetService;
|
||||||
import org.apache.solr.cloud.ZkController;
|
import org.apache.solr.cloud.ZkController;
|
||||||
|
import org.apache.solr.cloud.ZkSolrResourceLoader;
|
||||||
import org.apache.solr.common.SolrException;
|
import org.apache.solr.common.SolrException;
|
||||||
import org.apache.solr.common.util.NamedList;
|
import org.apache.solr.common.util.NamedList;
|
||||||
import org.apache.solr.schema.IndexSchema;
|
import org.apache.solr.schema.IndexSchema;
|
||||||
|
@ -74,10 +75,22 @@ public abstract class ConfigSetService {
|
||||||
SolrResourceLoader coreLoader = createCoreResourceLoader(dcore);
|
SolrResourceLoader coreLoader = createCoreResourceLoader(dcore);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
// ConfigSet properties are loaded from ConfigSetProperties.DEFAULT_FILENAME file.
|
||||||
|
// ConfigSet flags are loaded from the metadata of the ZK node of the configset.
|
||||||
|
NamedList properties = createConfigSetProperties(dcore, coreLoader);
|
||||||
|
NamedList flags = getConfigSetFlags(dcore, coreLoader);
|
||||||
|
|
||||||
|
boolean trusted =
|
||||||
|
(coreLoader instanceof ZkSolrResourceLoader
|
||||||
|
&& flags != null
|
||||||
|
&& flags.get("trusted") != null
|
||||||
|
&& !flags.getBooleanArg("trusted")
|
||||||
|
) ? false: true;
|
||||||
|
|
||||||
SolrConfig solrConfig = createSolrConfig(dcore, coreLoader);
|
SolrConfig solrConfig = createSolrConfig(dcore, coreLoader);
|
||||||
IndexSchema schema = createIndexSchema(dcore, solrConfig);
|
IndexSchema schema = createIndexSchema(dcore, solrConfig);
|
||||||
NamedList properties = createConfigSetProperties(dcore, coreLoader);
|
return new ConfigSet(configName(dcore), solrConfig, schema, properties, trusted);
|
||||||
return new ConfigSet(configName(dcore), solrConfig, schema, properties);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
|
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
|
||||||
"Could not load conf for core " + dcore.getName() +
|
"Could not load conf for core " + dcore.getName() +
|
||||||
|
@ -116,6 +129,18 @@ public abstract class ConfigSetService {
|
||||||
return ConfigSetProperties.readFromResourceLoader(loader, cd.getConfigSetPropertiesName());
|
return ConfigSetProperties.readFromResourceLoader(loader, cd.getConfigSetPropertiesName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected NamedList getConfigSetFlags(CoreDescriptor cd, SolrResourceLoader loader) {
|
||||||
|
if (loader instanceof ZkSolrResourceLoader) {
|
||||||
|
try {
|
||||||
|
return ConfigSetProperties.readFromResourceLoader(loader, ".");
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a SolrResourceLoader for a core
|
* Create a SolrResourceLoader for a core
|
||||||
* @param cd the core's CoreDescriptor
|
* @param cd the core's CoreDescriptor
|
||||||
|
|
|
@ -951,7 +951,8 @@ public class CoreContainer {
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigSet coreConfig = coreConfigService.getConfig(dcore);
|
ConfigSet coreConfig = coreConfigService.getConfig(dcore);
|
||||||
log.info("Creating SolrCore '{}' using configuration from {}", dcore.getName(), coreConfig.getName());
|
dcore.setConfigSetTrusted(coreConfig.isTrusted());
|
||||||
|
log.info("Creating SolrCore '{}' using configuration from {}, trusted={}", dcore.getName(), coreConfig.getName(), dcore.isConfigSetTrusted());
|
||||||
try {
|
try {
|
||||||
core = new SolrCore(dcore, coreConfig);
|
core = new SolrCore(dcore, coreConfig);
|
||||||
} catch (SolrException e) {
|
} catch (SolrException e) {
|
||||||
|
|
|
@ -65,6 +65,14 @@ public class CoreDescriptor {
|
||||||
|
|
||||||
public static final String DEFAULT_EXTERNAL_PROPERTIES_FILE = "conf" + File.separator + "solrcore.properties";
|
public static final String DEFAULT_EXTERNAL_PROPERTIES_FILE = "conf" + File.separator + "solrcore.properties";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this core was configured using a configSet that was trusted.
|
||||||
|
* This helps in avoiding the loading of plugins that have potential
|
||||||
|
* vulnerabilities, when the configSet was not uploaded from a trusted
|
||||||
|
* user.
|
||||||
|
*/
|
||||||
|
private boolean trustedConfigSet = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the standard properties in persistable form
|
* Get the standard properties in persistable form
|
||||||
* @return the standard core properties in persistable form
|
* @return the standard core properties in persistable form
|
||||||
|
@ -170,6 +178,7 @@ public class CoreDescriptor {
|
||||||
this.coreProperties.setProperty(CORE_NAME, coreName);
|
this.coreProperties.setProperty(CORE_NAME, coreName);
|
||||||
this.originalCoreProperties.setProperty(CORE_NAME, coreName);
|
this.originalCoreProperties.setProperty(CORE_NAME, coreName);
|
||||||
this.substitutableProperties.setProperty(SOLR_CORE_PROP_PREFIX + CORE_NAME, coreName);
|
this.substitutableProperties.setProperty(SOLR_CORE_PROP_PREFIX + CORE_NAME, coreName);
|
||||||
|
this.trustedConfigSet = other.trustedConfigSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -372,4 +381,12 @@ public class CoreDescriptor {
|
||||||
public String getConfigSetPropertiesName() {
|
public String getConfigSetPropertiesName() {
|
||||||
return coreProperties.getProperty(CORE_CONFIGSET_PROPERTIES);
|
return coreProperties.getProperty(CORE_CONFIGSET_PROPERTIES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isConfigSetTrusted() {
|
||||||
|
return trustedConfigSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConfigSetTrusted(boolean trusted) {
|
||||||
|
this.trustedConfigSet = trusted;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,10 @@
|
||||||
package org.apache.solr.core;
|
package org.apache.solr.core;
|
||||||
|
|
||||||
import org.apache.solr.common.SolrException;
|
import org.apache.solr.common.SolrException;
|
||||||
|
import org.apache.solr.common.SolrException.ErrorCode;
|
||||||
import org.apache.solr.common.util.NamedList;
|
import org.apache.solr.common.util.NamedList;
|
||||||
import org.apache.solr.search.SolrIndexSearcher;
|
import org.apache.solr.search.SolrIndexSearcher;
|
||||||
|
import org.apache.solr.util.plugin.SolrCoreAware;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -30,7 +32,7 @@ import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
class RunExecutableListener extends AbstractSolrEventListener {
|
class RunExecutableListener extends AbstractSolrEventListener implements SolrCoreAware {
|
||||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||||
|
|
||||||
public RunExecutableListener(SolrCore core) {
|
public RunExecutableListener(SolrCore core) {
|
||||||
|
@ -66,6 +68,15 @@ class RunExecutableListener extends AbstractSolrEventListener {
|
||||||
if ("false".equals(args.get("wait")) || Boolean.FALSE.equals(args.get("wait"))) wait=false;
|
if ("false".equals(args.get("wait")) || Boolean.FALSE.equals(args.get("wait"))) wait=false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void inform(SolrCore core) {
|
||||||
|
if (!core.getCoreDescriptor().isConfigSetTrusted()) {
|
||||||
|
throw new SolrException(ErrorCode.UNAUTHORIZED, "The configset for this collection was uploaded without any authentication in place,"
|
||||||
|
+ " and this operation is not available for collections with untrusted configsets. To have this component, re-upload the configset"
|
||||||
|
+ " after enabling authentication and authorization.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* External executable listener.
|
* External executable listener.
|
||||||
*
|
*
|
||||||
|
|
|
@ -962,6 +962,7 @@ public final class SolrCore implements SolrInfoMBean, SolrMetricProducer, Closea
|
||||||
// Finally tell anyone who wants to know
|
// Finally tell anyone who wants to know
|
||||||
resourceLoader.inform(resourceLoader);
|
resourceLoader.inform(resourceLoader);
|
||||||
resourceLoader.inform(this); // last call before the latch is released.
|
resourceLoader.inform(this); // last call before the latch is released.
|
||||||
|
this.updateHandler.informEventListeners(this);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
// release the latch, otherwise we block trying to do the close. This
|
// release the latch, otherwise we block trying to do the close. This
|
||||||
// should be fine, since counting down on a latch of 0 is still fine
|
// should be fine, since counting down on a latch of 0 is still fine
|
||||||
|
|
|
@ -16,14 +16,20 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.solr.handler.admin;
|
package org.apache.solr.handler.admin;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.hadoop.fs.Path;
|
||||||
import org.apache.solr.api.Api;
|
import org.apache.solr.api.Api;
|
||||||
import org.apache.solr.client.solrj.SolrResponse;
|
import org.apache.solr.client.solrj.SolrResponse;
|
||||||
import org.apache.solr.cloud.OverseerSolrResponse;
|
import org.apache.solr.cloud.OverseerSolrResponse;
|
||||||
|
@ -36,6 +42,7 @@ import org.apache.solr.common.cloud.ZkNodeProps;
|
||||||
import org.apache.solr.common.params.ConfigSetParams;
|
import org.apache.solr.common.params.ConfigSetParams;
|
||||||
import org.apache.solr.common.params.ConfigSetParams.ConfigSetAction;
|
import org.apache.solr.common.params.ConfigSetParams.ConfigSetAction;
|
||||||
import org.apache.solr.common.params.SolrParams;
|
import org.apache.solr.common.params.SolrParams;
|
||||||
|
import org.apache.solr.common.util.ContentStream;
|
||||||
import org.apache.solr.common.util.NamedList;
|
import org.apache.solr.common.util.NamedList;
|
||||||
import org.apache.solr.common.util.SimpleOrderedMap;
|
import org.apache.solr.common.util.SimpleOrderedMap;
|
||||||
import org.apache.solr.common.util.Utils;
|
import org.apache.solr.common.util.Utils;
|
||||||
|
@ -43,6 +50,10 @@ import org.apache.solr.core.CoreContainer;
|
||||||
import org.apache.solr.handler.RequestHandlerBase;
|
import org.apache.solr.handler.RequestHandlerBase;
|
||||||
import org.apache.solr.request.SolrQueryRequest;
|
import org.apache.solr.request.SolrQueryRequest;
|
||||||
import org.apache.solr.response.SolrQueryResponse;
|
import org.apache.solr.response.SolrQueryResponse;
|
||||||
|
import org.apache.solr.security.AuthenticationPlugin;
|
||||||
|
import org.apache.solr.security.AuthorizationContext;
|
||||||
|
import org.apache.solr.security.PermissionNameProvider;
|
||||||
|
import org.apache.zookeeper.CreateMode;
|
||||||
import org.apache.zookeeper.KeeperException;
|
import org.apache.zookeeper.KeeperException;
|
||||||
import static org.apache.solr.cloud.OverseerConfigSetMessageHandler.BASE_CONFIGSET;
|
import static org.apache.solr.cloud.OverseerConfigSetMessageHandler.BASE_CONFIGSET;
|
||||||
import static org.apache.solr.cloud.OverseerConfigSetMessageHandler.CONFIGSETS_ACTION_PREFIX;
|
import static org.apache.solr.cloud.OverseerConfigSetMessageHandler.CONFIGSETS_ACTION_PREFIX;
|
||||||
|
@ -59,7 +70,7 @@ import static org.apache.solr.cloud.Overseer.QUEUE_OPERATION;
|
||||||
/**
|
/**
|
||||||
* A {@link org.apache.solr.request.SolrRequestHandler} for ConfigSets API requests.
|
* A {@link org.apache.solr.request.SolrRequestHandler} for ConfigSets API requests.
|
||||||
*/
|
*/
|
||||||
public class ConfigSetsHandler extends RequestHandlerBase {
|
public class ConfigSetsHandler extends RequestHandlerBase implements PermissionNameProvider {
|
||||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||||
protected final CoreContainer coreContainer;
|
protected final CoreContainer coreContainer;
|
||||||
public static long DEFAULT_ZK_TIMEOUT = 300*1000;
|
public static long DEFAULT_ZK_TIMEOUT = 300*1000;
|
||||||
|
@ -95,6 +106,10 @@ public class ConfigSetsHandler extends RequestHandlerBase {
|
||||||
ConfigSetAction action = ConfigSetAction.get(a);
|
ConfigSetAction action = ConfigSetAction.get(a);
|
||||||
if (action == null)
|
if (action == null)
|
||||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unknown action: " + a);
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unknown action: " + a);
|
||||||
|
if (action == ConfigSetAction.UPLOAD) {
|
||||||
|
handleConfigUploadRequest(req, rsp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
invokeAction(req, rsp, action);
|
invokeAction(req, rsp, action);
|
||||||
} else {
|
} else {
|
||||||
throw new SolrException(ErrorCode.BAD_REQUEST, "action is a required param");
|
throw new SolrException(ErrorCode.BAD_REQUEST, "action is a required param");
|
||||||
|
@ -121,6 +136,68 @@ public class ConfigSetsHandler extends RequestHandlerBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleConfigUploadRequest(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
|
||||||
|
String configSetName = req.getParams().get(NAME);
|
||||||
|
if (StringUtils.isBlank(configSetName)) {
|
||||||
|
throw new SolrException(ErrorCode.BAD_REQUEST,
|
||||||
|
"The configuration name should be provided in the \"name\" parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
SolrZkClient zkClient = coreContainer.getZkController().getZkClient();
|
||||||
|
String configPathInZk = ZkConfigManager.CONFIGS_ZKNODE + Path.SEPARATOR + configSetName;
|
||||||
|
|
||||||
|
if (zkClient.exists(configPathInZk, true)) {
|
||||||
|
throw new SolrException(ErrorCode.BAD_REQUEST,
|
||||||
|
"The configuration " + configSetName + " already exists in zookeeper");
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<ContentStream> contentStreamsIterator = req.getContentStreams().iterator();
|
||||||
|
|
||||||
|
if (!contentStreamsIterator.hasNext()) {
|
||||||
|
throw new SolrException(ErrorCode.BAD_REQUEST,
|
||||||
|
"No stream found for the config data to be uploaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream inputStream = contentStreamsIterator.next().getStream();
|
||||||
|
|
||||||
|
// Create a node for the configuration in zookeeper
|
||||||
|
boolean trusted = getTrusted(req);
|
||||||
|
zkClient.makePath(configPathInZk, ("{\"trusted\": " + Boolean.toString(trusted) + "}").
|
||||||
|
getBytes(StandardCharsets.UTF_8), true);
|
||||||
|
|
||||||
|
ZipInputStream zis = new ZipInputStream(inputStream, StandardCharsets.UTF_8);
|
||||||
|
ZipEntry zipEntry = null;
|
||||||
|
while ((zipEntry = zis.getNextEntry()) != null) {
|
||||||
|
String filePathInZk = configPathInZk + "/" + zipEntry.getName();
|
||||||
|
if (zipEntry.isDirectory()) {
|
||||||
|
zkClient.makePath(filePathInZk, true);
|
||||||
|
} else {
|
||||||
|
createZkNodeIfNotExistsAndSetData(zkClient, filePathInZk,
|
||||||
|
IOUtils.toByteArray(zis));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zis.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean getTrusted(SolrQueryRequest req) {
|
||||||
|
AuthenticationPlugin authcPlugin = coreContainer.getAuthenticationPlugin();
|
||||||
|
log.info("Trying to upload a configset. authcPlugin: {}, user principal: {}",
|
||||||
|
authcPlugin, req.getUserPrincipal());
|
||||||
|
if (authcPlugin != null && req.getUserPrincipal() != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createZkNodeIfNotExistsAndSetData(SolrZkClient zkClient,
|
||||||
|
String filePathInZk, byte[] data) throws Exception {
|
||||||
|
if (!zkClient.exists(filePathInZk, true)) {
|
||||||
|
zkClient.create(filePathInZk, data, CreateMode.PERSISTENT, true);
|
||||||
|
} else {
|
||||||
|
zkClient.setData(filePathInZk, data, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void handleResponse(String operation, ZkNodeProps m,
|
private void handleResponse(String operation, ZkNodeProps m,
|
||||||
SolrQueryResponse rsp, long timeout) throws KeeperException, InterruptedException {
|
SolrQueryResponse rsp, long timeout) throws KeeperException, InterruptedException {
|
||||||
long time = System.nanoTime();
|
long time = System.nanoTime();
|
||||||
|
@ -226,4 +303,16 @@ public class ConfigSetsHandler extends RequestHandlerBase {
|
||||||
public Boolean registerV2() {
|
public Boolean registerV2() {
|
||||||
return Boolean.TRUE;
|
return Boolean.TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Name getPermissionName(AuthorizationContext ctx) {
|
||||||
|
switch (ctx.getHttpMethod()) {
|
||||||
|
case "GET":
|
||||||
|
return Name.CONFIG_READ_PERM;
|
||||||
|
case "POST":
|
||||||
|
return Name.CONFIG_EDIT_PERM;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ import com.google.common.collect.Lists;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.solr.common.EmptyEntityResolver;
|
import org.apache.solr.common.EmptyEntityResolver;
|
||||||
import org.apache.solr.common.SolrException;
|
import org.apache.solr.common.SolrException;
|
||||||
|
import org.apache.solr.common.SolrException.ErrorCode;
|
||||||
import org.apache.solr.common.SolrInputDocument;
|
import org.apache.solr.common.SolrInputDocument;
|
||||||
import org.apache.solr.common.params.CommonParams;
|
import org.apache.solr.common.params.CommonParams;
|
||||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||||
|
@ -134,6 +135,12 @@ public class XMLLoader extends ContentStreamLoader {
|
||||||
|
|
||||||
String tr = req.getParams().get(CommonParams.TR,null);
|
String tr = req.getParams().get(CommonParams.TR,null);
|
||||||
if(tr!=null) {
|
if(tr!=null) {
|
||||||
|
if (req.getCore().getCoreDescriptor().isConfigSetTrusted() == false) {
|
||||||
|
throw new SolrException(ErrorCode.UNAUTHORIZED, "The configset for this collection was uploaded without any authentication in place,"
|
||||||
|
+ " and this operation is not available for collections with untrusted configsets. To use this feature, re-upload the configset"
|
||||||
|
+ " after enabling authentication and authorization.");
|
||||||
|
}
|
||||||
|
|
||||||
final Transformer t = getTransformer(tr,req);
|
final Transformer t = getTransformer(tr,req);
|
||||||
final DOMResult result = new DOMResult();
|
final DOMResult result = new DOMResult();
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ public interface PermissionNameProvider {
|
||||||
CORE_EDIT_PERM("core-admin-edit", null),
|
CORE_EDIT_PERM("core-admin-edit", null),
|
||||||
READ_PERM("read", "*"),
|
READ_PERM("read", "*"),
|
||||||
UPDATE_PERM("update", "*"),
|
UPDATE_PERM("update", "*"),
|
||||||
CONFIG_EDIT_PERM("config-edit", "*"),
|
CONFIG_EDIT_PERM("config-edit", unmodifiableSet(new HashSet<>(asList("*", null)))),
|
||||||
CONFIG_READ_PERM("config-read", "*"),
|
CONFIG_READ_PERM("config-read", "*"),
|
||||||
SCHEMA_READ_PERM("schema-read", "*"),
|
SCHEMA_READ_PERM("schema-read", "*"),
|
||||||
SCHEMA_EDIT_PERM("schema-edit", "*"),
|
SCHEMA_EDIT_PERM("schema-edit", "*"),
|
||||||
|
|
|
@ -72,6 +72,22 @@ public abstract class UpdateHandler implements SolrInfoMBean {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call the {@link SolrCoreAware#inform(SolrCore)} on all the applicable registered listeners.
|
||||||
|
*/
|
||||||
|
public void informEventListeners(SolrCore core) {
|
||||||
|
for (SolrEventListener listener: commitCallbacks) {
|
||||||
|
if (listener instanceof SolrCoreAware) {
|
||||||
|
((SolrCoreAware) listener).inform(core);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (SolrEventListener listener: optimizeCallbacks) {
|
||||||
|
if (listener instanceof SolrCoreAware) {
|
||||||
|
((SolrCoreAware) listener).inform(core);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void callPostCommitCallbacks() {
|
protected void callPostCommitCallbacks() {
|
||||||
for (SolrEventListener listener : commitCallbacks) {
|
for (SolrEventListener listener : commitCallbacks) {
|
||||||
listener.postCommit();
|
listener.postCommit();
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.apache.solr.update.processor;
|
package org.apache.solr.update.processor;
|
||||||
|
|
||||||
import org.apache.solr.common.SolrException;
|
import org.apache.solr.common.SolrException;
|
||||||
|
import org.apache.solr.common.SolrException.ErrorCode;
|
||||||
import org.apache.solr.common.util.NamedList;
|
import org.apache.solr.common.util.NamedList;
|
||||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||||
import org.apache.solr.core.SolrCore;
|
import org.apache.solr.core.SolrCore;
|
||||||
|
@ -217,6 +218,11 @@ public class StatelessScriptUpdateProcessorFactory extends UpdateRequestProcesso
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void inform(SolrCore core) {
|
public void inform(SolrCore core) {
|
||||||
|
if (!core.getCoreDescriptor().isConfigSetTrusted()) {
|
||||||
|
throw new SolrException(ErrorCode.UNAUTHORIZED, "The configset for this collection was uploaded without any authentication in place,"
|
||||||
|
+ " and this operation is not available for collections with untrusted configsets. To use this component, re-upload the configset"
|
||||||
|
+ " after enabling authentication and authorization.");
|
||||||
|
}
|
||||||
resourceLoader = core.getResourceLoader();
|
resourceLoader = core.getResourceLoader();
|
||||||
|
|
||||||
// test that our engines & scripts are valid
|
// test that our engines & scripts are valid
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<schema name="minimal" version="1.1">
|
||||||
|
<types>
|
||||||
|
<fieldType name="string" class="solr.StrField"/>
|
||||||
|
</types>
|
||||||
|
<fields>
|
||||||
|
<dynamicField name="*" type="string" indexed="true" stored="true" />
|
||||||
|
</fields>
|
||||||
|
</schema>
|
|
@ -0,0 +1,61 @@
|
||||||
|
<?xml version="1.0" ?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- This is a "kitchen sink" config file that tests can use.
|
||||||
|
When writting a new test, feel free to add *new* items (plugins,
|
||||||
|
config options, etc...) as long as they don't break any existing
|
||||||
|
tests. if you need to test something esoteric please add a new
|
||||||
|
"solrconfig-your-esoteric-purpose.xml" config file.
|
||||||
|
|
||||||
|
Note in particular that this test is used by MinimalSchemaTest so
|
||||||
|
Anything added to this file needs to work correctly even if there
|
||||||
|
is now uniqueKey or defaultSearch Field.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<config>
|
||||||
|
|
||||||
|
<dataDir>${solr.data.dir:}</dataDir>
|
||||||
|
|
||||||
|
<directoryFactory name="DirectoryFactory"
|
||||||
|
class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}"/>
|
||||||
|
|
||||||
|
<luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
|
||||||
|
|
||||||
|
<updateHandler class="solr.DirectUpdateHandler2">
|
||||||
|
<commitWithin>
|
||||||
|
<softCommit>${solr.commitwithin.softcommit:true}</softCommit>
|
||||||
|
</commitWithin>
|
||||||
|
|
||||||
|
</updateHandler>
|
||||||
|
<requestHandler name="/select" class="solr.SearchHandler">
|
||||||
|
<lst name="defaults">
|
||||||
|
<str name="echoParams">explicit</str>
|
||||||
|
<str name="indent">true</str>
|
||||||
|
<str name="df">text</str>
|
||||||
|
</lst>
|
||||||
|
|
||||||
|
</requestHandler>
|
||||||
|
|
||||||
|
<requestHandler name="/update/xslt"
|
||||||
|
startup="lazy"
|
||||||
|
class="solr.XsltUpdateRequestHandler"/>
|
||||||
|
|
||||||
|
<requestHandler name="/update" class="solr.UpdateRequestHandler" />
|
||||||
|
</config>
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<schema name="minimal" version="1.1">
|
||||||
|
<types>
|
||||||
|
<fieldType name="string" class="solr.StrField"/>
|
||||||
|
</types>
|
||||||
|
<fields>
|
||||||
|
<dynamicField name="*" type="string" indexed="true" stored="true" />
|
||||||
|
</fields>
|
||||||
|
</schema>
|
|
@ -0,0 +1,61 @@
|
||||||
|
<?xml version="1.0" ?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- This is a "kitchen sink" config file that tests can use.
|
||||||
|
When writting a new test, feel free to add *new* items (plugins,
|
||||||
|
config options, etc...) as long as they don't break any existing
|
||||||
|
tests. if you need to test something esoteric please add a new
|
||||||
|
"solrconfig-your-esoteric-purpose.xml" config file.
|
||||||
|
|
||||||
|
Note in particular that this test is used by MinimalSchemaTest so
|
||||||
|
Anything added to this file needs to work correctly even if there
|
||||||
|
is now uniqueKey or defaultSearch Field.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<config>
|
||||||
|
|
||||||
|
<dataDir>${solr.data.dir:}</dataDir>
|
||||||
|
|
||||||
|
<directoryFactory name="DirectoryFactory"
|
||||||
|
class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}"/>
|
||||||
|
|
||||||
|
<luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
|
||||||
|
|
||||||
|
<updateHandler class="solr.DirectUpdateHandler2">
|
||||||
|
<commitWithin>
|
||||||
|
<softCommit>${solr.commitwithin.softcommit:true}</softCommit>
|
||||||
|
</commitWithin>
|
||||||
|
|
||||||
|
</updateHandler>
|
||||||
|
<requestHandler name="/select" class="solr.SearchHandler">
|
||||||
|
<lst name="defaults">
|
||||||
|
<str name="echoParams">explicit</str>
|
||||||
|
<str name="indent">true</str>
|
||||||
|
<str name="df">text</str>
|
||||||
|
</lst>
|
||||||
|
|
||||||
|
</requestHandler>
|
||||||
|
|
||||||
|
<requestHandler name="/update/xslt"
|
||||||
|
startup="lazy"
|
||||||
|
class="solr.XsltUpdateRequestHandler"/>
|
||||||
|
|
||||||
|
<requestHandler name="/update" class="solr.UpdateRequestHandler" />
|
||||||
|
</config>
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
* 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
|
||||||
|
XSL transform used to test the XSLTUpdateRequestHandler.
|
||||||
|
Transforms a test XML into standard Solr <add><doc/></add> format.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
|
||||||
|
<xsl:template match="/">
|
||||||
|
<add>
|
||||||
|
<xsl:apply-templates select="/random/document"/>
|
||||||
|
</add>
|
||||||
|
</xsl:template>
|
||||||
|
|
||||||
|
<xsl:template match="document">
|
||||||
|
<doc boost="5.5">
|
||||||
|
<xsl:apply-templates select="*"/>
|
||||||
|
</doc>
|
||||||
|
</xsl:template>
|
||||||
|
|
||||||
|
<xsl:template match="node">
|
||||||
|
<field name="{@name}">
|
||||||
|
<xsl:if test="@enhance!=''">
|
||||||
|
<xsl:attribute name="boost"><xsl:value-of select="@enhance"/></xsl:attribute>
|
||||||
|
</xsl:if>
|
||||||
|
<xsl:value-of select="@value"/>
|
||||||
|
</field>
|
||||||
|
</xsl:template>
|
||||||
|
|
||||||
|
</xsl:stylesheet>
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<schema name="minimal" version="1.1">
|
||||||
|
<types>
|
||||||
|
<fieldType name="string" class="solr.StrField"/>
|
||||||
|
</types>
|
||||||
|
<fields>
|
||||||
|
<dynamicField name="*" type="string" indexed="true" stored="true" />
|
||||||
|
</fields>
|
||||||
|
</schema>
|
|
@ -0,0 +1,69 @@
|
||||||
|
<?xml version="1.0" ?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- This is a "kitchen sink" config file that tests can use.
|
||||||
|
When writting a new test, feel free to add *new* items (plugins,
|
||||||
|
config options, etc...) as long as they don't break any existing
|
||||||
|
tests. if you need to test something esoteric please add a new
|
||||||
|
"solrconfig-your-esoteric-purpose.xml" config file.
|
||||||
|
|
||||||
|
Note in particular that this test is used by MinimalSchemaTest so
|
||||||
|
Anything added to this file needs to work correctly even if there
|
||||||
|
is now uniqueKey or defaultSearch Field.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<config>
|
||||||
|
|
||||||
|
<dataDir>${solr.data.dir:}</dataDir>
|
||||||
|
|
||||||
|
<directoryFactory name="DirectoryFactory"
|
||||||
|
class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}"/>
|
||||||
|
|
||||||
|
<luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
|
||||||
|
|
||||||
|
<updateHandler class="solr.DirectUpdateHandler2">
|
||||||
|
<commitWithin>
|
||||||
|
<softCommit>${solr.commitwithin.softcommit:true}</softCommit>
|
||||||
|
</commitWithin>
|
||||||
|
|
||||||
|
<listener event="postCommit" class="solr.RunExecutableListener">
|
||||||
|
<str name="exe">/var/opt/resin3/__PORT__/scripts/solr/snapshooter</str>
|
||||||
|
<str name="dir">/var/opt/resin3/__PORT__</str>
|
||||||
|
<bool name="wait">true</bool>
|
||||||
|
<arr name="args"> <str>arg1</str> <str>arg2</str> </arr>
|
||||||
|
<arr name="env"> <str>MYVAR=val1</str> </arr>
|
||||||
|
</listener>
|
||||||
|
|
||||||
|
</updateHandler>
|
||||||
|
<requestHandler name="/select" class="solr.SearchHandler">
|
||||||
|
<lst name="defaults">
|
||||||
|
<str name="echoParams">explicit</str>
|
||||||
|
<str name="indent">true</str>
|
||||||
|
<str name="df">text</str>
|
||||||
|
</lst>
|
||||||
|
|
||||||
|
</requestHandler>
|
||||||
|
|
||||||
|
<requestHandler name="/update/xslt"
|
||||||
|
startup="lazy"
|
||||||
|
class="solr.XsltUpdateRequestHandler"/>
|
||||||
|
|
||||||
|
<requestHandler name="/update" class="solr.UpdateRequestHandler" />
|
||||||
|
</config>
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<schema name="minimal" version="1.1">
|
||||||
|
<types>
|
||||||
|
<fieldType name="string" class="solr.StrField"/>
|
||||||
|
</types>
|
||||||
|
<fields>
|
||||||
|
<dynamicField name="*" type="string" indexed="true" stored="true" />
|
||||||
|
</fields>
|
||||||
|
</schema>
|
|
@ -0,0 +1,23 @@
|
||||||
|
function processAdd(cmd) {
|
||||||
|
// Integer.valueOf is needed here to get a tru java object, because
|
||||||
|
// all javascript numbers are floating point (ie: java.lang.Double)
|
||||||
|
cmd.getSolrInputDocument().addField("script_added_i",
|
||||||
|
java.lang.Integer.valueOf(42));
|
||||||
|
cmd.getSolrInputDocument().addField("script_added_d", 42.3);
|
||||||
|
|
||||||
|
}
|
||||||
|
function processDelete() {
|
||||||
|
// NOOP
|
||||||
|
}
|
||||||
|
function processCommit() {
|
||||||
|
// NOOP
|
||||||
|
}
|
||||||
|
function processRollback() {
|
||||||
|
// NOOP
|
||||||
|
}
|
||||||
|
function processMergeIndexes() {
|
||||||
|
// NOOP
|
||||||
|
}
|
||||||
|
function finish() {
|
||||||
|
// NOOP
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?xml version="1.0" ?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- This is a "kitchen sink" config file that tests can use.
|
||||||
|
When writting a new test, feel free to add *new* items (plugins,
|
||||||
|
config options, etc...) as long as they don't break any existing
|
||||||
|
tests. if you need to test something esoteric please add a new
|
||||||
|
"solrconfig-your-esoteric-purpose.xml" config file.
|
||||||
|
|
||||||
|
Note in particular that this test is used by MinimalSchemaTest so
|
||||||
|
Anything added to this file needs to work correctly even if there
|
||||||
|
is now uniqueKey or defaultSearch Field.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<config>
|
||||||
|
|
||||||
|
<dataDir>${solr.data.dir:}</dataDir>
|
||||||
|
|
||||||
|
<directoryFactory name="DirectoryFactory"
|
||||||
|
class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}"/>
|
||||||
|
|
||||||
|
<luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
|
||||||
|
|
||||||
|
<updateHandler class="solr.DirectUpdateHandler2">
|
||||||
|
<commitWithin>
|
||||||
|
<softCommit>${solr.commitwithin.softcommit:true}</softCommit>
|
||||||
|
</commitWithin>
|
||||||
|
|
||||||
|
</updateHandler>
|
||||||
|
<requestHandler name="/select" class="solr.SearchHandler">
|
||||||
|
<lst name="defaults">
|
||||||
|
<str name="echoParams">explicit</str>
|
||||||
|
<str name="indent">true</str>
|
||||||
|
<str name="df">text</str>
|
||||||
|
</lst>
|
||||||
|
|
||||||
|
</requestHandler>
|
||||||
|
|
||||||
|
<updateRequestProcessorChain name="force-script-engine" default="true">
|
||||||
|
<processor class="solr.StatelessScriptUpdateProcessorFactory">
|
||||||
|
<str name="engine">javascript</str>
|
||||||
|
<str name="script">missleading.extension.updateprocessor.js.txt</str>
|
||||||
|
</processor>
|
||||||
|
<processor class="solr.RunUpdateProcessorFactory" />
|
||||||
|
</updateRequestProcessorChain>
|
||||||
|
|
||||||
|
<requestHandler name="/update" class="solr.UpdateRequestHandler" />
|
||||||
|
</config>
|
||||||
|
|
|
@ -16,50 +16,96 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.solr.cloud;
|
package org.apache.solr.cloud;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
import static org.apache.solr.cloud.OverseerConfigSetMessageHandler.BASE_CONFIGSET;
|
||||||
|
import static org.apache.solr.common.params.CommonParams.NAME;
|
||||||
|
import static org.apache.solr.core.ConfigSetProperties.DEFAULT_FILENAME;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Deque;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.entity.ByteArrayEntity;
|
||||||
|
import org.apache.http.message.BasicHeader;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
import org.apache.lucene.util.TestUtil;
|
||||||
import org.apache.solr.SolrTestCaseJ4;
|
import org.apache.solr.SolrTestCaseJ4;
|
||||||
import org.apache.solr.client.solrj.SolrClient;
|
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.CloudSolrClient;
|
||||||
|
import org.apache.solr.client.solrj.impl.HttpClientUtil;
|
||||||
|
import org.apache.solr.client.solrj.impl.HttpSolrClient;
|
||||||
import org.apache.solr.client.solrj.request.ConfigSetAdminRequest;
|
import org.apache.solr.client.solrj.request.ConfigSetAdminRequest;
|
||||||
import org.apache.solr.client.solrj.request.ConfigSetAdminRequest.Create;
|
import org.apache.solr.client.solrj.request.ConfigSetAdminRequest.Create;
|
||||||
import org.apache.solr.client.solrj.request.ConfigSetAdminRequest.Delete;
|
import org.apache.solr.client.solrj.request.ConfigSetAdminRequest.Delete;
|
||||||
import org.apache.solr.client.solrj.request.ConfigSetAdminRequest.List;
|
import org.apache.solr.client.solrj.request.QueryRequest;
|
||||||
|
import org.apache.solr.client.solrj.response.CollectionAdminResponse;
|
||||||
import org.apache.solr.client.solrj.response.ConfigSetAdminResponse;
|
import org.apache.solr.client.solrj.response.ConfigSetAdminResponse;
|
||||||
|
import org.apache.solr.common.SolrException;
|
||||||
|
import org.apache.solr.common.SolrException.ErrorCode;
|
||||||
|
import org.apache.solr.common.SolrInputDocument;
|
||||||
import org.apache.solr.common.cloud.SolrZkClient;
|
import org.apache.solr.common.cloud.SolrZkClient;
|
||||||
import org.apache.solr.common.cloud.ZkConfigManager;
|
import org.apache.solr.common.cloud.ZkConfigManager;
|
||||||
|
import org.apache.solr.common.cloud.ZkStateReader;
|
||||||
|
import org.apache.solr.common.params.CollectionParams.CollectionAction;
|
||||||
|
import org.apache.solr.common.params.CommonParams;
|
||||||
import org.apache.solr.common.params.ConfigSetParams;
|
import org.apache.solr.common.params.ConfigSetParams;
|
||||||
import org.apache.solr.common.params.ConfigSetParams.ConfigSetAction;
|
import org.apache.solr.common.params.ConfigSetParams.ConfigSetAction;
|
||||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||||
import org.apache.solr.common.params.SolrParams;
|
import org.apache.solr.common.params.SolrParams;
|
||||||
|
import org.apache.solr.common.util.Base64;
|
||||||
import org.apache.solr.common.util.NamedList;
|
import org.apache.solr.common.util.NamedList;
|
||||||
import org.apache.solr.common.util.Utils;
|
import org.apache.solr.common.util.Utils;
|
||||||
import org.apache.solr.core.ConfigSetProperties;
|
import org.apache.solr.core.ConfigSetProperties;
|
||||||
|
import org.apache.solr.core.TestDynamicLoading;
|
||||||
|
import org.apache.solr.security.BasicAuthIntegrationTest;
|
||||||
|
import org.apache.zookeeper.CreateMode;
|
||||||
import org.apache.zookeeper.KeeperException;
|
import org.apache.zookeeper.KeeperException;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.noggit.JSONParser;
|
||||||
|
import org.noggit.ObjectBuilder;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import static org.apache.solr.cloud.OverseerConfigSetMessageHandler.BASE_CONFIGSET;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import static org.apache.solr.common.params.CommonParams.NAME;
|
|
||||||
import static org.apache.solr.core.ConfigSetProperties.DEFAULT_FILENAME;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple ConfigSets API tests on user errors and simple success cases.
|
* Simple ConfigSets API tests on user errors and simple success cases.
|
||||||
*/
|
*/
|
||||||
public class TestConfigSetsAPI extends SolrTestCaseJ4 {
|
public class TestConfigSetsAPI extends SolrTestCaseJ4 {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||||
private MiniSolrCloudCluster solrCluster;
|
private MiniSolrCloudCluster solrCluster;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -231,6 +277,349 @@ public class TestConfigSetsAPI extends SolrTestCaseJ4 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUploadErrors() throws Exception {
|
||||||
|
final SolrClient solrClient = new HttpSolrClient(
|
||||||
|
solrCluster.getJettySolrRunners().get(0).getBaseUrl().toString());
|
||||||
|
|
||||||
|
ByteBuffer emptyData = ByteBuffer.allocate(0);
|
||||||
|
|
||||||
|
// Checking error when no configuration name is specified in request
|
||||||
|
Map map = postDataAndGetResponse(solrCluster.getSolrClient(),
|
||||||
|
solrCluster.getJettySolrRunners().get(0).getBaseUrl().toString()
|
||||||
|
+ "/admin/configs?action=UPLOAD&wt=json", emptyData, null, null);
|
||||||
|
assertNotNull(map);
|
||||||
|
long statusCode = (long) getObjectByPath(map, false,
|
||||||
|
Arrays.asList("responseHeader", "status"));
|
||||||
|
assertEquals(400l, statusCode);
|
||||||
|
|
||||||
|
SolrZkClient zkClient = new SolrZkClient(solrCluster.getZkServer().getZkAddress(),
|
||||||
|
AbstractZkTestCase.TIMEOUT, 45000, null);
|
||||||
|
|
||||||
|
// Create dummy config files in zookeeper
|
||||||
|
zkClient.makePath("/configs/myconf", true);
|
||||||
|
zkClient.create("/configs/myconf/firstDummyFile",
|
||||||
|
"first dummy content".getBytes(StandardCharsets.UTF_8), CreateMode.PERSISTENT, true);
|
||||||
|
zkClient.create("/configs/myconf/anotherDummyFile",
|
||||||
|
"second dummy content".getBytes(StandardCharsets.UTF_8), CreateMode.PERSISTENT, true);
|
||||||
|
|
||||||
|
// Checking error when configuration name specified already exists
|
||||||
|
map = postDataAndGetResponse(solrCluster.getSolrClient(),
|
||||||
|
solrCluster.getJettySolrRunners().get(0).getBaseUrl().toString()
|
||||||
|
+ "/admin/configs?action=UPLOAD&wt=json&name=myconf", emptyData, null, null);
|
||||||
|
assertNotNull(map);
|
||||||
|
statusCode = (long) getObjectByPath(map, false,
|
||||||
|
Arrays.asList("responseHeader", "status"));
|
||||||
|
assertEquals(400l, statusCode);
|
||||||
|
assertTrue("Expected file doesnt exist in zk. It's possibly overwritten",
|
||||||
|
zkClient.exists("/configs/myconf/firstDummyFile", true));
|
||||||
|
assertTrue("Expected file doesnt exist in zk. It's possibly overwritten",
|
||||||
|
zkClient.exists("/configs/myconf/anotherDummyFile", true));
|
||||||
|
|
||||||
|
zkClient.close();
|
||||||
|
solrClient.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpload() throws Exception {
|
||||||
|
String suffix = "-untrusted";
|
||||||
|
uploadConfigSet("regular", suffix, null, null);
|
||||||
|
// try to create a collection with the uploaded configset
|
||||||
|
createCollection("newcollection", "regular" + suffix, 1, 1, solrCluster.getSolrClient());
|
||||||
|
xsltRequest("newcollection");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUploadWithRunExecutableListener() throws Exception {
|
||||||
|
String suffix = "-untrusted";
|
||||||
|
uploadConfigSet("with-run-executable-listener", suffix, null, null);
|
||||||
|
// try to create a collection with the uploaded configset
|
||||||
|
CollectionAdminResponse resp = createCollection("newcollection3", "with-run-executable-listener" + suffix, 1, 1, solrCluster.getSolrClient());
|
||||||
|
log.info("Client saw errors: "+resp.getErrorMessages());
|
||||||
|
assertTrue(resp.getErrorMessages() != null && resp.getErrorMessages().size() > 0);
|
||||||
|
assertTrue(resp.getErrorMessages().getVal(0).
|
||||||
|
contains("The configset for this collection was uploaded without any authentication"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUploadWithScriptUpdateProcessor() throws Exception {
|
||||||
|
for (boolean withAuthorization: Arrays.asList(false, true)) {
|
||||||
|
String suffix;
|
||||||
|
if (withAuthorization) {
|
||||||
|
suffix = "-trusted";
|
||||||
|
protectConfigsHandler();
|
||||||
|
uploadConfigSet("with-script-processor", suffix, "solr", "SolrRocks");
|
||||||
|
} else {
|
||||||
|
suffix = "-untrusted";
|
||||||
|
uploadConfigSet("with-script-processor", suffix, null, null);
|
||||||
|
}
|
||||||
|
// try to create a collection with the uploaded configset
|
||||||
|
CollectionAdminResponse resp = createCollection("newcollection2", "with-script-processor"+suffix,
|
||||||
|
1, 1, solrCluster.getSolrClient());
|
||||||
|
|
||||||
|
if (withAuthorization) {
|
||||||
|
scriptRequest("newcollection2");
|
||||||
|
} else {
|
||||||
|
log.info("Client saw errors: "+resp.getErrorMessages());
|
||||||
|
assertTrue(resp.getErrorMessages() != null && resp.getErrorMessages().size() > 0);
|
||||||
|
assertTrue(resp.getErrorMessages().getVal(0).
|
||||||
|
contains("The configset for this collection was uploaded without any authentication"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SolrZkClient zkClient() {
|
||||||
|
ZkStateReader reader = solrCluster.getSolrClient().getZkStateReader();
|
||||||
|
if (reader == null)
|
||||||
|
solrCluster.getSolrClient().connect();
|
||||||
|
return solrCluster.getSolrClient().getZkStateReader().getZkClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void protectConfigsHandler() throws Exception {
|
||||||
|
String authcPrefix = "/admin/authentication";
|
||||||
|
String authzPrefix = "/admin/authorization";
|
||||||
|
|
||||||
|
String securityJson = "{\n" +
|
||||||
|
" 'authentication':{\n" +
|
||||||
|
" 'class':'solr.BasicAuthPlugin',\n" +
|
||||||
|
" 'credentials':{'solr':'orwp2Ghgj39lmnrZOTm7Qtre1VqHFDfwAEzr0ApbN3Y= Ju5osoAqOX8iafhWpPP01E5P+sg8tK8tHON7rCYZRRw='}},\n" +
|
||||||
|
" 'authorization':{\n" +
|
||||||
|
" 'class':'solr.RuleBasedAuthorizationPlugin',\n" +
|
||||||
|
" 'user-role':{'solr':'admin'},\n" +
|
||||||
|
" 'permissions':[{'name':'security-edit','role':'admin'}, {'name':'config-edit','role':'admin'}]}}";
|
||||||
|
|
||||||
|
HttpClient cl = null;
|
||||||
|
try {
|
||||||
|
cl = HttpClientUtil.createClient(null);
|
||||||
|
JettySolrRunner randomJetty = solrCluster.getRandomJetty(random());
|
||||||
|
String baseUrl = randomJetty.getBaseUrl().toString();
|
||||||
|
|
||||||
|
zkClient().setData("/security.json", securityJson.replaceAll("'", "\"").getBytes(UTF_8), true);
|
||||||
|
BasicAuthIntegrationTest.verifySecurityStatus(cl, baseUrl + authcPrefix, "authentication/class", "solr.BasicAuthPlugin", 50);
|
||||||
|
BasicAuthIntegrationTest.verifySecurityStatus(cl, baseUrl + authzPrefix, "authorization/class", "solr.RuleBasedAuthorizationPlugin", 50);
|
||||||
|
} finally {
|
||||||
|
if (cl != null) {
|
||||||
|
HttpClientUtil.close(cl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Thread.sleep(5000); // TODO: Without a delay, the test fails. Some problem with Authc/Authz framework?
|
||||||
|
}
|
||||||
|
|
||||||
|
private void uploadConfigSet(String configSetName, String suffix, String username, String password) throws Exception {
|
||||||
|
// Read zipped sample config
|
||||||
|
ByteBuffer sampleZippedConfig = TestDynamicLoading
|
||||||
|
.getFileContent(
|
||||||
|
createTempZipFile("solr/configsets/upload/"+configSetName), false);
|
||||||
|
|
||||||
|
SolrZkClient zkClient = new SolrZkClient(solrCluster.getZkServer().getZkAddress(),
|
||||||
|
AbstractZkTestCase.TIMEOUT, 45000, null);
|
||||||
|
try {
|
||||||
|
ZkConfigManager configManager = new ZkConfigManager(zkClient);
|
||||||
|
assertFalse(configManager.configExists(configSetName+suffix));
|
||||||
|
|
||||||
|
Map map = postDataAndGetResponse(solrCluster.getSolrClient(),
|
||||||
|
solrCluster.getJettySolrRunners().get(0).getBaseUrl().toString() + "/admin/configs?action=UPLOAD&wt=json&name="+configSetName+suffix,
|
||||||
|
sampleZippedConfig, username, password);
|
||||||
|
assertNotNull(map);
|
||||||
|
long statusCode = (long) getObjectByPath(map, false, Arrays.asList("responseHeader", "status"));
|
||||||
|
assertEquals(0l, statusCode);
|
||||||
|
|
||||||
|
assertTrue("managed-schema file should have been uploaded",
|
||||||
|
zkClient.exists("/configs/"+configSetName+suffix+"/managed-schema", true));
|
||||||
|
assertTrue("managed-schema file contents on zookeeper are not exactly same as that of the file uploaded in config",
|
||||||
|
Arrays.equals(zkClient.getData("/configs/"+configSetName+suffix+"/managed-schema", null, null, true),
|
||||||
|
readFile("solr/configsets/upload/"+configSetName+"/managed-schema")));
|
||||||
|
|
||||||
|
assertTrue("solrconfig.xml file should have been uploaded",
|
||||||
|
zkClient.exists("/configs/"+configSetName+suffix+"/solrconfig.xml", true));
|
||||||
|
byte data[] = zkClient.getData("/configs/"+configSetName+suffix, null, null, true);
|
||||||
|
//assertEquals("{\"trusted\": false}", new String(data, StandardCharsets.UTF_8));
|
||||||
|
assertTrue("solrconfig.xml file contents on zookeeper are not exactly same as that of the file uploaded in config",
|
||||||
|
Arrays.equals(zkClient.getData("/configs/"+configSetName+suffix+"/solrconfig.xml", null, null, true),
|
||||||
|
readFile("solr/configsets/upload/"+configSetName+"/solrconfig.xml")));
|
||||||
|
} finally {
|
||||||
|
zkClient.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a zip file (in the temp directory) containing all the files within the specified directory
|
||||||
|
* and return the path for the zip file.
|
||||||
|
*/
|
||||||
|
private String createTempZipFile(String directoryPath) {
|
||||||
|
File zipFile = new File(solrCluster.getBaseDir().toFile().getAbsolutePath() +
|
||||||
|
File.separator + TestUtil.randomSimpleString(random(), 6, 8) + ".zip");
|
||||||
|
|
||||||
|
File directory = TestDynamicLoading.getFile(directoryPath);
|
||||||
|
log.info("Directory: "+directory.getAbsolutePath());
|
||||||
|
try {
|
||||||
|
zip (directory, zipFile);
|
||||||
|
log.info("Zipfile: "+zipFile.getAbsolutePath());
|
||||||
|
return zipFile.getAbsolutePath();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void zip(File directory, File zipfile) throws IOException {
|
||||||
|
URI base = directory.toURI();
|
||||||
|
Deque<File> queue = new LinkedList<File>();
|
||||||
|
queue.push(directory);
|
||||||
|
OutputStream out = new FileOutputStream(zipfile);
|
||||||
|
ZipOutputStream zout = new ZipOutputStream(out);
|
||||||
|
try {
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
directory = queue.pop();
|
||||||
|
for (File kid : directory.listFiles()) {
|
||||||
|
String name = base.relativize(kid.toURI()).getPath();
|
||||||
|
if (kid.isDirectory()) {
|
||||||
|
queue.push(kid);
|
||||||
|
name = name.endsWith("/") ? name : name + "/";
|
||||||
|
zout.putNextEntry(new ZipEntry(name));
|
||||||
|
} else {
|
||||||
|
zout.putNextEntry(new ZipEntry(name));
|
||||||
|
|
||||||
|
InputStream in = new FileInputStream(kid);
|
||||||
|
try {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
while (true) {
|
||||||
|
int readCount = in.read(buffer);
|
||||||
|
if (readCount < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
zout.write(buffer, 0, readCount);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
zout.closeEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
zout.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void xsltRequest(String collection) throws SolrServerException, IOException {
|
||||||
|
String baseUrl = solrCluster.getJettySolrRunners().get(0).getBaseUrl().toString();
|
||||||
|
try (HttpSolrClient client = new HttpSolrClient(baseUrl + "/" + collection)) {
|
||||||
|
String xml =
|
||||||
|
"<random>" +
|
||||||
|
" <document>" +
|
||||||
|
" <node name=\"id\" value=\"12345\"/>" +
|
||||||
|
" <node name=\"name\" value=\"kitten\"/>" +
|
||||||
|
" <node name=\"text\" enhance=\"3\" value=\"some other day\"/>" +
|
||||||
|
" <node name=\"title\" enhance=\"4\" value=\"A story\"/>" +
|
||||||
|
" <node name=\"timestamp\" enhance=\"5\" value=\"2011-07-01T10:31:57.140Z\"/>" +
|
||||||
|
" </document>" +
|
||||||
|
"</random>";
|
||||||
|
|
||||||
|
SolrQuery query = new SolrQuery();
|
||||||
|
query.setQuery( "*:*" );//for anything
|
||||||
|
query.add("qt","/update");
|
||||||
|
query.add(CommonParams.TR, "xsl-update-handler-test.xsl");
|
||||||
|
query.add("stream.body", xml);
|
||||||
|
query.add("commit", "true");
|
||||||
|
try {
|
||||||
|
client.query(query);
|
||||||
|
fail("This should've returned a 401.");
|
||||||
|
} catch (SolrException ex) {
|
||||||
|
assertEquals(ErrorCode.UNAUTHORIZED.code, ex.code());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void scriptRequest(String collection) throws SolrServerException, IOException {
|
||||||
|
SolrClient client = solrCluster.getSolrClient();
|
||||||
|
SolrInputDocument doc = sdoc("id", "4055", "subject", "Solr");
|
||||||
|
client.add(collection, doc);
|
||||||
|
client.commit(collection);
|
||||||
|
|
||||||
|
assertEquals("42", client.query(collection, params("q", "*:*")).getResults().get(0).get("script_added_i"));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CollectionAdminResponse createCollection(String collectionName, String confSetName, int numShards,
|
||||||
|
int replicationFactor, SolrClient client) throws SolrServerException, IOException {
|
||||||
|
ModifiableSolrParams params = new ModifiableSolrParams();
|
||||||
|
params.set("action", CollectionAction.CREATE.toString());
|
||||||
|
params.set("collection.configName", confSetName);
|
||||||
|
params.set("name", collectionName);
|
||||||
|
params.set("numShards", numShards);
|
||||||
|
params.set("replicationFactor", replicationFactor);
|
||||||
|
SolrRequest request = new QueryRequest(params);
|
||||||
|
request.setPath("/admin/collections");
|
||||||
|
|
||||||
|
CollectionAdminResponse res = new CollectionAdminResponse();
|
||||||
|
res.setResponse(client.request(request));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map postDataAndGetResponse(CloudSolrClient cloudClient,
|
||||||
|
String uri, ByteBuffer bytarr, String username, String password) throws IOException {
|
||||||
|
HttpPost httpPost = null;
|
||||||
|
HttpEntity entity;
|
||||||
|
String response = null;
|
||||||
|
Map m = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
httpPost = new HttpPost(uri);
|
||||||
|
|
||||||
|
if (username != null) {
|
||||||
|
String userPass = username + ":" + password;
|
||||||
|
String encoded = Base64.byteArrayToBase64(userPass.getBytes(UTF_8));
|
||||||
|
BasicHeader header = new BasicHeader("Authorization", "Basic " + encoded);
|
||||||
|
httpPost.setHeader(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
httpPost.setHeader("Content-Type", "application/octet-stream");
|
||||||
|
httpPost.setEntity(new ByteArrayEntity(bytarr.array(), bytarr
|
||||||
|
.arrayOffset(), bytarr.limit()));
|
||||||
|
entity = cloudClient.getLbClient().getHttpClient().execute(httpPost)
|
||||||
|
.getEntity();
|
||||||
|
try {
|
||||||
|
response = EntityUtils.toString(entity, StandardCharsets.UTF_8);
|
||||||
|
m = (Map) ObjectBuilder.getVal(new JSONParser(
|
||||||
|
new StringReader(response)));
|
||||||
|
} catch (JSONParser.ParseException e) {
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
httpPost.releaseConnection();
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object getObjectByPath(Map root, boolean onlyPrimitive, java.util.List<String> hierarchy) {
|
||||||
|
Map obj = root;
|
||||||
|
for (int i = 0; i < hierarchy.size(); i++) {
|
||||||
|
String s = hierarchy.get(i);
|
||||||
|
if (i < hierarchy.size() - 1) {
|
||||||
|
if (!(obj.get(s) instanceof Map)) return null;
|
||||||
|
obj = (Map) obj.get(s);
|
||||||
|
if (obj == null) return null;
|
||||||
|
} else {
|
||||||
|
Object val = obj.get(s);
|
||||||
|
if (onlyPrimitive && val instanceof Map) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] readFile(String fname) throws IOException {
|
||||||
|
byte[] buf = null;
|
||||||
|
try (FileInputStream fis = new FileInputStream(getFile(fname))) {
|
||||||
|
buf = new byte[fis.available()];
|
||||||
|
fis.read(buf);
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteErrors() throws Exception {
|
public void testDeleteErrors() throws Exception {
|
||||||
final String baseUrl = solrCluster.getJettySolrRunners().get(0).getBaseUrl().toString();
|
final String baseUrl = solrCluster.getJettySolrRunners().get(0).getBaseUrl().toString();
|
||||||
|
@ -304,7 +693,7 @@ public class TestConfigSetsAPI extends SolrTestCaseJ4 {
|
||||||
AbstractZkTestCase.TIMEOUT, AbstractZkTestCase.TIMEOUT, null);
|
AbstractZkTestCase.TIMEOUT, AbstractZkTestCase.TIMEOUT, null);
|
||||||
try {
|
try {
|
||||||
// test empty
|
// test empty
|
||||||
List list = new List();
|
ConfigSetAdminRequest.List list = new ConfigSetAdminRequest.List();
|
||||||
ConfigSetAdminResponse.List response = list.process(solrClient);
|
ConfigSetAdminResponse.List response = list.process(solrClient);
|
||||||
Collection<String> actualConfigSets = response.getConfigSets();
|
Collection<String> actualConfigSets = response.getConfigSets();
|
||||||
assertEquals(0, actualConfigSets.size());
|
assertEquals(0, actualConfigSets.size());
|
||||||
|
|
|
@ -218,7 +218,7 @@ public class TestCodecSupport extends SolrTestCaseJ4 {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
c = new SolrCore(new CoreDescriptor(h.getCoreContainer(), newCoreName, testSolrHome.resolve(newCoreName)),
|
c = new SolrCore(new CoreDescriptor(h.getCoreContainer(), newCoreName, testSolrHome.resolve(newCoreName)),
|
||||||
new ConfigSet("fakeConfigset", config, schema, null));
|
new ConfigSet("fakeConfigset", config, schema, null, true));
|
||||||
assertNull(h.getCoreContainer().registerCore(newCoreName, c, false, false));
|
assertNull(h.getCoreContainer().registerCore(newCoreName, c, false, false));
|
||||||
h.coreName = newCoreName;
|
h.coreName = newCoreName;
|
||||||
assertEquals("We are not using the correct core", "solrconfig_codec2.xml", h.getCore().getConfigResource());
|
assertEquals("We are not using the correct core", "solrconfig_codec2.xml", h.getCore().getConfigResource());
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.apache.solr.util.SimplePostTool;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -257,8 +258,16 @@ public class TestDynamicLoading extends AbstractFullDistribZkTestBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ByteBuffer getFileContent(String f) throws IOException {
|
public static ByteBuffer getFileContent(String f) throws IOException {
|
||||||
|
return getFileContent(f, true);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param loadFromClassPath if true, it will look in the classpath to find the file,
|
||||||
|
* otherwise load from absolute filesystem path.
|
||||||
|
*/
|
||||||
|
public static ByteBuffer getFileContent(String f, boolean loadFromClassPath) throws IOException {
|
||||||
ByteBuffer jar;
|
ByteBuffer jar;
|
||||||
try (FileInputStream fis = new FileInputStream(getFile(f))) {
|
File file = loadFromClassPath ? getFile(f): new File(f);
|
||||||
|
try (FileInputStream fis = new FileInputStream(file)) {
|
||||||
byte[] buf = new byte[fis.available()];
|
byte[] buf = new byte[fis.available()];
|
||||||
fis.read(buf);
|
fis.read(buf);
|
||||||
jar = ByteBuffer.wrap(buf);
|
jar = ByteBuffer.wrap(buf);
|
||||||
|
|
|
@ -27,6 +27,7 @@ public interface ConfigSetParams
|
||||||
|
|
||||||
public enum ConfigSetAction {
|
public enum ConfigSetAction {
|
||||||
CREATE,
|
CREATE,
|
||||||
|
UPLOAD,
|
||||||
DELETE,
|
DELETE,
|
||||||
LIST;
|
LIST;
|
||||||
|
|
||||||
|
|
|
@ -470,7 +470,11 @@ public class MiniSolrCloudCluster {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Path getBaseDir() {
|
||||||
|
return baseDir;
|
||||||
|
}
|
||||||
|
|
||||||
public CloudSolrClient getSolrClient() {
|
public CloudSolrClient getSolrClient() {
|
||||||
return solrClient;
|
return solrClient;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue