Feature: support spring cloud config data (#451)

* configdata

* configdata

* feature:support spring cloud configData

* feature:support spring cloud configData

* feature:support spring cloud configData

* feature:support spring cloud configData

* feature:support spring cloud configData

* feature:support spring cloud configData

* feature:support spring cloud configData

* feature:support spring cloud configData

* feature:support spring cloud configData

* feature:support spring cloud configData

* feature:support spring cloud configData

* feature:support spring cloud configData

* Update PolarisConfigFilePullerTest.java

Co-authored-by: wulingxiao <1251605638@qqcom>
Co-authored-by: Haotian Zhang <928016560@qq.com>
Co-authored-by: lepdou <lepdou@gmail.com>
pull/512/head
lingxiao,wu 2 years ago committed by GitHub
parent fe2da06f92
commit 91dd3d79ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -6,6 +6,7 @@
- [Feature: add rate limit filter debug log](https://github.com/Tencent/spring-cloud-tencent/pull/437)
- [Add configurable heartbeat interval support](https://github.com/Tencent/spring-cloud-tencent/pull/444)
- [feat:enhance Feign and RestTemplate and support Polaris monitor.](https://github.com/Tencent/spring-cloud-tencent/pull/447)
- [Feature: support spring cloud config data.](https://github.com/Tencent/spring-cloud-tencent/pull/451)
- [Optimize feign & rest-template circuit-breaker logic](https://github.com/Tencent/spring-cloud-tencent/pull/454)
- [Feature: Add disposable metadata transfer support](https://github.com/Tencent/spring-cloud-tencent/pull/459)
- [docs:update mvnw.](https://github.com/Tencent/spring-cloud-tencent/pull/476)

@ -27,6 +27,7 @@ import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.configuration.api.core.ConfigFileService;
import com.tencent.polaris.configuration.factory.ConfigFileServiceFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -50,6 +51,7 @@ public class PolarisConfigBootstrapAutoConfiguration {
}
@Bean
@ConditionalOnMissingBean
public PolarisPropertySourceManager polarisPropertySourceManager() {
return new PolarisPropertySourceManager();
}

@ -17,21 +17,12 @@
package com.tencent.cloud.polaris.config.adapter;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.tencent.cloud.polaris.config.config.ConfigFileGroup;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.config.enums.ConfigFileFormat;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.polaris.configuration.api.core.ConfigFileMetadata;
import com.tencent.polaris.configuration.api.core.ConfigFileService;
import com.tencent.polaris.configuration.api.core.ConfigKVFile;
import com.tencent.polaris.configuration.client.internal.DefaultConfigFileMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.core.annotation.Order;
@ -39,7 +30,6 @@ import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
* Spring cloud reserved core configuration loading SPI.
@ -51,28 +41,18 @@ import org.springframework.util.StringUtils;
@Order(0)
public class PolarisConfigFileLocator implements PropertySourceLocator {
private static final Logger LOGGER = LoggerFactory.getLogger(PolarisConfigFileLocator.class);
private static final String POLARIS_CONFIG_PROPERTY_SOURCE_NAME = "polaris-config";
private final PolarisConfigProperties polarisConfigProperties;
private final PolarisContextProperties polarisContextProperties;
private final ConfigFileService configFileService;
private final PolarisPropertySourceManager polarisPropertySourceManager;
private final Environment environment;
private final PolarisConfigFilePuller polarisConfigFilePuller;
public PolarisConfigFileLocator(PolarisConfigProperties polarisConfigProperties,
PolarisContextProperties polarisContextProperties, ConfigFileService configFileService,
PolarisPropertySourceManager polarisPropertySourceManager, Environment environment) {
this.polarisConfigProperties = polarisConfigProperties;
this.polarisContextProperties = polarisContextProperties;
this.configFileService = configFileService;
this.polarisPropertySourceManager = polarisPropertySourceManager;
this.environment = environment;
this.polarisConfigFilePuller = PolarisConfigFilePuller.get(polarisContextProperties, configFileService,
polarisPropertySourceManager);
}
@Override
@ -81,126 +61,16 @@ public class PolarisConfigFileLocator implements PropertySourceLocator {
POLARIS_CONFIG_PROPERTY_SOURCE_NAME);
// load spring boot default config files
initInternalConfigFiles(compositePropertySource);
String[] activeProfiles = environment.getActiveProfiles();
String serviceName = environment.getProperty("spring.application.name");
polarisConfigFilePuller.initInternalConfigFiles(compositePropertySource, activeProfiles, serviceName);
// load custom config files
List<ConfigFileGroup> configFileGroups = polarisConfigProperties.getGroups();
if (CollectionUtils.isEmpty(configFileGroups)) {
return compositePropertySource;
}
initCustomPolarisConfigFiles(compositePropertySource, configFileGroups);
polarisConfigFilePuller.initCustomPolarisConfigFiles(compositePropertySource, configFileGroups);
return compositePropertySource;
}
private void initInternalConfigFiles(CompositePropertySource compositePropertySource) {
List<ConfigFileMetadata> internalConfigFiles = getInternalConfigFiles();
for (ConfigFileMetadata configFile : internalConfigFiles) {
PolarisPropertySource polarisPropertySource = loadPolarisPropertySource(
configFile.getNamespace(), configFile.getFileGroup(), configFile.getFileName());
compositePropertySource.addPropertySource(polarisPropertySource);
polarisPropertySourceManager.addPropertySource(polarisPropertySource);
LOGGER.info("[SCT Config] Load and inject polaris config file. file = {}", configFile);
}
}
private List<ConfigFileMetadata> getInternalConfigFiles() {
String namespace = polarisContextProperties.getNamespace();
String serviceName = polarisContextProperties.getService();
if (!StringUtils.hasText(serviceName)) {
serviceName = environment.getProperty("spring.application.name");
}
List<ConfigFileMetadata> internalConfigFiles = new LinkedList<>();
// priority: application-${profile} > application > boostrap-${profile} > boostrap
String[] activeProfiles = environment.getActiveProfiles();
for (String activeProfile : activeProfiles) {
if (!StringUtils.hasText(activeProfile)) {
continue;
}
internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "application-" + activeProfile + ".properties"));
internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "application-" + activeProfile + ".yml"));
}
internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "application.properties"));
internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "application.yml"));
for (String activeProfile : activeProfiles) {
if (!StringUtils.hasText(activeProfile)) {
continue;
}
internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "bootstrap-" + activeProfile + ".properties"));
internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "bootstrap-" + activeProfile + ".yml"));
}
internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "bootstrap.properties"));
internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "bootstrap.yml"));
return internalConfigFiles;
}
private void initCustomPolarisConfigFiles(CompositePropertySource compositePropertySource,
List<ConfigFileGroup> configFileGroups) {
String namespace = polarisContextProperties.getNamespace();
for (ConfigFileGroup configFileGroup : configFileGroups) {
String group = configFileGroup.getName();
if (!StringUtils.hasText(group)) {
throw new IllegalArgumentException("polaris config group name cannot be empty.");
}
List<String> files = configFileGroup.getFiles();
if (CollectionUtils.isEmpty(files)) {
return;
}
for (String fileName : files) {
PolarisPropertySource polarisPropertySource = loadPolarisPropertySource(namespace, group, fileName);
compositePropertySource.addPropertySource(polarisPropertySource);
polarisPropertySourceManager.addPropertySource(polarisPropertySource);
LOGGER.info("[SCT Config] Load and inject polaris config file success."
+ " namespace = {}, group = {}, fileName = {}", namespace, group, fileName);
}
}
}
private PolarisPropertySource loadPolarisPropertySource(String namespace, String group, String fileName) {
ConfigKVFile configKVFile;
// unknown extension is resolved as properties file
if (ConfigFileFormat.isPropertyFile(fileName)
|| ConfigFileFormat.isUnknownFile(fileName)) {
configKVFile = configFileService.getConfigPropertiesFile(namespace, group, fileName);
}
else if (ConfigFileFormat.isYamlFile(fileName)) {
configKVFile = configFileService.getConfigYamlFile(namespace, group, fileName);
}
else {
LOGGER.warn("[SCT Config] Unsupported config file. namespace = {}, group = {}, fileName = {}", namespace,
group, fileName);
throw new IllegalStateException("Only configuration files in the format of properties / yaml / yaml"
+ " can be injected into the spring context");
}
Map<String, Object> map = new ConcurrentHashMap<>();
for (String key : configKVFile.getPropertyNames()) {
map.put(key, configKVFile.getProperty(key, null));
}
return new PolarisPropertySource(namespace, group, fileName, configKVFile, map);
}
}

