Initial commit

This commit is contained in:
Marcin Grzejszczak
2023-09-08 16:55:47 +02:00
parent d89786c54f
commit d00a374ca5
835 changed files with 1764 additions and 58548 deletions

View File

@@ -0,0 +1,185 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config</artifactId>
<version>4.0.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
<version>4.0.0-SNAPSHOT</version>
<name>Spring Cloud Config Client</name>
<description>This project is a Spring configuration client.</description>
<url>https://spring.io</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>https://www.spring.io</url>
</organization>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
<comments>Copyright 2014-2021 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.</comments>
</license>
</licenses>
<developers>
<developer>
<id>dsyer</id>
<name>Dave Syer</name>
<email>dsyer at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>lead</role>
</roles>
</developer>
<developer>
<id>sgibb</id>
<name>Spencer Gibb</name>
<email>sgibb at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>lead</role>
</roles>
</developer>
<developer>
<id>mgrzejszczak</id>
<name>Marcin Grzejszczak</name>
<email>mgrzejszczak at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>developer</role>
</roles>
</developer>
<developer>
<id>rbaxter</id>
<name>Ryan Baxter</name>
<email>rbaxter at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>developer</role>
</roles>
</developer>
<developer>
<id>omaciaszeksharma</id>
<name>Olga Maciaszek-Sharma</name>
<email>omaciaszeksharma at pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>https://www.spring.io</organizationUrl>
<roles>
<role>developer</role>
</roles>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-config.git/spring-cloud-config-client</connection>
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-config.git/spring-cloud-config-client</developerConnection>
<url>https://github.com/spring-cloud/spring-cloud-config/spring-cloud-config-client</url>
</scm>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>3.0.0-SNAPSHOT</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>3.0.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>3.0.0-SNAPSHOT</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<version>4.0.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
<version>4.0.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>6.0.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.14.0-rc2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>2.0.0-RC2</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>3.0.0-SNAPSHOT</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>3.0.0-SNAPSHOT</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.0-rc2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.1.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<version>3.0.0-SNAPSHOT</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@@ -1,100 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-config-client</artifactId>
<packaging>jar</packaging>
<name>Spring Cloud Config Client</name>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config</artifactId>
<version>4.1.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<url>https://spring.io</url>
<description>
<![CDATA[
This project is a Spring configuration client.
]]>
</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-test-support</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -1,121 +0,0 @@
/*
* Copyright 2013-2022 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.client;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.config.ConfigDataLocation;
import org.springframework.cloud.config.environment.PropertySource;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.util.ClassUtils;
/**
* Expose a ConfigClientProperties just so that there is a way to inspect the properties
* bound to it. It won't be available in time for autowiring into the bootstrap context,
* but the values in this properties object will be the same as the ones used to bind to
* the config server, if there is one.
*
* @author Dave Syer
* @author Marcos Barbero
*
*/
@Configuration(proxyBeanMethods = false)
public class ConfigClientAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public ConfigClientProperties configClientProperties(Environment environment, ApplicationContext context) {
if (context.getParent() != null && BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context.getParent(),
ConfigClientProperties.class).length > 0) {
return BeanFactoryUtils.beanOfTypeIncludingAncestors(context.getParent(), ConfigClientProperties.class);
}
ConfigClientProperties client = new ConfigClientProperties(environment);
return client;
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HealthIndicator.class)
@ConditionalOnEnabledHealthIndicator("config")
protected static class ConfigServerHealthIndicatorConfiguration {
@Bean
public ConfigClientHealthProperties configClientHealthProperties() {
return new ConfigClientHealthProperties();
}
@Bean
public ConfigServerHealthIndicator clientConfigServerHealthIndicator(ConfigClientHealthProperties properties,
ConfigurableEnvironment environment) {
return new ConfigServerHealthIndicator(environment, properties);
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ContextRefresher.class)
@ConditionalOnBean(ContextRefresher.class)
@ConditionalOnProperty("spring.cloud.config.watch.enabled")
protected static class ConfigClientWatchConfiguration {
@Bean
public ConfigClientWatch configClientWatch(ContextRefresher contextRefresher) {
return new ConfigClientWatch(contextRefresher);
}
}
}
class ConfigClientHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
if (!ClassUtils.isPresent("org.springframework.cloud.config.client.ConfigServerConfigDataLoader",
classLoader)) {
return;
}
hints.reflection()
.registerType(TypeReference.of(ConfigClientAutoConfiguration.class),
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
.registerType(TypeReference.of(ConfigDataLocation.class),
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS))
.registerType(TypeReference.of("org.springframework.boot.context.config.ConfigDataProperties"),
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.DECLARED_FIELDS, MemberCategory.INTROSPECT_DECLARED_METHODS))
.registerType(TypeReference.of(org.springframework.cloud.config.environment.Environment.class),
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INTROSPECT_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS))
.registerType(TypeReference.of(PropertySource.class),
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INTROSPECT_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS));
}
}

View File

@@ -1,25 +0,0 @@
/*
* Copyright 2013-2021 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.client;
public class ConfigClientFailFastException extends IllegalStateException {
public ConfigClientFailFastException(String message, Exception error) {
super(message, error);
}
}

View File

@@ -1,45 +0,0 @@
/*
* Copyright 2013-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.client;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;
/**
* @author Spencer Gibb
*/
@ConfigurationProperties("health.config")
public class ConfigClientHealthProperties {
/**
* Time to live for cached result. Default 5 min.
*/
@DurationUnit(ChronoUnit.MINUTES)
private Duration timeToLive = Duration.ofMinutes(5);
public Duration getTimeToLive() {
return this.timeToLive;
}
public void setTimeToLive(Duration timeToLive) {
this.timeToLive = timeToLive;
}
}

View File

@@ -1,499 +0,0 @@
/*
* Copyright 2013-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.client;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.config.environment.EnvironmentMediaType;
import org.springframework.cloud.configuration.TlsProperties;
import org.springframework.core.env.Environment;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponentsBuilder;
/**
* @author Dave Syer
*
*/
@ConfigurationProperties(ConfigClientProperties.PREFIX)
public class ConfigClientProperties {
/**
* Prefix for Spring Cloud Config properties.
*/
public static final String PREFIX = "spring.cloud.config";
/**
* Default application name.
*/
public static final String DEFAULT_APPLICATION = "application";
/**
* Placeholder string that allows ${spring.cloud.config.name} to override
* ${spring.application.name:application}.
*/
public static final String NAME_PLACEHOLDER = "${" + ConfigClientProperties.PREFIX
+ ".name:${spring.application.name:" + DEFAULT_APPLICATION + "}}";
/**
* Name of config discovery enabled property.
*/
public static final String CONFIG_DISCOVERY_ENABLED = PREFIX + ".discovery.enabled";
/**
* Token header name.
*/
public static final String TOKEN_HEADER = "X-Config-Token";
/**
* State header name.
*/
public static final String STATE_HEADER = "X-Config-State";
/**
* Authorization header name.
*/
public static final String AUTHORIZATION = "authorization";
/**
* Default profile value.
*/
public static final String DEFAULT_PROFILE = "default";
/**
* Flag to say that remote configuration is enabled. Default true;
*/
private boolean enabled = true;
/**
* The default profile to use when fetching remote configuration (comma-separated).
* Default is "default".
*/
private String profile = DEFAULT_PROFILE;
/**
* Name of application used to fetch remote properties.
*/
@Value("${spring.application.name:" + DEFAULT_APPLICATION + "}")
private String name;
/**
* The label name to use to pull remote configuration properties. The default is set
* on the server (generally "main" for a git based server).
*/
private String label;
/**
* The username to use (HTTP Basic) when contacting the remote server.
*/
private String username;
/**
* The password to use (HTTP Basic) when contacting the remote server.
*/
private String password;
/**
* The URI of the remote server (default http://localhost:8888).
*/
private String[] uri = { "http://localhost:8888" };
/**
* The strategy to use when call to server fails and there are multiple URLs
* configured on the uri property (default {@link MultipleUriStrategy#ALWAYS}).
*/
private MultipleUriStrategy multipleUriStrategy = MultipleUriStrategy.ALWAYS;
/**
* The Accept header media type to send to config server.
*/
private String mediaType = EnvironmentMediaType.V2_JSON;
/**
* Discovery properties.
*/
private Discovery discovery = new Discovery();
/**
* TLS properties.
*/
private TlsProperties tls = new TlsProperties();
/**
* Flag to indicate that failure to connect to the server is fatal (default false).
*/
private boolean failFast = false;
/**
* Security Token passed thru to underlying environment repository.
*/
private String token;
/**
* timeout on waiting to read data from the Config Server.
*/
private int requestReadTimeout = (60 * 1000 * 3) + 5000;
/**
* timeout on waiting to connect to the Config Server.
*/
private int requestConnectTimeout = 1000 * 10;
/**
* Flag to indicate whether to send state. Default true.
*/
private boolean sendState = true;
/**
* Additional headers used to create the client request.
*/
private Map<String, String> headers = new HashMap<>();
ConfigClientProperties() {
}
public ConfigClientProperties(Environment environment) {
String[] profiles = environment.getActiveProfiles();
if (profiles.length == 0) {
profiles = environment.getDefaultProfiles();
}
this.setProfile(StringUtils.arrayToCommaDelimitedString(profiles));
}
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String[] getUri() {
return this.uri;
}
public void setUri(String[] url) {
this.uri = url;
}
public MultipleUriStrategy getMultipleUriStrategy() {
return multipleUriStrategy;
}
public void setMultipleUriStrategy(MultipleUriStrategy multipleUriStrategy) {
this.multipleUriStrategy = multipleUriStrategy;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getProfile() {
return this.profile;
}
public void setProfile(String env) {
this.profile = env;
}
public String getLabel() {
return this.label;
}
public void setLabel(String label) {
this.label = label;
}
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public Credentials getCredentials(int index) {
return extractCredentials(index);
}
public String getMediaType() {
return this.mediaType;
}
public void setMediaType(String mediaType) {
this.mediaType = mediaType;
}
public Discovery getDiscovery() {
return this.discovery;
}
public void setDiscovery(Discovery discovery) {
this.discovery = discovery;
}
public TlsProperties getTls() {
return tls;
}
public void setTls(TlsProperties tls) {
this.tls = tls;
}
public boolean isFailFast() {
return this.failFast;
}
public void setFailFast(boolean failFast) {
this.failFast = failFast;
}
public String getToken() {
return this.token;
}
public void setToken(String token) {
this.token = token;
}
public int getRequestReadTimeout() {
return this.requestReadTimeout;
}
public void setRequestReadTimeout(int requestReadTimeout) {
this.requestReadTimeout = requestReadTimeout;
}
public int getRequestConnectTimeout() {
return this.requestConnectTimeout;
}
public void setRequestConnectTimeout(int requestConnectTimeout) {
this.requestConnectTimeout = requestConnectTimeout;
}
public boolean isSendState() {
return this.sendState;
}
public void setSendState(boolean sendState) {
this.sendState = sendState;
}
public Map<String, String> getHeaders() {
return this.headers;
}
public void setHeaders(Map<String, String> headers) {
this.headers = headers;
}
private Credentials extractCredentials(int index) {
Credentials result = new Credentials();
int noOfUrl = this.uri.length;
if (index < 0 || index >= noOfUrl) {
throw new IllegalStateException("Trying to access an invalid array index");
}
String uri = this.uri[index];
result.uri = uri;
Credentials explicitCredentials = getUsernamePassword();
result.username = explicitCredentials.username;
result.password = explicitCredentials.password;
try {
URL url = new URL(uri);
String userInfo = url.getUserInfo();
// no credentials in url, return explicit credentials
if (ObjectUtils.isEmpty(userInfo) || ":".equals(userInfo)) {
return result;
}
String bare = UriComponentsBuilder.fromHttpUrl(uri).userInfo(null).build().toUriString();
result.uri = bare;
// if userInfo does not contain a :, then append a : to it
if (!userInfo.contains(":")) {
userInfo = userInfo + ":";
}
int sepIndex = userInfo.indexOf(":");
// set username and password from uri
result.username = userInfo.substring(0, sepIndex);
result.password = userInfo.substring(sepIndex + 1);
// override password if explicitly set
if (explicitCredentials.password != null) {
// Explicit username / password takes precedence
result.password = explicitCredentials.password;
}
// override username if explicitly set
if (!"user".equals(explicitCredentials.username)) {
// But the username can be overridden
result.username = explicitCredentials.username;
}
result.password = URLDecoder.decode(result.password, StandardCharsets.UTF_8.toString());
result.username = URLDecoder.decode(result.username, StandardCharsets.UTF_8.toString());
return result;
}
catch (MalformedURLException | UnsupportedEncodingException e) {
throw new IllegalStateException("Invalid URL: " + uri, e);
}
}
private Credentials getUsernamePassword() {
Credentials credentials = new Credentials();
if (StringUtils.hasText(this.password)) {
credentials.password = this.password.trim();
}
if (StringUtils.hasText(this.username)) {
credentials.username = this.username.trim();
}
else {
credentials.username = "user";
}
return credentials;
}
public ConfigClientProperties override(org.springframework.core.env.Environment environment) {
ConfigClientProperties override = new ConfigClientProperties();
BeanUtils.copyProperties(this, override);
override.setName(environment.resolvePlaceholders(NAME_PLACEHOLDER));
if (environment.containsProperty(ConfigClientProperties.PREFIX + ".profile")) {
override.setProfile(environment.getProperty(ConfigClientProperties.PREFIX + ".profile"));
}
if (environment.containsProperty(ConfigClientProperties.PREFIX + ".label")) {
override.setLabel(environment.getProperty(ConfigClientProperties.PREFIX + ".label"));
}
return override;
}
@Override
public String toString() {
return "ConfigClientProperties [enabled=" + this.enabled + ", profile=" + this.profile + ", name=" + this.name
+ ", label=" + this.label + ", username=" + this.username + ", password=" + this.password + ", uri="
+ Arrays.toString(this.uri) + ", mediaType=" + this.mediaType + ", discovery=" + this.discovery
+ ", failFast=" + this.failFast + ", token=" + this.token + ", requestConnectTimeout="
+ this.requestConnectTimeout + ", requestReadTimeout=" + this.requestReadTimeout + ", sendState="
+ this.sendState + ", headers=" + this.headers + "]";
}
/**
* Credentials properties.
*/
public static class Credentials {
private String username;
private String password;
private String uri;
public String getUsername() {
return this.username;
}
public String getPassword() {
return this.password;
}
public String getUri() {
return this.uri;
}
}
/**
* Discovery properties.
*/
public static class Discovery {
/**
* Default config server service id name.
*/
public static final String DEFAULT_CONFIG_SERVER = "configserver";
/**
* Flag to indicate that config server discovery is enabled (config server URL
* will be looked up via discovery).
*/
private boolean enabled;
/**
* Service id to locate config server.
*/
private String serviceId = DEFAULT_CONFIG_SERVER;
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getServiceId() {
return this.serviceId;
}
public void setServiceId(String serviceId) {
this.serviceId = serviceId;
}
}
/**
* Enumerates possible strategies to use when multiple URLs are provided and an error
* occurs.
*/
public enum MultipleUriStrategy {
/**
* Try the next URL in the list on any error.
*/
ALWAYS,
/**
* Try the next URL in the list only if no response was received.
*/
CONNECTION_TIMEOUT_ONLY
}
}

View File

@@ -1,175 +0,0 @@
/*
* Copyright 2013-2021 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.client;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import org.apache.commons.logging.Log;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder;
import org.apache.hc.core5.http.io.SocketConfig;
import org.apache.hc.core5.util.Timeout;
import org.springframework.cloud.configuration.SSLContextFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.Base64Utils;
import org.springframework.web.client.RestTemplate;
import static org.springframework.cloud.config.client.ConfigClientProperties.AUTHORIZATION;
public class ConfigClientRequestTemplateFactory {
private final Log log;
private final ConfigClientProperties properties;
public ConfigClientRequestTemplateFactory(Log log, ConfigClientProperties properties) {
this.log = log;
this.properties = properties;
}
public Log getLog() {
return this.log;
}
public ConfigClientProperties getProperties() {
return this.properties;
}
public RestTemplate create() {
if (properties.getRequestReadTimeout() < 0) {
throw new IllegalStateException("Invalid Value for Read Timeout set.");
}
if (properties.getRequestConnectTimeout() < 0) {
throw new IllegalStateException("Invalid Value for Connect Timeout set.");
}
ClientHttpRequestFactory requestFactory = createHttpRequestFactory(properties);
RestTemplate template = new RestTemplate(requestFactory);
Map<String, String> headers = new HashMap<>(properties.getHeaders());
headers.remove(AUTHORIZATION); // To avoid redundant addition of header
if (!headers.isEmpty()) {
template.setInterceptors(Arrays.asList(new GenericRequestHeaderInterceptor(headers)));
}
return template;
}
protected ClientHttpRequestFactory createHttpRequestFactory(ConfigClientProperties client) {
if (client.getTls().isEnabled()) {
try {
PoolingHttpClientConnectionManager connectionManager = createConnectionManagerForTls(client);
HttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();
HttpComponentsClientHttpRequestFactory result = new HttpComponentsClientHttpRequestFactory(httpClient);
result.setConnectTimeout(client.getRequestConnectTimeout());
return result;
}
catch (GeneralSecurityException | IOException ex) {
log.error(ex);
throw new IllegalStateException("Failed to create config client with TLS.", ex);
}
}
SimpleClientHttpRequestFactory result = new SimpleClientHttpRequestFactory();
result.setReadTimeout(client.getRequestReadTimeout());
result.setConnectTimeout(client.getRequestConnectTimeout());
return result;
}
protected PoolingHttpClientConnectionManager createConnectionManagerForTls(ConfigClientProperties client)
throws GeneralSecurityException, IOException {
SSLContextFactory factory = new SSLContextFactory(client.getTls());
SSLContext sslContext = factory.createSSLContext();
SSLConnectionSocketFactoryBuilder sslConnectionSocketFactoryBuilder = SSLConnectionSocketFactoryBuilder
.create();
sslConnectionSocketFactoryBuilder.setSslContext(sslContext);
SocketConfig.Builder socketBuilder = createSocketBuilderForTls(client);
PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
.setDefaultSocketConfig(socketBuilder.build())
.setSSLSocketFactory(sslConnectionSocketFactoryBuilder.build()).build();
return connectionManager;
}
protected SocketConfig.Builder createSocketBuilderForTls(ConfigClientProperties client) {
SocketConfig.Builder socketBuilder = SocketConfig.custom()
.setSoTimeout(Timeout.of(client.getRequestReadTimeout(), TimeUnit.MILLISECONDS));
return socketBuilder;
}
public void addAuthorizationToken(HttpHeaders httpHeaders, String username, String password) {
String authorization = properties.getHeaders().get(AUTHORIZATION);
if (password != null && authorization != null) {
throw new IllegalStateException("You must set either 'password' or 'authorization'");
}
if (password != null) {
byte[] token = Base64Utils.encode((username + ":" + password).getBytes());
httpHeaders.add("Authorization", "Basic " + new String(token));
}
else if (authorization != null) {
httpHeaders.add("Authorization", authorization);
}
}
/**
* Adds the provided headers to the request.
*/
public static class GenericRequestHeaderInterceptor implements ClientHttpRequestInterceptor {
private final Map<String, String> headers;
public GenericRequestHeaderInterceptor(Map<String, String> headers) {
this.headers = headers;
}
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
for (Map.Entry<String, String> header : this.headers.entrySet()) {
request.getHeaders().add(header.getKey(), header.getValue());
}
return execution.execute(request, body);
}
protected Map<String, String> getHeaders() {
return this.headers;
}
}
}

View File

@@ -1,59 +0,0 @@
/*
* Copyright 2013-2020 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.client;
import org.springframework.boot.BootstrapRegistry;
import org.springframework.boot.BootstrapRegistryInitializer;
import org.springframework.cloud.config.client.ConfigServerBootstrapper.LoaderInterceptor;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.ClassUtils;
/**
* Bootstrapper.
*
* @author Marcin Grzejszczak
* @since 3.0.0
*/
public class ConfigClientRetryBootstrapper implements BootstrapRegistryInitializer {
static final boolean RETRY_IS_PRESENT = ClassUtils.isPresent("org.springframework.retry.annotation.Retryable",
null);
@Override
public void initialize(BootstrapRegistry registry) {
if (!RETRY_IS_PRESENT) {
return;
}
registry.registerIfAbsent(LoaderInterceptor.class, context -> loadContext -> {
ConfigServerConfigDataResource resource = loadContext.getResource();
if (resource.getProperties().isFailFast()) {
RetryProperties properties = resource.getRetryProperties();
RetryTemplate retryTemplate = RetryTemplateFactory.create(properties, resource.getLog());
return retryTemplate.execute(retryContext -> {
if (resource.getLog().isDebugEnabled()) {
resource.getLog().debug("Retry: count=" + retryContext.getRetryCount());
}
return loadContext.getInvocation().apply(loadContext.getLoaderContext(), resource);
});
}
return loadContext.getInvocation().apply(loadContext.getLoaderContext(), resource);
});
}
}

View File

@@ -1,46 +0,0 @@
/*
* 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.client;
/**
* @author Spencer Gibb
*/
public final class ConfigClientStateHolder {
private ConfigClientStateHolder() {
throw new IllegalStateException("Can't instantiate a utility class");
}
private static ThreadLocal<String> state = new ThreadLocal<>();
public static void resetState() {
state.remove();
}
public static String getState() {
return state.get();
}
public static void setState(String newState) {
if (newState == null) {
resetState();
return;
}
state.set(newState);
}
}

View File

