Skip to content

Commit

Permalink
SOLR-15745: Move public v2 "core-admin" APIs to annotation framework (#…
Browse files Browse the repository at this point in the history
…565)

Solr's been in the slow process of moving its v2 APIs away from the
existing apispec/mapping framework towards one that relies on more
explicit annotations to specify API properties.

This commit converts the 'reload', 'swap', 'rename', 'unload',
'merge-indexes', and 'split' commands of `/v2/cores/<core>` over to the
preferred framework.

(NOTE: the spelling 'indexes' in the 'merge-indexes' command was
retained here for consistency/compatibility.  We should consider
changing it going forward.)
  • Loading branch information
gerlowskija committed Jan 27, 2022
1 parent cfacd18 commit df88d65
Show file tree
Hide file tree
Showing 13 changed files with 721 additions and 170 deletions.
26 changes: 15 additions & 11 deletions solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
Expand Up @@ -17,9 +17,6 @@

package org.apache.solr.handler;

import java.io.IOException;
import java.util.*;

import com.google.common.collect.Maps;
import org.apache.solr.api.Command;
import org.apache.solr.api.EndPoint;
Expand Down Expand Up @@ -48,18 +45,21 @@
import org.apache.solr.response.SolrQueryResponse;
import org.apache.zookeeper.KeeperException;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

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.client.solrj.SolrRequest.METHOD.*;
import static org.apache.solr.cloud.api.collections.CollectionHandlingUtils.REQUESTID;
import static org.apache.solr.common.params.CollectionParams.ACTION;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.*;
import static org.apache.solr.core.RateLimiterConfig.RL_CONFIG_KEY;
import static org.apache.solr.security.PermissionNameProvider.Name.COLL_EDIT_PERM;
import static org.apache.solr.security.PermissionNameProvider.Name.COLL_READ_PERM;
import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_EDIT_PERM;
import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_READ_PERM;
import static org.apache.solr.security.PermissionNameProvider.Name.*;