@ -0,0 +1,195 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. 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.config.adapter;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.tencent.cloud.polaris.config.config.ConfigFileGroup;
import com.tencent.cloud.polaris.config.configdata.PolarisConfigDataLoader;
import com.tencent.cloud.polaris.config.enums.ConfigFileFormat;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.polaris.configuration.api.core.ConfigFileMetadata;
import com.tencent.polaris.configuration.api.core.ConfigFileService;
import com.tencent.polaris.configuration.api.core.ConfigKVFile;
import com.tencent.polaris.configuration.client.internal.DefaultConfigFileMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
* PolarisConfigFilePuller pull configFile from Polaris.
*
* @author wlx
*/
public final class PolarisConfigFilePuller {
private static final Logger LOGGER = LoggerFactory.getLogger(PolarisConfigFileLocator.class);
private PolarisContextProperties polarisContextProperties;
private ConfigFileService configFileService;
private PolarisPropertySourceManager polarisPropertySourceManager;
private PolarisConfigFilePuller() {
}
/**
* InitInternalConfigFiles for {@link PolarisConfigDataLoader}.
*
* @param compositePropertySource compositePropertySource
* @param activeProfiles activeProfiles
* @param serviceName serviceName
*/
public void initInternalConfigFiles(CompositePropertySource compositePropertySource, String[] activeProfiles, String serviceName) {
List<ConfigFileMetadata> internalConfigFiles = getInternalConfigFiles(activeProfiles, serviceName);
for (ConfigFileMetadata configFile : internalConfigFiles) {
PolarisPropertySource polarisPropertySource = loadPolarisPropertySource(
configFile.getNamespace(), configFile.getFileGroup(), configFile.getFileName());
compositePropertySource.addPropertySource(polarisPropertySource);
polarisPropertySourceManager.addPropertySource(polarisPropertySource);
LOGGER.info("[SCT Config] Load and inject polaris config file. file = {}", configFile);
}
}
/**
* Init multiple CustomPolarisConfigFile.
*
* @param compositePropertySource compositePropertySource
* @param configFileGroups configFileGroups
*/
public void initCustomPolarisConfigFiles(CompositePropertySource compositePropertySource,
List<ConfigFileGroup> configFileGroups) {
configFileGroups.forEach(
configFileGroup -> initCustomPolarisConfigFile(compositePropertySource, configFileGroup)
);
}
/**
* Init single CustomPolarisConfigFile.
*
* @param compositePropertySource compositePropertySource
* @param configFileGroup configFileGroup
*/
public void initCustomPolarisConfigFile(CompositePropertySource compositePropertySource,
ConfigFileGroup configFileGroup) {
String namespace = polarisContextProperties.getNamespace();
String group = configFileGroup.getName();
if (!StringUtils.hasText(group)) {
throw new IllegalArgumentException("polaris config group name cannot be empty.");
}
List<String> files = configFileGroup.getFiles();
if (CollectionUtils.isEmpty(files)) {
return;
}
for (String fileName : files) {
PolarisPropertySource polarisPropertySource = loadPolarisPropertySource(namespace, group, fileName);
compositePropertySource.addPropertySource(polarisPropertySource);
polarisPropertySourceManager.addPropertySource(polarisPropertySource);
LOGGER.info(
"[SCT Config] Load and inject polaris config file success. namespace = {}, group = {}, fileName = {}",
namespace, group, fileName);
}
}
private PolarisPropertySource loadPolarisPropertySource(String namespace, String group, String fileName) {
ConfigKVFile configKVFile;
// unknown extension is resolved as properties file
if (ConfigFileFormat.isPropertyFile(fileName)
|| ConfigFileFormat.isUnknownFile(fileName)) {
configKVFile = configFileService.getConfigPropertiesFile(namespace, group, fileName);
}
else if (ConfigFileFormat.isYamlFile(fileName)) {
configKVFile = configFileService.getConfigYamlFile(namespace, group, fileName);
}
else {
LOGGER.warn("[SCT Config] Unsupported config file. namespace = {}, group = {}, fileName = {}", namespace,
group, fileName);
throw new IllegalStateException("Only configuration files in the format of properties / yaml / yaml"
+ " can be injected into the spring context");
}
Map<String, Object> map = new ConcurrentHashMap<>();
for (String key : configKVFile.getPropertyNames()) {
map.put(key, configKVFile.getProperty(key, null));
}
return new PolarisPropertySource(namespace, group, fileName, configKVFile, map);
}
private List<ConfigFileMetadata> getInternalConfigFiles(String[] activeProfiles, String serviceName) {
String namespace = polarisContextProperties.getNamespace();
if (StringUtils.hasText(polarisContextProperties.getService())) {
serviceName = polarisContextProperties.getService();
}
// priority: application-${profile} > application > boostrap-${profile} > boostrap
return getInternalConfigFiles(activeProfiles, namespace, serviceName);
}
private List<ConfigFileMetadata> getInternalConfigFiles(String[] activeProfiles, String namespace, String serviceName) {
List<ConfigFileMetadata> internalConfigFiles = new LinkedList<>();
for (String activeProfile : activeProfiles) {
if (!StringUtils.hasText(activeProfile)) {
continue;
}
internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "application-" + activeProfile + ".properties"));
internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "application-" + activeProfile + ".yml"));
}
internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "application.properties"));
internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "application.yml"));
for (String activeProfile : activeProfiles) {
if (!StringUtils.hasText(activeProfile)) {
continue;
}
internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "bootstrap-" + activeProfile + ".properties"));
internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "bootstrap-" + activeProfile + ".yml"));
}
internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "bootstrap.properties"));
internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "bootstrap.yml"));
return internalConfigFiles;
}
/**
* Factory method to create PolarisConfigFilePuller for
* {@link PolarisConfigDataLoader},{@link PolarisConfigFileLocator}.
*
* @param polarisContextProperties polarisContextProperties
* @param configFileService configFileService
* @param polarisPropertySourceManager polarisPropertySourceManager
* @return PolarisConfigFilePuller instance
*/
public static PolarisConfigFilePuller get(PolarisContextProperties polarisContextProperties, ConfigFileService configFileService,
PolarisPropertySourceManager polarisPropertySourceManager) {
PolarisConfigFilePuller puller = new PolarisConfigFilePuller();
puller.polarisContextProperties = polarisContextProperties;
puller.configFileService = configFileService;
puller.polarisPropertySourceManager = polarisPropertySourceManager;
return puller;
}
}