@@ -1,84 +0,0 @@
/*
* Copyright 2013-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.client;
import java.io.Closeable;
import java.util.concurrent.atomic.AtomicBoolean;
import jakarta.annotation.PostConstruct;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.Scheduled;
import static org.springframework.util.StringUtils.hasText;
/**
* @author Spencer Gibb
*/
public class ConfigClientWatch implements Closeable, EnvironmentAware {
private static Log log = LogFactory.getLog(ConfigServicePropertySourceLocator.class);
private final AtomicBoolean running = new AtomicBoolean(false);
private final ContextRefresher refresher;
private Environment environment;
public ConfigClientWatch(ContextRefresher refresher) {
this.refresher = refresher;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@PostConstruct
public void start() {
this.running.compareAndSet(false, true);
}
@Scheduled(initialDelayString = "${spring.cloud.config.watch.initialDelay:180000}",
fixedDelayString = "${spring.cloud.config.watch.delay:500}")
public void watchConfigServer() {
if (this.running.get()) {
String newState = this.environment.getProperty("config.client.state");
String oldState = ConfigClientStateHolder.getState();
// only refresh if state has changed
if (stateChanged(oldState, newState)) {
ConfigClientStateHolder.setState(newState);
this.refresher.refresh();
}
}
}
/* for testing */ boolean stateChanged(String oldState, String newState) {
return (!hasText(oldState) && hasText(newState)) || (hasText(oldState) && !oldState.equals(newState));
}
@Override
public void close() {
this.running.compareAndSet(true, false);
}
}

View File

@@ -1,114 +0,0 @@
/*
* Copyright 2013-2020 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.client;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.springframework.boot.BootstrapContext;
import org.springframework.boot.BootstrapRegistry;
import org.springframework.boot.BootstrapRegistry.InstanceSupplier;
import org.springframework.boot.BootstrapRegistryInitializer;
import org.springframework.boot.context.config.ConfigData;
import org.springframework.boot.context.config.ConfigDataLoaderContext;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;
public class ConfigServerBootstrapper implements BootstrapRegistryInitializer {
private Function<BootstrapContext, RestTemplate> restTemplateFactory;
private LoaderInterceptor loaderInterceptor;
static ConfigServerBootstrapper create() {
return new ConfigServerBootstrapper();
}
// TODO: document there will be a ConfigClientProperties in BootstrapContext
public ConfigServerBootstrapper withRestTemplateFactory(
Function<BootstrapContext, RestTemplate> restTemplateFactory) {
this.restTemplateFactory = restTemplateFactory;
return this;
}
public ConfigServerBootstrapper withLoaderInterceptor(LoaderInterceptor loaderInterceptor) {
this.loaderInterceptor = loaderInterceptor;
return this;
}
@Override
public void initialize(BootstrapRegistry registry) {
if (restTemplateFactory != null) {
registry.register(RestTemplate.class, restTemplateFactory::apply);
}
if (loaderInterceptor != null) {
registry.register(LoaderInterceptor.class, InstanceSupplier.of(loaderInterceptor));
}
}
public interface LoaderInterceptor extends Function<LoadContext, ConfigData> {
}
@FunctionalInterface
public interface LoaderInvocation
extends BiFunction<ConfigDataLoaderContext, ConfigServerConfigDataResource, ConfigData> {
}
public static class LoadContext {
private final ConfigDataLoaderContext loaderContext;
private final ConfigServerConfigDataResource resource;
private final Binder binder;
private final LoaderInvocation invocation;
LoadContext(ConfigDataLoaderContext loaderContext, ConfigServerConfigDataResource resource, Binder binder,
LoaderInvocation invocation) {
Assert.notNull(loaderContext, "loaderContext may not be null");
Assert.notNull(resource, "resource may not be null");
Assert.notNull(binder, "binder may not be null");
Assert.notNull(invocation, "invocation may not be null");
this.loaderContext = loaderContext;
this.resource = resource;
this.binder = binder;
this.invocation = invocation;
}
public ConfigDataLoaderContext getLoaderContext() {
return this.loaderContext;
}
public ConfigServerConfigDataResource getResource() {
return this.resource;
}
public Binder getBinder() {
return this.binder;
}
public LoaderInvocation getInvocation() {
return this.invocation;
}
}
}

View File

@@ -1,345 +0,0 @@
/*
* Copyright 2013-2022 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.client;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.springframework.boot.context.config.ConfigData;
import org.springframework.boot.context.config.ConfigData.Option;
import org.springframework.boot.context.config.ConfigDataLoader;
import org.springframework.boot.context.config.ConfigDataLoaderContext;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.boot.origin.Origin;
import org.springframework.boot.origin.OriginTrackedValue;
import org.springframework.cloud.config.client.ConfigClientProperties.MultipleUriStrategy;
import org.springframework.cloud.config.client.ConfigServerBootstrapper.LoadContext;
import org.springframework.cloud.config.client.ConfigServerBootstrapper.LoaderInterceptor;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.core.Ordered;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
import static org.springframework.cloud.config.client.ConfigClientProperties.STATE_HEADER;
import static org.springframework.cloud.config.client.ConfigClientProperties.TOKEN_HEADER;
public class ConfigServerConfigDataLoader implements ConfigDataLoader<ConfigServerConfigDataResource>, Ordered {
/**
* PropertySource name for the config client.
*/
public static final String CONFIG_CLIENT_PROPERTYSOURCE_NAME = "configClient";
private static final EnumSet<Option> ALL_OPTIONS = EnumSet.allOf(Option.class);
protected final Log logger;
public ConfigServerConfigDataLoader(DeferredLogFactory logFactory) {
this.logger = logFactory.getLog(getClass());
}
@Override
public int getOrder() {
return -1;
}
@Override
public ConfigData load(ConfigDataLoaderContext context, ConfigServerConfigDataResource resource) {
if (context.getBootstrapContext().isRegistered(ConfigServerInstanceMonitor.class)) {
// force initialization if needed
context.getBootstrapContext().get(ConfigServerInstanceMonitor.class);
}
if (context.getBootstrapContext().isRegistered(LoaderInterceptor.class)) {
LoaderInterceptor interceptor = context.getBootstrapContext().get(LoaderInterceptor.class);
if (interceptor != null) {
Binder binder = context.getBootstrapContext().get(Binder.class);
try {
return interceptor.apply(new LoadContext(context, resource, binder, this::doLoad));
}
catch (ConfigClientFailFastException e) {
context.getBootstrapContext().addCloseListener(event -> {
throw e;
});
return new ConfigData(Collections.emptyList());
}
}
}
return doLoad(context, resource);
}
public ConfigData doLoad(ConfigDataLoaderContext context, ConfigServerConfigDataResource resource) {
ConfigClientProperties properties = resource.getProperties();
List<PropertySource<?>> propertySources = new ArrayList<>();
Exception error = null;
String errorBody = null;
try {
String[] labels = new String[] { "" };
if (StringUtils.hasText(properties.getLabel())) {
labels = StringUtils.commaDelimitedListToStringArray(properties.getLabel());
}
String state = ConfigClientStateHolder.getState();
// Try all the labels until one works
for (String label : labels) {
Environment result = getRemoteEnvironment(context, resource, label.trim(), state);
if (result != null) {
log(result);
// result.getPropertySources() can be null if using xml
if (result.getPropertySources() != null) {
for (org.springframework.cloud.config.environment.PropertySource source : result
.getPropertySources()) {
@SuppressWarnings("unchecked")
Map<String, Object> map = translateOrigins(source.getName(),
(Map<String, Object>) source.getSource());
propertySources.add(0,
new OriginTrackedMapPropertySource("configserver:" + source.getName(), map, true));
}
}
HashMap<String, Object> map = new HashMap<>();
if (StringUtils.hasText(result.getState())) {
putValue(map, "config.client.state", result.getState());
}
if (StringUtils.hasText(result.getVersion())) {
putValue(map, "config.client.version", result.getVersion());
}
// the existence of this property source confirms a successful
// response from config server
propertySources.add(0, new MapPropertySource(CONFIG_CLIENT_PROPERTYSOURCE_NAME, map));
if (ALL_OPTIONS.size() == 1) {
// boot 2.4.2 and prior
return new ConfigData(propertySources);
}
else if (ALL_OPTIONS.size() == 2) {
// boot 2.4.3 and 2.4.4
return new ConfigData(propertySources, Option.IGNORE_IMPORTS, Option.IGNORE_PROFILES);
}
else if (ALL_OPTIONS.size() > 2) {
// boot 2.4.5+
return new ConfigData(propertySources, propertySource -> {
String propertySourceName = propertySource.getName();
List<Option> options = new ArrayList<>();
options.add(Option.IGNORE_IMPORTS);
options.add(Option.IGNORE_PROFILES);
// TODO: the profile is now available on the backend
// in a future minor, add the profile associated with a
// PropertySource see
// https://github.com/spring-cloud/spring-cloud-config/issues/1874
for (String profile : resource.getAcceptedProfiles()) {
// TODO: switch to match
// , is used as a profile-separator for property sources
// from vault
// - is the default profile-separator for property sources
// TODO This is error prone logic see
// https://github.com/spring-cloud/spring-cloud-config/issues/2291
if (propertySourceName.matches(".*[-,]" + profile + ".*")) {
// // TODO: switch to Options.with() when implemented
options.add(Option.PROFILE_SPECIFIC);
}
}
return ConfigData.Options.of(options.toArray(new Option[0]));
});
}
}
}
errorBody = String.format("None of labels %s found", Arrays.toString(labels));
}
catch (HttpServerErrorException e) {
error = e;
if (MediaType.APPLICATION_JSON.includes(e.getResponseHeaders().getContentType())) {
errorBody = e.getResponseBodyAsString();
}
}
catch (Exception e) {
error = e;
}
if (properties.isFailFast() || !resource.isOptional()) {
String reason;
if (properties.isFailFast()) {
reason = "the fail fast property is set";
}
else {
reason = "the resource is not optional";
}
throw new ConfigClientFailFastException("Could not locate PropertySource and " + reason + ", failing"
+ (errorBody == null ? "" : ": " + errorBody), error);
}
logger.warn("Could not locate PropertySource (" + resource + "): "
+ (error != null ? error.getMessage() : errorBody));
return null;
}
protected void log(Environment result) {
if (logger.isInfoEnabled()) {
logger.info(String.format("Located environment: name=%s, profiles=%s, label=%s, version=%s, state=%s",
result.getName(), result.getProfiles() == null ? "" : Arrays.asList(result.getProfiles()),
result.getLabel(), result.getVersion(), result.getState()));
}
if (logger.isDebugEnabled()) {
List<org.springframework.cloud.config.environment.PropertySource> propertySourceList = result
.getPropertySources();
if (propertySourceList != null) {
int propertyCount = 0;
for (org.springframework.cloud.config.environment.PropertySource propertySource : propertySourceList) {
propertyCount += propertySource.getSource().size();
}
logger.debug(String.format("Environment %s has %d property sources with %d properties.",
result.getName(), result.getPropertySources().size(), propertyCount));
}
}
}
protected Map<String, Object> translateOrigins(String name, Map<String, Object> source) {
Map<String, Object> withOrigins = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : source.entrySet()) {
boolean hasOrigin = false;
if (entry.getValue() instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> value = (Map<String, Object>) entry.getValue();
if (value.size() == 2 && value.containsKey("origin") && value.containsKey("value")) {
Origin origin = new ConfigServicePropertySourceLocator.ConfigServiceOrigin(name,
value.get("origin"));
OriginTrackedValue trackedValue = OriginTrackedValue.of(value.get("value"), origin);
withOrigins.put(entry.getKey(), trackedValue);
hasOrigin = true;
}
}
if (!hasOrigin) {
withOrigins.put(entry.getKey(), entry.getValue());
}
}
return withOrigins;
}
protected void putValue(HashMap<String, Object> map, String key, String value) {
if (StringUtils.hasText(value)) {
map.put(key, value);
}
}
protected Environment getRemoteEnvironment(ConfigDataLoaderContext context, ConfigServerConfigDataResource resource,
String label, String state) {
ConfigClientProperties properties = resource.getProperties();
RestTemplate restTemplate = context.getBootstrapContext().get(RestTemplate.class);
String path = "/{name}/{profile}";
String name = properties.getName();
String profile = resource.getProfiles();
String token = properties.getToken();
int noOfUrls = properties.getUri().length;
if (noOfUrls > 1) {
logger.info("Multiple Config Server Urls found listed.");
}
Object[] args = new String[] { name, profile };
if (StringUtils.hasText(label)) {
// workaround for Spring MVC matching / in paths
label = Environment.denormalize(label);
args = new String[] { name, profile, label };
path = path + "/{label}";
}
ResponseEntity<Environment> response = null;
List<MediaType> acceptHeader = Collections.singletonList(MediaType.parseMediaType(properties.getMediaType()));
ConfigClientRequestTemplateFactory requestTemplateFactory = context.getBootstrapContext()
.get(ConfigClientRequestTemplateFactory.class);
for (int i = 0; i < noOfUrls; i++) {
ConfigClientProperties.Credentials credentials = properties.getCredentials(i);
String uri = credentials.getUri();
String username = credentials.getUsername();
String password = credentials.getPassword();
logger.info("Fetching config from server at : " + uri);
try {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(acceptHeader);
requestTemplateFactory.addAuthorizationToken(headers, username, password);
if (StringUtils.hasText(token)) {
headers.add(TOKEN_HEADER, token);
}
if (StringUtils.hasText(state) && properties.isSendState()) {
headers.add(STATE_HEADER, state);
}
final HttpEntity<Void> entity = new HttpEntity<>((Void) null, headers);
response = restTemplate.exchange(uri + path, HttpMethod.GET, entity, Environment.class, args);
}
catch (HttpClientErrorException | HttpServerErrorException e) {
if (i < noOfUrls - 1 && properties.getMultipleUriStrategy() == MultipleUriStrategy.ALWAYS) {
logger.info("Failed to fetch configs from server at : " + uri
+ ". Will try the next url if available. Error : " + e.getMessage());
continue;
}
if (e.getStatusCode() != HttpStatus.NOT_FOUND) {
throw e;
}
}
catch (ResourceAccessException e) {
logger.info("Exception on Url - " + uri + ":" + e + ". Will be trying the next url if available");
if (i == noOfUrls - 1) {
throw e;
}
else {
continue;
}
}
if (response == null || response.getStatusCode() != HttpStatus.OK) {
return null;
}
Environment result = response.getBody();
return result;
}
return null;
}
@Deprecated
protected void addAuthorizationToken(ConfigClientProperties configClientProperties, HttpHeaders httpHeaders,
String username, String password) {
}
}

View File

@@ -1,276 +0,0 @@
/*
* Copyright 2013-2020 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.client;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.springframework.boot.BootstrapRegistry;
import org.springframework.boot.BootstrapRegistry.InstanceSupplier;
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.PropertyMapper;
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.cloud.client.ServiceInstance;
import org.springframework.core.Ordered;
import org.springframework.core.log.LogMessage;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import static org.springframework.cloud.config.client.ConfigClientProperties.CONFIG_DISCOVERY_ENABLED;
public class ConfigServerConfigDataLocationResolver
implements ConfigDataLocationResolver<ConfigServerConfigDataResource>, Ordered {
/**
* Prefix for Config Server imports.
*/
public static final String PREFIX = "configserver:";
private final Log log;
public ConfigServerConfigDataLocationResolver(DeferredLogFactory factory) {
this.log = factory.getLog(ConfigServerConfigDataLocationResolver.class);
}
@Override
public int getOrder() {
return -1;
}
protected PropertyHolder loadProperties(ConfigDataLocationResolverContext context, String uris) {
Binder binder = context.getBinder();
BindHandler bindHandler = getBindHandler(context);
ConfigClientProperties configClientProperties;
if (context.getBootstrapContext().isRegistered(ConfigClientProperties.class)) {
configClientProperties = binder
.bind(ConfigClientProperties.PREFIX, Bindable.of(ConfigClientProperties.class), bindHandler)
.orElseGet(ConfigClientProperties::new);
boolean discoveryEnabled = context.getBinder()
.bind(CONFIG_DISCOVERY_ENABLED, Bindable.of(Boolean.class), getBindHandler(context)).orElse(false);
// In the case where discovery is enabled we need to extract the config server
// uris, username, and password
// from the properties from the context. These are set in
// ConfigServerInstanceMonitor.refresh which will only
// be called the first time we fetch configuration.
if (discoveryEnabled) {
ConfigClientProperties bootstrapConfigClientProperties = context.getBootstrapContext()
.get(ConfigClientProperties.class);
configClientProperties.setUri(bootstrapConfigClientProperties.getUri());
configClientProperties.setPassword(bootstrapConfigClientProperties.getPassword());
configClientProperties.setUsername(bootstrapConfigClientProperties.getUsername());
}
}
else {
configClientProperties = binder
.bind(ConfigClientProperties.PREFIX, Bindable.of(ConfigClientProperties.class), bindHandler)
.orElseGet(ConfigClientProperties::new);
}
if (!StringUtils.hasText(configClientProperties.getName())
|| "application".equals(configClientProperties.getName())) {
// default to spring.application.name if name isn't set
String applicationName = binder.bind("spring.application.name", Bindable.of(String.class), bindHandler)
.orElse("application");
configClientProperties.setName(applicationName);
}
PropertyHolder holder = new PropertyHolder();
holder.properties = configClientProperties;
// bind retry, override later
holder.retryProperties = binder.bind(RetryProperties.PREFIX, RetryProperties.class)
.orElseGet(RetryProperties::new);
if (StringUtils.hasText(uris)) {
String[] uri = StringUtils.commaDelimitedListToStringArray(uris);
String paramStr = null;
for (int i = 0; i < uri.length; i++) {
int paramIdx = uri[i].indexOf('?');
if (paramIdx > 0) {
if (i == 0) {
// only gather params from first uri
paramStr = uri[i].substring(paramIdx + 1);
}
uri[i] = uri[i].substring(0, paramIdx);
}
}
if (StringUtils.hasText(paramStr)) {
Properties properties = StringUtils
.splitArrayElementsIntoProperties(StringUtils.delimitedListToStringArray(paramStr, "&"), "=");
if (properties != null) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(() -> properties.getProperty("fail-fast")).as(Boolean::valueOf)
.to(configClientProperties::setFailFast);
map.from(() -> properties.getProperty("max-attempts")).as(Integer::valueOf)
.to(holder.retryProperties::setMaxAttempts);
map.from(() -> properties.getProperty("max-interval")).as(Long::valueOf)
.to(holder.retryProperties::setMaxInterval);
map.from(() -> properties.getProperty("multiplier")).as(Double::valueOf)
.to(holder.retryProperties::setMultiplier);
map.from(() -> properties.getProperty("initial-interval")).as(Long::valueOf)
.to(holder.retryProperties::setInitialInterval);
}
}
configClientProperties.setUri(uri);
}
return holder;
}
private BindHandler getBindHandler(ConfigDataLocationResolverContext context) {
return context.getBootstrapContext().getOrElse(BindHandler.class, null);
}
@Deprecated
protected RestTemplate createRestTemplate(ConfigClientProperties properties) {
return null;
}
protected Log getLog() {
return this.log;
}
@Override
public boolean isResolvable(ConfigDataLocationResolverContext context, ConfigDataLocation location) {
if (!location.hasPrefix(getPrefix())) {
return false;
}
return context.getBinder().bind(ConfigClientProperties.PREFIX + ".enabled", Boolean.class).orElse(true);
}
protected String getPrefix() {
return PREFIX;
}
@Override
public List<ConfigServerConfigDataResource> resolve(ConfigDataLocationResolverContext context,
ConfigDataLocation location)
throws ConfigDataLocationNotFoundException, ConfigDataResourceNotFoundException {
return resolveProfileSpecific(context, location, null);
}
@Override
public List<ConfigServerConfigDataResource> resolveProfileSpecific(
ConfigDataLocationResolverContext resolverContext, ConfigDataLocation location, Profiles profiles)
throws ConfigDataLocationNotFoundException {
String uris = location.getNonPrefixedValue(getPrefix());
PropertyHolder propertyHolder = loadProperties(resolverContext, uris);
ConfigClientProperties properties = propertyHolder.properties;
ConfigurableBootstrapContext bootstrapContext = resolverContext.getBootstrapContext();
bootstrapContext.register(ConfigClientProperties.class,
InstanceSupplier.of(properties).withScope(BootstrapRegistry.Scope.PROTOTYPE));
bootstrapContext.addCloseListener(event -> event.getApplicationContext().getBeanFactory().registerSingleton(
"configDataConfigClientProperties", event.getBootstrapContext().get(ConfigClientProperties.class)));
bootstrapContext.registerIfAbsent(ConfigClientRequestTemplateFactory.class,
context -> new ConfigClientRequestTemplateFactory(log, context.get(ConfigClientProperties.class)));
bootstrapContext.registerIfAbsent(RestTemplate.class, context -> {
ConfigClientRequestTemplateFactory factory = context.get(ConfigClientRequestTemplateFactory.class);
RestTemplate restTemplate = createRestTemplate(factory.getProperties());
if (restTemplate != null) {
// shouldn't normally happen
return restTemplate;
}
return factory.create();
});
ConfigServerConfigDataResource resource = new ConfigServerConfigDataResource(properties, location.isOptional(),
profiles);
resource.setProfileSpecific(!ObjectUtils.isEmpty(profiles));
resource.setLog(log);
resource.setRetryProperties(propertyHolder.retryProperties);
boolean discoveryEnabled = resolverContext.getBinder()
.bind(CONFIG_DISCOVERY_ENABLED, Bindable.of(Boolean.class), getBindHandler(resolverContext))
.orElse(false);
boolean retryEnabled = resolverContext.getBinder().bind(ConfigClientProperties.PREFIX + ".fail-fast",
Bindable.of(Boolean.class), getBindHandler(resolverContext)).orElse(false);
if (discoveryEnabled) {
log.debug(LogMessage.format("discovery enabled"));
// register ConfigServerInstanceMonitor
bootstrapContext.registerIfAbsent(ConfigServerInstanceMonitor.class, context -> {
ConfigServerInstanceProvider.Function function = context
.get(ConfigServerInstanceProvider.Function.class);
ConfigServerInstanceProvider instanceProvider;
if (ConfigClientRetryBootstrapper.RETRY_IS_PRESENT && retryEnabled) {
log.debug(LogMessage.format("discovery plus retry enabled"));
RetryTemplate retryTemplate = RetryTemplateFactory.create(propertyHolder.retryProperties, log);
instanceProvider = new ConfigServerInstanceProvider(function, resolverContext.getBinder(),
getBindHandler(resolverContext)) {
@Override
public List<ServiceInstance> getConfigServerInstances(String serviceId) {
return retryTemplate.execute(retryContext -> super.getConfigServerInstances(serviceId));
}
};
}
else {
instanceProvider = new ConfigServerInstanceProvider(function, resolverContext.getBinder(),
getBindHandler(resolverContext));
}
instanceProvider.setLog(log);
ConfigClientProperties clientProperties = context.get(ConfigClientProperties.class);
ConfigServerInstanceMonitor instanceMonitor = new ConfigServerInstanceMonitor(log, clientProperties,
instanceProvider);
instanceMonitor.setRefreshOnStartup(false);
instanceMonitor.refresh();
return instanceMonitor;
});
// promote ConfigServerInstanceMonitor to bean so updates can be made to
// config client uri
bootstrapContext.addCloseListener(event -> {
ConfigServerInstanceMonitor configServerInstanceMonitor = event.getBootstrapContext()
.get(ConfigServerInstanceMonitor.class);
event.getApplicationContext().getBeanFactory().registerSingleton("configServerInstanceMonitor",
configServerInstanceMonitor);
});
}
List<ConfigServerConfigDataResource> locations = new ArrayList<>();
locations.add(resource);
return locations;
}
private class PropertyHolder {
ConfigClientProperties properties;
RetryProperties retryProperties;
}
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright 2015-2021 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.client;
import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor;
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.cloud.commons.ConfigDataMissingEnvironmentPostProcessor;
import org.springframework.core.env.Environment;
import static org.springframework.cloud.config.client.ConfigServerConfigDataLocationResolver.PREFIX;
import static org.springframework.cloud.util.PropertyUtils.bootstrapEnabled;
import static org.springframework.cloud.util.PropertyUtils.useLegacyProcessing;
public class ConfigServerConfigDataMissingEnvironmentPostProcessor extends ConfigDataMissingEnvironmentPostProcessor {
/**
* Order of post processor, set to run after
* {@link ConfigDataEnvironmentPostProcessor}.
*/
public static final int ORDER = ConfigDataEnvironmentPostProcessor.ORDER + 1000;
@Override
public int getOrder() {
return ORDER;
}
@Override
protected String getPrefix() {
return PREFIX;
}
@Override
protected boolean shouldProcessEnvironment(Environment environment) {
// don't run if using bootstrap or legacy processing
if (bootstrapEnabled(environment) || useLegacyProcessing(environment)) {
return false;
}
boolean configEnabled = environment.getProperty(ConfigClientProperties.PREFIX + ".enabled", Boolean.class,
true);
boolean importCheckEnabled = environment.getProperty(ConfigClientProperties.PREFIX + ".import-check.enabled",
Boolean.class, true);
if (!configEnabled || !importCheckEnabled) {
return false;
}
return true;
}
static class ImportExceptionFailureAnalyzer extends AbstractFailureAnalyzer<ImportException> {
@Override
protected FailureAnalysis analyze(Throwable rootFailure, ImportException cause) {
String description;
if (cause.missingPrefix) {
description = "The spring.config.import property is missing a " + PREFIX + " entry";
}
else {
description = "No spring.config.import property has been defined";
}
String action = "Add a spring.config.import=configserver: property to your configuration.\n"
+ "\tIf configuration is not required add spring.config.import=optional:configserver: instead.\n"
+ "\tTo disable this check, set spring.cloud.config.enabled=false or \n"
+ "\tspring.cloud.config.import-check.enabled=false.";
return new FailureAnalysis(description, action, cause);
}
}
}

View File

@@ -1,181 +0,0 @@
/*
* Copyright 2013-2020 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.client;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.apache.commons.logging.Log;
import org.springframework.boot.context.config.ConfigDataResource;
import org.springframework.boot.context.config.Profiles;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import static org.springframework.cloud.config.client.ConfigClientProperties.DEFAULT_APPLICATION;
import static org.springframework.cloud.config.client.ConfigClientProperties.DEFAULT_PROFILE;
public class ConfigServerConfigDataResource extends ConfigDataResource {
private final ConfigClientProperties properties;
private final boolean optional;
private final Profiles profiles;
private RetryProperties retryProperties;
private Log log;
private boolean isProfileSpecific = false;
public ConfigServerConfigDataResource(ConfigClientProperties properties, boolean optional, Profiles profiles) {
this.properties = properties;
this.optional = optional;
this.profiles = profiles;
}
public ConfigClientProperties getProperties() {
return this.properties;
}
public boolean isProfileSpecific() {
return isProfileSpecific;
}
public void setProfileSpecific(boolean profileSpecific) {
isProfileSpecific = profileSpecific;
}
public boolean isOptional() {
return this.optional;
}
public String getProfiles() {
if (StringUtils.hasText(properties.getProfile())
&& !properties.getProfile().equals(ConfigClientProperties.DEFAULT_PROFILE)) {
return properties.getProfile();
}
return StringUtils.collectionToCommaDelimitedString(getAcceptedProfiles());
}
List<String> getAcceptedProfiles() {
if (profiles == null) {
return Collections.singletonList(!properties.getProfile().equals(ConfigClientProperties.DEFAULT_PROFILE)
? properties.getProfile() : ConfigClientProperties.DEFAULT_PROFILE);
}
return this.profiles.getAccepted();
}
public void setLog(Log log) {
this.log = log;
}
public Log getLog() {
return this.log;
}
public RetryProperties getRetryProperties() {
return this.retryProperties;
}
public void setRetryProperties(RetryProperties retryProperties) {
this.retryProperties = retryProperties;
}
private String getApplicationName() {
return ObjectUtils.isEmpty(this.properties.getName()) ? DEFAULT_APPLICATION : this.getProperties().getName();
}
private String getProfilesForEquals() {
return ObjectUtils.isEmpty(this.getProfiles()) ? DEFAULT_PROFILE : this.getProfiles();
}
private boolean urisEqual(String[] thatUris) {
if (this.properties.getUri().length != thatUris.length) {
return false;
}
for (String uri : this.properties.getUri()) {
if (Arrays.stream(thatUris).noneMatch(thatUri -> uriEqual(uri, thatUri))) {
return false;
}
}
return true;
}
private boolean uriEqual(String thisUriString, String thatUriString) {
try {
UriComponents thisUri = UriComponentsBuilder.fromHttpUrl(thisUriString).build();
UriComponents thatUri = UriComponentsBuilder.fromHttpUrl(thatUriString).build();
return Objects.equals(thisUri.getHost(), thatUri.getHost())
&& Objects.equals(thisUri.getPort(), thatUri.getPort())
&& Objects.equals(thisUri.getPath(), thatUri.getPath());
}
catch (Exception e) {
return Objects.equals(thisUriString, thatUriString);
}
}
private int urisHashCode(String[] uris) {
return Arrays.stream(uris).mapToInt(uriString -> {
try {
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(uriString).build();
return Objects.hash(uriComponents.getHost(), uriComponents.getPath(), uriComponents.getPort());
}
catch (Exception e) {
return Arrays.hashCode(uris);
}
}).sum();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ConfigServerConfigDataResource that = (ConfigServerConfigDataResource) o;
return urisEqual(that.properties.getUri())
&& Objects.equals(this.getApplicationName(), that.getApplicationName())
&& Objects.equals(this.properties.getLabel(), that.properties.getLabel())
&& Objects.equals(this.getProfilesForEquals(), that.getProfilesForEquals())
&& Objects.equals(this.optional, that.optional);
}
@Override
public int hashCode() {
String[] uris = properties.getUri();
String name = properties.getName();
String label = properties.getLabel();
return Objects.hash(urisHashCode(uris), name, label, optional, getProfilesForEquals());
}
@Override
public String toString() {
return new ToStringCreator(this).append("uris", properties.getUri()).append("optional", optional)
.append("profiles", getProfiles()).toString();
}
}

View File

@@ -1,97 +0,0 @@
/*
* 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.client;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health.Builder;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import static org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME;
import static org.springframework.cloud.config.client.ConfigServerConfigDataLoader.CONFIG_CLIENT_PROPERTYSOURCE_NAME;
import static org.springframework.cloud.config.client.ConfigServerConfigDataLocationResolver.PREFIX;
/**
* @author Spencer Gibb
* @author Marcos Barbero
*/
public class ConfigServerHealthIndicator extends AbstractHealthIndicator {
private ConfigClientHealthProperties properties;
private ConfigurableEnvironment environment;
private long lastAccess = 0;
private List<PropertySource<?>> cached = new ArrayList<>();
public ConfigServerHealthIndicator(ConfigurableEnvironment environment, ConfigClientHealthProperties properties) {
this.environment = environment;
this.properties = properties;
}
@Override
protected void doHealthCheck(Builder builder) {
List<PropertySource<?>> propertySources = getPropertySource();
if (propertySources.isEmpty()) {
builder.unknown();
builder.unknown().withDetail("error", "no property sources located");
}
else {
builder.up();
List<String> sources = new ArrayList<>();
for (PropertySource<?> propertySource : propertySources) {
if (propertySource instanceof CompositePropertySource) {
for (PropertySource<?> ps : ((CompositePropertySource) propertySource).getPropertySources()) {
sources.add(ps.getName());
}
}
else if (propertySource != null) {
sources.add(propertySource.getName());
}
}
builder.withDetail("propertySources", sources);
}
}
private List<PropertySource<?>> getPropertySource() {
long accessTime = System.currentTimeMillis();
if (isCacheStale(accessTime)) {
this.lastAccess = accessTime;
this.cached = this.environment.getPropertySources().stream()
.filter(p -> p.getName().startsWith(CONFIG_CLIENT_PROPERTYSOURCE_NAME)
|| p.getName().startsWith(BOOTSTRAP_PROPERTY_SOURCE_NAME + "-")
|| p.getName().startsWith(PREFIX))
.collect(Collectors.toList());
}
return this.cached;
}
private boolean isCacheStale(long accessTime) {
if (this.cached == null) {
return true;
}
return (accessTime - this.lastAccess) >= this.properties.getTimeToLive().toMillis();
}
}

