mirror of
https://github.com/apache/lucene.git
synced 2025-02-21 17:46:28 +00:00
SOLR-5287: Allow at least solrconfig.xml and schema.xml to be edited via the admin screen
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1542345 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
367cb67231
commit
7f2b8bc419
@ -80,6 +80,9 @@ New Features
|
||||
|
||||
* SOLR-5320: Added support for tri-level compositeId routing.
|
||||
(Anshum Gupta via shalin)
|
||||
|
||||
* SOLR-5287: You can edit files in the conf directory from the admin UI
|
||||
(Erick Erickson, Stefan Matheis)
|
||||
|
||||
Bug Fixes
|
||||
----------------------
|
||||
|
@ -17,12 +17,15 @@
|
||||
|
||||
package org.apache.solr.handler.admin;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.solr.cloud.ZkSolrResourceLoader;
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.common.SolrException.ErrorCode;
|
||||
import org.apache.solr.common.cloud.SolrZkClient;
|
||||
import org.apache.solr.common.params.CommonParams;
|
||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||
import org.apache.solr.common.util.ContentStream;
|
||||
import org.apache.solr.common.util.ContentStreamBase;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.common.util.SimpleOrderedMap;
|
||||
@ -34,13 +37,17 @@ import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.response.RawResponseWriter;
|
||||
import org.apache.solr.response.SolrQueryResponse;
|
||||
import org.apache.zookeeper.KeeperException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
@ -60,10 +67,15 @@ import java.util.Set;
|
||||
* </lst>
|
||||
* <lst name="invariants">
|
||||
* <str name="hidden">synonyms.txt</str>
|
||||
* <str name="hidden">anotherfile.txt</str>
|
||||
* <str name="hidden">anotherfile.txt</str>
|
||||
* <str name="hidden">*</str>
|
||||
* </lst>
|
||||
* </requestHandler>
|
||||
* </pre>
|
||||
*
|
||||
* At present, there is only explicit file names (including path) or the glob '*' are supported. Variants like '*.xml'
|
||||
* are NOT supported.ere
|
||||
*
|
||||
* <p>
|
||||
* The ShowFileRequestHandler uses the {@link RawResponseWriter} (wt=raw) to return
|
||||
* file contents. If you need to use a different writer, you will need to change
|
||||
@ -75,12 +87,41 @@ import java.util.Set;
|
||||
* <pre>
|
||||
* http://localhost:8983/solr/admin/file?file=schema.xml&contentType=text/plain
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
*
|
||||
* @since solr 1.3
|
||||
*
|
||||
*
|
||||
* As of Solr 4.7, you can use this handler to modify any files in the conf directory, e.g. solrconfig.xml
|
||||
* or schema.xml, or even in sub-directories (e.g. velocity/error.vm) by POSTing a file. Here's an example cURL command
|
||||
* <pre>
|
||||
* curl -X POST --form "fileupload=@schema.new" 'http://localhost:8983/solr/collection1/admin/file?op=write&file=schema.xml'
|
||||
* </pre>
|
||||
*
|
||||
* or
|
||||
* <pre>
|
||||
* curl -X POST --form "fileupload=@error.new" 'http://localhost:8983/solr/collection1/admin/file?op=write&file=velocity/error.vm'
|
||||
* </pre>
|
||||
*
|
||||
* For the first iteration, this is probably going to be used from the Solr admin screen.
|
||||
*
|
||||
* NOTE: Specifying a directory or simply leaving the any "file=XXX" parameters will list the contents of a directory.
|
||||
*
|
||||
* NOTE: <b>You must reload the core/collection for any changes made via this handler to take effect!</b>
|
||||
*
|
||||
* NOTE: <b>If the core does not load (say schema.xml is not well formed for instance) you may be unable to replace
|
||||
* the files with this interface.</b>
|
||||
*
|
||||
* Configuration files in ZooKeeper are supported.
|
||||
*
|
||||
* Writing files out, @since solr 4.7
|
||||
*/
|
||||
public class ShowFileRequestHandler extends RequestHandlerBase
|
||||
{
|
||||
|
||||
protected static final Logger log = LoggerFactory
|
||||
.getLogger(ShowFileRequestHandler.class);
|
||||
|
||||
public static final String HIDDEN = "hidden";
|
||||
public static final String USE_CONTENT_TYPE = "contentType";
|
||||
|
||||
@ -113,65 +154,147 @@ public class ShowFileRequestHandler extends RequestHandlerBase
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException, KeeperException, InterruptedException
|
||||
{
|
||||
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp)
|
||||
throws InterruptedException, KeeperException, IOException {
|
||||
|
||||
CoreContainer coreContainer = req.getCore().getCoreDescriptor().getCoreContainer();
|
||||
if (coreContainer.isZooKeeperAware()) {
|
||||
showFromZooKeeper(req, rsp, coreContainer);
|
||||
} else {
|
||||
showFromFileSystem(req, rsp);
|
||||
String op = req.getParams().get("op");
|
||||
if (op == null) {
|
||||
if (coreContainer.isZooKeeperAware()) {
|
||||
showFromZooKeeper(req, rsp, coreContainer);
|
||||
} else {
|
||||
showFromFileSystem(req, rsp);
|
||||
}
|
||||
} else if ("write".equalsIgnoreCase(op)) {
|
||||
String fname = req.getParams().get("file", null);
|
||||
if (fname == null) {
|
||||
rsp.setException(new SolrException(ErrorCode.BAD_REQUEST, "No file name specified for write operation."));
|
||||
} else {
|
||||
if (coreContainer.isZooKeeperAware()) {
|
||||
if (isHiddenFile(rsp, fname) == false) {
|
||||
writeToZooKeeper(req, rsp, coreContainer);
|
||||
}
|
||||
} else {
|
||||
fname = fname.replace('\\', '/'); // normalize slashes. Should be done above too?
|
||||
if (isHiddenFile(rsp, fname) == false) {
|
||||
writeToFileSystem(req, rsp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showFromZooKeeper(SolrQueryRequest req, SolrQueryResponse rsp,
|
||||
CoreContainer coreContainer) throws KeeperException,
|
||||
InterruptedException, UnsupportedEncodingException {
|
||||
// See if we should deal with this file
|
||||
|
||||
private boolean isHiddenFile(SolrQueryResponse rsp, String fnameIn) {
|
||||
String fname = fnameIn.toUpperCase(Locale.ROOT);
|
||||
if (hiddenFiles.contains(fname) || hiddenFiles.contains("*")) {
|
||||
log.error("Cannot access " + fname);
|
||||
rsp.setException(new SolrException(ErrorCode.FORBIDDEN, "Can not access: " + fnameIn));
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is slightly off, a valid path is something like ./schema.xml. I don't think it's worth the effort though
|
||||
// to fix it to handle all possibilities though.
|
||||
if (fname.indexOf("..") >= 0 || fname.startsWith(".")) {
|
||||
log.error("Invalid path: " + fname);
|
||||
rsp.setException(new SolrException(ErrorCode.FORBIDDEN, "Invalid path: " + fnameIn));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Refactored to be usable from multiple methods. Gets the path of the requested file from ZK.
|
||||
// Returns null if the file is not found.
|
||||
//
|
||||
// Assumes that the file is in a parameter called "file".
|
||||
|
||||
private String getAdminFileFromZooKeeper(SolrQueryRequest req, SolrQueryResponse rsp, SolrZkClient zkClient)
|
||||
throws KeeperException, InterruptedException {
|
||||
String adminFile = null;
|
||||
SolrCore core = req.getCore();
|
||||
SolrZkClient zkClient = coreContainer.getZkController().getZkClient();
|
||||
|
||||
final ZkSolrResourceLoader loader = (ZkSolrResourceLoader) core
|
||||
.getResourceLoader();
|
||||
String confPath = loader.getCollectionZkPath();
|
||||
|
||||
|
||||
String fname = req.getParams().get("file", null);
|
||||
if (fname == null) {
|
||||
adminFile = confPath;
|
||||
} else {
|
||||
fname = fname.replace('\\', '/'); // normalize slashes
|
||||
if (hiddenFiles.contains(fname.toUpperCase(Locale.ROOT))) {
|
||||
rsp.setException(new SolrException(ErrorCode.FORBIDDEN, "Can not access: " + fname));
|
||||
return;
|
||||
}
|
||||
if (fname.indexOf("..") >= 0) {
|
||||
rsp.setException(new SolrException(ErrorCode.FORBIDDEN, "Invalid path: " + fname));
|
||||
return;
|
||||
if (isHiddenFile(rsp, fname)) {
|
||||
return null;
|
||||
}
|
||||
if (fname.startsWith("/")) { // Only files relative to conf are valid
|
||||
fname = fname.substring(1);
|
||||
}
|
||||
adminFile = confPath + "/" + fname;
|
||||
}
|
||||
|
||||
|
||||
// Make sure the file exists, is readable and is not a hidden file
|
||||
if (!zkClient.exists(adminFile, true)) {
|
||||
log.error("Can not find: " + adminFile);
|
||||
rsp.setException(new SolrException(ErrorCode.NOT_FOUND, "Can not find: "
|
||||
+ adminFile));
|
||||
+ adminFile));
|
||||
return null;
|
||||
}
|
||||
|
||||
return adminFile;
|
||||
}
|
||||
|
||||
// write the file contained in the parameter "file=XXX" to ZooKeeper. The file may be a path, e.g.
|
||||
// file=velocity/error.vm or file=schema.xml
|
||||
//
|
||||
// Important: Assumes that the file already exists in ZK, so far we aren't creating files there.
|
||||
private void writeToZooKeeper(SolrQueryRequest req, SolrQueryResponse rsp, CoreContainer coreContainer)
|
||||
throws KeeperException, InterruptedException, IOException {
|
||||
SolrZkClient zkClient = coreContainer.getZkController().getZkClient();
|
||||
|
||||
String adminFile = getAdminFileFromZooKeeper(req, rsp, zkClient);
|
||||
ContentStream stream = getOneInputStream(req, rsp);
|
||||
if (stream == null) {
|
||||
return; // Error already in rsp.
|
||||
}
|
||||
|
||||
byte[] data = IOUtils.toByteArray(new InputStreamReader(stream.getStream(), "UTF-8"), "UTF-8");
|
||||
String fname = req.getParams().get("file", null);
|
||||
// Persist the managed schema
|
||||
try {
|
||||
// Assumption: the path exists
|
||||
zkClient.setData(adminFile, data, true);
|
||||
log.info("Saved " + fname + " to ZooKeeper successfully.");
|
||||
} catch (KeeperException.BadVersionException e) {
|
||||
log.error("Cannot save file: " + fname + " to Zookeeper, " +
|
||||
"ZooKeeper error: " + e.getMessage());
|
||||
rsp.setException(new SolrException(ErrorCode.SERVER_ERROR, "Cannot save file: " + fname + " to Zookeeper, " +
|
||||
"ZooKeeper error: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
// Get a list of files from ZooKeeper for from the path in the file= parameter.
|
||||
private void showFromZooKeeper(SolrQueryRequest req, SolrQueryResponse rsp,
|
||||
CoreContainer coreContainer) throws KeeperException,
|
||||
InterruptedException, UnsupportedEncodingException {
|
||||
|
||||
SolrZkClient zkClient = coreContainer.getZkController().getZkClient();
|
||||
|
||||
String adminFile = getAdminFileFromZooKeeper(req, rsp, zkClient);
|
||||
|
||||
if (adminFile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Show a directory listing
|
||||
List<String> children = zkClient.getChildren(adminFile, null, true);
|
||||
if (children.size() > 0) {
|
||||
|
||||
NamedList<SimpleOrderedMap<Object>> files = new SimpleOrderedMap<SimpleOrderedMap<Object>>();
|
||||
for (String f : children) {
|
||||
if (hiddenFiles.contains(f.toUpperCase(Locale.ROOT))) {
|
||||
continue; // don't show 'hidden' files
|
||||
if (isHiddenFile(rsp, f)) {
|
||||
continue;
|
||||
}
|
||||
if (f.startsWith(".")) {
|
||||
continue; // skip hidden system files...
|
||||
}
|
||||
|
||||
|
||||
SimpleOrderedMap<Object> fileInfo = new SimpleOrderedMap<Object>();
|
||||
files.add(f, fileInfo);
|
||||
List<String> fchildren = zkClient.getChildren(adminFile, null, true);
|
||||
@ -199,9 +322,71 @@ public class ShowFileRequestHandler extends RequestHandlerBase
|
||||
rsp.setHttpCaching(false);
|
||||
}
|
||||
|
||||
private void showFromFileSystem(SolrQueryRequest req, SolrQueryResponse rsp) {
|
||||
|
||||
// Used when POSTing the configuration files to Solr (either ZooKeeper or locally).
|
||||
//
|
||||
// It takes some effort to insure that there is one (and only one) stream provided, there's no provision for
|
||||
// more than one stream at present.
|
||||
private ContentStream getOneInputStream(SolrQueryRequest req, SolrQueryResponse rsp) {
|
||||
String file = req.getParams().get("file");
|
||||
if (file == null) {
|
||||
log.error("You must specify a file for the write operation.");
|
||||
rsp.setException(new SolrException(ErrorCode.BAD_REQUEST, "You must specify a file for the write operation."));
|
||||
return null;
|
||||
}
|
||||
|
||||
// Now, this is truly clumsy
|
||||
Iterable<ContentStream> streams = req.getContentStreams();
|
||||
if (streams == null) {
|
||||
log.error("Input stream list was null for admin file write operation.");
|
||||
rsp.setException(new SolrException(ErrorCode.BAD_REQUEST, "Input stream list was null for admin file write operation."));
|
||||
return null;
|
||||
}
|
||||
Iterator<ContentStream> iter = streams.iterator();
|
||||
if (!iter.hasNext()) {
|
||||
log.error("No input streams were in the list for admin file write operation.");
|
||||
rsp.setException(new SolrException(ErrorCode.BAD_REQUEST, "No input streams were in the list for admin file write operation."));
|
||||
return null;
|
||||
}
|
||||
ContentStream stream = iter.next();
|
||||
if (iter.hasNext()) {
|
||||
log.error("More than one input stream was found for admin file write operation.");
|
||||
rsp.setException(new SolrException(ErrorCode.BAD_REQUEST, "More than one input stream was found for admin file write operation."));
|
||||
return null;
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
// Write the data passed in from the stream to the file indicated by the file=XXX parameter on the local file system
|
||||
private void writeToFileSystem(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException {
|
||||
ContentStream stream = getOneInputStream(req, rsp);
|
||||
if (stream == null) {
|
||||
return; // Error should already have been logged.
|
||||
}
|
||||
|
||||
File adminFile = getAdminFileFromFileSystem(req, rsp);
|
||||
if (adminFile == null || adminFile.isDirectory()) {
|
||||
String fname = req.getParams().get("file", null);
|
||||
|
||||
if (adminFile == null) {
|
||||
log.error("File " + fname + " was not found.");
|
||||
rsp.setException(new SolrException( ErrorCode.BAD_REQUEST, "File " + fname + " was not found."));
|
||||
return;
|
||||
}
|
||||
log.error("File " + fname + " is a directory.");
|
||||
rsp.setException(new SolrException( ErrorCode.BAD_REQUEST, "File " + fname + " is a directory."));
|
||||
return;
|
||||
}
|
||||
|
||||
FileUtils.copyInputStreamToFile(stream.getStream(), adminFile);
|
||||
log.info("Successfully saved file " + adminFile.getAbsolutePath() + " locally");
|
||||
}
|
||||
|
||||
// Find the file indicated by the "file=XXX" parameter or the root of the conf directory on the local
|
||||
// file system. Respects all the "interesting" stuff around what the resource loader does to find files.
|
||||
private File getAdminFileFromFileSystem(SolrQueryRequest req, SolrQueryResponse rsp) {
|
||||
File adminFile = null;
|
||||
|
||||
final SolrResourceLoader loader = req.getCore().getResourceLoader();
|
||||
File configdir = new File( loader.getConfigDir() );
|
||||
if (!configdir.exists()) {
|
||||
@ -209,8 +394,9 @@ public class ShowFileRequestHandler extends RequestHandlerBase
|
||||
try {
|
||||
configdir = new File( loader.getClassLoader().getResource(loader.getConfigDir()).toURI() );
|
||||
} catch (URISyntaxException e) {
|
||||
log.error("Can not access configuration directory!");
|
||||
rsp.setException(new SolrException( ErrorCode.FORBIDDEN, "Can not access configuration directory!", e));
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
String fname = req.getParams().get("file", null);
|
||||
@ -220,24 +406,38 @@ public class ShowFileRequestHandler extends RequestHandlerBase
|
||||
else {
|
||||
fname = fname.replace( '\\', '/' ); // normalize slashes
|
||||
if( hiddenFiles.contains( fname.toUpperCase(Locale.ROOT) ) ) {
|
||||
log.error("Can not access: "+ fname);
|
||||
rsp.setException(new SolrException( ErrorCode.FORBIDDEN, "Can not access: "+fname ));
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
if( fname.indexOf( ".." ) >= 0 ) {
|
||||
log.error("Invalid path: "+ fname);
|
||||
rsp.setException(new SolrException( ErrorCode.FORBIDDEN, "Invalid path: "+fname ));
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
adminFile = new File( configdir, fname );
|
||||
}
|
||||
|
||||
return adminFile;
|
||||
}
|
||||
|
||||
// Return the file indicated (or the directory listing) from the local file system.
|
||||
private void showFromFileSystem(SolrQueryRequest req, SolrQueryResponse rsp) {
|
||||
File adminFile = getAdminFileFromFileSystem(req, rsp);
|
||||
|
||||
if (adminFile == null) { // exception already recorded
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the file exists, is readable and is not a hidden file
|
||||
if( !adminFile.exists() ) {
|
||||
log.error("Can not find: "+adminFile.getName() + " ["+adminFile.getAbsolutePath()+"]");
|
||||
rsp.setException(new SolrException
|
||||
( ErrorCode.NOT_FOUND, "Can not find: "+adminFile.getName()
|
||||
+ " ["+adminFile.getAbsolutePath()+"]" ));
|
||||
return;
|
||||
}
|
||||
if( !adminFile.canRead() || adminFile.isHidden() ) {
|
||||
log.error("Can not show: "+adminFile.getName() + " ["+adminFile.getAbsolutePath()+"]");
|
||||
rsp.setException(new SolrException
|
||||
( ErrorCode.NOT_FOUND, "Can not show: "+adminFile.getName()
|
||||
+ " ["+adminFile.getAbsolutePath()+"]" ));
|
||||
@ -246,8 +446,8 @@ public class ShowFileRequestHandler extends RequestHandlerBase
|
||||
|
||||
// Show a directory listing
|
||||
if( adminFile.isDirectory() ) {
|
||||
|
||||
int basePath = configdir.getAbsolutePath().length() + 1;
|
||||
// it's really a directory, just go for it.
|
||||
int basePath = adminFile.getAbsolutePath().length() + 1;
|
||||
NamedList<SimpleOrderedMap<Object>> files = new SimpleOrderedMap<SimpleOrderedMap<Object>>();
|
||||
for( File f : adminFile.listFiles() ) {
|
||||
String path = f.getAbsolutePath().substring( basePath );
|
||||
@ -270,7 +470,7 @@ public class ShowFileRequestHandler extends RequestHandlerBase
|
||||
}
|
||||
fileInfo.add( "modified", new Date( f.lastModified() ) );
|
||||
}
|
||||
rsp.add( "files", files );
|
||||
rsp.add("files", files);
|
||||
}
|
||||
else {
|
||||
// Include the file contents
|
||||
@ -286,13 +486,12 @@ public class ShowFileRequestHandler extends RequestHandlerBase
|
||||
}
|
||||
rsp.setHttpCaching(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////// SolrInfoMBeans methods //////////////////////
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Admin Get File -- view config files directly";
|
||||
return "Admin Config File -- view or update config files directly";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -86,6 +86,13 @@
|
||||
|
||||
<requestHandler name="/admin/" class="org.apache.solr.handler.admin.AdminHandlers" />
|
||||
|
||||
<requestHandler name="/admin/file" class="solr.admin.ShowFileRequestHandler" >
|
||||
<lst name="invariants">
|
||||
<str name="hidden">bogus.txt</str>
|
||||
</lst>
|
||||
</requestHandler>
|
||||
|
||||
|
||||
<updateRequestProcessorChain name="distrib-dup-test-chain-explicit">
|
||||
<!-- explicit test using processors before and after distrib -->
|
||||
<processor class="solr.RegexReplaceProcessorFactory">
|
||||
|
@ -234,6 +234,12 @@
|
||||
|
||||
<requestHandler name="/admin/" class="org.apache.solr.handler.admin.AdminHandlers" />
|
||||
|
||||
<requestHandler name="/admin/file" class="solr.admin.ShowFileRequestHandler" >
|
||||
<lst name="invariants">
|
||||
<str name="hidden">bogus.txt</str>
|
||||
</lst>
|
||||
</requestHandler>
|
||||
|
||||
<!-- test query parameter defaults -->
|
||||
<requestHandler name="defaults" class="solr.StandardRequestHandler">
|
||||
<lst name="defaults">
|
||||
|
@ -0,0 +1,96 @@
|
||||
package org.apache.solr.cloud;
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import org.apache.solr.client.solrj.impl.HttpSolrServer;
|
||||
import org.apache.solr.client.solrj.request.QueryRequest;
|
||||
import org.apache.solr.common.cloud.SolrZkClient;
|
||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||
|
||||
public class TestModifyConfFiles extends AbstractFullDistribZkTestBase {
|
||||
|
||||
public TestModifyConfFiles() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doTest() throws Exception {
|
||||
int which = r.nextInt(clients.size());
|
||||
HttpSolrServer client = (HttpSolrServer) clients.get(which);
|
||||
|
||||
ModifiableSolrParams params = new ModifiableSolrParams();
|
||||
params.set("op", "write");
|
||||
params.set("file", "schema.xml");
|
||||
QueryRequest request = new QueryRequest(params);
|
||||
request.setPath("/admin/file");
|
||||
try {
|
||||
client.request(request);
|
||||
fail("Should have caught exception");
|
||||
} catch (Exception e) {
|
||||
assertEquals(e.getMessage(), "Input stream list was null for admin file write operation.");
|
||||
}
|
||||
|
||||
params.remove("file");
|
||||
params.set("stream.body", "Testing rewrite of schema.xml file.");
|
||||
request = new QueryRequest(params);
|
||||
request.setPath("/admin/file");
|
||||
try {
|
||||
client.request(request);
|
||||
fail("Should have caught exception");
|
||||
} catch (Exception e) {
|
||||
assertEquals(e.getMessage(), "No file name specified for write operation.");
|
||||
}
|
||||
|
||||
params.set("file", "bogus.txt");
|
||||
request = new QueryRequest(params);
|
||||
request.setPath("/admin/file");
|
||||
try {
|
||||
client.request(request);
|
||||
fail("Should have caught exception");
|
||||
} catch (Exception e) {
|
||||
assertEquals(e.getMessage(), "Can not access: bogus.txt");
|
||||
}
|
||||
|
||||
params.set("file", "schema.xml");
|
||||
request = new QueryRequest(params);
|
||||
request.setPath("/admin/file");
|
||||
|
||||
client.request(request);
|
||||
|
||||
SolrZkClient zkClient = cloudClient.getZkStateReader().getZkClient();
|
||||
String contents = new String(zkClient.getData("/configs/conf1/schema.xml", null, null, true), "UTF-8");
|
||||
|
||||
//String schema = getFileContentFromZooKeeper("schema.xml");
|
||||
|
||||
assertTrue("Schema contents should have changed!", "Testing rewrite of schema.xml file.".equals(contents));
|
||||
|
||||
// Create a velocity/whatever node. Put a bit of data in it. See if you can change it.
|
||||
zkClient.makePath("/configs/conf1/velocity/test.vm", false, true);
|
||||
|
||||
params.set("stream.body", "Some bogus stuff for a test.");
|
||||
params.set("file", "velocity/test.vm");
|
||||
request = new QueryRequest(params);
|
||||
request.setPath("/admin/file");
|
||||
|
||||
client.request(request);
|
||||
|
||||
contents = new String(zkClient.getData("/configs/conf1/velocity/test.vm", null, null, true), "UTF-8");
|
||||
assertTrue("Should have found new content in a velocity/test.vm.",
|
||||
contents.indexOf("Some bogus stuff for a test.") != -1);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.schema;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.rules.SystemPropertiesRestoreRule;
|
||||
import org.apache.commons.codec.Charsets;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||
import org.apache.solr.common.util.ContentStream;
|
||||
import org.apache.solr.common.util.ContentStreamBase;
|
||||
import org.apache.solr.core.CoreContainer;
|
||||
import org.apache.solr.core.SolrCore;
|
||||
import org.apache.solr.request.LocalSolrQueryRequest;
|
||||
import org.apache.solr.request.SolrRequestHandler;
|
||||
import org.apache.solr.response.SolrQueryResponse;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.RuleChain;
|
||||
import org.junit.rules.TestRule;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ModifyConfFileTest extends SolrTestCaseJ4 {
|
||||
private File solrHomeDirectory = new File(TEMP_DIR, this.getClass().getName());
|
||||
@Rule
|
||||
public TestRule solrTestRules = RuleChain.outerRule(new SystemPropertiesRestoreRule());
|
||||
|
||||
private CoreContainer init() throws Exception {
|
||||
System.setProperty("solr.test.sys.prop1", "propone");
|
||||
System.setProperty("solr.test.sys.prop2", "proptwo");
|
||||
|
||||
if (solrHomeDirectory.exists()) {
|
||||
FileUtils.deleteDirectory(solrHomeDirectory);
|
||||
}
|
||||
assertTrue("Failed to mkdirs workDir", solrHomeDirectory.mkdirs());
|
||||
|
||||
copySolrHomeToTemp(solrHomeDirectory, "core1", true);
|
||||
FileUtils.write(new File(new File(solrHomeDirectory, "core1"), "core.properties"), "", Charsets.UTF_8.toString());
|
||||
final CoreContainer cores = new CoreContainer(solrHomeDirectory.getAbsolutePath());
|
||||
cores.load();
|
||||
return cores;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigWrite() throws Exception {
|
||||
|
||||
final CoreContainer cc = init();
|
||||
try {
|
||||
//final CoreAdminHandler admin = new CoreAdminHandler(cc);
|
||||
|
||||
SolrCore core = cc.getCore("core1");
|
||||
SolrQueryResponse rsp = new SolrQueryResponse();
|
||||
SolrRequestHandler handler = core.getRequestHandler("/admin/file");
|
||||
|
||||
ModifiableSolrParams params = params("file","schema.xml", "op","write");
|
||||
core.execute(handler, new LocalSolrQueryRequest(core, params), rsp);
|
||||
assertEquals(rsp.getException().getMessage(), "Input stream list was null for admin file write operation.");
|
||||
|
||||
params = params("op", "write", "stream.body", "Testing rewrite of schema.xml file.");
|
||||
core.execute(handler, new LocalSolrQueryRequest(core, params), rsp);
|
||||
assertEquals(rsp.getException().getMessage(), "No file name specified for write operation.");
|
||||
|
||||
|
||||
params = params("op", "write", "file", "bogus.txt");
|
||||
core.execute(handler, new LocalSolrQueryRequest(core, params), rsp);
|
||||
assertEquals(rsp.getException().getMessage(), "Can not access: bogus.txt");
|
||||
|
||||
ArrayList<ContentStream> streams = new ArrayList<ContentStream>( 2 );
|
||||
streams.add( new ContentStreamBase.StringStream( "Testing rewrite of schema.xml file." ) );
|
||||
//streams.add( new ContentStreamBase.StringStream( "there" ) );
|
||||
|
||||
params = params("op", "write", "file", "schema.xml", "stream.body", "Testing rewrite of schema.xml file.");
|
||||
LocalSolrQueryRequest locReq = new LocalSolrQueryRequest(core, params);
|
||||
locReq.setContentStreams(streams);
|
||||
core.execute(handler, locReq, rsp);
|
||||
|
||||
String contents = FileUtils.readFileToString(new File(core.getCoreDescriptor().getInstanceDir(), "conf/schema.xml"));
|
||||
assertEquals("Schema contents should have changed!", "Testing rewrite of schema.xml file.", contents);
|
||||
|
||||
streams.add(new ContentStreamBase.StringStream("This should barf"));
|
||||
locReq = new LocalSolrQueryRequest(core, params);
|
||||
locReq.setContentStreams(streams);
|
||||
core.execute(handler, locReq, rsp);
|
||||
assertEquals(rsp.getException().getMessage(), "More than one input stream was found for admin file write operation.");
|
||||
|
||||
streams.clear();
|
||||
streams.add(new ContentStreamBase.StringStream("Some bogus stuff for a test."));
|
||||
params = params("op", "write", "file", "velocity/test.vm");
|
||||
locReq = new LocalSolrQueryRequest(core, params);
|
||||
locReq.setContentStreams(streams);
|
||||
core.execute(handler, locReq, rsp);
|
||||
contents = FileUtils.readFileToString(new File(core.getCoreDescriptor().getInstanceDir(),
|
||||
"conf/velocity/test.vm"));
|
||||
assertEquals("Schema contents should have changed!", "Some bogus stuff for a test.", contents);
|
||||
|
||||
core.close();
|
||||
} finally {
|
||||
cc.shutdown();
|
||||
if (solrHomeDirectory.exists()) {
|
||||
FileUtils.deleteDirectory(solrHomeDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1116,7 +1116,9 @@
|
||||
<requestHandler name="/admin/file" class="solr.admin.ShowFileRequestHandler" >
|
||||
-->
|
||||
<!-- If you wish to hide files under ${solr.home}/conf, explicitly
|
||||
register the ShowFileRequestHandler using:
|
||||
register the ShowFileRequestHandler using the definition below.
|
||||
NOTE: The glob pattern ('*') is the only pattern supported at present, *.xml will
|
||||
not exclude all files ending in '.xml'. Use it to exclude _all_ updates
|
||||
-->
|
||||
<!--
|
||||
<requestHandler name="/admin/file"
|
||||
@ -1124,6 +1126,7 @@
|
||||
<lst name="invariants">
|
||||
<str name="hidden">synonyms.txt</str>
|
||||
<str name="hidden">anotherfile.txt</str>
|
||||
<str name="hidden">*</str>
|
||||
</lst>
|
||||
</requestHandler>
|
||||
-->
|
||||
|
@ -1662,11 +1662,18 @@ public abstract class SolrTestCaseJ4 extends LuceneTestCase {
|
||||
// the stock files in there. Seems to be indicated for some tests when we remove the default, hard-coded
|
||||
// solr.xml from being automatically synthesized from SolrConfigXmlOld.DEFAULT_SOLR_XML.
|
||||
public static void copySolrHomeToTemp(File dstRoot, String collection) throws IOException {
|
||||
copySolrHomeToTemp(dstRoot, collection, false);
|
||||
}
|
||||
public static void copySolrHomeToTemp(File dstRoot, String collection, boolean newStyle) throws IOException {
|
||||
if (!dstRoot.exists()) {
|
||||
assertTrue("Failed to make subdirectory ", dstRoot.mkdirs());
|
||||
}
|
||||
|
||||
FileUtils.copyFile(new File(SolrTestCaseJ4.TEST_HOME(), "solr.xml"), new File(dstRoot, "solr.xml"));
|
||||
if (newStyle) {
|
||||
FileUtils.copyFile(new File(SolrTestCaseJ4.TEST_HOME(), "solr-no-core.xml"), new File(dstRoot, "solr.xml"));
|
||||
} else {
|
||||
FileUtils.copyFile(new File(SolrTestCaseJ4.TEST_HOME(), "solr.xml"), new File(dstRoot, "solr.xml"));
|
||||
}
|
||||
|
||||
File subHome = new File(dstRoot, collection + File.separator + "conf");
|
||||
String top = SolrTestCaseJ4.TEST_HOME() + "/collection1/conf";
|
||||
|
Loading…
x
Reference in New Issue
Block a user