@ -50,6 +50,12 @@ public class PolarisConfigProperties {
*/
private boolean autoRefresh = true;
/**
* When the local configuration is consistent with the remote configuration, whether to
* preferentially load the remote configuration.
*/
private boolean preference = true;
/**
* List of injected configuration files.
*/
@ -94,4 +100,12 @@ public class PolarisConfigProperties {
public void setGroups(List<ConfigFileGroup> groups) {
this.groups = groups;
}
public boolean isPreference() {
return preference;
}
public void setPreference(boolean preference) {
this.preference = preference;
}
}

@ -0,0 +1,152 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. 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.config.configdata;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import com.tencent.cloud.polaris.config.adapter.PolarisConfigFilePuller;
import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager;
import com.tencent.cloud.polaris.config.config.ConfigFileGroup;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.configuration.api.core.ConfigFileService;
import com.tencent.polaris.configuration.factory.ConfigFileServiceFactory;
import org.apache.commons.logging.Log;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.context.config.ConfigData;
import org.springframework.boot.context.config.ConfigDataLoader;
import org.springframework.boot.context.config.ConfigDataLoaderContext;
import org.springframework.boot.context.config.ConfigDataResourceNotFoundException;
import org.springframework.boot.context.config.Profiles;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import static org.springframework.boot.context.config.ConfigData.Option.IGNORE_IMPORTS;
import static org.springframework.boot.context.config.ConfigData.Option.IGNORE_PROFILES;
import static org.springframework.boot.context.config.ConfigData.Option.PROFILE_SPECIFIC;
/**
* Implementation of {@link ConfigDataLoader}.can be used to load {@link ConfigData} for a given
* {@link PolarisConfigDataResource} .
* <p>
* Load {@link ConfigData} via {@link PolarisConfigDataLoader}
*
* @author wlx
* @date 2022/7/5 11:14
*/
public class PolarisConfigDataLoader implements ConfigDataLoader<PolarisConfigDataResource> {
private static final String POLARIS_CONFIG_PROPERTY_SOURCE_NAME = "polaris-config";
private final Log log;
private ConfigFileService configFileService;
private PolarisConfigFilePuller puller;
static final AtomicBoolean INTERNAL_CONFIG_FILES_LOADED = new AtomicBoolean(false);
static final AtomicBoolean CUSTOM_POLARIS_CONFIG_FILE_LOADED = new AtomicBoolean(false);
public PolarisConfigDataLoader(DeferredLogFactory logFactory) {
this.log = logFactory.getLog(getClass());
}
@Override
public ConfigData load(ConfigDataLoaderContext context, PolarisConfigDataResource resource)
throws ConfigDataResourceNotFoundException {
try {
return load(context.getBootstrapContext(), resource);
}
catch (Exception e) {
log.warn("Error getting properties from polaris: " + resource, e);
if (!resource.isOptional()) {
throw new ConfigDataResourceNotFoundException(resource, e);
}
return null;
}
}
public ConfigData load(ConfigurableBootstrapContext bootstrapContext, PolarisConfigDataResource resource) {
CompositePropertySource compositePropertySource = locate(bootstrapContext, resource);
return new ConfigData(compositePropertySource.getPropertySources(), getOptions(resource));
}
private CompositePropertySource locate(ConfigurableBootstrapContext bootstrapContext,
PolarisConfigDataResource resource) {
CompositePropertySource compositePropertySource = new CompositePropertySource(
POLARIS_CONFIG_PROPERTY_SOURCE_NAME);
SDKContext sdkContext = bootstrapContext.get(SDKContext.class);
if (null == this.configFileService) {
this.configFileService = ConfigFileServiceFactory.createConfigFileService(sdkContext);
}
if (null == this.puller) {
this.puller = PolarisConfigFilePuller.get(resource.getPolarisContextProperties(),
configFileService, bootstrapContext.get(PolarisPropertySourceManager.class));
}
Profiles profiles = resource.getProfiles();
if (INTERNAL_CONFIG_FILES_LOADED.compareAndSet(false, true)) {
log.info("loading internal config files");
List<String> profilesActive = profiles.getActive();
String[] activeProfiles = profilesActive.toArray(new String[]{});
this.puller.initInternalConfigFiles(compositePropertySource, activeProfiles, resource.getServiceName());
}
PolarisConfigProperties polarisConfigProperties = resource.getPolarisConfigProperties();
if (!CollectionUtils.isEmpty(polarisConfigProperties.getGroups()) &&
CUSTOM_POLARIS_CONFIG_FILE_LOADED.compareAndSet(false, true)) {
log.info("loading custom config files");
this.puller.initCustomPolarisConfigFiles(compositePropertySource,
polarisConfigProperties.getGroups());
}
// load config data
if (StringUtils.hasText(resource.getFileName())) {
log.info("loading config data config file, group:" + resource.getGroupName() + " file: " + resource.getFileName());
this.puller.initCustomPolarisConfigFile(compositePropertySource, configFileGroup(resource));
}
return compositePropertySource;
}
private ConfigData.Option[] getOptions(PolarisConfigDataResource resource) {
List<ConfigData.Option> options = new ArrayList<>();
options.add(IGNORE_IMPORTS);
options.add(IGNORE_PROFILES);
PolarisConfigProperties polarisConfigProperties = resource.getPolarisConfigProperties();
if (polarisConfigProperties.isPreference()) {
// mark it as 'PROFILE_SPECIFIC' config, it has higher priority
options.add(PROFILE_SPECIFIC);
}
return options.toArray(new ConfigData.Option[]{});
}
private ConfigFileGroup configFileGroup(PolarisConfigDataResource polarisConfigDataResource) {
String fileName = polarisConfigDataResource.getFileName();
String groupName = polarisConfigDataResource.getGroupName();
ConfigFileGroup configFileGroup = new ConfigFileGroup();
configFileGroup.setName(groupName);
List<String> files = new ArrayList<>();
files.add(fileName);
configFileGroup.setFiles(files);
return configFileGroup;
}
}

@ -0,0 +1,291 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. 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.config.configdata;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import com.tencent.cloud.polaris.config.ConfigurationModifier;
import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.context.ModifyAddress;
import com.tencent.cloud.polaris.context.PolarisConfigModifier;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.polaris.api.utils.StringUtils;
import com.tencent.polaris.client.api.SDKContext;
import org.apache.commons.logging.Log;
import org.springframework.boot.BootstrapRegistry;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.context.config.ConfigDataLocation;
import org.springframework.boot.context.config.ConfigDataLocationNotFoundException;
import org.springframework.boot.context.config.ConfigDataLocationResolver;
import org.springframework.boot.context.config.ConfigDataLocationResolverContext;
import org.springframework.boot.context.config.ConfigDataResourceNotFoundException;
import org.springframework.boot.context.config.Profiles;
import org.springframework.boot.context.properties.bind.BindHandler;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.Ordered;
/**
* Implementation of {@link ConfigDataLocationResolver}, used to resolve {@link ConfigDataLocation locations}
* into one or more {@link PolarisConfigDataResource polarisConfigDataResource}.
*
* @author wlx
*/
public class PolarisConfigDataLocationResolver implements
ConfigDataLocationResolver<PolarisConfigDataResource>, Ordered {
/**
* Prefix for Config Server imports.
*/
public static final String PREFIX = "polaris";
/**
* Prefix for Polaris configurationProperties.
*/
public static final String POLARIS_PREFIX = "spring.cloud.polaris";
/**
* COLON.
*/
public static final String COLON = ":";
/**
* Empty String.
*/
public static final String EMPTY_STRING = "";
private final Log log;
public PolarisConfigDataLocationResolver(DeferredLogFactory logFactory) {
this.log = logFactory.getLog(getClass());
}
@Override
public boolean isResolvable(ConfigDataLocationResolverContext context, ConfigDataLocation location) {
if (!location.hasPrefix(PREFIX)) {
return false;
}
return context.getBinder()
.bind("spring.cloud.polaris.config.enabled", Boolean.class)
.orElse(true);
}
@Override
public List<PolarisConfigDataResource> resolve(
ConfigDataLocationResolverContext context, ConfigDataLocation location)
throws ConfigDataLocationNotFoundException,
ConfigDataResourceNotFoundException {
return Collections.emptyList();
}
@Override
public List<PolarisConfigDataResource> resolveProfileSpecific(
ConfigDataLocationResolverContext resolverContext,
ConfigDataLocation location, Profiles profiles)
throws ConfigDataLocationNotFoundException {
ConfigurableBootstrapContext bootstrapContext = resolverContext.getBootstrapContext();
PolarisConfigProperties polarisConfigProperties = loadPolarisConfigProperties(
resolverContext,
PolarisConfigProperties.class,
POLARIS_PREFIX + ".config"
);
if (Objects.isNull(polarisConfigProperties)) {
polarisConfigProperties = new PolarisConfigProperties();
}
PolarisContextProperties polarisContextProperties = loadPolarisConfigProperties(
resolverContext,
PolarisContextProperties.class,
POLARIS_PREFIX
);
if (Objects.isNull(polarisContextProperties)) {
polarisContextProperties = new PolarisContextProperties();
}
// prepare and init earlier Polaris SDKContext to pull config files from remote.
prepareAndInitEarlierPolarisSdkContext(resolverContext, polarisConfigProperties, polarisContextProperties);
bootstrapContext.registerIfAbsent(PolarisConfigProperties.class,
BootstrapRegistry.InstanceSupplier.of(polarisConfigProperties));
bootstrapContext.registerIfAbsent(PolarisContextProperties.class,
BootstrapRegistry.InstanceSupplier.of(polarisContextProperties));
bootstrapContext.registerIfAbsent(PolarisPropertySourceManager.class,
BootstrapRegistry.InstanceSupplier.of(new PolarisPropertySourceManager()));
bootstrapContext.addCloseListener(
event -> {
// destroy earlier Polaris sdkContext
event.getBootstrapContext().get(SDKContext.class).destroy();
// register PolarisPropertySourceManager to context
PolarisPropertySourceManager polarisPropertySourceManager = event.getBootstrapContext().get(PolarisPropertySourceManager.class);
event.getApplicationContext().getBeanFactory().registerSingleton(
"polarisPropertySourceManager", polarisPropertySourceManager);
}
);
return loadConfigDataResources(resolverContext,
location, profiles, polarisConfigProperties, polarisContextProperties);
}
@Override
public int getOrder() {
return -1;
}
protected <T> T loadPolarisConfigProperties(
ConfigDataLocationResolverContext context,
Class<T> typeClass,
String prefix) {
Binder binder = context.getBinder();
BindHandler bindHandler = getBindHandler(context);
T instance;
if (!registerNotNecessary(typeClass) && context.getBootstrapContext().isRegistered(typeClass)) {
instance = context.getBootstrapContext().get(typeClass);
}
else {
instance = binder.bind(prefix, Bindable.of(typeClass), bindHandler)
.map(properties -> binder.bind(prefix, Bindable.ofInstance(properties), bindHandler)
.orElse(properties))
.orElseGet(() -> binder.bind(prefix, Bindable.of(typeClass), bindHandler)
.orElseGet(() -> null));
}
return instance;
}
private BindHandler getBindHandler(ConfigDataLocationResolverContext context) {
return context.getBootstrapContext().getOrElse(BindHandler.class, null);
}
private List<PolarisConfigDataResource> loadConfigDataResources(ConfigDataLocationResolverContext resolverContext,
ConfigDataLocation location,
Profiles profiles,
PolarisConfigProperties polarisConfigProperties,
PolarisContextProperties polarisContextProperties) {
List<PolarisConfigDataResource> result = new ArrayList<>();
boolean optional = location.isOptional();
String groupFileName = getRealGroupFileName(location);
String serviceName = loadPolarisConfigProperties(resolverContext,
String.class, "spring.application.name");
if (StringUtils.isBlank(serviceName)) {
serviceName = "application";
log.warn("No spring.application.name found, defaulting to 'application'");
}
String groupName = StringUtils.isBlank(groupFileName) ? EMPTY_STRING : parseGroupName(groupFileName, serviceName);
if (StringUtils.isNotBlank(groupName)) {
log.info("group from configDataLocation is " + groupName);
}
String fileName = StringUtils.isBlank(groupFileName) ? EMPTY_STRING : parseFileName(groupFileName);
if (StringUtils.isNotBlank(fileName)) {
log.info("file from configDataLocation is " + fileName);
}
PolarisConfigDataResource polarisConfigDataResource = new PolarisConfigDataResource(
polarisConfigProperties,
polarisContextProperties,
profiles, optional,
fileName, groupName, serviceName
);
result.add(polarisConfigDataResource);
return result;
}
private String getRealGroupFileName(ConfigDataLocation location) {
String prefixedValue = location.getNonPrefixedValue(PREFIX);
if (StringUtils.isBlank(prefixedValue) || !prefixedValue.startsWith(COLON)) {
return prefixedValue;
}
return prefixedValue.substring(1);
}
private String parseFileName(String groupFileName) {
String[] split = groupFileName.split(COLON);
if (split.length > 1) {
return split[1];
}
else {
return split[0];
}
}
private String parseGroupName(String groupFileName, String serviceName) {
String[] split = groupFileName.split(COLON);
if (split.length > 1) {
return split[0];
}
else {
return serviceName;
}
}
private void prepareAndInitEarlierPolarisSdkContext(ConfigDataLocationResolverContext resolverContext,
PolarisConfigProperties polarisConfigProperties,
PolarisContextProperties polarisContextProperties) {
ConfigurableBootstrapContext bootstrapContext = resolverContext.getBootstrapContext();
if (!bootstrapContext.isRegistered(SDKContext.class)) {
SDKContext sdkContext = sdkContext(resolverContext,
polarisConfigProperties, polarisContextProperties);
sdkContext.init();
bootstrapContext.register(SDKContext.class, BootstrapRegistry.InstanceSupplier.of(sdkContext));
}
}
private SDKContext sdkContext(ConfigDataLocationResolverContext resolverContext,
PolarisConfigProperties polarisConfigProperties,
PolarisContextProperties polarisContextProperties) {
List<PolarisConfigModifier> modifierList = modifierList(polarisConfigProperties, polarisContextProperties);
return SDKContext.initContextByConfig(polarisContextProperties.configuration(modifierList, () -> {
return loadPolarisConfigProperties(resolverContext, String.class, "spring.cloud.client.ip-address");
}));
}
private List<PolarisConfigModifier> modifierList(PolarisConfigProperties polarisConfigProperties,
PolarisContextProperties polarisContextProperties) {
// add ModifyAddress and ConfigurationModifier to load SDKContext
List<PolarisConfigModifier> modifierList = new ArrayList<>();
ModifyAddress modifyAddress = new ModifyAddress();
modifyAddress.setProperties(polarisContextProperties);
ConfigurationModifier configurationModifier = new ConfigurationModifier(polarisConfigProperties,
polarisContextProperties);
modifierList.add(modifyAddress);
modifierList.add(configurationModifier);
return modifierList;
}
private boolean registerNotNecessary(Class<?> typeClass) {
return typeClass.isPrimitive() ||
Number.class.isAssignableFrom(typeClass) ||
String.class.isAssignableFrom(typeClass) ||
Character.class.isAssignableFrom(typeClass) ||
Boolean.class.isAssignableFrom(typeClass);
}
}

@ -0,0 +1,62 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. 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.config.configdata;
import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor;
import org.springframework.cloud.commons.ConfigDataMissingEnvironmentPostProcessor;
import org.springframework.cloud.util.PropertyUtils;
import org.springframework.core.env.Environment;
/**
* PolarisConfigDataMissingEnvironmentPostProcessor to check if miss PolarisConfigData config,if miss config
* will throw {@link ImportException}.
*
* @author wlx
* @see ConfigDataMissingEnvironmentPostProcessor
* @see ConfigDataMissingEnvironmentPostProcessor.ImportException
*/
public class PolarisConfigDataMissingEnvironmentPostProcessor extends ConfigDataMissingEnvironmentPostProcessor {
/**
* run after {@link ConfigDataEnvironmentPostProcessor}.
*/
public static final int ORDER = ConfigDataEnvironmentPostProcessor.ORDER + 1;
@Override
public int getOrder() {
return ORDER;
}
@Override
protected boolean shouldProcessEnvironment(Environment environment) {
// if using bootstrap or legacy processing don't run
if (!PropertyUtils.bootstrapEnabled(environment) && !PropertyUtils.useLegacyProcessing(environment)) {
boolean configEnabled = environment.getProperty("spring.cloud.polaris.config.enabled", Boolean.class, true);
boolean importCheckEnabled = environment.getProperty("spring.cloud.polaris.config.import-check.enabled", Boolean.class, true);
return configEnabled && importCheckEnabled;
}
else {
return false;
}
}
@Override
protected String getPrefix() {
return PolarisConfigDataLocationResolver.PREFIX;
}
}

@ -0,0 +1,114 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. 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.config.configdata;
import java.util.Objects;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import org.springframework.boot.context.config.ConfigData;
import org.springframework.boot.context.config.ConfigDataResource;
import org.springframework.boot.context.config.Profiles;
/**
* A polaris configData resource from which {@link ConfigData} can be loaded.
*
* @author wlx
* @date 2022/7/5 11:13
*/
public class PolarisConfigDataResource extends ConfigDataResource {
private final PolarisConfigProperties polarisConfigProperties;
private final PolarisContextProperties polarisContextProperties;
private final Profiles profiles;
private final boolean optional;
private final String fileName;
private final String groupName;
private final String serviceName;
public PolarisConfigDataResource(PolarisConfigProperties polarisConfigProperties,
PolarisContextProperties polarisContextProperties,
Profiles profiles, boolean optional,
String fileName, String groupName, String serviceName) {
this.polarisConfigProperties = polarisConfigProperties;
this.polarisContextProperties = polarisContextProperties;
this.profiles = profiles;
this.optional = optional;
this.fileName = fileName;
this.groupName = groupName;
this.serviceName = serviceName;
}
public PolarisConfigProperties getPolarisConfigProperties() {
return polarisConfigProperties;
}
public PolarisContextProperties getPolarisContextProperties() {
return polarisContextProperties;
}
public Profiles getProfiles() {
return profiles;
}
public boolean isOptional() {
return optional;
}
public String getFileName() {
return fileName;
}
public String getGroupName() {
return groupName;
}
public String getServiceName() {
return serviceName;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PolarisConfigDataResource that = (PolarisConfigDataResource) o;
return optional == that.optional &&
polarisConfigProperties.equals(that.polarisConfigProperties) &&
polarisContextProperties.equals(that.polarisContextProperties) &&
profiles.equals(that.profiles) &&
fileName.equals(that.fileName) &&
groupName.equals(that.groupName) &&
serviceName.equals(that.serviceName);
}
@Override
public int hashCode() {
return Objects.hash(polarisConfigProperties, polarisContextProperties, profiles, optional, fileName, groupName, serviceName);
}
}

@ -0,0 +1,35 @@
package com.tencent.cloud.polaris.config.configdata;
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.cloud.commons.ConfigDataMissingEnvironmentPostProcessor;
/**
* Class for most {@code FailureAnalyzer} implementations, to analyze ImportException when
* miss Polaris configData config.
* <p>Refer to the Nacos project implementation
* <code><a href=https://github.com/alibaba/spring-cloud-alibaba/blob/2021.x/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/configdata/NacosConfigDataMissingEnvironmentPostProcessor.java>
* ImportExceptionFailureAnalyzer</a></code>
*
* @author wlx
* @see AbstractFailureAnalyzer
*/
public class PolarisImportExceptionFailureAnalyzer extends
AbstractFailureAnalyzer<ConfigDataMissingEnvironmentPostProcessor.ImportException> {
@Override
protected FailureAnalysis analyze(Throwable rootFailure, ConfigDataMissingEnvironmentPostProcessor.ImportException cause) {
String description;
if (cause.missingPrefix) {
description = "The spring.config.import property is missing a " + PolarisConfigDataLocationResolver.PREFIX
+ " entry";
}
else {
description = "No spring.config.import property has been defined";
}
String action = "Add a spring.config.import=polaris property to your configuration.\n"
+ "\tIf configuration is not required add spring.config.import=optional:polaris instead.\n"
+ "\tTo disable this check, set spring.cloud.polaris.config.import-check.enabled=false.";
return new FailureAnalysis(description, action, cause);
}
}

@ -41,6 +41,18 @@
"defaultValue": "true",
"description": "Whether to connect to a remote server, suitable for local development mode.",
"sourceType": "com.tencent.cloud.polaris.config.config.PolarisConfigProperties"
},
{
"name": "spring.cloud.polaris.config.import-check.enabled",
"type": "java.lang.Boolean",
"defaultValue": true,
"description": "Whether to enable import-check."
},
{
"name": "spring.cloud.polaris.config.preference",
"type": "java.lang.Boolean",
"defaultValue": true,
"description": "Whether to preferentially load the remote configuration."
}
]
}