View File

@@ -1,139 +0,0 @@
/*
* Copyright 2013-2020 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.client;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.cloud.client.discovery.event.HeartbeatMonitor;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.util.Assert;
final class ConfigServerInstanceMonitor implements SmartApplicationListener {
private final Log log;
private final ConfigClientProperties config;
private final ConfigServerInstanceProvider instanceProvider;
private final HeartbeatMonitor monitor = new HeartbeatMonitor();
/**
* If bootstrap, this should be true, for config data false.
*/
private boolean refreshOnStartup = true;
ConfigServerInstanceMonitor(Log log, ConfigClientProperties config, ConfigServerInstanceProvider instanceProvider) {
this.log = log;
this.config = config;
this.instanceProvider = instanceProvider;
}
void setRefreshOnStartup(boolean refreshOnStartup) {
this.refreshOnStartup = refreshOnStartup;
}
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return ContextRefreshedEvent.class.isAssignableFrom(eventType)
|| HeartbeatEvent.class.isAssignableFrom(eventType);
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
startup((ContextRefreshedEvent) event);
}
else if (event instanceof HeartbeatEvent) {
heartbeat((HeartbeatEvent) event);
}
}
public void startup(ContextRefreshedEvent event) {
if (refreshOnStartup) {
refresh();
}
}
public void heartbeat(HeartbeatEvent event) {
if (this.monitor.update(event.getValue())) {
refresh();
}
}
void refresh() {
try {
String serviceId = this.config.getDiscovery().getServiceId();
Assert.hasText(serviceId, () -> ConfigClientProperties.PREFIX + ".service-id may not be null or empty");
List<String> listOfUrls = new ArrayList<>();
List<ServiceInstance> serviceInstances = this.instanceProvider.getConfigServerInstances(serviceId);
for (int i = 0; i < serviceInstances.size(); i++) {
ServiceInstance server = serviceInstances.get(i);
String url = getHomePage(server);
if (server.getMetadata().containsKey("password")) {
String user = server.getMetadata().get("user");
user = user == null ? "user" : user;
this.config.setUsername(user);
String password = server.getMetadata().get("password");
this.config.setPassword(password);
}
if (server.getMetadata().containsKey("configPath")) {
String path = server.getMetadata().get("configPath");
if (url.endsWith("/") && path.startsWith("/")) {
url = url.substring(0, url.length() - 1);
}
url = url + path;
}
listOfUrls.add(url);
}
if (log.isDebugEnabled()) {
log.debug("Updating config uris to " + listOfUrls);
}
String[] uri = new String[listOfUrls.size()];
uri = listOfUrls.toArray(uri);
this.config.setUri(uri);
}
catch (Exception ex) {
if (this.config.isFailFast()) {
throw ex;
}
else if (log.isWarnEnabled()) {
log.warn("Could not locate configserver via discovery", ex);
}
}
}
private String getHomePage(ServiceInstance server) {
return server.getUri().toString() + "/";
}
}

View File

@@ -1,97 +0,0 @@
/*
* 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.client;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.context.properties.bind.BindHandler;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.retry.annotation.Retryable;
/**
* Fetches config server instances.
*
* @author Nastya Smirnova
*/
public class ConfigServerInstanceProvider {
private Log log = LogFactory.getLog(getClass());
private final Function function;
private BindHandler bindHandler;
private Binder binder;
@Deprecated
public ConfigServerInstanceProvider(DiscoveryClient client) {
this.function = client::getInstances;
}
public ConfigServerInstanceProvider(Function function) {
this.function = function;
}
public ConfigServerInstanceProvider(Function function, Binder binder, BindHandler bindHandler) {
this.function = function;
this.binder = binder;
this.bindHandler = bindHandler;
}
void setLog(Log log) {
this.log = log;
}
@Retryable(interceptor = "configServerRetryInterceptor")
public List<ServiceInstance> getConfigServerInstances(String serviceId) {
if (log.isDebugEnabled()) {
log.debug("Locating configserver (" + serviceId + ") via discovery");
}
List<ServiceInstance> instances;
if (binder == null || bindHandler == null) {
instances = this.function.apply(serviceId);
}
else {
instances = this.function.apply(serviceId, binder, bindHandler, log);
}
if (instances.isEmpty()) {
throw new IllegalStateException("No instances found of configserver (" + serviceId + ")");
}
if (log.isDebugEnabled()) {
log.debug("Located configserver (" + serviceId + ") via discovery. No of instances found: "
+ instances.size());
}
return instances;
}
@FunctionalInterface
public interface Function {
List<ServiceInstance> apply(String serviceId);
default List<ServiceInstance> apply(String serviceId, Binder binder, BindHandler bindHandler, Log log) {
return apply(serviceId);
};
}
}

View File

@@ -1,80 +0,0 @@
/*
* Copyright 2013-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.client;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.annotation.Retryable;
import org.springframework.retry.interceptor.RetryInterceptorBuilder;
import org.springframework.retry.interceptor.RetryOperationsInterceptor;
/**
* @author Dave Syer
* @author Tristan Hanson
*
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
public class ConfigServiceBootstrapConfiguration {
@Autowired
private ConfigurableEnvironment environment;
@Bean
@ConditionalOnMissingBean
public ConfigClientProperties configClientProperties() {
ConfigClientProperties client = new ConfigClientProperties(this.environment);
return client;
}
@Bean
@ConditionalOnMissingBean(ConfigServicePropertySourceLocator.class)
@ConditionalOnProperty(name = ConfigClientProperties.PREFIX + ".enabled", matchIfMissing = true)
public ConfigServicePropertySourceLocator configServicePropertySource(ConfigClientProperties properties) {
return new ConfigServicePropertySourceLocator(properties);
}
@ConditionalOnProperty(ConfigClientProperties.PREFIX + ".fail-fast")
@ConditionalOnClass({ Retryable.class, Aspect.class, AopAutoConfiguration.class })
@Configuration(proxyBeanMethods = false)
@EnableRetry(proxyTargetClass = true)
@Import(AopAutoConfiguration.class)
@EnableConfigurationProperties(RetryProperties.class)
protected static class RetryConfiguration {
@Bean
@ConditionalOnMissingBean(name = "configServerRetryInterceptor")
public RetryOperationsInterceptor configServerRetryInterceptor(RetryProperties properties) {
return RetryInterceptorBuilder.stateless().backOffOptions(properties.getInitialInterval(),
properties.getMultiplier(), properties.getMaxInterval()).maxAttempts(properties.getMaxAttempts())
.build();
}
}
}

View File

@@ -1,358 +0,0 @@
/*
* Copyright 2013-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.client;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.boot.origin.Origin;
import org.springframework.boot.origin.OriginTrackedValue;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.cloud.bootstrap.support.OriginTrackedCompositePropertySource;
import org.springframework.cloud.config.client.ConfigClientProperties.Credentials;
import org.springframework.cloud.config.client.ConfigClientProperties.MultipleUriStrategy;
import org.springframework.cloud.config.client.validation.InvalidApplicationNameException;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.environment.PropertySource;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.MapPropertySource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.retry.annotation.Retryable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
import static org.springframework.cloud.config.client.ConfigClientProperties.NAME_PLACEHOLDER;
import static org.springframework.cloud.config.client.ConfigClientProperties.STATE_HEADER;
import static org.springframework.cloud.config.client.ConfigClientProperties.TOKEN_HEADER;
/**
* @author Dave Syer
* @author Mathieu Ouellet
* @author Marnee DeRider
*/
@Order(0)
public class ConfigServicePropertySourceLocator implements PropertySourceLocator {
private static Log logger = LogFactory.getLog(ConfigServicePropertySourceLocator.class);
private RestTemplate restTemplate;
private ConfigClientProperties defaultProperties;
public ConfigServicePropertySourceLocator(ConfigClientProperties defaultProperties) {
this.defaultProperties = defaultProperties;
}
/**
* Combine properties from the config client properties and the active profiles from
* the environment.
* @param properties config client properties,
* @param environment application environment.
* @return A list of combined profiles.
*/
private List<String> combineProfiles(ConfigClientProperties properties,
org.springframework.core.env.Environment environment) {
List<String> combinedProfiles = new ArrayList<>();
if (!ObjectUtils.isEmpty(properties.getProfile())) {
combinedProfiles = Stream.of(properties.getProfile().split(",")).map(String::trim).filter(s -> !s.isEmpty())
.collect(Collectors.toList());
}
if (environment.getActiveProfiles().length > 0) {
List<String> finalCombinedProfiles = combinedProfiles;
List<String> filteredActiveProfiles = Stream.of(environment.getActiveProfiles())
.filter(s -> !finalCombinedProfiles.contains(s)).collect(Collectors.toList());
combinedProfiles.addAll(filteredActiveProfiles);
}
else if (environment.getDefaultProfiles().length > 0 && combinedProfiles.isEmpty()) {
combinedProfiles = Arrays.asList(environment.getDefaultProfiles());
}
return combinedProfiles;
}
@Override
@Retryable(interceptor = "configServerRetryInterceptor")
public org.springframework.core.env.PropertySource<?> locate(org.springframework.core.env.Environment environment) {
ConfigClientProperties properties = this.defaultProperties.override(environment);
properties.setProfile(String.join(",", combineProfiles(properties, environment)));
if (StringUtils.startsWithIgnoreCase(properties.getName(), "application-")) {
InvalidApplicationNameException exception = new InvalidApplicationNameException(properties.getName());
if (properties.isFailFast()) {
throw exception;
}
else {
logger.warn(NAME_PLACEHOLDER + " resolved to " + properties.getName()
+ ", not going to load remote properties. Ensure application name doesn't start with 'application-'");
return null;
}
}
CompositePropertySource composite = new OriginTrackedCompositePropertySource("configService");
ConfigClientRequestTemplateFactory requestTemplateFactory = new ConfigClientRequestTemplateFactory(logger,
properties);
Exception error = null;
String errorBody = null;
try {
String[] labels = new String[] { "" };
if (StringUtils.hasText(properties.getLabel())) {
labels = StringUtils.commaDelimitedListToStringArray(properties.getLabel());
}
String state = ConfigClientStateHolder.getState();
// Try all the labels until one works
for (String label : labels) {
Environment result = getRemoteEnvironment(requestTemplateFactory, label.trim(), state);
if (result != null) {
log(result);
// result.getPropertySources() can be null if using xml
if (result.getPropertySources() != null) {
for (PropertySource source : result.getPropertySources()) {
@SuppressWarnings("unchecked")
Map<String, Object> map = translateOrigins(source.getName(),
(Map<String, Object>) source.getSource());
composite.addPropertySource(new OriginTrackedMapPropertySource(source.getName(), map));
}
}
HashMap<String, Object> map = new HashMap<>();
if (StringUtils.hasText(result.getState())) {
putValue(map, "config.client.state", result.getState());
}
if (StringUtils.hasText(result.getVersion())) {
putValue(map, "config.client.version", result.getVersion());
}
// the existence of this property source confirms a successful
// response from config server
composite.addFirstPropertySource(new MapPropertySource("configClient", map));
return composite;
}
}
errorBody = String.format("None of labels %s found", Arrays.toString(labels));
}
catch (HttpServerErrorException e) {
error = e;
if (MediaType.APPLICATION_JSON.includes(e.getResponseHeaders().getContentType())) {
errorBody = e.getResponseBodyAsString();
}
}
catch (Exception e) {
error = e;
}
if (properties.isFailFast()) {
throw new IllegalStateException("Could not locate PropertySource and the fail fast property is set, failing"
+ (errorBody == null ? "" : ": " + errorBody), error);
}
logger.warn("Could not locate PropertySource: " + (error != null ? error.getMessage() : errorBody));
return null;
}
@Override
@Retryable(interceptor = "configServerRetryInterceptor")
public Collection<org.springframework.core.env.PropertySource<?>> locateCollection(
org.springframework.core.env.Environment environment) {
return PropertySourceLocator.locateCollection(this, environment);
}
private void log(Environment result) {
if (logger.isInfoEnabled()) {
logger.info(String.format("Located environment: name=%s, profiles=%s, label=%s, version=%s, state=%s",
result.getName(), result.getProfiles() == null ? "" : Arrays.asList(result.getProfiles()),
result.getLabel(), result.getVersion(), result.getState()));
}
if (logger.isDebugEnabled()) {
List<PropertySource> propertySourceList = result.getPropertySources();
if (propertySourceList != null) {
int propertyCount = 0;
for (PropertySource propertySource : propertySourceList) {
propertyCount += propertySource.getSource().size();
}
logger.debug(String.format("Environment %s has %d property sources with %d properties.",
result.getName(), result.getPropertySources().size(), propertyCount));
}
}
}
private Map<String, Object> translateOrigins(String name, Map<String, Object> source) {
Map<String, Object> withOrigins = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : source.entrySet()) {
boolean hasOrigin = false;
if (entry.getValue() instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> value = (Map<String, Object>) entry.getValue();
if (value.size() == 2 && value.containsKey("origin") && value.containsKey("value")) {
Origin origin = new ConfigServiceOrigin(name, value.get("origin"));
OriginTrackedValue trackedValue = OriginTrackedValue.of(value.get("value"), origin);
withOrigins.put(entry.getKey(), trackedValue);
hasOrigin = true;
}
}
if (!hasOrigin) {
withOrigins.put(entry.getKey(), entry.getValue());
}
}
return withOrigins;
}
private void putValue(HashMap<String, Object> map, String key, String value) {
if (StringUtils.hasText(value)) {
map.put(key, value);
}
}
private Environment getRemoteEnvironment(ConfigClientRequestTemplateFactory requestTemplateFactory, String label,
String state) {
RestTemplate restTemplate = this.restTemplate == null ? requestTemplateFactory.create() : this.restTemplate;
ConfigClientProperties properties = requestTemplateFactory.getProperties();
String path = "/{name}/{profile}";
String name = properties.getName();
String profile = properties.getProfile();
String token = properties.getToken();
int noOfUrls = properties.getUri().length;
if (noOfUrls > 1) {
logger.info("Multiple Config Server Urls found listed.");
}
Object[] args = new String[] { name, profile };
if (StringUtils.hasText(label)) {
// workaround for Spring MVC matching / in paths
label = Environment.denormalize(label);
args = new String[] { name, profile, label };
path = path + "/{label}";
}
ResponseEntity<Environment> response = null;
List<MediaType> acceptHeader = Collections.singletonList(MediaType.parseMediaType(properties.getMediaType()));
for (int i = 0; i < noOfUrls; i++) {
Credentials credentials = properties.getCredentials(i);
String uri = credentials.getUri();
String username = credentials.getUsername();
String password = credentials.getPassword();
logger.info("Fetching config from server at : " + uri);
try {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(acceptHeader);
requestTemplateFactory.addAuthorizationToken(headers, username, password);
if (StringUtils.hasText(token)) {
headers.add(TOKEN_HEADER, token);
}
if (StringUtils.hasText(state) && properties.isSendState()) {
headers.add(STATE_HEADER, state);
}
final HttpEntity<Void> entity = new HttpEntity<>((Void) null, headers);
response = restTemplate.exchange(uri + path, HttpMethod.GET, entity, Environment.class, args);
}
catch (HttpClientErrorException | HttpServerErrorException e) {
if (i < noOfUrls - 1 && defaultProperties.getMultipleUriStrategy() == MultipleUriStrategy.ALWAYS) {
logger.info("Failed to fetch configs from server at : " + uri
+ ". Will try the next url if available. Error : " + e.getMessage());
continue;
}
if (e.getStatusCode() != HttpStatus.NOT_FOUND) {
throw e;
}
}
catch (ResourceAccessException e) {
logger.info("Exception on Url - " + uri + ":" + e + ". Will be trying the next url if available");
if (i == noOfUrls - 1) {
throw e;
}
else {
continue;
}
}
if (response == null || response.getStatusCode() != HttpStatus.OK) {
return null;
}
Environment result = response.getBody();
return result;
}
return null;
}
public void setRestTemplate(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
/**
* Adds the provided headers to the request.
*/
@Deprecated
public static class GenericRequestHeaderInterceptor
extends ConfigClientRequestTemplateFactory.GenericRequestHeaderInterceptor {
public GenericRequestHeaderInterceptor(Map<String, String> headers) {
super(headers);
}
}
static class ConfigServiceOrigin implements Origin {
private final String remotePropertySource;
private final Object origin;
ConfigServiceOrigin(String remotePropertySource, Object origin) {
this.remotePropertySource = remotePropertySource;
Assert.notNull(origin, "origin may not be null");
this.origin = origin;
}
@Override
public String toString() {
return "Config Server " + this.remotePropertySource + ":" + this.origin.toString();
}
}
}

View File

@@ -1,64 +0,0 @@
/*
* Copyright 2013-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.client;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.commons.util.UtilAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Bootstrap configuration for a config client that wants to lookup the config server via
* discovery.
*
* @author Dave Syer
*/
@ConditionalOnProperty(ConfigClientProperties.CONFIG_DISCOVERY_ENABLED)
@Configuration(proxyBeanMethods = false)
@Import({ UtilAutoConfiguration.class })
@EnableDiscoveryClient
public class DiscoveryClientConfigServiceBootstrapConfiguration {
@Bean
public ConfigServerInstanceProvider configServerInstanceProvider(
ObjectProvider<ConfigServerInstanceProvider.Function> function,
ObjectProvider<DiscoveryClient> discoveryClient) {
ConfigServerInstanceProvider.Function fn = function.getIfAvailable();
if (fn != null) {
return new ConfigServerInstanceProvider(fn);
}
DiscoveryClient client = discoveryClient.getIfAvailable();
if (client == null) {
throw new IllegalStateException("ConfigServerInstanceProvider requires a DiscoveryClient or Function");
}
return new ConfigServerInstanceProvider(client::getInstances);
}
@Bean
public ConfigServerInstanceMonitor configServerInstanceMonitor(ConfigClientProperties properties,
ConfigServerInstanceProvider provider) {
return new ConfigServerInstanceMonitor(LogFactory.getLog(ConfigServerInstanceMonitor.class), properties,
provider);
}
}

View File

@@ -1,85 +0,0 @@
/*
* Copyright 2014-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.client;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author Dave Syer
*
*/
@ConfigurationProperties(RetryProperties.PREFIX)
public class RetryProperties {
/**
* ConfigurationProperties prefix.
*/
public static final String PREFIX = "spring.cloud.config.retry";
/**
* Initial retry interval in milliseconds.
*/
long initialInterval = 1000;
/**
* Multiplier for next interval.
*/
double multiplier = 1.1;
/**
* Maximum interval for backoff.
*/
long maxInterval = 2000;
/**
* Maximum number of attempts.
*/
int maxAttempts = 6;
public long getInitialInterval() {
return this.initialInterval;
}
public void setInitialInterval(long initialInterval) {
this.initialInterval = initialInterval;
}
public double getMultiplier() {
return this.multiplier;
}
public void setMultiplier(double multiplier) {
this.multiplier = multiplier;
}
public long getMaxInterval() {
return this.maxInterval;
}
public void setMaxInterval(long maxInterval) {
this.maxInterval = maxInterval;
}
public int getMaxAttempts() {
return this.maxAttempts;
}
public void setMaxAttempts(int maxAttempts) {
this.maxAttempts = maxAttempts;
}
}

View File

