feat(config-server): add MongoDB environment repository support (#2390)
* feat(config-server): add MongoDB environment repository support This commit introduces MongoDB as a new backend option for the Spring Cloud Config Server, enabling users to store and manage their configuration properties in a MongoDB database. * resolve pr comments * add documentation for mongodb backend * resolve comments
This commit is contained in:
committed by
GitHub
parent
3c69958559
commit
634f46b424
@@ -15,6 +15,7 @@
|
||||
*** xref:server/environment-repository/aws-parameter-store-backend.adoc[]
|
||||
*** xref:server/environment-repository/aws-secrets-manager-backend.adoc[]
|
||||
*** xref:server/environment-repository/credhub-backend.adoc[]
|
||||
*** xref:server/environment-repository/mongo-backend.adoc[]
|
||||
*** xref:server/environment-repository/composite-repositories.adoc[]
|
||||
*** xref:server/environment-repository/custom-enviroment-repository.adoc[]
|
||||
*** xref:server/environment-repository/property-overrides.adoc[]
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
[[mongo-backend]]
|
||||
= MongoDB Backend
|
||||
:page-section-summary-toc: 1
|
||||
|
||||
Spring Cloud Config Server supports MongoDB as a backend for configuration properties.
|
||||
You can enable this feature by adding `spring-boot-starter-data-mongodb` to the classpath and using the `mongodb` profile.
|
||||
|
||||
[source,xml,indent=0]
|
||||
.pom.xml
|
||||
----
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
Configure your application's `application.properties` or `application.yml` to point to your MongoDB instance:
|
||||
|
||||
[source,yaml]
|
||||
----
|
||||
spring:
|
||||
profiles:
|
||||
active: mongodb
|
||||
data:
|
||||
mongodb:
|
||||
database: your-database-name
|
||||
port: '27017'
|
||||
host: localhost
|
||||
----
|
||||
|
||||
The configuration properties should be stored in documents within the `properties` collection. Each document represents a set of properties for a given application, profile, and label.
|
||||
|
||||
Example MongoDB document:
|
||||
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"application": "myapp",
|
||||
"profile": "development",
|
||||
"label": "master",
|
||||
"properties": {
|
||||
"property1": "value1",
|
||||
"property2": "value2"
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
You can disable autoconfiguration for `MongoDbEnvironmentRepository` by setting the `spring.cloud.config.server.mongodb.enabled` property to `false`.
|
||||
|
||||
The default values for MongoDB backend configuration are as follows:
|
||||
|
||||
- **Collection Name:** `"properties"` (Name of the MongoDB collection to query for configuration properties.)
|
||||
|
||||
- **Default Label:** `"master"` (Default label to use if none is specified.)
|
||||
|
||||
NOTE: You can change these defaults by setting `spring.cloud.config.server.mongodb.collection` and `spring.cloud.config.server.mongodb.defaultLabel` in your application's configuration.
|
||||
@@ -147,6 +147,11 @@
|
||||
<artifactId>google-auth-library-oauth2-http</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
@@ -232,6 +237,16 @@
|
||||
<artifactId>spring-core-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-testcontainers</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>mongodb</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
|
||||
@@ -69,6 +69,9 @@ import org.springframework.cloud.config.server.environment.HttpRequestConfigToke
|
||||
import org.springframework.cloud.config.server.environment.JdbcEnvironmentProperties;
|
||||
import org.springframework.cloud.config.server.environment.JdbcEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.JdbcEnvironmentRepositoryFactory;
|
||||
import org.springframework.cloud.config.server.environment.MongoDbEnvironmentProperties;
|
||||
import org.springframework.cloud.config.server.environment.MongoDbEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.MongoDbEnvironmentRepositoryFactory;
|
||||
import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentProperties;
|
||||
import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentRepository;
|
||||
import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentRepositoryFactory;
|
||||
@@ -99,6 +102,7 @@ import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.credhub.core.CredHubOperations;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.vault.core.VaultTemplate;
|
||||
@@ -112,19 +116,21 @@ import org.springframework.vault.core.VaultTemplate;
|
||||
* @author Scott Frederick
|
||||
* @author Tejas Pandilwar
|
||||
* @author Iulian Antohe
|
||||
* @author Alexandros Pappas
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties({ SvnKitEnvironmentProperties.class, CredhubEnvironmentProperties.class,
|
||||
JdbcEnvironmentProperties.class, NativeEnvironmentProperties.class, VaultEnvironmentProperties.class,
|
||||
RedisEnvironmentProperties.class, AwsS3EnvironmentProperties.class,
|
||||
AwsSecretsManagerEnvironmentProperties.class, AwsParameterStoreEnvironmentProperties.class,
|
||||
GoogleSecretManagerEnvironmentProperties.class })
|
||||
GoogleSecretManagerEnvironmentProperties.class, MongoDbEnvironmentProperties.class })
|
||||
@Import({ CompositeRepositoryConfiguration.class, JdbcRepositoryConfiguration.class, VaultConfiguration.class,
|
||||
VaultRepositoryConfiguration.class, SpringVaultRepositoryConfiguration.class, CredhubConfiguration.class,
|
||||
CredhubRepositoryConfiguration.class, SvnRepositoryConfiguration.class, NativeRepositoryConfiguration.class,
|
||||
GitRepositoryConfiguration.class, RedisRepositoryConfiguration.class, GoogleCloudSourceConfiguration.class,
|
||||
AwsS3RepositoryConfiguration.class, AwsSecretsManagerRepositoryConfiguration.class,
|
||||
AwsParameterStoreRepositoryConfiguration.class, GoogleSecretManagerRepositoryConfiguration.class,
|
||||
MongoRepositoryConfiguration.class,
|
||||
// DefaultRepositoryConfiguration must be last
|
||||
DefaultRepositoryConfiguration.class })
|
||||
public class EnvironmentRepositoryConfiguration {
|
||||
@@ -378,6 +384,19 @@ public class EnvironmentRepositoryConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(MongoTemplate.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.config.server.mongodb.enabled", matchIfMissing = true)
|
||||
static class MongoDbFactoryConfig {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(MongoTemplate.class)
|
||||
public MongoDbEnvironmentRepositoryFactory mongoDbEnvironmentRepositoryFactory(MongoTemplate mongoTemplate) {
|
||||
return new MongoDbEnvironmentRepositoryFactory(mongoTemplate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@@ -579,3 +598,18 @@ class GoogleSecretManagerRepositoryConfiguration {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Profile("mongodb")
|
||||
@ConditionalOnClass(MongoTemplate.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.config.server.mongodb.enabled", matchIfMissing = true)
|
||||
class MongoRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(MongoTemplate.class)
|
||||
public MongoDbEnvironmentRepository mongoDbEnvironmentRepository(MongoDbEnvironmentRepositoryFactory factory,
|
||||
MongoDbEnvironmentProperties environmentProperties) {
|
||||
return factory.build(environmentProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2018-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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 org.springframework.cloud.config.server.environment;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.config.server.support.EnvironmentRepositoryProperties;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* Properties related to MongoDB environment repository.
|
||||
*
|
||||
* @author Alexandros Pappas
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.config.server.mongodb")
|
||||
public class MongoDbEnvironmentProperties implements EnvironmentRepositoryProperties {
|
||||
|
||||
/**
|
||||
* Flag to indicate that MongoDB environment repository configuration is enabled.
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* Order of the MongoDB environment repository.
|
||||
*/
|
||||
private int order = Ordered.LOWEST_PRECEDENCE - 10;
|
||||
|
||||
/**
|
||||
* Name of the MongoDB collection to query for configuration properties.
|
||||
*/
|
||||
private String collection = "properties";
|
||||
|
||||
/**
|
||||
* Flag to determine how to handle query exceptions.
|
||||
*/
|
||||
private boolean failOnError = true;
|
||||
|
||||
/**
|
||||
* Default label to use if none is specified.
|
||||
*/
|
||||
private String defaultLabel = "master";
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public String getCollection() {
|
||||
return collection;
|
||||
}
|
||||
|
||||
public void setCollection(String collection) {
|
||||
this.collection = collection;
|
||||
}
|
||||
|
||||
public boolean isFailOnError() {
|
||||
return failOnError;
|
||||
}
|
||||
|
||||
public void setFailOnError(boolean failOnError) {
|
||||
this.failOnError = failOnError;
|
||||
}
|
||||
|
||||
public String getDefaultLabel() {
|
||||
return defaultLabel;
|
||||
}
|
||||
|
||||
public void setDefaultLabel(String defaultLabel) {
|
||||
this.defaultLabel = defaultLabel;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2018-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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 org.springframework.cloud.config.server.environment;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.mongodb.MongoException;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Alexandros Pappas
|
||||
*/
|
||||
public class MongoDbEnvironmentRepository implements EnvironmentRepository, Ordered {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(JdbcEnvironmentRepository.class);
|
||||
|
||||
private final MongoTemplate mongoTemplate;
|
||||
|
||||
private final MongoDbEnvironmentProperties properties;
|
||||
|
||||
public MongoDbEnvironmentRepository(MongoTemplate mongoTemplate, MongoDbEnvironmentProperties properties) {
|
||||
this.mongoTemplate = mongoTemplate;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String profile, String label) {
|
||||
label = StringUtils.hasText(label) ? label : this.properties.getDefaultLabel();
|
||||
profile = StringUtils.hasText(profile) ? profile : "default";
|
||||
|
||||
// Prepare the environment with applications and profiles
|
||||
String[] profilesArray = StringUtils.commaDelimitedListToStringArray(profile);
|
||||
Environment environment = new Environment(application, profilesArray, label, null, null);
|
||||
|
||||
// Prepend "application," to config if not already present
|
||||
String config = application.startsWith("application") ? application : "application," + application;
|
||||
|
||||
List<String> applications = Arrays.stream(StringUtils.commaDelimitedListToStringArray(config)).distinct()
|
||||
.collect(Collectors.toList());
|
||||
List<String> profiles = Arrays.stream(profilesArray).distinct().collect(Collectors.toList());
|
||||
|
||||
// Reverse for the intended processing order
|
||||
Collections.reverse(applications);
|
||||
Collections.reverse(profiles);
|
||||
|
||||
// Add property sources for each combination of application and profile
|
||||
for (String env : profiles) {
|
||||
for (String app : applications) {
|
||||
addPropertySource(environment, app, env, label);
|
||||
}
|
||||
}
|
||||
// add properties without profile, equivalent to foo.yml, application.yml
|
||||
for (String app : applications) {
|
||||
addPropertySource(environment, app, null, label);
|
||||
}
|
||||
return environment;
|
||||
}
|
||||
|
||||
private void addPropertySource(Environment environment, String application, String profile, String label) {
|
||||
try {
|
||||
Criteria criteria = Criteria.where("application").is(application).and("label").is(label);
|
||||
if (profile != null) {
|
||||
criteria = criteria.and("profile").is(profile);
|
||||
}
|
||||
else {
|
||||
// Handling properties without profile by explicitly looking for them
|
||||
criteria = criteria.andOperator(Criteria.where("profile").is(null));
|
||||
}
|
||||
|
||||
Query query = new Query(criteria);
|
||||
List<Map> propertyMaps = this.mongoTemplate.find(query, Map.class, this.properties.getCollection());
|
||||
|
||||
for (Map propertyMap : propertyMaps) {
|
||||
String propertySourceName = (profile != null) ? application + "-" + profile : application;
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> source = (Map<String, Object>) propertyMap.get("properties");
|
||||
if (source != null && !source.isEmpty()) {
|
||||
environment.add(new PropertySource(propertySourceName, source));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (DataAccessException | MongoException e) {
|
||||
if (!this.properties.isFailOnError()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Failed to retrieve configuration from MongoDB", e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.properties.getOrder();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2018-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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 org.springframework.cloud.config.server.environment;
|
||||
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
|
||||
/**
|
||||
* Factory for creating instances of MongoDbEnvironmentRepository.
|
||||
*
|
||||
* @author Alexandros Pappas
|
||||
*/
|
||||
public class MongoDbEnvironmentRepositoryFactory
|
||||
implements EnvironmentRepositoryFactory<MongoDbEnvironmentRepository, MongoDbEnvironmentProperties> {
|
||||
|
||||
private final MongoTemplate mongoTemplate;
|
||||
|
||||
public MongoDbEnvironmentRepositoryFactory(MongoTemplate mongoTemplate) {
|
||||
this.mongoTemplate = mongoTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoDbEnvironmentRepository build(MongoDbEnvironmentProperties environmentProperties) {
|
||||
return new MongoDbEnvironmentRepository(this.mongoTemplate, environmentProperties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2018-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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 org.springframework.cloud.config.server.environment;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
|
||||
import org.springframework.boot.test.context.runner.ContextConsumer;
|
||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||
import org.springframework.cloud.config.server.test.ConfigServerTestUtils;
|
||||
import org.springframework.cloud.config.server.test.TestConfigServerApplication;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests to verify MongoDbEnvironmentRepository configuration.
|
||||
*
|
||||
* @author Alexandros Pappas
|
||||
*/
|
||||
public class MongoDbEnvironmentRepositoryConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void mongoDbEnvironmentRepositoryBeansConfiguredWhenDefault() {
|
||||
new WebApplicationContextRunner().withUserConfiguration(TestConfigServerApplication.class)
|
||||
.withPropertyValues("spring.profiles.active=test,mongodb", "spring.main.web-application-type=none")
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(MongoDbEnvironmentRepositoryFactory.class);
|
||||
assertThat(context).hasSingleBean(MongoDbEnvironmentRepository.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mongoDbEnvironmentRepositoryBeansConfiguredWhenEnabled() throws IOException {
|
||||
getApplicationContextWithMongoDbEnabled(true, context -> {
|
||||
assertThat(context).hasSingleBean(MongoDbEnvironmentRepositoryFactory.class);
|
||||
assertThat(context).hasSingleBean(MongoDbEnvironmentRepository.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mongoDbEnvironmentRepositoryFactoryNotConfiguredWhenDisabled() throws IOException {
|
||||
getApplicationContextWithMongoDbEnabled(false,
|
||||
context -> assertThat(context).doesNotHaveBean(MongoDbEnvironmentRepositoryFactory.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mongoDbEnvironmentRepositoryNotConfiguredWhenDisabled() throws IOException {
|
||||
getApplicationContextWithMongoDbEnabled(false,
|
||||
context -> assertThat(context).doesNotHaveBean(MongoDbEnvironmentRepository.class));
|
||||
}
|
||||
|
||||
private void getApplicationContextWithMongoDbEnabled(boolean mongoDbEnabled,
|
||||
ContextConsumer<? super AssertableWebApplicationContext> consumer) throws IOException {
|
||||
String uri = ConfigServerTestUtils.prepareLocalRepo();
|
||||
new WebApplicationContextRunner().withUserConfiguration(TestConfigServerApplication.class)
|
||||
.withPropertyValues("spring.profiles.active=test,mongodb", "spring.main.web-application-type=none",
|
||||
"spring.cloud.config.server.git.uri:" + uri,
|
||||
"spring.cloud.config.server.mongodb.enabled:" + mongoDbEnabled)
|
||||
.run(consumer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright 2018-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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 org.springframework.cloud.config.server.environment;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.mongodb.MongoException;
|
||||
import com.mongodb.MongoTimeoutException;
|
||||
import org.bson.Document;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.testcontainers.containers.MongoDBContainer;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import wiremock.com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
|
||||
/**
|
||||
* @author Alexandros Pappas
|
||||
*/
|
||||
@SpringBootTest
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
|
||||
@ActiveProfiles("mongodb")
|
||||
@Testcontainers
|
||||
@Tag("DockerRequired")
|
||||
public class MongoDbEnvironmentRepositoryTests {
|
||||
|
||||
@Container
|
||||
@ServiceConnection
|
||||
static MongoDBContainer mongoContainer = new MongoDBContainer("mongo:5.0");
|
||||
|
||||
@Autowired
|
||||
private MongoTemplate mongoTemplate;
|
||||
|
||||
@BeforeEach
|
||||
void setup() throws IOException {
|
||||
mongoContainer.start();
|
||||
mongoTemplate.dropCollection("properties");
|
||||
InputStream inputStream = new ClassPathResource("/data-mongo.json").getInputStream();
|
||||
String json = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
|
||||
List<Document> documents = Arrays.asList(new ObjectMapper().readValue(json, Document[].class));
|
||||
mongoTemplate.getCollection("properties").insertMany(documents);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicProperties() {
|
||||
MongoDbEnvironmentProperties properties = new MongoDbEnvironmentProperties();
|
||||
Environment env = new MongoDbEnvironmentRepository(mongoTemplate, properties).findOne("foo", "bar", "");
|
||||
assertThat(env.getName()).isEqualTo("foo");
|
||||
assertThat(env.getProfiles()).isEqualTo(new String[] { "bar" });
|
||||
assertThat(env.getLabel()).isEqualTo("master");
|
||||
assertThat(env.getPropertySources()).isNotEmpty();
|
||||
assertThat(env.getPropertySources().get(0).getName()).isEqualTo("foo-bar");
|
||||
assertThat(env.getPropertySources().get(0).getSource().get("a_b_c")).isEqualTo("foo-bar");
|
||||
assertThat(env.getPropertySources().get(1).getName()).isEqualTo("application-bar");
|
||||
assertThat(env.getPropertySources().get(1).getSource().get("a_b_c")).isEqualTo("application-bar");
|
||||
assertThat(env.getPropertySources().get(2).getName()).isEqualTo("foo");
|
||||
assertThat(env.getPropertySources().get(2).getSource().get("a_b_c")).isEqualTo("foo-null");
|
||||
assertThat(env.getPropertySources().get(3).getName()).isEqualTo("application");
|
||||
assertThat(env.getPropertySources().get(3).getSource().get("a_b_c")).isEqualTo("application-null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultProfile() {
|
||||
MongoDbEnvironmentProperties properties = new MongoDbEnvironmentProperties();
|
||||
Environment env = new MongoDbEnvironmentRepository(mongoTemplate, properties).findOne("foo", "", "");
|
||||
assertThat(env.getName()).isEqualTo("foo");
|
||||
assertThat(env.getProfiles()).isEqualTo(new String[] { "default" });
|
||||
assertThat(env.getLabel()).isEqualTo("master");
|
||||
assertThat(env.getPropertySources()).isNotEmpty();
|
||||
assertThat(env.getPropertySources().get(0).getName()).isEqualTo("foo-default");
|
||||
assertThat(env.getPropertySources().get(0).getSource().get("a_b_c")).isEqualTo("foo-default");
|
||||
assertThat(env.getPropertySources().get(1).getName()).isEqualTo("application-default");
|
||||
assertThat(env.getPropertySources().get(1).getSource().get("a_b_c")).isEqualTo("application-default");
|
||||
assertThat(env.getPropertySources().get(2).getName()).isEqualTo("foo");
|
||||
assertThat(env.getPropertySources().get(2).getSource().get("a_b_c")).isEqualTo("foo-null");
|
||||
assertThat(env.getPropertySources().get(3).getName()).isEqualTo("application");
|
||||
assertThat(env.getPropertySources().get(3).getSource().get("a_b_c")).isEqualTo("application-null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProfileNotExist() {
|
||||
MongoDbEnvironmentProperties properties = new MongoDbEnvironmentProperties();
|
||||
Environment env = new MongoDbEnvironmentRepository(mongoTemplate, properties).findOne("foo", "not_exist", "");
|
||||
assertThat(env.getName()).isEqualTo("foo");
|
||||
assertThat(env.getProfiles()).isEqualTo(new String[] { "not_exist" });
|
||||
assertThat(env.getLabel()).isEqualTo("master");
|
||||
assertThat(env.getPropertySources()).isNotEmpty();
|
||||
assertThat(env.getPropertySources().get(0).getName()).isEqualTo("foo");
|
||||
assertThat(env.getPropertySources().get(0).getSource().get("a_b_c")).isEqualTo("foo-null");
|
||||
assertThat(env.getPropertySources().get(1).getName()).isEqualTo("application");
|
||||
assertThat(env.getPropertySources().get(1).getSource().get("a_b_c")).isEqualTo("application-null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplicationNotExist() {
|
||||
MongoDbEnvironmentProperties properties = new MongoDbEnvironmentProperties();
|
||||
Environment env = new MongoDbEnvironmentRepository(mongoTemplate, properties).findOne("not_exist", "bar", "");
|
||||
assertThat(env.getName()).isEqualTo("not_exist");
|
||||
assertThat(env.getProfiles()).isEqualTo(new String[] { "bar" });
|
||||
assertThat(env.getLabel()).isEqualTo("master");
|
||||
assertThat(env.getPropertySources()).isNotEmpty();
|
||||
assertThat(env.getPropertySources().get(0).getName()).isEqualTo("application-bar");
|
||||
assertThat(env.getPropertySources().get(0).getSource().get("a_b_c")).isEqualTo("application-bar");
|
||||
assertThat(env.getPropertySources().get(1).getName()).isEqualTo("application");
|
||||
assertThat(env.getPropertySources().get(1).getSource().get("a_b_c")).isEqualTo("application-null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplicationProfileBothNotExist() {
|
||||
MongoDbEnvironmentProperties properties = new MongoDbEnvironmentProperties();
|
||||
Environment env = new MongoDbEnvironmentRepository(mongoTemplate, properties).findOne("not_exist", "not_exist",
|
||||
"");
|
||||
assertThat(env.getName()).isEqualTo("not_exist");
|
||||
assertThat(env.getProfiles()).isEqualTo(new String[] { "not_exist" });
|
||||
assertThat(env.getLabel()).isEqualTo("master");
|
||||
assertThat(env.getPropertySources()).isNotEmpty();
|
||||
assertThat(env.getPropertySources().get(0).getName()).isEqualTo("application");
|
||||
assertThat(env.getPropertySources().get(0).getSource().get("a_b_c")).isEqualTo("application-null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomLabel() {
|
||||
MongoDbEnvironmentProperties properties = new MongoDbEnvironmentProperties();
|
||||
properties.setDefaultLabel("main");
|
||||
Environment env = new MongoDbEnvironmentRepository(mongoTemplate, properties).findOne("foo", "bar", "");
|
||||
assertThat(env.getName()).isEqualTo("foo");
|
||||
assertThat(env.getProfiles()).isEqualTo(new String[] { "bar" });
|
||||
assertThat(env.getLabel()).isEqualTo("main");
|
||||
assertThat(env.getPropertySources()).isNotEmpty();
|
||||
assertThat(env.getPropertySources().get(0).getName()).isEqualTo("foo-bar");
|
||||
assertThat(env.getPropertySources().get(0).getSource().get("a_b_c")).isEqualTo("foo-bar");
|
||||
assertThat(env.getPropertySources().get(1).getName()).isEqualTo("application-bar");
|
||||
assertThat(env.getPropertySources().get(1).getSource().get("a_b_c")).isEqualTo("application-bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailOnError() {
|
||||
MongoTemplate failingMongoTemplate = Mockito.spy(mongoTemplate);
|
||||
Mockito.doThrow(new MongoTimeoutException("Timed out after 30000 ms while waiting for a server."))
|
||||
.when(failingMongoTemplate).find(any(Query.class), any(), anyString());
|
||||
MongoDbEnvironmentRepository repository = new MongoDbEnvironmentRepository(failingMongoTemplate,
|
||||
new MongoDbEnvironmentProperties());
|
||||
assertThatThrownBy(() -> repository.findOne("foo", "bar", "")).isInstanceOf(MongoException.class)
|
||||
.hasMessageContaining("Timed out after 30000 ms while waiting for a server.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
[
|
||||
{
|
||||
"application": "foo",
|
||||
"profile": "bar",
|
||||
"label": "master",
|
||||
"properties": {
|
||||
"a_b_c": "foo-bar"
|
||||
}
|
||||
},
|
||||
{
|
||||
"application": "foo",
|
||||
"profile": "default",
|
||||
"label": "master",
|
||||
"properties": {
|
||||
"a_b_c": "foo-default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"application": "foo",
|
||||
"profile": null,
|
||||
"label": "master",
|
||||
"properties": {
|
||||
"a_b_c": "foo-null"
|
||||
}
|
||||
},
|
||||
{
|
||||
"application": "application",
|
||||
"profile": "bar",
|
||||
"label": "master",
|
||||
"properties": {
|
||||
"a_b_c": "application-bar"
|
||||
}
|
||||
},
|
||||
{
|
||||
"application": "application",
|
||||
"profile": "default",
|
||||
"label": "master",
|
||||
"properties": {
|
||||
"a_b_c": "application-default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"application": "application",
|
||||
"profile": null,
|
||||
"label": "master",
|
||||
"properties": {
|
||||
"a_b_c": "application-null"
|
||||
}
|
||||
},
|
||||
{
|
||||
"application": "foo",
|
||||
"profile": "bar",
|
||||
"label": "main",
|
||||
"properties": {
|
||||
"a_b_c": "foo-bar"
|
||||
}
|
||||
},
|
||||
{
|
||||
"application": "application",
|
||||
"profile": "bar",
|
||||
"label": "main",
|
||||
"properties": {
|
||||
"a_b_c": "application-bar"
|
||||
}
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user