@ -2,4 +2,17 @@ org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.tencent.cloud.polaris.config.PolarisConfigBootstrapAutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tencent.cloud.polaris.config.PolarisConfigAutoConfiguration,\
com.tencent.cloud.polaris.config.endpoint.PolarisConfigEndpointAutoConfiguration
com.tencent.cloud.polaris.config.endpoint.PolarisConfigEndpointAutoConfiguration,\
com.tencent.cloud.polaris.config.PolarisConfigBootstrapAutoConfiguration
# ConfigData Location Resolvers
org.springframework.boot.context.config.ConfigDataLocationResolver=\
com.tencent.cloud.polaris.config.configdata.PolarisConfigDataLocationResolver
# ConfigData Loaders
org.springframework.boot.context.config.ConfigDataLoader=\
com.tencent.cloud.polaris.config.configdata.PolarisConfigDataLoader
org.springframework.boot.diagnostics.FailureAnalyzer=\
com.tencent.cloud.polaris.config.configdata.PolarisImportExceptionFailureAnalyzer
org.springframework.boot.env.EnvironmentPostProcessor=\
com.tencent.cloud.polaris.config.configdata.PolarisConfigDataMissingEnvironmentPostProcessor

@ -42,8 +42,7 @@ import static org.mockito.Mockito.when;
/**
* test for {@link PolarisConfigFileLocator}.
*
* @author lepdou 2022-06-11
*@author lepdou 2022-06-11
*/
@RunWith(MockitoJUnitRunner.class)
public class PolarisConfigFileLocatorTest {

@ -0,0 +1,170 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. 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.config.adapter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Lists;
import com.tencent.cloud.polaris.config.config.ConfigFileGroup;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.polaris.configuration.api.core.ConfigFileService;
import com.tencent.polaris.configuration.api.core.ConfigKVFile;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.core.env.CompositePropertySource;
import static org.mockito.Mockito.when;
/**
* Test for {@link PolarisConfigFilePuller}.
*
* @author wlx
*/
@RunWith(MockitoJUnitRunner.class)
public class PolarisConfigFilePullerTest {
@Mock
private PolarisContextProperties polarisContextProperties;
@Mock
private ConfigFileService configFileService;
@Mock
private PolarisPropertySourceManager polarisPropertySourceManager;
private final String testNamespace = "testNamespace";
private final String testServiceName = "testServiceName";
private final String polarisConfigPropertySourceName = "polaris-config";
@Test
public void testPullInternalConfigFiles() {
PolarisConfigFilePuller puller = PolarisConfigFilePuller.get(polarisContextProperties, configFileService,
polarisPropertySourceManager);
when(polarisContextProperties.getNamespace()).thenReturn(testNamespace);
when(polarisContextProperties.getService()).thenReturn(testServiceName);
// application.properties
Map<String, Object> applicationProperties = new HashMap<>();
applicationProperties.put("k1", "v1");
applicationProperties.put("k2", "v2");
applicationProperties.put("k3", "v3");
ConfigKVFile propertiesFile = new MockedConfigKVFile(applicationProperties);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "application.properties"))
.thenReturn(propertiesFile);
Map<String, Object> emptyMap = new HashMap<>();
ConfigKVFile emptyConfigFile = new MockedConfigKVFile(emptyMap);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "application.yml")).thenReturn(emptyConfigFile);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "bootstrap.properties")).thenReturn(emptyConfigFile);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap.yml")).thenReturn(emptyConfigFile);
CompositePropertySource compositePropertySource = new CompositePropertySource(polarisConfigPropertySourceName);
puller.initInternalConfigFiles(compositePropertySource, new String[]{}, testServiceName);
Assert.assertEquals("v1", compositePropertySource.getProperty("k1"));
Assert.assertEquals("v2", compositePropertySource.getProperty("k2"));
Assert.assertEquals("v3", compositePropertySource.getProperty("k3"));
}
@Test
public void testPullInternalConfigFilesWithProfile() {
PolarisConfigFilePuller puller = PolarisConfigFilePuller.get(polarisContextProperties, configFileService,
polarisPropertySourceManager);
when(polarisContextProperties.getNamespace()).thenReturn(testNamespace);
when(polarisContextProperties.getService()).thenReturn(testServiceName);
// application.properties
Map<String, Object> applicationProperties = new HashMap<>();
applicationProperties.put("k1", "v1");
applicationProperties.put("k2", "v2");
applicationProperties.put("k3", "v3");
ConfigKVFile propertiesFile = new MockedConfigKVFile(applicationProperties);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "application.properties"))
.thenReturn(propertiesFile);
// application-dev.properties
Map<String, Object> devProperties = new HashMap<>();
devProperties.put("k1", "v11");
ConfigKVFile devFile = new MockedConfigKVFile(devProperties);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "application-dev.properties"))
.thenReturn(devFile);
Map<String, Object> emptyMap = new HashMap<>();
ConfigKVFile emptyConfigFile = new MockedConfigKVFile(emptyMap);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "application.yml")).thenReturn(emptyConfigFile);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "application-dev.yml")).thenReturn(emptyConfigFile);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "bootstrap.properties")).thenReturn(emptyConfigFile);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "bootstrap-dev.properties")).thenReturn(emptyConfigFile);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap.yml")).thenReturn(emptyConfigFile);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap-dev.yml")).thenReturn(emptyConfigFile);
List<String> active = new ArrayList<>();
active.add("dev");
String[] activeProfiles = active.toArray(new String[]{});
CompositePropertySource compositePropertySource = new CompositePropertySource(polarisConfigPropertySourceName);
puller.initInternalConfigFiles(compositePropertySource, activeProfiles, testServiceName);
Assert.assertEquals("v11", compositePropertySource.getProperty("k1"));
Assert.assertEquals("v2", compositePropertySource.getProperty("k2"));
Assert.assertEquals("v3", compositePropertySource.getProperty("k3"));
}
@Test
public void testPullCustomConfigFilesWithProfile() {
PolarisConfigFilePuller puller = PolarisConfigFilePuller.get(polarisContextProperties, configFileService,
polarisPropertySourceManager);
when(polarisContextProperties.getNamespace()).thenReturn(testNamespace);
List<ConfigFileGroup> customFiles = new LinkedList<>();
ConfigFileGroup configFileGroup = new ConfigFileGroup();
String customGroup = "group1";
configFileGroup.setName(customGroup);
String customFile1 = "file1.properties";
String customFile2 = "file2.properties";
configFileGroup.setFiles(Lists.newArrayList(customFile1, customFile2));
customFiles.add(configFileGroup);
// file1.properties
Map<String, Object> file1Map = new HashMap<>();
file1Map.put("k1", "v1");
file1Map.put("k2", "v2");
ConfigKVFile file1 = new MockedConfigKVFile(file1Map);
when(configFileService.getConfigPropertiesFile(testNamespace, customGroup, customFile1)).thenReturn(file1);
// file2.properties
Map<String, Object> file2Map = new HashMap<>();
file2Map.put("k1", "v11");
file2Map.put("k3", "v3");
ConfigKVFile file2 = new MockedConfigKVFile(file2Map);
when(configFileService.getConfigPropertiesFile(testNamespace, customGroup, customFile2)).thenReturn(file2);
CompositePropertySource compositePropertySource = new CompositePropertySource(polarisConfigPropertySourceName);
puller.initCustomPolarisConfigFiles(compositePropertySource, customFiles);
Assert.assertEquals("v1", compositePropertySource.getProperty("k1"));
Assert.assertEquals("v2", compositePropertySource.getProperty("k2"));
Assert.assertEquals("v3", compositePropertySource.getProperty("k3"));
}
}