@@ -1,57 +0,0 @@
/*
* Copyright 2014-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.client;
import java.lang.reflect.Field;
import org.apache.commons.logging.Log;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.ReflectionUtils;
public final class RetryTemplateFactory {
private static final Field field;
static {
field = ReflectionUtils.findField(RetryTemplate.class, "logger");
if (field != null) {
ReflectionUtils.makeAccessible(field);
}
}
private RetryTemplateFactory() {
}
public static RetryTemplate create(RetryProperties properties, Log log) {
RetryTemplate retryTemplate = RetryTemplate.builder().maxAttempts(properties.getMaxAttempts())
.exponentialBackoff(properties.getInitialInterval(), properties.getMultiplier(),
properties.getMaxInterval())
.build();
try {
field.set(retryTemplate, log);
}
catch (IllegalAccessException e) {
if (log.isErrorEnabled()) {
log.error("error setting retry log", e);
}
}
return retryTemplate;
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright 2013-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.client.diagnostics.analyzer;
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.cloud.config.client.validation.InvalidApplicationNameException;
/**
* An {@link AbstractFailureAnalyzer} that analyzes {@link InvalidApplicationNameException
* InvalidApplicationNameException}.
*
* @author Anshul Mehra
*/
public class InvalidApplicationNameExceptionFailureAnalyzer
extends AbstractFailureAnalyzer<InvalidApplicationNameException> {
@Override
protected FailureAnalysis analyze(Throwable rootFailure, InvalidApplicationNameException cause) {
StringBuilder description = new StringBuilder(String.format("%s:%n", cause.getMessage()));
description.append(String.format("%n Property: %s", cause.getProperty()));
description.append(String.format("%n Value: %s", cause.getValue()));
String action = "Change ${spring.application.name} or the ${spring.cloud.config.name} "
+ "override so that it does not begin with 'application-'.";
return new FailureAnalysis(description.toString(), action, cause);
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2013-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.client.validation;
import static org.springframework.cloud.config.client.ConfigClientProperties.NAME_PLACEHOLDER;
/**
* A {@code InvalidApplicationNameException} is thrown when config client detects an
* invalid application name.
*
* @author Anshul Mehra
*/
public class InvalidApplicationNameException extends RuntimeException {
private final String property = NAME_PLACEHOLDER;
private final String value;
public InvalidApplicationNameException(String currentResolvedValue) {
super("Application name must not start with 'application-'");
this.value = currentResolvedValue;
}
public String getProperty() {
return this.property;
}
public String getValue() {
return this.value;
}
}

View File

@@ -1,167 +0,0 @@
/*
* Copyright 2013-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.environment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Simple plain text serializable encapsulation of a list of property sources. Basically a
* DTO for {@link org.springframework.core.env.Environment}, but also applicable outside
* the domain of a Spring application.
*
* @author Dave Syer
* @author Spencer Gibb
*
*/
public class Environment {
/**
* "(_)" is uncommon in a git repo name, but "/" cannot be matched by Spring MVC.
*/
public static final String SLASH_PLACEHOLDER = "(_)";
private String name;
private String[] profiles = new String[0];
private String label;
private List<PropertySource> propertySources = new ArrayList<>();
private String version;
private String state;
public Environment(String name, String... profiles) {
this(name, profiles, "master", null, null);
}
/**
* Copies all fields except propertySources.
* @param env Spring Environment
*/
public Environment(Environment env) {
this(env.getName(), env.getProfiles(), env.getLabel(), env.getVersion(), env.getState());
}
@JsonCreator
public Environment(@JsonProperty("name") String name, @JsonProperty("profiles") String[] profiles,
@JsonProperty("label") String label, @JsonProperty("version") String version,
@JsonProperty("state") String state) {
super();
this.name = name;
this.profiles = profiles;
this.label = label;
this.version = version;
this.state = state;
}
/**
* Utility method for normalizing names and labels.
* @param s String to normalize.
* @return if s contains (_), replace with slash.
*/
public static String normalize(String s) {
if (s != null && s.contains(SLASH_PLACEHOLDER)) {
// "(_)" is uncommon in a git repo name, but "/" cannot be matched
// by Spring MVC
return s.replace(SLASH_PLACEHOLDER, "/");
}
return s;
}
/**
* Utility method for denormalizing names and labels.
* @param s String to denormalize.
* @return if s contains slash, replace with (_).
*/
public static String denormalize(String s) {
if (s != null && s.contains("/")) {
return s.replace("/", SLASH_PLACEHOLDER);
}
return s;
}
public void add(PropertySource propertySource) {
this.propertySources.add(propertySource);
}
public void addAll(List<PropertySource> propertySources) {
this.propertySources.addAll(propertySources);
}
public void addFirst(PropertySource propertySource) {
this.propertySources.add(0, propertySource);
}
public List<PropertySource> getPropertySources() {
return this.propertySources;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getLabel() {
return this.label;
}
public void setLabel(String label) {
this.label = label;
}
public String[] getProfiles() {
return this.profiles;
}
public void setProfiles(String[] profiles) {
this.profiles = profiles;
}
public String getVersion() {
return this.version;
}
public void setVersion(String version) {
this.version = version;
}
public String getState() {
return this.state;
}
public void setState(String state) {
this.state = state;
}
@Override
public String toString() {
return "Environment [name=" + this.name + ", profiles=" + Arrays.asList(this.profiles) + ", label=" + this.label
+ ", propertySources=" + this.propertySources + ", version=" + this.version + ", state=" + this.state
+ "]";
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2012-2017 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.environment;
/**
* Media types that can be consumed and produced by Environment endpoints.
*
* @author Spencer Gibb
* @since 2.0.0
*/
public final class EnvironmentMediaType {
/**
* Constant for the Config Server V1 media type.
*/
public static final String V1_JSON = "application/vnd.spring-cloud.config-server.v1+json";
/**
* Constant for the Config Server V2 media type.
*/
public static final String V2_JSON = "application/vnd.spring-cloud.config-server.v2+json";
private EnvironmentMediaType() {
}
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright 2013-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.environment;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Simple plain text serializable encapsulation of a named source of key-value pairs.
* Basically a DTO for {@link PropertySource}, but also applicable outside the domain of a
* Spring application.
*
* @author Dave Syer
*/
public class PropertySource {
private String name;
private Map<?, ?> source;
private org.springframework.core.env.PropertySource<?> originalPropertySource;
@JsonCreator
public PropertySource(@JsonProperty("name") String name, @JsonProperty("source") Map<?, ?> source) {
this.name = name;
this.source = source;
}
@JsonIgnore
public PropertySource(String name, Map<?, ?> source,
org.springframework.core.env.PropertySource<?> originalPropertySource) {
this.name = name;
this.source = source;
this.originalPropertySource = originalPropertySource;
}
public String getName() {
return this.name;
}
public Map<?, ?> getSource() {
return this.source;
}
@JsonIgnore
public org.springframework.core.env.PropertySource<?> getOriginalPropertySource() {
return this.originalPropertySource;
}
@Override
public String toString() {
return "PropertySource [name=" + this.name + "]";
}
}

View File

@@ -1,60 +0,0 @@
/*
* Copyright 2013-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.environment;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A description of a property's value, including its origin if available.
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public final class PropertyValueDescriptor {
private final Object value;
private String origin;
@JsonCreator
public PropertyValueDescriptor(@JsonProperty("value") Object value, @JsonProperty("origin") String origin) {
this.value = value;
this.origin = origin;
}
public Object getValue() {
return this.value;
}
public String getOrigin() {
return this.origin;
}
public void setOrigin(String origin) {
this.origin = origin;
}
/**
* Places in config server call to string expecting to get the value.
* @return the value toString if not null.
*/
@Override
public String toString() {
return this.value == null ? null : this.value.toString();
}
}

View File

@@ -1,24 +0,0 @@
# Bootstrap components
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.config.client.ConfigServiceBootstrapConfiguration,\
org.springframework.cloud.config.client.DiscoveryClientConfigServiceBootstrapConfiguration
# Environment PostProcessor
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.config.client.ConfigServerConfigDataMissingEnvironmentPostProcessor
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.cloud.config.client.ConfigServerConfigDataMissingEnvironmentPostProcessor.ImportExceptionFailureAnalyzer,\
org.springframework.cloud.config.client.diagnostics.analyzer.InvalidApplicationNameExceptionFailureAnalyzer
# ConfigData Location Resolvers
org.springframework.boot.context.config.ConfigDataLocationResolver=\
org.springframework.cloud.config.client.ConfigServerConfigDataLocationResolver
# ConfigData Loaders
org.springframework.boot.context.config.ConfigDataLoader=\
org.springframework.cloud.config.client.ConfigServerConfigDataLoader
# Spring Boot BootstrapRegistryInitializers
org.springframework.boot.BootstrapRegistryInitializer=\
org.springframework.cloud.config.client.ConfigClientRetryBootstrapper

View File

@@ -1,2 +0,0 @@
org.springframework.aot.hint.RuntimeHintsRegistrar=\
org.springframework.cloud.config.client.ConfigClientHints

View File

@@ -1 +0,0 @@
org.springframework.cloud.config.client.ConfigClientAutoConfiguration

View File

@@ -1,127 +0,0 @@
/*
* 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.client;
import java.util.Arrays;
import java.util.Collections;
import org.junit.jupiter.api.AfterEach;
import org.mockito.Mockito;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.commons.util.UtilAutoConfiguration;
import org.springframework.cloud.config.client.ConfigClientProperties.Credentials;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.springframework.cloud.config.client.ConfigClientProperties.Discovery.DEFAULT_CONFIG_SERVER;
public abstract class BaseDiscoveryClientConfigServiceBootstrapConfigurationTests {
protected AnnotationConfigApplicationContext context;
protected DiscoveryClient client = Mockito.mock(DiscoveryClient.class);
protected ServiceInstance info = new DefaultServiceInstance("app:8877", "app", "foo", 8877, false);
@AfterEach
public void close() {
if (this.context != null) {
this.context.close();
}
}
void givenDiscoveryClientReturnsNoInfo() {
given(this.client.getInstances(DEFAULT_CONFIG_SERVER)).willReturn(Collections.<ServiceInstance>emptyList());
}
void givenDiscoveryClientReturnsInfo() {
given(this.client.getInstances(DEFAULT_CONFIG_SERVER)).willReturn(Collections.singletonList(this.info));
}
void givenDiscoveryClientReturnsInfoForMultipleInstances(ServiceInstance info1, ServiceInstance info2) {
given(this.client.getInstances(DEFAULT_CONFIG_SERVER)).willReturn(Arrays.asList(info1, info2));
}
void givenDiscoveryClientReturnsInfoOnThirdTry() {
given(this.client.getInstances(DEFAULT_CONFIG_SERVER)).willReturn(Collections.<ServiceInstance>emptyList())
.willReturn(Collections.<ServiceInstance>emptyList()).willReturn(Collections.singletonList(this.info));
}
void expectDiscoveryClientConfigServiceBootstrapConfigurationIsSetup() {
assertThat(this.context.getBeanNamesForType(DiscoveryClientConfigServiceBootstrapConfiguration.class).length)
.isEqualTo(1);
}
void expectConfigClientPropertiesHasDefaultConfiguration() {
expectConfigClientPropertiesHasConfiguration("http://localhost:8888");
}
void expectConfigClientPropertiesHasConfigurationFromEureka() {
expectConfigClientPropertiesHasConfiguration("http://foo:8877/");
}
void expectConfigClientPropertiesHasConfiguration(final String expectedUri) {
ConfigClientProperties properties = this.context.getBean(ConfigClientProperties.class);
Credentials credentials = properties.getCredentials(0);
assertThat(credentials.getUri()).isEqualTo(expectedUri);
}
void expectConfigClientPropertiesHasMultipleUris(final String expectedUri1, final String expectedUri2) {
ConfigClientProperties properties = this.context.getBean(ConfigClientProperties.class);
assertThat(properties.getUri().length).isEqualTo(2);
Credentials credentials1 = properties.getCredentials(0);
Credentials credentials2 = properties.getCredentials(1);
assertThat(credentials1.getUri()).isEqualTo(expectedUri1);
assertThat(credentials2.getUri()).isEqualTo(expectedUri2);
}
void verifyDiscoveryClientCalledThreeTimes() {
verify(this.client, times(3)).getInstances(DEFAULT_CONFIG_SERVER);
}
void verifyDiscoveryClientCalledOnce() {
verify(this.client).getInstances(DEFAULT_CONFIG_SERVER);
}
void setup(String... env) {
setup(true, true, env);
}
void setup(boolean refresh, boolean registerDiscoveryClient, String... env) {
this.context = new AnnotationConfigApplicationContext();
TestPropertyValues.of(env).applyTo(this.context);
TestPropertyValues.of("eureka.client.enabled=false").applyTo(this.context);
if (registerDiscoveryClient) {
this.context.getDefaultListableBeanFactory().registerSingleton("discoveryClient", this.client);
}
this.context.register(UtilAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class,
DiscoveryClientConfigServiceBootstrapConfiguration.class, ConfigServiceBootstrapConfiguration.class,
ConfigClientProperties.class);
if (refresh) {
this.context.refresh();
}
}
}

View File

@@ -1,103 +0,0 @@
/*
* Copyright 2002-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.client;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.config.client.validation.InvalidApplicationNameException;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
public class ConfigClientAutoConfigurationTests {
@Test
public void sunnyDay() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
ConfigClientAutoConfiguration.class);
assertThat(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, ConfigClientProperties.class).length)
.isEqualTo(1);
context.close();
}
@Test
public void withParent() {
ConfigurableApplicationContext context = new SpringApplicationBuilder(ConfigClientAutoConfiguration.class)
.child(Object.class).web(WebApplicationType.NONE).properties("spring.cloud.bootstrap.enabled=true")
.run();
assertThat(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, ConfigClientProperties.class).length)
.isEqualTo(1);
context.close();
}
@Test
public void invalidApplicationNameOverrideWithFailFastEnabledFailsToStartup() {
SpringApplication application = new SpringApplicationBuilder(ConfigClientAutoConfiguration.class)
.web(WebApplicationType.NONE).properties("spring.cloud.config.fail-fast=true",
"spring.cloud.bootstrap.enabled=true", "spring.cloud.config.name=application-service")
.application();
assertThatThrownBy(application::run).isInstanceOf(InvalidApplicationNameException.class).extracting("value")
.isEqualTo("application-service");
}
@Test
public void invalidApplicationNameOverrideWithFailFastDisabledStartsUpButNoConfigServerPropertiesAreLoaded() {
SpringApplication application = new SpringApplicationBuilder(ConfigClientAutoConfiguration.class)
.web(WebApplicationType.NONE)
.properties("spring.cloud.config.name=application-service", "spring.cloud.bootstrap.enabled=true")
.application();
ConfigurableApplicationContext context = application.run();
assertThat(context.getEnvironment().getPropertySources().get("configService")).isNull();
context.close();
}
@Test
public void invalidApplicationNameWithFailFastEnabledFailsToStartup() {
SpringApplication application = new SpringApplicationBuilder(ConfigClientAutoConfiguration.class)
.web(WebApplicationType.NONE).properties("spring.cloud.config.fail-fast=true",
"spring.cloud.bootstrap.enabled=true", "spring.application.name=application-service")
.application();
assertThatThrownBy(application::run).isInstanceOf(InvalidApplicationNameException.class).extracting("value")
.isEqualTo("application-service");
}
@Test
public void invalidApplicationNameWithFailFastDisabledStartsUpButNoConfigServerPropertiesAreLoaded() {
SpringApplication application = new SpringApplicationBuilder(ConfigClientAutoConfiguration.class)
.web(WebApplicationType.NONE)
.properties("spring.application.name=application-service", "spring.cloud.bootstrap.enabled=true")
.application();
ConfigurableApplicationContext context = application.run();
assertThat(context.getEnvironment().getPropertySources().get("configService")).isNull();
context.close();
}
}

View File

@@ -1,96 +0,0 @@
/*
* Copyright 2013-2023 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.client;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatchers;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* This test verifies that we are recreating ConfigClientProperties every time we receive
* a request to load configuration data from the config server. Since
* optional:configserver is last in the list of spring.config.import it will be processed
* before applicationname.yaml. applicationname.yaml contains the value for
* spring.application.name but since Boot has not processed that import yet the initial
* request to the config server will use the default application name. After the config
* server import is processed Boot will then load applicationname.yaml and
* spring.application.name will be set.
*
* At this point Boot has collected all active profiles so it will load all
* spring.config.import statements again with active profiles. The subsequent call then
* will not have spring.application.name set in the context so the config server config
* data loader will make a request to the config server with the correct application name.
*
* @author Ryan Baxter
*/
public class ConfigClientConfigDataLoaderTest {
ConfigurableApplicationContext context;
@Test
void context() {
RestTemplate rest = mock(RestTemplate.class);
Environment environment = new Environment("test", "default");
ResponseEntity<Environment> responseEntity = mock(ResponseEntity.class);
when(responseEntity.getStatusCode()).thenReturn(HttpStatus.OK);
when(responseEntity.getBody()).thenReturn(environment);
when(rest.exchange(eq("http://localhost:8888/{name}/{profile}"), eq(HttpMethod.GET),
ArgumentMatchers.any(HttpEntity.class), eq(Environment.class), eq("application"),
ArgumentMatchers.<String>any())).thenReturn(responseEntity);
when(rest.exchange(eq("http://localhost:8888/{name}/{profile}"), eq(HttpMethod.GET),
ArgumentMatchers.any(HttpEntity.class), eq(Environment.class), eq("foo"),
ArgumentMatchers.<String>any())).thenReturn(responseEntity);
context = setup(rest).run();
verify(rest).exchange(eq("http://localhost:8888/{name}/{profile}"), eq(HttpMethod.GET),
ArgumentMatchers.any(HttpEntity.class), eq(Environment.class), eq("application"),
ArgumentMatchers.<String>any());
verify(rest, times(1)).exchange(eq("http://localhost:8888/{name}/{profile}"), eq(HttpMethod.GET),
ArgumentMatchers.any(HttpEntity.class), eq(Environment.class), eq("foo"),
ArgumentMatchers.<String>any());
}
@AfterEach
void after() {
context.close();
}
SpringApplicationBuilder setup(RestTemplate restTemplate, String... env) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(
DiscoveryClientConfigDataConfigurationTests.TestConfig.class)
.properties("spring.config.import=classpath:applicationname.yaml, optional:configserver:");
builder.addBootstrapRegistryInitializer(
registry -> registry.register(RestTemplate.class, context -> restTemplate));
return builder;
}
}

View File

@@ -1,186 +0,0 @@
/*
* Copyright 2013-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.client;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.cloud.config.client.ConfigClientProperties.Credentials;
import org.springframework.cloud.config.client.ConfigClientProperties.MultipleUriStrategy;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Dave Syer
*
*/
public class ConfigClientPropertiesTests {
private ConfigClientProperties locator = new ConfigClientProperties(new StandardEnvironment());
@Test
public void vanilla() {
this.locator.setUri(new String[] { "http://localhost:9999" });
this.locator.setPassword("secret");
Credentials credentials = this.locator.getCredentials(0);
assertThat(credentials.getUri()).isEqualTo("http://localhost:9999");
assertThat(credentials.getUsername()).isEqualTo("user");
assertThat(credentials.getPassword()).isEqualTo("secret");
}
@Test
public void uriCreds() {
this.locator.setUri(new String[] { "http://foo:bar@localhost:9999" });
Credentials credentials = this.locator.getCredentials(0);
assertThat(credentials.getUri()).isEqualTo("http://localhost:9999");
assertThat(credentials.getUsername()).isEqualTo("foo");
assertThat(credentials.getPassword()).isEqualTo("bar");
}
@Test
public void uriCredsWithAtInPassword() {
this.locator.setUri(new String[] { "http://foo:bar%40@localhost:9999" });
Credentials credentials = this.locator.getCredentials(0);
assertThat(credentials.getUri()).isEqualTo("http://localhost:9999");
assertThat(credentials.getUsername()).isEqualTo("foo");
assertThat(credentials.getPassword()).isEqualTo("bar@");
}
@Test
public void explicitPassword() {
this.locator.setUri(new String[] { "http://foo:bar@localhost:9999" });
this.locator.setPassword("secret");
Credentials credentials = this.locator.getCredentials(0);
assertThat(credentials.getUri()).isEqualTo("http://localhost:9999");
assertThat(credentials.getUsername()).isEqualTo("foo");
assertThat(credentials.getPassword()).isEqualTo("secret");
}
@Test
public void testIfNoColonPresentInUriCreds() {
this.locator.setUri(new String[] { "http://foobar@localhost:9999" });
this.locator.setPassword("secret");
Credentials credentials = this.locator.getCredentials(0);
assertThat(credentials.getUri()).isEqualTo("http://localhost:9999");
assertThat(credentials.getUsername()).isEqualTo("foobar");
assertThat(credentials.getPassword()).isEqualTo("secret");
}
@Test
public void testIfColonPresentAtTheEndInUriCreds() {
this.locator.setUri(new String[] { "http://foobar:@localhost:9999" });
this.locator.setPassword("secret");
Credentials credentials = this.locator.getCredentials(0);
assertThat(credentials.getUri()).isEqualTo("http://localhost:9999");
assertThat(credentials.getUsername()).isEqualTo("foobar");
assertThat(credentials.getPassword()).isEqualTo("secret");
}
@Test
public void testIfColonPresentAtTheStartInUriCreds() {
this.locator.setUri(new String[] { "http://:foobar@localhost:9999" });
Credentials credentials = this.locator.getCredentials(0);
assertThat(credentials.getUri()).isEqualTo("http://localhost:9999");
assertThat(credentials.getUsername()).isEqualTo("");
assertThat(credentials.getPassword()).isEqualTo("foobar");
}
@Test
public void testIfColonPresentAtTheStartAndEndInUriCreds() {
this.locator.setUri(new String[] { "http://:foobar:@localhost:9999" });
Credentials credentials = this.locator.getCredentials(0);
assertThat(credentials.getUri()).isEqualTo("http://localhost:9999");
assertThat(credentials.getUsername()).isEqualTo("");
assertThat(credentials.getPassword()).isEqualTo("foobar:");
}
@Test
public void testIfSpacePresentAsUriCreds() {
this.locator.setUri(new String[] { "http:// @localhost:9999" });
this.locator.setPassword("secret");
Credentials credentials = this.locator.getCredentials(0);
assertThat(credentials.getUri()).isEqualTo("http://localhost:9999");
assertThat(credentials.getUsername()).isEqualTo(" ");
assertThat(credentials.getPassword()).isEqualTo("secret");
}
@Test
public void changeNameInOverride() {
this.locator.setName("one");
ConfigurableEnvironment environment = new StandardEnvironment();
TestPropertyValues.of("spring.application.name:two").applyTo(environment);
ConfigClientProperties override = this.locator.override(environment);
assertThat(override.getName()).isEqualTo("two");
}
@Test
public void testThatExplicitUsernamePasswordTakePrecedence() {
ConfigClientProperties properties = new ConfigClientProperties(new MockEnvironment());
properties.setUri(new String[] { "https://userInfoName:userInfoPW@localhost:8888/" });
properties.setUsername("explicitName");
properties.setPassword("explicitPW");
Credentials credentials = properties.getCredentials(0);
assertThat(credentials.getPassword()).isEqualTo("explicitPW");
assertThat(credentials.getUsername()).isEqualTo("explicitName");
}
@Test
public void checkIfExceptionThrownForNegativeIndex() {
Assertions.assertThatThrownBy(() -> {
this.locator.setUri(new String[] { "http://localhost:8888", "http://localhost:8889" });
Credentials credentials = this.locator.getCredentials(-1);
}).isInstanceOf(IllegalStateException.class).hasMessageContaining("Trying to access an invalid array index");
}
@Test
public void checkIfExceptionThrownForPositiveInvalidIndex() {
Assertions.assertThatThrownBy(() -> {
this.locator.setUri(new String[] { "http://localhost:8888", "http://localhost:8889" });
Credentials credentials = this.locator.getCredentials(3);
}).isInstanceOf(IllegalStateException.class).hasMessageContaining("Trying to access an invalid array index");
}
@Test
public void checkIfExceptionThrownForIndexEqualToLength() {
Assertions.assertThatThrownBy(() -> {
this.locator.setUri(new String[] { "http://localhost:8888", "http://localhost:8889" });
Credentials credentials = this.locator.getCredentials(2);
}).isInstanceOf(IllegalStateException.class).hasMessageContaining("Trying to access an invalid array index");
}
@Test
public void testThatDefaultMultipleUriStrategyIsAlways() {
ConfigClientProperties properties = new ConfigClientProperties(new MockEnvironment());
assertThat(properties.getMultipleUriStrategy()).isNotNull();
assertThat(properties.getMultipleUriStrategy().name()).isEqualTo(MultipleUriStrategy.ALWAYS.name());
}
@Test
public void testThatExplicitMultipleUriStrategyTakesPrecedence() {
ConfigClientProperties properties = new ConfigClientProperties(new MockEnvironment());
properties.setMultipleUriStrategy(MultipleUriStrategy.CONNECTION_TIMEOUT_ONLY);
assertThat(properties.getMultipleUriStrategy()).isNotNull();
assertThat(properties.getMultipleUriStrategy().name())
.isEqualTo(MultipleUriStrategy.CONNECTION_TIMEOUT_ONLY.name());
}
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright 2013-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.client;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Spencer Gibb
*/
public class ConfigClientWatchTests {
@Test
public void stateChangedWorks() {
ConfigClientWatch watch = new ConfigClientWatch(null);
assertThat(watch.stateChanged(null, "1")).isTrue();
assertThat(watch.stateChanged("1", "2")).isTrue();
assertThat(watch.stateChanged("1", null)).isTrue();
assertThat(watch.stateChanged("1", "1")).isFalse();
watch.close();
}
}

View File

@@ -1,45 +0,0 @@
/*
* 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.client;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
public class ConfigServerBootstrapConfigurationTests {
@Test
public void withHealthIndicator() {
ConfigurableApplicationContext context = new SpringApplicationBuilder(
PropertySourceBootstrapConfiguration.class, ConfigServiceBootstrapConfiguration.class)
.child(ConfigClientAutoConfiguration.class).properties("spring.cloud.bootstrap.enabled=true")
.web(WebApplicationType.NONE).run();
assertThat(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, ConfigClientProperties.class).length)
.isEqualTo(1);
assertThat(
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, ConfigServerHealthIndicator.class).length)
.isEqualTo(1);
context.close();
}
}

View File

@@ -1,180 +0,0 @@
/*
* Copyright 2013-2020 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.client;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.BootstrapContext;
import org.springframework.boot.BootstrapRegistry;
import org.springframework.boot.BootstrapRegistryInitializer;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.config.ConfigData;
import org.springframework.boot.context.config.ConfigData.Option;
import org.springframework.boot.context.config.ConfigData.Options;
import org.springframework.boot.context.properties.bind.BindContext;
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.context.properties.source.ConfigurationPropertyName;
import org.springframework.cloud.config.client.ConfigServerBootstrapper.LoaderInterceptor;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.PropertySource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
public class ConfigServerConfigDataCustomizationIntegrationTests {
@Test
void customizableRestTemplate() {
ConfigurableApplicationContext context = null;
try {
BindHandlerBootstrapper bindHandlerBootstrapper = new BindHandlerBootstrapper();
context = new SpringApplicationBuilder(TestConfig.class)
.addBootstrapRegistryInitializer(bindHandlerBootstrapper)
.addBootstrapRegistryInitializer(ConfigServerBootstrapper.create()
.withLoaderInterceptor(new Interceptor()).withRestTemplateFactory(this::restTemplate))
.addBootstrapRegistryInitializer(registry -> registry.addCloseListener(event -> {
BootstrapContext bootstrapContext = event.getBootstrapContext();
ConfigurableListableBeanFactory beanFactory = event.getApplicationContext().getBeanFactory();
RestTemplate restTemplate = bootstrapContext.get(RestTemplate.class);
beanFactory.registerSingleton("holder", new RestTemplateHolder(restTemplate));
beanFactory.registerSingleton("interceptor", bootstrapContext.get(LoaderInterceptor.class));
})).run("--spring.config.import=optional:configserver:", "--custom.prop=customval",
"--spring.cloud.config.label=mylabel");
RestTemplateHolder holder = context.getBean(RestTemplateHolder.class);
assertThat(holder).isNotNull();
assertThat(holder.restTemplate).isInstanceOf(CustomRestTemplate.class);
CustomRestTemplate custom = (CustomRestTemplate) holder.restTemplate;
assertThat(custom.customProp).isEqualTo("customval");
LoaderInterceptor loaderInterceptor = context.getBean(LoaderInterceptor.class);
assertThat(loaderInterceptor).isNotNull().isInstanceOf(Interceptor.class);
Interceptor interceptor = (Interceptor) loaderInterceptor;
assertThat(interceptor.applied).isTrue();
assertThat(interceptor.hasBinder).isTrue();
assertThat(bindHandlerBootstrapper.onSuccessCount).isGreaterThan(1);
}
finally {
if (context != null) {
context.close();
}
}
}
CustomRestTemplate restTemplate(BootstrapContext context) {
ConfigClientProperties properties = context.get(ConfigClientProperties.class);
String custom = context.get(Binder.class).bind("custom.prop", String.class).orElse("default-custom-prop");
return new CustomRestTemplate(custom);
}
@SpringBootConfiguration
@EnableAutoConfiguration
static class TestConfig {
}
static class Interceptor implements LoaderInterceptor {
boolean applied;
boolean hasBinder;
@Override
public ConfigData apply(ConfigServerBootstrapper.LoadContext context) {
applied = true;
hasBinder = context.getBinder() != null;
ConfigData configData = context.getInvocation().apply(context.getLoaderContext(), context.getResource());
assertThat(configData).as("ConfigData was null for location %s", context.getResource()).isNotNull();
if (!context.getResource().isProfileSpecific()) {
assertThat(configData.getPropertySources()).hasSize(1);
PropertySource<?> propertySource = configData.getPropertySources().iterator().next();
Options options = configData.getOptions(propertySource);
assertThat(options).as("ConfigData.options was null for location %s property source %s",
context.getResource(), propertySource.getName()).isNotNull();
assertThat(options.contains(Option.IGNORE_IMPORTS)).isTrue();
assertThat(options.contains(Option.PROFILE_SPECIFIC)).isFalse();
}
else {
assertThat(configData.getPropertySources()).hasSize(1);
}
return configData;
}
}
static class RestTemplateHolder {
final RestTemplate restTemplate;
RestTemplateHolder(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
}
static class CustomRestTemplate extends RestTemplate {
private final String customProp;
CustomRestTemplate(String customProp) {
this.customProp = customProp;
}
@Override
@SuppressWarnings("unchecked")
public <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
Class<T> responseType, Object... uriVariables) throws RestClientException {
ResponseEntity<T> response = (ResponseEntity<T>) ResponseEntity.of(Optional.of(new Environment("test")));
return response;
}
}
static class BindHandlerBootstrapper implements BootstrapRegistryInitializer {
private int onSuccessCount = 0;
@Override
public void initialize(BootstrapRegistry registry) {
registry.register(BindHandler.class, context -> new BindHandler() {
@Override
public Object onSuccess(ConfigurationPropertyName name, Bindable<?> target, BindContext context,
Object result) {
onSuccessCount++;
return result;
}
});
}
}
}

View File

@@ -1,672 +0,0 @@
/*
* Copyright 2013-2023 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.client;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.Captor;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
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.test.util.TestPropertyValues;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.environment.PropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.mock.http.client.MockClientHttpRequest;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
import static java.lang.String.format;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.cloud.config.client.ConfigClientProperties.AUTHORIZATION;
import static org.springframework.cloud.config.environment.EnvironmentMediaType.V2_JSON;
/**
* Unit Test for {@link ConfigServerConfigDataLoader}.
*
* <p>
* This test was based on {@link ConfigServicePropertySourceLocatorTests}. The
* {@link ConfigServicePropertySourceLocator} is used only when legacy bootstrap is in
* use. Otherwise, {@link ConfigServerConfigDataLoader} is used.
* </p>
*
* @author Marnee DeRider
*/
public class ConfigServerConfigDataLoaderTests {
private static final Log logger = LogFactory.getLog(ConfigServerConfigDataLoaderTests.class);
private static final String LABEL = "main";
private static final String NAME = "application";
private static final String PROFILES = "dev";
private static final String URI_TEMPLATE = "%s/%s/%s/%s";
@Captor
private ArgumentCaptor<HttpEntity<Void>> httpEntityArgumentCaptor;
private ConfigurableBootstrapContext bootstrapContext;
private ConfigDataLoaderContext context;
private ConfigurableEnvironment environment;
private ConfigServerConfigDataLoader loader;
private ConfigClientProperties properties;
private ConfigServerConfigDataResource resource;
private RestTemplate restTemplate;
@BeforeEach
public void init() {
MockitoAnnotations.openMocks(this);
environment = new StandardEnvironment();
loader = new ConfigServerConfigDataLoader(destination -> logger);
restTemplate = mock(RestTemplate.class);
context = mock(ConfigDataLoaderContext.class);
bootstrapContext = mock(ConfigurableBootstrapContext.class);
resource = mock(ConfigServerConfigDataResource.class);
properties = new ConfigClientProperties(this.environment);
properties.setName(NAME);
properties.setLabel(LABEL);
when(context.getBootstrapContext()).thenReturn(bootstrapContext);
when(bootstrapContext.get(ConfigClientRequestTemplateFactory.class))
.thenReturn(mock(ConfigClientRequestTemplateFactory.class));
when(bootstrapContext.get(RestTemplate.class)).thenReturn(restTemplate);
when(resource.getProperties()).thenReturn(properties);
when(resource.getProfiles()).thenReturn(PROFILES);
}
@SuppressWarnings("unchecked")
@Test
public void sunnyDayWithoutLabel() {
Environment body = new Environment("app", "master");
mockRequestResponseWithoutLabel(new ResponseEntity<>(body, HttpStatus.OK));
properties.setLabel(null);
assertThat(this.loader.load(context, resource)).isNotNull();
Mockito.verify(this.restTemplate).exchange(anyString(), any(HttpMethod.class),
httpEntityArgumentCaptor.capture(), any(Class.class), anyString(), anyString());
HttpEntity<Void> httpEntity = httpEntityArgumentCaptor.getValue();
assertThat(httpEntity.getHeaders().getAccept()).containsExactly(MediaType.parseMediaType(V2_JSON));
}
@Test
public void customMediaType() {
Environment body = new Environment("app", "master");
mockRequestResponseWithoutLabel(new ResponseEntity<>(body, HttpStatus.OK));
properties.setMediaType("application/json");
properties.setLabel(null);
assertThat(loader.load(context, resource)).isNotNull();
Mockito.verify(this.restTemplate).exchange(anyString(), any(HttpMethod.class),
httpEntityArgumentCaptor.capture(), ArgumentMatchers.<Class<Environment>>any(), anyString(),
anyString());
HttpEntity<Void> httpEntity = httpEntityArgumentCaptor.getValue();
assertThat(httpEntity.getHeaders().getAccept()).containsExactly(MediaType.parseMediaType("application/json"));
}
@Test
public void sunnyDayWithLabel() {
Environment body = new Environment("app", "master");
properties.setLabel("v1.0.0");
mockRequestResponseWithLabel(new ResponseEntity<>(body, HttpStatus.OK), "v1.0.0");
TestPropertyValues.of("spring.cloud.config.label:v1.0.0").applyTo(this.environment);
assertThat(this.loader.load(context, resource)).isNotNull();
}
@Test
public void sunnyDayWithLabelThatContainsASlash() {
Environment body = new Environment("app", "master");
String label = "release(_)v1.0.0";
mockRequestResponseWithLabel(new ResponseEntity<>(body, HttpStatus.OK), label);
properties.setLabel(label);
TestPropertyValues.of("spring.cloud.config.label:release/v1.0.0").applyTo(this.environment);
assertThat(this.loader.load(context, resource)).isNotNull();
}
@Test
public void failFast() throws Exception {
ClientHttpRequestFactory requestFactory = mock(ClientHttpRequestFactory.class);
mockRequestResponse(requestFactory, null, HttpStatus.INTERNAL_SERVER_ERROR);
RestTemplate restTemplate = new RestTemplate(requestFactory);
properties.setFailFast(true);
when(bootstrapContext.get(RestTemplate.class)).thenReturn(restTemplate);
ConfigClientFailFastException exception = Assertions.assertThrows(ConfigClientFailFastException.class,
() -> this.loader.load(context, resource));
assertThat(exception.getCause()).isInstanceOf(HttpServerErrorException.class);
assertThat(exception.getMessage()).contains("fail fast property is set");
}
@Test
public void failFastWhenNotFound() throws Exception {
ClientHttpRequestFactory requestFactory = mock(ClientHttpRequestFactory.class);
mockRequestResponse(requestFactory, null, HttpStatus.NOT_FOUND);
RestTemplate restTemplate = new RestTemplate(requestFactory);
properties.setFailFast(true);
properties.setLabel("WeSetUpToReturn_NOT_FOUND_ForThisLabel");
when(bootstrapContext.get(RestTemplate.class)).thenReturn(restTemplate);
ConfigClientFailFastException exception = Assertions.assertThrows(ConfigClientFailFastException.class,
() -> this.loader.load(context, resource));
assertThat(exception.getMessage()).contains(
"fail fast property is set, failing: None of labels [WeSetUpToReturn_NOT_FOUND_ForThisLabel] found");
}
@Test
public void failFastWhenRequestTimesOut() {
mockRequestTimedOut();
properties.setFailFast(true);
ConfigClientFailFastException exception = Assertions.assertThrows(ConfigClientFailFastException.class,
() -> this.loader.load(context, resource));
assertThat(exception.getCause()).isExactlyInstanceOf(ResourceAccessException.class);
assertThat(exception.getMessage()).contains("fail fast property is set");
}
@Test
public void failFastWhenBothPasswordAndAuthorizationPropertiesSet() throws Exception {
ClientHttpRequestFactory requestFactory = mock(ClientHttpRequestFactory.class);
ClientHttpRequest request = mock(ClientHttpRequest.class);
when(requestFactory.createRequest(any(URI.class), any(HttpMethod.class))).thenReturn(request);
properties.setFailFast(true);
properties.setUsername("username");
properties.setPassword("password");
properties.getHeaders().put(AUTHORIZATION, "Basic dXNlcm5hbWU6cGFzc3dvcmQNCg==");
IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class,
() -> this.loader.load(context, resource));
assertThat(exception.getMessage())
.contains("Could not locate PropertySource and the fail fast property is set, failing");
}
@Test
public void interceptorShouldAddHeadersWhenHeadersPropertySet() throws Exception {
MockClientHttpRequest request = new MockClientHttpRequest();
ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class);
byte[] body = new byte[] {};
Map<String, String> headers = new HashMap<>();
headers.put("X-Example-Version", "2.1");
new ConfigClientRequestTemplateFactory.GenericRequestHeaderInterceptor(headers).intercept(request, body,
execution);
Mockito.verify(execution).execute(request, body);
assertThat(request.getHeaders().getFirst("X-Example-Version")).isEqualTo("2.1");
}
@Test
public void shouldAddAuthorizationHeaderWhenPasswordSet() {
HttpHeaders headers = new HttpHeaders();
String username = "user";
String password = "pass";
factory(properties).addAuthorizationToken(headers, username, password);
assertThat(headers).hasSize(1);
}
@Test
public void shouldAddAuthorizationHeaderWhenAuthorizationSet() {
HttpHeaders headers = new HttpHeaders();
properties.getHeaders().put(AUTHORIZATION, "Basic dXNlcm5hbWU6cGFzc3dvcmQNCg==");
String username = "user";
factory(properties).addAuthorizationToken(headers, username, null);
assertThat(headers).hasSize(1);
}
@Test
public void shouldThrowExceptionWhenPasswordAndAuthorizationBothSet() {
HttpHeaders headers = new HttpHeaders();
properties.getHeaders().put(AUTHORIZATION, "Basic dXNlcm5hbWU6cGFzc3dvcmQNCg==");
String username = "user";
String password = "pass";
IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class,
() -> factory(properties).addAuthorizationToken(headers, username, password));
assertThat(exception.getMessage()).contains("You must set either 'password' or 'authorization'");
}
@Test
public void shouldThrowExceptionWhenNegativeReadTimeoutSet() {
properties.setRequestReadTimeout(-1);
IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class,
() -> factory(properties).create());
assertThat(exception.getMessage()).contains("Invalid Value for Read Timeout set.");
}
@Test
public void shouldThrowExceptionWhenNegativeConnectTimeoutSet() {
properties.setRequestConnectTimeout(-1);
IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class,
() -> factory(properties).create());
assertThat(exception.getMessage()).contains("Invalid Value for Connect Timeout set.");
}
@Test
public void shouldNotUseNextUriFor_400_And_CONNECTION_TIMEOUT_ONLY_Strategy() throws Exception {
assertNextUriIsNotTriedForClientError(ConfigClientProperties.MultipleUriStrategy.CONNECTION_TIMEOUT_ONLY,
HttpStatus.BAD_REQUEST);
}
@Test
public void shouldUseNextUriFor_400_And_ALWAYS_Strategy() throws Exception {
assertNextUriIsTried(ConfigClientProperties.MultipleUriStrategy.ALWAYS, HttpStatus.BAD_REQUEST);
}
@Test
public void shouldNotUseNextUriFor_404_And_CONNECTION_TIMEOUT_ONLY_Strategy() throws Exception {
assertNextUriIsNotTriedForNotFoundError(ConfigClientProperties.MultipleUriStrategy.CONNECTION_TIMEOUT_ONLY,
HttpStatus.NOT_FOUND);
}
@Test
public void shouldUseNextUriFor_404_And_ALWAYS_Strategy() throws Exception {
assertNextUriIsTried(ConfigClientProperties.MultipleUriStrategy.ALWAYS, HttpStatus.NOT_FOUND);
}
@Test
public void shouldNotUseNextUriFor_500_And_CONNECTION_TIMEOUT_ONLY_Strategy() throws Exception {
assertNextUriIsNotTriedForServerError(ConfigClientProperties.MultipleUriStrategy.CONNECTION_TIMEOUT_ONLY,
HttpStatus.INTERNAL_SERVER_ERROR);
}
@Test
public void shouldUseNextUriFor_500_And_ALWAYS_Strategy() throws Exception {
assertNextUriIsTried(ConfigClientProperties.MultipleUriStrategy.ALWAYS, HttpStatus.INTERNAL_SERVER_ERROR);
}
@Test
public void shouldUseNextUriFor_TimeOut_And_ALWAYS_Strategy() throws Exception {
assertNextUriIsTriedOnTimeout(ConfigClientProperties.MultipleUriStrategy.ALWAYS);
}
@Test
public void shouldUseNextUriFor_TimeOut_And_CONNECTION_TIMEOUT_ONLY_Strategy() throws Exception {
assertNextUriIsTriedOnTimeout(ConfigClientProperties.MultipleUriStrategy.CONNECTION_TIMEOUT_ONLY);
}
@Test
public void shouldNotUseNextUriWhenOneIsSuccessful() throws Exception {
// Set up with three URIs.
String badURI1 = "http://baduri1";
String goodURI = "http://localhost:8888";
String badURI2 = "http://baduri2";
String[] uris = new String[] { badURI1, goodURI, badURI2 };
properties.setUri(uris);
properties.setFailFast(true);
properties.setMultipleUriStrategy(ConfigClientProperties.MultipleUriStrategy.ALWAYS);
this.loader = new ConfigServerConfigDataLoader(destination -> logger);
ClientHttpRequestFactory requestFactory = mock(ClientHttpRequestFactory.class);
RestTemplate restTemplate = new RestTemplate(requestFactory);
when(bootstrapContext.get(RestTemplate.class)).thenReturn(restTemplate);
// Second URI will be successful, and the third one should never be called, so
// locateCollection
// should return a value.
mockRequestResponse(requestFactory, badURI1, HttpStatus.BAD_REQUEST);
mockRequestResponse(requestFactory, goodURI, HttpStatus.OK);
mockRequestResponse(requestFactory, badURI2, HttpStatus.INTERNAL_SERVER_ERROR);
assertThat(this.loader.load(context, resource)).isNotNull();
}
@Test
public void shouldUseMultipleURIs() throws Exception {
// Set up with four URIs. All should be called until one is successful
String badURI1 = "http://baduri1";
String badURI2 = "http://baduri2";
String badURI3 = "http://baduri3";
String goodURI = "http://localhost:8888";
String[] uris = new String[] { badURI1, goodURI, badURI2 };
properties.setUri(uris);
properties.setFailFast(true);
properties.setMultipleUriStrategy(ConfigClientProperties.MultipleUriStrategy.ALWAYS);
this.loader = new ConfigServerConfigDataLoader(destination -> logger);
ClientHttpRequestFactory requestFactory = mock(ClientHttpRequestFactory.class);
RestTemplate restTemplate = new RestTemplate(requestFactory);
when(bootstrapContext.get(RestTemplate.class)).thenReturn(restTemplate);
// Second URI will be successful, and the third one should never be called, so
// locateCollection
// should return a value.
mockRequestResponse(requestFactory, badURI1, HttpStatus.BAD_REQUEST);
mockRequestResponse(requestFactory, badURI2, HttpStatus.INTERNAL_SERVER_ERROR);
mockRequestResponse(requestFactory, badURI3, HttpStatus.NOT_FOUND);
mockRequestResponse(requestFactory, goodURI, HttpStatus.OK);
assertThat(this.loader.load(context, resource)).isNotNull();
}
@Test
void nonProfileSpecific() {
PropertySource p1 = new PropertySource("p1", new HashMap<>());
PropertySource p2 = new PropertySource("p2", new HashMap<>());
ConfigData configData = setupConfigServerConfigDataLoader(Arrays.asList(p1, p2), "application-slash", null);
assertThat(configData.getPropertySources().size()).isEqualTo(3);
assertThat(configData.getOptions(configData.getPropertySources().get(0))
.contains(ConfigData.Option.IGNORE_IMPORTS)).isTrue();
assertThat(configData.getOptions(configData.getPropertySources().get(1))
.contains(ConfigData.Option.IGNORE_IMPORTS)).isTrue();
assertThat(configData.getOptions(configData.getPropertySources().get(2))
.contains(ConfigData.Option.IGNORE_IMPORTS)).isTrue();
}
@Disabled
@Test
// TODO Enable once we have
// https://github.com/spring-cloud/spring-cloud-config/issues/2291
void filterPropertySourcesThatAreNotProfileSpecific() {
PropertySource p1 = new PropertySource("p1", Collections.singletonMap("foo", "bar"));
PropertySource p2 = new PropertySource("p2", Collections.singletonMap("hello", "world"));
ConfigData configData = setupConfigServerConfigDataLoader(Arrays.asList(p1, p2), "application-slash", "dev");
assertThat(configData.getPropertySources().size()).isEqualTo(0);
}
@Disabled
@Test
// TODO Enable once we have
// https://github.com/spring-cloud/spring-cloud-config/issues/2291
void returnPropertySourcesThatAreProfileSpecific() {
PropertySource p1 = new PropertySource("p1-dev", Collections.singletonMap("foo", "bar"));
PropertySource p2 = new PropertySource("p2-dev", Collections.singletonMap("hello", "world"));
List<PropertySource> propertySources = Arrays.asList(p1, p2);
ConfigData configData = setupConfigServerConfigDataLoader(propertySources, "application-slash", "dev");
assertThat(configData.getPropertySources().size()).isEqualTo(2);
assertThat(configData.getOptions(configData.getPropertySources().get(0))
.contains(ConfigData.Option.IGNORE_IMPORTS)).isTrue();
assertThat(configData.getOptions(configData.getPropertySources().get(1))
.contains(ConfigData.Option.IGNORE_IMPORTS)).isTrue();
}
@Disabled
@Test
// TODO Enable once we have
// https://github.com/spring-cloud/spring-cloud-config/issues/2291
void filterPropertySourcesWithDocuments() {
PropertySource p1 = new PropertySource(
"configserver:git@github.com:demo/support-configuration-repo.git/application.yml",
Collections.singletonMap("foo", "bar"));
PropertySource p2 = new PropertySource(
"configserver:git@github.com:demo/support-configuration-repo.git/application-foo.yml",
Collections.singletonMap("foo", "bar"));
PropertySource p3 = new PropertySource(
"configserver:git@github.com:demo/support-configuration-repo.git/Config resource 'file [/var/folders/k3/zv8hzdm17vv69j485fv3cf9r0000gp/T/config-repo-14772121892716396795/commons/application.properties' via location 'commons/' (document #0)",
Collections.singletonMap("hello", "world"));
PropertySource p4 = new PropertySource(
"configserver:git@github.com:demo/support-configuration-repo.git/Config resource 'file [/var/folders/k3/zv8hzdm17vv69j485fv3cf9r0000gp/T/config-repo-14772121892716396795/commons/application-foo.properties' via location 'commons/' (document #0)",
Collections.singletonMap("hello", "world"));
Map<String, Object> activatesOnProfileCamelCase = new HashMap<>();
activatesOnProfileCamelCase.put("spring.config.activate.onProfile", "foo");
PropertySource p5 = new PropertySource(
"configserver:git@github.com:demo/support-configuration-repo.git/Config resource 'file [/var/folders/k3/zv8hzdm17vv69j485fv3cf9r0000gp/T/config-repo-14772121892716396795/commons/application.properties' via location 'commons/' (document #1)",
activatesOnProfileCamelCase);
Map<String, Object> activatesOnProfile = new HashMap<>();
activatesOnProfile.put("spring.config.activate.on-profile", "foo");
PropertySource p6 = new PropertySource(
"configserver:git@github.com:demo/support-configuration-repo.git/Config resource 'file [/var/folders/k3/zv8hzdm17vv69j485fv3cf9r0000gp/T/config-repo-14772121892716396795/commons/application.properties' via location 'commons/' (document #2)",
activatesOnProfile);
PropertySource p7 = new PropertySource(
"configserver:git@github.com:demo/support-configuration-repo.git/application-foo.yaml",
Collections.singletonMap("hello", "world"));
PropertySource p8 = new PropertySource(
"configserver:git@github.com:demo/support-configuration-repo.git/Config resource 'file [/var/folders/k3/zv8hzdm17vv69j485fv3cf9r0000gp/T/config-repo-14772121892716396795/commons/application-foo.yaml' via location 'commons/' (document #0)",
Collections.singletonMap("hello", "world"));
PropertySource p9 = new PropertySource(
"configserver:git@github.com:demo/support-configuration-repo.git/Config resource 'file [/var/folders/k3/zv8hzdm17vv69j485fv3cf9r0000gp/T/config-repo-14772121892716396795/commons/application-foo.yaml' via location 'commons/' (document #1)",
Collections.singletonMap("hello", "world"));
ConfigData configData = setupConfigServerConfigDataLoader(Arrays.asList(p1, p2, p3, p4, p5, p6, p7, p8, p9),
"application-slash", "foo");
assertThat(configData.getPropertySources().size()).isEqualTo(7);
}
private ConfigData setupConfigServerConfigDataLoader(List<PropertySource> propertySources, String applicationName,
String... profileList) {
RestTemplate rest = mock(RestTemplate.class);
Environment environment = new Environment("test", profileList);
environment.addAll(propertySources);
ResponseEntity<Environment> responseEntity = mock(ResponseEntity.class);
when(responseEntity.getStatusCode()).thenReturn(HttpStatus.OK);
when(responseEntity.getBody()).thenReturn(environment);
when(rest.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), eq(Environment.class),
eq(applicationName), ArgumentMatchers.<String>any())).thenReturn(responseEntity);
ConfigurableBootstrapContext bootstrapContext = mock(ConfigurableBootstrapContext.class);
when(bootstrapContext.get(eq(ConfigClientRequestTemplateFactory.class)))
.thenReturn(mock(ConfigClientRequestTemplateFactory.class));
when(bootstrapContext.get(eq(RestTemplate.class))).thenReturn(rest);
ConfigServerConfigDataLoader loader = new ConfigServerConfigDataLoader(destination -> mock(Log.class));
ConfigDataLoaderContext context = mock(ConfigDataLoaderContext.class);
when(context.getBootstrapContext()).thenReturn(bootstrapContext);
ConfigClientProperties properties = new ConfigClientProperties();
properties.setName(applicationName);
Profiles profiles = mock(Profiles.class);
when(profiles.getAccepted())
.thenReturn(profileList == null ? Collections.singletonList("default") : Arrays.asList(profileList));
ConfigServerConfigDataResource resource = new ConfigServerConfigDataResource(properties, false, profiles);
resource.setProfileSpecific(!ObjectUtils.isEmpty(profileList));
return loader.doLoad(context, resource);
}
private ConfigClientRequestTemplateFactory factory(ConfigClientProperties properties) {
return new ConfigClientRequestTemplateFactory(LogFactory.getLog(getClass()), properties);
}
private void assertNextUriIsNotTried(ConfigClientProperties.MultipleUriStrategy multipleUriStrategy,
HttpStatus firstUriResponse, Class<? extends Exception> expectedCause) throws Exception {
// Set up with two URIs.
String badURI = "http://baduri";
String goodURI = "http://localhost:8888";
String[] uris = new String[] { badURI, goodURI };
properties.setUri(uris);
properties.setFailFast(true);
// Strategy is CONNECTION_TIMEOUT_ONLY, so it should not try the next URI for
// INTERNAL_SERVER_ERROR
properties.setMultipleUriStrategy(multipleUriStrategy);
this.loader = new ConfigServerConfigDataLoader(destination -> logger);
ClientHttpRequestFactory requestFactory = mock(ClientHttpRequestFactory.class);
RestTemplate restTemplate = new RestTemplate(requestFactory);
mockRequestResponse(requestFactory, badURI, firstUriResponse);
mockRequestResponse(requestFactory, goodURI, HttpStatus.OK);
when(bootstrapContext.get(RestTemplate.class)).thenReturn(restTemplate);
ConfigClientFailFastException exception = Assertions.assertThrows(ConfigClientFailFastException.class,
() -> this.loader.load(context, resource));
if (expectedCause != null) {
assertThat(exception.getCause()).isInstanceOf(expectedCause);
}
assertThat(exception.getMessage()).contains("fail fast property is set");
}
@SuppressWarnings("SameParameterValue")
private void assertNextUriIsNotTriedForClientError(ConfigClientProperties.MultipleUriStrategy multipleUriStrategy,
HttpStatus firstUriResponse) throws Exception {
assertNextUriIsNotTried(multipleUriStrategy, firstUriResponse, HttpClientErrorException.class);
}
@SuppressWarnings("SameParameterValue")
private void assertNextUriIsNotTriedForNotFoundError(ConfigClientProperties.MultipleUriStrategy multipleUriStrategy,
HttpStatus firstUriResponse) throws Exception {
// NOT_FOUND is treated differently
assertNextUriIsNotTried(multipleUriStrategy, firstUriResponse, null);
}
@SuppressWarnings("SameParameterValue")
private void assertNextUriIsNotTriedForServerError(ConfigClientProperties.MultipleUriStrategy multipleUriStrategy,
HttpStatus firstUriResponse) throws Exception {
assertNextUriIsNotTried(multipleUriStrategy, firstUriResponse, HttpServerErrorException.class);
}
@SuppressWarnings("SameParameterValue")
private void assertNextUriIsTried(ConfigClientProperties.MultipleUriStrategy multipleUriStrategy,
HttpStatus firstUriResponse) throws Exception {
// Set up with two URIs.
String badURI = "http://baduri";
String goodURI = "http://localhost:8888";
String[] uris = new String[] { badURI, goodURI };
properties.setUri(uris);
properties.setFailFast(true);
// Strategy is ALWAYS, so it should try all URIs until successful
properties.setMultipleUriStrategy(multipleUriStrategy);
this.loader = new ConfigServerConfigDataLoader(destination -> logger);
ClientHttpRequestFactory requestFactory = mock(ClientHttpRequestFactory.class);
RestTemplate restTemplate = new RestTemplate(requestFactory);
mockRequestResponse(requestFactory, badURI, firstUriResponse);
mockRequestResponse(requestFactory, goodURI, HttpStatus.OK);
when(bootstrapContext.get(RestTemplate.class)).thenReturn(restTemplate);
assertThat(this.loader.load(context, resource)).isNotNull();
}
private void assertNextUriIsTriedOnTimeout(ConfigClientProperties.MultipleUriStrategy multipleUriStrategy)
throws Exception {
// Set up with two URIs.
String badURI = "http://baduri";
String goodURI = "http://localhost:8888";
String[] uris = new String[] { badURI, goodURI };
properties.setUri(uris);
properties.setFailFast(true);
// Strategy should not matter when the error is connection timed out
properties.setMultipleUriStrategy(multipleUriStrategy);
this.loader = new ConfigServerConfigDataLoader(destination -> logger);
ClientHttpRequestFactory requestFactory = mock(ClientHttpRequestFactory.class);
RestTemplate restTemplate = new RestTemplate(requestFactory);
when(bootstrapContext.get(RestTemplate.class)).thenReturn(restTemplate);
// First URI times out. Second one is successful
mockRequestTimedOut(requestFactory, badURI);
mockRequestResponse(requestFactory, goodURI, HttpStatus.OK);
assertThat(this.loader.load(context, resource)).isNotNull();
}
private void mockRequestResponse(ClientHttpRequestFactory requestFactory, String baseURI, HttpStatus status)
throws Exception {
ClientHttpRequest request = mock(ClientHttpRequest.class);
ClientHttpResponse response = mock(ClientHttpResponse.class);
if (baseURI == null) {
when(requestFactory.createRequest(any(URI.class), any(HttpMethod.class))).thenReturn(request);
}
else {
when(requestFactory.createRequest(eq(new URI(format(URI_TEMPLATE, baseURI, NAME, PROFILES, LABEL))),
any(HttpMethod.class))).thenReturn(request);
}
when(request.getHeaders()).thenReturn(new HttpHeaders());
when(request.execute()).thenReturn(response);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
when(response.getHeaders()).thenReturn(headers);
when(response.getStatusCode()).thenReturn(status);
when(response.getRawStatusCode()).thenReturn(status.value());
when(response.getBody()).thenReturn(new ByteArrayInputStream("{}".getBytes()));
}
@SuppressWarnings("unchecked")
private void mockRequestResponseWithLabel(ResponseEntity<?> response, String label) {
when(this.restTemplate.exchange(any(String.class), any(HttpMethod.class), any(HttpEntity.class),
any(Class.class), anyString(), anyString(), eq(label))).thenReturn(response);
}
@SuppressWarnings("unchecked")
private void mockRequestResponseWithoutLabel(ResponseEntity<?> response) {
when(this.restTemplate.exchange(any(String.class), any(HttpMethod.class), any(HttpEntity.class),
any(Class.class), anyString(), anyString())).thenReturn(response);
}
@SuppressWarnings("unchecked")
private void mockRequestTimedOut() {
when(this.restTemplate.exchange(any(String.class), any(HttpMethod.class), any(HttpEntity.class),
any(Class.class), anyString(), anyString(), anyString())).thenThrow(ResourceAccessException.class);
}
private void mockRequestTimedOut(ClientHttpRequestFactory requestFactory, String baseURI) throws Exception {
ClientHttpRequest request = mock(ClientHttpRequest.class);
if (baseURI == null) {
when(requestFactory.createRequest(any(URI.class), any(HttpMethod.class))).thenReturn(request);
}
else {
when(requestFactory.createRequest(eq(new URI(format(URI_TEMPLATE, baseURI, NAME, PROFILES, LABEL))),
any(HttpMethod.class))).thenReturn(request);
}
when(request.getHeaders()).thenReturn(new HttpHeaders());
when(request.execute()).thenThrow(IOException.class);
}
}

