Skip to content

Commit 597242a

Browse files
committed
Merge branch 'master' into feature/namespace-item-num-limit
# Conflicts: # CHANGES.md # apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/config/BizConfig.java # docs/en/portal/apollo-user-guide.md # docs/zh/portal/apollo-user-guide.md
2 parents 998459e + 94c28af commit 597242a

14 files changed

Lines changed: 243 additions & 58 deletions

File tree

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ Apollo 2.4.0
88
* [Update the server config link in system info page](https://github.com/apolloconfig/apollo/pull/5204)
99
* [Feature support portal restTemplate Client connection pool config](https://github.com/apolloconfig/apollo/pull/5200)
1010
* [Feature added the ability for administrators to globally search for Value](https://github.com/apolloconfig/apollo/pull/5182)
11+
* [Fix: Resolve issues with duplicate comments and blank lines in configuration management](https://github.com/apolloconfig/apollo/pull/5232)
1112
* [Fix link namespace published items show missing some items](https://github.com/apolloconfig/apollo/pull/5240)
13+
* [Feature: Add limit and whitelist for namespace count per appid+cluster](https://github.com/apolloconfig/apollo/pull/5228)
1214
* [Feature added limit the number of items under a single namespace](https://github.com/apolloconfig/apollo/pull/5228)
1315

1416
------------------

apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/config/BizConfig.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@
2121
import com.ctrip.framework.apollo.common.config.RefreshablePropertySource;
2222
import com.google.common.base.Strings;
2323
import com.google.common.collect.Maps;
24+
import com.google.common.collect.Sets;
2425
import com.google.gson.Gson;
2526
import com.google.gson.reflect.TypeToken;
2627
import java.lang.reflect.Type;
2728
import java.util.Collections;
2829
import java.util.List;
2930
import java.util.Map;
31+
import java.util.Set;
3032
import java.util.concurrent.TimeUnit;
3133
import java.util.stream.Collectors;
3234
import org.springframework.stereotype.Component;
@@ -37,6 +39,8 @@ public class BizConfig extends RefreshableConfig {
3739
private static final int DEFAULT_ITEM_KEY_LENGTH = 128;
3840
private static final int DEFAULT_ITEM_VALUE_LENGTH = 20000;
3941

42+
private static final int DEFAULT_MAX_NAMESPACE_NUM = 200;
43+
4044
private static final int DEFAULT_MAX_ITEM_NUM = 1000;
4145

4246
private static final int DEFAULT_APPNAMESPACE_CACHE_REBUILD_INTERVAL = 60; //60s
@@ -102,6 +106,19 @@ public int itemValueLengthLimit() {
102106
return checkInt(limit, 5, Integer.MAX_VALUE, DEFAULT_ITEM_VALUE_LENGTH);
103107
}
104108

109+
public boolean isNamespaceNumLimitEnabled() {
110+
return getBooleanProperty("namespace.num.limit.enabled", false);
111+
}
112+
113+
public int namespaceNumLimit() {
114+
int limit = getIntProperty("namespace.num.limit", DEFAULT_MAX_NAMESPACE_NUM);
115+
return checkInt(limit, 0, Integer.MAX_VALUE, DEFAULT_MAX_NAMESPACE_NUM);
116+
}
117+
118+
public Set<String> namespaceNumLimitWhite() {
119+
return Sets.newHashSet(getArrayProperty("namespace.num.limit.white", new String[0]));
120+
}
121+
105122
public boolean isItemNumLimitEnabled() {
106123
return getBooleanProperty("item.num.limit.enabled", false);
107124
}

apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/repository/NamespaceRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,6 @@ public interface NamespaceRepository extends PagingAndSortingRepository<Namespac
4444

4545
int countByNamespaceNameAndAppIdNot(String namespaceName, String appId);
4646

47+
int countByAppIdAndClusterName(String appId, String clusterName);
48+
4749
}

apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/service/NamespaceService.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
package com.ctrip.framework.apollo.biz.service;
1818

19+
import com.ctrip.framework.apollo.biz.config.BizConfig;
1920
import com.ctrip.framework.apollo.biz.entity.Audit;
2021
import com.ctrip.framework.apollo.biz.entity.Cluster;
2122
import com.ctrip.framework.apollo.biz.entity.Item;
@@ -68,6 +69,7 @@ public class NamespaceService {
6869
private final NamespaceLockService namespaceLockService;
6970
private final InstanceService instanceService;
7071
private final MessageSender messageSender;
72+
private final BizConfig bizConfig;
7173

7274
public NamespaceService(
7375
final ReleaseHistoryService releaseHistoryService,
@@ -81,7 +83,8 @@ public NamespaceService(
8183
final @Lazy ClusterService clusterService,
8284
final @Lazy NamespaceBranchService namespaceBranchService,
8385
final NamespaceLockService namespaceLockService,
84-
final InstanceService instanceService) {
86+
final InstanceService instanceService,
87+
final BizConfig bizConfig) {
8588
this.releaseHistoryService = releaseHistoryService;
8689
this.namespaceRepository = namespaceRepository;
8790
this.auditService = auditService;
@@ -94,6 +97,7 @@ public NamespaceService(
9497
this.namespaceBranchService = namespaceBranchService;
9598
this.namespaceLockService = namespaceLockService;
9699
this.instanceService = instanceService;
100+
this.bizConfig = bizConfig;
97101
}
98102

99103

@@ -349,6 +353,14 @@ public Namespace save(Namespace entity) {
349353
if (!isNamespaceUnique(entity.getAppId(), entity.getClusterName(), entity.getNamespaceName())) {
350354
throw new ServiceException("namespace not unique");
351355
}
356+
357+
if (bizConfig.isNamespaceNumLimitEnabled() && !bizConfig.namespaceNumLimitWhite().contains(entity.getAppId())) {
358+
int nowCount = namespaceRepository.countByAppIdAndClusterName(entity.getAppId(), entity.getClusterName());
359+
if (nowCount >= bizConfig.namespaceNumLimit()) {
360+
throw new ServiceException("namespace[appId = " + entity.getAppId() + ", cluster= " + entity.getClusterName() + "] nowCount= " + nowCount + ", maxCount =" + bizConfig.namespaceNumLimit());
361+
}
362+
}
363+
352364
entity.setId(0);//protection
353365
Namespace namespace = namespaceRepository.save(entity);
354366

apollo-biz/src/test/java/com/ctrip/framework/apollo/biz/service/NamespaceServiceIntegrationTest.java

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.ctrip.framework.apollo.biz.service;
1818

1919
import com.ctrip.framework.apollo.biz.AbstractIntegrationTest;
20+
import com.ctrip.framework.apollo.biz.config.BizConfig;
2021
import com.ctrip.framework.apollo.biz.entity.Cluster;
2122
import com.ctrip.framework.apollo.biz.entity.Commit;
2223
import com.ctrip.framework.apollo.biz.entity.InstanceConfig;
@@ -25,23 +26,31 @@
2526
import com.ctrip.framework.apollo.biz.entity.Release;
2627
import com.ctrip.framework.apollo.biz.entity.ReleaseHistory;
2728
import com.ctrip.framework.apollo.biz.repository.InstanceConfigRepository;
29+
import com.ctrip.framework.apollo.biz.repository.NamespaceRepository;
2830
import com.ctrip.framework.apollo.common.entity.AppNamespace;
2931

32+
import com.ctrip.framework.apollo.common.exception.ServiceException;
3033
import java.text.ParseException;
3134
import java.text.SimpleDateFormat;
35+
import java.util.Arrays;
3236
import java.util.Date;
37+
import java.util.HashSet;
38+
import org.junit.Assert;
3339
import org.junit.Test;
3440
import org.springframework.beans.factory.annotation.Autowired;
41+
import org.springframework.boot.test.mock.mockito.MockBean;
3542
import org.springframework.data.domain.Page;
3643
import org.springframework.data.domain.PageRequest;
3744
import org.springframework.test.context.jdbc.Sql;
3845

3946
import java.util.List;
47+
import org.springframework.test.util.ReflectionTestUtils;
4048

4149
import static org.junit.Assert.assertEquals;
4250
import static org.junit.Assert.assertNotNull;
4351
import static org.junit.Assert.assertNull;
4452
import static org.junit.Assert.assertTrue;
53+
import static org.mockito.Mockito.when;
4554

4655
public class NamespaceServiceIntegrationTest extends AbstractIntegrationTest {
4756

@@ -62,6 +71,11 @@ public class NamespaceServiceIntegrationTest extends AbstractIntegrationTest {
6271
private ReleaseHistoryService releaseHistoryService;
6372
@Autowired
6473
private InstanceConfigRepository instanceConfigRepository;
74+
@Autowired
75+
private NamespaceRepository namespaceRepository;
76+
77+
@MockBean
78+
private BizConfig bizConfig;
6579

6680
private String testApp = "testApp";
6781
private String testCluster = "default";
@@ -134,4 +148,85 @@ public void testGetCommitsByModifiedTime() throws ParseException {
134148
}
135149

136150

151+
@Test
152+
@Sql(scripts = "/sql/namespace-test.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
153+
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
154+
public void testNamespaceNumLimit() {
155+
156+
when(bizConfig.isNamespaceNumLimitEnabled()).thenReturn(true);
157+
when(bizConfig.namespaceNumLimit()).thenReturn(2);
158+
159+
Namespace namespace = new Namespace();
160+
namespace.setAppId(testApp);
161+
namespace.setClusterName(testCluster);
162+
namespace.setNamespaceName("demo-namespace");
163+
namespaceService.save(namespace);
164+
165+
try {
166+
Namespace namespace2 = new Namespace();
167+
namespace2.setAppId(testApp);
168+
namespace2.setClusterName(testCluster);
169+
namespace2.setNamespaceName("demo-namespace2");
170+
namespaceService.save(namespace2);
171+
172+
Assert.fail();
173+
} catch (Exception e) {
174+
Assert.assertTrue(e instanceof ServiceException);
175+
}
176+
177+
int nowCount = namespaceRepository.countByAppIdAndClusterName(testApp, testCluster);
178+
Assert.assertEquals(2, nowCount);
179+
180+
}
181+
182+
@Test
183+
@Sql(scripts = "/sql/namespace-test.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
184+
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
185+
public void testNamespaceNumLimitFalse() {
186+
187+
when(bizConfig.namespaceNumLimit()).thenReturn(2);
188+
189+
Namespace namespace = new Namespace();
190+
namespace.setAppId(testApp);
191+
namespace.setClusterName(testCluster);
192+
namespace.setNamespaceName("demo-namespace");
193+
namespaceService.save(namespace);
194+
195+
Namespace namespace2 = new Namespace();
196+
namespace2.setAppId(testApp);
197+
namespace2.setClusterName(testCluster);
198+
namespace2.setNamespaceName("demo-namespace2");
199+
namespaceService.save(namespace2);
200+
201+
int nowCount = namespaceRepository.countByAppIdAndClusterName(testApp, testCluster);
202+
Assert.assertEquals(3, nowCount);
203+
204+
}
205+
206+
@Test
207+
@Sql(scripts = "/sql/namespace-test.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
208+
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
209+
public void testNamespaceNumLimitWhite() {
210+
211+
when(bizConfig.isNamespaceNumLimitEnabled()).thenReturn(true);
212+
when(bizConfig.namespaceNumLimit()).thenReturn(2);
213+
when(bizConfig.namespaceNumLimitWhite()).thenReturn(new HashSet<>(Arrays.asList(testApp)));
214+
215+
Namespace namespace = new Namespace();
216+
namespace.setAppId(testApp);
217+
namespace.setClusterName(testCluster);
218+
namespace.setNamespaceName("demo-namespace");
219+
namespaceService.save(namespace);
220+
221+
Namespace namespace2 = new Namespace();
222+
namespace2.setAppId(testApp);
223+
namespace2.setClusterName(testCluster);
224+
namespace2.setNamespaceName("demo-namespace2");
225+
namespaceService.save(namespace2);
226+
227+
int nowCount = namespaceRepository.countByAppIdAndClusterName(testApp, testCluster);
228+
Assert.assertEquals(3, nowCount);
229+
230+
}
231+
137232
}

0 commit comments

Comments
 (0)