Initial commit
This commit is contained in:
185
spring-cloud-config-client/.flattened-pom.xml
Normal file
185
spring-cloud-config-client/.flattened-pom.xml
Normal 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>
|
||||
@@ -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>
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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() + "/";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
+ "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,2 +0,0 @@
|
||||
org.springframework.aot.hint.RuntimeHintsRegistrar=\
|
||||
org.springframework.cloud.config.client.ConfigClientHints
|
||||
@@ -1 +0,0 @@
|
||||
org.springframework.cloud.config.client.ConfigClientAutoConfiguration
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
spring:
|
||||
application:
|
||||
name: foo
|
||||
Reference in New Issue
Block a user