View File

@@ -1,242 +0,0 @@
/*
* Copyright 2013-2020 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.client;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.context.config.ConfigDataLocation;
import org.springframework.boot.context.config.ConfigDataLocationResolverContext;
import org.springframework.boot.context.config.Profiles;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.logging.DeferredLog;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class ConfigServerConfigDataLocationResolverTests {
private ConfigServerConfigDataLocationResolver resolver;
private ConfigDataLocationResolverContext context = mock(ConfigDataLocationResolverContext.class);
private MockEnvironment environment;
private Binder environmentBinder;
@BeforeEach
void setup() {
this.environment = new MockEnvironment();
this.environmentBinder = Binder.get(this.environment);
this.resolver = new ConfigServerConfigDataLocationResolver(destination -> new DeferredLog());
when(context.getBinder()).thenReturn(environmentBinder);
}
@Test
void isResolvableReturnsFalseWithIncorrectPrefix() {
assertThat(this.resolver.isResolvable(this.context, ConfigDataLocation.of("test:"))).isFalse();
}
@Test
void isResolvableReturnsTrueWithCorrectPrefix() {
assertThat(this.resolver.isResolvable(this.context, ConfigDataLocation.of("configserver:"))).isTrue();
}
@Test
void isResolvableReturnsFalseWhenDisabled() {
this.environment.setProperty(ConfigClientProperties.PREFIX + ".enabled", "false");
assertThat(this.resolver.isResolvable(this.context, ConfigDataLocation.of("configserver:"))).isFalse();
}
@Test
void defaultSpringProfiles() {
ConfigServerConfigDataResource resource = testResolveProfileSpecific();
assertThat(resource.getProfiles()).isEqualTo("default");
}
@Test
void configClientProfilesOverridesSpringProfilesActive() {
this.environment.setProperty(ConfigClientProperties.PREFIX + ".profile", "myprofile");
ConfigServerConfigDataResource resource = testResolveProfileSpecific();
assertThat(resource.getProfiles()).isEqualTo("myprofile");
}
@Test
void configClientProfilesAcceptedProfiles() {
this.environment.setProperty(ConfigClientProperties.PREFIX + ".profile", "myprofile");
ConfigServerConfigDataResource resource = testResolve();
assertThat(resource.getAcceptedProfiles()).contains("myprofile");
}
@Test
void configClientProfilesDefaultAcceptedProfiles() {
ConfigServerConfigDataResource resource = testResolve();
assertThat(resource.getAcceptedProfiles()).contains("default");
}
@Test
void configClientSpringProfilesActiveOverridesDefaultClientProfiles() {
ConfigServerConfigDataResource resource = testResolveProfileSpecific("myactiveprofile");
assertThat(resource.getProfiles()).isEqualTo("myactiveprofile");
}
@Test
void assertConfigDataResourceHasNullProfiles() {
ConfigServerConfigDataResource resource = testResolve();
assertThat(resource.isProfileSpecific()).isFalse();
}
@Test
void configNameDefaultsToApplication() {
ConfigServerConfigDataResource resource = testResolveProfileSpecific();
assertThat(resource.getProperties().getName()).isEqualTo("application");
}
@Test
void configNameDefaultsToSpringApplicationName() {
this.environment.setProperty("spring.application.name", "myapp");
ConfigServerConfigDataResource resource = testResolveProfileSpecific();
assertThat(resource.getProperties().getName()).isEqualTo("myapp");
}
@Test
void configNameOverridesSpringApplicationName() {
this.environment.setProperty("spring.application.name", "myapp");
this.environment.setProperty(ConfigClientProperties.PREFIX + ".name", "myconfigname");
ConfigServerConfigDataResource resource = testResolveProfileSpecific();
assertThat(resource.getProperties().getName()).isEqualTo("myconfigname");
}
@Test
void retryPropertiesShouldBeDefaultByDefault() {
ConfigServerConfigDataResource resource = testResolveProfileSpecific();
RetryProperties defaultRetry = new RetryProperties();
assertThat(resource.getRetryProperties().getMaxAttempts()).isEqualTo(defaultRetry.getMaxAttempts());
assertThat(resource.getRetryProperties().getMaxInterval()).isEqualTo(defaultRetry.getMaxInterval());
assertThat(resource.getRetryProperties().getInitialInterval()).isEqualTo(defaultRetry.getInitialInterval());
assertThat(resource.getRetryProperties().getMultiplier()).isEqualTo(defaultRetry.getMultiplier());
}
@Test
void uriInLocationOverridesProperty() {
String locationUri = "http://actualuri";
ConfigServerConfigDataResource resource = testUri("http://shouldbeoverridden", locationUri);
assertThat(resource.getProperties().getUri()).containsExactly(locationUri);
}
@Test
void uriWithParamsParsesProperties() {
String locationUri = "http://actualuri";
ConfigServerConfigDataResource resource = testUri("http://shouldbeoverridden",
locationUri + "?fail-fast=true&max-attempts=10&max-interval=1500&multiplier=1.2&initial-interval=1100");
assertThat(resource.getProperties().getUri()).containsExactly(locationUri);
assertThat(resource.getProperties().isFailFast()).isTrue();
assertThat(resource.getRetryProperties().getMaxAttempts()).isEqualTo(10);
assertThat(resource.getRetryProperties().getMaxInterval()).isEqualTo(1500);
assertThat(resource.getRetryProperties().getInitialInterval()).isEqualTo(1100);
assertThat(resource.getRetryProperties().getMultiplier()).isEqualTo(1.2);
}
@Test
void urisInLocationOverridesProperty() {
String locationUri = "http://actualuri1,http://actualuri2";
ConfigServerConfigDataResource resource = testUri("http://shouldbeoverridden", locationUri);
assertThat(resource.getProperties().getUri()).containsExactly(locationUri.split(","));
}
@Test
void createNewConfigClientPropertiesInBootstrapContext() {
ConfigurableBootstrapContext bootstrapContext = mock(ConfigurableBootstrapContext.class);
when(bootstrapContext.isRegistered(eq(ConfigClientProperties.class))).thenReturn(false);
ConfigClientProperties configClientProperties = new ConfigClientProperties();
configClientProperties.setUri(new String[] { "http://myuri" });
when(bootstrapContext.get(eq(ConfigClientProperties.class))).thenReturn(configClientProperties);
when(context.getBootstrapContext()).thenReturn(bootstrapContext);
List<ConfigServerConfigDataResource> resources = this.resolver.resolveProfileSpecific(context,
ConfigDataLocation.of("configserver:http://locationuri"), mock(Profiles.class));
assertThat(resources).hasSize(1);
verify(bootstrapContext, times(0)).get(eq(ConfigClientProperties.class));
ConfigServerConfigDataResource resource = resources.get(0);
assertThat(resource.getProperties().getUri()).isEqualTo(new String[] { "http://locationuri" });
}
@Test
void multipleImportEntriesDoesNotShareSameURIs() {
ConfigurableBootstrapContext bootstrapContext = mock(ConfigurableBootstrapContext.class);
when(bootstrapContext.isRegistered(eq(ConfigClientProperties.class))).thenReturn(true);
ConfigClientProperties configClientProperties = new ConfigClientProperties();
configClientProperties.setUri(new String[] { "http://myuri" });
when(bootstrapContext.get(eq(ConfigClientProperties.class))).thenReturn(configClientProperties);
when(context.getBootstrapContext()).thenReturn(bootstrapContext);
List<ConfigServerConfigDataResource> resources1 = this.resolver.resolveProfileSpecific(context,
ConfigDataLocation.of("configserver:http://urlNo1"), mock(Profiles.class));
List<ConfigServerConfigDataResource> resources2 = this.resolver.resolveProfileSpecific(context,
ConfigDataLocation.of("configserver:http://urlNo2"), mock(Profiles.class));
assertThat(resources1).hasSize(1);
assertThat(resources2).hasSize(1);
ConfigServerConfigDataResource resource1 = resources1.get(0);
assertThat(resource1.getProperties().getUri()).isEqualTo(new String[] { "http://urlNo1" });
ConfigServerConfigDataResource resource2 = resources2.get(0);
assertThat(resource2.getProperties().getUri()).isEqualTo(new String[] { "http://urlNo2" });
}
private ConfigServerConfigDataResource testUri(String propertyUri, String locationUri) {
this.environment.setProperty(ConfigClientProperties.PREFIX + ".uri", propertyUri);
when(context.getBootstrapContext()).thenReturn(mock(ConfigurableBootstrapContext.class));
Profiles profiles = mock(Profiles.class);
List<ConfigServerConfigDataResource> resources = this.resolver.resolveProfileSpecific(context,
ConfigDataLocation.of("configserver:" + locationUri), profiles);
assertThat(resources).hasSize(1);
return resources.get(0);
}
private ConfigServerConfigDataResource testResolveProfileSpecific() {
return testResolveProfileSpecific("default");
}
private ConfigServerConfigDataResource testResolveProfileSpecific(String activeProfile) {
when(context.getBootstrapContext()).thenReturn(mock(ConfigurableBootstrapContext.class));
Profiles profiles = mock(Profiles.class);
if (activeProfile != null) {
when(profiles.getAccepted()).thenReturn(Collections.singletonList(activeProfile));
}
List<ConfigServerConfigDataResource> resources = this.resolver.resolveProfileSpecific(context,
ConfigDataLocation.of("configserver:"), profiles);
assertThat(resources).hasSize(1);
return resources.get(0);
}
private ConfigServerConfigDataResource testResolve() {
when(context.getBootstrapContext()).thenReturn(mock(ConfigurableBootstrapContext.class));
List<ConfigServerConfigDataResource> resources = this.resolver.resolve(context,
ConfigDataLocation.of("configserver:"));
assertThat(resources).hasSize(1);
return resources.get(0);
}
}

View File

@@ -1,106 +0,0 @@
/*
* Copyright 2015-2021 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.client;
import org.junit.jupiter.api.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;
/**
* @author Ryan Baxter
*/
class ConfigServerConfigDataMissingEnvironmentPostProcessorTests {
@Test
void noSpringConfigImport() {
MockEnvironment environment = new MockEnvironment();
SpringApplication app = mock(SpringApplication.class);
ConfigServerConfigDataMissingEnvironmentPostProcessor processor = new ConfigServerConfigDataMissingEnvironmentPostProcessor();
assertThatThrownBy(() -> processor.postProcessEnvironment(environment, app))
.isInstanceOf(ConfigServerConfigDataMissingEnvironmentPostProcessor.ImportException.class);
}
@Test
void boostrap() {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("spring.cloud.bootstrap.enabled", "true");
SpringApplication app = mock(SpringApplication.class);
ConfigServerConfigDataMissingEnvironmentPostProcessor processor = new ConfigServerConfigDataMissingEnvironmentPostProcessor();
assertThatCode(() -> processor.postProcessEnvironment(environment, app)).doesNotThrowAnyException();
}
@Test
void legacy() {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("spring.config.use-legacy-processing", "true");
SpringApplication app = mock(SpringApplication.class);
ConfigServerConfigDataMissingEnvironmentPostProcessor processor = new ConfigServerConfigDataMissingEnvironmentPostProcessor();
assertThatCode(() -> processor.postProcessEnvironment(environment, app)).doesNotThrowAnyException();
}
@Test
void configNotEnabled() {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("spring.cloud.config.enabled", "false");
SpringApplication app = mock(SpringApplication.class);
ConfigServerConfigDataMissingEnvironmentPostProcessor processor = new ConfigServerConfigDataMissingEnvironmentPostProcessor();
assertThatCode(() -> processor.postProcessEnvironment(environment, app)).doesNotThrowAnyException();
}
@Test
void importCheckNotEnabled() {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("spring.cloud.config.import-check.enabled", "false");
SpringApplication app = mock(SpringApplication.class);
ConfigServerConfigDataMissingEnvironmentPostProcessor processor = new ConfigServerConfigDataMissingEnvironmentPostProcessor();
assertThatCode(() -> processor.postProcessEnvironment(environment, app)).doesNotThrowAnyException();
}
@Test
void importSinglePropertySource() {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("spring.config.import", "configserver:http://localhost:8888");
SpringApplication app = mock(SpringApplication.class);
ConfigServerConfigDataMissingEnvironmentPostProcessor processor = new ConfigServerConfigDataMissingEnvironmentPostProcessor();
assertThatCode(() -> processor.postProcessEnvironment(environment, app)).doesNotThrowAnyException();
}
@Test
void importMultiplePropertySource() {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("spring.config.import", "configserver:http://localhost:8888,file:./app.properties");
SpringApplication app = mock(SpringApplication.class);
ConfigServerConfigDataMissingEnvironmentPostProcessor processor = new ConfigServerConfigDataMissingEnvironmentPostProcessor();
assertThatCode(() -> processor.postProcessEnvironment(environment, app)).doesNotThrowAnyException();
}
@Test
void importMultiplePropertySourceAsList() {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("spring.config.import[0]", "configserver:http://localhost:8888");
environment.setProperty("spring.config.import[1]", "file:./app.properties");
SpringApplication app = mock(SpringApplication.class);
ConfigServerConfigDataMissingEnvironmentPostProcessor processor = new ConfigServerConfigDataMissingEnvironmentPostProcessor();
assertThatCode(() -> processor.postProcessEnvironment(environment, app)).doesNotThrowAnyException();
}
}