@ -39,8 +39,7 @@ import static org.mockito.Mockito.when;
/**
* test for {@link PolarisPropertySourceAutoRefresher}.
*
* @author lepdou 2022-06-11
*@author lepdou 2022-06-11
*/
@RunWith(MockitoJUnitRunner.class)
public class PolarisPropertiesSourceAutoRefresherTest {

@ -0,0 +1,288 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. 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.config.configdata;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Lists;
import com.tencent.cloud.polaris.config.adapter.MockedConfigKVFile;
import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.configuration.api.core.ConfigFileService;
import com.tencent.polaris.configuration.api.core.ConfigKVFile;
import com.tencent.polaris.configuration.factory.ConfigFileServiceFactory;
import org.junit.Assert;
import org.junit.Test;
import org.junit.jupiter.api.AfterAll;
import org.junit.runner.RunWith;
import org.mockito.MockedStatic;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.context.config.ConfigData;
import org.springframework.boot.context.config.ConfigDataLoaderContext;
import org.springframework.boot.context.config.Profiles;
import org.springframework.boot.logging.DeferredLogs;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.PropertySource;
import static com.tencent.cloud.polaris.config.configdata.PolarisConfigDataLoader.CUSTOM_POLARIS_CONFIG_FILE_LOADED;
import static com.tencent.cloud.polaris.config.configdata.PolarisConfigDataLoader.INTERNAL_CONFIG_FILES_LOADED;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;
/**
* Test for {@link PolarisConfigDataLoader}.
*
* @author wlx
*/
@RunWith(MockitoJUnitRunner.class)
public class PolarisConfigDataLoaderTest {
private static final SDKContext sdkContext = SDKContext.initContext();
private final String testNamespace = "testNamespace";
private final String testServiceName = "testServiceName";
private final String polarisConfigPropertySourceName = "polaris-config";
@Test
public void loadConfigDataInternalConfigFilesTest() {
try (MockedStatic<ConfigFileServiceFactory> mockedStatic = mockStatic(ConfigFileServiceFactory.class)) {
ConfigDataLoaderContext context = mock(ConfigDataLoaderContext.class);
PolarisConfigDataResource polarisConfigDataResource = mock(PolarisConfigDataResource.class);
ConfigurableBootstrapContext bootstrapContext = mock(ConfigurableBootstrapContext.class);
PolarisConfigProperties polarisConfigProperties = mock(PolarisConfigProperties.class);
PolarisContextProperties polarisContextProperties = mock(PolarisContextProperties.class);
ConfigFileService configFileService = mock(ConfigFileService.class);
Profiles profiles = mock(Profiles.class);
Map<String, Object> emptyMap = new HashMap<>();
ConfigKVFile emptyConfigFile = new MockedConfigKVFile(emptyMap);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "application.yml")).thenReturn(emptyConfigFile);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "bootstrap.properties")).thenReturn(emptyConfigFile);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap.yml")).thenReturn(emptyConfigFile);
Map<String, Object> applicationProperties = new HashMap<>();
applicationProperties.put("k1", "v1");
applicationProperties.put("k2", "v2");
applicationProperties.put("k3", "v3");
ConfigKVFile propertiesFile = new MockedConfigKVFile(applicationProperties);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "application.properties"))
.thenReturn(propertiesFile);
when(context.getBootstrapContext()).thenReturn(bootstrapContext);
when(bootstrapContext.get(eq(SDKContext.class))).thenReturn(sdkContext);
when(bootstrapContext.get(eq(PolarisPropertySourceManager.class))).thenReturn(new PolarisPropertySourceManager());
when(polarisContextProperties.getNamespace()).thenReturn(testNamespace);
when(polarisContextProperties.getService()).thenReturn(testServiceName);
when(polarisConfigProperties.getGroups()).thenReturn(null);
when(profiles.getActive()).thenReturn(Lists.newArrayList());
PolarisConfigDataLoader polarisConfigDataLoader = new PolarisConfigDataLoader(new DeferredLogs());
if (INTERNAL_CONFIG_FILES_LOADED.get()) {
INTERNAL_CONFIG_FILES_LOADED.compareAndSet(true, false);
}
if (CUSTOM_POLARIS_CONFIG_FILE_LOADED.get()) {
CUSTOM_POLARIS_CONFIG_FILE_LOADED.compareAndSet(true, false);
}
when(polarisConfigDataResource.getPolarisConfigProperties()).thenReturn(polarisConfigProperties);
when(polarisConfigDataResource.getPolarisContextProperties()).thenReturn(polarisContextProperties);
when(polarisConfigDataResource.getServiceName()).thenReturn(testServiceName);
when(polarisConfigDataResource.getProfiles()).thenReturn(profiles);
mockedStatic.when(() -> {
ConfigFileServiceFactory.createConfigFileService(sdkContext);
}).thenReturn(configFileService);
ConfigData configData = polarisConfigDataLoader.load(context, polarisConfigDataResource);
List<PropertySource<?>> propertySources = configData.getPropertySources();
CompositePropertySource compositePropertySource = new CompositePropertySource(polarisConfigPropertySourceName);
propertySources.forEach(compositePropertySource::addPropertySource);
Assert.assertEquals("v1", compositePropertySource.getProperty("k1"));
Assert.assertEquals("v2", compositePropertySource.getProperty("k2"));
Assert.assertEquals("v3", compositePropertySource.getProperty("k3"));
}
}
@Test
public void loadConfigDataInternalConfigFilesTestWithProfile() {
try (MockedStatic<ConfigFileServiceFactory> mockedStatic = mockStatic(ConfigFileServiceFactory.class)) {
ConfigDataLoaderContext context = mock(ConfigDataLoaderContext.class);
PolarisConfigDataResource polarisConfigDataResource = mock(PolarisConfigDataResource.class);
ConfigurableBootstrapContext bootstrapContext = mock(ConfigurableBootstrapContext.class);
PolarisConfigProperties polarisConfigProperties = mock(PolarisConfigProperties.class);
PolarisContextProperties polarisContextProperties = mock(PolarisContextProperties.class);
ConfigFileService configFileService = mock(ConfigFileService.class);
Profiles profiles = mock(Profiles.class);
Map<String, Object> applicationProperties = new HashMap<>();
applicationProperties.put("k1", "v1");
applicationProperties.put("k2", "v2");
applicationProperties.put("k3", "v3");
ConfigKVFile propertiesFile = new MockedConfigKVFile(applicationProperties);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "application.properties"))
.thenReturn(propertiesFile);
// application-dev.properties
Map<String, Object> devProperties = new HashMap<>();
devProperties.put("k1", "v11");
ConfigKVFile devFile = new MockedConfigKVFile(devProperties);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "application-dev.properties"))
.thenReturn(devFile);
Map<String, Object> emptyMap = new HashMap<>();
ConfigKVFile emptyConfigFile = new MockedConfigKVFile(emptyMap);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "application.yml")).thenReturn(emptyConfigFile);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "application-dev.yml")).thenReturn(emptyConfigFile);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "bootstrap.properties")).thenReturn(emptyConfigFile);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "bootstrap-dev.properties")).thenReturn(emptyConfigFile);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap.yml")).thenReturn(emptyConfigFile);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap-dev.yml")).thenReturn(emptyConfigFile);
when(polarisConfigProperties.getGroups()).thenReturn(null);
when(polarisConfigProperties.getGroups()).thenReturn(null);
List<String> active = new ArrayList<>();
active.add("dev");
when(profiles.getActive()).thenReturn(active);
when(context.getBootstrapContext()).thenReturn(bootstrapContext);
when(bootstrapContext.get(eq(SDKContext.class))).thenReturn(sdkContext);
when(bootstrapContext.get(eq(PolarisPropertySourceManager.class))).thenReturn(new PolarisPropertySourceManager());
when(polarisContextProperties.getNamespace()).thenReturn(testNamespace);
when(polarisContextProperties.getService()).thenReturn(testServiceName);
when(polarisConfigProperties.getGroups()).thenReturn(null);
PolarisConfigDataLoader polarisConfigDataLoader = new PolarisConfigDataLoader(new DeferredLogs());
if (INTERNAL_CONFIG_FILES_LOADED.get()) {
INTERNAL_CONFIG_FILES_LOADED.compareAndSet(true, false);
}
if (CUSTOM_POLARIS_CONFIG_FILE_LOADED.get()) {
CUSTOM_POLARIS_CONFIG_FILE_LOADED.compareAndSet(true, false);
}
when(polarisConfigDataResource.getPolarisConfigProperties()).thenReturn(polarisConfigProperties);
when(polarisConfigDataResource.getPolarisContextProperties()).thenReturn(polarisContextProperties);
when(polarisConfigDataResource.getServiceName()).thenReturn(testServiceName);
when(polarisConfigDataResource.getProfiles()).thenReturn(profiles);
mockedStatic.when(() -> {
ConfigFileServiceFactory.createConfigFileService(sdkContext);
}).thenReturn(configFileService);
ConfigData configData = polarisConfigDataLoader.load(context, polarisConfigDataResource);
List<PropertySource<?>> propertySources = configData.getPropertySources();
CompositePropertySource compositePropertySource = new CompositePropertySource(polarisConfigPropertySourceName);
propertySources.forEach(compositePropertySource::addPropertySource);
Assert.assertEquals("v11", compositePropertySource.getProperty("k1"));
Assert.assertEquals("v2", compositePropertySource.getProperty("k2"));
Assert.assertEquals("v3", compositePropertySource.getProperty("k3"));
}
}
@Test
public void loadConfigDataCustomConfigFilesTestWithProfile() {
try (MockedStatic<ConfigFileServiceFactory> mockedStatic = mockStatic(ConfigFileServiceFactory.class)) {
ConfigDataLoaderContext context = mock(ConfigDataLoaderContext.class);
PolarisConfigDataResource polarisConfigDataResource = mock(PolarisConfigDataResource.class);
ConfigurableBootstrapContext bootstrapContext = mock(ConfigurableBootstrapContext.class);
PolarisConfigProperties polarisConfigProperties = mock(PolarisConfigProperties.class);
PolarisContextProperties polarisContextProperties = mock(PolarisContextProperties.class);
ConfigFileService configFileService = mock(ConfigFileService.class);
Profiles profiles = mock(Profiles.class);
Map<String, Object> emptyMap = new HashMap<>();
ConfigKVFile emptyConfigFile = new MockedConfigKVFile(emptyMap);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "application.properties")).thenReturn(emptyConfigFile);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "application.yml")).thenReturn(emptyConfigFile);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "bootstrap.properties")).thenReturn(emptyConfigFile);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap.yml")).thenReturn(emptyConfigFile);
String customGroup = "group1";
String customFile1 = "file1.properties";
when(polarisConfigDataResource.getFileName()).thenReturn(customFile1);
when(polarisConfigDataResource.getGroupName()).thenReturn(customGroup);
when(polarisConfigProperties.getGroups()).thenReturn(null);
when(profiles.getActive()).thenReturn(Lists.newArrayList());
// file1.properties
Map<String, Object> file1Map = new HashMap<>();
file1Map.put("k1", "v1");
file1Map.put("k2", "v2");
file1Map.put("k3", "v3");
ConfigKVFile file1 = new MockedConfigKVFile(file1Map);
when(configFileService.getConfigPropertiesFile(testNamespace, customGroup, customFile1)).thenReturn(file1);
when(context.getBootstrapContext()).thenReturn(bootstrapContext);
when(bootstrapContext.get(eq(SDKContext.class))).thenReturn(sdkContext);
when(bootstrapContext.get(eq(PolarisPropertySourceManager.class))).thenReturn(new PolarisPropertySourceManager());
when(polarisContextProperties.getNamespace()).thenReturn(testNamespace);
when(polarisContextProperties.getService()).thenReturn(testServiceName);
when(polarisConfigProperties.getGroups()).thenReturn(null);
when(profiles.getActive()).thenReturn(Lists.newArrayList());
PolarisConfigDataLoader polarisConfigDataLoader = new PolarisConfigDataLoader(new DeferredLogs());
if (INTERNAL_CONFIG_FILES_LOADED.get()) {
INTERNAL_CONFIG_FILES_LOADED.compareAndSet(true, false);
}
if (CUSTOM_POLARIS_CONFIG_FILE_LOADED.get()) {
CUSTOM_POLARIS_CONFIG_FILE_LOADED.compareAndSet(true, false);
}
when(polarisConfigDataResource.getPolarisConfigProperties()).thenReturn(polarisConfigProperties);
when(polarisConfigDataResource.getPolarisContextProperties()).thenReturn(polarisContextProperties);
when(polarisConfigDataResource.getServiceName()).thenReturn(testServiceName);
when(polarisConfigDataResource.getProfiles()).thenReturn(profiles);
mockedStatic.when(() -> {
ConfigFileServiceFactory.createConfigFileService(sdkContext);
}).thenReturn(configFileService);
ConfigData configData = polarisConfigDataLoader.load(context, polarisConfigDataResource);
List<PropertySource<?>> propertySources = configData.getPropertySources();
CompositePropertySource compositePropertySource = new CompositePropertySource(polarisConfigPropertySourceName);
propertySources.forEach(compositePropertySource::addPropertySource);
Assert.assertEquals("v1", compositePropertySource.getProperty("k1"));
Assert.assertEquals("v2", compositePropertySource.getProperty("k2"));
Assert.assertEquals("v3", compositePropertySource.getProperty("k3"));
}
}
@AfterAll
static void afterAll() {
if (sdkContext != null) {
sdkContext.destroy();
}
}
}

