From 7afb7d0668365cd78ec68c7e5b900921b3f5e5f0 Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Mon, 23 Dec 2024 11:52:29 -0500 Subject: [PATCH 1/9] Tweak 'list-configset' path to be more REST-ful Convert v2 configset APIs to JAX-RS Also makes slight tweaks to line APIs up with the more REST-ful design chosen for other v2 APIs. - 'clone' (i.e. 'create-from-existing') is not available at `POST /api/configsets {...}` - 'delete' is now available at `DELETE /api/configsets/csName` - 'list' is now available at `GET /api/configsets` --- .../client/api/endpoint/ConfigsetsApi.java | 68 +++++++++++++++++++ .../api/endpoint/ListConfigsetsApi.java | 32 --------- .../api/model/CloneConfigsetRequestBody.java | 18 +++++ .../solr/handler/admin/ConfigSetsHandler.java | 41 +++++------ ...eConfigSetAPI.java => CloneConfigSet.java} | 62 +++++++++-------- .../handler/configsets/ConfigSetAPIBase.java | 15 +++- ...ConfigSetAPI.java => DeleteConfigSet.java} | 39 ++++++----- .../handler/configsets/ListConfigSets.java | 4 +- .../request/beans/CreateConfigPayload.java | 32 --------- 9 files changed, 167 insertions(+), 144 deletions(-) create mode 100644 solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigsetsApi.java delete mode 100644 solr/api/src/java/org/apache/solr/client/api/endpoint/ListConfigsetsApi.java create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java rename solr/core/src/java/org/apache/solr/handler/configsets/{CreateConfigSetAPI.java => CloneConfigSet.java} (53%) rename solr/core/src/java/org/apache/solr/handler/configsets/{DeleteConfigSetAPI.java => DeleteConfigSet.java} (64%) delete mode 100644 solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/CreateConfigPayload.java diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigsetsApi.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigsetsApi.java new file mode 100644 index 00000000000..b3db8366b3a --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigsetsApi.java @@ -0,0 +1,68 @@ +/* + * 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.client.api.endpoint; + +import io.swagger.v3.oas.annotations.Operation; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import org.apache.solr.client.api.model.CloneConfigsetRequestBody; +import org.apache.solr.client.api.model.ListConfigsetsResponse; +import org.apache.solr.client.api.model.SolrJerseyResponse; + +public interface ConfigsetsApi { + + /** V2 API definition for listing the configsets available to this SolrCloud cluster. */ + @Path("/configsets") + interface List { + @GET + @Operation( + summary = "List the configsets available to Solr.", + tags = {"configsets"}) + ListConfigsetsResponse listConfigSet() throws Exception; + } + + /** + * V2 API definition for creating a (possibly slightly modified) copy of an existing configset + * + *

Equivalent to the existing v1 API /admin/configs?action=CREATE + */ + @Path("/configsets") + interface Clone { + @POST + @Operation( + summary = "Create a new configset modeled on an existing one.", + tags = {"configsets"}) + SolrJerseyResponse cloneExistingConfigSet(CloneConfigsetRequestBody requestBody) + throws Exception; + } + + /** + * V2 API definition for deleting an existing configset. + * + *

Equivalent to the existing v1 API /admin/configs?action=DELETE + */ + @Path("/configsets/{configsetName}") + interface Delete { + @DELETE + @Operation(summary = "Delete an existing configset.", tags = "configsets") + SolrJerseyResponse deleteConfigSet(@PathParam("configsetName") String configSetName) + throws Exception; + } +} diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/ListConfigsetsApi.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/ListConfigsetsApi.java deleted file mode 100644 index 7e0cf620b7f..00000000000 --- a/solr/api/src/java/org/apache/solr/client/api/endpoint/ListConfigsetsApi.java +++ /dev/null @@ -1,32 +0,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. - */ -package org.apache.solr.client.api.endpoint; - -import io.swagger.v3.oas.annotations.Operation; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import org.apache.solr.client.api.model.ListConfigsetsResponse; - -/** V2 API definition for listing configsets. */ -@Path("/cluster/configs") -public interface ListConfigsetsApi { - @GET - @Operation( - summary = "List the configsets available to Solr.", - tags = {"configsets"}) - ListConfigsetsResponse listConfigSet() throws Exception; -} diff --git a/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java b/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java new file mode 100644 index 00000000000..24bf1bcfa5a --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java @@ -0,0 +1,18 @@ +package org.apache.solr.client.api.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import org.apache.solr.client.api.endpoint.ConfigsetsApi; + +/** Request body for {@link ConfigsetsApi.Clone#createConfigsetFromExisting()} */ +public class CloneConfigsetRequestBody { + public static final String DEFAULT_CONFIGSET = "_default"; + + @JsonProperty(required = true) + public String name; + + @JsonProperty(defaultValue = DEFAULT_CONFIGSET) + public String baseConfigSet; + + @JsonProperty public Map properties; +} 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 ff69b1ee147..f7864fd86cb 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 @@ -28,8 +28,7 @@ import org.apache.solr.api.AnnotatedApi; import org.apache.solr.api.Api; import org.apache.solr.api.JerseyResource; -import org.apache.solr.api.PayloadObj; -import org.apache.solr.client.solrj.request.beans.CreateConfigPayload; +import org.apache.solr.client.api.model.CloneConfigsetRequestBody; import org.apache.solr.cloud.ConfigSetCmds; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; @@ -41,8 +40,8 @@ import org.apache.solr.core.CoreContainer; import org.apache.solr.handler.RequestHandlerBase; import org.apache.solr.handler.api.V2ApiUtils; -import org.apache.solr.handler.configsets.CreateConfigSetAPI; -import org.apache.solr.handler.configsets.DeleteConfigSetAPI; +import org.apache.solr.handler.configsets.CloneConfigSet; +import org.apache.solr.handler.configsets.DeleteConfigSet; import org.apache.solr.handler.configsets.ListConfigSets; import org.apache.solr.handler.configsets.UploadConfigSetAPI; import org.apache.solr.handler.configsets.UploadConfigSetFileAPI; @@ -96,17 +95,10 @@ public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throw switch (action) { case DELETE: - final DeleteConfigSetAPI deleteConfigSetAPI = new DeleteConfigSetAPI(coreContainer); - final SolrQueryRequest v2DeleteReq = - new DelegatingSolrQueryRequest(req) { - @Override - public Map getPathTemplateValues() { - return Map.of( - DeleteConfigSetAPI.CONFIGSET_NAME_PLACEHOLDER, - req.getParams().required().get(NAME)); - } - }; - deleteConfigSetAPI.deleteConfigSet(v2DeleteReq, rsp); + final DeleteConfigSet deleteConfigSetAPI = new DeleteConfigSet(coreContainer, req, rsp); + final var deleteResponse = + deleteConfigSetAPI.deleteConfigSet(req.getParams().required().get(NAME)); + V2ApiUtils.squashIntoSolrResponseWithoutHeader(rsp, deleteResponse); break; case UPLOAD: final SolrQueryRequest v2UploadReq = @@ -153,12 +145,12 @@ public SolrParams getParams() { } // Map v1 parameters into v2 format and process request - final CreateConfigPayload createPayload = new CreateConfigPayload(); - createPayload.name = newConfigSetName; + final var requestBody = new CloneConfigsetRequestBody(); + requestBody.name = newConfigSetName; if (req.getParams().get(ConfigSetCmds.BASE_CONFIGSET) != null) { - createPayload.baseConfigSet = req.getParams().get(ConfigSetCmds.BASE_CONFIGSET); + requestBody.baseConfigSet = req.getParams().get(ConfigSetCmds.BASE_CONFIGSET); } - createPayload.properties = new HashMap<>(); + requestBody.properties = new HashMap<>(); req.getParams().stream() .filter(entry -> entry.getKey().startsWith(ConfigSetCmds.CONFIG_SET_PROPERTY_PREFIX)) .forEach( @@ -167,10 +159,11 @@ public SolrParams getParams() { entry.getKey().substring(ConfigSetCmds.CONFIG_SET_PROPERTY_PREFIX.length()); final Object value = (entry.getValue().length == 1) ? entry.getValue()[0] : entry.getValue(); - createPayload.properties.put(newKey, value); + requestBody.properties.put(newKey, value); }); - final CreateConfigSetAPI createConfigSetAPI = new CreateConfigSetAPI(coreContainer); - createConfigSetAPI.create(new PayloadObj<>("create", null, createPayload, req, rsp)); + final CloneConfigSet createConfigSetAPI = new CloneConfigSet(coreContainer, req, rsp); + final var createResponse = createConfigSetAPI.cloneExistingConfigSet(requestBody); + V2ApiUtils.squashIntoSolrResponseWithoutHeader(rsp, createResponse); break; default: throw new IllegalStateException("Unexpected ConfigSetAction detected: " + action); @@ -208,8 +201,6 @@ public Boolean registerV2() { @Override public Collection getApis() { final List apis = new ArrayList<>(); - apis.addAll(AnnotatedApi.getApis(new CreateConfigSetAPI(coreContainer))); - apis.addAll(AnnotatedApi.getApis(new DeleteConfigSetAPI(coreContainer))); apis.addAll(AnnotatedApi.getApis(new UploadConfigSetAPI(coreContainer))); apis.addAll(AnnotatedApi.getApis(new UploadConfigSetFileAPI(coreContainer))); @@ -218,7 +209,7 @@ public Collection getApis() { @Override public Collection> getJerseyResources() { - return List.of(ListConfigSets.class); + return List.of(ListConfigSets.class, CloneConfigSet.class, DeleteConfigSet.class); } @Override diff --git a/solr/core/src/java/org/apache/solr/handler/configsets/CreateConfigSetAPI.java b/solr/core/src/java/org/apache/solr/handler/configsets/CloneConfigSet.java similarity index 53% rename from solr/core/src/java/org/apache/solr/handler/configsets/CreateConfigSetAPI.java rename to solr/core/src/java/org/apache/solr/handler/configsets/CloneConfigSet.java index 796903ff73c..0b4a82457bf 100644 --- a/solr/core/src/java/org/apache/solr/handler/configsets/CreateConfigSetAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/configsets/CloneConfigSet.java @@ -17,54 +17,55 @@ package org.apache.solr.handler.configsets; -import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST; import static org.apache.solr.common.params.CommonParams.NAME; import static org.apache.solr.handler.admin.ConfigSetsHandler.DISABLE_CREATE_AUTH_CHECKS; import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_EDIT_PERM; +import jakarta.inject.Inject; import java.util.HashMap; import java.util.Map; -import org.apache.solr.api.Command; -import org.apache.solr.api.EndPoint; -import org.apache.solr.api.PayloadObj; -import org.apache.solr.client.solrj.request.beans.CreateConfigPayload; +import org.apache.solr.client.api.endpoint.ConfigsetsApi; +import org.apache.solr.client.api.model.CloneConfigsetRequestBody; +import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.cloud.ConfigSetCmds; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.ConfigSetParams; import org.apache.solr.core.CoreContainer; +import org.apache.solr.jersey.PermissionName; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; -/** - * V2 API for creating a new configset as a copy of an existing one. - * - *

This API (POST /v2/cluster/configs {"create": {...}}) is analogous to the v1 - * /admin/configs?action=CREATE command. - */ -@EndPoint(method = POST, path = "/cluster/configs", permission = CONFIG_EDIT_PERM) -public class CreateConfigSetAPI extends ConfigSetAPIBase { +/** V2 API implementation for {@link ConfigsetsApi.Clone} */ +public class CloneConfigSet extends ConfigSetAPIBase implements ConfigsetsApi.Clone { - public CreateConfigSetAPI(CoreContainer coreContainer) { - super(coreContainer); + @Inject + public CloneConfigSet( + CoreContainer coreContainer, + SolrQueryRequest solrQueryRequest, + SolrQueryResponse solrQueryResponse) { + super(coreContainer, solrQueryRequest, solrQueryResponse); } - @Command(name = "create") - public void create(PayloadObj obj) throws Exception { - final CreateConfigPayload createConfigPayload = obj.get(); - if (configSetService.checkConfigExists(createConfigPayload.name)) { + @Override + @PermissionName(CONFIG_EDIT_PERM) + public SolrJerseyResponse cloneExistingConfigSet(CloneConfigsetRequestBody requestBody) + throws Exception { + final var response = instantiateJerseyResponse(SolrJerseyResponse.class); + if (configSetService.checkConfigExists(requestBody.name)) { throw new SolrException( - SolrException.ErrorCode.BAD_REQUEST, - "ConfigSet already exists: " + createConfigPayload.name); + SolrException.ErrorCode.BAD_REQUEST, "ConfigSet already exists: " + requestBody.name); } // is there a base config that already exists - if (!configSetService.checkConfigExists(createConfigPayload.baseConfigSet)) { + if (!configSetService.checkConfigExists(requestBody.baseConfigSet)) { throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, - "Base ConfigSet does not exist: " + createConfigPayload.baseConfigSet); + "Base ConfigSet does not exist: " + requestBody.baseConfigSet); } if (!DISABLE_CREATE_AUTH_CHECKS - && !isTrusted(obj.getRequest().getUserPrincipal(), coreContainer.getAuthenticationPlugin()) - && configSetService.isConfigSetTrusted(createConfigPayload.baseConfigSet)) { + && !isTrusted(solrQueryRequest.getUserPrincipal(), coreContainer.getAuthenticationPlugin()) + && configSetService.isConfigSetTrusted(requestBody.baseConfigSet)) { throw new SolrException( SolrException.ErrorCode.UNAUTHORIZED, "Can't create a configset with an unauthenticated request from a trusted " @@ -72,16 +73,17 @@ public void create(PayloadObj obj) throws Exception { } final Map configsetCommandMsg = new HashMap<>(); - configsetCommandMsg.put(NAME, createConfigPayload.name); - configsetCommandMsg.put(ConfigSetCmds.BASE_CONFIGSET, createConfigPayload.baseConfigSet); - if (createConfigPayload.properties != null) { - for (Map.Entry e : createConfigPayload.properties.entrySet()) { + configsetCommandMsg.put(NAME, requestBody.name); + configsetCommandMsg.put(ConfigSetCmds.BASE_CONFIGSET, requestBody.baseConfigSet); + if (requestBody.properties != null) { + for (Map.Entry e : requestBody.properties.entrySet()) { configsetCommandMsg.put( ConfigSetCmds.CONFIG_SET_PROPERTY_PREFIX + e.getKey(), e.getValue()); } } runConfigSetCommand( - obj.getResponse(), ConfigSetParams.ConfigSetAction.CREATE, configsetCommandMsg); + solrQueryResponse, ConfigSetParams.ConfigSetAction.CREATE, configsetCommandMsg); + return response; } } diff --git a/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java b/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java index 3f401e31bd8..b559db91e59 100644 --- a/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java +++ b/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java @@ -28,6 +28,7 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; +import org.apache.solr.api.JerseyResource; import org.apache.solr.client.solrj.SolrResponse; import org.apache.solr.cloud.OverseerSolrResponseSerializer; import org.apache.solr.cloud.OverseerTaskQueue; @@ -53,18 +54,26 @@ *

Contains utilities for tasks common in configset manipulation, including running configset * "commands" and checking configset "trusted-ness". */ -public class ConfigSetAPIBase { +public class ConfigSetAPIBase extends JerseyResource { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); protected final CoreContainer coreContainer; + + protected final SolrQueryRequest solrQueryRequest; + protected final SolrQueryResponse solrQueryResponse; protected final Optional distributedCollectionConfigSetCommandRunner; - protected final ConfigSetService configSetService; - public ConfigSetAPIBase(CoreContainer coreContainer) { + public ConfigSetAPIBase( + CoreContainer coreContainer, + SolrQueryRequest solrQueryRequest, + SolrQueryResponse solrQueryResponse) { this.coreContainer = coreContainer; + this.solrQueryRequest = solrQueryRequest; + this.solrQueryResponse = solrQueryResponse; + this.distributedCollectionConfigSetCommandRunner = coreContainer.getDistributedCollectionCommandRunner(); this.configSetService = coreContainer.getConfigSetService(); diff --git a/solr/core/src/java/org/apache/solr/handler/configsets/DeleteConfigSetAPI.java b/solr/core/src/java/org/apache/solr/handler/configsets/DeleteConfigSet.java similarity index 64% rename from solr/core/src/java/org/apache/solr/handler/configsets/DeleteConfigSetAPI.java rename to solr/core/src/java/org/apache/solr/handler/configsets/DeleteConfigSet.java index 4867dd160fd..8f7b6cc0b4a 100644 --- a/solr/core/src/java/org/apache/solr/handler/configsets/DeleteConfigSetAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/configsets/DeleteConfigSet.java @@ -16,40 +16,37 @@ */ package org.apache.solr.handler.configsets; -import static org.apache.solr.client.solrj.SolrRequest.METHOD.DELETE; import static org.apache.solr.common.params.CommonParams.NAME; import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_EDIT_PERM; +import jakarta.inject.Inject; import java.util.HashMap; import java.util.Map; -import org.apache.solr.api.EndPoint; +import org.apache.solr.client.api.endpoint.ConfigsetsApi; +import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.ConfigSetParams; import org.apache.solr.common.util.StrUtils; import org.apache.solr.core.CoreContainer; +import org.apache.solr.jersey.PermissionName; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; -/** - * V2 API for deleting an existing configset - * - *

This API (DELETE /v2/cluster/configs/configsetName) is analogous to the v1 - * /admin/configs?action=DELETE command. - */ -public class DeleteConfigSetAPI extends ConfigSetAPIBase { - - public static final String CONFIGSET_NAME_PLACEHOLDER = "name"; +/** V2 API implementation for {@link ConfigsetsApi.Delete} */ +public class DeleteConfigSet extends ConfigSetAPIBase implements ConfigsetsApi.Delete { - public DeleteConfigSetAPI(CoreContainer coreContainer) { - super(coreContainer); + @Inject + public DeleteConfigSet( + CoreContainer coreContainer, + SolrQueryRequest solrQueryRequest, + SolrQueryResponse solrQueryResponse) { + super(coreContainer, solrQueryRequest, solrQueryResponse); } - @EndPoint( - method = DELETE, - path = "/cluster/configs/{" + CONFIGSET_NAME_PLACEHOLDER + "}", - permission = CONFIG_EDIT_PERM) - public void deleteConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { - final String configSetName = req.getPathTemplateValues().get("name"); + @Override + @PermissionName(CONFIG_EDIT_PERM) + public SolrJerseyResponse deleteConfigSet(String configSetName) throws Exception { + final var response = instantiateJerseyResponse(SolrJerseyResponse.class); if (StrUtils.isNullOrEmpty(configSetName)) { throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "No configset name provided to delete"); @@ -57,6 +54,8 @@ public void deleteConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws final Map configsetCommandMsg = new HashMap<>(); configsetCommandMsg.put(NAME, configSetName); - runConfigSetCommand(rsp, ConfigSetParams.ConfigSetAction.DELETE, configsetCommandMsg); + runConfigSetCommand( + solrQueryResponse, ConfigSetParams.ConfigSetAction.DELETE, configsetCommandMsg); + return response; } } diff --git a/solr/core/src/java/org/apache/solr/handler/configsets/ListConfigSets.java b/solr/core/src/java/org/apache/solr/handler/configsets/ListConfigSets.java index 5f5d28adcfc..b3748a7bc40 100644 --- a/solr/core/src/java/org/apache/solr/handler/configsets/ListConfigSets.java +++ b/solr/core/src/java/org/apache/solr/handler/configsets/ListConfigSets.java @@ -22,7 +22,7 @@ import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.HttpHeaders; import org.apache.solr.api.JerseyResource; -import org.apache.solr.client.api.endpoint.ListConfigsetsApi; +import org.apache.solr.client.api.endpoint.ConfigsetsApi; import org.apache.solr.client.api.model.ListConfigsetsResponse; import org.apache.solr.core.CoreContainer; import org.apache.solr.jersey.PermissionName; @@ -32,7 +32,7 @@ * *

This API (GET /v2/cluster/configs) is analogous to the v1 /admin/configs?action=LIST command. */ -public class ListConfigSets extends JerseyResource implements ListConfigsetsApi { +public class ListConfigSets extends JerseyResource implements ConfigsetsApi.List { @Context public HttpHeaders headers; diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/CreateConfigPayload.java b/solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/CreateConfigPayload.java deleted file mode 100644 index 5f7f2e6687d..00000000000 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/CreateConfigPayload.java +++ /dev/null @@ -1,32 +0,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. - */ -package org.apache.solr.client.solrj.request.beans; - -import java.util.Map; -import org.apache.solr.common.annotation.JsonProperty; -import org.apache.solr.common.util.ReflectMapWriter; - -public class CreateConfigPayload implements ReflectMapWriter { - public static final String DEFAULT_CONFIGSET = - "_default"; // TODO Better location for this in SolrJ? - - @JsonProperty(required = true) - public String name; - - @JsonProperty public String baseConfigSet = DEFAULT_CONFIGSET; - @JsonProperty public Map properties; -} From 6c545310f855f5234f00da9dffe5c842fa21fb85 Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Mon, 23 Dec 2024 12:59:18 -0500 Subject: [PATCH 2/9] Quick fix to compile and run tests --- .../solr/client/api/model/CloneConfigsetRequestBody.java | 2 +- .../apache/solr/handler/configsets/ConfigSetAPIBase.java | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java b/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java index 24bf1bcfa5a..e937fed0c53 100644 --- a/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java +++ b/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java @@ -4,7 +4,7 @@ import java.util.Map; import org.apache.solr.client.api.endpoint.ConfigsetsApi; -/** Request body for {@link ConfigsetsApi.Clone#createConfigsetFromExisting()} */ +/** Request body for {@link ConfigsetsApi.Clone#cloneExistingConfigSet(CloneConfigsetRequestBody)} */ public class CloneConfigsetRequestBody { public static final String DEFAULT_CONFIGSET = "_default"; diff --git a/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java b/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java index b559db91e59..4bca7eccdab 100644 --- a/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java +++ b/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java @@ -79,6 +79,12 @@ public ConfigSetAPIBase( this.configSetService = coreContainer.getConfigSetService(); } + // TODO Delete once the 'Upload' APIs have been converted + public ConfigSetAPIBase( + CoreContainer coreContainer) { + this(coreContainer, null, null); + } + protected void runConfigSetCommand( SolrQueryResponse rsp, ConfigSetParams.ConfigSetAction action, From c065f87e441237f36f28a272e27d2fa8f68464bd Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Fri, 27 Dec 2024 06:44:22 -0500 Subject: [PATCH 3/9] Add test-failure reminder --- test-failures.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 test-failures.txt diff --git a/test-failures.txt b/test-failures.txt new file mode 100644 index 00000000000..9843869063e --- /dev/null +++ b/test-failures.txt @@ -0,0 +1,14 @@ + - org.apache.solr.cloud.TestConfigSetsAPI.testNewSingleFileAfterSchemaAPIV2 (:solr:core) + Test history: https://ge.apache.org/scans/tests?search.rootProjectNames=solr-root&tests.container=org.apache.solr.cloud.TestConfigSetsAPI&tests.test=testNewSingleFileAfterSchemaAPIV2 http://fucit.org/solr-jenkins-reports/history-trend-of-recent-failures.html#series/org.apache.solr.cloud.TestConfigSetsAPI.testNewSingleFileAfterSchemaAPIV2 + Test output: /Users/gerlowskija/checkouts/solr/solr/core/build/test-results/test/outputs/OUTPUT-org.apache.solr.cloud.TestConfigSetsAPI.txt + Reproduce with: ./gradlew :solr:core:test --tests "org.apache.solr.cloud.TestConfigSetsAPI.testNewSingleFileAfterSchemaAPIV2" -Ptests.minheapsize=2g -Ptests.heapsize=2g -Ptests.jvms=8 "-Ptests.jvmargs=-XX:TieredStopAtLevel=1 -XX:+UseParallelGC -XX:ActiveProcessorCount=1 -XX:ReservedCodeCacheSize=120m" -Ptests.seed=A988C1EF728FB399 -Ptests.file.encoding=UTF-8 + + - org.apache.solr.cloud.TestConfigSetsAPI.testCreateWithTrust (:solr:core) + Test history: https://ge.apache.org/scans/tests?search.rootProjectNames=solr-root&tests.container=org.apache.solr.cloud.TestConfigSetsAPI&tests.test=testCreateWithTrust http://fucit.org/solr-jenkins-reports/history-trend-of-recent-failures.html#series/org.apache.solr.cloud.TestConfigSetsAPI.testCreateWithTrust + Test output: /Users/gerlowskija/checkouts/solr/solr/core/build/test-results/test/outputs/OUTPUT-org.apache.solr.cloud.TestConfigSetsAPI.txt + Reproduce with: ./gradlew :solr:core:test --tests "org.apache.solr.cloud.TestConfigSetsAPI.testCreateWithTrust" -Ptests.minheapsize=2g -Ptests.heapsize=2g -Ptests.jvms=8 "-Ptests.jvmargs=-XX:TieredStopAtLevel=1 -XX:+UseParallelGC -XX:ActiveProcessorCount=1 -XX:ReservedCodeCacheSize=120m" -Ptests.seed=A988C1EF728FB399 -Ptests.file.encoding=UTF-8 + + - org.apache.solr.cloud.TestConfigSetsAPI.testNewSingleFileAfterSchemaAPIV1 (:solr:core) + Test history: https://ge.apache.org/scans/tests?search.rootProjectNames=solr-root&tests.container=org.apache.solr.cloud.TestConfigSetsAPI&tests.test=testNewSingleFileAfterSchemaAPIV1 http://fucit.org/solr-jenkins-reports/history-trend-of-recent-failures.html#series/org.apache.solr.cloud.TestConfigSetsAPI.testNewSingleFileAfterSchemaAPIV1 + Test output: /Users/gerlowskija/checkouts/solr/solr/core/build/test-results/test/outputs/OUTPUT-org.apache.solr.cloud.TestConfigSetsAPI.txt + Reproduce with: ./gradlew :solr:core:test --tests "org.apache.solr.cloud.TestConfigSetsAPI.testNewSingleFileAfterSchemaAPIV1" -Ptests.minheapsize=2g -Ptests.heapsize=2g -Ptests.jvms=8 "-Ptests.jvmargs=-XX:TieredStopAtLevel=1 -XX:+UseParallelGC -XX:ActiveProcessorCount=1 -XX:ReservedCodeCacheSize=120m" -Ptests.seed=A988C1EF728FB399 -Ptests.file.encoding=UTF-8 From 02c1a9b4b8b00981a5ed4b64d758839c5efce78a Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Fri, 27 Dec 2024 22:04:33 -0500 Subject: [PATCH 4/9] Convert upload-configset APIs to JAX-RS; still test failures --- .../client/api/endpoint/ConfigsetsApi.java | 35 +++++- .../api/model/CloneConfigsetRequestBody.java | 4 +- .../solr/handler/admin/ConfigSetsHandler.java | 62 ++++------- .../handler/configsets/ConfigSetAPIBase.java | 8 +- ...ConfigSetAPI.java => UploadConfigSet.java} | 103 ++++++++++++++---- .../configsets/UploadConfigSetFileAPI.java | 96 ---------------- .../apache/solr/cloud/TestConfigSetsAPI.java | 6 +- 7 files changed, 140 insertions(+), 174 deletions(-) rename solr/core/src/java/org/apache/solr/handler/configsets/{UploadConfigSetAPI.java => UploadConfigSet.java} (53%) delete mode 100644 solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSetFileAPI.java diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigsetsApi.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigsetsApi.java index b3db8366b3a..8bb5f20a877 100644 --- a/solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigsetsApi.java +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigsetsApi.java @@ -20,8 +20,12 @@ import jakarta.ws.rs.DELETE; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.QueryParam; +import java.io.IOException; +import java.io.InputStream; import org.apache.solr.client.api.model.CloneConfigsetRequestBody; import org.apache.solr.client.api.model.ListConfigsetsResponse; import org.apache.solr.client.api.model.SolrJerseyResponse; @@ -58,11 +62,38 @@ SolrJerseyResponse cloneExistingConfigSet(CloneConfigsetRequestBody requestBody) * *

Equivalent to the existing v1 API /admin/configs?action=DELETE */ - @Path("/configsets/{configsetName}") + @Path("/configsets/{configSetName}") interface Delete { @DELETE @Operation(summary = "Delete an existing configset.", tags = "configsets") - SolrJerseyResponse deleteConfigSet(@PathParam("configsetName") String configSetName) + SolrJerseyResponse deleteConfigSet(@PathParam("configSetName") String configSetName) throws Exception; } + + /** + * V2 API definitions for uploading a configset, in whole or part. + * + *

Equivalent to the existing v1 API /admin/configs?action=UPLOAD + */ + @Path("/configsets/{configSetName}") + interface Upload { + @PUT + @Operation(summary = "Create a new configset.", tags = "configsets") + SolrJerseyResponse uploadConfigSet( + @PathParam("configSetName") String configSetName, + @QueryParam("overwrite") Boolean overwrite, + @QueryParam("cleanup") Boolean cleanup, + InputStream requestBody) + throws IOException; + + @PUT + @Path("{filePath}") + SolrJerseyResponse uploadConfigSetFile( + @PathParam("configSetName") String configSetName, + @PathParam("filePath") String filePath, + @QueryParam("overwrite") Boolean overwrite, + @QueryParam("cleanup") Boolean cleanup, + InputStream requestBody) + throws IOException; + } } diff --git a/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java b/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java index e937fed0c53..15a26730d48 100644 --- a/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java +++ b/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java @@ -4,7 +4,9 @@ import java.util.Map; import org.apache.solr.client.api.endpoint.ConfigsetsApi; -/** Request body for {@link ConfigsetsApi.Clone#cloneExistingConfigSet(CloneConfigsetRequestBody)} */ +/** + * Request body for {@link ConfigsetsApi.Clone#cloneExistingConfigSet(CloneConfigsetRequestBody)} + */ public class CloneConfigsetRequestBody { public static final String DEFAULT_CONFIGSET = "_default"; 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 f7864fd86cb..535deb54e44 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 @@ -17,35 +17,30 @@ package org.apache.solr.handler.admin; import static org.apache.solr.common.params.CommonParams.NAME; -import static org.apache.solr.handler.configsets.UploadConfigSetFileAPI.FILEPATH_PLACEHOLDER; import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; -import java.util.Map; -import org.apache.solr.api.AnnotatedApi; import org.apache.solr.api.Api; import org.apache.solr.api.JerseyResource; import org.apache.solr.client.api.model.CloneConfigsetRequestBody; +import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.cloud.ConfigSetCmds; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.params.ConfigSetParams; import org.apache.solr.common.params.ConfigSetParams.ConfigSetAction; -import org.apache.solr.common.params.DefaultSolrParams; -import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.core.CoreContainer; import org.apache.solr.handler.RequestHandlerBase; import org.apache.solr.handler.api.V2ApiUtils; import org.apache.solr.handler.configsets.CloneConfigSet; +import org.apache.solr.handler.configsets.ConfigSetAPIBase; import org.apache.solr.handler.configsets.DeleteConfigSet; import org.apache.solr.handler.configsets.ListConfigSets; -import org.apache.solr.handler.configsets.UploadConfigSetAPI; -import org.apache.solr.handler.configsets.UploadConfigSetFileAPI; -import org.apache.solr.request.DelegatingSolrQueryRequest; +import org.apache.solr.handler.configsets.UploadConfigSet; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.security.AuthorizationContext; @@ -101,38 +96,24 @@ public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throw V2ApiUtils.squashIntoSolrResponseWithoutHeader(rsp, deleteResponse); break; case UPLOAD: - final SolrQueryRequest v2UploadReq = - new DelegatingSolrQueryRequest(req) { - @Override - public Map getPathTemplateValues() { - final Map templateValsByName = new HashMap<>(); - - templateValsByName.put( - UploadConfigSetAPI.CONFIGSET_NAME_PLACEHOLDER, - req.getParams().required().get(NAME)); - if (!req.getParams().get(ConfigSetParams.FILE_PATH, "").isEmpty()) { - templateValsByName.put( - FILEPATH_PLACEHOLDER, req.getParams().get(ConfigSetParams.FILE_PATH)); - } - return templateValsByName; - } - - // Set the v1 default vals where they differ from v2's - @Override - public SolrParams getParams() { - final ModifiableSolrParams v1Defaults = new ModifiableSolrParams(); - v1Defaults.add(ConfigSetParams.OVERWRITE, "false"); - v1Defaults.add(ConfigSetParams.CLEANUP, "false"); - return new DefaultSolrParams(super.getParams(), v1Defaults); - } - }; + final var uploadApi = new UploadConfigSet(coreContainer, req, rsp); + final var configSetName = req.getParams().required().get(NAME); + final var overwrite = req.getParams().getBool(ConfigSetParams.OVERWRITE, false); + final var cleanup = req.getParams().getBool(ConfigSetParams.CLEANUP, false); + final var configSetData = ConfigSetAPIBase.ensureNonEmptyInputStream(req); + SolrJerseyResponse uploadResponse; if (req.getParams() .get(ConfigSetParams.FILE_PATH, "") .isEmpty()) { // Uploading a whole configset - new UploadConfigSetAPI(coreContainer).uploadConfigSet(v2UploadReq, rsp); + uploadResponse = + uploadApi.uploadConfigSet(configSetName, overwrite, cleanup, configSetData); } else { // Uploading a single file - new UploadConfigSetFileAPI(coreContainer).updateConfigSetFile(v2UploadReq, rsp); + final var filePath = req.getParams().get(ConfigSetParams.FILE_PATH); + uploadResponse = + uploadApi.uploadConfigSetFile( + configSetName, filePath, overwrite, cleanup, configSetData); } + V2ApiUtils.squashIntoSolrResponseWithoutHeader(rsp, uploadResponse); break; case LIST: final ListConfigSets listConfigSetsAPI = new ListConfigSets(coreContainer); @@ -149,6 +130,8 @@ public SolrParams getParams() { requestBody.name = newConfigSetName; if (req.getParams().get(ConfigSetCmds.BASE_CONFIGSET) != null) { requestBody.baseConfigSet = req.getParams().get(ConfigSetCmds.BASE_CONFIGSET); + } else { + requestBody.baseConfigSet = "_default"; } requestBody.properties = new HashMap<>(); req.getParams().stream() @@ -200,16 +183,13 @@ public Boolean registerV2() { @Override public Collection getApis() { - final List apis = new ArrayList<>(); - apis.addAll(AnnotatedApi.getApis(new UploadConfigSetAPI(coreContainer))); - apis.addAll(AnnotatedApi.getApis(new UploadConfigSetFileAPI(coreContainer))); - - return apis; + return new ArrayList<>(); } @Override public Collection> getJerseyResources() { - return List.of(ListConfigSets.class, CloneConfigSet.class, DeleteConfigSet.class); + return List.of( + ListConfigSets.class, CloneConfigSet.class, DeleteConfigSet.class, UploadConfigSet.class); } @Override diff --git a/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java b/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java index 4bca7eccdab..f6e99167f30 100644 --- a/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java +++ b/solr/core/src/java/org/apache/solr/handler/configsets/ConfigSetAPIBase.java @@ -79,12 +79,6 @@ public ConfigSetAPIBase( this.configSetService = coreContainer.getConfigSetService(); } - // TODO Delete once the 'Upload' APIs have been converted - public ConfigSetAPIBase( - CoreContainer coreContainer) { - this(coreContainer, null, null); - } - protected void runConfigSetCommand( SolrQueryResponse rsp, ConfigSetParams.ConfigSetAction action, @@ -111,7 +105,7 @@ protected void ensureConfigSetUploadEnabled() { } } - protected InputStream ensureNonEmptyInputStream(SolrQueryRequest req) throws IOException { + public static InputStream ensureNonEmptyInputStream(SolrQueryRequest req) throws IOException { Iterator contentStreamsIterator = req.getContentStreams().iterator(); if (!contentStreamsIterator.hasNext()) { diff --git a/solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSetAPI.java b/solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSet.java similarity index 53% rename from solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSetAPI.java rename to solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSet.java index 79d1b34d5ca..d42adf94999 100644 --- a/solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSetAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSet.java @@ -6,7 +6,7 @@ * (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 + * 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, @@ -16,9 +16,9 @@ */ package org.apache.solr.handler.configsets; -import static org.apache.solr.client.solrj.SolrRequest.METHOD.PUT; import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_EDIT_PERM; +import jakarta.inject.Inject; import java.io.IOException; import java.io.InputStream; import java.lang.invoke.MethodHandles; @@ -27,46 +27,47 @@ import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import org.apache.solr.api.EndPoint; +import org.apache.solr.client.api.endpoint.ConfigsetsApi; +import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.common.SolrException; -import org.apache.solr.common.params.ConfigSetParams; +import org.apache.solr.common.cloud.ZkMaintenanceUtils; import org.apache.solr.core.ConfigSetService; import org.apache.solr.core.CoreContainer; +import org.apache.solr.jersey.PermissionName; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.util.FileTypeMagicUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -/** - * V2 API for uploading a new configset (or overwriting an existing one). - * - *

This API (PUT /v2/cluster/configs/configsetName) is analogous to the v1 - * /admin/configs?action=UPLOAD command. - */ -public class UploadConfigSetAPI extends ConfigSetAPIBase { - - public static final String CONFIGSET_NAME_PLACEHOLDER = "name"; +public class UploadConfigSet extends ConfigSetAPIBase implements ConfigsetsApi.Upload { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - public UploadConfigSetAPI(CoreContainer coreContainer) { - super(coreContainer); + @Inject + public UploadConfigSet( + CoreContainer coreContainer, + SolrQueryRequest solrQueryRequest, + SolrQueryResponse solrQueryResponse) { + super(coreContainer, solrQueryRequest, solrQueryResponse); } - @EndPoint(method = PUT, path = "/cluster/configs/{name}", permission = CONFIG_EDIT_PERM) - public void uploadConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { + @Override + @PermissionName(CONFIG_EDIT_PERM) + public SolrJerseyResponse uploadConfigSet( + String configSetName, Boolean overwrite, Boolean cleanup, InputStream requestBody) + throws IOException { + final var response = instantiateJerseyResponse(SolrJerseyResponse.class); ensureConfigSetUploadEnabled(); - final String configSetName = req.getPathTemplateValues().get("name"); boolean overwritesExisting = configSetService.checkConfigExists(configSetName); boolean requestIsTrusted = - isTrusted(req.getUserPrincipal(), coreContainer.getAuthenticationPlugin()); + isTrusted(solrQueryRequest.getUserPrincipal(), coreContainer.getAuthenticationPlugin()); // Get upload parameters - boolean allowOverwrite = req.getParams().getBool(ConfigSetParams.OVERWRITE, true); - boolean cleanup = req.getParams().getBool(ConfigSetParams.CLEANUP, false); - final InputStream inputStream = ensureNonEmptyInputStream(req); + if (overwrite == null) overwrite = true; + if (cleanup == null) cleanup = false; - if (overwritesExisting && !allowOverwrite) { + if (overwritesExisting && !overwrite) { throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "The configuration " + configSetName + " already exists"); @@ -84,7 +85,7 @@ public void uploadConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws // singleFilePath is not passed. createBaseNode(configSetService, overwritesExisting, requestIsTrusted, configSetName); - try (ZipInputStream zis = new ZipInputStream(inputStream, StandardCharsets.UTF_8)) { + try (ZipInputStream zis = new ZipInputStream(requestBody, StandardCharsets.UTF_8)) { boolean hasEntry = false; ZipEntry zipEntry; while ((zipEntry = zis.getNextEntry()) != null) { @@ -111,6 +112,60 @@ public void uploadConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws && !configSetService.isConfigSetTrusted(configSetName)) { configSetService.setConfigSetTrust(configSetName, true); } + return response; + } + + @Override + @PermissionName(CONFIG_EDIT_PERM) + public SolrJerseyResponse uploadConfigSetFile( + String configSetName, + String filePath, + Boolean overwrite, + Boolean cleanup, + InputStream requestBody) + throws IOException { + final var response = instantiateJerseyResponse(SolrJerseyResponse.class); + ensureConfigSetUploadEnabled(); + + boolean overwritesExisting = configSetService.checkConfigExists(configSetName); + boolean requestIsTrusted = + isTrusted(solrQueryRequest.getUserPrincipal(), coreContainer.getAuthenticationPlugin()); + + // Get upload parameters + + String singleFilePath = filePath != null ? filePath : ""; + if (overwrite == null) overwrite = true; + if (cleanup == null) cleanup = false; + + String fixedSingleFilePath = singleFilePath; + if (fixedSingleFilePath.charAt(0) == '/') { + fixedSingleFilePath = fixedSingleFilePath.substring(1); + } + byte[] data = requestBody.readAllBytes(); + if (fixedSingleFilePath.isEmpty()) { + throw new SolrException( + SolrException.ErrorCode.BAD_REQUEST, + "The file path provided for upload, '" + singleFilePath + "', is not valid."); + } else if (ZkMaintenanceUtils.isFileForbiddenInConfigSets(fixedSingleFilePath) + || FileTypeMagicUtil.isFileForbiddenInConfigset(data)) { + throw new SolrException( + SolrException.ErrorCode.BAD_REQUEST, + "The file type provided for upload, '" + + singleFilePath + + "', is forbidden for use in configSets."); + } else if (cleanup) { + // Cleanup is not allowed while using singleFilePath upload + throw new SolrException( + SolrException.ErrorCode.BAD_REQUEST, + "ConfigSet uploads do not allow cleanup=true when file path is used."); + } else { + // Create a node for the configuration in config + // For creating the baseNode, the cleanup parameter is only allowed to be true when + // singleFilePath is not passed. + createBaseNode(configSetService, overwritesExisting, requestIsTrusted, configSetName); + configSetService.uploadFileToConfig(configSetName, fixedSingleFilePath, data, overwrite); + } + return response; } private void deleteUnusedFiles( diff --git a/solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSetFileAPI.java b/solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSetFileAPI.java deleted file mode 100644 index 2380a79a92b..00000000000 --- a/solr/core/src/java/org/apache/solr/handler/configsets/UploadConfigSetFileAPI.java +++ /dev/null @@ -1,96 +0,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. - */ -package org.apache.solr.handler.configsets; - -import static org.apache.solr.client.solrj.SolrRequest.METHOD.PUT; -import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_EDIT_PERM; - -import java.io.InputStream; -import org.apache.solr.api.EndPoint; -import org.apache.solr.common.SolrException; -import org.apache.solr.common.cloud.ZkMaintenanceUtils; -import org.apache.solr.common.params.ConfigSetParams; -import org.apache.solr.core.CoreContainer; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.util.FileTypeMagicUtil; - -/** - * V2 API for adding or updating a single file within a configset. - * - *

This API (PUT /v2/cluster/configs/configsetName/someFilePath) is analogous to the v1 - * /admin/configs?action=UPLOAD&filePath=someFilePath command. - */ -public class UploadConfigSetFileAPI extends ConfigSetAPIBase { - - public static final String CONFIGSET_NAME_PLACEHOLDER = - UploadConfigSetAPI.CONFIGSET_NAME_PLACEHOLDER; - public static final String FILEPATH_PLACEHOLDER = "*"; - - private static final String API_PATH = - "/cluster/configs/{" + CONFIGSET_NAME_PLACEHOLDER + "}/" + FILEPATH_PLACEHOLDER; - - public UploadConfigSetFileAPI(CoreContainer coreContainer) { - super(coreContainer); - } - - @EndPoint(method = PUT, path = API_PATH, permission = CONFIG_EDIT_PERM) - public void updateConfigSetFile(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { - ensureConfigSetUploadEnabled(); - - final String configSetName = req.getPathTemplateValues().get("name"); - boolean overwritesExisting = configSetService.checkConfigExists(configSetName); - boolean requestIsTrusted = - isTrusted(req.getUserPrincipal(), coreContainer.getAuthenticationPlugin()); - - // Get upload parameters - - String singleFilePath = req.getPathTemplateValues().getOrDefault(FILEPATH_PLACEHOLDER, ""); - boolean allowOverwrite = req.getParams().getBool(ConfigSetParams.OVERWRITE, true); - boolean cleanup = req.getParams().getBool(ConfigSetParams.CLEANUP, false); - final InputStream inputStream = ensureNonEmptyInputStream(req); - - String fixedSingleFilePath = singleFilePath; - if (fixedSingleFilePath.charAt(0) == '/') { - fixedSingleFilePath = fixedSingleFilePath.substring(1); - } - byte[] data = inputStream.readAllBytes(); - if (fixedSingleFilePath.isEmpty()) { - throw new SolrException( - SolrException.ErrorCode.BAD_REQUEST, - "The file path provided for upload, '" + singleFilePath + "', is not valid."); - } else if (ZkMaintenanceUtils.isFileForbiddenInConfigSets(fixedSingleFilePath) - || FileTypeMagicUtil.isFileForbiddenInConfigset(data)) { - throw new SolrException( - SolrException.ErrorCode.BAD_REQUEST, - "The file type provided for upload, '" - + singleFilePath - + "', is forbidden for use in configSets."); - } else if (cleanup) { - // Cleanup is not allowed while using singleFilePath upload - throw new SolrException( - SolrException.ErrorCode.BAD_REQUEST, - "ConfigSet uploads do not allow cleanup=true when file path is used."); - } else { - // Create a node for the configuration in config - // For creating the baseNode, the cleanup parameter is only allowed to be true when - // singleFilePath is not passed. - createBaseNode(configSetService, overwritesExisting, requestIsTrusted, configSetName); - configSetService.uploadFileToConfig(configSetName, fixedSingleFilePath, data, allowOverwrite); - } - } -} 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 886ffb6afca..94661991579 100644 --- a/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java +++ b/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java @@ -1543,7 +1543,7 @@ private long uploadGivenConfigSet( final ByteBuffer fileBytes = TestSolrConfigHandler.getFileContent(file.getAbsolutePath(), false); final String uriEnding = - "/cluster/configs/" + "/configsets/" + configSetName + suffix + (!overwrite ? "?overwrite=false" : "") @@ -1595,10 +1595,10 @@ private long uploadSingleConfigSetFile( final ByteBuffer sampleConfigFile = TestSolrConfigHandler.getFileContent(file.getAbsolutePath(), false); final String uriEnding = - "/cluster/configs/" + "/configsets/" + configSetName + suffix - + "/" + /* + "/" */ + uploadPath + (!overwrite ? "?overwrite=false" : "") + (cleanup ? "?cleanup=true" : ""); From 380229dd56dff4168236b334ec0c2ddeadbfdd5a Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Sat, 28 Dec 2024 22:12:33 -0500 Subject: [PATCH 5/9] Fix TestConfigSetAPI failure --- .../solr/client/api/endpoint/ConfigsetsApi.java | 7 ++++--- .../org/apache/solr/cloud/TestConfigSetsAPI.java | 4 +++- test-failures.txt | 14 -------------- 3 files changed, 7 insertions(+), 18 deletions(-) delete mode 100644 test-failures.txt diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigsetsApi.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigsetsApi.java index 8bb5f20a877..9961b4c9f28 100644 --- a/solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigsetsApi.java +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/ConfigsetsApi.java @@ -17,6 +17,7 @@ package org.apache.solr.client.api.endpoint; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.parameters.RequestBody; import jakarta.ws.rs.DELETE; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; @@ -83,17 +84,17 @@ SolrJerseyResponse uploadConfigSet( @PathParam("configSetName") String configSetName, @QueryParam("overwrite") Boolean overwrite, @QueryParam("cleanup") Boolean cleanup, - InputStream requestBody) + @RequestBody(required = true) InputStream requestBody) throws IOException; @PUT - @Path("{filePath}") + @Path("{filePath:.+}") SolrJerseyResponse uploadConfigSetFile( @PathParam("configSetName") String configSetName, @PathParam("filePath") String filePath, @QueryParam("overwrite") Boolean overwrite, @QueryParam("cleanup") Boolean cleanup, - InputStream requestBody) + @RequestBody(required = true) InputStream requestBody) throws IOException; } } 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 94661991579..baa659b7e68 100644 --- a/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java +++ b/solr/core/src/test/org/apache/solr/cloud/TestConfigSetsAPI.java @@ -1594,11 +1594,13 @@ private long uploadSingleConfigSetFile( final ByteBuffer sampleConfigFile = TestSolrConfigHandler.getFileContent(file.getAbsolutePath(), false); + if (uploadPath != null && !uploadPath.startsWith("/")) { + uploadPath = "/" + uploadPath; + } final String uriEnding = "/configsets/" + configSetName + suffix - /* + "/" */ + uploadPath + (!overwrite ? "?overwrite=false" : "") + (cleanup ? "?cleanup=true" : ""); diff --git a/test-failures.txt b/test-failures.txt deleted file mode 100644 index 9843869063e..00000000000 --- a/test-failures.txt +++ /dev/null @@ -1,14 +0,0 @@ - - org.apache.solr.cloud.TestConfigSetsAPI.testNewSingleFileAfterSchemaAPIV2 (:solr:core) - Test history: https://ge.apache.org/scans/tests?search.rootProjectNames=solr-root&tests.container=org.apache.solr.cloud.TestConfigSetsAPI&tests.test=testNewSingleFileAfterSchemaAPIV2 http://fucit.org/solr-jenkins-reports/history-trend-of-recent-failures.html#series/org.apache.solr.cloud.TestConfigSetsAPI.testNewSingleFileAfterSchemaAPIV2 - Test output: /Users/gerlowskija/checkouts/solr/solr/core/build/test-results/test/outputs/OUTPUT-org.apache.solr.cloud.TestConfigSetsAPI.txt - Reproduce with: ./gradlew :solr:core:test --tests "org.apache.solr.cloud.TestConfigSetsAPI.testNewSingleFileAfterSchemaAPIV2" -Ptests.minheapsize=2g -Ptests.heapsize=2g -Ptests.jvms=8 "-Ptests.jvmargs=-XX:TieredStopAtLevel=1 -XX:+UseParallelGC -XX:ActiveProcessorCount=1 -XX:ReservedCodeCacheSize=120m" -Ptests.seed=A988C1EF728FB399 -Ptests.file.encoding=UTF-8 - - - org.apache.solr.cloud.TestConfigSetsAPI.testCreateWithTrust (:solr:core) - Test history: https://ge.apache.org/scans/tests?search.rootProjectNames=solr-root&tests.container=org.apache.solr.cloud.TestConfigSetsAPI&tests.test=testCreateWithTrust http://fucit.org/solr-jenkins-reports/history-trend-of-recent-failures.html#series/org.apache.solr.cloud.TestConfigSetsAPI.testCreateWithTrust - Test output: /Users/gerlowskija/checkouts/solr/solr/core/build/test-results/test/outputs/OUTPUT-org.apache.solr.cloud.TestConfigSetsAPI.txt - Reproduce with: ./gradlew :solr:core:test --tests "org.apache.solr.cloud.TestConfigSetsAPI.testCreateWithTrust" -Ptests.minheapsize=2g -Ptests.heapsize=2g -Ptests.jvms=8 "-Ptests.jvmargs=-XX:TieredStopAtLevel=1 -XX:+UseParallelGC -XX:ActiveProcessorCount=1 -XX:ReservedCodeCacheSize=120m" -Ptests.seed=A988C1EF728FB399 -Ptests.file.encoding=UTF-8 - - - org.apache.solr.cloud.TestConfigSetsAPI.testNewSingleFileAfterSchemaAPIV1 (:solr:core) - Test history: https://ge.apache.org/scans/tests?search.rootProjectNames=solr-root&tests.container=org.apache.solr.cloud.TestConfigSetsAPI&tests.test=testNewSingleFileAfterSchemaAPIV1 http://fucit.org/solr-jenkins-reports/history-trend-of-recent-failures.html#series/org.apache.solr.cloud.TestConfigSetsAPI.testNewSingleFileAfterSchemaAPIV1 - Test output: /Users/gerlowskija/checkouts/solr/solr/core/build/test-results/test/outputs/OUTPUT-org.apache.solr.cloud.TestConfigSetsAPI.txt - Reproduce with: ./gradlew :solr:core:test --tests "org.apache.solr.cloud.TestConfigSetsAPI.testNewSingleFileAfterSchemaAPIV1" -Ptests.minheapsize=2g -Ptests.heapsize=2g -Ptests.jvms=8 "-Ptests.jvmargs=-XX:TieredStopAtLevel=1 -XX:+UseParallelGC -XX:ActiveProcessorCount=1 -XX:ReservedCodeCacheSize=120m" -Ptests.seed=A988C1EF728FB399 -Ptests.file.encoding=UTF-8 From a243bc41c51cf5f50f67ab9efbc4006819b7f769 Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Sat, 28 Dec 2024 22:27:07 -0500 Subject: [PATCH 6/9] Update ref-guide and javadocs --- .../api/model/CloneConfigsetRequestBody.java | 5 +-- .../handler/configsets/ListConfigSets.java | 2 +- .../configsets/ListConfigSetsAPITest.java | 2 +- solr/packaging/test/test_zk.bats | 2 +- .../pages/configsets-api.adoc | 40 +++++++------------ 5 files changed, 18 insertions(+), 33 deletions(-) diff --git a/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java b/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java index 15a26730d48..857c099e071 100644 --- a/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java +++ b/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java @@ -2,11 +2,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Map; -import org.apache.solr.client.api.endpoint.ConfigsetsApi; -/** - * Request body for {@link ConfigsetsApi.Clone#cloneExistingConfigSet(CloneConfigsetRequestBody)} - */ +/** Request body for ConfigsetsApi.Clone */ public class CloneConfigsetRequestBody { public static final String DEFAULT_CONFIGSET = "_default"; diff --git a/solr/core/src/java/org/apache/solr/handler/configsets/ListConfigSets.java b/solr/core/src/java/org/apache/solr/handler/configsets/ListConfigSets.java index b3748a7bc40..5b45fa38f22 100644 --- a/solr/core/src/java/org/apache/solr/handler/configsets/ListConfigSets.java +++ b/solr/core/src/java/org/apache/solr/handler/configsets/ListConfigSets.java @@ -30,7 +30,7 @@ /** * V2 API implementation for listing all available configsets. * - *

This API (GET /v2/cluster/configs) is analogous to the v1 /admin/configs?action=LIST command. + *

This API (GET /v2/configsets) is analogous to the v1 /admin/configs?action=LIST command. */ public class ListConfigSets extends JerseyResource implements ConfigsetsApi.List { diff --git a/solr/core/src/test/org/apache/solr/handler/configsets/ListConfigSetsAPITest.java b/solr/core/src/test/org/apache/solr/handler/configsets/ListConfigSetsAPITest.java index 40100be48ac..776d0800e03 100644 --- a/solr/core/src/test/org/apache/solr/handler/configsets/ListConfigSetsAPITest.java +++ b/solr/core/src/test/org/apache/solr/handler/configsets/ListConfigSetsAPITest.java @@ -61,7 +61,7 @@ public void testSuccessfulListConfigsets() throws Exception { } /** - * Test the v2 to v1 response mapping for /cluster/configs + * Test the v2 to v1 response mapping for GET /configsets * *

{@link org.apache.solr.handler.admin.ConfigSetsHandler} uses {@link ListConfigSets} (and its * response class {@link ListConfigsetsResponse}) internally to serve the v1 version of this diff --git a/solr/packaging/test/test_zk.bats b/solr/packaging/test/test_zk.bats index 240fb072b31..05b4b1b4f2b 100644 --- a/solr/packaging/test/test_zk.bats +++ b/solr/packaging/test/test_zk.bats @@ -123,7 +123,7 @@ teardown() { refute_output --partial "ERROR" sleep 1 - run curl "http://localhost:${SOLR_PORT}/api/cluster/configs?omitHeader=true" + run curl "http://localhost:${SOLR_PORT}/api/cluster/configsets" assert_output --partial '"configSets":["_default","techproducts2"]' } diff --git a/solr/solr-ref-guide/modules/configuration-guide/pages/configsets-api.adoc b/solr/solr-ref-guide/modules/configuration-guide/pages/configsets-api.adoc index 1a5dc43fe48..eeb48138a20 100644 --- a/solr/solr-ref-guide/modules/configuration-guide/pages/configsets-api.adoc +++ b/solr/solr-ref-guide/modules/configuration-guide/pages/configsets-api.adoc @@ -35,7 +35,7 @@ NOTE: This API can only be used with Solr running in SolrCloud mode. If you are not running Solr in SolrCloud mode but would still like to use shared configurations, please see the section xref:config-sets.adoc[]. The API works by passing commands to the `configs` endpoint. -The path to the endpoint varies depending on the API being used: the v1 API uses `solr/admin/configs`, while the v2 API uses `api/cluster/configs`. +The path to the endpoint varies depending on the API being used: the v1 API uses `/solr/admin/configs`, while the v2 API uses `/api/configsets`. Examples of both types are provided below. [[configsets-list]] @@ -64,7 +64,7 @@ With the v2 API, the `list` command is implied when there is no data sent with t [source,bash] ---- -http://localhost:8983/api/cluster/configs?omitHeader=true +http://localhost:8983/api/configsets?omitHeader=true ---- ==== ====== @@ -181,7 +181,7 @@ With the v2 API, the name of the configset to upload is provided as a path param $ (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" + "http://localhost:8983/api/configsets/myConfigSet" ---- With this API, the default behavior is to overwrite the configset if it already exists. @@ -211,14 +211,14 @@ 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__`. +They can be substituted in `/configsets/__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" + "http://localhost:8983/api/configsets/myConfigSet/solrconfig.xml" ---- With this API, the default behavior is to overwrite the file if it already exists within the configset. @@ -280,30 +280,18 @@ http://localhost:8983/solr/admin/configs?action=CREATE&name=myConfigSet&baseConf V2 API:: + ==== -With the v2 API, the `create` command is provided as part of the JSON data that contains the required parameters: +With the v2 API, the `create` command is implicit and parameters are specified in a `POST` request body. [source,bash] ---- curl -X POST -H 'Content-type: application/json' -d '{ - "create":{ - "name": "myConfigSet", - "baseConfigSet": "predefinedTemplate", - "configSetProp.immutable": "false"}}' - http://localhost:8983/api/cluster/configs?omitHeader=true ----- - -With the v2 API, configset properties can also be provided via the `properties` map: - -[source,bash] ----- -curl -X POST -H 'Content-type: application/json' -d '{ - "create":{ - "name": "myConfigSet", - "baseConfigSet": "predefinedTemplate", - "properties": { - "immutable": "false" - }}}' - http://localhost:8983/api/cluster/configs?omitHeader=true + "name": "myConfigSet", + "baseConfigSet": "predefinedTemplate", + "properties": { + "immutable": "false" + } +}' + http://localhost:8983/api/configsets?omitHeader=true ---- ==== ====== @@ -359,7 +347,7 @@ The name of the configset to delete is provided as a path parameter: [source,bash] ---- -curl -X DELETE http://localhost:8983/api/cluster/configs/myConfigSet?omitHeader=true +curl -X DELETE http://localhost:8983/api/configsets/myConfigSet?omitHeader=true ---- ==== ====== From 3e93dd3dc80a12b52c2fbc11560bc3f064cf381a Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Sat, 28 Dec 2024 22:39:17 -0500 Subject: [PATCH 7/9] Make check happy --- .../api/model/CloneConfigsetRequestBody.java | 16 ++++++++++++++++ .../solr/handler/configsets/CloneConfigSet.java | 2 +- .../solr/handler/configsets/DeleteConfigSet.java | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java b/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java index 857c099e071..14e22225986 100644 --- a/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java +++ b/solr/api/src/java/org/apache/solr/client/api/model/CloneConfigsetRequestBody.java @@ -1,3 +1,19 @@ +/* + * 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.client.api.model; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/solr/core/src/java/org/apache/solr/handler/configsets/CloneConfigSet.java b/solr/core/src/java/org/apache/solr/handler/configsets/CloneConfigSet.java index 0b4a82457bf..e55c74e04fa 100644 --- a/solr/core/src/java/org/apache/solr/handler/configsets/CloneConfigSet.java +++ b/solr/core/src/java/org/apache/solr/handler/configsets/CloneConfigSet.java @@ -35,7 +35,7 @@ import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; -/** V2 API implementation for {@link ConfigsetsApi.Clone} */ +/** V2 API implementation for ConfigsetsApi.Clone */ public class CloneConfigSet extends ConfigSetAPIBase implements ConfigsetsApi.Clone { @Inject diff --git a/solr/core/src/java/org/apache/solr/handler/configsets/DeleteConfigSet.java b/solr/core/src/java/org/apache/solr/handler/configsets/DeleteConfigSet.java index 8f7b6cc0b4a..1a4b363a833 100644 --- a/solr/core/src/java/org/apache/solr/handler/configsets/DeleteConfigSet.java +++ b/solr/core/src/java/org/apache/solr/handler/configsets/DeleteConfigSet.java @@ -32,7 +32,7 @@ import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; -/** V2 API implementation for {@link ConfigsetsApi.Delete} */ +/** V2 API implementation for ConfigsetsApi.Delete */ public class DeleteConfigSet extends ConfigSetAPIBase implements ConfigsetsApi.Delete { @Inject From 4ff39da1213292aadd8b6580d2ecb2f3350e12bf Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Fri, 3 Jan 2025 06:23:46 -0500 Subject: [PATCH 8/9] Fix integration test --- solr/packaging/test/test_zk.bats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solr/packaging/test/test_zk.bats b/solr/packaging/test/test_zk.bats index 05b4b1b4f2b..d516637cfb8 100644 --- a/solr/packaging/test/test_zk.bats +++ b/solr/packaging/test/test_zk.bats @@ -123,7 +123,7 @@ teardown() { refute_output --partial "ERROR" sleep 1 - run curl "http://localhost:${SOLR_PORT}/api/cluster/configsets" + run curl "http://localhost:${SOLR_PORT}/api/configsets" assert_output --partial '"configSets":["_default","techproducts2"]' } From acbad717da6e4ce6aae1117e872caccb23d15680 Mon Sep 17 00:00:00 2001 From: Jason Gerlowski Date: Fri, 3 Jan 2025 06:32:43 -0500 Subject: [PATCH 9/9] Add CHANGES.txt entry --- solr/CHANGES.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index a806d567b39..1fd659b966e 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -154,6 +154,10 @@ Improvements specific collections or cores. Collection information can be fetched by a call to `GET /api/collections/collectionName`, and core information with a call to `GET /api/cores/coreName/segments`. (Jason Gerlowski) +* SOLR-16396: All v2 configset APIs have been moved to the slightly different path: `/api/configsets`, to better align with the design of + other v2 APIs. SolrJ now offers (experimental) SolrRequest implementations for all v2 configset APIs in + `org.apache.solr.client.solrj.request.ConfigsetsApi`. (Jason Gerlowski) + Optimizations --------------------- * SOLR-17578: Remove ZkController internal core supplier, for slightly faster reconnection after Zookeeper session loss. (Pierre Salagnac)