Skip to content

Commit

Permalink
fix:
Browse files Browse the repository at this point in the history
1. Added switch control function on and off
2. Add unit tests and usage documentation
3. Update the CHANGES.md
4. Function and code style optimization
  • Loading branch information
youngzil committed Sep 29, 2024
1 parent abb9a20 commit 2195d79
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 8 deletions.
5 changes: 3 additions & 2 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Apollo 2.4.0
* [Update the server config link in system info page](https://github.com/apolloconfig/apollo/pull/5204)
* [Feature support portal restTemplate Client connection pool config](https://github.com/apolloconfig/apollo/pull/5200)
* [Feature added the ability for administrators to globally search for Value](https://github.com/apolloconfig/apollo/pull/5182)

* [Feature added determine appid+cluster namespace num limit logic](https://github.com/apolloconfig/apollo/pull/5227)
*
------------------
All issues and pull requests are [here](https://github.com/apolloconfig/apollo/milestone/15?closed=1)
All issues and pull requests are [here](https://github.com/apolloconfig/apollo/milestone/15?closed=1)
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ public class BizConfig extends RefreshableConfig {
private static final int DEFAULT_ITEM_VALUE_LENGTH = 20000;

private static final int DEFAULT_MAX_NAMESPACE_NUM = 200;
private static final String[] DEFAULT_NAMESPACE_NUM_LIMIT_WHITE = new String[]{};

private static final int DEFAULT_APPNAMESPACE_CACHE_REBUILD_INTERVAL = 60; //60s
private static final int DEFAULT_GRAY_RELEASE_RULE_SCAN_INTERVAL = 60; //60s
Expand Down Expand Up @@ -105,13 +104,17 @@ public int itemValueLengthLimit() {
return checkInt(limit, 5, Integer.MAX_VALUE, DEFAULT_ITEM_VALUE_LENGTH);
}

public boolean isNamespaceNumLimitEnabled() {
return getBooleanProperty("namespace.num.limit.enabled", false);
}

public int namespaceNumLimit() {
int limit = getIntProperty("namespace.num.limit", DEFAULT_MAX_NAMESPACE_NUM);
return checkInt(limit, 0, Integer.MAX_VALUE, DEFAULT_MAX_NAMESPACE_NUM);
}

public Set<String> namespaceNumLimitWhite() {
return Sets.newHashSet(getArrayProperty("namespace.num.limit.white", DEFAULT_NAMESPACE_NUM_LIMIT_WHITE));
return Sets.newHashSet(getArrayProperty("namespace.num.limit.white", new String[0]));
}

public Map<Long, Integer> namespaceValueLengthLimitOverride() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -354,9 +354,9 @@ public Namespace save(Namespace entity) {
throw new ServiceException("namespace not unique");
}

if (!bizConfig.namespaceNumLimitWhite().contains(entity.getAppId())){
int nowCount = namespaceRepository.countByAppIdAndClusterName(entity.getAppId(), entity.getClusterName()) ;
if(nowCount >= bizConfig.namespaceNumLimit()) {
if (bizConfig.isNamespaceNumLimitEnabled() && !bizConfig.namespaceNumLimitWhite().contains(entity.getAppId())) {
int nowCount = namespaceRepository.countByAppIdAndClusterName(entity.getAppId(), entity.getClusterName());
if (nowCount >= bizConfig.namespaceNumLimit()) {
throw new ServiceException("namespace[appId = " + entity.getAppId() + ", cluster= " + entity.getClusterName() + "] nowCount= " + nowCount + ", maxCount =" + bizConfig.namespaceNumLimit());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.ctrip.framework.apollo.biz.service;

import com.ctrip.framework.apollo.biz.AbstractIntegrationTest;
import com.ctrip.framework.apollo.biz.config.BizConfig;
import com.ctrip.framework.apollo.biz.entity.Cluster;
import com.ctrip.framework.apollo.biz.entity.Commit;
import com.ctrip.framework.apollo.biz.entity.InstanceConfig;
Expand All @@ -25,23 +26,31 @@
import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.entity.ReleaseHistory;
import com.ctrip.framework.apollo.biz.repository.InstanceConfigRepository;
import com.ctrip.framework.apollo.biz.repository.NamespaceRepository;
import com.ctrip.framework.apollo.common.entity.AppNamespace;

import com.ctrip.framework.apollo.common.exception.ServiceException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.context.jdbc.Sql;

import java.util.List;
import org.springframework.test.util.ReflectionTestUtils;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;

public class NamespaceServiceIntegrationTest extends AbstractIntegrationTest {

Expand All @@ -62,6 +71,11 @@ public class NamespaceServiceIntegrationTest extends AbstractIntegrationTest {
private ReleaseHistoryService releaseHistoryService;
@Autowired
private InstanceConfigRepository instanceConfigRepository;
@Autowired
private NamespaceRepository namespaceRepository;

@Mock
private BizConfig bizConfig;

private String testApp = "testApp";
private String testCluster = "default";
Expand Down Expand Up @@ -134,4 +148,97 @@ public void testGetCommitsByModifiedTime() throws ParseException {
}


@Test
@Sql(scripts = "/sql/namespace-test.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testNamespaceNumLimit() {

ReflectionTestUtils.setField(namespaceService, "bizConfig", bizConfig);
when(bizConfig.isNamespaceNumLimitEnabled()).thenReturn(true);
when(bizConfig.namespaceNumLimit()).thenReturn(2);


Namespace namespace = new Namespace();
namespace.setAppId(testApp);
namespace.setClusterName(testCluster);
namespace.setNamespaceName("demo-namespace");
namespaceService.save(namespace);

try {
Namespace namespace2 = new Namespace();
namespace2.setAppId(testApp);
namespace2.setClusterName(testCluster);
namespace2.setNamespaceName("demo-namespace2");
namespaceService.save(namespace2);

Assert.fail();
} catch (Exception e) {
Assert.assertTrue(e instanceof ServiceException);
}

int nowCount = namespaceRepository.countByAppIdAndClusterName(testApp, testCluster);
Assert.assertEquals(2, nowCount);

}

@Test
@Sql(scripts = "/sql/namespace-test.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testNamespaceNumLimitFalse() {

ReflectionTestUtils.setField(namespaceService, "bizConfig", bizConfig);
when(bizConfig.namespaceNumLimit()).thenReturn(2);

Namespace namespace = new Namespace();
namespace.setAppId(testApp);
namespace.setClusterName(testCluster);
namespace.setNamespaceName("demo-namespace");
namespaceService.save(namespace);

try {
Namespace namespace2 = new Namespace();
namespace2.setAppId(testApp);
namespace2.setClusterName(testCluster);
namespace2.setNamespaceName("demo-namespace2");
namespaceService.save(namespace2);
} catch (Exception e) {
Assert.assertTrue(e instanceof ServiceException);
}

int nowCount = namespaceRepository.countByAppIdAndClusterName(testApp, testCluster);
Assert.assertEquals(3, nowCount);

}

@Test
@Sql(scripts = "/sql/namespace-test.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testNamespaceNumLimitWhite() {

ReflectionTestUtils.setField(namespaceService, "bizConfig", bizConfig);
when(bizConfig.isNamespaceNumLimitEnabled()).thenReturn(true);
when(bizConfig.namespaceNumLimit()).thenReturn(2);
when(bizConfig.namespaceNumLimitWhite()).thenReturn(new HashSet<>(Arrays.asList(testApp)));

Namespace namespace = new Namespace();
namespace.setAppId(testApp);
namespace.setClusterName(testCluster);
namespace.setNamespaceName("demo-namespace");
namespaceService.save(namespace);

try {
Namespace namespace2 = new Namespace();
namespace2.setAppId(testApp);
namespace2.setClusterName(testCluster);
namespace2.setNamespaceName("demo-namespace2");
namespaceService.save(namespace2);
} catch (Exception e) {
Assert.assertTrue(e instanceof ServiceException);
}

int nowCount = namespaceRepository.countByAppIdAndClusterName(testApp, testCluster);
Assert.assertEquals(3, nowCount);

}

}
Binary file added doc/images/namespace-num-limit-enabled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/namespace-num-limit-white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/namespace-num-limit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions docs/en/portal/apollo-user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,33 @@ Please note that modifications to system parameters may affect the performance o

![System-parameterization-of-global-search-configuration-items](../images/System-parameterization-of-global-search-configuration-items.png)



## 6.4 单个命名空间下的配置项数量限制

Starting from version 2.4.0, apollo-portal provides the function of checking the upper limit of the number of namespaces that can be created under the appld+cluster dimension. This function is disabled by default and needs to be enabled by configuring the system `namespace.num.limit.enabled`. At the same time, the system parameter `namespace.num.limit` is provided to dynamically configure the upper limit of the number of Namespaces under the appld+cluster dimension. The default value is 200. Considering that some basic components such as gateways, message queues, Redis, and databases require special processing, a new system parameter `namespace.num.limit.white` is added to configure the verification whitelist, which is not affected by the upper limit of the number of Namespaces.

**Setting method:**

1. Log in to the Apollo Configuration Center interface with a super administrator account.
2. Go to the `Administrator Tools - System Parameters - ConfigDB Configuration Management` page and add or modify the `namespace.num.limit.enabled` configuration item to true/false to enable/disable this function. It is disabled by default.

[//]: # ( ![item-num-limit-enabled]&#40;https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/namespace-num-limit-enabled.png&#41;)
![item-num-limit-enabled](../../../doc/images/namespace-num-limit-enabled.png)

3. Go to the `Administrator Tools - System Parameters - ConfigDB Configuration Management` page to add or modify the `namespace.num.limit` configuration item to configure the upper limit of the number of namespaces under a single appld+cluster. The default value is 200

[//]: # ( ![item-num-limit]&#40;https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/namespace-num-limit.png&#41;)
![item-num-limit](../../../doc/images/namespace-num-limit.png)

4. Go to `Administrator Tools - System Parameters - ConfigDB Configuration Management` page to add or modify the `namespace.num.limit.white` configuration item to configure the whitelist for namespace quantity limit verification. Multiple AppIds are separated by English commas.

[//]: # ( ![item-num-limit]&#40;https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/namespace-num-limit-white.png&#41;)
![item-num-limit](../../../doc/images/namespace-num-limit-white.png)




# VII. Best practices

## 7.1 Security Related
Expand Down
24 changes: 23 additions & 1 deletion docs/zh/portal/apollo-user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,28 @@ Apollo从1.6.0版本开始增加访问密钥机制,从而只有经过身份验

![System-parameterization-of-global-search-configuration-items](../images/System-parameterization-of-global-search-configuration-items.png)


## 6.4 单个命名空间下的配置项数量限制
从2.4.0版本开始,apollo-portal提供了appld+cluster维度下可以创建的命名空间数量上限校验的功能,此功能默认关闭,需要配置系统 `namespace.num.limit.enabled` 开启,同时提供了系统参数`namespace.num.limit`来动态配置appld+cluster维度下的Namespace数量上限值,默认为200个,考虑到一些基础组件如网关、消息队列、Redis、数据库等需要特殊处理,新增了系统参数`namespace.num.limit.white` 来配置校验白名单,不受Namespace数量上限的影响

**设置方法:**
1. 用超级管理员账号登录到Apollo配置中心的界面
2. 进入 `管理员工具 - 系统参数 - ConfigDB 配置管理` 页面新增或修改 `namespace.num.limit.enabled` 配置项为true/false 即可开启/关闭此功能,默认关闭

[//]: # ( ![item-num-limit-enabled]&#40;https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/namespace-num-limit-enabled.png&#41;)
![item-num-limit-enabled](../../../doc/images/namespace-num-limit-enabled.png)
3. 进入 `管理员工具 - 系统参数 - ConfigDB 配置管理` 页面新增或修改 `namespace.num.limit` 配置项来配置单个appld+cluster下的namespace数量上限值,默认为200

[//]: # ( ![item-num-limit]&#40;https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/namespace-num-limit.png&#41;)
![item-num-limit](../../../doc/images/namespace-num-limit.png)

4. 进入 `管理员工具 - 系统参数 - ConfigDB 配置管理` 页面新增或修改 `namespace.num.limit.white` 配置项来配置namespace数量上限校验的白名单,多个AppId使用英文逗号分隔

[//]: # ( ![item-num-limit]&#40;https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/namespace-num-limit-white.png&#41;)
![item-num-limit](../../../doc/images/namespace-num-limit-white.png)



# 七、最佳实践

## 7.1 安全相关
Expand Down Expand Up @@ -512,4 +534,4 @@ Apollo 支持细粒度的权限控制,请务必根据实际情况做好权限
1. `apollo-configservice``apollo-adminservice`是基于内网可信网络设计的,所以出于安全考虑,禁止`apollo-configservice``apollo-adminservice`直接暴露在公网
2. 对敏感配置可以考虑开启[访问秘钥](#_62-%e9%85%8d%e7%bd%ae%e8%ae%bf%e9%97%ae%e5%af%86%e9%92%a5),从而只有经过身份验证的客户端才能访问敏感配置
3. 1.7.1及以上版本可以考虑为`apollo-adminservice`开启[访问控制](zh/deployment/distributed-deployment-guide?id=_326-admin-serviceaccesscontrolenabled-配置apollo-adminservice是否开启访问控制),从而只有[受控的](zh/deployment/distributed-deployment-guide?id=_3112-admin-serviceaccesstokens-设置apollo-portal访问各环境apollo-adminservice所需的access-token)`apollo-portal`才能访问对应接口,增强安全性
4. 2.1.0及以上版本可以考虑为`eureka`开启[访问控制](zh/deployment/distributed-deployment-guide?id=_329-apolloeurekaserversecurityenabled-配置是否开启eureka-server的登录认证),从而只有受控的`apollo-configservice``apollo-adminservice`可以注册到`eureka`,增强安全性
4. 2.1.0及以上版本可以考虑为`eureka`开启[访问控制](zh/deployment/distributed-deployment-guide?id=_329-apolloeurekaserversecurityenabled-配置是否开启eureka-server的登录认证),从而只有受控的`apollo-configservice``apollo-adminservice`可以注册到`eureka`,增强安全性

0 comments on commit 2195d79

Please sign in to comment.