View File

@@ -1,90 +0,0 @@
/*
* Copyright 2013-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.client;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.cloud.commons.ConfigDataMissingEnvironmentPostProcessor;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.config.client.ConfigServerConfigDataLocationResolver.PREFIX;
/**
* @author Spencer Gibb
*/
@ExtendWith(OutputCaptureExtension.class)
public class ConfigServerConfigDataNoImportIntegrationTests {
private static final String APP_NAME = "testConfigDataNoImport";
@Test
public void exceptionThrownIfNoImport(CapturedOutput output) {
Assertions
.assertThatThrownBy(() -> new SpringApplicationBuilder(Config.class).web(WebApplicationType.NONE)
.run("--spring.application.name=" + APP_NAME))
.isInstanceOf(ConfigDataMissingEnvironmentPostProcessor.ImportException.class);
assertThat(output).contains("No spring.config.import property has been defined")
.contains("Add a spring.config.import=configserver: property to your configuration");
}
@Test
public void exceptionThrownIfImportMissing(CapturedOutput output) {
Assertions
.assertThatThrownBy(() -> new SpringApplicationBuilder(Config.class).web(WebApplicationType.NONE).run(
"--spring.config.import=optional:file:somefile.properties",
"--spring.application.name=" + APP_NAME))
.isInstanceOf(ConfigDataMissingEnvironmentPostProcessor.ImportException.class);
assertThat(output).contains("spring.config.import property is missing a " + PREFIX)
.contains("Add a spring.config.import=configserver: property to your configuration");
}
@Test
public void noExceptionThrownIfConfigDisabled() {
try (ConfigurableApplicationContext context = new SpringApplicationBuilder(Config.class)
.web(WebApplicationType.NONE)
.run("--spring.cloud.config.enabled=false", "--spring.application.name=" + APP_NAME)) {
// nothing to do
}
}
@Test
public void noExceptionThrownIfImportCheckDisabled() {
try (ConfigurableApplicationContext context = new SpringApplicationBuilder(Config.class)
.web(WebApplicationType.NONE)
.run("--spring.cloud.config.import-check.enabled=false", "--spring.application.name=" + APP_NAME)) {
// nothing to do
}
}
@Configuration
@EnableAutoConfiguration
static class Config {
}
}

View File

@@ -1,332 +0,0 @@
/*
* Copyright 2013-2023 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.client;
import java.util.Arrays;
import java.util.Collections;
import org.junit.jupiter.api.Test;
import org.springframework.boot.context.config.Profiles;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author Ryan Baxter
*/
class ConfigServerConfigDataResourceTests {
@Test
void testEquals() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888" });
ConfigClientProperties r2Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888" });
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true,
mock(Profiles.class));
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, true,
mock(Profiles.class));
assertThat(r1).isEqualTo(r2);
assertThat(r1.hashCode()).isEqualTo(r2.hashCode());
}
@Test
void testEqualsDifferentSchemes() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "https://localhost:8888" });
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties.setUri(new String[] { "http://localhost:8888" });
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true,
mock(Profiles.class));
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, true,
mock(Profiles.class));
assertThat(r1).isEqualTo(r2);
assertThat(r1.hashCode()).isEqualTo(r2.hashCode());
}
@Test
void testEqualsDifferentHosts() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888" });
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties.setUri(new String[] { "http://remotehost:8888" });
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true,
mock(Profiles.class));
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, true,
mock(Profiles.class));
assertThat(r1).isNotEqualTo(r2);
assertThat(r1.hashCode()).isNotEqualTo(r2.hashCode());
}
@Test
void testEqualsDifferentPorts() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888" });
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties.setUri(new String[] { "http://localhost" });
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true,
mock(Profiles.class));
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, true,
mock(Profiles.class));
assertThat(r1).isNotEqualTo(r2);
assertThat(r1.hashCode()).isNotEqualTo(r2.hashCode());
}
@Test
void testEqualsOptionalFalse() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888" });
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties.setUri(new String[] { "http://localhost:8888" });
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, false,
mock(Profiles.class));
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, false,
mock(Profiles.class));
assertThat(r1).isEqualTo(r2);
assertThat(r1.hashCode()).isEqualTo(r2.hashCode());
}
@Test
void testEqualsSamePath() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888/p1" });
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties.setUri(new String[] { "http://localhost:8888/p1" });
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true,
mock(Profiles.class));
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, true,
mock(Profiles.class));
assertThat(r1).isEqualTo(r2);
assertThat(r1.hashCode()).isEqualTo(r2.hashCode());
}
@Test
void testEqualsDifferentPath() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888/p1" });
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties.setUri(new String[] { "http://localhost:8888/p2" });
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true,
mock(Profiles.class));
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, true,
mock(Profiles.class));
assertThat(r1).isNotEqualTo(r2);
assertThat(r1.hashCode()).isNotEqualTo(r2.hashCode());
}
@Test
void testMultipleUrisDifferentOrder() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888", "http://localhost:9999", "http://localhost:7777" });
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties.setUri(new String[] { "http://localhost:7777", "http://localhost:8888", "http://localhost:9999" });
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true,
mock(Profiles.class));
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, true,
mock(Profiles.class));
assertThat(r1).isEqualTo(r2);
assertThat(r1.hashCode()).isEqualTo(r2.hashCode());
}
@Test
void testMultipleUrisDifferentOrderWithDifferentSchemes() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888", "http://localhost:9999", "http://localhost:7777" });
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties
.setUri(new String[] { "https://localhost:7777", "https://localhost:8888", "https://localhost:9999" });
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true,
mock(Profiles.class));
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, true,
mock(Profiles.class));
assertThat(r1).isEqualTo(r2);
assertThat(r1.hashCode()).isEqualTo(r2.hashCode());
}
@Test
void testMultipleUrisDifferentLengths() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888", "http://localhost:9999", "http://localhost:7777" });
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties.setUri(new String[] { "http://localhost:7777", "http://localhost:8888", "http://localhost:9999",
"http://localhost:6666" });
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true,
mock(Profiles.class));
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, true,
mock(Profiles.class));
assertThat(r1).isNotEqualTo(r2);
assertThat(r1.hashCode()).isNotEqualTo(r2.hashCode());
}
@Test
void testEqualsOptionalFalseAndTrue() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888" });
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties.setUri(new String[] { "http://localhost:8888" });
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true,
mock(Profiles.class));
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, false,
mock(Profiles.class));
assertThat(r1).isNotEqualTo(r2);
assertThat(r1.hashCode()).isNotEqualTo(r2.hashCode());
}
@Test
void testEqualsNullProfilesAndDefaultProfile() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888" });
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties.setUri(new String[] { "http://localhost:8888" });
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true, null);
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, true,
mock(Profiles.class));
assertThat(r1).isEqualTo(r2);
assertThat(r1.hashCode()).isEqualTo(r2.hashCode());
}
@Test
void testEqualsNullProfiles() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888" });
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties.setUri(new String[] { "http://localhost:8888" });
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true, null);
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, true, null);
assertThat(r1).isEqualTo(r2);
assertThat(r1.hashCode()).isEqualTo(r2.hashCode());
}
@Test
void testEqualsProfilesAndProperties() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888" });
Profiles r1Profiles = mock(Profiles.class);
when(r1Profiles.getAccepted()).thenReturn(Collections.singletonList("foo"));
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties.setUri(new String[] { "http://localhost:8888" });
r2Properties.setProfile("foo");
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true, r1Profiles);
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, true, null);
assertThat(r1).isEqualTo(r2);
assertThat(r1.hashCode()).isEqualTo(r2.hashCode());
}
@Test
void testEqualsProfiles() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888" });
Profiles r1Profiles = mock(Profiles.class);
when(r1Profiles.getAccepted()).thenReturn(Collections.singletonList("foo"));
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties.setUri(new String[] { "http://localhost:8888" });
Profiles r2Profiles = mock(Profiles.class);
when(r2Profiles.getAccepted()).thenReturn(Collections.singletonList("foo"));
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true, r1Profiles);
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, true, r2Profiles);
assertThat(r1).isEqualTo(r2);
assertThat(r1.hashCode()).isEqualTo(r2.hashCode());
}
@Test
void testEqualsProfilesDifferent() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888" });
Profiles r1Profiles = mock(Profiles.class);
when(r1Profiles.getAccepted()).thenReturn(Arrays.asList("foo", "bar"));
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties.setUri(new String[] { "http://localhost:8888" });
Profiles r2Profiles = mock(Profiles.class);
when(r2Profiles.getAccepted()).thenReturn(Collections.singletonList("foo"));
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true, r1Profiles);
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, true, r2Profiles);
assertThat(r1).isNotEqualTo(r2);
assertThat(r1.hashCode()).isNotEqualTo(r2.hashCode());
}
@Test
void testEqualsProfilesDifferentOrder() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888" });
Profiles r1Profiles = mock(Profiles.class);
when(r1Profiles.getAccepted()).thenReturn(Arrays.asList("foo", "bar"));
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties.setUri(new String[] { "http://localhost:8888" });
Profiles r2Profiles = mock(Profiles.class);
when(r2Profiles.getAccepted()).thenReturn(Arrays.asList("bar", "foo"));
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true, r1Profiles);
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, true, r2Profiles);
assertThat(r1).isNotEqualTo(r2);
assertThat(r1.hashCode()).isNotEqualTo(r2.hashCode());
}
@Test
void testEqualsProfilesProperties() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888" });
Profiles r1Profiles = mock(Profiles.class);
when(r1Profiles.getAccepted()).thenReturn(Arrays.asList("foo", "bar"));
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties.setUri(new String[] { "http://localhost:8888" });
r2Properties.setProfile("foo,bar");
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true, r1Profiles);
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, true, null);
assertThat(r1).isEqualTo(r2);
assertThat(r1.hashCode()).isEqualTo(r2.hashCode());
}
@Test
void testEqualsLabels() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888" });
r1Properties.setLabel("foo");
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties.setUri(new String[] { "http://localhost:8888" });
r2Properties.setLabel("foo");
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true, null);
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, true, null);
assertThat(r1).isEqualTo(r2);
assertThat(r1.hashCode()).isEqualTo(r2.hashCode());
}
@Test
void testEqualsDifferentLabels() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "http://localhost:8888" });
r1Properties.setLabel("foo");
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties.setUri(new String[] { "http://localhost:8888" });
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true, null);
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, true, null);
assertThat(r1).isNotEqualTo(r2);
assertThat(r1.hashCode()).isNotEqualTo(r2.hashCode());
}
@Test
void testInvalidUri() {
ConfigClientProperties r1Properties = new ConfigClientProperties();
r1Properties.setUri(new String[] { "//" });
ConfigServerConfigDataResource r1 = new ConfigServerConfigDataResource(r1Properties, true, null);
ConfigClientProperties r2Properties = new ConfigClientProperties();
r2Properties.setUri(new String[] { "//" });
ConfigServerConfigDataResource r2 = new ConfigServerConfigDataResource(r2Properties, true, null);
assertThat(r1).isEqualTo(r2);
}
}

View File

@@ -1,100 +0,0 @@
/*
* Copyright 2014-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.client;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.health.Status;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Dave Syer
* @author Marcos Barbero
*
*/
@Disabled
public class ConfigServerHealthIndicatorTests {
private ConfigurableEnvironment environment = mock(ConfigurableEnvironment.class);
private ConfigServerHealthIndicator indicator = new ConfigServerHealthIndicator(this.environment,
new ConfigClientHealthProperties());
@Test
public void testDefaultStatus() {
MutablePropertySources sources = new MutablePropertySources();
doReturn(sources).when(this.environment).getPropertySources();
// UNKNOWN is better than DOWN since it doesn't stop the app from working
assertThat(this.indicator.health().getStatus()).isEqualTo(Status.UNKNOWN);
}
@Test
public void testExceptionStatus() {
// TODO: is this needed any more
}
@Test
public void testServerUp() {
setupPropertySources();
Map<String, Object> details = this.indicator.getHealth(true).getDetails();
List<String> propertySources = (List) details.get("propertySources");
assertThat(propertySources.contains("bootstrapProperties-test")).isTrue();
assertThat(propertySources.contains("configserver:test")).isTrue();
assertThat(propertySources.contains("configClient")).isTrue();
assertThat(propertySources.size()).isEqualTo(3);
assertThat(this.indicator.health().getStatus()).isEqualTo(Status.UP);
}
protected void setupPropertySources() {
PropertySource<?> source = new MapPropertySource("configClient", Collections.emptyMap());
PropertySource<?> configServerSource = new MapPropertySource("configserver:test", Collections.emptyMap());
PropertySource<?> bootstrapSource = new MapPropertySource("bootstrapProperties-test", Collections.emptyMap());
MutablePropertySources sources = new MutablePropertySources();
sources.addFirst(source);
sources.addFirst(bootstrapSource);
sources.addFirst(configServerSource);
doReturn(sources).when(this.environment).getPropertySources();
}
@Test
public void healthIsCached() {
setupPropertySources();
// not cached
assertThat(this.indicator.health().getStatus()).isEqualTo(Status.UP);
// cached
assertThat(this.indicator.health().getStatus()).isEqualTo(Status.UP);
verify(this.environment, times(1)).getPropertySources();
}
}

View File

