diff --git a/CHANGELOG.md b/CHANGELOG.md index 381a4b79a..b4e28a13d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,3 +5,4 @@ - [feat: Update config server IP to 127.0.0.1 and adjust tests.](https://github.com/Tencent/spring-cloud-tencent/pull/1796) - [feat: Support Polaris config env value and add related tests](https://github.com/Tencent/spring-cloud-tencent/pull/1797) - [refactor: modify the initialization of ApplicationContextAwareUtils.](https://github.com/Tencent/spring-cloud-tencent/pull/1778) +- [feat: support enable/disable cloud location provider via configuration.](https://github.com/Tencent/spring-cloud-tencent/pull/1799) diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/OrderConstant.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/OrderConstant.java index 341340246..b94d141b8 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/OrderConstant.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/OrderConstant.java @@ -229,5 +229,10 @@ public class OrderConstant { * Order of fault injection modifier. */ public static Integer FAULT_INJECTION_ORDER = 2; + + /** + * Order of location configuration modifier. + */ + public static Integer LOCATION_ORDER = 2; } } diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/PostInitPolarisSDKContext.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/PostInitPolarisSDKContext.java index 115bce3d3..42b232153 100644 --- a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/PostInitPolarisSDKContext.java +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/PostInitPolarisSDKContext.java @@ -36,15 +36,17 @@ public class PostInitPolarisSDKContext { String zone = staticMetadataManager.getZone(); String campus = staticMetadataManager.getCampus(); - ValueContext valueContext = sdkContext.getValueContext(); - if (StringUtils.isNotBlank(region)) { - valueContext.setValue(RoutingProto.NearbyRoutingConfig.LocationLevel.REGION.name(), region); - } - if (zone != null) { - valueContext.setValue(RoutingProto.NearbyRoutingConfig.LocationLevel.ZONE.name(), zone); - } - if (StringUtils.isNotBlank(campus)) { - valueContext.setValue(RoutingProto.NearbyRoutingConfig.LocationLevel.CAMPUS.name(), campus); + if (!StringUtils.isAllEmpty(region, zone, campus)) { + ValueContext valueContext = sdkContext.getValueContext(); + if (StringUtils.isNotBlank(region)) { + valueContext.setValue(RoutingProto.NearbyRoutingConfig.LocationLevel.REGION.name(), region); + } + if (zone != null) { + valueContext.setValue(RoutingProto.NearbyRoutingConfig.LocationLevel.ZONE.name(), zone); + } + if (StringUtils.isNotBlank(campus)) { + valueContext.setValue(RoutingProto.NearbyRoutingConfig.LocationLevel.CAMPUS.name(), campus); + } } } } diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/PolarisContextAutoConfiguration.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/PolarisContextAutoConfiguration.java index 2ca05693c..580ec5558 100644 --- a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/PolarisContextAutoConfiguration.java +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/PolarisContextAutoConfiguration.java @@ -29,6 +29,8 @@ import com.tencent.cloud.polaris.context.admin.PolarisAdminProperties; import com.tencent.cloud.polaris.context.config.extend.consul.ConsulProperties; import com.tencent.cloud.polaris.context.config.extend.tsf.TsfCoreProperties; import com.tencent.cloud.polaris.context.config.extend.tsf.TsfInstanceMetadataProvider; +import com.tencent.cloud.polaris.context.config.location.PolarisLocationConfigModifier; +import com.tencent.cloud.polaris.context.config.location.PolarisLocationProperties; import com.tencent.cloud.polaris.context.event.PushGatewayEventReporterConfigModifier; import com.tencent.cloud.polaris.context.event.PushGatewayEventReporterProperties; import com.tencent.cloud.polaris.context.listener.PolarisContextApplicationEventListener; @@ -114,4 +116,16 @@ public class PolarisContextAutoConfiguration { public TsfInstanceMetadataProvider tsfInstanceMetadataProvider(TsfCoreProperties tsfCoreProperties) { return new TsfInstanceMetadataProvider(tsfCoreProperties); } + + @Bean + @ConditionalOnMissingBean + public PolarisLocationProperties polarisLocationProperties() { + return new PolarisLocationProperties(); + } + + @Bean + @ConditionalOnMissingBean + public PolarisLocationConfigModifier polarisLocationConfigModifier(PolarisLocationProperties polarisLocationProperties) { + return new PolarisLocationConfigModifier(polarisLocationProperties); + } } diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/extend/tsf/TsfCoreEnvironmentPostProcessor.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/extend/tsf/TsfCoreEnvironmentPostProcessor.java index 0edc5e807..07c841dc7 100644 --- a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/extend/tsf/TsfCoreEnvironmentPostProcessor.java +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/extend/tsf/TsfCoreEnvironmentPostProcessor.java @@ -159,6 +159,9 @@ public final class TsfCoreEnvironmentPostProcessor implements EnvironmentPostPro if (TsfContextUtils.isOnlyTsfConsulEnabled(environment)) { // context + defaultProperties.put("spring.cloud.polaris.location.cloud.enabled", environment.getProperty("spring.cloud.polaris.location.cloud.enabled", "false")); + + // discovery defaultProperties.put("spring.cloud.polaris.discovery.enabled", "false"); defaultProperties.put("spring.cloud.polaris.discovery.register", "false"); diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/location/PolarisLocationConfigModifier.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/location/PolarisLocationConfigModifier.java new file mode 100644 index 000000000..d6e777a50 --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/location/PolarisLocationConfigModifier.java @@ -0,0 +1,72 @@ +/* + * Tencent is pleased to support the open source community by making spring-cloud-tencent available. + * + * Copyright (C) 2021 Tencent. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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 com.tencent.cloud.polaris.context.config.location; + +import java.util.List; + +import com.tencent.cloud.common.constant.OrderConstant; +import com.tencent.cloud.polaris.context.PolarisConfigModifier; +import com.tencent.polaris.api.plugin.location.LocationProvider; +import com.tencent.polaris.factory.config.ConfigurationImpl; +import com.tencent.polaris.factory.config.global.LocationConfigImpl; +import com.tencent.polaris.factory.config.global.LocationProviderConfigImpl; + +/** + * Config modifier for Polaris location providers. + * + * @author Haotian Zhang + */ +public class PolarisLocationConfigModifier implements PolarisConfigModifier { + + private final PolarisLocationProperties properties; + + public PolarisLocationConfigModifier(PolarisLocationProperties properties) { + this.properties = properties; + } + + @Override + public void modify(ConfigurationImpl configuration) { + LocationConfigImpl locationConfig = (LocationConfigImpl) configuration.getGlobal().getLocation(); + List providers = locationConfig.getProviders(); + if (providers == null) { + return; + } + + String cloudTypeName = LocationProvider.ProviderType.CLOUD.getName(); + + if (!properties.getCloud().isEnabled()) { + // Remove cloud provider if disabled + providers.removeIf(p -> cloudTypeName.equals(p.getType())); + return; + } + + // Only add the cloud provider entry if it does not already exist + boolean cloudProviderExists = providers.stream() + .anyMatch(p -> cloudTypeName.equals(p.getType())); + if (!cloudProviderExists) { + LocationProviderConfigImpl cloudProviderConfig = new LocationProviderConfigImpl(); + cloudProviderConfig.setType(cloudTypeName); + providers.add(cloudProviderConfig); + } + } + + @Override + public int getOrder() { + return OrderConstant.Modifier.LOCATION_ORDER; + } +} diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/location/PolarisLocationProperties.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/location/PolarisLocationProperties.java new file mode 100644 index 000000000..2f722b0c5 --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/location/PolarisLocationProperties.java @@ -0,0 +1,75 @@ +/* + * Tencent is pleased to support the open source community by making spring-cloud-tencent available. + * + * Copyright (C) 2021 Tencent. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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 com.tencent.cloud.polaris.context.config.location; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Properties for Polaris location provider configuration. + * + * @author Haotian Zhang + */ +@ConfigurationProperties(prefix = "spring.cloud.polaris.location") +public class PolarisLocationProperties { + + /** + * Cloud location provider configuration. + */ + private Cloud cloud = new Cloud(); + + public Cloud getCloud() { + return cloud; + } + + public void setCloud(Cloud cloud) { + this.cloud = cloud; + } + + @Override + public String toString() { + return "PolarisLocationProperties{" + + "cloud=" + cloud + + '}'; + } + + /** + * Properties for cloud location provider. + */ + public static class Cloud { + + /** + * Whether to enable the cloud location provider. Default is true. + */ + private boolean enabled = true; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + @Override + public String toString() { + return "Cloud{" + + "enabled=" + enabled + + '}'; + } + } +} diff --git a/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/config/location/PolarisLocationConfigModifierTest.java b/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/config/location/PolarisLocationConfigModifierTest.java new file mode 100644 index 000000000..080a9b380 --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/config/location/PolarisLocationConfigModifierTest.java @@ -0,0 +1,133 @@ +/* + * Tencent is pleased to support the open source community by making spring-cloud-tencent available. + * + * Copyright (C) 2021 Tencent. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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 com.tencent.cloud.polaris.context.config.location; + +import com.tencent.cloud.common.constant.OrderConstant; +import com.tencent.cloud.polaris.context.PolarisSDKContextManager; +import com.tencent.polaris.api.plugin.location.LocationProvider; +import com.tencent.polaris.factory.config.global.LocationConfigImpl; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link PolarisLocationConfigModifier}. + * + * @author Haotian Zhang + */ +@DisplayName("PolarisLocationConfigModifier") +class PolarisLocationConfigModifierTest { + + private final ApplicationContextRunner enabledContextRunner = new ApplicationContextRunner() + .withUserConfiguration(TestApplication.class) + .withPropertyValues("spring.cloud.polaris.enabled=true") + .withPropertyValues("spring.cloud.polaris.location.cloud.enabled=true") + .withPropertyValues("spring.application.name=test") + .withPropertyValues("spring.cloud.gateway.enabled=false"); + + private final ApplicationContextRunner disabledContextRunner = new ApplicationContextRunner() + .withUserConfiguration(TestApplication.class) + .withPropertyValues("spring.cloud.polaris.enabled=true") + .withPropertyValues("spring.cloud.polaris.location.cloud.enabled=false") + .withPropertyValues("spring.application.name=test") + .withPropertyValues("spring.cloud.gateway.enabled=false"); + + @BeforeEach + void setUp() { + PolarisSDKContextManager.innerDestroy(); + } + + /** + * Test purpose: verify that cloud location provider is added by default. + * Test scenario: no explicit location config, cloud.enabled defaults to true. + * Verification: providers list contains one entry with type "cloud". + */ + @DisplayName("cloud provider is added when enabled by default") + @Test + void testModify_cloudEnabledByDefault() { + enabledContextRunner.run(context -> { + PolarisSDKContextManager manager = context.getBean(PolarisSDKContextManager.class); + LocationConfigImpl locationConfig = (LocationConfigImpl) manager.getSDKContext() + .getConfig().getGlobal().getLocation(); + boolean hasCloudProvider = locationConfig.getProviders().stream() + .anyMatch(p -> LocationProvider.ProviderType.CLOUD.getName().equals(p.getType())); + assertThat(hasCloudProvider).isTrue(); + }); + } + + /** + * Test purpose: verify that cloud location provider is removed when disabled. + * Test scenario: spring.cloud.polaris.location.cloud.enabled=false. + * Verification: providers list contains no entry with type "cloud". + */ + @DisplayName("cloud provider is removed when disabled") + @Test + void testModify_cloudDisabled() { + disabledContextRunner.run(context -> { + PolarisSDKContextManager manager = context.getBean(PolarisSDKContextManager.class); + LocationConfigImpl locationConfig = (LocationConfigImpl) manager.getSDKContext() + .getConfig().getGlobal().getLocation(); + boolean hasCloudProvider = locationConfig.getProviders().stream() + .anyMatch(p -> LocationProvider.ProviderType.CLOUD.getName().equals(p.getType())); + assertThat(hasCloudProvider).isFalse(); + }); + } + + /** + * Test purpose: verify that no duplicate cloud provider entry is added. + * Test scenario: cloud is enabled (default), Spring context is loaded once. + * Verification: providers list contains exactly one entry with type "cloud". + */ + @DisplayName("no duplicate cloud provider entry is added") + @Test + void testModify_noDuplicateCloudProvider() { + enabledContextRunner.run(context -> { + PolarisSDKContextManager manager = context.getBean(PolarisSDKContextManager.class); + LocationConfigImpl locationConfig = (LocationConfigImpl) manager.getSDKContext() + .getConfig().getGlobal().getLocation(); + long cloudProviderCount = locationConfig.getProviders().stream() + .filter(p -> LocationProvider.ProviderType.CLOUD.getName().equals(p.getType())) + .count(); + assertThat(cloudProviderCount).isEqualTo(1); + }); + } + + /** + * Test purpose: verify getOrder returns the location order constant. + * Test scenario: obtain modifier bean from context and call getOrder. + * Verification: order equals OrderConstant.Modifier.LOCATION_ORDER. + */ + @DisplayName("getOrder returns LOCATION_ORDER") + @Test + void testGetOrder() { + enabledContextRunner.run(context -> { + PolarisLocationConfigModifier modifier = context.getBean(PolarisLocationConfigModifier.class); + assertThat(modifier.getOrder()).isEqualTo(OrderConstant.Modifier.LOCATION_ORDER); + }); + } + + @SpringBootApplication + protected static class TestApplication { + + } +} diff --git a/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/config/location/PolarisLocationPropertiesTest.java b/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/config/location/PolarisLocationPropertiesTest.java new file mode 100644 index 000000000..c1f206653 --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/config/location/PolarisLocationPropertiesTest.java @@ -0,0 +1,104 @@ +/* + * Tencent is pleased to support the open source community by making spring-cloud-tencent available. + * + * Copyright (C) 2021 Tencent. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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 com.tencent.cloud.polaris.context.config.location; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link PolarisLocationProperties}. + * + * @author Haotian Zhang + */ +@DisplayName("PolarisLocationProperties") +class PolarisLocationPropertiesTest { + + /** + * Test purpose: verify PolarisLocationProperties default values. + * Test scenario: no configuration provided, use default constructor. + * Verification: cloud.enabled is true by default. + */ + @DisplayName("cloud.enabled defaults to true") + @Test + void testDefaultCloudEnabled() { + // Arrange + PolarisLocationProperties properties = new PolarisLocationProperties(); + + // Act & Assert + assertThat(properties.getCloud().isEnabled()).isTrue(); + } + + /** + * Test purpose: verify PolarisLocationProperties.Cloud setter and getter. + * Test scenario: set cloud.enabled to false via setter. + * Verification: getter returns false. + */ + @DisplayName("Cloud enabled can be set to false") + @Test + void testSetCloudEnabled() { + // Arrange + PolarisLocationProperties properties = new PolarisLocationProperties(); + PolarisLocationProperties.Cloud cloud = new PolarisLocationProperties.Cloud(); + cloud.setEnabled(false); + + // Act + properties.setCloud(cloud); + + // Assert + assertThat(properties.getCloud().isEnabled()).isFalse(); + } + + /** + * Test purpose: verify PolarisLocationProperties toString output. + * Test scenario: default properties instance. + * Verification: toString contains cloud and enabled info. + */ + @DisplayName("toString contains cloud info") + @Test + void testToString() { + // Arrange + PolarisLocationProperties properties = new PolarisLocationProperties(); + + // Act + String result = properties.toString(); + + // Assert + assertThat(result).contains("cloud="); + assertThat(result).contains("enabled=true"); + } + + /** + * Test purpose: verify PolarisLocationProperties.Cloud toString output. + * Test scenario: default Cloud instance. + * Verification: toString contains enabled=true. + */ + @DisplayName("Cloud toString contains enabled info") + @Test + void testCloudToString() { + // Arrange + PolarisLocationProperties.Cloud cloud = new PolarisLocationProperties.Cloud(); + + // Act + String result = cloud.toString(); + + // Assert + assertThat(result).contains("enabled=true"); + } +}