@ -0,0 +1,74 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. 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.config.configdata;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.context.config.ConfigDataLocation;
import org.springframework.boot.context.config.ConfigDataLocationResolverContext;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.logging.DeferredLogs;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
/**
* Test for {@link PolarisConfigDataLocationResolver}.
*
* @author wlx
*/
@RunWith(MockitoJUnitRunner.class)
public class PolarisConfigDataLocationResolverTest {
private final PolarisConfigDataLocationResolver resolver = new PolarisConfigDataLocationResolver(new DeferredLogs());
@Mock
private ConfigDataLocationResolverContext context;
private final MockEnvironment environment = new MockEnvironment();
private final Binder environmentBinder = Binder.get(this.environment);
@Test
public void testIsResolvable() {
when(context.getBinder()).thenReturn(environmentBinder);
assertThat(
this.resolver.isResolvable(this.context, ConfigDataLocation.of("configserver:")))
.isFalse();
assertThat(
this.resolver.isResolvable(this.context, ConfigDataLocation.of("polaris:")))
.isTrue();
assertThat(
this.resolver.isResolvable(this.context, ConfigDataLocation.of("polaris")))
.isTrue();
}
@Test
public void unEnabledPolarisConfigData() {
environment.setProperty("spring.cloud.polaris.config.enabled", "false");
when(context.getBinder()).thenReturn(environmentBinder);
assertThat(
this.resolver.isResolvable(this.context, ConfigDataLocation.of("polaris:")))
.isFalse();
}
}