@@ -1,89 +0,0 @@
/*
* Copyright 2013-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.client;
import java.lang.reflect.Field;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Tristan Hanson
*
*/
public class ConfigServiceBootstrapConfigurationTest {
private AnnotationConfigApplicationContext context;
@BeforeEach
public void setUp() throws Exception {
this.context = new AnnotationConfigApplicationContext();
}
@AfterEach
public void tearDown() throws Exception {
if (this.context != null) {
this.context.close();
}
}
@Test
public void overrideConfigServicePropertySourceLocatorWhenBeanIsProvided() {
TestPropertyValues.of("spring.cloud.config.enabled=true").applyTo(this.context);
this.context.register(ConfigServicePropertySourceLocatorOverrideConfig.class);
this.context.register(ConfigServiceBootstrapConfiguration.class);
this.context.refresh();
ConfigServicePropertySourceLocator locator = this.context.getBean(ConfigServicePropertySourceLocator.class);
Field restTemplateField = ReflectionUtils.findField(ConfigServicePropertySourceLocator.class, "restTemplate");
restTemplateField.setAccessible(true);
RestTemplate restTemplate = (RestTemplate) ReflectionUtils.getField(restTemplateField, locator);
assertThat(restTemplate).isNotNull();
}
@Configuration(proxyBeanMethods = false)
protected static class ConfigServicePropertySourceLocatorOverrideConfig {
@Autowired
private Environment environment;
@Bean
public ConfigServicePropertySourceLocator locator() {
ConfigServicePropertySourceLocator locator = new ConfigServicePropertySourceLocator(
new ConfigClientProperties(this.environment));
locator.setRestTemplate(new RestTemplate());
return locator;
}
}
}

View File

@@ -1,604 +0,0 @@
/*
* 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.client;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.LogFactory;
import org.assertj.core.api.AbstractThrowableAssert;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.cloud.config.client.ConfigClientRequestTemplateFactory.GenericRequestHeaderInterceptor;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.environment.PropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.mock.http.client.MockClientHttpRequest;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.springframework.cloud.config.client.ConfigClientProperties.AUTHORIZATION;
import static org.springframework.cloud.config.environment.EnvironmentMediaType.V2_JSON;
public class ConfigServicePropertySourceLocatorTests {
private ConfigurableEnvironment environment = new StandardEnvironment();
private ConfigServicePropertySourceLocator locator = new ConfigServicePropertySourceLocator(
new ConfigClientProperties(this.environment));
private RestTemplate restTemplate = Mockito.mock(RestTemplate.class);
@Test
public void sunnyDay() {
Environment body = new Environment("app", "master");
mockRequestResponseWithoutLabel(new ResponseEntity<>(body, HttpStatus.OK));
this.locator.setRestTemplate(this.restTemplate);
ArgumentCaptor<HttpEntity> argumentCaptor = ArgumentCaptor.forClass(HttpEntity.class);
assertThat(this.locator.locateCollection(this.environment)).isNotNull();
Mockito.verify(this.restTemplate).exchange(anyString(), any(HttpMethod.class), argumentCaptor.capture(),
any(Class.class), anyString(), anyString());
HttpEntity httpEntity = argumentCaptor.getValue();
assertThat(httpEntity.getHeaders().getAccept()).containsExactly(MediaType.parseMediaType(V2_JSON));
}
@Test
public void customMediaType() {
Environment body = new Environment("app", "master");
mockRequestResponseWithoutLabel(new ResponseEntity<>(body, HttpStatus.OK));
ConfigClientProperties properties = new ConfigClientProperties(this.environment);
properties.setMediaType("application/json");
ConfigServicePropertySourceLocator locator = new ConfigServicePropertySourceLocator(properties);
locator.setRestTemplate(this.restTemplate);
ArgumentCaptor<HttpEntity> argumentCaptor = ArgumentCaptor.forClass(HttpEntity.class);
assertThat(locator.locateCollection(this.environment)).isNotNull();
Mockito.verify(this.restTemplate).exchange(anyString(), any(HttpMethod.class), argumentCaptor.capture(),
any(Class.class), anyString(), anyString());
HttpEntity httpEntity = argumentCaptor.getValue();
assertThat(httpEntity.getHeaders().getAccept()).containsExactly(MediaType.parseMediaType("application/json"));
}
@Test
public void sunnyDayWithLabel() {
Environment body = new Environment("app", "master");
mockRequestResponseWithLabel(new ResponseEntity<>(body, HttpStatus.OK), "v1.0.0");
this.locator.setRestTemplate(this.restTemplate);
TestPropertyValues.of("spring.cloud.config.label:v1.0.0").applyTo(this.environment);
assertThat(this.locator.locateCollection(this.environment)).isNotNull();
}
@Test
public void sunnyDayWithLabelThatContainsASlash() {
Environment body = new Environment("app", "master");
mockRequestResponseWithLabel(new ResponseEntity<>(body, HttpStatus.OK), "release(_)v1.0.0");
this.locator.setRestTemplate(this.restTemplate);
TestPropertyValues.of("spring.cloud.config.label:release/v1.0.0").applyTo(this.environment);
assertThat(this.locator.locateCollection(this.environment)).isNotNull();
}
@Test
public void sunnyDayWithNoSuchLabel() {
mockRequestResponseWithLabel(new ResponseEntity<>((Void) null, HttpStatus.NOT_FOUND), "nosuchlabel");
this.locator.setRestTemplate(this.restTemplate);
assertThat(this.locator.locateCollection(this.environment)).isEmpty();
}
@Test
public void sunnyDayWithNoSuchLabelAndFailFast() {
Assertions.assertThatThrownBy(() -> {
ConfigClientProperties defaults = new ConfigClientProperties(this.environment);
defaults.setFailFast(true);
this.locator = new ConfigServicePropertySourceLocator(defaults);
mockRequestResponseWithLabel(new ResponseEntity<>((Void) null, HttpStatus.NOT_FOUND), "release(_)v1.0.0");
this.locator.setRestTemplate(this.restTemplate);
TestPropertyValues.of("spring.cloud.config.label:release/v1.0.1").applyTo(this.environment);
this.locator.locateCollection(this.environment);
}).isInstanceOf(IllegalStateException.class).hasMessageContaining(
"Could not locate PropertySource and the fail fast property is set, failing: None of labels [release/v1.0.1] found");
}
@Test
public void failsQuietly() {
mockRequestResponseWithoutLabel(new ResponseEntity<>("Wah!", HttpStatus.INTERNAL_SERVER_ERROR));
this.locator.setRestTemplate(this.restTemplate);
assertThat(this.locator.locateCollection(this.environment)).isEmpty();
}
@Test
public void failFast() throws Exception {
Assertions.assertThatThrownBy(() -> {
ClientHttpRequestFactory requestFactory = Mockito.mock(ClientHttpRequestFactory.class);
mockRequestResponse(requestFactory, null, HttpStatus.INTERNAL_SERVER_ERROR);
RestTemplate restTemplate = new RestTemplate(requestFactory);
ConfigClientProperties defaults = new ConfigClientProperties(this.environment);
defaults.setFailFast(true);
this.locator = new ConfigServicePropertySourceLocator(defaults);
this.locator.setRestTemplate(restTemplate);
this.locator.locateCollection(this.environment);
}).isInstanceOf(IllegalStateException.class).hasCauseInstanceOf(HttpServerErrorException.class)
.hasMessageContaining("fail fast property is set");
}
@Test
public void failFastWhenNotFound() throws Exception {
Assertions.assertThatThrownBy(() -> {
ClientHttpRequestFactory requestFactory = Mockito.mock(ClientHttpRequestFactory.class);
mockRequestResponse(requestFactory, null, HttpStatus.NOT_FOUND);
RestTemplate restTemplate = new RestTemplate(requestFactory);
ConfigClientProperties defaults = new ConfigClientProperties(this.environment);
defaults.setFailFast(true);
this.locator = new ConfigServicePropertySourceLocator(defaults);
this.locator.setRestTemplate(restTemplate);
this.locator.locateCollection(this.environment);
}).isInstanceOf(IllegalStateException.class)
.hasMessageContaining("fail fast property is set, failing: None of labels [] found");
}
@Test
public void failFastWhenRequestTimesOut() {
Assertions.assertThatThrownBy(() -> {
mockRequestTimedOut();
ConfigClientProperties defaults = new ConfigClientProperties(this.environment);
defaults.setFailFast(true);
this.locator = new ConfigServicePropertySourceLocator(defaults);
this.locator.setRestTemplate(this.restTemplate);
this.locator.locateCollection(this.environment);
}).isInstanceOf(IllegalStateException.class).hasMessageContaining("fail fast property is set");
}
@Test
public void failFastWhenBothPasswordAndAuthorizationPropertiesSet() throws Exception {
Assertions.assertThatThrownBy(() -> {
ClientHttpRequestFactory requestFactory = Mockito.mock(ClientHttpRequestFactory.class);
ClientHttpRequest request = Mockito.mock(ClientHttpRequest.class);
Mockito.when(requestFactory.createRequest(Mockito.any(URI.class), Mockito.any(HttpMethod.class)))
.thenReturn(request);
ConfigClientProperties defaults = new ConfigClientProperties(this.environment);
defaults.setFailFast(true);
defaults.setUsername("username");
defaults.setPassword("password");
defaults.getHeaders().put(AUTHORIZATION, "Basic dXNlcm5hbWU6cGFzc3dvcmQNCg==");
this.locator = new ConfigServicePropertySourceLocator(defaults);
this.locator.locateCollection(this.environment);
}).isInstanceOf(IllegalStateException.class)
.hasMessageContaining("Could not locate PropertySource and the fail fast property is set, failing");
}
@Test
public void interceptorShouldAddHeadersWhenHeadersPropertySet() throws Exception {
MockClientHttpRequest request = new MockClientHttpRequest();
ClientHttpRequestExecution execution = Mockito.mock(ClientHttpRequestExecution.class);
byte[] body = new byte[] {};
Map<String, String> headers = new HashMap<>();
headers.put("X-Example-Version", "2.1");
new GenericRequestHeaderInterceptor(headers).intercept(request, body, execution);
Mockito.verify(execution).execute(request, body);
assertThat(request.getHeaders().getFirst("X-Example-Version")).isEqualTo("2.1");
}
@Test
public void shouldAddAuthorizationHeaderWhenPasswordSet() {
HttpHeaders headers = new HttpHeaders();
ConfigClientProperties defaults = new ConfigClientProperties(this.environment);
String username = "user";
String password = "pass";
factory(defaults).addAuthorizationToken(headers, username, password);
assertThat(headers).hasSize(1);
}
@Test
public void shouldAddAuthorizationHeaderWhenAuthorizationSet() {
HttpHeaders headers = new HttpHeaders();
ConfigClientProperties defaults = new ConfigClientProperties(this.environment);
defaults.getHeaders().put(AUTHORIZATION, "Basic dXNlcm5hbWU6cGFzc3dvcmQNCg==");
String username = "user";
String password = null;
factory(defaults).addAuthorizationToken(headers, username, password);
assertThat(headers).hasSize(1);
}
@Test
public void shouldThrowExceptionWhenPasswordAndAuthorizationBothSet() {
Assertions.assertThatThrownBy(() -> {
HttpHeaders headers = new HttpHeaders();
ConfigClientProperties defaults = new ConfigClientProperties(this.environment);
defaults.getHeaders().put(AUTHORIZATION, "Basic dXNlcm5hbWU6cGFzc3dvcmQNCg==");
String username = "user";
String password = "pass";
factory(defaults).addAuthorizationToken(headers, username, password);
}).isInstanceOf(IllegalStateException.class)
.hasMessageContaining("You must set either 'password' or 'authorization'");
}
@Test
public void shouldThrowExceptionWhenNegativeReadTimeoutSet() {
Assertions.assertThatThrownBy(() -> {
ConfigClientProperties defaults = new ConfigClientProperties(this.environment);
defaults.setRequestReadTimeout(-1);
factory(defaults).create();
}).isInstanceOf(IllegalStateException.class).hasMessageContaining("Invalid Value for Read Timeout set.");
}
@Test
public void shouldThrowExceptionWhenNegativeConnectTimeoutSet() {
Assertions.assertThatThrownBy(() -> {
ConfigClientProperties defaults = new ConfigClientProperties(this.environment);
defaults.setRequestConnectTimeout(-1);
factory(defaults).create();
}).isInstanceOf(IllegalStateException.class).hasMessageContaining("Invalid Value for Connect Timeout set.");
}
@Test
public void shouldNotUseNextUriFor_400_And_CONNECTION_TIMEOUT_ONLY_Strategy() throws Exception {
assertNextUriIsNotTriedForClientError(ConfigClientProperties.MultipleUriStrategy.CONNECTION_TIMEOUT_ONLY,
HttpStatus.BAD_REQUEST);
}
@Test
public void shouldUseNextUriFor_400_And_ALWAYS_Strategy() throws Exception {
assertNextUriIsTried(ConfigClientProperties.MultipleUriStrategy.ALWAYS, HttpStatus.BAD_REQUEST);
}
@Test
public void shouldNotUseNextUriFor_404_And_CONNECTION_TIMEOUT_ONLY_Strategy() throws Exception {
assertNextUriIsNotTriedForNotFoundError(ConfigClientProperties.MultipleUriStrategy.CONNECTION_TIMEOUT_ONLY,
HttpStatus.NOT_FOUND);
}
@Test
public void shouldUseNextUriFor_404_And_ALWAYS_Strategy() throws Exception {
assertNextUriIsTried(ConfigClientProperties.MultipleUriStrategy.ALWAYS, HttpStatus.NOT_FOUND);
}
@Test
public void shouldNotUseNextUriFor_500_And_CONNECTION_TIMEOUT_ONLY_Strategy() throws Exception {
assertNextUriIsNotTriedForServerError(ConfigClientProperties.MultipleUriStrategy.CONNECTION_TIMEOUT_ONLY,
HttpStatus.INTERNAL_SERVER_ERROR);
}
@Test
public void shouldUseNextUriFor_500_And_ALWAYS_Strategy() throws Exception {
assertNextUriIsTried(ConfigClientProperties.MultipleUriStrategy.ALWAYS, HttpStatus.INTERNAL_SERVER_ERROR);
}
@Test
public void shouldUseNextUriFor_TimeOut_And_ALWAYS_Strategy() throws Exception {
assertNextUriIsTriedOnTimeout(ConfigClientProperties.MultipleUriStrategy.ALWAYS);
}
@Test
public void shouldUseNextUriFor_TimeOut_And_CONNECTION_TIMEOUT_ONLY_Strategy() throws Exception {
assertNextUriIsTriedOnTimeout(ConfigClientProperties.MultipleUriStrategy.CONNECTION_TIMEOUT_ONLY);
}
@Test
public void shouldNotUseNextUriWhenOneIsSuccessful() throws Exception {
// Set up with three URIs.
ConfigClientProperties clientProperties = new ConfigClientProperties(this.environment);
String badURI1 = "http://baduri1";
String goodURI = "http://localhost:8888";
String badURI2 = "http://baduri2";
String[] uris = new String[] { badURI1, goodURI, badURI2 };
clientProperties.setUri(uris);
clientProperties.setFailFast(true);
clientProperties.setMultipleUriStrategy(ConfigClientProperties.MultipleUriStrategy.ALWAYS);
this.locator = new ConfigServicePropertySourceLocator(clientProperties);
ClientHttpRequestFactory requestFactory = Mockito.mock(ClientHttpRequestFactory.class);
RestTemplate restTemplate = new RestTemplate(requestFactory);
// Second URI will be successful, and the third one should never be called, so
// locateCollection
// should return a value.
mockRequestResponse(requestFactory, badURI1, HttpStatus.BAD_REQUEST);
mockRequestResponse(requestFactory, goodURI, HttpStatus.OK);
mockRequestResponse(requestFactory, badURI2, HttpStatus.INTERNAL_SERVER_ERROR);
this.locator.setRestTemplate(restTemplate);
assertThat(this.locator.locateCollection(this.environment)).isNotNull();
}
@Test
public void shouldUseMultipleURIs() throws Exception {
// Set up with four URIs. All should be called until one is successful
ConfigClientProperties clientProperties = new ConfigClientProperties(this.environment);
String badURI1 = "http://baduri1";
String badURI2 = "http://baduri2";
String badURI3 = "http://baduri3";
String goodURI = "http://localhost:8888";
String[] uris = new String[] { badURI1, goodURI, badURI2 };
clientProperties.setUri(uris);
clientProperties.setFailFast(true);
clientProperties.setMultipleUriStrategy(ConfigClientProperties.MultipleUriStrategy.ALWAYS);
this.locator = new ConfigServicePropertySourceLocator(clientProperties);
ClientHttpRequestFactory requestFactory = Mockito.mock(ClientHttpRequestFactory.class);
RestTemplate restTemplate = new RestTemplate(requestFactory);
// Second URI will be successful, and the third one should never be called, so
// locateCollection
// should return a value.
mockRequestResponse(requestFactory, badURI1, HttpStatus.BAD_REQUEST);
mockRequestResponse(requestFactory, badURI2, HttpStatus.INTERNAL_SERVER_ERROR);
mockRequestResponse(requestFactory, badURI3, HttpStatus.NOT_FOUND);
mockRequestResponse(requestFactory, goodURI, HttpStatus.OK);
this.locator.setRestTemplate(restTemplate);
assertThat(this.locator.locateCollection(this.environment)).isNotNull();
}
@Test
public void checkInterceptorHasNoAuthorizationHeaderPresent() {
ConfigClientProperties defaults = new ConfigClientProperties(this.environment);
defaults.getHeaders().put(AUTHORIZATION, "Basic dXNlcm5hbWU6cGFzc3dvcmQNCg==");
defaults.getHeaders().put("key", "value");
RestTemplate restTemplate = factory(defaults).create();
Iterator<ClientHttpRequestInterceptor> iterator = restTemplate.getInterceptors().iterator();
while (iterator.hasNext()) {
GenericRequestHeaderInterceptor genericRequestHeaderInterceptor = (GenericRequestHeaderInterceptor) iterator
.next();
assertThat(genericRequestHeaderInterceptor.getHeaders().get(AUTHORIZATION)).isEqualTo(null);
}
}
private ConfigClientRequestTemplateFactory factory(ConfigClientProperties properties) {
return new ConfigClientRequestTemplateFactory(LogFactory.getLog(getClass()), properties);
}
@SuppressWarnings({ "unchecked", "raw" })
@Test
public void shouldPreserveOrder() {
Environment body = new Environment("app", "master");
LinkedHashMap<Object, Object> properties = new LinkedHashMap<>();
properties.put("zuul.routes.specificproduct.path", originValue("/v1/product/electronics/**",
"Config Server /config-repo/zuul-service/zuul-service.yml:5:13"));
properties.put("zuul.routes.specificproduct.service-id", originValue("electronic-product-service",
"Config Server /config-repo/zuul-service/zuul-service.yml:6:19"));
properties.put("zuul.routes.specificproduct.strip-prefix",
originValue("false", "Config Server /config-repo/zuul-service/zuul-service.yml:7:21"));
properties.put("zuul.routes.specificproduct.sensitiveHeaders",
originValue("", "Config Server /config-repo/zuul-service/zuul-service.yml:8:24"));
properties.put("zuul.routes.genericproduct.path",
originValue("/v1/product/**", "Config Server /config-repo/zuul-service/zuul-service.yml:10:13"));
properties.put("zuul.routes.genericproduct.service-id",
originValue("product-service", "Config Server /config-repo/zuul-service/zuul-service.yml:11:19"));
properties.put("zuul.routes.genericproduct.strip-prefix",
originValue("false", "Config Server /config-repo/zuul-service/zuul-service.yml:12:21"));
properties.put("zuul.routes.genericproduct.sensitiveHeaders",
originValue("", "Config Server /config-repo/zuul-service/zuul-service.yml:13:24"));
body.add(new PropertySource("source1", properties));
mockRequestResponseWithoutLabel(new ResponseEntity<>(body, HttpStatus.OK));
this.locator.setRestTemplate(this.restTemplate);
List<org.springframework.core.env.PropertySource<?>> propertySources = new ArrayList<>(
this.locator.locateCollection(this.environment));
assertThat(propertySources).hasSize(2);
org.springframework.core.env.PropertySource<?> propertySource = propertySources.get(1);
Map source = (Map) propertySource.getSource();
Iterator iterator = source.keySet().iterator();
assertThat(iterator.next()).isEqualTo("zuul.routes.specificproduct.path");
assertThat(iterator.next()).isEqualTo("zuul.routes.specificproduct.service-id");
assertThat(source).isInstanceOf(LinkedHashMap.class);
}
private void assertNextUriIsNotTried(ConfigClientProperties.MultipleUriStrategy multipleUriStrategy,
HttpStatus firstUriResponse, Class<? extends Exception> expectedCause) {
AbstractThrowableAssert throwableAssert = Assertions.assertThatThrownBy(() -> {
// Set up with two URIs.
ConfigClientProperties clientProperties = new ConfigClientProperties(this.environment);
String badURI = "http://baduri";
String goodURI = "http://localhost:8888";
String[] uris = new String[] { badURI, goodURI };
clientProperties.setUri(uris);
clientProperties.setFailFast(true);
// Strategy is CONNECTION_TIMEOUT_ONLY, so it should not try the next URI for
// INTERNAL_SERVER_ERROR
clientProperties.setMultipleUriStrategy(multipleUriStrategy);
this.locator = new ConfigServicePropertySourceLocator(clientProperties);
ClientHttpRequestFactory requestFactory = Mockito.mock(ClientHttpRequestFactory.class);
RestTemplate restTemplate = new RestTemplate(requestFactory);
mockRequestResponse(requestFactory, badURI, firstUriResponse);
mockRequestResponse(requestFactory, goodURI, HttpStatus.OK);
this.locator.setRestTemplate(restTemplate);
this.locator.locateCollection(this.environment);
});
if (expectedCause == null) {
throwableAssert.hasNoCause().hasMessageContaining("fail fast property is set");
}
else {
throwableAssert.hasCauseInstanceOf(expectedCause).hasMessageContaining("fail fast property is set");
}
}
@SuppressWarnings("SameParameterValue")
private void assertNextUriIsNotTriedForClientError(ConfigClientProperties.MultipleUriStrategy multipleUriStrategy,
HttpStatus firstUriResponse) throws Exception {
assertNextUriIsNotTried(multipleUriStrategy, firstUriResponse, HttpClientErrorException.class);
}
@SuppressWarnings("SameParameterValue")
private void assertNextUriIsNotTriedForNotFoundError(ConfigClientProperties.MultipleUriStrategy multipleUriStrategy,
HttpStatus firstUriResponse) throws Exception {
// NOT_FOUND is treated differently
assertNextUriIsNotTried(multipleUriStrategy, firstUriResponse, null);
}
@SuppressWarnings("SameParameterValue")
private void assertNextUriIsNotTriedForServerError(ConfigClientProperties.MultipleUriStrategy multipleUriStrategy,
HttpStatus firstUriResponse) throws Exception {
assertNextUriIsNotTried(multipleUriStrategy, firstUriResponse, HttpServerErrorException.class);
}
@SuppressWarnings("SameParameterValue")
private void assertNextUriIsTried(ConfigClientProperties.MultipleUriStrategy multipleUriStrategy,
HttpStatus firstUriResponse) throws Exception {
// Set up with two URIs.
ConfigClientProperties clientProperties = new ConfigClientProperties(this.environment);
String badURI = "http://baduri";
String goodURI = "http://localhost:8888";
String[] uris = new String[] { badURI, goodURI };
clientProperties.setUri(uris);
clientProperties.setFailFast(true);
// Strategy is ALWAYS, so it should try all URIs until successful
clientProperties.setMultipleUriStrategy(multipleUriStrategy);
this.locator = new ConfigServicePropertySourceLocator(clientProperties);
ClientHttpRequestFactory requestFactory = Mockito.mock(ClientHttpRequestFactory.class);
RestTemplate restTemplate = new RestTemplate(requestFactory);
mockRequestResponse(requestFactory, badURI, firstUriResponse);
mockRequestResponse(requestFactory, goodURI, HttpStatus.OK);
this.locator.setRestTemplate(restTemplate);
assertThat(this.locator.locateCollection(this.environment)).isNotNull();
}
private void assertNextUriIsTriedOnTimeout(ConfigClientProperties.MultipleUriStrategy multipleUriStrategy)
throws Exception {
// Set up with two URIs.
ConfigClientProperties clientProperties = new ConfigClientProperties(this.environment);
String badURI = "http://baduri";
String goodURI = "http://localhost:8888";
String[] uris = new String[] { badURI, goodURI };
clientProperties.setUri(uris);
clientProperties.setFailFast(true);
// Strategy should not matter when the error is connection timed out
clientProperties.setMultipleUriStrategy(multipleUriStrategy);
this.locator = new ConfigServicePropertySourceLocator(clientProperties);
ClientHttpRequestFactory requestFactory = Mockito.mock(ClientHttpRequestFactory.class);
RestTemplate restTemplate = new RestTemplate(requestFactory);
// First URI times out. Second one is successful
mockRequestTimedOut(requestFactory, badURI);
mockRequestResponse(requestFactory, goodURI, HttpStatus.OK);
this.locator.setRestTemplate(restTemplate);
assertThat(this.locator.locateCollection(this.environment)).isNotNull();
}
private Map<String, Object> originValue(String value, String origin) {
HashMap<String, Object> map = new HashMap<>();
map.put("value", value);
map.put("origin", origin);
return map;
}
private void mockRequestResponse(ClientHttpRequestFactory requestFactory, String baseURI, HttpStatus status)
throws Exception {
ClientHttpRequest request = Mockito.mock(ClientHttpRequest.class);
ClientHttpResponse response = Mockito.mock(ClientHttpResponse.class);
if (baseURI == null) {
Mockito.when(requestFactory.createRequest(Mockito.any(URI.class), Mockito.any(HttpMethod.class)))
.thenReturn(request);
}
else {
Mockito.when(requestFactory.createRequest(Mockito.eq(new URI(baseURI + "/application/default")),
Mockito.any(HttpMethod.class))).thenReturn(request);
}
Mockito.when(request.getHeaders()).thenReturn(new HttpHeaders());
Mockito.when(request.execute()).thenReturn(response);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
Mockito.when(response.getHeaders()).thenReturn(headers);
Mockito.when(response.getStatusCode()).thenReturn(status);
Mockito.when(response.getRawStatusCode()).thenReturn(status.value());
Mockito.when(response.getBody()).thenReturn(new ByteArrayInputStream("{}".getBytes()));
}
@SuppressWarnings("unchecked")
private void mockRequestResponseWithLabel(ResponseEntity<?> response, String label) {
Mockito.when(this.restTemplate.exchange(Mockito.any(String.class), Mockito.any(HttpMethod.class),
Mockito.any(HttpEntity.class), Mockito.any(Class.class), anyString(), anyString(),
ArgumentMatchers.eq(label))).thenReturn(response);
}
@SuppressWarnings("unchecked")
private void mockRequestResponseWithoutLabel(ResponseEntity<?> response) {
Mockito.when(this.restTemplate.exchange(Mockito.any(String.class), Mockito.any(HttpMethod.class),
Mockito.any(HttpEntity.class), Mockito.any(Class.class), anyString(), anyString()))
.thenReturn(response);
}
@SuppressWarnings("unchecked")
private void mockRequestTimedOut() {
Mockito.when(this.restTemplate.exchange(Mockito.any(String.class), Mockito.any(HttpMethod.class),
Mockito.any(HttpEntity.class), Mockito.any(Class.class), anyString(), anyString()))
.thenThrow(ResourceAccessException.class);
}
private void mockRequestTimedOut(ClientHttpRequestFactory requestFactory, String baseURI) throws Exception {
ClientHttpRequest request = Mockito.mock(ClientHttpRequest.class);
if (baseURI == null) {
Mockito.when(requestFactory.createRequest(Mockito.any(URI.class), Mockito.any(HttpMethod.class)))
.thenReturn(request);
}
else {
Mockito.when(requestFactory.createRequest(Mockito.eq(new URI(baseURI + "/application/default")),
Mockito.any(HttpMethod.class))).thenReturn(request);
}
Mockito.when(request.getHeaders()).thenReturn(new HttpHeaders());
Mockito.when(request.execute()).thenThrow(IOException.class);
}
}

View File

@@ -1,183 +0,0 @@
/*
* Copyright 2013-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.client;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.BootstrapRegistry;
import org.springframework.boot.BootstrapRegistryInitializer;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.properties.bind.BindHandler;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.config.client.ConfigClientProperties.Credentials;
import org.springframework.cloud.test.ClassPathExclusions;
import org.springframework.context.ConfigurableApplicationContext;
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;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.cloud.config.client.ConfigClientProperties.Discovery.DEFAULT_CONFIG_SERVER;
/**
* @author Dave Syer
*/
@ClassPathExclusions({ "spring-retry-*.jar", "spring-boot-starter-aop-*.jar" })
public class DiscoveryClientConfigDataConfigurationNoRetryTests {
protected ConfigurableApplicationContext context;
protected DiscoveryClient client = mock(DiscoveryClient.class);
protected ServiceInstance info = new DefaultServiceInstance("app:8877", "app", "foo", 8877, false);
@AfterEach
public void close() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void shouldFailWithExceptionGetConfigServerInstanceFromDiscoveryClient() throws Exception {
givenDiscoveryClientReturnsNoInfo();
ConfigServerInstanceProvider.Function function = mock(ConfigServerInstanceProvider.Function.class);
when(function.apply(anyString(), any(Binder.class), any(BindHandler.class), any(Log.class)))
.thenAnswer(invocation -> client.getInstances(invocation.getArgument(0)));
assertThatThrownBy(() -> context = setup(true, function, "spring.cloud.config.discovery.enabled=true",
"spring.cloud.config.fail-fast=true").run()).isInstanceOf(IllegalStateException.class)
.hasMessageContaining("No instances found of configserver");
verify(function).apply(eq(DEFAULT_CONFIG_SERVER), any(Binder.class), any(BindHandler.class), any(Log.class));
verify(function, never()).apply(eq(DEFAULT_CONFIG_SERVER));
}
@Test
public void shouldFailWithMessageGetConfigServerInstanceFromDiscoveryClient() throws Exception {
givenDiscoveryClientReturnsNoInfo();
ConfigServerInstanceProvider.Function function = mock(ConfigServerInstanceProvider.Function.class);
when(function.apply(anyString(), any(Binder.class), any(BindHandler.class), any(Log.class)))
.thenAnswer(invocation -> client.getInstances(invocation.getArgument(0)));
context = setup(true, function, "spring.cloud.config.discovery.enabled=true",
"spring.cloud.config.fail-fast=false").run();
// expectDiscoveryClientConfigServiceBootstrapConfigurationIsSetup();
expectConfigClientPropertiesHasDefaultConfiguration();
verifyDiscoveryClientCalledOnce();
verify(function).apply(eq(DEFAULT_CONFIG_SERVER), any(Binder.class), any(BindHandler.class), any(Log.class));
verify(function, never()).apply(eq(DEFAULT_CONFIG_SERVER));
}
@Test
public void shouldSucceedGetConfigServerInstanceFromDiscoveryClient() throws Exception {
givenDiscoveryClientReturnsInfo();
ConfigServerInstanceProvider.Function function = mock(ConfigServerInstanceProvider.Function.class);
when(function.apply(eq(DEFAULT_CONFIG_SERVER), any(Binder.class), any(BindHandler.class), any(Log.class)))
.thenAnswer(invocation -> client.getInstances(invocation.getArgument(0)));
context = setup(true, function, "spring.cloud.config.discovery.enabled=true",
"spring.cloud.config.fail-fast=true").run();
// expectDiscoveryClientConfigServiceBootstrapConfigurationIsSetup();
// expectConfigClientPropertiesHasConfigurationFromEureka();
verifyDiscoveryClientCalledOnce();
verify(function).apply(eq(DEFAULT_CONFIG_SERVER), any(Binder.class), any(BindHandler.class), any(Log.class));
verify(function, never()).apply(eq(DEFAULT_CONFIG_SERVER));
}
SpringApplicationBuilder setup(boolean addInstanceProvider, ConfigServerInstanceProvider.Function function,
String... env) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(TestConfig.class)
.properties(addDefaultEnv(env));
if (addInstanceProvider) {
builder.addBootstrapRegistryInitializer(instanceProviderBootstrapper(function));
// ignore actual calls to config server since we're just testing discovery
// client.
builder.addBootstrapRegistryInitializer(registry -> registry
.register(ConfigServerBootstrapper.LoaderInterceptor.class, ctx -> loadContext -> null));
}
return builder.addBootstrapRegistryInitializer(registry -> registry.addCloseListener(event -> {
ConfigServerInstanceMonitor monitor = event.getBootstrapContext().get(ConfigServerInstanceMonitor.class);
assertThat(monitor).as("ConfigServerInstanceMonitor was not created when it should").isNotNull();
}));
}
protected BootstrapRegistryInitializer instanceProviderBootstrapper(
ConfigServerInstanceProvider.Function function) {
return registry -> registry.register(ConfigServerInstanceProvider.Function.class,
BootstrapRegistry.InstanceSupplier.from(() -> function));
}
private String[] addDefaultEnv(String[] env) {
Set<String> set = new LinkedHashSet<>();
if (env != null && env.length > 0) {
set.addAll(Arrays.asList(env));
}
set.add("spring.cloud.config.discovery.enabled=true");
set.add("spring.config.import=optional:configserver:");
return set.toArray(new String[0]);
}
void givenDiscoveryClientReturnsInfo() {
givenDiscoveryClientReturnsInfo(this.info);
}
void givenDiscoveryClientReturnsInfo(ServiceInstance... instances) {
given(this.client.getInstances(DEFAULT_CONFIG_SERVER)).willReturn(Arrays.asList(instances));
}
void givenDiscoveryClientReturnsNoInfo() {
given(this.client.getInstances(DEFAULT_CONFIG_SERVER)).willReturn(Collections.emptyList());
}
void verifyDiscoveryClientCalledOnce() {
verify(this.client).getInstances(DEFAULT_CONFIG_SERVER);
}
void expectConfigClientPropertiesHasDefaultConfiguration() {
expectConfigClientPropertiesHasConfiguration("http://localhost:8888");
}
void expectConfigClientPropertiesHasConfiguration(final String expectedUri) {
ConfigClientProperties properties = this.context.getBean(ConfigClientProperties.class);
Credentials credentials = properties.getCredentials(0);
assertThat(credentials.getUri()).isEqualTo(expectedUri);
}
@SpringBootConfiguration
@EnableAutoConfiguration
static class TestConfig {
}
}