/**
* All V2 APIs that have a prefix of /api/cluster/
Expand Down Expand Up @@ -293,7 +293,11 @@ public static SolrQueryRequest wrapParams(SolrQueryRequest req, Map<String, Obje
ModifiableSolrParams solrParams = new ModifiableSolrParams();
m.forEach((k, v) -> {
if (v == null) return;
solrParams.add(k.toString(), String.valueOf(v));
if (v instanceof String[]) {
solrParams.add(k, (String[]) v);
} else {
solrParams.add(k, String.valueOf(v));
}
});
DefaultSolrParams dsp = new DefaultSolrParams(req.getParams(), solrParams);
req.setParams(dsp);
Expand Down
Expand Up @@ -39,9 +39,15 @@
import org.apache.solr.handler.admin.api.AllCoresStatusAPI;
import org.apache.solr.handler.admin.api.CreateCoreAPI;
import org.apache.solr.handler.admin.api.InvokeClassAPI;
import org.apache.solr.handler.admin.api.MergeIndexesAPI;
import org.apache.solr.handler.admin.api.OverseerOperationAPI;
import org.apache.solr.handler.admin.api.RejoinLeaderElectionAPI;
import org.apache.solr.handler.admin.api.ReloadCoreAPI;
import org.apache.solr.handler.admin.api.RenameCoreAPI;
import org.apache.solr.handler.admin.api.SingleCoreStatusAPI;
import org.apache.solr.handler.admin.api.SplitCoreAPI;
import org.apache.solr.handler.admin.api.SwapCoresAPI;
import org.apache.solr.handler.admin.api.UnloadCoreAPI;
import org.apache.solr.logging.MDCLoggingContext;
import org.apache.solr.metrics.SolrMetricManager;
import org.apache.solr.metrics.SolrMetricsContext;
Expand Down Expand Up @@ -429,6 +435,12 @@ public Collection<Api> getApis() {
apis.addAll(AnnotatedApi.getApis(new InvokeClassAPI(this)));
apis.addAll(AnnotatedApi.getApis(new RejoinLeaderElectionAPI(this)));
apis.addAll(AnnotatedApi.getApis(new OverseerOperationAPI(this)));
apis.addAll(AnnotatedApi.getApis(new ReloadCoreAPI(this)));
apis.addAll(AnnotatedApi.getApis(new SwapCoresAPI(this)));
apis.addAll(AnnotatedApi.getApis(new RenameCoreAPI(this)));
apis.addAll(AnnotatedApi.getApis(new UnloadCoreAPI(this)));
apis.addAll(AnnotatedApi.getApis(new MergeIndexesAPI(this)));
apis.addAll(AnnotatedApi.getApis(new SplitCoreAPI(this)));
return apis;
}

Expand Down
@@ -0,0 +1,91 @@
/*
* 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.admin.api;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.solr.api.Command;
import org.apache.solr.api.EndPoint;
import org.apache.solr.api.PayloadObj;
import org.apache.solr.common.annotation.JsonProperty;
import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.common.params.UpdateParams;
import org.apache.solr.common.util.ReflectMapWriter;
import org.apache.solr.handler.admin.CoreAdminHandler;

import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST;
import static org.apache.solr.handler.ClusterAPI.wrapParams;
import static org.apache.solr.security.PermissionNameProvider.Name.CORE_EDIT_PERM;

/**
* V2 API for merging one or more Solr cores into the target core.
*
* The new API (POST /v2/cores/coreName {'merge-indexes': {...}}) is equivalent to the v1
* /admin/cores?action=mergeindexes command.
*/
@EndPoint(
path = {"/cores/{core}"},
method = POST,
permission = CORE_EDIT_PERM)
public class MergeIndexesAPI {
private static final String V2_MERGE_INDEXES_CORE_CMD = "merge-indexes";

private final CoreAdminHandler coreHandler;

public MergeIndexesAPI(CoreAdminHandler coreHandler) {
this.coreHandler = coreHandler;
}

@Command(name = V2_MERGE_INDEXES_CORE_CMD)
public void mergeIndexesIntoCore(PayloadObj<MergeIndexesPayload> obj) throws Exception {
final MergeIndexesPayload v2Body = obj.get();
final Map<String, Object> v1Params = v2Body.toMap(new HashMap<>());
v1Params.put(CoreAdminParams.ACTION, CoreAdminParams.CoreAdminAction.MERGEINDEXES.name().toLowerCase(Locale.ROOT));
v1Params.put(CoreAdminParams.CORE, obj.getRequest().getPathTemplateValues().get(CoreAdminParams.CORE));
if (! CollectionUtils.isEmpty(v2Body.indexDir)) {
v1Params.put("indexDir", v2Body.indexDir.toArray(new String[v2Body.indexDir.size()]));
}
if (! CollectionUtils.isEmpty(v2Body.srcCore)) {
v1Params.put("srcCore", v2Body.srcCore.toArray(new String[v2Body.srcCore.size()]));
}
// V1 API uses 'update.chain' instead of 'updateChain'.
if (v2Body.updateChain != null) {
v1Params.put(UpdateParams.UPDATE_CHAIN, v1Params.remove("updateChain"));
}

coreHandler.handleRequestBody(wrapParams(obj.getRequest(), v1Params), obj.getResponse());
}

public static class MergeIndexesPayload implements ReflectMapWriter {
@JsonProperty
public List<String> indexDir;

@JsonProperty
public List<String> srcCore;

@JsonProperty
public String updateChain;

@JsonProperty
public String async;
}
}
@@ -0,0 +1,69 @@
/*
* 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.admin.api;

import org.apache.solr.api.Command;
import org.apache.solr.api.EndPoint;
import org.apache.solr.api.PayloadObj;
import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.common.util.ReflectMapWriter;
import org.apache.solr.handler.admin.CoreAdminHandler;

import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST;
import static org.apache.solr.common.params.CommonParams.ACTION;
import static org.apache.solr.handler.ClusterAPI.wrapParams;
import static org.apache.solr.security.PermissionNameProvider.Name.CORE_EDIT_PERM;

/**
* V2 API for reloading an individual core.
*
* The new API (POST /v2/cores/coreName {'reload': {...}}) is equivalent to the v1
* /admin/cores?action=reload command.
*
* @see ReloadCorePayload
*/
@EndPoint(
path = {"/cores/{core}"},
method = POST,
permission = CORE_EDIT_PERM)
public class ReloadCoreAPI {
private static final String V2_RELOAD_CORE_CMD = "reload";

private final CoreAdminHandler coreHandler;

public ReloadCoreAPI(CoreAdminHandler coreHandler) {
this.coreHandler = coreHandler;
}

@Command(name = V2_RELOAD_CORE_CMD)
public void reloadCore(PayloadObj<ReloadCorePayload> obj) throws Exception {
final String coreName = obj.getRequest().getPathTemplateValues().get(CoreAdminParams.CORE);

final Map<String, Object> v1Params = new HashMap<>();
v1Params.put(ACTION, CoreAdminParams.CoreAdminAction.RELOAD.name().toLowerCase(Locale.ROOT));
v1Params.put(CoreAdminParams.CORE, coreName);

coreHandler.handleRequestBody(wrapParams(obj.getRequest(), v1Params), obj.getResponse());
}

public static class ReloadCorePayload implements ReflectMapWriter {}
}
@@ -0,0 +1,72 @@
/*
* 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.admin.api;

import org.apache.solr.api.Command;
import org.apache.solr.api.EndPoint;
import org.apache.solr.api.PayloadObj;
import org.apache.solr.common.annotation.JsonProperty;
import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.common.util.ReflectMapWriter;
import org.apache.solr.handler.admin.CoreAdminHandler;

import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST;
import static org.apache.solr.handler.ClusterAPI.wrapParams;
import static org.apache.solr.security.PermissionNameProvider.Name.CORE_EDIT_PERM;

/**
* V2 API for renaming an existing Solr core.
*
* The new API (POST /v2/cores/coreName {'rename': {...}}) is equivalent to the v1
* /admin/cores?action=rename command.
*/
@EndPoint(
path = {"/cores/{core}"},
method = POST,
permission = CORE_EDIT_PERM)
public class RenameCoreAPI {
private static final String V2_RENAME_CORE_CMD = "rename";

private final CoreAdminHandler coreHandler;

public RenameCoreAPI(CoreAdminHandler coreHandler) {
this.coreHandler = coreHandler;
}

@Command(name = V2_RENAME_CORE_CMD)
public void renameCore(PayloadObj<RenameCorePayload> obj) throws Exception {
final RenameCorePayload v2Body = obj.get();
final Map<String, Object> v1Params = v2Body.toMap(new HashMap<>());
v1Params.put(CoreAdminParams.ACTION, CoreAdminParams.CoreAdminAction.RENAME.name().toLowerCase(Locale.ROOT));
v1Params.put(CoreAdminParams.CORE, obj.getRequest().getPathTemplateValues().get(CoreAdminParams.CORE));

// V1 API uses 'other' instead of 'to' to represent the new core name.
v1Params.put(CoreAdminParams.OTHER, v1Params.remove("to"));

coreHandler.handleRequestBody(wrapParams(obj.getRequest(), v1Params), obj.getResponse());
}

public static class RenameCorePayload implements ReflectMapWriter {
@JsonProperty(required = true)
public String to;
}
}

0 comments on commit df88d65

Please sign in to comment.