diff --git a/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/grayReleaseRule/GrayReleaseRulesHolder.java b/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/grayReleaseRule/GrayReleaseRulesHolder.java index e7f3b904301..6b478ce8949 100644 --- a/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/grayReleaseRule/GrayReleaseRulesHolder.java +++ b/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/grayReleaseRule/GrayReleaseRulesHolder.java @@ -68,6 +68,8 @@ public class GrayReleaseRulesHolder implements ReleaseMessageListener, Initializ private Multimap grayReleaseRuleCache; //store clientAppId+clientNamespace+ip -> ruleId map private Multimap reversedGrayReleaseRuleCache; + //store clientAppId+clientNamespace+label -> ruleId map + private Multimap reversedGrayReleaseRuleLabelCache; //an auto increment version to indicate the age of rules private AtomicLong loadVersion; @@ -80,6 +82,8 @@ public GrayReleaseRulesHolder(final GrayReleaseRuleRepository grayReleaseRuleRep TreeMultimap.create(String.CASE_INSENSITIVE_ORDER, Ordering.natural())); reversedGrayReleaseRuleCache = Multimaps.synchronizedSetMultimap( TreeMultimap.create(String.CASE_INSENSITIVE_ORDER, Ordering.natural())); + reversedGrayReleaseRuleLabelCache = Multimaps.synchronizedSetMultimap( + TreeMultimap.create(String.CASE_INSENSITIVE_ORDER, Ordering.natural())); executorService = Executors.newScheduledThreadPool(1, ApolloThreadFactory .create("GrayReleaseRulesHolder", true)); } @@ -152,15 +156,29 @@ public Long findReleaseIdFromGrayReleaseRule(String clientAppId, String clientIp } /** - * Check whether there are gray release rules for the clientAppId, clientIp, namespace - * combination. Please note that even there are gray release rules, it doesn't mean it will always - * load gray releases. Because gray release rules actually apply to one more dimension - cluster. + * Check whether there are gray release rules for the clientAppId, clientIp, clientLabel, namespace combination. + * Please note that even there are gray release rules, it doesn't mean it will always load gray + * releases. Because gray release rules actually apply to one more dimension - cluster. */ - public boolean hasGrayReleaseRule(String clientAppId, String clientIp, String namespaceName) { - return reversedGrayReleaseRuleCache.containsKey(assembleReversedGrayReleaseRuleKey(clientAppId, + public boolean hasGrayReleaseRule(String clientAppId, String clientIp, String clientLabel, + String namespaceName) { + // check ip gray rule + if (reversedGrayReleaseRuleCache.containsKey(assembleReversedGrayReleaseRuleKey(clientAppId, namespaceName, clientIp)) || reversedGrayReleaseRuleCache.containsKey (assembleReversedGrayReleaseRuleKey(clientAppId, namespaceName, GrayReleaseRuleItemDTO - .ALL_IP)); + .ALL_IP))) { + return true; + } + // check label gray rule + if (!Strings.isNullOrEmpty(clientLabel) && + (reversedGrayReleaseRuleLabelCache.containsKey( + assembleReversedGrayReleaseRuleKey(clientAppId, namespaceName, clientLabel)) || + reversedGrayReleaseRuleLabelCache.containsKey( + assembleReversedGrayReleaseRuleKey(clientAppId, namespaceName, + GrayReleaseRuleItemDTO.ALL_Label)))) { + return true; + } + return false; } private void scanGrayReleaseRules() { @@ -232,6 +250,10 @@ private void addCache(String key, GrayReleaseRuleCache ruleCache) { reversedGrayReleaseRuleCache.put(assembleReversedGrayReleaseRuleKey(ruleItemDTO .getClientAppId(), ruleCache.getNamespaceName(), clientIp), ruleCache.getRuleId()); } + for (String label : ruleItemDTO.getClientLabelList()) { + reversedGrayReleaseRuleLabelCache.put(assembleReversedGrayReleaseRuleKey(ruleItemDTO + .getClientAppId(), ruleCache.getNamespaceName(), label), ruleCache.getRuleId()); + } } } grayReleaseRuleCache.put(key, ruleCache); @@ -244,6 +266,10 @@ private void removeCache(String key, GrayReleaseRuleCache ruleCache) { reversedGrayReleaseRuleCache.remove(assembleReversedGrayReleaseRuleKey(ruleItemDTO .getClientAppId(), ruleCache.getNamespaceName(), clientIp), ruleCache.getRuleId()); } + for (String label : ruleItemDTO.getClientLabelList()) { + reversedGrayReleaseRuleLabelCache.remove(assembleReversedGrayReleaseRuleKey(ruleItemDTO + .getClientAppId(), ruleCache.getNamespaceName(), label), ruleCache.getRuleId()); + } } } @@ -282,8 +308,8 @@ private String assembleGrayReleaseRuleKey(String configAppId, String configClust } private String assembleReversedGrayReleaseRuleKey(String clientAppId, String - clientNamespaceName, String clientIp) { - return STRING_JOINER.join(clientAppId, clientNamespaceName, clientIp); + clientNamespaceName, String clientIpOrLabel) { + return STRING_JOINER.join(clientAppId, clientNamespaceName, clientIpOrLabel); } } diff --git a/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/registry/configuration/support/ApolloServiceRegistryProperties.java b/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/registry/configuration/support/ApolloServiceRegistryProperties.java index 9238492f1c6..9d851eff4c0 100644 --- a/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/registry/configuration/support/ApolloServiceRegistryProperties.java +++ b/apollo-biz/src/main/java/com/ctrip/framework/apollo/biz/registry/configuration/support/ApolloServiceRegistryProperties.java @@ -17,10 +17,12 @@ package com.ctrip.framework.apollo.biz.registry.configuration.support; import com.ctrip.framework.apollo.biz.registry.ServiceInstance; +import com.google.common.base.Strings; import java.net.URI; import java.util.HashMap; import java.util.Map; import javax.annotation.PostConstruct; +import javax.servlet.ServletContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cloud.commons.util.InetUtils; @@ -72,6 +74,9 @@ public class ApolloServiceRegistryProperties implements ServiceInstance { @Autowired private InetUtils inetUtils; + @Autowired + private ServletContext servletContext; + /** * if user doesn't config, then resolve them on the runtime. */ @@ -84,7 +89,9 @@ public void postConstruct() { if (this.uri == null) { String host = this.inetUtils.findFirstNonLoopbackHostInfo().getIpAddress(); Integer port = propertyResolver.getRequiredProperty("server.port", Integer.class); - String uriString = "http://" + host + ":" + port + "/"; + String contextPath = Strings.isNullOrEmpty(this.servletContext.getContextPath()) ? "/" + : this.servletContext.getContextPath(); + String uriString = "http://" + host + ":" + port + contextPath; this.uri = URI.create(uriString); } } diff --git a/apollo-biz/src/test/java/com/ctrip/framework/apollo/biz/grayReleaseRule/GrayReleaseRulesHolderTest.java b/apollo-biz/src/test/java/com/ctrip/framework/apollo/biz/grayReleaseRule/GrayReleaseRulesHolderTest.java index 503f36ae3d6..6143599d554 100644 --- a/apollo-biz/src/test/java/com/ctrip/framework/apollo/biz/grayReleaseRule/GrayReleaseRulesHolderTest.java +++ b/apollo-biz/src/test/java/com/ctrip/framework/apollo/biz/grayReleaseRule/GrayReleaseRulesHolderTest.java @@ -109,19 +109,29 @@ public void testScanGrayReleaseRules() throws Exception { assertNull(grayReleaseRulesHolder.findReleaseIdFromGrayReleaseRule(anotherClientAppId, anotherClientIp, anotherClientLabel, someAppId, someClusterName, someNamespaceName)); - assertTrue(grayReleaseRulesHolder.hasGrayReleaseRule(someClientAppId, someClientIp, - someNamespaceName)); - assertTrue(grayReleaseRulesHolder.hasGrayReleaseRule(someClientAppId.toUpperCase(), someClientIp, - someNamespaceName.toUpperCase())); - assertFalse(grayReleaseRulesHolder.hasGrayReleaseRule(someClientAppId, anotherClientIp, - someNamespaceName)); - assertFalse(grayReleaseRulesHolder.hasGrayReleaseRule(someClientAppId, someClientIp, - anotherNamespaceName)); + assertTrue( + grayReleaseRulesHolder.hasGrayReleaseRule(someClientAppId, someClientIp, someClientLabel, + someNamespaceName)); + assertTrue( + grayReleaseRulesHolder.hasGrayReleaseRule(someClientAppId.toUpperCase(), someClientIp, + someClientLabel, someNamespaceName.toUpperCase())); + assertTrue( + grayReleaseRulesHolder.hasGrayReleaseRule(someClientAppId, anotherClientIp, someClientLabel, + someNamespaceName)); + assertTrue( + grayReleaseRulesHolder.hasGrayReleaseRule(someClientAppId.toUpperCase(), anotherClientIp, + someClientLabel, someNamespaceName.toUpperCase())); + assertFalse( + grayReleaseRulesHolder.hasGrayReleaseRule(someClientAppId, anotherClientIp, + anotherClientLabel, someNamespaceName)); + assertFalse( + grayReleaseRulesHolder.hasGrayReleaseRule(someClientAppId, someClientIp, someClientLabel, + anotherNamespaceName)); assertFalse(grayReleaseRulesHolder.hasGrayReleaseRule(anotherClientAppId, anotherClientIp, - someNamespaceName)); + anotherClientLabel, someNamespaceName)); assertFalse(grayReleaseRulesHolder.hasGrayReleaseRule(anotherClientAppId, anotherClientIp, - anotherNamespaceName)); + anotherClientLabel, anotherNamespaceName)); GrayReleaseRule anotherRule = assembleGrayReleaseRule(someAppId, someClusterName, someNamespaceName, Lists.newArrayList(assembleRuleItem(anotherClientAppId, Sets.newHashSet @@ -143,18 +153,22 @@ public void testScanGrayReleaseRules() throws Exception { assertEquals(someReleaseId, grayReleaseRulesHolder.findReleaseIdFromGrayReleaseRule (anotherClientAppId, anotherClientIp, anotherClientLabel, someAppId, someClusterName, someNamespaceName)); + assertFalse( + grayReleaseRulesHolder.hasGrayReleaseRule(someClientAppId, someClientIp, someClientLabel, someNamespaceName)); + assertFalse( + grayReleaseRulesHolder.hasGrayReleaseRule(someClientAppId, someClientIp, someClientLabel, anotherNamespaceName)); - assertFalse(grayReleaseRulesHolder.hasGrayReleaseRule(someClientAppId, someClientIp, - someNamespaceName)); - assertFalse(grayReleaseRulesHolder.hasGrayReleaseRule(someClientAppId, someClientIp, - anotherNamespaceName)); - assertTrue(grayReleaseRulesHolder.hasGrayReleaseRule(anotherClientAppId, anotherClientIp, - someNamespaceName)); - assertFalse(grayReleaseRulesHolder.hasGrayReleaseRule(anotherClientAppId, someClientIp, - someNamespaceName)); + anotherClientLabel, someNamespaceName)); + assertTrue(grayReleaseRulesHolder.hasGrayReleaseRule(anotherClientAppId, anotherClientIp, + someClientLabel, someNamespaceName)); + assertTrue(grayReleaseRulesHolder.hasGrayReleaseRule(anotherClientAppId, someClientIp, + anotherClientLabel, someNamespaceName)); + assertFalse( + grayReleaseRulesHolder.hasGrayReleaseRule(anotherClientAppId, someClientIp, someClientLabel, + someNamespaceName)); assertFalse(grayReleaseRulesHolder.hasGrayReleaseRule(anotherClientAppId, anotherClientIp, - anotherNamespaceName)); + anotherClientLabel, anotherNamespaceName)); } private GrayReleaseRule assembleGrayReleaseRule(String appId, String clusterName, String diff --git a/apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/controller/ConfigFileController.java b/apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/controller/ConfigFileController.java index 297de2ee0aa..180e73442bf 100644 --- a/apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/controller/ConfigFileController.java +++ b/apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/controller/ConfigFileController.java @@ -175,7 +175,7 @@ String queryConfig(ConfigFileOutputFormat outputFormat, String appId, String clu //1. check whether this client has gray release rules boolean hasGrayReleaseRule = grayReleaseRulesHolder.hasGrayReleaseRule(appId, clientIp, - namespace); + clientLabel, namespace); String cacheKey = assembleCacheKey(outputFormat, appId, clusterName, namespace, dataCenter); @@ -200,7 +200,7 @@ String queryConfig(ConfigFileOutputFormat outputFormat, String appId, String clu } //5. Double check if this client needs to load gray release, if yes, load from db again //This step is mainly to avoid cache pollution - if (grayReleaseRulesHolder.hasGrayReleaseRule(appId, clientIp, namespace)) { + if (grayReleaseRulesHolder.hasGrayReleaseRule(appId, clientIp, clientLabel, namespace)) { Tracer.logEvent("ConfigFile.Cache.GrayReleaseConflict", cacheKey); return loadConfig(outputFormat, appId, clusterName, namespace, dataCenter, clientIp, clientLabel, request, response); diff --git a/apollo-configservice/src/test/java/com/ctrip/framework/apollo/configservice/controller/ConfigFileControllerTest.java b/apollo-configservice/src/test/java/com/ctrip/framework/apollo/configservice/controller/ConfigFileControllerTest.java index 62e66b3933c..f5db7533d42 100644 --- a/apollo-configservice/src/test/java/com/ctrip/framework/apollo/configservice/controller/ConfigFileControllerTest.java +++ b/apollo-configservice/src/test/java/com/ctrip/framework/apollo/configservice/controller/ConfigFileControllerTest.java @@ -95,8 +95,8 @@ public void setUp() throws Exception { when(namespaceUtil.filterNamespaceName(someNamespace)).thenReturn(someNamespace); when(namespaceUtil.normalizeNamespace(someAppId, someNamespace)).thenReturn(someNamespace); - when(grayReleaseRulesHolder.hasGrayReleaseRule(anyString(), anyString(), anyString())) - .thenReturn(false); + when(grayReleaseRulesHolder.hasGrayReleaseRule(anyString(), anyString(), anyString(), + anyString())).thenReturn(false); watchedKeys2CacheKey = (Multimap) ReflectionTestUtils @@ -199,8 +199,8 @@ public void testQueryConfigWithGrayRelease() throws Exception { Map configurations = ImmutableMap.of(someKey, someValue); - when(grayReleaseRulesHolder.hasGrayReleaseRule(someAppId, someClientIp, someNamespace)) - .thenReturn(true); + when(grayReleaseRulesHolder.hasGrayReleaseRule(someAppId, someClientIp, someClientLabel, + someNamespace)).thenReturn(true); ApolloConfig someApolloConfig = mock(ApolloConfig.class); when(someApolloConfig.getConfigurations()).thenReturn(configurations); diff --git a/apollo-portal/src/main/resources/static/scripts/controller/ConfigExportController.js b/apollo-portal/src/main/resources/static/scripts/controller/ConfigExportController.js index 354d5ef319a..3f33b654cb0 100644 --- a/apollo-portal/src/main/resources/static/scripts/controller/ConfigExportController.js +++ b/apollo-portal/src/main/resources/static/scripts/controller/ConfigExportController.js @@ -63,7 +63,7 @@ config_export_module.controller('ConfigExportController', } var selectedEnvStr = selectedEnvs.join(","); - $window.location.href = '/configs/export?envs=' + selectedEnvStr; + $window.location.href = AppUtil.prefixPath() + '/configs/export?envs=' + selectedEnvStr; toastr.success($translate.instant('ConfigExport.ExportSuccess')); }; @@ -93,7 +93,7 @@ config_export_module.controller('ConfigExportController', form.append('file', file); $http({ method: 'POST', - url: '/configs/import?envs=' + selectedEnvStr + "&conflictAction=" + url: AppUtil.prefixPath() + '/configs/import?envs=' + selectedEnvStr + "&conflictAction=" + $scope.conflictAction, data: form, headers: {'Content-Type': undefined}, diff --git a/apollo-portal/src/main/resources/static/scripts/directive/import-namespace-modal-directive.js b/apollo-portal/src/main/resources/static/scripts/directive/import-namespace-modal-directive.js index 440730496fa..767b74c40f8 100644 --- a/apollo-portal/src/main/resources/static/scripts/directive/import-namespace-modal-directive.js +++ b/apollo-portal/src/main/resources/static/scripts/directive/import-namespace-modal-directive.js @@ -54,7 +54,7 @@ function importNamespaceModalDirective($window, $q, $translate, $http, toastr, A form.append('file', file); $http({ method: 'POST', - url: '/apps/' + toImportNamespace.baseInfo.appId + '/envs/' + scope.env + '/clusters/' + url: AppUtil.prefixPath() + '/apps/' + toImportNamespace.baseInfo.appId + '/envs/' + scope.env + '/clusters/' + toImportNamespace.baseInfo.clusterName + '/namespaces/' + toImportNamespace.baseInfo.namespaceName + "/items/import", data: form,