You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ho...@apache.org on 2020/11/02 20:24:38 UTC
[lucene-solr] branch branch_8x updated: SOLR-14907: Adding V2 API
for ConfigSet Upload. (#1996)
This is an automated email from the ASF dual-hosted git repository.
houston pushed a commit to branch branch_8x
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git
The following commit(s) were added to refs/heads/branch_8x by this push:
new 655e019 SOLR-14907: Adding V2 API for ConfigSet Upload. (#1996)
655e019 is described below
commit 655e019a35b24555734cbdef665b04d01ce9647d
Author: Houston Putman <ho...@apache.org>
AuthorDate: Mon Nov 2 14:06:45 2020 -0500
SOLR-14907: Adding V2 API for ConfigSet Upload. (#1996)
---
solr/CHANGES.txt | 3 +-
.../java/org/apache/solr/handler/ClusterAPI.java | 32 +++
.../solr/handler/admin/ConfigSetsHandler.java | 20 +-
.../org/apache/solr/cloud/TestConfigSetsAPI.java | 274 +++++++++++++++------
solr/solr-ref-guide/src/configsets-api.adoc | 73 +++++-
5 files changed, 315 insertions(+), 87 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 0fe9149..3c66082 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -10,7 +10,8 @@ Consult the LUCENE_CHANGES.txt file for additional, low level, changes in this r
New Features
---------------------
-(No changes)
+
+* SOLR-14907: Add v2 API for configSet upload, including single-file insertion. (Houston Putman)
Improvements
---------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
index ca16ede..945e4c7 100644
--- a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
@@ -44,6 +44,7 @@ import org.apache.solr.response.SolrQueryResponse;
import static org.apache.solr.client.solrj.SolrRequest.METHOD.DELETE;
import static org.apache.solr.client.solrj.SolrRequest.METHOD.GET;
import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST;
+import static org.apache.solr.client.solrj.SolrRequest.METHOD.PUT;
import static org.apache.solr.cloud.api.collections.OverseerCollectionMessageHandler.REQUESTID;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDROLE;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.CLUSTERPROP;
@@ -129,6 +130,37 @@ public class ClusterAPI {
}
+ @EndPoint(method = PUT,
+ path = "/cluster/configs/{name}",
+ permission = CONFIG_EDIT_PERM
+ )
+ public void uploadConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
+ req = wrapParams(req,
+ "action", ConfigSetParams.ConfigSetAction.UPLOAD.toString(),
+ CommonParams.NAME, req.getPathTemplateValues().get("name"),
+ ConfigSetParams.OVERWRITE, true,
+ ConfigSetParams.CLEANUP, false);
+ configSetsHandler.handleRequestBody(req, rsp);
+ }
+
+ @EndPoint(method = PUT,
+ path = "/cluster/configs/{name}/*",
+ permission = CONFIG_EDIT_PERM
+ )
+ public void insertIntoConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
+ String path = req.getPathTemplateValues().get("*");
+ if (path == null || path.isEmpty()) {
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "In order to insert a file in a configSet, a filePath must be provided in the url after the name of the configSet.");
+ }
+ req = wrapParams(req,
+ "action", ConfigSetParams.ConfigSetAction.UPLOAD.toString(),
+ CommonParams.NAME, req.getPathTemplateValues().get("name"),
+ ConfigSetParams.FILE_PATH, path,
+ ConfigSetParams.OVERWRITE, true,
+ ConfigSetParams.CLEANUP, false);
+ configSetsHandler.handleRequestBody(req, rsp);
+ }
+
@SuppressWarnings({"rawtypes"})
public static SolrQueryRequest wrapParams(SolrQueryRequest req, Object... def) {
Map m = Utils.makeMap(def);
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/ConfigSetsHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/ConfigSetsHandler.java
index 736aeb7..cb6d2a3 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/ConfigSetsHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/ConfigSetsHandler.java
@@ -68,6 +68,7 @@ import static org.apache.solr.common.params.CommonParams.NAME;
import static org.apache.solr.common.params.ConfigSetParams.ConfigSetAction.CREATE;
import static org.apache.solr.common.params.ConfigSetParams.ConfigSetAction.DELETE;
import static org.apache.solr.common.params.ConfigSetParams.ConfigSetAction.LIST;
+import static org.apache.solr.common.params.ConfigSetParams.ConfigSetAction.UPLOAD;
/**
* A {@link org.apache.solr.request.SolrRequestHandler} for ConfigSets API requests.
@@ -193,10 +194,10 @@ public class ConfigSetsHandler extends RequestHandlerBase implements PermissionN
fixedSingleFilePath = fixedSingleFilePath.substring(1);
}
if (fixedSingleFilePath.isEmpty()) {
- throw new SolrException(ErrorCode.BAD_REQUEST, "The filePath provided for upload, '" + singleFilePath + "', is not valid.");
+ throw new SolrException(ErrorCode.BAD_REQUEST, "The file path provided for upload, '" + singleFilePath + "', is not valid.");
} else if (cleanup) {
// Cleanup is not allowed while using singleFilePath upload
- throw new SolrException(ErrorCode.BAD_REQUEST, "ConfigSet uploads do not allow cleanup=true when filePath is used.");
+ throw new SolrException(ErrorCode.BAD_REQUEST, "ConfigSet uploads do not allow cleanup=true when file path is used.");
} else {
try {
// Create a node for the configuration in zookeeper
@@ -206,7 +207,7 @@ public class ConfigSetsHandler extends RequestHandlerBase implements PermissionN
zkClient.makePath(filePathInZk, IOUtils.toByteArray(inputStream), CreateMode.PERSISTENT, null, !allowOverwrite, true);
} catch(KeeperException.NodeExistsException nodeExistsException) {
throw new SolrException(ErrorCode.BAD_REQUEST,
- "The path " + singleFilePath + " for configSet " + configSetName + " already exists. In order to overwrite, provide overwrite=true.");
+ "The path " + singleFilePath + " for configSet " + configSetName + " already exists. In order to overwrite, provide overwrite=true or use an HTTP PUT with the V2 API.");
}
}
return;
@@ -230,7 +231,9 @@ public class ConfigSetsHandler extends RequestHandlerBase implements PermissionN
ZipInputStream zis = new ZipInputStream(inputStream, StandardCharsets.UTF_8);
ZipEntry zipEntry = null;
+ boolean hasEntry = false;
while ((zipEntry = zis.getNextEntry()) != null) {
+ hasEntry = true;
String filePathInZk = configPathInZk + "/" + zipEntry.getName();
if (filePathInZk.endsWith("/")) {
filesToDelete.remove(filePathInZk.substring(0, filePathInZk.length() -1));
@@ -245,6 +248,10 @@ public class ConfigSetsHandler extends RequestHandlerBase implements PermissionN
}
}
zis.close();
+ if (!hasEntry) {
+ throw new SolrException(ErrorCode.BAD_REQUEST,
+ "Either empty zipped data, or non-zipped data was uploaded. In order to upload a configSet, you must zip a non-empty directory to upload.");
+ }
deleteUnusedFiles(zkClient, filesToDelete);
// If the request is doing a full trusted overwrite of an untrusted configSet (overwrite=true, cleanup=true), then trust the configSet.
@@ -400,6 +407,13 @@ public class ConfigSetsHandler extends RequestHandlerBase implements PermissionN
}
public enum ConfigSetOperation {
+ UPLOAD_OP(UPLOAD) {
+ @Override
+ public Map<String, Object> call(SolrQueryRequest req, SolrQueryResponse rsp, ConfigSetsHandler h) throws Exception {
+ h.handleConfigUploadRequest(req, rsp);
+ return null;
+ }
+ },
CREATE_OP(CREATE) {
@Override
public Map<String, Object> call(SolrQueryRequest req, SolrQueryResponse rsp, ConfigSetsHandler h) throws Exception {
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java b/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java
index c84301f..60ae2c2 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java
@@ -54,7 +54,9 @@ import com.google.common.collect.ImmutableMap;
import org.apache.commons.io.FileUtils;
import org.apache.http.HttpEntity;
import org.apache.http.auth.BasicUserPrincipal;
+import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;
@@ -344,7 +346,7 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
@SuppressWarnings({"rawtypes"})
Map map = postDataAndGetResponse(cluster.getSolrClient(),
cluster.getJettySolrRunners().get(0).getBaseUrl().toString()
- + "/admin/configs?action=UPLOAD", emptyData, null);
+ + "/admin/configs?action=UPLOAD", emptyData, null, false);
assertNotNull(map);
unIgnoreException("The configuration name should be provided");
long statusCode = (long) getObjectByPath(map, false,
@@ -365,7 +367,7 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
ignoreException("already exists");
map = postDataAndGetResponse(cluster.getSolrClient(),
cluster.getJettySolrRunners().get(0).getBaseUrl().toString()
- + "/admin/configs?action=UPLOAD&name=myconf", emptyData, null);
+ + "/admin/configs?action=UPLOAD&name=myconf", emptyData, null, false);
assertNotNull(map);
unIgnoreException("already exists`");
statusCode = (long) getObjectByPath(map, false,
@@ -381,7 +383,16 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
}
@Test
- public void testUploadDisabled() throws Exception {
+ public void testUploadDisabledV1() throws Exception {
+ testUploadDisabled(false);
+ }
+
+ @Test
+ public void testUploadDisabledV2() throws Exception {
+ testUploadDisabled(true);
+ }
+
+ public void testUploadDisabled(boolean v2) throws Exception {
try (SolrZkClient zkClient = new SolrZkClient(cluster.getZkServer().getZkAddress(),
AbstractZkTestCase.TIMEOUT, 45000, null)) {
@@ -389,7 +400,7 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
for (boolean enabled: new boolean[] {true, false}) {
System.setProperty("configset.upload.enabled", String.valueOf(enabled));
try {
- long statusCode = uploadConfigSet("regular", "test-enabled-is-" + enabled, null, zkClient);
+ long statusCode = uploadConfigSet("regular", "test-enabled-is-" + enabled, null, zkClient, v2);
assertEquals("ConfigSet upload enabling/disabling not working as expected for enabled=" + enabled + ".",
enabled? 0l: 400l, statusCode);
} finally {
@@ -401,20 +412,29 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
}
@Test
- public void testOverwrite() throws Exception {
+ public void testOverwriteV1() throws Exception {
+ testOverwrite(false);
+ }
+
+ @Test
+ public void testOverwriteV2() throws Exception {
+ testOverwrite(true);
+ }
+
+ public void testOverwrite(boolean v2) throws Exception {
String configsetName = "regular";
- String configsetSuffix = "testOverwrite-1";
+ String configsetSuffix = "testOverwrite-1-" + v2;
uploadConfigSetWithAssertions(configsetName, configsetSuffix, null);
try (SolrZkClient zkClient = new SolrZkClient(cluster.getZkServer().getZkAddress(),
AbstractZkTestCase.TIMEOUT, 45000, null)) {
int solrconfigZkVersion = getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml");
ignoreException("The configuration regulartestOverwrite-1 already exists in zookeeper");
assertEquals("Can't overwrite an existing configset unless the overwrite parameter is set",
- 400, uploadConfigSet(configsetName, configsetSuffix, null, zkClient, false, false));
+ 400, uploadConfigSet(configsetName, configsetSuffix, null, false, false, v2));
unIgnoreException("The configuration regulartestOverwrite-1 already exists in zookeeper");
assertEquals("Expecting version to remain equal",
solrconfigZkVersion, getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml"));
- assertEquals(0, uploadConfigSet(configsetName, configsetSuffix, null, zkClient, true, false));
+ assertEquals(0, uploadConfigSet(configsetName, configsetSuffix, null, true, false, v2));
assertTrue("Expecting version bump",
solrconfigZkVersion < getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml"));
}
@@ -422,25 +442,35 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
}
@Test
- public void testOverwriteWithCleanup() throws Exception {
+ public void testOverwriteWithCleanupV1() throws Exception {
+ testOverwriteWithCleanup(false);
+ }
+
+ @Test
+ public void testOverwriteWithCleanupV2() throws Exception {
+ testOverwriteWithCleanup(true);
+ }
+
+ public void testOverwriteWithCleanup(boolean v2) throws Exception {
String configsetName = "regular";
- String configsetSuffix = "testOverwriteWithCleanup-1";
+ String configsetSuffix = "testOverwriteWithCleanup-1-" + v2;
uploadConfigSetWithAssertions(configsetName, configsetSuffix, null);
try (SolrZkClient zkClient = new SolrZkClient(cluster.getZkServer().getZkAddress(),
AbstractZkTestCase.TIMEOUT, 45000, null)) {
+ String configPath = "/configs/" + configsetName + configsetSuffix;
List<String> extraFiles = Arrays.asList(
- "/configs/regulartestOverwriteWithCleanup-1/foo1",
- "/configs/regulartestOverwriteWithCleanup-1/foo2",
- "/configs/regulartestOverwriteWithCleanup-1/foo2/1",
- "/configs/regulartestOverwriteWithCleanup-1/foo2/2");
+ configPath + "/foo1",
+ configPath + "/foo2",
+ configPath + "/foo2/1",
+ configPath + "/foo2/2");
for (String f : extraFiles) {
zkClient.makePath(f, true);
}
- assertEquals(0, uploadConfigSet(configsetName, configsetSuffix, null, zkClient, true, false));
+ assertEquals(0, uploadConfigSet(configsetName, configsetSuffix, null, true, false, v2));
for (String f : extraFiles) {
assertTrue("Expecting file " + f + " to exist in ConfigSet but it's gone", zkClient.exists(f, true));
}
- assertEquals(0, uploadConfigSet(configsetName, configsetSuffix, null, zkClient, true, true));
+ assertEquals(0, uploadConfigSet(configsetName, configsetSuffix, null, true, true, v2));
for (String f : extraFiles) {
assertFalse("Expecting file " + f + " to be deleted from ConfigSet but it wasn't", zkClient.exists(f, true));
}
@@ -449,38 +479,49 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
}
@Test
- public void testOverwriteWithTrust() throws Exception {
+ public void testOverwriteWithTrustV1() throws Exception {
+ testOverwriteWithTrust(false);
+ }
+
+ @Test
+ public void testOverwriteWithTrustV2() throws Exception {
+ testOverwriteWithTrust(true);
+ }
+
+ public void testOverwriteWithTrust(boolean v2) throws Exception {
String configsetName = "regular";
- String configsetSuffix = "testOverwriteWithTrust-1";
+ String configsetSuffix = "testOverwriteWithTrust-1-" + v2;
uploadConfigSetWithAssertions(configsetName, configsetSuffix, null);
try (SolrZkClient zkClient = new SolrZkClient(cluster.getZkServer().getZkAddress(),
AbstractZkTestCase.TIMEOUT, 45000, null)) {
assertFalse(isTrusted(zkClient, configsetName, configsetSuffix));
int solrconfigZkVersion = getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml");
// Was untrusted, overwrite with untrusted
- assertEquals(0, uploadConfigSet(configsetName, configsetSuffix, null, zkClient, true, false));
+ assertEquals(0, uploadConfigSet(configsetName, configsetSuffix, null, true, false, v2));
assertTrue("Expecting version bump",
solrconfigZkVersion < getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml"));
assertFalse(isTrusted(zkClient, configsetName, configsetSuffix));
solrconfigZkVersion = getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml");
// Was untrusted, overwrite with trusted but no cleanup
- assertEquals(0, uploadConfigSet(configsetName, configsetSuffix, "solr", zkClient, true, false));
+ assertEquals(0, uploadConfigSet(configsetName, configsetSuffix, "solr", true, false, v2));
assertTrue("Expecting version bump",
solrconfigZkVersion < getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml"));
assertFalse(isTrusted(zkClient, configsetName, configsetSuffix));
solrconfigZkVersion = getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml");
// Was untrusted, overwrite with trusted with cleanup but fail on unzipping.
- // Should not set trusted=true
- assertEquals(500, uploadBadConfigSet(configsetName, configsetSuffix, "solr", zkClient, true, true));
+ // Should not set trusted=true in configSet
+ ignoreException("Either empty zipped data, or non-zipped data was passed. In order to upload a configSet, you must zip a non-empty directory to upload.");
+ assertEquals(400, uploadBadConfigSet(configsetName, configsetSuffix, "solr", true, true, v2));
assertEquals("Expecting version bump",
solrconfigZkVersion, getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml"));
assertFalse(isTrusted(zkClient, configsetName, configsetSuffix));
solrconfigZkVersion = getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml");
+ ignoreException("Either empty zipped data, or non-zipped data was passed. In order to upload a configSet, you must zip a non-empty directory to upload.");
// Was untrusted, overwrite with trusted with cleanup
- assertEquals(0, uploadConfigSet(configsetName, configsetSuffix, "solr", zkClient, true, true));
+ assertEquals(0, uploadConfigSet(configsetName, configsetSuffix, "solr", true, true, v2));
assertTrue("Expecting version bump",
solrconfigZkVersion < getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml"));
assertTrue(isTrusted(zkClient, configsetName, configsetSuffix));
@@ -489,7 +530,7 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
// Was trusted, try to overwrite with untrusted with no cleanup
ignoreException("Trying to make an unstrusted ConfigSet update on a trusted configSet");
assertEquals("Can't upload a trusted configset with an untrusted request",
- 400, uploadConfigSet(configsetName, configsetSuffix, null, zkClient, true, false));
+ 400, uploadConfigSet(configsetName, configsetSuffix, null, true, false, v2));
assertEquals("Expecting version to remain equal",
solrconfigZkVersion, getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml"));
assertTrue(isTrusted(zkClient, configsetName, configsetSuffix));
@@ -497,21 +538,21 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
// Was trusted, try to overwrite with untrusted with cleanup
ignoreException("Trying to make an unstrusted ConfigSet update on a trusted configSet");
assertEquals("Can't upload a trusted configset with an untrusted request",
- 400, uploadConfigSet(configsetName, configsetSuffix, null, zkClient, true, true));
+ 400, uploadConfigSet(configsetName, configsetSuffix, null, true, true, v2));
assertEquals("Expecting version to remain equal",
solrconfigZkVersion, getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml"));
assertTrue(isTrusted(zkClient, configsetName, configsetSuffix));
unIgnoreException("Trying to make an unstrusted ConfigSet update on a trusted configSet");
// Was trusted, overwrite with trusted no cleanup
- assertEquals(0, uploadConfigSet(configsetName, configsetSuffix, "solr", zkClient, true, false));
+ assertEquals(0, uploadConfigSet(configsetName, configsetSuffix, "solr", true, false, v2));
assertTrue("Expecting version bump",
solrconfigZkVersion < getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml"));
assertTrue(isTrusted(zkClient, configsetName, configsetSuffix));
solrconfigZkVersion = getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml");
// Was trusted, overwrite with trusted with cleanup
- assertEquals(0, uploadConfigSet(configsetName, configsetSuffix, "solr", zkClient, true, true));
+ assertEquals(0, uploadConfigSet(configsetName, configsetSuffix, "solr", true, true, v2));
assertTrue("Expecting version bump",
solrconfigZkVersion < getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml"));
assertTrue(isTrusted(zkClient, configsetName, configsetSuffix));
@@ -520,47 +561,74 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
}
@Test
- public void testSingleFileOverwrite() throws Exception {
+ public void testSingleFileOverwriteV1() throws Exception {
+ testSingleFileOverwrite(false);
+ }
+
+ @Test
+ public void testSingleFileOverwriteV2() throws Exception {
+ testSingleFileOverwrite(true);
+ }
+
+ public void testSingleFileOverwrite(boolean v2) throws Exception {
String configsetName = "regular";
- String configsetSuffix = "testSinglePathOverwrite-1";
+ String configsetSuffix = "testSinglePathOverwrite-1-" + v2;
uploadConfigSetWithAssertions(configsetName, configsetSuffix, null);
try (SolrZkClient zkClient = new SolrZkClient(cluster.getZkServer().getZkAddress(),
AbstractZkTestCase.TIMEOUT, 45000, null)) {
int solrconfigZkVersion = getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml");
ignoreException("The configuration regulartestOverwrite-1 already exists in zookeeper");
assertEquals("Can't overwrite an existing configset unless the overwrite parameter is set",
- 400, uploadSingleConfigSetFile(configsetName, configsetSuffix, null, zkClient, "solr/configsets/upload/regular/solrconfig.xml", "solrconfig.xml", false, false));
+ 400, uploadSingleConfigSetFile(configsetName, configsetSuffix, null, "solr/configsets/upload/regular/solrconfig.xml", "solrconfig.xml", false, false, v2));
unIgnoreException("The configuration regulartestOverwrite-1 already exists in zookeeper");
assertEquals("Expecting version to remain equal",
solrconfigZkVersion, getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml"));
- assertEquals(0, uploadSingleConfigSetFile(configsetName, configsetSuffix, null, zkClient, "solr/configsets/upload/regular/solrconfig.xml", "solrconfig.xml", true, false));
+ assertEquals(0, uploadSingleConfigSetFile(configsetName, configsetSuffix, null, "solr/configsets/upload/regular/solrconfig.xml", "solrconfig.xml", true, false, v2));
assertTrue("Expecting version bump",
solrconfigZkVersion < getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "solrconfig.xml"));
}
}
@Test
- public void testNewSingleFile() throws Exception {
+ public void testNewSingleFileV1() throws Exception {
+ testNewSingleFile(false);
+ }
+
+ @Test
+ public void testNewSingleFileV2() throws Exception {
+ testNewSingleFile(true);
+ }
+
+ public void testNewSingleFile(boolean v2) throws Exception {
String configsetName = "regular";
- String configsetSuffix = "testSinglePathNew-1";
+ String configsetSuffix = "testSinglePathNew-1-" + v2;
uploadConfigSetWithAssertions(configsetName, configsetSuffix, null);
try (SolrZkClient zkClient = new SolrZkClient(cluster.getZkServer().getZkAddress(),
AbstractZkTestCase.TIMEOUT, 45000, null)) {
- assertEquals(0, uploadSingleConfigSetFile(configsetName, configsetSuffix, null, zkClient, "solr/configsets/upload/regular/solrconfig.xml", "/test/upload/path/solrconfig.xml", false, false));
+ assertEquals(0, uploadSingleConfigSetFile(configsetName, configsetSuffix, null, "solr/configsets/upload/regular/solrconfig.xml", "/test/upload/path/solrconfig.xml", false, false, v2));
assertEquals("Expecting first version of new file", 0, getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "test/upload/path/solrconfig.xml"));
assertConfigsetFiles(configsetName, configsetSuffix, zkClient);
}
}
@Test
- public void testSingleWithCleanup() throws Exception {
+ public void testSingleWithCleanupV1() throws Exception {
+ testSingleWithCleanup(false);
+ }
+
+ @Test
+ public void testSingleWithCleanupV2() throws Exception {
+ testSingleWithCleanup(true);
+ }
+
+ public void testSingleWithCleanup(boolean v2) throws Exception {
String configsetName = "regular";
- String configsetSuffix = "testSinglePathCleanup-1";
+ String configsetSuffix = "testSinglePathCleanup-1-" + v2;
uploadConfigSetWithAssertions(configsetName, configsetSuffix, null);
try (SolrZkClient zkClient = new SolrZkClient(cluster.getZkServer().getZkAddress(),
AbstractZkTestCase.TIMEOUT, 45000, null)) {
ignoreException("ConfigSet uploads do not allow cleanup=true when filePath is used.");
- assertEquals(400, uploadSingleConfigSetFile(configsetName, configsetSuffix, null, zkClient, "solr/configsets/upload/regular/solrconfig.xml", "/test/upload/path/solrconfig.xml", false, true));
+ assertEquals(400, uploadSingleConfigSetFile(configsetName, configsetSuffix, null, "solr/configsets/upload/regular/solrconfig.xml", "/test/upload/path/solrconfig.xml", true, true, v2));
assertFalse("New file should not exist, since the trust check did not succeed.", zkClient.exists("/configs/"+configsetName+configsetSuffix+"/test/upload/path/solrconfig.xml", true));
assertConfigsetFiles(configsetName, configsetSuffix, zkClient);
unIgnoreException("ConfigSet uploads do not allow cleanup=true when filePath is used.");
@@ -568,20 +636,29 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
}
@Test
- public void testSingleFileTrusted() throws Exception {
+ public void testSingleFileTrustedV1() throws Exception {
+ testSingleFileTrusted(false);
+ }
+
+ @Test
+ public void testSingleFileTrustedV2() throws Exception {
+ testSingleFileTrusted(true);
+ }
+
+ public void testSingleFileTrusted(boolean v2) throws Exception {
String configsetName = "regular";
- String configsetSuffix = "testSinglePathTrusted-1";
+ String configsetSuffix = "testSinglePathTrusted-1-" + v2;
uploadConfigSetWithAssertions(configsetName, configsetSuffix, "solr");
try (SolrZkClient zkClient = new SolrZkClient(cluster.getZkServer().getZkAddress(),
AbstractZkTestCase.TIMEOUT, 45000, null)) {
- assertEquals(0, uploadSingleConfigSetFile(configsetName, configsetSuffix, "solr", zkClient, "solr/configsets/upload/regular/solrconfig.xml", "/test/upload/path/solrconfig.xml", true, false));
+ assertEquals(0, uploadSingleConfigSetFile(configsetName, configsetSuffix, "solr", "solr/configsets/upload/regular/solrconfig.xml", "/test/upload/path/solrconfig.xml", true, false, v2));
assertEquals("Expecting first version of new file", 0, getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "test/upload/path/solrconfig.xml"));
assertTrue(isTrusted(zkClient, configsetName, configsetSuffix));
assertConfigsetFiles(configsetName, configsetSuffix, zkClient);
ignoreException("Trying to make an unstrusted ConfigSet update on a trusted configSet");
assertEquals("Can't upload a trusted configset with an untrusted request",
- 400, uploadSingleConfigSetFile(configsetName, configsetSuffix, null, zkClient, "solr/configsets/upload/regular/solrconfig.xml", "/test/different/path/solrconfig.xml", true, false));
+ 400, uploadSingleConfigSetFile(configsetName, configsetSuffix, null, "solr/configsets/upload/regular/solrconfig.xml", "/test/different/path/solrconfig.xml", true, false, v2));
assertFalse("New file should not exist, since the trust check did not succeed.", zkClient.exists("/configs/"+configsetName+configsetSuffix+"/test/different/path/solrconfig.xml", true));
assertTrue(isTrusted(zkClient, configsetName, configsetSuffix));
assertConfigsetFiles(configsetName, configsetSuffix, zkClient);
@@ -590,7 +667,7 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
ignoreException("Trying to make an unstrusted ConfigSet update on a trusted configSet");
int extraFileZkVersion = getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "test/upload/path/solrconfig.xml");
assertEquals("Can't upload a trusted configset with an untrusted request",
- 400, uploadSingleConfigSetFile(configsetName, configsetSuffix, null, zkClient, "solr/configsets/upload/regular/solrconfig.xml", "/test/upload/path/solrconfig.xml", true, false));
+ 400, uploadSingleConfigSetFile(configsetName, configsetSuffix, null, "solr/configsets/upload/regular/solrconfig.xml", "/test/upload/path/solrconfig.xml", true, false, v2));
assertEquals("Expecting version to remain equal",
extraFileZkVersion, getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "test/upload/path/solrconfig.xml"));
assertTrue(isTrusted(zkClient, configsetName, configsetSuffix));
@@ -600,27 +677,36 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
}
@Test
- public void testSingleFileUntrusted() throws Exception {
+ public void testSingleFileUntrustedV1() throws Exception {
+ testSingleFileUntrusted(false);
+ }
+
+ @Test
+ public void testSingleFileUntrustedV2() throws Exception {
+ testSingleFileUntrusted(true);
+ }
+
+ public void testSingleFileUntrusted(boolean v2) throws Exception {
String configsetName = "regular";
- String configsetSuffix = "testSinglePathUntrusted-1";
+ String configsetSuffix = "testSinglePathUntrusted-1-" + v2;
uploadConfigSetWithAssertions(configsetName, configsetSuffix, null);
try (SolrZkClient zkClient = new SolrZkClient(cluster.getZkServer().getZkAddress(),
AbstractZkTestCase.TIMEOUT, 45000, null)) {
// New file with trusted request
- assertEquals(0, uploadSingleConfigSetFile(configsetName, configsetSuffix, "solr", zkClient, "solr/configsets/upload/regular/solrconfig.xml", "/test/upload/path/solrconfig.xml", false, false));
+ assertEquals(0, uploadSingleConfigSetFile(configsetName, configsetSuffix, "solr", "solr/configsets/upload/regular/solrconfig.xml", "/test/upload/path/solrconfig.xml", false, false, v2));
assertEquals("Expecting first version of new file", 0, getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "test/upload/path/solrconfig.xml"));
assertFalse(isTrusted(zkClient, configsetName, configsetSuffix));
assertConfigsetFiles(configsetName, configsetSuffix, zkClient);
// New file with untrusted request
- assertEquals(0, uploadSingleConfigSetFile(configsetName, configsetSuffix, null, zkClient, "solr/configsets/upload/regular/solrconfig.xml", "/test/different/path/solrconfig.xml", false, false));
+ assertEquals(0, uploadSingleConfigSetFile(configsetName, configsetSuffix, null, "solr/configsets/upload/regular/solrconfig.xml", "/test/different/path/solrconfig.xml", false, false, v2));
assertEquals("Expecting first version of new file", 0, getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "test/different/path/solrconfig.xml"));
assertFalse(isTrusted(zkClient, configsetName, configsetSuffix));
assertConfigsetFiles(configsetName, configsetSuffix, zkClient);
// Overwrite with trusted request
int extraFileZkVersion = getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "test/different/path/solrconfig.xml");
- assertEquals(0, uploadSingleConfigSetFile(configsetName, configsetSuffix, "solr", zkClient, "solr/configsets/upload/regular/solrconfig.xml", "/test/different/path/solrconfig.xml", true, false));
+ assertEquals(0, uploadSingleConfigSetFile(configsetName, configsetSuffix, "solr", "solr/configsets/upload/regular/solrconfig.xml", "/test/different/path/solrconfig.xml", true, false, v2));
assertTrue("Expecting version bump",
extraFileZkVersion < getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "test/different/path/solrconfig.xml"));
assertFalse(isTrusted(zkClient, configsetName, configsetSuffix));
@@ -628,7 +714,7 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
// Overwrite with untrusted request
extraFileZkVersion = getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "test/upload/path/solrconfig.xml");
- assertEquals(0, uploadSingleConfigSetFile(configsetName, configsetSuffix, null, zkClient, "solr/configsets/upload/regular/solrconfig.xml", "/test/upload/path/solrconfig.xml", true, false));
+ assertEquals(0, uploadSingleConfigSetFile(configsetName, configsetSuffix, null, "solr/configsets/upload/regular/solrconfig.xml", "/test/upload/path/solrconfig.xml", true, false, v2));
assertTrue("Expecting version bump",
extraFileZkVersion < getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "test/upload/path/solrconfig.xml"));
assertFalse(isTrusted(zkClient, configsetName, configsetSuffix));
@@ -637,7 +723,7 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
// Make sure that cleanup flag does not result in configSet being trusted.
ignoreException("ConfigSet uploads do not allow cleanup=true when filePath is used.");
extraFileZkVersion = getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "test/different/path/solrconfig.xml");
- assertEquals(400, uploadSingleConfigSetFile(configsetName, configsetSuffix, "solr", zkClient, "solr/configsets/upload/regular/solrconfig.xml", "/test/different/path/solrconfig.xml", true, true));
+ assertEquals(400, uploadSingleConfigSetFile(configsetName, configsetSuffix, "solr", "solr/configsets/upload/regular/solrconfig.xml", "/test/different/path/solrconfig.xml", true, true, v2));
assertEquals("Expecting version to stay the same",
extraFileZkVersion, getConfigZNodeVersion(zkClient, configsetName, configsetSuffix, "test/different/path/solrconfig.xml"));
assertFalse("The cleanup=true flag allowed for trust overwriting in a filePath upload.", isTrusted(zkClient, configsetName, configsetSuffix));
@@ -647,14 +733,23 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
}
@Test
- public void testSingleFileNewConfig() throws Exception {
+ public void testSingleFileNewConfigV1() throws Exception {
+ testSingleFileNewConfig(false);
+ }
+
+ @Test
+ public void testSingleFileNewConfigV2() throws Exception {
+ testSingleFileNewConfig(true);
+ }
+
+ public void testSingleFileNewConfig(boolean v2) throws Exception {
String configsetName = "regular";
- String configsetSuffixTrusted = "testSinglePathNewConfig-1";
- String configsetSuffixUntrusted = "testSinglePathNewConfig-2";
+ String configsetSuffixTrusted = "testSinglePathNewConfig-1-" + v2;
+ String configsetSuffixUntrusted = "testSinglePathNewConfig-2-" + v2;
try (SolrZkClient zkClient = new SolrZkClient(cluster.getZkServer().getZkAddress(),
AbstractZkTestCase.TIMEOUT, 45000, null)) {
// New file with trusted request
- assertEquals(0, uploadSingleConfigSetFile(configsetName, configsetSuffixTrusted, "solr", zkClient, "solr/configsets/upload/regular/solrconfig.xml", "solrconfig.xml", false, false));
+ assertEquals(0, uploadSingleConfigSetFile(configsetName, configsetSuffixTrusted, "solr", "solr/configsets/upload/regular/solrconfig.xml", "solrconfig.xml", false, false, v2));
assertEquals("Expecting first version of new file", 0, getConfigZNodeVersion(zkClient, configsetName, configsetSuffixTrusted, "solrconfig.xml"));
assertTrue(isTrusted(zkClient, configsetName, configsetSuffixTrusted));
List<String> children = zkClient.getChildren(String.format(Locale.ROOT,"/configs/%s%s", configsetName, configsetSuffixTrusted), null, true);
@@ -662,7 +757,7 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
assertEquals("Incorrect file uploaded.", "solrconfig.xml", children.get(0));
// New file with trusted request
- assertEquals(0, uploadSingleConfigSetFile(configsetName, configsetSuffixUntrusted, null, zkClient, "solr/configsets/upload/regular/solrconfig.xml", "solrconfig.xml", false, false));
+ assertEquals(0, uploadSingleConfigSetFile(configsetName, configsetSuffixUntrusted, null, "solr/configsets/upload/regular/solrconfig.xml", "solrconfig.xml", false, false, v2));
assertEquals("Expecting first version of new file", 0, getConfigZNodeVersion(zkClient, configsetName, configsetSuffixUntrusted, "solrconfig.xml"));
assertFalse(isTrusted(zkClient, configsetName, configsetSuffixUntrusted));
children = zkClient.getChildren(String.format(Locale.ROOT,"/configs/%s%s", configsetName, configsetSuffixUntrusted), null, true);
@@ -761,7 +856,7 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
SolrZkClient zkClient = new SolrZkClient(cluster.getZkServer().getZkAddress(),
AbstractZkTestCase.TIMEOUT, 45000, null);
try {
- long statusCode = uploadConfigSet(configSetName, suffix, username, zkClient);
+ long statusCode = uploadConfigSet(configSetName, suffix, username, zkClient, true);
assertEquals(0l, statusCode);
assertConfigsetFiles(configSetName, suffix, zkClient);
} finally {
@@ -785,51 +880,68 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
}
private long uploadConfigSet(String configSetName, String suffix, String username,
- SolrZkClient zkClient) throws IOException {
+ SolrZkClient zkClient, boolean v2) throws IOException {
ZkConfigManager configManager = new ZkConfigManager(zkClient);
assertFalse(configManager.configExists(configSetName + suffix));
- return uploadConfigSet(configSetName, suffix, username, zkClient, false, false);
+ return uploadConfigSet(configSetName, suffix, username, false, false, v2);
}
private long uploadConfigSet(String configSetName, String suffix, String username,
- SolrZkClient zkClient, boolean overwrite, boolean cleanup) throws IOException {
+ boolean overwrite, boolean cleanup, boolean v2) throws IOException {
// Read zipped sample config
ByteBuffer sampleZippedConfig = TestDynamicLoading
.getFileContent(
createTempZipFile("solr/configsets/upload/"+configSetName), false);
- @SuppressWarnings({"rawtypes"})
- Map map = postDataAndGetResponse(cluster.getSolrClient(),
- cluster.getJettySolrRunners().get(0).getBaseUrl().toString() + "/admin/configs?action=UPLOAD&name="+configSetName+suffix + (overwrite? "&overwrite=true" : "") + (cleanup? "&cleanup=true" : ""),
- sampleZippedConfig, username);
- assertNotNull(map);
- long statusCode = (long) getObjectByPath(map, false, Arrays.asList("responseHeader", "status"));
- return statusCode;
+ return uploadGivenConfigSet(sampleZippedConfig, configSetName, suffix, username, overwrite, cleanup, v2);
}
private long uploadBadConfigSet(String configSetName, String suffix, String username,
- SolrZkClient zkClient, boolean overwrite, boolean cleanup) throws IOException {
+ boolean overwrite, boolean cleanup, boolean v2) throws IOException {
// Read single file from sample configs. This should fail the unzipping
ByteBuffer sampleBadZippedFile = TestDynamicLoading.getFileContent(TestDynamicLoading.getFile("solr/configsets/upload/regular/solrconfig.xml").getAbsolutePath(), false);
+ return uploadGivenConfigSet(sampleBadZippedFile, configSetName, suffix, username, overwrite, cleanup, v2);
+ }
+
+ private long uploadGivenConfigSet(ByteBuffer file, String configSetName, String suffix, String username,
+ boolean overwrite, boolean cleanup, boolean v2) throws IOException {
+ String uriEnding;
+ boolean usePut = false;
+ if (v2) {
+ uriEnding = "/api/cluster/configs/" + configSetName+suffix + (!overwrite? "?overwrite=false" : "") + (cleanup? "?cleanup=true" : "");
+ usePut = true;
+ } else {
+ uriEnding = "/solr/admin/configs?action=UPLOAD&name="+configSetName+suffix + (overwrite? "&overwrite=true" : "") + (cleanup? "&cleanup=true" : "");
+ }
+
@SuppressWarnings({"rawtypes"})
Map map = postDataAndGetResponse(cluster.getSolrClient(),
- cluster.getJettySolrRunners().get(0).getBaseUrl().toString() + "/admin/configs?action=UPLOAD&name="+configSetName+suffix + (overwrite? "&overwrite=true" : "") + (cleanup? "&cleanup=true" : ""),
- sampleBadZippedFile, username);
+ cluster.getJettySolrRunners().get(0).getBaseUrl().toString().replace("/solr", "") + uriEnding,
+ file, username, usePut);
assertNotNull(map);
long statusCode = (long) getObjectByPath(map, false, Arrays.asList("responseHeader", "status"));
return statusCode;
}
private long uploadSingleConfigSetFile(String configSetName, String suffix, String username,
- SolrZkClient zkClient, String filePath, String uploadPath, boolean overwrite, boolean cleanup) throws IOException {
+ String filePath, String uploadPath, boolean overwrite, boolean cleanup, boolean v2) throws IOException {
// Read single file from sample configs
ByteBuffer sampleConfigFile = TestDynamicLoading.getFileContent(TestDynamicLoading.getFile(filePath).getAbsolutePath(), false);
+ String uriEnding;
+ boolean usePut = false;
+ if (v2) {
+ uriEnding = "/api/cluster/configs/" + configSetName+suffix + "/" + uploadPath + (!overwrite? "?overwrite=false" : "") + (cleanup? "?cleanup=true" : "");
+ usePut = true;
+ } else {
+ uriEnding = "/solr/admin/configs?action=UPLOAD&name="+configSetName+suffix+"&filePath="+uploadPath + (overwrite? "&overwrite=true" : "") + (cleanup? "&cleanup=true" : "");
+ }
+
@SuppressWarnings({"rawtypes"})
Map map = postDataAndGetResponse(cluster.getSolrClient(),
- cluster.getJettySolrRunners().get(0).getBaseUrl().toString() + "/admin/configs?action=UPLOAD&name="+configSetName+suffix+"&filePath="+uploadPath + (overwrite? "&overwrite=true" : "") + (cleanup? "&cleanup=true" : ""),
- sampleConfigFile, username);
+ cluster.getJettySolrRunners().get(0).getBaseUrl().toString().replace("/solr", "") + uriEnding,
+ sampleConfigFile, username, usePut);
assertNotNull(map);
long statusCode = (long) getObjectByPath(map, false, Arrays.asList("responseHeader", "status"));
return statusCode;
@@ -927,24 +1039,28 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
@SuppressWarnings({"rawtypes"})
public static Map postDataAndGetResponse(CloudSolrClient cloudClient,
- String uri, ByteBuffer bytarr, String username) throws IOException {
- HttpPost httpPost = null;
+ String uri, ByteBuffer bytarr, String username, boolean usePut) throws IOException {
+ HttpEntityEnclosingRequestBase httpRequest = null;
HttpEntity entity;
String response = null;
Map m = null;
try {
- httpPost = new HttpPost(uri);
+ if (usePut) {
+ httpRequest = new HttpPut(uri);
+ } else {
+ httpRequest = new HttpPost(uri);
+ }
if (username != null) {
- httpPost.addHeader(new BasicHeader("user", username));
+ httpRequest.addHeader(new BasicHeader("user", username));
}
- httpPost.setHeader("Content-Type", "application/octet-stream");
- httpPost.setEntity(new ByteArrayEntity(bytarr.array(), bytarr
+ httpRequest.setHeader("Content-Type", "application/octet-stream");
+ httpRequest.setEntity(new ByteArrayEntity(bytarr.array(), bytarr
.arrayOffset(), bytarr.limit()));
log.info("Uploading configset with user {}", username);
- entity = cloudClient.getLbClient().getHttpClient().execute(httpPost)
+ entity = cloudClient.getLbClient().getHttpClient().execute(httpRequest)
.getEntity();
try {
response = EntityUtils.toString(entity, UTF_8);
@@ -954,7 +1070,7 @@ public class TestConfigSetsAPI extends SolrCloudTestCase {
throw new AssertionError(e);
}
} finally {
- httpPost.releaseConnection();
+ httpRequest.releaseConnection();
}
return m;
}
diff --git a/solr/solr-ref-guide/src/configsets-api.adoc b/solr/solr-ref-guide/src/configsets-api.adoc
index 9fc328a..85c3793 100644
--- a/solr/solr-ref-guide/src/configsets-api.adoc
+++ b/solr/solr-ref-guide/src/configsets-api.adoc
@@ -102,21 +102,29 @@ The configset to be created when the upload is complete. This parameter is requi
`overwrite`::
If set to `true`, Solr will overwrite an existing configset with the same name (if false, the request will fail).
If `filePath` is provided, then this option specifies whether the specified file within the configSet should be overwritten if it already exists.
-Default is `false`.
+Default is `false` when using the v1 API, but `true` when using the v2 API.
`cleanup`::
When overwriting an existing configset (`overwrite=true`), this parameter tells Solr to delete the files in ZooKeeper that existed in the old configset but not in the one being uploaded. Default is `false`.
This parameter cannot be set to true when `filePath` is used.
-filePath::
+`filePath`::
This parameter allows the uploading of a single, non-zipped file to the given path under the configSet in ZooKeeper.
This functionality respects the `overwrite` parameter, so a request will fail if the given file path already exists in the configSet and overwrite is set to `false`.
The `cleanup` parameter cannot be set to true when `filePath` is used.
-The body of the request should be a zip file that contains the configset. The zip file must be created from within the `conf` directory (i.e., `solrconfig.xml` must be the top level entry in the zip file).
+If uploading an entire configSet, the body of the request should be a zip file that contains the configset. The zip file must be created from within the `conf` directory (i.e., `solrconfig.xml` must be the top level entry in the zip file).
Here is an example on how to create the zip file named "myconfig.zip" and upload it as a configset named "myConfigSet":
+[.dynamic-tabs]
+--
+[example.tab-pane#v1uploadconfigset]
+====
+[.tab-label]*V1 API*
+
+With the v1 API, the `upload` command must be capitalized as `UPLOAD`:
+
[source,bash]
----
$ (cd solr/server/solr/configsets/sample_techproducts_configs/conf && zip -r - *) > myconfigset.zip
@@ -130,8 +138,65 @@ The same can be achieved using a Unix pipe with a single request as follows:
----
$ (cd server/solr/configsets/sample_techproducts_configs/conf && zip -r - *) | curl -X POST --header "Content-Type:application/octet-stream" --data-binary @- "http://localhost:8983/solr/admin/configs?action=UPLOAD&name=myConfigSet"
----
+====
+
+[example.tab-pane#v2uploadconfigset]
+====
+[.tab-label]*V2 API*
+
+With the v2 API, the name of the configset to upload is provided as a path parameter:
+
+[source,bash]
+----
+$ (cd solr/server/solr/configsets/sample_techproducts_configs/conf && zip -r - *) > myconfigset.zip
+
+$ curl -X PUT --header "Content-Type:application/octet-stream" --data-binary @myconfigset.zip
+ "http://localhost:8983/api/cluster/configs/myConfigSet"
+----
+
+With this REST API, the default behavior is to overwrite the configSet if it already exists.
+This behavior can be disabled by providing the URL param `overwrite=false`, in which case the request will fail if the configSet already exists.
+====
+--
+
+Here is an example on how to upload a single file to a configset named "myConfigSet":
+
+[.dynamic-tabs]
+--
+[example.tab-pane#v1uploadsinglefile]
+====
+[.tab-label]*V1 API*
+
+With the v1 API, the `upload` command must be capitalized as `UPLOAD`.
+The filename to upload is provided via the `filePath` URL param:
+
+[source,bash]
+----
+curl -X POST --header "Content-Type:application/octet-stream"
+ --data-binary @solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml
+ "http://localhost:8983/solr/admin/configs?action=UPLOAD&name=myConfigSet&filePath=solrconfig.xml&overwrite=true"
+----
+====
-NOTE: The `UPLOAD` command does not yet have a v2 equivalent API.
+[example.tab-pane#v2uploadsinglefile]
+====
+[.tab-label]*V2 API*
+
+With the v2 API, the name of the configset and file are both provided in the URL.
+They can be substituted in `/cluster/configs/{config_name}/{file_name}`.
+The filename may be nested and include `/` characters.
+
+[source,bash]
+----
+curl -X PUT --header "Content-Type:application/octet-stream"
+ --data-binary @solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml
+ "http://localhost:8983/api/cluster/configs/myConfigSet/solrconfig.xml"
+----
+
+With this REST API, the default behavior is to overwrite the file if it already exists within the configSet.
+This behavior can be disabled by providing the URL param `overwrite=false`, in which case the request will fail if the file already exists within the configSet.
+====
+--
[[configsets-create]]
== Create a Configset