diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/CompositeConfiguration.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/CompositeConfiguration.java index bd1aa753..d34968b2 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/CompositeConfiguration.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/CompositeConfiguration.java @@ -39,18 +39,22 @@ public class CompositeConfiguration { private List environmentRepos = new ArrayList<>(); + private ConfigServerProperties properties; + @Bean @Primary @ConditionalOnBean(SearchPathLocator.class) public SearchPathCompositeEnvironmentRepository searchPathCompositeEnvironmentRepository() { - return new SearchPathCompositeEnvironmentRepository(this.environmentRepos); + return new SearchPathCompositeEnvironmentRepository(this.environmentRepos, + properties.isFailOnCompositeError()); } @Bean @Primary @ConditionalOnMissingBean(SearchPathLocator.class) public CompositeEnvironmentRepository compositeEnvironmentRepository() { - return new CompositeEnvironmentRepository(this.environmentRepos); + return new CompositeEnvironmentRepository(this.environmentRepos, + properties.isFailOnCompositeError()); } @Autowired @@ -58,4 +62,9 @@ public class CompositeConfiguration { this.environmentRepos = repos; } + @Autowired + public void setProperties(ConfigServerProperties properties) { + this.properties = properties; + } + } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerProperties.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerProperties.java index 309f263d..90b5dc24 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerProperties.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerProperties.java @@ -73,6 +73,17 @@ public class ConfigServerProperties { */ private String defaultProfile = "default"; + /** + * Flag indicating that if there are any errors reading properties from a subordinate + * environment repository in a composite environment repository, then the entire + * composite read should fail. Useful when set to false when a Vault repository is in + * the composite to allow clients to still read properties from other repositories + * without providing a valid Vault token. + * + * Defaults to true, resulting in a failure on any error. + */ + private boolean failOnCompositeError = true; + /** * Decryption configuration for when server handles encrypted properties before * sending them to clients. @@ -147,6 +158,14 @@ public class ConfigServerProperties { this.defaultProfile = defaultProfile; } + public boolean isFailOnCompositeError() { + return failOnCompositeError; + } + + public void setFailOnCompositeError(boolean failOnCompositeError) { + this.failOnCompositeError = failOnCompositeError; + } + /** * Encryption properties. */ diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EnvironmentRepositoryConfiguration.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EnvironmentRepositoryConfiguration.java index e3f0d70e..8ea0f295 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EnvironmentRepositoryConfiguration.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EnvironmentRepositoryConfiguration.java @@ -429,16 +429,20 @@ class CompositeRepositoryConfiguration { @Bean @ConditionalOnSearchPathLocator public SearchPathCompositeEnvironmentRepository searchPathCompositeEnvironmentRepository( - List environmentRepositories) { - return new SearchPathCompositeEnvironmentRepository(environmentRepositories); + List environmentRepositories, + ConfigServerProperties properties) { + return new SearchPathCompositeEnvironmentRepository(environmentRepositories, + properties.isFailOnCompositeError()); } @Primary @Bean @ConditionalOnMissingSearchPathLocator public CompositeEnvironmentRepository compositeEnvironmentRepository( - List environmentRepositories) { - return new CompositeEnvironmentRepository(environmentRepositories); + List environmentRepositories, + ConfigServerProperties properties) { + return new CompositeEnvironmentRepository(environmentRepositories, + properties.isFailOnCompositeError()); } } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/CompositeEnvironmentRepository.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/CompositeEnvironmentRepository.java index 1fb0b62d..b3bf8f07 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/CompositeEnvironmentRepository.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/CompositeEnvironmentRepository.java @@ -19,6 +19,9 @@ package org.springframework.cloud.config.server.environment; import java.util.Collections; import java.util.List; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.springframework.cloud.config.environment.Environment; import org.springframework.core.OrderComparator; @@ -30,17 +33,23 @@ import org.springframework.core.OrderComparator; */ public class CompositeEnvironmentRepository implements EnvironmentRepository { + Log log = LogFactory.getLog(getClass()); + protected List environmentRepositories; + private boolean failOnError; + /** * Creates a new {@link CompositeEnvironmentRepository}. * @param environmentRepositories The list of {@link EnvironmentRepository}s to create * the composite from. + * @param failOnError whether to throw an exception if there is an error. */ - public CompositeEnvironmentRepository(List environmentRepositories) { + public CompositeEnvironmentRepository(List environmentRepositories, boolean failOnError) { // Sort the environment repositories by the priority Collections.sort(environmentRepositories, OrderComparator.INSTANCE); this.environmentRepositories = environmentRepositories; + this.failOnError = failOnError; } @Override @@ -61,6 +70,15 @@ public class CompositeEnvironmentRepository implements EnvironmentRepository { else { for (EnvironmentRepository repo : environmentRepositories) { env.addAll(repo.findOne(application, profile, label, includeOrigin).getPropertySources()); + } + catch (Exception e) { + if (failOnError) { + throw e; + } + else { + log.info("Error adding environment for " + repo); + } + } } } return env; diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JdbcEnvironmentProperties.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JdbcEnvironmentProperties.java index 61e34459..b5782f1f 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JdbcEnvironmentProperties.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JdbcEnvironmentProperties.java @@ -40,6 +40,11 @@ public class JdbcEnvironmentProperties implements EnvironmentRepositoryPropertie /** SQL used to query database for keys and values. */ private String sql = DEFAULT_SQL; + /** + * Flag to determine how to handle query exceptions. + */ + private boolean failOnError = true; + public boolean isEnabled() { return enabled; } @@ -65,4 +70,12 @@ public class JdbcEnvironmentProperties implements EnvironmentRepositoryPropertie this.sql = sql; } + public boolean isFailOnError() { + return failOnError; + } + + public void setFailOnError(boolean failOnError) { + this.failOnError = failOnError; + } + } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JdbcEnvironmentRepository.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JdbcEnvironmentRepository.java index 061811c7..7c01171f 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JdbcEnvironmentRepository.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JdbcEnvironmentRepository.java @@ -27,6 +27,9 @@ import java.util.List; import java.util.Map; import java.util.Properties; +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; @@ -50,6 +53,8 @@ import org.springframework.util.StringUtils; */ public class JdbcEnvironmentRepository implements EnvironmentRepository, Ordered { + private static Log logger = LogFactory.getLog(JdbcEnvironmentRepository.class); + private final JdbcTemplate jdbc; private final PropertiesResultSetExtractor extractor = new PropertiesResultSetExtractor(); @@ -58,10 +63,13 @@ public class JdbcEnvironmentRepository implements EnvironmentRepository, Ordered private String sql; + private boolean failOnError; + public JdbcEnvironmentRepository(JdbcTemplate jdbc, JdbcEnvironmentProperties properties) { this.jdbc = jdbc; this.order = properties.getOrder(); this.sql = properties.getSql(); + this.failOnError = properties.isFailOnError(); } public String getSql() { @@ -96,10 +104,24 @@ public class JdbcEnvironmentRepository implements EnvironmentRepository, Ordered Collections.reverse(envs); for (String app : applications) { for (String env : envs) { - Map next = (Map) this.jdbc.query(this.sql, - new Object[] { app, env, label }, this.extractor); - if (!next.isEmpty()) { - environment.add(new PropertySource(app + "-" + env, next)); + try { + Map next = (Map) this.jdbc.query( + this.sql, new Object[] { app, env, label }, this.extractor); + if (!next.isEmpty()) { + environment.add(new PropertySource(app + "-" + env, next)); + } + } + catch (DataAccessException e) { + if (!failOnError) { + if (logger.isDebugEnabled()) { + logger.debug( + "Failed to retrieve configuration from JDBC Repository", + e); + } + } + else { + throw e; + } } } } @@ -115,6 +137,14 @@ public class JdbcEnvironmentRepository implements EnvironmentRepository, Ordered this.order = order; } + public boolean isFailOnError() { + return failOnError; + } + + public void setFailOnError(boolean failOnError) { + this.failOnError = failOnError; + } + } class PropertiesResultSetExtractor implements ResultSetExtractor> { diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/SearchPathCompositeEnvironmentRepository.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/SearchPathCompositeEnvironmentRepository.java index 84617686..5ac29967 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/SearchPathCompositeEnvironmentRepository.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/SearchPathCompositeEnvironmentRepository.java @@ -32,9 +32,10 @@ public class SearchPathCompositeEnvironmentRepository extends CompositeEnvironme * Creates a new {@link SearchPathCompositeEnvironmentRepository}. * @param environmentRepositories The {@link EnvironmentRepository}s to create this * composite from. + * @param failOnError whether to throw an exception if there is an error. */ - public SearchPathCompositeEnvironmentRepository(List environmentRepositories) { - super(environmentRepositories); + public SearchPathCompositeEnvironmentRepository(List environmentRepositories, boolean failOnError) { + super(environmentRepositories, failOnError); } @Override diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/CompositeEnvironmentRepositoryTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/CompositeEnvironmentRepositoryTests.java index e0ee7268..52a16162 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/CompositeEnvironmentRepositoryTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/CompositeEnvironmentRepositoryTests.java @@ -76,7 +76,7 @@ public class CompositeEnvironmentRepositoryTests { repos.add(new TestOrderedEnvironmentRepository(3, e1, loc1)); repos.add(new TestOrderedEnvironmentRepository(2, e3, loc2)); repos.add(new TestOrderedEnvironmentRepository(1, e2, loc3)); - SearchPathCompositeEnvironmentRepository compositeRepo = new SearchPathCompositeEnvironmentRepository(repos); + SearchPathCompositeEnvironmentRepository compositeRepo = new SearchPathCompositeEnvironmentRepository(repos, true); Environment compositeEnv = compositeRepo.findOne("foo", "bar", "world", false); List propertySources = compositeEnv.getPropertySources(); assertThat(propertySources.size()).isEqualTo(5); @@ -121,16 +121,15 @@ public class CompositeEnvironmentRepositoryTests { List repos2 = new ArrayList(); repos2.add(new TestOrderedEnvironmentRepository(3, e1, loc1)); repos2.add(new TestOrderedEnvironmentRepository(3, e2, loc2)); - SearchPathCompositeEnvironmentRepository compositeRepo = new SearchPathCompositeEnvironmentRepository(repos); + SearchPathCompositeEnvironmentRepository compositeRepo = new SearchPathCompositeEnvironmentRepository(repos, true); SearchPathCompositeEnvironmentRepository multiCompositeRepo = new SearchPathCompositeEnvironmentRepository( - repos2); + repos2, true); Environment env = compositeRepo.findOne("app", "dev", "label", false); assertThat(env.getVersion()).isEqualTo("1"); assertThat(env.getState()).isEqualTo("state"); Environment multiEnv = multiCompositeRepo.findOne("app", "dev", "label", false); assertThat(multiEnv.getVersion()).isEqualTo(null); assertThat(multiEnv.getState()).isEqualTo(null); - } @Test @@ -142,6 +141,38 @@ public class CompositeEnvironmentRepositoryTests { } } + @Test + public void testFailingSubordinateRepositorySkipped() { + PropertySource p1 = mock(PropertySource.class); + doReturn("p1").when(p1).getName(); + PropertySource p2 = mock(PropertySource.class); + doReturn("p2").when(p2).getName(); + String sLoc1 = "loc1"; + String sLoc2 = "loc2"; + Environment e1 = new Environment("app", "dev"); + e1.add(p1); + e1.setVersion("1"); + e1.setState("state"); + Environment e2 = new Environment("app", "dev"); + e2.add(p2); + e2.setVersion("2"); + e2.setState("state2"); + SearchPathLocator.Locations loc1 = new SearchPathLocator.Locations("app", "dev", + "label", "version", new String[] { sLoc1 }); + SearchPathLocator.Locations loc2 = new SearchPathLocator.Locations("app", "dev", + "label", "version", new String[] { sLoc1, sLoc2 }); + List repos = new ArrayList(); + repos.add(new TestOrderedEnvironmentRepository(2, e1, loc1)); + repos.add(new TestFailingEnvironmentRepository(1, e2, loc2)); + + SearchPathCompositeEnvironmentRepository compositeRepo = new SearchPathCompositeEnvironmentRepository( + repos, false); + Environment env = compositeRepo.findOne("app", "dev", "label", false); + List propertySources = env.getPropertySources(); + assertThat(propertySources.size()).isEqualTo(1); + assertThat(propertySources.get(0).getName()).isEqualTo("p1"); + } + private static class TestOrderedEnvironmentRepository implements EnvironmentRepository, SearchPathLocator, Ordered { private Environment env; @@ -178,6 +209,27 @@ public class CompositeEnvironmentRepositoryTests { } + private static class TestFailingEnvironmentRepository + extends TestOrderedEnvironmentRepository { + + TestFailingEnvironmentRepository(int order, Environment env, + Locations locations) { + super(order, env, locations); + } + + @Override + public Environment findOne(String application, String profile, String label, + boolean includeOrigin) { + throw new IllegalArgumentException("Failing for some reason"); + } + + @Override + public Environment findOne(String application, String profile, String label) { + throw new IllegalArgumentException("Failing for some reason"); + } + + } + @Configuration(proxyBeanMethods = false) static class OverrideCompositeConfig { @@ -185,7 +237,7 @@ public class CompositeEnvironmentRepositoryTests { @Primary CompositeEnvironmentRepository customCompositeEnvironmentRepository() { return new CompositeEnvironmentRepository(Arrays.asList( - new TestOrderedEnvironmentRepository(1, new Environment("app", "dev"), null))); + new TestOrderedEnvironmentRepository(1, new Environment("app", "dev"), null)), true); } }