@ -0,0 +1,105 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. 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.config.configdata;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
/**
* Test for {@link PolarisConfigDataMissingEnvironmentPostProcessor}.
*
* @author wlx
*/
public class PolarisConfigDataMissingEnvironmentPostProcessorTest {
@Test
public void missConfigData() {
MockEnvironment environment = new MockEnvironment();
SpringApplication app = mock(SpringApplication.class);
PolarisConfigDataMissingEnvironmentPostProcessor processor = new PolarisConfigDataMissingEnvironmentPostProcessor();
assertThatThrownBy(() -> processor.postProcessEnvironment(environment, app))
.isInstanceOf(PolarisConfigDataMissingEnvironmentPostProcessor.ImportException.class);
}
@Test
public void bootstrapEnabledTest() {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("spring.cloud.bootstrap.enabled", "true");
SpringApplication app = mock(SpringApplication.class);
PolarisConfigDataMissingEnvironmentPostProcessor processor = new PolarisConfigDataMissingEnvironmentPostProcessor();
// if bootstrap enabled,don't throw ImportException
assertThatCode(() -> processor.postProcessEnvironment(environment, app)).doesNotThrowAnyException();
}
@Test
public void legacyProcessingTest() {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("spring.config.use-legacy-processing", "true");
SpringApplication app = mock(SpringApplication.class);
PolarisConfigDataMissingEnvironmentPostProcessor processor = new PolarisConfigDataMissingEnvironmentPostProcessor();
// if use-legacy-processing,don't throw ImportException
assertThatCode(() -> processor.postProcessEnvironment(environment, app)).doesNotThrowAnyException();
}
@Test
public void closeImportCheck() {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("spring.cloud.polaris.config.import-check.enabled", "false");
SpringApplication app = mock(SpringApplication.class);
PolarisConfigDataMissingEnvironmentPostProcessor processor = new PolarisConfigDataMissingEnvironmentPostProcessor();
// if import-check.enabled is false,don't throw ImportException
assertThatCode(() -> processor.postProcessEnvironment(environment, app)).doesNotThrowAnyException();
}
@Test
public void closePolarisConfig() {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("spring.cloud.polaris.config.enabled", "false");
SpringApplication app = mock(SpringApplication.class);
PolarisConfigDataMissingEnvironmentPostProcessor processor = new PolarisConfigDataMissingEnvironmentPostProcessor();
// if polaris.config is false,don't throw ImportException
assertThatCode(() -> processor.postProcessEnvironment(environment, app)).doesNotThrowAnyException();
}
@Test
public void normalConfigDataImport() {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("spring.config.import", "polaris");
SpringApplication app = mock(SpringApplication.class);
PolarisConfigDataMissingEnvironmentPostProcessor processor = new PolarisConfigDataMissingEnvironmentPostProcessor();
// config polaris config import ,don't throw ImportException
assertThatCode(() -> processor.postProcessEnvironment(environment, app)).doesNotThrowAnyException();
}
@Test
public void importOtherConfigDataWithoutPolaris() {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("spring.config.import", "file:application.properties");
SpringApplication app = mock(SpringApplication.class);
PolarisConfigDataMissingEnvironmentPostProcessor processor = new PolarisConfigDataMissingEnvironmentPostProcessor();
assertThatThrownBy(() -> processor.postProcessEnvironment(environment, app))
.isInstanceOf(PolarisConfigDataMissingEnvironmentPostProcessor.ImportException.class);
}
}

