This is an automated email from the ASF dual-hosted git repository.
deardeng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 788baa53665 [feature](compute group) Support show compute groups in
non cloud mode (#66697)
788baa53665 is described below
commit 788baa53665c96312a10b2d3830094b34d21c5c7
Author: deardeng <[email protected]>
AuthorDate: Wed Aug 19 14:22:23 2026 +0800
[feature](compute group) Support show compute groups in non cloud mode
(#66697)
SHOW CLUSTERS / SHOW COMPUTE GROUPS were rejected with
ERR_NOT_CLOUD_MODE in non cloud mode. In non cloud mode a resource group
(the backend location tag) is the counterpart of a cloud compute group,
so show it instead:
- only Name/BackendNum (cluster/backend_num for SHOW CLUSTERS) are
shown, IsCurrent/Users/SubComputeGroups/Policy/Properties are cloud
only.
- a user only sees the resource groups it is allowed to use, which is
the compute group resolved from resource_tags.location, the same
visibility the query engine uses to pick backends. This replaces the
global ADMIN check, and matches cloud mode where clusters are filtered
by usage priv.
<img width="454" height="538" alt="image"
src="https://github.com/user-attachments/assets/3374efd8-0a00-43de-94dc-043d73c21ed1"
/>
---
.../trees/plans/commands/ShowClustersCommand.java | 42 ++++++++++-------
.../trees/plans/commands/ShowComputeGroupTest.java | 55 ++++++++++++++++++----
.../show_p0/test_show_compute_groups_docker.out | 9 ++++
.../suites/show_p0/test_show_compute_groups.groovy | 53 +++++++++++++++++++++
.../show_p0/test_show_compute_groups_docker.groovy | 35 ++++++++++++++
5 files changed, 167 insertions(+), 27 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowClustersCommand.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowClustersCommand.java
index c774849ed48..92bf2c30b85 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowClustersCommand.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowClustersCommand.java
@@ -25,10 +25,7 @@ import org.apache.doris.catalog.ScalarType;
import org.apache.doris.cloud.catalog.CloudComputeGroupMeta;
import org.apache.doris.cloud.qe.ComputeGroupException;
import org.apache.doris.cloud.system.CloudSystemInfoService;
-import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.Config;
-import org.apache.doris.common.ErrorCode;
-import org.apache.doris.common.ErrorReport;
import org.apache.doris.mysql.privilege.Auth;
import org.apache.doris.mysql.privilege.PrivBitSet;
import org.apache.doris.mysql.privilege.PrivPredicate;
@@ -39,6 +36,7 @@ import org.apache.doris.qe.ConnectContext;
import org.apache.doris.qe.ShowResultSet;
import org.apache.doris.qe.ShowResultSetMetaData;
import org.apache.doris.qe.StmtExecutor;
+import org.apache.doris.resource.computegroup.ComputeGroup;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
@@ -49,7 +47,9 @@ import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
import java.util.stream.Collectors;
/**
@@ -64,6 +64,11 @@ public class ShowClustersCommand extends ShowCommand {
public static final ImmutableList<String> COMPUTE_GROUP_TITLE_NAMES = new
ImmutableList.Builder<String>()
.add("Name").add("IsCurrent").add("Users").add("BackendNum")
.add("SubComputeGroups").add("Policy").add("Properties").build();
+ // non cloud mode, a resource group(backend location tag) is the
counterpart of a cloud compute group
+ public static final ImmutableList<String> CLUSTER_TITLE_NAMES_NON_CLOUD =
new ImmutableList.Builder<String>()
+ .add("cluster").add("backend_num").build();
+ public static final ImmutableList<String>
COMPUTE_GROUP_TITLE_NAMES_NON_CLOUD = new ImmutableList.Builder<String>()
+ .add("Name").add("BackendNum").build();
private static final Logger LOG =
LogManager.getLogger(ShowClustersCommand.class);
private final boolean isComputeGroup;
@@ -73,22 +78,23 @@ public class ShowClustersCommand extends ShowCommand {
this.isComputeGroup = isComputeGroup;
}
- private void validate(ConnectContext ctx) throws AnalysisException {
- if (Config.isNotCloudMode()) {
- // just user admin
- if
(!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get().getCurrentUserIdentity(),
- PrivPredicate.of(PrivBitSet.of(Privilege.ADMIN_PRIV,
Privilege.NODE_PRIV), Operator.OR))) {
-
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR,
"ADMIN");
- }
- }
- }
-
@Override
public ShowResultSet doRun(ConnectContext ctx, StmtExecutor executor)
throws Exception {
- validate(ctx);
final List<List<String>> rows = Lists.newArrayList();
- if (!Config.isCloudMode()) {
- ErrorReport.reportAnalysisException(ErrorCode.ERR_NOT_CLOUD_MODE);
+ if (Config.isNotCloudMode()) {
+ // resource group is the compute group of non cloud mode, a user
only sees the resource groups
+ // it is allowed to use, same as the cloud mode which filters
clusters by usage priv.
+ ComputeGroup userComputeGroup = ctx.getComputeGroup();
+ if (ComputeGroup.INVALID_COMPUTE_GROUP == userComputeGroup) {
+ return new ShowResultSet(getMetaData(), rows);
+ }
+ Map<String, Long> backendNumByGroup =
Env.getCurrentSystemInfo().getAllClusterBackends(false).stream()
+ .map(be -> be.getLocationTag().value)
+ .filter(userComputeGroup::containsBackend)
+ .collect(Collectors.groupingBy(name -> name, TreeMap::new,
Collectors.counting()));
+ for (Map.Entry<String, Long> entry : backendNumByGroup.entrySet())
{
+ rows.add(Lists.newArrayList(entry.getKey(),
String.valueOf(entry.getValue())));
+ }
return new ShowResultSet(getMetaData(), rows);
}
@@ -181,9 +187,9 @@ public class ShowClustersCommand extends ShowCommand {
ImmutableList<String> titleNames = null;
if (isComputeGroup) {
- titleNames = COMPUTE_GROUP_TITLE_NAMES;
+ titleNames = Config.isNotCloudMode() ?
COMPUTE_GROUP_TITLE_NAMES_NON_CLOUD : COMPUTE_GROUP_TITLE_NAMES;
} else {
- titleNames = CLUSTER_TITLE_NAMES;
+ titleNames = Config.isNotCloudMode() ?
CLUSTER_TITLE_NAMES_NON_CLOUD : CLUSTER_TITLE_NAMES;
}
for (String title : titleNames) {
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowComputeGroupTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowComputeGroupTest.java
index 06588301872..cab5353856c 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowComputeGroupTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowComputeGroupTest.java
@@ -18,11 +18,14 @@
package org.apache.doris.nereids.trees.plans.commands;
import org.apache.doris.catalog.Column;
-import org.apache.doris.common.AnalysisException;
+import org.apache.doris.catalog.Env;
import org.apache.doris.common.Config;
import org.apache.doris.qe.ShowResultSetMetaData;
+import org.apache.doris.resource.Tag;
+import org.apache.doris.system.Backend;
import org.apache.doris.utframe.TestWithFeService;
+import com.google.common.collect.Lists;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -38,6 +41,7 @@ public class ShowComputeGroupTest extends TestWithFeService {
@Test
public void testShowComputeGroupsInCloudMode() throws Exception {
Config.deploy_mode = "cloud";
+ Config.cloud_unique_id = "cloud_unique_id";
ShowClustersCommand command = new ShowClustersCommand(true);
ShowResultSetMetaData metaData = command.getMetaData();
Assertions.assertNotNull(metaData);
@@ -56,15 +60,47 @@ public class ShowComputeGroupTest extends TestWithFeService
{
@Test
public void testShowComputeGroupsInNonCloudMode() throws Exception {
- Config.deploy_mode = "not-cloud";
+ Config.deploy_mode = "";
+ Config.cloud_unique_id = "";
+ Tag groupA = Tag.create(Tag.TYPE_LOCATION, "group_a");
+ Backend groupABackend1 = addNewBackend();
+ Backend groupABackend2 = addNewBackend();
+ Backend groupBBackend = addNewBackend();
+ groupABackend1.setTagMap(groupA.toMap());
+ groupABackend2.setTagMap(groupA.toMap());
+ groupBBackend.setTagMap(Tag.create(Tag.TYPE_LOCATION,
"group_b").toMap());
+
ShowClustersCommand command = new ShowClustersCommand(true);
- Assertions.assertThrows(AnalysisException.class, () -> {
- command.doRun(connectContext, null);
- });
+ List<String> columnNames = command.getMetaData().getColumns().stream()
+ .map(Column::getName).collect(Collectors.toList());
+ Assertions.assertEquals(Lists.newArrayList("Name", "BackendNum"),
columnNames);
+ List<List<String>> rows = command.doRun(connectContext,
null).getResultRows();
+ List<List<String>> expectedRows = Lists.newArrayList(
+ Lists.newArrayList(Tag.VALUE_DEFAULT_TAG, "1"),
+ Lists.newArrayList("group_a", "2"),
+ Lists.newArrayList("group_b", "1"));
+ Assertions.assertEquals(expectedRows, rows);
+
+ // a user restricted by resource_tags.location only sees the resource
groups it can use,
+ // this is the compute group bound to the session when the user logs
in.
+ executeSql("CREATE USER show_cg_user IDENTIFIED BY '12345'");
+ try {
+ executeSql("SET PROPERTY FOR 'show_cg_user'
'resource_tags.location' = 'group_a'");
+
connectContext.setComputeGroup(Env.getCurrentEnv().getAuth().getComputeGroup("show_cg_user"));
+ Assertions.assertEquals(expectedRows.subList(1, 2),
command.doRun(connectContext, null).getResultRows());
+
+ executeSql("SET PROPERTY FOR 'show_cg_user'
'resource_tags.location' = 'no_such_resource_group'");
+
connectContext.setComputeGroup(Env.getCurrentEnv().getAuth().getComputeGroup("show_cg_user"));
+ Assertions.assertTrue(command.doRun(connectContext,
null).getResultRows().isEmpty());
+ } finally {
+ connectContext.setComputeGroup(null);
+ }
}
@Test
public void testShowClustersInCloudMode() throws Exception {
+ Config.deploy_mode = "cloud";
+ Config.cloud_unique_id = "cloud_unique_id";
ShowClustersCommand command = new ShowClustersCommand(false);
ShowResultSetMetaData metaData = command.getMetaData();
Assertions.assertNotNull(metaData);
@@ -82,10 +118,11 @@ public class ShowComputeGroupTest extends
TestWithFeService {
@Test
public void testShowClustersInNonCloudMode() throws Exception {
- Config.deploy_mode = "not-cloud";
+ Config.deploy_mode = "";
+ Config.cloud_unique_id = "";
ShowClustersCommand command = new ShowClustersCommand(false);
- Assertions.assertThrows(AnalysisException.class, () -> {
- command.doRun(connectContext, null);
- });
+ List<String> columnNames = command.getMetaData().getColumns().stream()
+ .map(Column::getName).collect(Collectors.toList());
+ Assertions.assertEquals(Lists.newArrayList("cluster", "backend_num"),
columnNames);
}
}
diff --git a/regression-test/data/show_p0/test_show_compute_groups_docker.out
b/regression-test/data/show_p0/test_show_compute_groups_docker.out
new file mode 100644
index 00000000000..9543f08d0d6
--- /dev/null
+++ b/regression-test/data/show_p0/test_show_compute_groups_docker.out
@@ -0,0 +1,9 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !show_compute_groups --
+group_a 2
+group_b 1
+
+-- !show_clusters --
+group_a 2
+group_b 1
+
diff --git a/regression-test/suites/show_p0/test_show_compute_groups.groovy
b/regression-test/suites/show_p0/test_show_compute_groups.groovy
new file mode 100644
index 00000000000..1ff55109a72
--- /dev/null
+++ b/regression-test/suites/show_p0/test_show_compute_groups.groovy
@@ -0,0 +1,53 @@
+// 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.
+
+suite("test_show_compute_groups") {
+ if (isCloudMode()) {
+ return
+ }
+
+ // in non cloud mode, a compute group is a resource group, aka the backend
location tag
+ def groups = sql "SHOW COMPUTE GROUPS"
+ assertTrue(!groups.isEmpty())
+ groups.each { row ->
+ assertEquals(2, row.size())
+ assertTrue(Integer.parseInt(row[1].toString()) > 0)
+ }
+ assertEquals(groups, sql("SHOW CLUSTERS"))
+
+ // a common user only sees the resource groups it is allowed to use
+ String user = 'test_show_compute_groups_user'
+ String pwd = 'C123_567p'
+ try_sql("DROP USER ${user}")
+ sql """CREATE USER '${user}' IDENTIFIED BY '${pwd}'"""
+ try {
+ def boundGroup = groups[0]
+ def noDbJdbcUrl =
context.config.jdbcUrl.replaceFirst(/(jdbc:mysql:\/\/[^\/]+\/)[^?]*/, '$1')
+ sql """SET PROPERTY FOR '${user}' 'resource_tags.location' =
'${boundGroup[0]}'"""
+ connect(user, "${pwd}", noDbJdbcUrl) {
+ assertEquals([boundGroup], sql("SHOW COMPUTE GROUPS"))
+ }
+
+ // no such resource group, the user sees nothing instead of an error
+ sql """SET PROPERTY FOR '${user}' 'resource_tags.location' =
'no_such_resource_group'"""
+ connect(user, "${pwd}", noDbJdbcUrl) {
+ assertTrue(sql("SHOW COMPUTE GROUPS").isEmpty())
+ }
+ } finally {
+ try_sql("DROP USER ${user}")
+ }
+}
diff --git
a/regression-test/suites/show_p0/test_show_compute_groups_docker.groovy
b/regression-test/suites/show_p0/test_show_compute_groups_docker.groovy
new file mode 100644
index 00000000000..7fc280bc4fd
--- /dev/null
+++ b/regression-test/suites/show_p0/test_show_compute_groups_docker.groovy
@@ -0,0 +1,35 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+import org.apache.doris.regression.suite.ClusterOptions
+
+suite("test_show_compute_groups_docker", "docker") {
+ def options = new ClusterOptions()
+ options.cloudMode = false
+ options.feNum = 1
+ options.beNum = 3
+
+ docker(options) {
+ def backends = sql_return_maparray "SHOW BACKENDS"
+ sql """ALTER SYSTEM MODIFY BACKEND "${backends[0].BackendId}" SET
("tag.location" = "group_a")"""
+ sql """ALTER SYSTEM MODIFY BACKEND "${backends[1].BackendId}" SET
("tag.location" = "group_a")"""
+ sql """ALTER SYSTEM MODIFY BACKEND "${backends[2].BackendId}" SET
("tag.location" = "group_b")"""
+
+ order_qt_show_compute_groups "SHOW COMPUTE GROUPS"
+ order_qt_show_clusters "SHOW CLUSTERS"
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]