View File

@@ -1,296 +0,0 @@
/*
* Copyright 2013-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.client;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.boot.BootstrapRegistry;
import org.springframework.boot.BootstrapRegistryInitializer;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.cloud.config.client.ConfigClientProperties.Credentials;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.fail;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.springframework.cloud.config.client.ConfigClientProperties.Discovery.DEFAULT_CONFIG_SERVER;
/**
* @author Dave Syer
*/
public class DiscoveryClientConfigDataConfigurationTests {
protected ConfigurableApplicationContext context;
protected DiscoveryClient client = Mockito.mock(DiscoveryClient.class);
protected ServiceInstance info = new DefaultServiceInstance("app:8877", "app", "foo", 8877, false);
@AfterEach
public void close() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void offByDefault() {
context = new SpringApplicationBuilder(TestConfig.class)
.properties("spring.config.import=optional:configserver:")
.addBootstrapRegistryInitializer(registry -> registry.addCloseListener(event -> {
try {
event.getBootstrapContext().get(ConfigServerInstanceMonitor.class);
fail("ConfigServerInstanceMonitor was created when it shouldn't");
}
catch (IllegalStateException e) {
// expected
}
})).run();
}
@Test
public void onWhenRequested() {
givenDiscoveryClientReturnsInfo();
setupAndRun();
verifyDiscoveryClientCalledOnce();
expectConfigClientPropertiesHasConfigurationFromDiscovery();
}
@Test
public void onWhenHeartbeat() {
givenDiscoveryClientReturnsInfo();
setupAndRun();
verifyDiscoveryClientCalledOnce();
this.context.publishEvent(new HeartbeatEvent(this.context, "new"));
expectConfigClientPropertiesHasConfigurationFromDiscovery();
}
@Test
public void secureWhenRequested() {
this.info = new DefaultServiceInstance("app:443", "app", "foo", 443, true);
givenDiscoveryClientReturnsInfo();
setupAndRun();
verifyDiscoveryClientCalledOnce();
expectConfigClientPropertiesHasConfiguration("https://foo:443/");
}
@Test
public void multipleInstancesReturnedFromDiscovery() {
ServiceInstance info1 = new DefaultServiceInstance("app1:8888", "app", "localhost", 8888, true);
ServiceInstance info2 = new DefaultServiceInstance("app2:8888", "app", "localhost1", 8888, false);
givenDiscoveryClientReturnsInfo(info1, info2);
setupAndRun();
verifyDiscoveryClientCalledOnce();
ConfigClientProperties properties = this.context.getBean(ConfigClientProperties.class);
assertThat(properties.getUri().length).isEqualTo(2);
Credentials credentials1 = properties.getCredentials(0);
Credentials credentials2 = properties.getCredentials(1);
assertThat(credentials1.getUri()).isEqualTo("https://localhost:8888/");
assertThat(credentials2.getUri()).isEqualTo("http://localhost1:8888/");
}
@Test
public void setsPasssword() {
this.info.getMetadata().put("password", "bar");
givenDiscoveryClientReturnsInfo();
setupAndRun();
ConfigClientProperties locator = this.context.getBean(ConfigClientProperties.class);
Credentials credentials = locator.getCredentials(0);
assertThat(credentials.getUri()).isEqualTo("http://foo:8877/");
assertThat(credentials.getPassword()).isEqualTo("bar");
assertThat(credentials.getUsername()).isEqualTo("user");
}
@Test
public void setsPath() {
this.info.getMetadata().put("configPath", "/bar");
givenDiscoveryClientReturnsInfo();
setupAndRun();
expectConfigClientPropertiesHasConfiguration("http://foo:8877/bar");
}
@Test
public void shouldFailGetConfigServerInstanceFromDiscoveryClient() {
givenDiscoveryClientReturnsNoInfo();
setupAndRun();
verifyDiscoveryClientCalledOnce();
expectConfigClientPropertiesHasDefaultConfiguration();
}
@Test
public void shouldRetryAndSucceedGetConfigServerInstanceFromDiscoveryClient() {
givenDiscoveryClientReturnsInfoOnThirdTry();
context = setup("spring.cloud.config.retry.maxAttempts=3", "spring.cloud.config.retry.initialInterval=10",
"spring.cloud.config.fail-fast=true").run();
verifyDiscoveryClientCalledThreeTimes();
this.context.publishEvent(new HeartbeatEvent(this.context, "new"));
expectConfigClientPropertiesHasConfigurationFromDiscovery();
}
@Test
public void shouldNotRetryIfNotFailFastPropertySet() {
givenDiscoveryClientReturnsInfoOnThirdTry();
context = setup("spring.cloud.config.retry.maxAttempts=3", "spring.cloud.config.retry.initialInterval=10")
.run();
verifyDiscoveryClientCalledOnce();
expectConfigClientPropertiesHasDefaultConfiguration();
}
@Test
public void shouldRetryAndFailWithExceptionGetConfigServerInstanceFromDiscoveryClient() {
givenDiscoveryClientReturnsNoInfo();
assertThatThrownBy(() -> context = setup("spring.cloud.config.retry.maxAttempts=3",
"spring.cloud.config.retry.initialInterval=10", "spring.cloud.config.fail-fast=true").run())
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("No instances found of configserver");
}
@Test
public void shouldRetryAndFailWithMessageGetConfigServerInstanceFromDiscoveryClient() {
givenDiscoveryClientReturnsNoInfo();
context = setup("spring.cloud.config.retry.maxAttempts=3", "spring.cloud.config.retry.initialInterval=10",
"spring.cloud.config.fail-fast=false").run();
expectConfigClientPropertiesHasDefaultConfiguration();
}
SpringApplicationBuilder setup(String... env) {
return setup(true, env);
}
SpringApplicationBuilder setup(boolean addInstanceProvider, String... env) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(TestConfig.class)
.properties(addDefaultEnv(env));
if (addInstanceProvider) {
builder.addBootstrapRegistryInitializer(instanceProviderBootstrapper());
// ignore actual calls to config server since we're just testing discovery
// client.
builder.addBootstrapRegistryInitializer(registry -> registry
.register(ConfigServerBootstrapper.LoaderInterceptor.class, ctx -> loadContext -> null));
}
return builder.addBootstrapRegistryInitializer(registry -> registry.addCloseListener(event -> {
ConfigServerInstanceMonitor monitor = event.getBootstrapContext().get(ConfigServerInstanceMonitor.class);
assertThat(monitor).as("ConfigServerInstanceMonitor was not created when it should").isNotNull();
}));
}
protected BootstrapRegistryInitializer instanceProviderBootstrapper() {
return registry -> registry.register(ConfigServerInstanceProvider.Function.class,
BootstrapRegistry.InstanceSupplier.from(() -> this.client::getInstances));
}
protected void setupAndRun(String... env) {
context = setup(addDefaultEnv(env)).run();
}
private String[] addDefaultEnv(String[] env) {
Set<String> set = new LinkedHashSet<>();
if (env != null && env.length > 0) {
set.addAll(Arrays.asList(env));
}
set.add("spring.cloud.config.discovery.enabled=true");
set.add("spring.config.import=optional:configserver:");
return set.toArray(new String[0]);
}
void givenDiscoveryClientReturnsInfo() {
givenDiscoveryClientReturnsInfo(this.info);
}
void givenDiscoveryClientReturnsInfo(ServiceInstance... instances) {
given(this.client.getInstances(DEFAULT_CONFIG_SERVER)).willReturn(Arrays.asList(instances));
}
void givenDiscoveryClientReturnsNoInfo() {
given(this.client.getInstances(DEFAULT_CONFIG_SERVER)).willReturn(Collections.emptyList());
}
void givenDiscoveryClientReturnsInfoOnThirdTry() {
given(this.client.getInstances(DEFAULT_CONFIG_SERVER)).willReturn(Collections.emptyList())
.willReturn(Collections.emptyList()).willReturn(Collections.singletonList(this.info));
}
void verifyDiscoveryClientCalledOnce() {
verify(this.client).getInstances(DEFAULT_CONFIG_SERVER);
}
void verifyDiscoveryClientCalledThreeTimes() {
verify(this.client, times(3)).getInstances(DEFAULT_CONFIG_SERVER);
}
void expectConfigClientPropertiesHasConfigurationFromDiscovery() {
expectConfigClientPropertiesHasConfiguration("http://foo:8877/");
}
void expectConfigClientPropertiesHasDefaultConfiguration() {
expectConfigClientPropertiesHasConfiguration("http://localhost:8888");
}
void expectConfigClientPropertiesHasConfiguration(final String expectedUri) {
ConfigClientProperties properties = this.context.getBean(ConfigClientProperties.class);
Credentials credentials = properties.getCredentials(0);
assertThat(credentials.getUri()).isEqualTo(expectedUri);
}
@SpringBootConfiguration
@EnableAutoConfiguration
static class TestConfig {
}
}

View File

@@ -1,60 +0,0 @@
/*
* 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.client;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.test.ClassPathExclusions;
import static org.springframework.cloud.config.client.ConfigClientProperties.Discovery.DEFAULT_CONFIG_SERVER;
@ClassPathExclusions({ "spring-retry-*.jar", "spring-boot-starter-aop-*.jar" })
public class DiscoveryClientConfigServiceBootstrapConfigurationNoSpringRetryTests
extends BaseDiscoveryClientConfigServiceBootstrapConfigurationTests {
@Test
public void shouldFailWithExceptionGetConfigServerInstanceFromDiscoveryClient() throws Exception {
org.assertj.core.api.Assertions.assertThatThrownBy(() -> {
givenDiscoveryClientReturnsNoInfo();
setup("spring.cloud.config.discovery.enabled=true", "spring.cloud.config.fail-fast=true");
}).isInstanceOf(IllegalStateException.class)
.hasMessageContaining("No instances found of configserver (" + DEFAULT_CONFIG_SERVER + ")");
}
@Test
public void shouldFailWithMessageGetConfigServerInstanceFromDiscoveryClient() throws Exception {
givenDiscoveryClientReturnsNoInfo();
setup("spring.cloud.config.discovery.enabled=true", "spring.cloud.config.fail-fast=false");
expectDiscoveryClientConfigServiceBootstrapConfigurationIsSetup();
expectConfigClientPropertiesHasDefaultConfiguration();
verifyDiscoveryClientCalledOnce();
}
@Test
public void shouldSucceedGetConfigServerInstanceFromDiscoveryClient() throws Exception {
givenDiscoveryClientReturnsInfo();
setup("spring.cloud.config.discovery.enabled=true", "spring.cloud.config.fail-fast=true");
expectDiscoveryClientConfigServiceBootstrapConfigurationIsSetup();
expectConfigClientPropertiesHasConfigurationFromEureka();
verifyDiscoveryClientCalledOnce();
}
}

View File

@@ -1,200 +0,0 @@
/*
* Copyright 2013-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.client;
import java.util.Collections;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.cloud.config.client.ConfigClientProperties.Credentials;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.springframework.cloud.config.client.ConfigClientProperties.Discovery.DEFAULT_CONFIG_SERVER;
/**
* @author Dave Syer
*/
public class DiscoveryClientConfigServiceBootstrapConfigurationTests
extends BaseDiscoveryClientConfigServiceBootstrapConfigurationTests {
@Test
public void offByDefault() throws Exception {
this.context = new AnnotationConfigApplicationContext(DiscoveryClientConfigServiceBootstrapConfiguration.class);
assertThat(this.context.getBeanNamesForType(DiscoveryClient.class).length).isEqualTo(0);
assertThat(this.context.getBeanNamesForType(DiscoveryClientConfigServiceBootstrapConfiguration.class).length)
.isEqualTo(0);
}
@Test
public void onWhenRequested() throws Exception {
givenDiscoveryClientReturnsInfo();
setup("spring.cloud.config.discovery.enabled=true");
expectDiscoveryClientConfigServiceBootstrapConfigurationIsSetup();
verifyDiscoveryClientCalledOnce();
expectConfigClientPropertiesHasConfigurationFromEureka();
}
@Test
public void configServerInstanceProviderFunction() {
ConfigServerInstanceProvider.Function function = mock(ConfigServerInstanceProvider.Function.class);
given(function.apply(DEFAULT_CONFIG_SERVER)).willReturn(Collections.singletonList(this.info));
setup(false, false, "spring.cloud.config.discovery.enabled=true");
this.context.getDefaultListableBeanFactory().registerSingleton("myFunction", function);
this.context.refresh();
expectDiscoveryClientConfigServiceBootstrapConfigurationIsSetup();
verify(function).apply(DEFAULT_CONFIG_SERVER);
expectConfigClientPropertiesHasConfigurationFromEureka();
}
@Test
public void onWhenHeartbeat() throws Exception {
setup("spring.cloud.config.discovery.enabled=true");
expectDiscoveryClientConfigServiceBootstrapConfigurationIsSetup();
givenDiscoveryClientReturnsInfo();
verifyDiscoveryClientCalledOnce();
this.context.publishEvent(new HeartbeatEvent(this.context, "new"));
expectConfigClientPropertiesHasConfigurationFromEureka();
}
@Test
public void secureWhenRequested() throws Exception {
this.info = new DefaultServiceInstance("app:443", "app", "foo", 443, true);
givenDiscoveryClientReturnsInfo();
setup("spring.cloud.config.discovery.enabled=true");
expectDiscoveryClientConfigServiceBootstrapConfigurationIsSetup();
verifyDiscoveryClientCalledOnce();
expectConfigClientPropertiesHasConfiguration("https://foo:443/");
}
@Test
public void multipleInstancesReturnedFromDiscovery() {
ServiceInstance info1 = new DefaultServiceInstance("app1:8888", "app", "localhost", 8888, true);
ServiceInstance info2 = new DefaultServiceInstance("app2:8888", "app", "localhost1", 8888, false);
givenDiscoveryClientReturnsInfoForMultipleInstances(info1, info2);
setup("spring.cloud.config.discovery.enabled=true");
expectDiscoveryClientConfigServiceBootstrapConfigurationIsSetup();
verifyDiscoveryClientCalledOnce();
expectConfigClientPropertiesHasMultipleUris("https://localhost:8888/", "http://localhost1:8888/");
}
@Test
public void setsPasssword() throws Exception {
this.info.getMetadata().put("password", "bar");
givenDiscoveryClientReturnsInfo();
setup("spring.cloud.config.discovery.enabled=true");
ConfigClientProperties locator = this.context.getBean(ConfigClientProperties.class);
Credentials credentials = locator.getCredentials(0);
assertThat(credentials.getUri()).isEqualTo("http://foo:8877/");
assertThat(credentials.getPassword()).isEqualTo("bar");
assertThat(credentials.getUsername()).isEqualTo("user");
}
@Test
public void setsPath() throws Exception {
this.info.getMetadata().put("configPath", "/bar");
givenDiscoveryClientReturnsInfo();
setup("spring.cloud.config.discovery.enabled=true");
expectConfigClientPropertiesHasConfiguration("http://foo:8877/bar");
}
@Test
public void shouldFailGetConfigServerInstanceFromDiscoveryClient() throws Exception {
givenDiscoveryClientReturnsNoInfo();
setup("spring.cloud.config.discovery.enabled=true");
expectDiscoveryClientConfigServiceBootstrapConfigurationIsSetup();
verifyDiscoveryClientCalledOnce();
expectConfigClientPropertiesHasDefaultConfiguration();
}
@Test
public void shouldRetryAndSucceedGetConfigServerInstanceFromDiscoveryClient() throws Exception {
givenDiscoveryClientReturnsInfoOnThirdTry();
setup("spring.cloud.config.discovery.enabled=true", "spring.cloud.config.retry.maxAttempts=3",
"spring.cloud.config.retry.initialInterval=10", "spring.cloud.config.fail-fast=true");
expectDiscoveryClientConfigServiceBootstrapConfigurationIsSetup();
verifyDiscoveryClientCalledThreeTimes();
this.context.publishEvent(new HeartbeatEvent(this.context, "new"));
expectConfigClientPropertiesHasConfigurationFromEureka();
}
@Test
public void shouldNotRetryIfNotFailFastPropertySet() throws Exception {
givenDiscoveryClientReturnsInfoOnThirdTry();
setup("spring.cloud.config.discovery.enabled=true", "spring.cloud.config.retry.maxAttempts=3",
"spring.cloud.config.retry.initialInterval=10");
expectDiscoveryClientConfigServiceBootstrapConfigurationIsSetup();
verifyDiscoveryClientCalledOnce();
expectConfigClientPropertiesHasDefaultConfiguration();
}
@Test
public void shouldRetryAndFailWithExceptionGetConfigServerInstanceFromDiscoveryClient() {
org.assertj.core.api.Assertions.assertThatThrownBy(() -> {
givenDiscoveryClientReturnsNoInfo();
setup("spring.cloud.config.discovery.enabled=true", "spring.cloud.config.retry.maxAttempts=3",
"spring.cloud.config.retry.initialInterval=10", "spring.cloud.config.fail-fast=true");
}).isInstanceOf(IllegalStateException.class);
}
@Test
public void shouldRetryAndFailWithMessageGetConfigServerInstanceFromDiscoveryClient() throws Exception {
givenDiscoveryClientReturnsNoInfo();
setup("spring.cloud.config.discovery.enabled=true", "spring.cloud.config.retry.maxAttempts=3",
"spring.cloud.config.retry.initialInterval=10", "spring.cloud.config.fail-fast=false");
expectDiscoveryClientConfigServiceBootstrapConfigurationIsSetup();
expectConfigClientPropertiesHasDefaultConfiguration();
}
}

View File

@@ -1,42 +0,0 @@
/*
* Copyright 2013-2020 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.environment;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class EnvironmentTests {
@Test
public void normalizeWorks() {
assertThat(Environment.normalize("abc123")).isEqualTo("abc123");
assertThat(Environment.normalize("abc(_)123")).isEqualTo("abc/123");
assertThat(Environment.normalize("abc(%5F)123")).isEqualTo("abc(%5F)123");
assertThat(Environment.normalize("abc%28_%29123")).isEqualTo("abc%28_%29123");
assertThat(Environment.normalize("abc%28%5F%29123")).isEqualTo("abc%28%5F%29123");
}
@Test
public void denormalizeWorks() {
assertThat(Environment.denormalize("abc123")).isEqualTo("abc123");
assertThat(Environment.denormalize("abc/123")).isEqualTo("abc(_)123");
assertThat(Environment.denormalize("abc%2F123")).isEqualTo("abc%2F123");
assertThat(Environment.denormalize("abc%25F123")).isEqualTo("abc%25F123");
}
}

View File

@@ -1,3 +0,0 @@
spring:
application:
name: foo