@ -0,0 +1,52 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. 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.config.configdata;
import com.tencent.polaris.api.utils.StringUtils;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.catchThrowable;
import static org.mockito.Mockito.mock;
/**
* Test for {@link PolarisImportExceptionFailureAnalyzer}.
*
* @author wlx
*/
public class PolarisImportExceptionFailureAnalyzerTest {
@Test
public void failureAnalyzerTest() {
SpringApplication app = mock(SpringApplication.class);
MockEnvironment environment = new MockEnvironment();
PolarisConfigDataMissingEnvironmentPostProcessor processor = new PolarisConfigDataMissingEnvironmentPostProcessor();
assertThatThrownBy(() -> processor.postProcessEnvironment(environment, app))
.isInstanceOf(PolarisConfigDataMissingEnvironmentPostProcessor.ImportException.class);
Throwable throwable = catchThrowable(() -> processor.postProcessEnvironment(environment, app));
PolarisImportExceptionFailureAnalyzer failureAnalyzer = new PolarisImportExceptionFailureAnalyzer();
FailureAnalysis analyze = failureAnalyzer.analyze(throwable);
assertThat(StringUtils.isNotBlank(analyze.getAction())).isTrue();
}
}

@ -7,3 +7,7 @@ spring:
namespace: default
config:
connect-remote-server: false
# auto-refresh: true
config:
import:
- optional:polaris

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-tencent-examples</artifactId>
<groupId>com.tencent.cloud</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>polaris-config-data-example</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Cloud Tencent dependency -->
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,55 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. 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.config.example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* the endpoint for get config.
*
* @author lepdou 2022-03-10
*/
@RestController
@RefreshScope
public class ConfigController {
@Value("${timeout:1000}")
private int timeout;
@Autowired
private Person person;
@Autowired
private Environment environment;
@GetMapping("/timeout")
public int timeout() {
environment.getProperty("timeout", "1000");
return timeout;
}
@GetMapping("/person")
public String person() {
return person.toString();
}
}

@ -0,0 +1,58 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. 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.config.example;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* example property object.
*
* @author lepdou 2022-03-28
*/
@Component
@ConfigurationProperties(prefix = "teacher")
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}

@ -0,0 +1,49 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. 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.config.example;
import java.util.Set;
import com.tencent.cloud.polaris.config.annotation.PolarisConfigKVFileChangeListener;
import com.tencent.cloud.polaris.config.listener.ConfigChangeEvent;
import org.springframework.stereotype.Component;
/**
* Custom Config Listener Example .
*
* @author Palmer Xu 2022-06-06
*/
@Component
public final class PersonConfigChangeListener {
/**
* PolarisConfigKVFileChangeListener Example .
* @param event instance of {@link ConfigChangeEvent}
*/
@PolarisConfigKVFileChangeListener(interestedKeyPrefixes = "teacher")
public void onChange(ConfigChangeEvent event) {
Set<String> changedKeys = event.changedKeys();
for (String changedKey : changedKeys) {
System.out.printf("%s = %s \n", changedKey, event.getChange(changedKey));
}
}
}

@ -0,0 +1,16 @@
package com.tencent.cloud.polaris.config.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author wlx
* @date 2022/7/6 9:15
*/
@SpringBootApplication
public class PolarisConfigDataExampleApplication {
public static void main(String[] args) {
SpringApplication.run(PolarisConfigDataExampleApplication.class, args);
}
}

@ -0,0 +1,26 @@
server:
port: 48085
spring:
application:
name: polaris-config-data-example
cloud:
polaris:
address: grpc://183.47.111.80:8091
namespace: default
config:
auto-refresh: true # auto refresh when config file changed
groups:
- name: ${spring.application.name} # group name
files: [ "config/application.properties", "config/bootstrap.yml" ]
config:
import:
- optional:polaris
- optional:polaris:test.yml
- optional:polaris:configdataexample:test.yml
- optional:polaris:config/bootstrap.yml
management:
endpoints:
web:
exposure:
include:
- polaris-config

@ -30,6 +30,11 @@
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
</dependencies>
<build>

@ -24,6 +24,7 @@
<module>polaris-router-example</module>
<module>metadata-transfer-example</module>
<module>polaris-router-grayrelease-example</module>
<module>polaris-config-data-example</module>
</modules>
<properties>

@ -21,11 +21,6 @@
</dependency>
<!-- Spring Cloud Tencent dependencies end -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!-- Polaris dependencies start -->
<dependency>
<groupId>com.tencent.polaris</groupId>

@ -53,4 +53,8 @@ public class ModifyAddress implements PolarisConfigModifier {
public int getOrder() {
return ContextConstant.ModifierOrder.FIRST;
}
public void setProperties(PolarisContextProperties properties) {
this.properties = properties;
}
}

@ -47,8 +47,11 @@ public class PolarisContextAutoConfiguration {
@Bean(name = "polarisContext", initMethod = "init", destroyMethod = "destroy")
@ConditionalOnMissingBean
public SDKContext polarisContext(PolarisContextProperties properties, Environment environment,
List<PolarisConfigModifier> modifierList) throws PolarisException {
return SDKContext.initContextByConfig(properties.configuration(environment, modifierList));
List<PolarisConfigModifier> modifierList)
throws PolarisException {
return SDKContext.initContextByConfig(properties.configuration(modifierList, () -> {
return environment.getProperty("spring.cloud.client.ip-address");
}));
}
@Bean

@ -21,6 +21,7 @@ package com.tencent.cloud.polaris.context.config;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import com.tencent.cloud.polaris.context.PolarisConfigModifier;
@ -31,7 +32,6 @@ import com.tencent.polaris.factory.config.ConfigurationImpl;
import org.apache.commons.lang.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.env.Environment;
import org.springframework.util.CollectionUtils;
/**
@ -67,7 +67,7 @@ public class PolarisContextProperties {
*/
private String service;
public Configuration configuration(Environment environment, List<PolarisConfigModifier> modifierList) {
public Configuration configuration(List<PolarisConfigModifier> modifierList, Supplier<String> ipAddressSupplier) {
// 1. Read user-defined polaris.yml configuration
ConfigurationImpl configuration = (ConfigurationImpl) ConfigAPIFactory
.defaultConfig(ConfigProvider.DEFAULT_CONFIG);
@ -75,7 +75,7 @@ public class PolarisContextProperties {
// 2. Override user-defined polaris.yml configuration with SCT configuration
String defaultHost = this.localIpAddress;
if (StringUtils.isBlank(localIpAddress)) {
defaultHost = environment.getProperty("spring.cloud.client.ip-address");
defaultHost = ipAddressSupplier.get();
}
configuration.getGlobal().getAPI().setBindIP(defaultHost);
@ -101,11 +101,11 @@ public class PolarisContextProperties {
this.address = address;
}
String getLocalIpAddress() {
public String getLocalIpAddress() {
return localIpAddress;
}
void setLocalIpAddress(String localIpAddress) {
public void setLocalIpAddress(String localIpAddress) {
this.localIpAddress = localIpAddress;
}

@ -30,7 +30,7 @@ import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for {@link PolarisLoadBalancerAutoConfiguration}
* Test for {@link PolarisLoadBalancerAutoConfiguration}.
*
* @author Haotian Zhang
*/

Loading…
Cancel
Save