This is an automated email from the ASF dual-hosted git repository. ishan pushed a commit to branch jira/SOLR15694 in repository https://gitbox.apache.org/repos/asf/solr.git
commit d3ae80845e2b1cc476057c20bef6d703a6731c5e Author: Noble Paul <[email protected]> AuthorDate: Fri Dec 10 16:51:14 2021 +1100 new format --- .../apache/solr/cloud/OverseerNodePrioritizer.java | 3 +- .../java/org/apache/solr/cloud/ZkController.java | 18 ++- .../apache/solr/cloud/api/collections/Assign.java | 17 +-- .../java/org/apache/solr/core/CoreContainer.java | 6 +- .../src/java/org/apache/solr/core/NodeRole.java | 77 ---------- .../src/java/org/apache/solr/core/NodeRoles.java | 107 +++++++++++++ .../java/org/apache/solr/handler/ClusterAPI.java | 166 +++++++++++++-------- .../test/org/apache/solr/cloud/NodeRolesTest.java | 40 +++-- .../client/solrj/cloud/DistribStateManager.java | 23 --- 9 files changed, 258 insertions(+), 199 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java b/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java index f88c686..4bd21dd 100644 --- a/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java +++ b/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java @@ -28,6 +28,7 @@ import org.apache.solr.common.params.CoreAdminParams; import org.apache.solr.common.params.CoreAdminParams.CoreAdminAction; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.util.Utils; +import org.apache.solr.core.NodeRoles; import org.apache.solr.handler.ClusterAPI; import org.apache.solr.handler.component.ShardHandler; import org.apache.solr.handler.component.ShardHandlerFactory; @@ -72,7 +73,7 @@ public class OverseerNodePrioritizer { } } - overseerDesignates.addAll(ClusterAPI.getNodesByRole("overseer", new ZkDistribStateManager(zkStateReader.getZkClient()))); + overseerDesignates.addAll(ClusterAPI.getNodesByRole(NodeRoles.Role.OVERSEER, NodeRoles.PREFERRED, new ZkDistribStateManager(zkStateReader.getZkClient()))); if (overseerDesignates.isEmpty()) return; String ldr = OverseerTaskProcessor.getLeaderNode(zk); if(overseerDesignates.contains(ldr)) return; diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java index 05c6c5b..e7b15bf 100644 --- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java +++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java @@ -73,12 +73,7 @@ import org.apache.solr.common.util.SolrNamedThreadFactory; import org.apache.solr.common.util.StrUtils; import org.apache.solr.common.util.URLUtil; import org.apache.solr.common.util.Utils; -import org.apache.solr.core.CloseHook; -import org.apache.solr.core.CloudConfig; -import org.apache.solr.core.CoreContainer; -import org.apache.solr.core.CoreDescriptor; -import org.apache.solr.core.SolrCore; -import org.apache.solr.core.SolrCoreInitializationException; +import org.apache.solr.core.*; import org.apache.solr.handler.component.HttpShardHandler; import org.apache.solr.logging.MDCLoggingContext; import org.apache.solr.search.SolrIndexSearcher; @@ -855,6 +850,13 @@ public class ZkController implements Closeable { ZkCmdExecutor cmdExecutor = new ZkCmdExecutor(zkClient.getZkClientTimeout()); cmdExecutor.ensureExists(ZkStateReader.LIVE_NODES_ZKNODE, zkClient); cmdExecutor.ensureExists(ZkStateReader.NODE_ROLES, zkClient); + for (NodeRoles.Role role : NodeRoles.Role.values()) { + cmdExecutor.ensureExists(ZkStateReader.NODE_ROLES + "/" + role.roleName, zkClient); + for (String v : role.supportedVals()) { + cmdExecutor.ensureExists(ZkStateReader.NODE_ROLES + "/" + role.roleName + "/"+v, zkClient); + } + } + cmdExecutor.ensureExists(ZkStateReader.COLLECTIONS_ZKNODE, zkClient); cmdExecutor.ensureExists(ZkStateReader.ALIASES, zkClient); byte[] emptyJson = "{}".getBytes(StandardCharsets.UTF_8); @@ -1089,8 +1091,8 @@ public class ZkController implements Closeable { ops.add(Op.create(nodePath, null, zkClient.getZkACLProvider().getACLsToAdd(nodePath), CreateMode.EPHEMERAL)); // Create the roles node as well - ops.add(Op.create(ZkStateReader.NODE_ROLES + "/" + nodeName, - Utils.toJSON(cc.nodeRoles), zkClient.getZkACLProvider().getACLsToAdd(nodePath), CreateMode.EPHEMERAL)); + cc.nodeRoles.ROLES.forEach((role, val) -> ops.add(Op.create(ZkStateReader.NODE_ROLES + "/" + role.roleName+ "/"+val +"/"+ nodeName, + null, zkClient.getZkACLProvider().getACLsToAdd(nodePath), CreateMode.EPHEMERAL))); zkClient.multi(ops, true); } diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java index beda953..8941705 100644 --- a/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java +++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java @@ -53,9 +53,9 @@ import org.apache.solr.common.cloud.Slice; import org.apache.solr.common.cloud.ZkNodeProps; import org.apache.solr.common.cloud.ZkStateReader; import org.apache.solr.common.util.StrUtils; -import org.apache.solr.common.util.Utils; import org.apache.solr.core.CoreContainer; -import org.apache.solr.core.NodeRole; +import org.apache.solr.core.NodeRoles; +import org.apache.solr.handler.ClusterAPI; import org.apache.solr.util.NumberUtils; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; @@ -258,15 +258,10 @@ public class Assign { public static void filterNonDataNodes(DistribStateManager zk, List<String> liveNodes) { try { - zk.forEachChild(ZkStateReader.NODE_ROLES, (name, data) -> { - if (data != null && data.getData() != null && data.getData().length > 0) { - @SuppressWarnings("unchecked") - List<String> roles = (List<String>) Utils.fromJSON(data.getData()); - if (!roles.contains(NodeRole.Role.DATA.toString())) { - liveNodes.remove(name); - } - } - }); + List<String> noData = ClusterAPI.getNodesByRole(NodeRoles.Role.DATA, NodeRoles.OFF, zk); + if(!noData.isEmpty()){ + liveNodes.removeAll(noData); + } } catch (Exception e) { throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Problem fetching roles from Zookeeper", e); } diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java index 44c63db..f7e9a1f 100644 --- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java +++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java @@ -246,7 +246,7 @@ public class CoreContainer { private final ObjectCache objectCache = new ObjectCache(); - public final NodeRole nodeRoles = new NodeRole(System.getProperty(NodeRole.NODE_ROLES_PROP)); + public final NodeRoles nodeRoles = new NodeRoles(System.getProperty(NodeRoles.NODE_ROLES_PROP)); private final ClusterSingletons clusterSingletons = new ClusterSingletons( () -> getZkController() != null && @@ -947,9 +947,9 @@ public class CoreContainer { }); clusterSingletons.setReady(); - if (nodeRoles.getRoles().contains(NodeRole.Role.OVERSEER)) { + if (NodeRoles.PREFERRED.equals(nodeRoles.getRoleVal(NodeRoles.Role.OVERSEER))) { try { - log.info("This node is started as an overseer"); + log.info("This node is started as a preferred overseer"); zkSys.getZkController().setPreferredOverseer(); } catch (KeeperException | InterruptedException e) { throw new SolrException(ErrorCode.SERVER_ERROR, e); diff --git a/solr/core/src/java/org/apache/solr/core/NodeRole.java b/solr/core/src/java/org/apache/solr/core/NodeRole.java deleted file mode 100644 index a4cf757..0000000 --- a/solr/core/src/java/org/apache/solr/core/NodeRole.java +++ /dev/null @@ -1,77 +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.core; - -import java.io.IOException; -import java.lang.invoke.MethodHandles; -import java.util.*; - -import org.apache.solr.common.IteratorWriter; -import org.apache.solr.common.StringUtils; -import org.apache.solr.common.util.StrUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class NodeRole implements IteratorWriter { - private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - public static final String NODE_ROLES_PROP = "solr.node.roles"; - - private final Set<Role> roles; - - public NodeRole(String role) { - if (StringUtils.isEmpty(role)) { - // if no roles were specified, assume "data" role for backcompat reasons - roles = Set.of(Role.DATA); - return; - } - Set<String> rolesSet = new HashSet<>(StrUtils.split(role, ',')); - if (rolesSet.isEmpty()) { - // if no roles were specified, assume "data" role for backcompat reasons - roles = Set.of(Role.DATA); - return; - } - roles = new TreeSet<>(); - for (String r: rolesSet) { - roles.add(Role.valueOfCaseInsensitive(r)); - } - } - - public Set<Role> getRoles() { - return roles; - } - - @Override - public void writeIter(ItemWriter iw) throws IOException { - for (Role role: roles) iw.add(role.toString()); - } - - public enum Role { - DATA, OVERSEER; - - public static Role valueOfCaseInsensitive(String value) { - // Given a user string "overseer", convert to OVERSEER and return the enum value - String canonicalValue = value.toUpperCase().replace(' ', '_'); - return Role.valueOf(canonicalValue); - } - - @Override - public String toString() { - return super.toString().toLowerCase(); - } - } -} diff --git a/solr/core/src/java/org/apache/solr/core/NodeRoles.java b/solr/core/src/java/org/apache/solr/core/NodeRoles.java new file mode 100644 index 0000000..fb3f011 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/core/NodeRoles.java @@ -0,0 +1,107 @@ +/* + * 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.core; + +import java.lang.invoke.MethodHandles; +import java.util.*; +import org.apache.solr.common.MapWriter; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.StringUtils; +import org.apache.solr.common.util.StrUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NodeRoles implements MapWriter { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + public static final String NODE_ROLES_PROP = "solr.node.roles"; + + public Map<Role, String> ROLES ; + + public NodeRoles(String role) { + ROLES = new EnumMap<>(Role.class); + if (StringUtils.isEmpty(role)) { + role = DEFAULT_ROLE; + } + List<String> rolesList = StrUtils.splitSmart(role, ','); + for (String s : rolesList) { + List<String> roleVal = StrUtils.splitSmart(s,':'); + Role r = Role.getRole(roleVal.get(0)); + if(r.supportedVals().contains(roleVal.get(1))) { + ROLES.put(r, roleVal.get(1)); + } else { + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "UNKNOWN role value :"+roleVal.get(0)); + } + } + ROLES = Collections.unmodifiableMap(ROLES); + } + + public String getRoleVal(Role role) { + return ROLES.get(role); + } + + + + @Override + public void writeMap(EntryWriter ew) { + ROLES.forEach((role, s) -> ew.putNoEx(role.roleName, s)); + } + + public enum Role { + DATA("data") , + OVERSEER("overseer") { + @Override + public Set<String> supportedVals() { + return OVERSEER_VALS; + } + }, + COORDINATOR("coordinator"); + + public final String roleName; + + Role(String name) { + this.roleName = name; + } + + public static Role getRole(String value) { + // Given a user string "overseer", convert to OVERSEER and return the enum value + for (Role role : Role.values()) { + if(value.equals(role.roleName)) return role; + } + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown role : "+value); + } + + public Set<String> supportedVals() { + return ON_OFF; + + } + + @Override + public String toString() { + return super.toString().toLowerCase(); + } + } + + public static final String ON = "on"; + public static final String OFF = "off"; + public static final Set<String> ON_OFF = Set.of(ON,OFF); + public static final String ALLOWED = "allowed"; + public static final String DISALLOWED = "disallowed"; + public static final String PREFERRED = "preferred"; + public static final Set<String> OVERSEER_VALS = Set.of(ALLOWED, DISALLOWED, PREFERRED); + public static final String DEFAULT_ROLE = "data:on,overseer:allowed"; +} 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 5f512d2..796ac49 100644 --- a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java @@ -42,6 +42,7 @@ import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.util.ReflectMapWriter; import org.apache.solr.common.util.Utils; import org.apache.solr.core.CoreContainer; +import org.apache.solr.core.NodeRoles; import org.apache.solr.handler.admin.CollectionsHandler; import org.apache.solr.handler.admin.ConfigSetsHandler; import org.apache.solr.request.SolrQueryRequest; @@ -63,20 +64,21 @@ import static org.apache.solr.security.PermissionNameProvider.Name.COLL_READ_PER import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_EDIT_PERM; import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_READ_PERM; -/** All V2 APIs that have a prefix of /api/cluster/ - * +/** + * All V2 APIs that have a prefix of /api/cluster/ */ public class ClusterAPI { private final CollectionsHandler collectionsHandler; private final ConfigSetsHandler configSetsHandler; - public final Commands commands = new Commands(); - public final ConfigSetCommands configSetCommands = new ConfigSetCommands(); + public final Commands commands = new Commands(); + public final ConfigSetCommands configSetCommands = new ConfigSetCommands(); public ClusterAPI(CollectionsHandler ch, ConfigSetsHandler configSetsHandler) { this.collectionsHandler = ch; this.configSetsHandler = configSetsHandler; } + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); @EndPoint(method = GET, @@ -85,14 +87,45 @@ public class ClusterAPI { @SuppressWarnings("unchecked") // nocommit: it must also output all data nodes that didn't start with -Dsolr.node.roles public void roles(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { - Map <String, List<String>> result = new LinkedHashMap<>(); - collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager(). - forEachChild(ZkStateReader.NODE_ROLES, (node, data) -> { - if (data != null && data.getData() != null) { - result.put(node, ((List<String>) Utils.fromJSON(data.getData()))); + Map<String, Object> result = new LinkedHashMap<>(); + + rsp.add("node-roles", readRecursive(ZkStateReader.NODE_ROLES, collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager(), 3)); + } + + Object readRecursive(String path, DistribStateManager zk, int depth) { + if (depth == 0) return null; + Map<String, Object> result = null; + boolean hasSubValues = false; + try { + List<String> children = zk.listData(path); + if (children != null && !children.isEmpty()) { + result = new HashMap<>(); + } else { + return depth >=1 ? Collections.emptyList(): null; } - }); - rsp.add("node-roles", result); + for (String child : children) { + Object c = readRecursive(path + "/" + child, zk, depth - 1); + if (c != null) { + hasSubValues = true; + } + result.put(child, c); + } + + + } catch (Exception e) { + } + + + + if (result == null) { + return null; + } + if (depth == 1) { + return result.keySet(); + } else { + return result; + } + } @EndPoint(method = GET, @@ -100,28 +133,35 @@ public class ClusterAPI { permission = COLL_READ_PERM) public void nodesWithRole(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { String role = req.getPathTemplateValues().get("role"); - rsp.add("nodes", - getNodesByRole(role, collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager())); + rsp.add(role, + readRecursive(ZkStateReader.NODE_ROLES + "/"+ role, + collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager(), 2)); + } + @EndPoint(method = GET, + path = "/cluster/node-roles/{role}/{role-val}", + permission = COLL_READ_PERM) + public void nodesWithRoleVal(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { + String role = req.getPathTemplateValues().get("role"); + String roleVal = req.getPathTemplateValues().get("role-val"); + List<String> nodes = collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager() + .getDistribStateManager().listData(ZkStateReader.NODE_ROLES + "/"+ role+"/"+roleVal); + rsp.add( role, Collections.singletonMap(roleVal, nodes)); } - public static List<String> getNodesByRole(String role, DistribStateManager zk) throws InterruptedException, IOException, KeeperException { - List<String> result = new ArrayList<>(); - zk.forEachChild(ZkStateReader.NODE_ROLES, (node, data) -> { - if (data != null && data.getData() != null) { - @SuppressWarnings("unchecked") - List<String> rolesData = (List<String>) Utils.fromJSON(data.getData()); - if (rolesData.contains(role)) { - result.add(node); - } - } - }); - return result; + public static List<String> getNodesByRole(NodeRoles.Role role, String val, DistribStateManager zk) + throws InterruptedException, IOException, KeeperException { + try { + return zk.listData(ZkStateReader.NODE_ROLES+"/"+ role+ "/"+val); + } catch (NoSuchElementException e) { + return Collections.emptyList(); + } + } @EndPoint(method = GET, - path = "/cluster/aliases", - permission = COLL_READ_PERM) + path = "/cluster/aliases", + permission = COLL_READ_PERM) public void aliases(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { final Map<String, Object> v1Params = Maps.newHashMap(); v1Params.put(ACTION, CollectionParams.CollectionAction.LISTALIASES.lowerName); @@ -129,58 +169,58 @@ public class ClusterAPI { } @EndPoint(method = GET, - path = "/cluster/overseer", - permission = COLL_READ_PERM) + path = "/cluster/overseer", + permission = COLL_READ_PERM) public void getOverseerStatus(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { collectionsHandler.handleRequestBody(wrapParams(req, "action", OVERSEERSTATUS.lowerName), rsp); } @EndPoint(method = GET, - path = "/cluster", - permission = COLL_READ_PERM) + path = "/cluster", + permission = COLL_READ_PERM) public void getCluster(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { collectionsHandler.handleRequestBody(wrapParams(req, "action", LIST.lowerName), rsp); } @EndPoint(method = DELETE, - path = "/cluster/command-status/{id}", - permission = COLL_EDIT_PERM) + path = "/cluster/command-status/{id}", + permission = COLL_EDIT_PERM) public void deleteCommandStatus(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { final Map<String, Object> v1Params = Maps.newHashMap(); v1Params.put(ACTION, DELETESTATUS.lowerName); v1Params.put(REQUESTID, req.getPathTemplateValues().get("id")); collectionsHandler.handleRequestBody(wrapParams(req, v1Params), rsp); } - + @EndPoint(method = DELETE, - path = "/cluster/command-status", - permission = COLL_EDIT_PERM) + path = "/cluster/command-status", + permission = COLL_EDIT_PERM) public void flushCommandStatus(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { CollectionsHandler.CollectionOperation.DELETESTATUS_OP.execute(req, rsp, collectionsHandler); } @EndPoint(method = DELETE, - path = "/cluster/configs/{name}", - permission = CONFIG_EDIT_PERM + path = "/cluster/configs/{name}", + permission = CONFIG_EDIT_PERM ) public void deleteConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { req = wrapParams(req, - "action", ConfigSetParams.ConfigSetAction.DELETE.toString(), - CommonParams.NAME, req.getPathTemplateValues().get("name")); + "action", ConfigSetParams.ConfigSetAction.DELETE.toString(), + CommonParams.NAME, req.getPathTemplateValues().get("name")); configSetsHandler.handleRequestBody(req, rsp); } @EndPoint(method = GET, - path = "/cluster/configs", - permission = CONFIG_READ_PERM) + path = "/cluster/configs", + permission = CONFIG_READ_PERM) public void listConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { req = wrapParams(req, "action", ConfigSetParams.ConfigSetAction.LIST.toString()); configSetsHandler.handleRequestBody(req, rsp); } @EndPoint(method = POST, - path = "/cluster/configs", - permission = CONFIG_EDIT_PERM + path = "/cluster/configs", + permission = CONFIG_EDIT_PERM ) public class ConfigSetCommands { @@ -188,8 +228,8 @@ public class ClusterAPI { @SuppressWarnings("unchecked") public void create(PayloadObj<CreateConfigPayload> obj) throws Exception { Map<String, Object> mapVals = obj.get().toMap(new HashMap<>()); - Map<String,Object> customProps = obj.get().properties; - if(customProps!= null) { + Map<String, Object> customProps = obj.get().properties; + if (customProps != null) { customProps.forEach((k, o) -> mapVals.put(ConfigSetCmds.CONFIG_SET_PROPERTY_PREFIX + k, o)); } mapVals.put("action", ConfigSetParams.ConfigSetAction.CREATE.toString()); @@ -199,8 +239,8 @@ public class ClusterAPI { } @EndPoint(method = PUT, - path = "/cluster/configs/{name}", - permission = CONFIG_EDIT_PERM + path = "/cluster/configs/{name}", + permission = CONFIG_EDIT_PERM ) public void uploadConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { req = wrapParams(req, @@ -212,8 +252,8 @@ public class ClusterAPI { } @EndPoint(method = PUT, - path = "/cluster/configs/{name}/*", - permission = CONFIG_EDIT_PERM + path = "/cluster/configs/{name}/*", + permission = CONFIG_EDIT_PERM ) public void insertIntoConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { String path = req.getPathTemplateValues().get("*"); @@ -236,17 +276,17 @@ public class ClusterAPI { public static SolrQueryRequest wrapParams(SolrQueryRequest req, Map<String, Object> m) { ModifiableSolrParams solrParams = new ModifiableSolrParams(); m.forEach((k, v) -> { - if(v == null) return; + if (v == null) return; solrParams.add(k.toString(), String.valueOf(v)); }); - DefaultSolrParams dsp = new DefaultSolrParams(req.getParams(),solrParams); + DefaultSolrParams dsp = new DefaultSolrParams(req.getParams(), solrParams); req.setParams(dsp); return req; } @EndPoint(method = GET, - path = "/cluster/command-status/{id}", - permission = COLL_READ_PERM) + path = "/cluster/command-status/{id}", + permission = COLL_READ_PERM) public void getCommandStatus(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { final Map<String, Object> v1Params = Maps.newHashMap(); v1Params.put(ACTION, REQUESTSTATUS.lowerName); @@ -255,8 +295,8 @@ public class ClusterAPI { } @EndPoint(method = GET, - path = "/cluster/nodes", - permission = COLL_READ_PERM) + path = "/cluster/nodes", + permission = COLL_READ_PERM) public void getNodes(SolrQueryRequest req, SolrQueryResponse rsp) { rsp.add("nodes", getCoreContainer().getZkController().getClusterState().getLiveNodes()); } @@ -266,13 +306,13 @@ public class ClusterAPI { } @EndPoint(method = POST, - path = "/cluster", - permission = COLL_EDIT_PERM) + path = "/cluster", + permission = COLL_EDIT_PERM) public class Commands { @Command(name = "add-role") public void addRole(PayloadObj<RoleInfo> obj) throws Exception { RoleInfo info = obj.get(); - Map<String,Object> m = info.toMap(new HashMap<>()); + Map<String, Object> m = info.toMap(new HashMap<>()); m.put("action", ADDROLE.toString()); collectionsHandler.handleRequestBody(wrapParams(obj.getRequest(), m), obj.getResponse()); } @@ -280,7 +320,7 @@ public class ClusterAPI { @Command(name = "remove-role") public void removeRole(PayloadObj<RoleInfo> obj) throws Exception { RoleInfo info = obj.get(); - Map<String,Object> m = info.toMap(new HashMap<>()); + Map<String, Object> m = info.toMap(new HashMap<>()); m.put("action", REMOVEROLE.toString()); collectionsHandler.handleRequestBody(wrapParams(obj.getRequest(), m), obj.getResponse()); } @@ -288,7 +328,7 @@ public class ClusterAPI { @Command(name = "set-obj-property") public void setObjProperty(PayloadObj<ClusterPropPayload> obj) { //Not using the object directly here because the API differentiate between {name:null} and {} - Map<String,Object> m = obj.getDataMap(); + Map<String, Object> m = obj.getDataMap(); ClusterProperties clusterProperties = new ClusterProperties(getCoreContainer().getZkController().getZkClient()); try { clusterProperties.setClusterProperties(m); @@ -298,8 +338,8 @@ public class ClusterAPI { } @Command(name = "set-property") - public void setProperty(PayloadObj<Map<String,String>> obj) throws Exception { - Map<String,Object> m = obj.getDataMap(); + public void setProperty(PayloadObj<Map<String, String>> obj) throws Exception { + Map<String, Object> m = obj.getDataMap(); m.put("action", CLUSTERPROP.toString()); collectionsHandler.handleRequestBody(wrapParams(obj.getRequest(), m), obj.getResponse()); } diff --git a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java index e5f2ee1..18a16fe 100644 --- a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java +++ b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java @@ -18,14 +18,14 @@ package org.apache.solr.cloud; import java.lang.invoke.MethodHandles; -import java.util.List; -import java.util.TreeSet; +import java.util.Collection; +import java.util.Collections; import org.apache.solr.client.solrj.embedded.JettySolrRunner; import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.client.solrj.request.V2Request; import org.apache.solr.client.solrj.response.V2Response; -import org.apache.solr.core.NodeRole; +import org.apache.solr.core.NodeRoles; import org.junit.After; import org.junit.Before; import org.slf4j.Logger; @@ -51,20 +51,34 @@ public class NodeRolesTest extends SolrCloudTestCase { JettySolrRunner j0 = cluster.getJettySolrRunner(0); JettySolrRunner j1 = null, j2 = null; - j1 = startNodeWithRoles("overseer"); + j1 = startNodeWithRoles("overseer:preferred,data:off"); V2Response rsp = new V2Request.Builder("/cluster/node-roles").GET().build().process(cluster.getSolrClient()); - assertEquals(List.of("overseer"), rsp._get(List.of("node-roles", j1.getNodeName()), null)); - + assertEquals(j1.getNodeName(), rsp._getStr("node-roles/overseer/preferred[0]", null)); + assertEquals(j1.getNodeName(), rsp._getStr("node-roles/data/off[0]", null)); OverseerRolesTest.waitForNewOverseer(20, j1.getNodeName(), false); //start another node that is overseer but has data - j2 = startNodeWithRoles("overseer,data"); + j2 = startNodeWithRoles("overseer:preferred,data:on"); rsp = new V2Request.Builder("/cluster/node-roles").GET().build().process(cluster.getSolrClient()); - assertEquals(List.of("data", "overseer"), rsp._get(List.of("node-roles", j2.getNodeName()), null)); + + + assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j2.getNodeName())); + assertTrue( ((Collection)rsp._get("node-roles/data/on", Collections.emptyList())).contains(j2.getNodeName())); + + rsp = new V2Request.Builder("/cluster/node-roles/overseer").GET().build().process(cluster.getSolrClient()); - assertEquals(new TreeSet<String>(List.of(j1.getNodeName(), j2.getNodeName())), new TreeSet<String>((List<String>) rsp._get("nodes", null))); + + + assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j2.getNodeName())); + assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j1.getNodeName())); + + rsp = new V2Request.Builder("/cluster/node-roles/overseer/preferred").GET().build().process(cluster.getSolrClient()); + assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j2.getNodeName())); + assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j1.getNodeName())); + + String COLLECTION_NAME = "TEST_ROLES"; CollectionAdminRequest @@ -79,17 +93,17 @@ public class NodeRolesTest extends SolrCloudTestCase { // Shutdown the dedicated overseer, make sure that node disappears from the roles output j1.stop(); - rsp = new V2Request.Builder("/cluster/node-roles").GET().build().process(cluster.getSolrClient()); - assertNull(rsp._get(List.of("node-roles", dedicatedOverseer), null)); + rsp = new V2Request.Builder("/cluster/node-roles/overseer/preferred").GET().build().process(cluster.getSolrClient()); + assertFalse (((Collection) rsp._get("overseer/preferred" , null)). contains(j1.getNodeName())); } private JettySolrRunner startNodeWithRoles(String roles) throws Exception { JettySolrRunner jetty; - System.setProperty(NodeRole.NODE_ROLES_PROP, roles); + System.setProperty(NodeRoles.NODE_ROLES_PROP, roles); try { jetty = cluster.startJettySolrRunner(); } finally { - System.clearProperty(NodeRole.NODE_ROLES_PROP); + System.clearProperty(NodeRoles.NODE_ROLES_PROP); } return jetty; } diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java index 84010c8..feae27f 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java @@ -24,10 +24,8 @@ import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; -import java.util.function.BiConsumer; import org.apache.solr.common.SolrCloseable; import org.apache.solr.common.cloud.PerReplicaStates; -import org.apache.solr.common.cloud.ZkStateReader; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Op; @@ -143,25 +141,4 @@ public interface DistribStateManager extends SolrCloseable { } } } - default void forEachChild(String path, BiConsumer<String, VersionedData> fun) - throws InterruptedException, IOException, KeeperException { - List<String> children = null; - try { - children = listData(path); - } catch (NoSuchElementException e) { - return; - } - if (!children.isEmpty()) { - for (String node: children) { - VersionedData data = null; - try { - data = getData(path + "/" + node); - } catch (NoSuchElementException e) { - // this node must've been deleted - continue; - } - fun.accept(node, data); - } - } - } }
