Compare commits

..
64 Commits
Author SHA1 Message Date
buildmaster 3d27038710 Update SNAPSHOT to 3.1.7 2023-06-29 11:22:46 +00:00
Ryan BaxterandRyan Baxter 221eb0937c Implement new apply method in ConfigServerInstanceProvider (#4175)
Co-authored-by: Ryan Baxter <524254+ryanjbaxter@users.noreply.github.com>
2023-06-13 09:03:36 -04:00
buildmaster c2dcdacaa2 Bumping versions 2023-04-28 14:22:31 +00:00
buildmaster 7fbf5a51de Bumping versions to 3.1.7-SNAPSHOT after release 2023-04-27 15:06:45 +00:00
buildmaster 078db236b8 Going back to snapshots 2023-04-27 15:06:45 +00:00
buildmaster 132cf9e4f7 Update SNAPSHOT to 3.1.6 2023-04-27 15:04:15 +00:00
spencergibb 147d50f2c2 Migrates from WebClient.exchange() to retrieve()
retrieve() should clean up resources automatically while exchange() requires explicit handling that wasn't being done.

Fixes gh-3862
2023-03-03 17:47:27 -05:00
spencergibb ded713ecc1 Removes comments 2023-03-03 17:44:24 -05:00
buildmaster 890f1ef4d6 Bumping versions to 3.1.6-SNAPSHOT after release 2023-02-23 23:14:31 +00:00
buildmaster 25796a27f0 Going back to snapshots 2023-02-23 23:14:31 +00:00
buildmaster 327b126207 Update SNAPSHOT to 3.1.5 2023-02-23 23:12:59 +00:00
spencergibb 5298970764 skip deploy of test module 2022-12-19 13:05:01 -05:00
Olga Maciaszek-Sharma 6fef357742 Fix javadocs. Add deprecation. 2022-12-13 12:31:22 +01:00
G1 5844550f9a feat: Adds RestTemplate default ConnectTimeout / ConnectRequestTimeout (#4148) 2022-12-13 12:21:57 +01:00
buildmaster c44426f579 Bumping versions 2022-11-29 14:26:47 +00:00
buildmaster b467a2f462 Going back to snapshots 2022-09-06 20:18:20 +00:00
buildmaster 97ee8144be Update SNAPSHOT to 3.1.4 2022-09-06 20:16:46 +00:00
buildmaster 4ba8c4ae53 Bumping versions to 3.1.5-SNAPSHOT after release 2022-09-02 23:42:42 +00:00
buildmaster 695787a326 Going back to snapshots 2022-09-02 23:42:41 +00:00
buildmaster e32db575b8 Update SNAPSHOT to 3.1.4 2022-09-02 23:40:48 +00:00
Olga Maciaszek-Sharma a30b87cdda Remove unused Codacy badge. Fixes gh-4105. 2022-09-01 15:58:57 +02:00
Vilius Šumskas 20a3671055 Fix CSS and JS on Eureka Dashboard (#4096)
* Fix CSS and JS on Eureka Dashboard

The dashboard interface was broken after changes in
https://github.com/spring-cloud/spring-cloud-netflix/pull/4075.
Bootstrap has changed a lot since version 3.

This patch converts dashboard's CSS to Bootstrap v.5 API.

Also, remove unnecessary JavaScript. Table striping and tab activation
is done completely in Bootstrap now. While at it, remove IE support.
It is not supported in Bootstrap v.5 anymore.

* Fix dashboard navigation tests
2022-08-31 11:42:26 +02:00
spencergibb 2497900d3e Removes compile dependency on config server.
Configures duplicate-finder-maven-plugin to ignore tests.
2022-08-05 16:29:25 -04:00
Olga Maciaszek-Sharma c6a7ebb918 merge 3.0.x 2022-07-01 15:37:50 +02:00
buildmaster 9d6e3523dd Bumping versions 2022-06-30 14:26:40 +00:00
buildmaster 38e6b0b2b5 Bumping versions to 3.0.7-SNAPSHOT after release 2022-06-29 22:12:30 +00:00
buildmaster 1229e07636 Going back to snapshots 2022-06-29 22:12:30 +00:00
buildmaster 852e72f5e6 Update SNAPSHOT to 3.0.6 2022-06-29 22:10:18 +00:00
Olga Maciaszek-Sharma 8b2b6b5115 Update license dates for bugfix backport. 2022-06-29 15:42:19 +02:00
Weix Sun f015f5cad4 Fixes gh-4032 (#4086) 2022-06-29 15:14:48 +02:00
Olga Maciaszek-Sharma 612b6edb41 Add new tests and refactor. 2022-06-28 18:52:06 +02:00
Olga Maciaszek-Sharma b633c8af96 Bugfix. 2022-06-28 18:51:57 +02:00
Olga Maciaszek-Sharma 079ae668c0 Add resolving Health Status from composite indicators. 2022-06-28 18:51:44 +02:00
Olga Maciaszek-Sharma a5e038cf13 Do not set status to down. (#4102) 2022-06-13 18:57:46 +02:00
Ryan Baxter ec903900c9 Return empty list in bootstraper when discovery client disabled. (#4099) 2022-06-03 16:03:21 -04:00
spencergibb 9b6b0aba4e Bumps to next snapshot version 2022-05-27 13:32:52 -04:00
buildmaster e5a52ee5ac Update SNAPSHOT to 3.1.3 2022-05-27 13:28:08 -04:00
Spencer Gibb 6548e0dab6 don't upload docs to maven central 2022-05-26 12:06:17 -04:00
buildmaster 2899b42107 Bumping versions 2022-04-28 14:26:35 +00:00
buildmaster a06f6e98bd Bumping versions 2022-04-27 14:26:45 +00:00
buildmaster 2b51d15d87 Bumping versions to 3.1.3-SNAPSHOT after release 2022-04-27 10:49:16 +00:00
buildmaster cb3cc3234f Going back to snapshots 2022-04-27 10:49:15 +00:00
buildmaster b11c4c9b20 Update SNAPSHOT to 3.1.2 2022-04-27 10:47:33 +00:00
Olga Maciaszek-Sharma 4b5d0a0b86 Revert "Update SNAPSHOT to 3.1.2"
This reverts commit 96b10d40c3.
2022-04-27 11:08:20 +02:00
Olga Maciaszek-Sharma 4fda5b40b8 Revert "Going back to snapshots"
This reverts commit 872d64494b.
2022-04-27 11:08:15 +02:00
Olga Maciaszek-Sharma 719688f4c8 Revert "Bumping versions to 3.1.3-SNAPSHOT after release"
This reverts commit 162b1c8a9b.
2022-04-27 11:08:12 +02:00
buildmaster 162b1c8a9b Bumping versions to 3.1.3-SNAPSHOT after release 2022-04-26 15:58:48 +00:00
buildmaster 872d64494b Going back to snapshots 2022-04-26 15:58:48 +00:00
buildmaster 96b10d40c3 Update SNAPSHOT to 3.1.2 2022-04-26 15:57:09 +00:00
Marcin Grzejszczak 28f1848b85 Update runAcceptanceTests.sh 2022-04-20 13:05:58 +02:00
Marcin Grzejszczak 83c74b0017 Update runAcceptanceTests.sh 2022-04-20 13:05:45 +02:00
Weix Sun a1a6ee244c Fixes gh-4032 (#4086) 2022-04-13 13:00:41 +02:00
alceil 6aafd20d60 Documented eureka.server.defaultOpenForTrafficCount (#4083) 2022-03-18 13:09:02 +01:00
Olga Maciaszek-Sharma 1efa843ed8 Get health from composite indicators (#4074) 2022-03-17 12:25:57 +01:00
Koki Igarashi eafecd3b6a Documents the eureka version in the context path (#4079) 2022-03-17 10:55:24 +01:00
Olga Maciaszek-Sharma e757479f85 Upgrade Eureka Server GUI dependencies and components. (#4075) 2022-03-11 16:23:06 +01:00
buildmaster c350c88267 Bumping versions 2022-02-24 14:29:24 +00:00
buildmaster 827be605b3 Bumping versions 2022-02-24 07:49:06 +00:00
Olga Maciaszek-Sharma 8f9797627f Merge remote-tracking branch 'MI-cool/reduceHighLoadOnRenew' into 3.1.x 2022-02-22 17:24:48 +01:00
buildmaster 75d77cd38e Bumping versions to 3.1.2-SNAPSHOT after release 2022-02-17 16:51:33 +00:00
buildmaster b518187da8 Going back to snapshots 2022-02-17 16:51:33 +00:00
MI-cool d5637172f2 fix test testRenew() 2021-12-27 14:45:50 +08:00
MI-cool b7bfc42697 remove unused import 2021-12-27 12:37:10 +08:00
MI-cool 53392471a5 reduce high load caused by EurekaInstanceRenewedEvent 2021-12-10 14:48:41 +08:00
50 changed files with 1090 additions and 581 deletions
-1
View File
@@ -23,7 +23,6 @@ Edit the files in the src/main/asciidoc/ directory instead.
image::https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/main.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/main"]
image::https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/main/graph/badge.svg["Codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/main"]
image::https://api.codacy.com/project/badge/Grade/a6885a06921e4f72a0df0b7aabd6d118["Codacy code quality", link="https://www.codacy.com/app/Spring-Cloud/spring-cloud-netflix?utm_source=github.com&utm_medium=referral&utm_content=spring-cloud/spring-cloud-netflix&utm_campaign=Badge_Grade"]
:doctype: book
+3 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>3.1.1</version>
<version>3.1.7</version>
</parent>
<artifactId>spring-cloud-netflix-docs</artifactId>
<packaging>jar</packaging>
@@ -18,6 +18,8 @@
.*.eureka.*
</configprops.inclusionPattern>
<upload-docs-zip.phase>deploy</upload-docs-zip.phase>
<!-- Don't upload docs jar to central / repo.spring.io -->
<maven-deploy-plugin-default.phase>none</maven-deploy-plugin-default.phase>
</properties>
<dependencies>
<dependency>
-1
View File
@@ -2,7 +2,6 @@ include::_attributes.adoc[]
image::https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/main.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/main"]
image::https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/main/graph/badge.svg["Codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/main"]
image::https://api.codacy.com/project/badge/Grade/a6885a06921e4f72a0df0b7aabd6d118["Codacy code quality", link="https://www.codacy.com/app/Spring-Cloud/spring-cloud-netflix?utm_source=github.com&utm_medium=referral&utm_content=spring-cloud/spring-cloud-netflix&utm_campaign=Badge_Grade"]
include::intro.adoc[]
+12 -12
View File
@@ -1,20 +1,20 @@
|===
|Name | Default | Description
|eureka.client.eureka-connection-idle-timeout-seconds | `30` | Indicates how much time (in seconds) that the HTTP connections to eureka server can stay idle before it can be closed. In the AWS environment, it is recommended that the values is 30 seconds or less, since the firewall cleans up the connection information after a few mins leaving the connection hanging in limbo.
|eureka.client.eureka-server-connect-timeout-seconds | `5` | Indicates how long to wait (in seconds) before a connection to eureka server needs to timeout. Note that the connections in the client are pooled by org.apache.http.client.HttpClient and this setting affects the actual connection creation and also the wait time to get the connection from the pool.
|eureka.client.eureka-connection-idle-timeout-seconds | `+++30+++` | Indicates how much time (in seconds) that the HTTP connections to eureka server can stay idle before it can be closed. In the AWS environment, it is recommended that the values is 30 seconds or less, since the firewall cleans up the connection information after a few mins leaving the connection hanging in limbo.
|eureka.client.eureka-server-connect-timeout-seconds | `+++5+++` | Indicates how long to wait (in seconds) before a connection to eureka server needs to timeout. Note that the connections in the client are pooled by org.apache.http.client.HttpClient and this setting affects the actual connection creation and also the wait time to get the connection from the pool.
|eureka.client.eureka-server-d-n-s-name | | Gets the DNS name to be queried to get the list of eureka servers.This information is not required if the contract returns the service urls by implementing serviceUrls. The DNS mechanism is used when useDnsForFetchingServiceUrls is set to true and the eureka client expects the DNS to configured a certain way so that it can fetch changing eureka servers dynamically. The changes are effective at runtime.
|eureka.client.eureka-server-port | | Gets the port to be used to construct the service url to contact eureka server when the list of eureka servers come from the DNS.This information is not required if the contract returns the service urls eurekaServerServiceUrls(String). The DNS mechanism is used when useDnsForFetchingServiceUrls is set to true and the eureka client expects the DNS to configured a certain way so that it can fetch changing eureka servers dynamically. The changes are effective at runtime.
|eureka.client.eureka-server-read-timeout-seconds | `8` | Indicates how long to wait (in seconds) before a read from eureka server needs to timeout.
|eureka.client.eureka-server-total-connections | `200` | Gets the total number of connections that is allowed from eureka client to all eureka servers.
|eureka.client.eureka-server-total-connections-per-host | `50` | Gets the total number of connections that is allowed from eureka client to a eureka server host.
|eureka.client.eureka-server-read-timeout-seconds | `+++8+++` | Indicates how long to wait (in seconds) before a read from eureka server needs to timeout.
|eureka.client.eureka-server-total-connections | `+++200+++` | Gets the total number of connections that is allowed from eureka client to all eureka servers.
|eureka.client.eureka-server-total-connections-per-host | `+++50+++` | Gets the total number of connections that is allowed from eureka client to a eureka server host.
|eureka.client.eureka-server-u-r-l-context | | Gets the URL context to be used to construct the service url to contact eureka server when the list of eureka servers come from the DNS. This information is not required if the contract returns the service urls from eurekaServerServiceUrls. The DNS mechanism is used when useDnsForFetchingServiceUrls is set to true and the eureka client expects the DNS to configured a certain way so that it can fetch changing eureka servers dynamically. The changes are effective at runtime.
|eureka.client.eureka-service-url-poll-interval-seconds | `0` | Indicates how often(in seconds) to poll for changes to eureka server information. Eureka servers could be added or removed and this setting controls how soon the eureka clients should know about it.
|eureka.client.prefer-same-zone-eureka | `true` | Indicates whether or not this instance should try to use the eureka server in the same zone for latency and/or other reason. Ideally eureka clients are configured to talk to servers in the same zone The changes are effective at runtime at the next registry fetch cycle as specified by registryFetchIntervalSeconds
|eureka.client.register-with-eureka | `true` | Indicates whether or not this instance should register its information with eureka server for discovery by others. In some cases, you do not want your instances to be discovered whereas you just want do discover other instances.
|eureka.server.peer-eureka-nodes-update-interval-ms | `0` |
|eureka.server.peer-eureka-status-refresh-time-interval-ms | `0` |
|ribbon.eureka.enabled | `true` | Enables the use of Eureka with Ribbon.
|spring.cloud.loadbalancer.eureka.approximate-zone-from-hostname | `false` | Used to determine whether we should try to get the `zone` value from host name.
|eureka.client.eureka-service-url-poll-interval-seconds | `+++0+++` | Indicates how often(in seconds) to poll for changes to eureka server information. Eureka servers could be added or removed and this setting controls how soon the eureka clients should know about it.
|eureka.client.prefer-same-zone-eureka | `+++true+++` | Indicates whether or not this instance should try to use the eureka server in the same zone for latency and/or other reason. Ideally eureka clients are configured to talk to servers in the same zone The changes are effective at runtime at the next registry fetch cycle as specified by registryFetchIntervalSeconds
|eureka.client.register-with-eureka | `+++true+++` | Indicates whether or not this instance should register its information with eureka server for discovery by others. In some cases, you do not want your instances to be discovered whereas you just want do discover other instances.
|eureka.server.peer-eureka-nodes-update-interval-ms | `+++0+++` |
|eureka.server.peer-eureka-status-refresh-time-interval-ms | `+++0+++` |
|ribbon.eureka.enabled | `+++true+++` | Enables the use of Eureka with Ribbon.
|spring.cloud.loadbalancer.eureka.approximate-zone-from-hostname | `+++false+++` | Used to determine whether we should try to get the `zone` value from host name.
|===
@@ -80,6 +80,9 @@ See {github-code}/spring-cloud-netflix-eureka-client/src/main/java/org/springfra
To disable the Eureka Discovery Client, you can set `eureka.client.enabled` to `false`. Eureka Discovery Client will also be disabled when `spring.cloud.discovery.enabled` is set to `false`.
NOTE: Specifying the version of the Spring Cloud Netflix Eureka server as a path parameter is not currently supported. This means you cannot set the version in the context path (`eurekaServerURLContext`). Instead, you can include the version in the server URL (for example, you can set `defaultZone: http://localhost:8761/eureka/v2`).
=== Authenticating with the Eureka Server
HTTP basic authentication is automatically added to your eureka client if one of the `eureka.client.serviceUrl.defaultZone` URLs has credentials embedded in it (curl style, as follows: `https://user:password@localhost:8761/eureka`).
@@ -109,6 +112,19 @@ NOTE: Because of a limitation in Eureka, it is not possible to support per-serve
If you want to customize the RestTemplate used by the Eureka HTTP Client you may want to create a bean of `EurekaClientHttpRequestFactorySupplier` and provide your own logic for generating a `ClientHttpRequestFactory` instance.
All default timeout-related properties for RestTemplate used by the Eureka HTTP Client are set to infinite. Therefore, to specify the timeout values, you must specify the value directly with the properties in `eureka.client.rest-template-timeout`. (All timeout properties are in milliseconds.)
.application.yml
[source,yaml]
----
eureka:
client:
rest-template-timeout:
connect-timeout: 5000
connect-request-timeout: 8000
socket-timeout: 10000
----
=== Status Page and Health Indicator
The status page and health indicators for a Eureka instance default to `/info` and `/health` respectively, which are the default locations of useful endpoints in a Spring Boot Actuator application.
@@ -430,6 +446,10 @@ dependencyManagement {
----
====
=== `defaultOpenForTrafficCount` and its effect on EurekaServer warmup time
Netflix Eureka's `waitTimeInMsWhenSyncEmpty` setting is not taken into account in Spring Cloud Eureka server at the beginning. In order to enable the warmup time, set `eureka.server.defaultOpenForTrafficCount=0`.
[[spring-cloud-eureka-server-zones-and-regions]]
=== High Availability, Zones and Regions
+23 -4
View File
@@ -3,14 +3,14 @@
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-netflix</artifactId>
<version>3.1.1</version>
<version>3.1.7</version>
<packaging>pom</packaging>
<name>Spring Cloud Netflix</name>
<description>Spring Cloud Netflix</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>3.1.1</version>
<version>3.1.8</version>
<relativePath />
</parent>
<scm>
@@ -21,8 +21,10 @@
</scm>
<properties>
<bintray.package>netflix</bintray.package>
<spring-cloud-commons.version>3.1.1</spring-cloud-commons.version>
<spring-cloud-config.version>3.1.1</spring-cloud-config.version>
<spring-cloud-commons.version>3.1.7</spring-cloud-commons.version>
<spring-cloud-config.version>3.1.8</spring-cloud-config.version>
<testcontainers.version>1.17.6</testcontainers.version>
<mockserverclient.version>5.15.0</mockserverclient.version>
<!-- Sonar -->
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
@@ -81,6 +83,7 @@
<ignoredResourcePattern>plugin.properties</ignoredResourcePattern>
<ignoredResourcePattern>mozilla/public-suffix-list.txt</ignoredResourcePattern>
</ignoredResourcePatterns>
<checkTestClasspath>false</checkTestClasspath>
</configuration>
</plugin>
</plugins>
@@ -146,6 +149,22 @@
<version>${eureka-jersey.version}</version>
</dependency>
<!-- end eureka deps -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mockserver</artifactId>
<version>${testcontainers.version}</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${testcontainers.version}</version>
</dependency>
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-client-java</artifactId>
<version>${mockserverclient.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<reporting>
+1 -1
View File
@@ -4,7 +4,7 @@ set -o errexit
mkdir -p target
SCRIPT_URL="https://raw.githubusercontent.com/spring-cloud-samples/brewery/main/runAcceptanceTests.sh"
SCRIPT_URL="https://raw.githubusercontent.com/spring-cloud-samples/brewery/3.1.x/runAcceptanceTests.sh"
AT_WHAT_TO_TEST="EUREKA"
cd target
+2 -2
View File
@@ -5,11 +5,11 @@
<parent>
<artifactId>spring-cloud-dependencies-parent</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>3.1.1</version>
<version>3.1.8</version>
<relativePath/>
</parent>
<artifactId>spring-cloud-netflix-dependencies</artifactId>
<version>3.1.1</version>
<version>3.1.7</version>
<packaging>pom</packaging>
<name>spring-cloud-netflix-dependencies</name>
<description>Spring Cloud Netflix Dependencies</description>
@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>3.1.1</version>
<version>3.1.7</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-netflix-eureka-client-tls-tests</artifactId>
@@ -115,6 +115,12 @@
</excludes>
</configuration>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>
+17 -2
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>3.1.1</version>
<version>3.1.7</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
@@ -40,7 +40,7 @@
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<optional>true</optional>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
@@ -140,5 +140,20 @@
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mockserver</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-client-java</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -19,19 +19,21 @@ package org.springframework.cloud.netflix.eureka;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import reactor.core.publisher.Mono;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.actuate.health.CompositeHealthContributor;
import org.springframework.boot.actuate.health.CompositeReactiveHealthContributor;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthContributor;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.NamedContributor;
import org.springframework.boot.actuate.health.ReactiveHealthContributor;
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.boot.actuate.health.StatusAggregator;
@@ -58,6 +60,7 @@ import org.springframework.util.Assert;
* @author Spencer Gibb
* @author Nowrin Anwar Joyita
* @author Bertrand Renuart
* @author Olga Maciaszek-Sharma
* @see HealthCheckHandler
* @see StatusAggregator
*/
@@ -67,7 +70,7 @@ public class EurekaHealthCheckHandler
private static final Map<Status, InstanceInfo.InstanceStatus> STATUS_MAPPING = new HashMap<Status, InstanceInfo.InstanceStatus>() {
{
put(Status.UNKNOWN, InstanceStatus.UNKNOWN);
put(Status.OUT_OF_SERVICE, InstanceStatus.OUT_OF_SERVICE);
put(Status.OUT_OF_SERVICE, InstanceStatus.DOWN);
put(Status.DOWN, InstanceStatus.DOWN);
put(Status.UP, InstanceStatus.UP);
}
@@ -77,14 +80,14 @@ public class EurekaHealthCheckHandler
private ApplicationContext applicationContext;
private Map<String, HealthIndicator> healthIndicators = new HashMap<>();
private Map<String, HealthContributor> healthContributors = new HashMap<>();
/**
* {@code true} until the context is stopped.
*/
private boolean running = true;
private Map<String, ReactiveHealthIndicator> reactiveHealthIndicators = new HashMap<>();
private Map<String, ReactiveHealthContributor> reactiveHealthContributors = new HashMap<>();
public EurekaHealthCheckHandler(StatusAggregator statusAggregator) {
this.statusAggregator = statusAggregator;
@@ -99,39 +102,28 @@ public class EurekaHealthCheckHandler
@Override
public void afterPropertiesSet() {
final Map<String, HealthIndicator> healthIndicators = applicationContext.getBeansOfType(HealthIndicator.class);
final Map<String, ReactiveHealthIndicator> reactiveHealthIndicators = applicationContext
.getBeansOfType(ReactiveHealthIndicator.class);
populateHealthIndicators(healthIndicators);
populateReactiveHealthIndicators(reactiveHealthIndicators);
populateHealthContributors(applicationContext.getBeansOfType(HealthContributor.class));
reactiveHealthContributors.putAll(applicationContext.getBeansOfType(ReactiveHealthContributor.class));
}
void populateHealthIndicators(Map<String, HealthIndicator> healthIndicators) {
for (Map.Entry<String, HealthIndicator> entry : healthIndicators.entrySet()) {
void populateHealthContributors(Map<String, HealthContributor> healthContributors) {
for (Map.Entry<String, HealthContributor> entry : healthContributors.entrySet()) {
// ignore EurekaHealthIndicator and flatten the rest of the composite
// otherwise there is a never ending cycle of down. See gh-643
if (entry.getValue() instanceof DiscoveryCompositeHealthContributor) {
DiscoveryCompositeHealthContributor indicator = (DiscoveryCompositeHealthContributor) entry.getValue();
indicator.forEach(contributor -> {
if (!(contributor.getContributor() instanceof EurekaHealthIndicator)) {
this.healthIndicators.put(contributor.getName(),
(HealthIndicator) contributor.getContributor());
indicator.getIndicators().forEach((name, discoveryHealthIndicator) -> {
if (!(discoveryHealthIndicator instanceof EurekaHealthIndicator)) {
this.healthContributors.put(name, (HealthIndicator) discoveryHealthIndicator::health);
}
});
}
else {
this.healthIndicators.put(entry.getKey(), entry.getValue());
this.healthContributors.put(entry.getKey(), entry.getValue());
}
}
}
void populateReactiveHealthIndicators(Map<String, ReactiveHealthIndicator> reactiveHealthIndicators) {
for (Map.Entry<String, ReactiveHealthIndicator> entry : reactiveHealthIndicators.entrySet()) {
this.reactiveHealthIndicators.put(entry.getKey(), entry.getValue());
}
}
@Override
public InstanceStatus getStatus(InstanceStatus instanceStatus) {
if (running) {
@@ -151,21 +143,39 @@ public class EurekaHealthCheckHandler
}
protected Status getStatus(StatusAggregator statusAggregator) {
Status status;
Set<Status> statusSet = new HashSet<>();
if (healthIndicators != null) {
statusSet.addAll(healthIndicators.values().stream().map(HealthIndicator::health).map(Health::getStatus)
.collect(Collectors.toSet()));
for (HealthContributor contributor : healthContributors.values()) {
processContributor(statusSet, contributor);
}
if (reactiveHealthIndicators != null) {
statusSet.addAll(reactiveHealthIndicators.values().stream().map(ReactiveHealthIndicator::health)
.map(Mono::block).filter(Objects::nonNull).map(Health::getStatus).collect(Collectors.toSet()));
for (ReactiveHealthContributor contributor : reactiveHealthContributors.values()) {
processContributor(statusSet, contributor);
}
return statusAggregator.getAggregateStatus(statusSet);
}
status = statusAggregator.getAggregateStatus(statusSet);
return status;
private void processContributor(Set<Status> statusSet, HealthContributor contributor) {
if (contributor instanceof CompositeHealthContributor) {
for (NamedContributor<HealthContributor> contrib : (CompositeHealthContributor) contributor) {
processContributor(statusSet, contrib.getContributor());
}
}
else if (contributor instanceof HealthIndicator) {
statusSet.add(((HealthIndicator) contributor).health().getStatus());
}
}
private void processContributor(Set<Status> statusSet, ReactiveHealthContributor contributor) {
if (contributor instanceof CompositeReactiveHealthContributor) {
for (NamedContributor<ReactiveHealthContributor> contrib : (CompositeReactiveHealthContributor) contributor) {
processContributor(statusSet, contrib.getContributor());
}
}
else if (contributor instanceof ReactiveHealthIndicator) {
Health health = ((ReactiveHealthIndicator) contributor).health().block();
if (health != null) {
statusSet.add(health.getStatus());
}
}
}
protected InstanceStatus mapToInstanceStatus(Status status) {
@@ -0,0 +1,96 @@
/*
* 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.netflix.eureka;
import java.util.Objects;
import org.apache.http.client.config.RequestConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.netflix.eureka.http.RestTemplateEurekaHttpClient;
import org.springframework.web.client.RestTemplate;
/**
* Properties for configuring timeouts used in {@link RestTemplate} required by
* {@link RestTemplateEurekaHttpClient}.
*
* @author Jiwon Jeon
* @since 3.1.6
*/
@ConfigurationProperties("eureka.client.rest-template-timeout")
public class RestTemplateTimeoutProperties {
/**
* Default values are set to -1 according to {@link RequestConfig .DEFAULT}.
*/
private int connectTimeout = -1;
private int connectRequestTimeout = -1;
private int socketTimeout = -1;
public int getConnectTimeout() {
return connectTimeout;
}
public int getConnectRequestTimeout() {
return connectRequestTimeout;
}
public int getSocketTimeout() {
return socketTimeout;
}
public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
}
public void setConnectRequestTimeout(int connectRequestTimeout) {
this.connectRequestTimeout = connectRequestTimeout;
}
public void setSocketTimeout(int socketTimeout) {
this.socketTimeout = socketTimeout;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
RestTemplateTimeoutProperties that = (RestTemplateTimeoutProperties) o;
return connectTimeout == that.connectTimeout && connectRequestTimeout == that.connectRequestTimeout
&& socketTimeout == that.socketTimeout;
}
@Override
public int hashCode() {
return Objects.hash(connectTimeout, connectRequestTimeout, socketTimeout);
}
@Override
public String toString() {
return "RestTemplateTimeoutProperties{" + ", connectTimeout=" + connectTimeout + ", connectRequestTimeout="
+ connectRequestTimeout + ", socketTimeout=" + socketTimeout + '}';
}
}
@@ -31,9 +31,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClas
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.configuration.SSLContextFactory;
import org.springframework.cloud.configuration.TlsProperties;
import org.springframework.cloud.netflix.eureka.MutableDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.RestTemplateTimeoutProperties;
import org.springframework.cloud.netflix.eureka.http.DefaultEurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.EurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs;
@@ -46,6 +48,7 @@ import org.springframework.web.reactive.function.client.WebClient;
* @author Daniel Lavoie
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(RestTemplateTimeoutProperties.class)
public class DiscoveryClientOptionalArgsConfiguration {
protected static final Log logger = LogFactory.getLog(DiscoveryClientOptionalArgsConfiguration.class);
@@ -75,8 +78,9 @@ public class DiscoveryClientOptionalArgsConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier() {
return new DefaultEurekaClientHttpRequestFactorySupplier();
EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier(
RestTemplateTimeoutProperties restTemplateTimeoutProperties) {
return new DefaultEurekaClientHttpRequestFactorySupplier(restTemplateTimeoutProperties);
}
@Bean
@@ -24,9 +24,9 @@ import com.netflix.discovery.EurekaClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.config.server.config.ConfigServerProperties;
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
/**
@@ -36,21 +36,22 @@ import org.springframework.util.StringUtils;
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnClass({ EurekaInstanceConfigBean.class, EurekaClient.class, ConfigServerProperties.class })
@ConditionalOnClass(value = { EurekaInstanceConfigBean.class, EurekaClient.class },
name = "org.springframework.cloud.config.server.config.ConfigServerProperties")
public class EurekaClientConfigServerAutoConfiguration {
@Autowired(required = false)
private EurekaInstanceConfig instance;
@Autowired(required = false)
private ConfigServerProperties server;
@Autowired
private Environment env;
@PostConstruct
public void init() {
if (this.instance == null || this.server == null) {
if (this.instance == null) {
return;
}
String prefix = this.server.getPrefix();
String prefix = this.env.getProperty("spring.cloud.config.server.prefix");
if (StringUtils.hasText(prefix) && !StringUtils.hasText(this.instance.getMetadataMap().get("configPath"))) {
this.instance.getMetadataMap().put("configPath", prefix);
}
@@ -16,11 +16,18 @@
package org.springframework.cloud.netflix.eureka.config;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import java.util.Collections;
import java.util.List;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import org.apache.commons.logging.Log;
import org.springframework.boot.BootstrapContext;
import org.springframework.boot.BootstrapRegistry;
import org.springframework.boot.BootstrapRegistryInitializer;
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.config.client.ConfigClientProperties;
import org.springframework.cloud.config.client.ConfigServerInstanceProvider;
import org.springframework.cloud.configuration.TlsProperties;
@@ -37,33 +44,53 @@ public class EurekaConfigServerBootstrapper implements BootstrapRegistryInitiali
if (!ClassUtils.isPresent("org.springframework.cloud.config.client.ConfigServerInstanceProvider", null)) {
return;
}
registry.registerIfAbsent(EurekaClientConfigBean.class, context -> {
Binder binder = context.get(Binder.class);
if (!getDiscoveryEnabled(binder)) {
return null;
// It is important that we pass a lambda for the Function or else we will get a
// ClassNotFoundException when config is not on the classpath
registry.registerIfAbsent(ConfigServerInstanceProvider.Function.class, EurekaFunction::create);
}
private static Boolean getDiscoveryEnabled(Binder binder) {
return binder.bind(ConfigClientProperties.CONFIG_DISCOVERY_ENABLED, Boolean.class).orElse(false)
&& binder.bind("eureka.client.enabled", Boolean.class).orElse(true)
&& binder.bind("spring.cloud.discovery.enabled", Boolean.class).orElse(true);
}
final static class EurekaFunction implements ConfigServerInstanceProvider.Function {
private final BootstrapContext context;
static EurekaFunction create(BootstrapContext context) {
return new EurekaFunction(context);
}
private EurekaFunction(BootstrapContext context) {
this.context = context;
}
@Override
public List<ServiceInstance> apply(String serviceId, Binder binder, BindHandler bindHandler, Log log) {
if (binder == null || !getDiscoveryEnabled(binder)) {
return Collections.emptyList();
}
return binder.bind(EurekaClientConfigBean.PREFIX, EurekaClientConfigBean.class)
EurekaClientConfigBean config = binder.bind(EurekaClientConfigBean.PREFIX, EurekaClientConfigBean.class)
.orElseGet(EurekaClientConfigBean::new);
});
registry.registerIfAbsent(ConfigServerInstanceProvider.Function.class, context -> {
Binder binder = context.get(Binder.class);
if (!getDiscoveryEnabled(binder)) {
return null;
}
EurekaClientConfigBean config = context.get(EurekaClientConfigBean.class);
EurekaHttpClient httpClient = new RestTemplateTransportClientFactory(
context.getOrElse(TlsProperties.class, null),
context.getOrElse(EurekaClientHttpRequestFactorySupplier.class,
new DefaultEurekaClientHttpRequestFactorySupplier()))
.newClient(HostnameBasedUrlRandomizer.randomEndpoint(config, binder));
return new EurekaConfigServerInstanceProvider(httpClient, config)::getInstances;
});
}
return new EurekaConfigServerInstanceProvider(httpClient, config).getInstances(serviceId);
}
@Override
public List<ServiceInstance> apply(String serviceId) {
// This should never be called now but is here for backward
// compatibility
return apply(serviceId, null, null, null);
}
private Boolean getDiscoveryEnabled(Binder binder) {
return binder.bind(ConfigClientProperties.CONFIG_DISCOVERY_ENABLED, Boolean.class).orElse(false);
}
}
@@ -19,10 +19,12 @@ package org.springframework.cloud.netflix.eureka.http;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.springframework.cloud.netflix.eureka.RestTemplateTimeoutProperties;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.lang.Nullable;
@@ -32,10 +34,26 @@ import org.springframework.lang.Nullable;
* {@link HttpClients}.
*
* @author Marcin Grzejszczak
* @author Jiwon Jeon
* @since 3.0.0
*/
public class DefaultEurekaClientHttpRequestFactorySupplier implements EurekaClientHttpRequestFactorySupplier {
private final RestTemplateTimeoutProperties restTemplateTimeoutProperties;
/**
* @deprecated in favour of
* {@link DefaultEurekaClientHttpRequestFactorySupplier#DefaultEurekaClientHttpRequestFactorySupplier(RestTemplateTimeoutProperties)}
*/
@Deprecated
public DefaultEurekaClientHttpRequestFactorySupplier() {
this.restTemplateTimeoutProperties = new RestTemplateTimeoutProperties();
}
public DefaultEurekaClientHttpRequestFactorySupplier(RestTemplateTimeoutProperties restTemplateTimeoutProperties) {
this.restTemplateTimeoutProperties = restTemplateTimeoutProperties;
}
@Override
public ClientHttpRequestFactory get(SSLContext sslContext, @Nullable HostnameVerifier hostnameVerifier) {
HttpClientBuilder httpClientBuilder = HttpClients.custom();
@@ -45,10 +63,20 @@ public class DefaultEurekaClientHttpRequestFactorySupplier implements EurekaClie
if (hostnameVerifier != null) {
httpClientBuilder = httpClientBuilder.setSSLHostnameVerifier(hostnameVerifier);
}
if (restTemplateTimeoutProperties != null) {
httpClientBuilder.setDefaultRequestConfig(buildRequestConfig());
}
CloseableHttpClient httpClient = httpClientBuilder.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
return requestFactory;
}
private RequestConfig buildRequestConfig() {
return RequestConfig.custom().setConnectTimeout(restTemplateTimeoutProperties.getConnectTimeout())
.setConnectionRequestTimeout(restTemplateTimeoutProperties.getConnectRequestTimeout())
.setSocketTimeout(restTemplateTimeoutProperties.getSocketTimeout()).build();
}
}
@@ -148,8 +148,7 @@ public class RestTemplateTransportClientFactory implements TransportClientFactor
converter.setObjectMapper(new ObjectMapper().setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE));
SimpleModule jsonModule = new SimpleModule();
jsonModule.setSerializerModifier(createJsonSerializerModifier()); // keyFormatter,
// compact));
jsonModule.setSerializerModifier(createJsonSerializerModifier());
converter.getObjectMapper().registerModule(jsonModule);
converter.getObjectMapper().configure(SerializationFeature.WRAP_ROOT_VALUE, true);
@@ -157,25 +156,10 @@ public class RestTemplateTransportClientFactory implements TransportClientFactor
converter.getObjectMapper().addMixIn(Applications.class, ApplicationsJsonMixIn.class);
converter.getObjectMapper().addMixIn(InstanceInfo.class, InstanceInfoJsonMixIn.class);
// converter.getObjectMapper().addMixIn(DataCenterInfo.class,
// DataCenterInfoXmlMixIn.class);
// converter.getObjectMapper().addMixIn(InstanceInfo.PortWrapper.class,
// PortWrapperXmlMixIn.class);
// converter.getObjectMapper().addMixIn(Application.class,
// ApplicationXmlMixIn.class);
// converter.getObjectMapper().addMixIn(Applications.class,
// ApplicationsXmlMixIn.class);
return converter;
}
public static BeanSerializerModifier createJsonSerializerModifier() { // final
// KeyFormatter
// keyFormatter,
// final
// boolean
// compactMode)
// {
public static BeanSerializerModifier createJsonSerializerModifier() {
return new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc,
@@ -16,8 +16,6 @@
package org.springframework.cloud.netflix.eureka.http;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import com.netflix.appinfo.InstanceInfo;
@@ -28,12 +26,12 @@ import com.netflix.discovery.shared.transport.EurekaHttpClient;
import com.netflix.discovery.shared.transport.EurekaHttpResponse;
import com.netflix.discovery.shared.transport.EurekaHttpResponse.EurekaHttpResponseBuilder;
import com.netflix.discovery.util.StringUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Mono;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
@@ -46,8 +44,6 @@ import static com.netflix.discovery.shared.transport.EurekaHttpResponse.anEureka
*/
public class WebClientEurekaHttpClient implements EurekaHttpClient {
protected final Log logger = LogFactory.getLog(getClass());
private WebClient webClient;
public WebClientEurekaHttpClient(WebClient webClient) {
@@ -56,16 +52,18 @@ public class WebClientEurekaHttpClient implements EurekaHttpClient {
@Override
public EurekaHttpResponse<Void> register(InstanceInfo info) {
return webClient.post().uri("apps/" + info.getAppName(), Void.class).body(BodyInserters.fromValue(info))
return webClient.post().uri("apps/" + info.getAppName()).body(BodyInserters.fromValue(info))
.header(HttpHeaders.ACCEPT_ENCODING, "gzip")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).exchange()
.map(response -> eurekaHttpResponse(response)).block();
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).retrieve()
.onStatus(HttpStatus::isError, this::ignoreError).toBodilessEntity().map(this::eurekaHttpResponse)
.block();
}
@Override
public EurekaHttpResponse<Void> cancel(String appName, String id) {
return webClient.delete().uri("apps/" + appName + '/' + id, Void.class).exchange()
.map(response -> eurekaHttpResponse(response)).block();
return webClient.delete().uri("apps/" + appName + '/' + id).retrieve()
.onStatus(HttpStatus::isError, this::ignoreError).toBodilessEntity().map(this::eurekaHttpResponse)
.block();
}
@Override
@@ -75,14 +73,15 @@ public class WebClientEurekaHttpClient implements EurekaHttpClient {
+ "&lastDirtyTimestamp=" + info.getLastDirtyTimestamp().toString()
+ (overriddenStatus != null ? "&overriddenstatus=" + overriddenStatus.name() : "");
ClientResponse response = webClient.put().uri(urlPath, InstanceInfo.class)
ResponseEntity<InstanceInfo> response = webClient.put().uri(urlPath)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).exchange().block();
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).retrieve()
.onStatus(HttpStatus::isError, this::ignoreError).toEntity(InstanceInfo.class).block();
EurekaHttpResponseBuilder<InstanceInfo> builder = anEurekaHttpResponse(statusCodeValueOf(response),
InstanceInfo.class).headers(headersOf(response));
InstanceInfo entity = response.toEntity(InstanceInfo.class).block().getBody();
InstanceInfo entity = response.getBody();
if (entity != null) {
builder.entity(entity);
@@ -98,9 +97,9 @@ public class WebClientEurekaHttpClient implements EurekaHttpClient {
String urlPath = "apps/" + appName + '/' + id + "/status?value=" + newStatus.name() + "&lastDirtyTimestamp="
+ info.getLastDirtyTimestamp().toString();
return webClient.put().uri(urlPath, Void.class)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).exchange()
.map(response -> eurekaHttpResponse(response)).block();
return webClient.put().uri(urlPath).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve().onStatus(HttpStatus::isError, this::ignoreError).toBodilessEntity()
.map(this::eurekaHttpResponse).block();
}
@Override
@@ -108,9 +107,9 @@ public class WebClientEurekaHttpClient implements EurekaHttpClient {
String urlPath = "apps/" + appName + '/' + id + "/status?lastDirtyTimestamp="
+ info.getLastDirtyTimestamp().toString();
return webClient.delete().uri(urlPath, Void.class)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).exchange()
.map(response -> eurekaHttpResponse(response)).block();
return webClient.delete().uri(urlPath).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve().onStatus(HttpStatus::isError, this::ignoreError).toBodilessEntity()
.map(this::eurekaHttpResponse).block();
}
@Override
@@ -125,13 +124,14 @@ public class WebClientEurekaHttpClient implements EurekaHttpClient {
url = url + (urlPath.contains("?") ? "&" : "?") + "regions=" + StringUtil.join(regions);
}
ClientResponse response = webClient.get().uri(url, Applications.class)
ResponseEntity<Applications> response = webClient.get().uri(url)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).exchange().block();
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).retrieve()
.onStatus(HttpStatus::isError, this::ignoreError).toEntity(Applications.class).block();
int statusCode = statusCodeValueOf(response);
Applications body = response.toEntity(Applications.class).block().getBody();
Applications body = response.getBody();
return anEurekaHttpResponse(statusCode, statusCode == HttpStatus.OK.value() && body != null ? body : null)
.headers(headersOf(response)).build();
@@ -155,11 +155,12 @@ public class WebClientEurekaHttpClient implements EurekaHttpClient {
@Override
public EurekaHttpResponse<Application> getApplication(String appName) {
ClientResponse response = webClient.get().uri("apps/" + appName, Application.class)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).exchange().block();
ResponseEntity<Application> response = webClient.get().uri("apps/" + appName)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).retrieve()
.onStatus(HttpStatus::isError, this::ignoreError).toEntity(Application.class).block();
int statusCode = statusCodeValueOf(response);
Application body = response.toEntity(Application.class).block().getBody();
Application body = response.getBody();
Application application = statusCode == HttpStatus.OK.value() && body != null ? body : null;
@@ -177,11 +178,12 @@ public class WebClientEurekaHttpClient implements EurekaHttpClient {
}
private EurekaHttpResponse<InstanceInfo> getInstanceInternal(String urlPath) {
ClientResponse response = webClient.get().uri(urlPath, InstanceInfo.class)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).exchange().block();
ResponseEntity<InstanceInfo> response = webClient.get().uri(urlPath)
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).retrieve()
.onStatus(HttpStatus::isError, this::ignoreError).toEntity(InstanceInfo.class).block();
int statusCode = statusCodeValueOf(response);
InstanceInfo body = response.toEntity(InstanceInfo.class).block().getBody();
InstanceInfo body = response.getBody();
return anEurekaHttpResponse(statusCode, statusCode == HttpStatus.OK.value() && body != null ? body : null)
.headers(headersOf(response)).build();
@@ -196,26 +198,19 @@ public class WebClientEurekaHttpClient implements EurekaHttpClient {
return this.webClient;
}
private static Map<String, String> headersOf(ClientResponse response) {
ClientResponse.Headers httpHeaders = response.headers();
if (httpHeaders == null) {
return Collections.emptyMap();
}
HttpHeaders asHeaders = httpHeaders.asHttpHeaders();
if (asHeaders == null) {
return Collections.emptyMap();
}
Map<String, String> headers = new HashMap<>();
asHeaders.entrySet().stream()
.forEach(entry -> entry.getValue().stream().forEach(v -> headers.put(entry.getKey(), v)));
return headers;
private Mono<? extends Throwable> ignoreError(ClientResponse response) {
return Mono.empty();
}
private int statusCodeValueOf(ClientResponse response) {
return response.statusCode().value();
private static Map<String, String> headersOf(ResponseEntity<?> response) {
return response.getHeaders().toSingleValueMap();
}
private EurekaHttpResponse<Void> eurekaHttpResponse(ClientResponse response) {
private int statusCodeValueOf(ResponseEntity<?> response) {
return response.getStatusCode().value();
}
private EurekaHttpResponse<Void> eurekaHttpResponse(ResponseEntity<?> response) {
return anEurekaHttpResponse(statusCodeValueOf(response)).headers(headersOf(response)).build();
}
@@ -16,18 +16,23 @@
package org.springframework.cloud.netflix.eureka;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.AbstractReactiveHealthIndicator;
import org.springframework.boot.actuate.health.CompositeHealthContributor;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthContributor;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.NamedContributor;
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
import org.springframework.boot.actuate.health.SimpleStatusAggregator;
import org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIndicator;
@@ -44,6 +49,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Jakub Narloch
* @author Nowrin Anwar Joyita
* @author Olga Maciaszek-Sharma
*/
class EurekaHealthCheckHandlerTests {
@@ -63,7 +69,7 @@ class EurekaHealthCheckHandlerTests {
}
@Test
void testAllUp() throws Exception {
void testAllUp() {
initialize(UpHealthConfiguration.class, ReactiveUpHealthConfiguration.class);
InstanceStatus status = healthCheckHandler.getStatus(InstanceStatus.UNKNOWN);
@@ -71,7 +77,7 @@ class EurekaHealthCheckHandlerTests {
}
@Test
void testHealthCheckNotReturnedWhenStopped() throws Exception {
void testHealthCheckNotReturnedWhenStopped() {
initialize(UpHealthConfiguration.class);
healthCheckHandler.stop();
@@ -84,7 +90,7 @@ class EurekaHealthCheckHandlerTests {
}
@Test
void testDownWithBlockingIndicators() throws Exception {
void testDownWithBlockingIndicators() {
initialize(UpHealthConfiguration.class, DownHealthConfiguration.class);
InstanceStatus status = healthCheckHandler.getStatus(InstanceStatus.UNKNOWN);
@@ -92,7 +98,7 @@ class EurekaHealthCheckHandlerTests {
}
@Test
void testDownWithReactiveIndicators() throws Exception {
void testDownWithReactiveIndicators() {
initialize(UpHealthConfiguration.class, ReactiveDownHealthConfiguration.class);
InstanceStatus status = healthCheckHandler.getStatus(InstanceStatus.UNKNOWN);
@@ -100,7 +106,7 @@ class EurekaHealthCheckHandlerTests {
}
@Test
void testDownWhenBlockingIndicatorUpAndReactiveDown() throws Exception {
void testDownWhenBlockingIndicatorUpAndReactiveDown() {
initialize(ReactiveUpHealthConfiguration.class, DownHealthConfiguration.class);
InstanceStatus status = this.healthCheckHandler.getStatus(InstanceStatus.UNKNOWN);
@@ -108,7 +114,7 @@ class EurekaHealthCheckHandlerTests {
}
@Test
void testDownWhenBlockingIndicatorDownAndReactiveUp() throws Exception {
void testDownWhenBlockingIndicatorDownAndReactiveUp() {
initialize(ReactiveUpHealthConfiguration.class, ReactiveDownHealthConfiguration.class);
InstanceStatus status = this.healthCheckHandler.getStatus(InstanceStatus.UNKNOWN);
@@ -116,7 +122,7 @@ class EurekaHealthCheckHandlerTests {
}
@Test
void testUnknown() throws Exception {
void testUnknown() {
initialize(FatalHealthConfiguration.class);
InstanceStatus status = healthCheckHandler.getStatus(InstanceStatus.UNKNOWN);
@@ -124,15 +130,38 @@ class EurekaHealthCheckHandlerTests {
}
@Test
@Disabled // FIXME: 3.0.0
void testEurekaIgnored() throws Exception {
void testEurekaIgnored() {
initialize(EurekaDownHealthConfiguration.class);
InstanceStatus status = healthCheckHandler.getStatus(InstanceStatus.UP);
assertThat(status).isEqualTo(InstanceStatus.UP);
}
private void initialize(Class<?>... configurations) throws Exception {
@Test
void testCompositeComponentsDown() {
initialize(CompositeComponentsDownHealthConfiguration.class);
InstanceStatus status = healthCheckHandler.getStatus(InstanceStatus.UP);
assertThat(status).isEqualTo(InstanceStatus.DOWN);
}
@Test
void testCompositeComponentsUp() {
initialize(CompositeComponentsUpHealthConfiguration.class);
InstanceStatus status = healthCheckHandler.getStatus(InstanceStatus.UP);
assertThat(status).isEqualTo(InstanceStatus.UP);
}
@Test
void testCompositeComponentsOneDown() {
initialize(CompositeComponentsOneDownHealthConfiguration.class);
InstanceStatus status = healthCheckHandler.getStatus(InstanceStatus.UP);
assertThat(status).isEqualTo(InstanceStatus.DOWN);
}
private void initialize(Class<?>... configurations) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(configurations);
healthCheckHandler.setApplicationContext(applicationContext);
healthCheckHandler.afterPropertiesSet();
@@ -144,7 +173,7 @@ class EurekaHealthCheckHandlerTests {
public HealthIndicator healthIndicator() {
return new AbstractHealthIndicator() {
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
protected void doHealthCheck(Health.Builder builder) {
builder.up();
}
};
@@ -158,7 +187,7 @@ class EurekaHealthCheckHandlerTests {
public HealthIndicator healthIndicator() {
return new AbstractHealthIndicator() {
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
protected void doHealthCheck(Health.Builder builder) {
builder.down();
}
};
@@ -172,7 +201,7 @@ class EurekaHealthCheckHandlerTests {
public HealthIndicator healthIndicator() {
return new AbstractHealthIndicator() {
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
protected void doHealthCheck(Health.Builder builder) {
builder.status("fatal");
}
};
@@ -238,4 +267,76 @@ class EurekaHealthCheckHandlerTests {
}
protected static class CompositeComponentsDownHealthConfiguration {
@Bean
public CompositeHealthContributor compositeHealthContributor() {
return new TestCompositeHealthContributor(InstanceStatus.DOWN, InstanceStatus.DOWN);
}
}
protected static class CompositeComponentsUpHealthConfiguration {
@Bean
public CompositeHealthContributor compositeHealthContributor() {
return new TestCompositeHealthContributor(InstanceStatus.UP, InstanceStatus.UP);
}
}
protected static class CompositeComponentsOneDownHealthConfiguration {
@Bean
public CompositeHealthContributor compositeHealthContributor() {
return new TestCompositeHealthContributor(InstanceStatus.UP, InstanceStatus.DOWN);
}
}
static class TestCompositeHealthContributor implements CompositeHealthContributor {
private final Map<String, HealthContributor> contributorMap = new HashMap<>();
TestCompositeHealthContributor(InstanceStatus firstContributorStatus, InstanceStatus secondContributorStatus) {
contributorMap.put("first", new AbstractHealthIndicator() {
@Override
protected void doHealthCheck(Health.Builder builder) {
builder.status(firstContributorStatus.name());
}
});
contributorMap.put("second", new AbstractHealthIndicator() {
@Override
protected void doHealthCheck(Health.Builder builder) {
builder.status(secondContributorStatus.name());
}
});
}
@Override
public HealthContributor getContributor(String name) {
return contributorMap.get(name);
}
@Override
public Iterator<NamedContributor<HealthContributor>> iterator() {
Iterator<Map.Entry<String, HealthContributor>> iterator = contributorMap.entrySet().iterator();
return new Iterator<NamedContributor<HealthContributor>>() {
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public NamedContributor<HealthContributor> next() {
Map.Entry<String, HealthContributor> entry = iterator.next();
return NamedContributor.of(entry.getKey(), entry.getValue());
}
};
}
}
}
@@ -0,0 +1,155 @@
/*
* 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.netflix.eureka.config;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.converters.jackson.serializer.InstanceInfoJsonBeanSerializer;
import com.netflix.discovery.shared.Application;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockserver.client.MockServerClient;
import org.testcontainers.containers.MockServerContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.environment.PropertySource;
import org.springframework.cloud.netflix.eureka.http.EurekaApplications;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response;
/**
* @author Ryan Baxter
*/
@Testcontainers
public class EurekaConfigServerBootstrapperIT {
public static final DockerImageName MOCKSERVER_IMAGE = DockerImageName.parse("mockserver/mockserver")
.withTag("mockserver-" + MockServerClient.class.getPackage().getImplementationVersion());
@Container
static MockServerContainer mockServer = new MockServerContainer(MOCKSERVER_IMAGE);
private ConfigurableApplicationContext context;
@BeforeEach
void before() {
}
@AfterEach
void after() {
this.context.close();
}
@Test
public void contextLoads() throws JsonProcessingException {
Environment environment = new Environment("test", "default");
Map<String, Object> properties = new HashMap<>();
properties.put("hello", "world");
PropertySource p = new PropertySource("p1", properties);
environment.add(p);
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule jsonModule = new SimpleModule();
jsonModule.setSerializerModifier(createJsonSerializerModifier());
objectMapper.registerModule(jsonModule);
try (MockServerClient mockServerClient = new MockServerClient(mockServer.getHost(),
mockServer.getMappedPort(MockServerContainer.PORT))) {
mockServerClient.when(request().withPath("/application/default"))
.respond(response().withBody(objectMapper.writeValueAsString(environment))
.withHeader("content-type", "application/json"));
InstanceInfo configServerInstanceInfo = InstanceInfo.Builder.newBuilder()
.setVIPAddress("eureka-configserver").setInstanceId("eureka-configserver")
.setAppName("eureka-configserver").setStatus(InstanceInfo.InstanceStatus.UP)
.enablePort(InstanceInfo.PortType.UNSECURE, true).setHostName("localhost")
.setPort(mockServer.getMappedPort(MockServerContainer.PORT)).build();
Application configServer = new Application("eureka-configserver",
Collections.singletonList(configServerInstanceInfo));
EurekaApplications eurekaApplications = new EurekaApplications("hashcode", 0L,
Collections.singletonList(configServer));
mockServerClient.when(request().withPath("/apps/"))
.respond(response()
.withBody("{\"applications\":" + objectMapper.writeValueAsString(eurekaApplications) + "}")
.withHeader("content-type", "application/json"));
this.context = setup().run();
assertThat(this.context.getEnvironment().getProperty("hello")).isEqualTo("world");
}
}
public static BeanSerializerModifier createJsonSerializerModifier() {
return new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc,
JsonSerializer<?> serializer) {
if (beanDesc.getBeanClass().isAssignableFrom(InstanceInfo.class)) {
return new InstanceInfoJsonBeanSerializer((BeanSerializerBase) serializer, false);
}
return serializer;
}
};
}
SpringApplicationBuilder setup(String... env) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(TestConfig.class)
.properties(addDefaultEnv(env));
return builder;
}
private String[] addDefaultEnv(String[] env) {
Set<String> set = new LinkedHashSet<>();
if (env != null && env.length > 0) {
set.addAll(Arrays.asList(env));
}
set.add("spring.config.import=classpath:bootstrapper.yaml");
set.add("spring.cloud.config.enabled=true");
set.add("spring.cloud.service-registry.auto-registration.enabled=false");
set.add("eureka.client.serviceUrl.defaultZone=http://" + mockServer.getHost() + ":"
+ mockServer.getMappedPort(MockServerContainer.PORT));
return set.toArray(new String[0]);
}
@SpringBootConfiguration
@EnableAutoConfiguration
static class TestConfig {
}
}
@@ -16,39 +16,79 @@
package org.springframework.cloud.netflix.eureka.config;
import org.apache.commons.logging.Log;
import org.junit.jupiter.api.Test;
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.config.client.ConfigClientProperties;
import org.springframework.cloud.config.client.ConfigServerInstanceProvider;
import org.springframework.web.client.ResourceAccessException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
import static org.mockito.Mockito.mock;
class EurekaConfigServerBootstrapperTests {
@Test
void notEnabledDoesNotAddInstanceProviderFn() {
void notEnabledReturnsEmptyList() {
new SpringApplicationBuilder(TestConfig.class)
.properties("spring.cloud.service-registry.auto-registration.enabled=false")
.addBootstrapRegistryInitializer(registry -> registry.addCloseListener(event -> {
ConfigServerInstanceProvider.Function providerFn = event.getBootstrapContext()
.get(ConfigServerInstanceProvider.Function.class);
assertThat(providerFn).as("ConfigServerInstanceProvider.Function was created when it shouldn't")
.isNull();
assertThat(providerFn.apply("id")).as("Should return empty list").isEmpty();
})).run().close();
}
@Test
public void discoveryClientNotEnabledProvidesEmptyList() {
new SpringApplicationBuilder(TestConfig.class)
.properties("spring.cloud.config.discovery.enabled=true", "spring.cloud.discovery.enabled=false",
"spring.cloud.service-registry.auto-registration.enabled=false")
.addBootstrapRegistryInitializer(registry -> registry.addCloseListener(event -> {
ConfigServerInstanceProvider.Function providerFn = event.getBootstrapContext()
.get(ConfigServerInstanceProvider.Function.class);
Binder binder = event.getBootstrapContext().get(Binder.class);
BindHandler bindHandler = event.getBootstrapContext().get(BindHandler.class);
assertThat(providerFn.apply("id", binder, bindHandler, mock(Log.class)))
.as("Should return empty list").isEmpty();
})).run().close();
}
@Test
public void eurekaClientNotEnabledProvidesEmptyList() {
new SpringApplicationBuilder(TestConfig.class)
.properties("spring.cloud.config.discovery.enabled=true", "eureka.client.enabled=false",
"spring.cloud.service-registry.auto-registration.enabled=false")
.addBootstrapRegistryInitializer(registry -> registry.addCloseListener(event -> {
ConfigServerInstanceProvider.Function providerFn = event.getBootstrapContext()
.get(ConfigServerInstanceProvider.Function.class);
Binder binder = event.getBootstrapContext().get(Binder.class);
BindHandler bindHandler = event.getBootstrapContext().get(BindHandler.class);
assertThat(providerFn.apply("id", binder, bindHandler, mock(Log.class)))
.as("Should return empty list").isEmpty();
})).run().close();
}
@Test
void enabledAddsInstanceProviderFn() {
new SpringApplicationBuilder(TestConfig.class)
.properties("spring.cloud.config.discovery.enabled=true",
"spring.cloud.service-registry.auto-registration.enabled=false")
.properties("spring.config.import: classpath:bootstrapper.yaml",
ConfigClientProperties.PREFIX + ".enabled=true")
.addBootstrapRegistryInitializer(registry -> registry.addCloseListener(event -> {
ConfigServerInstanceProvider.Function providerFn = event.getBootstrapContext()
.get(ConfigServerInstanceProvider.Function.class);
assertThat(providerFn).as("ConfigServerInstanceProvider.Function was not created when it should.")
.isNotNull();
Binder binder = event.getBootstrapContext().get(Binder.class);
BindHandler bindHandler = event.getBootstrapContext().get(BindHandler.class);
assertThatThrownBy(() -> providerFn.apply("id", binder, bindHandler, mock(Log.class)))
.isInstanceOf(ResourceAccessException.class)
.hasMessageContaining("I/O error on GET request for \"http://localhost:8761/eureka/apps/\"")
.as("Should have tried to connect to Eureka to fetch instances.");
})).run().close();
}
@@ -36,6 +36,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests the Eureka health check handler.
*
* @author Jakub Narloch
* @author Olga Maciaszek-Sharma
*/
@SpringBootTest(classes = EurekaHealthCheckTests.EurekaHealthCheckApplication.class,
webEnvironment = WebEnvironment.RANDOM_PORT, value = { "eureka.client.healthcheck.enabled=true", "debug=true" })
@@ -47,12 +48,24 @@ class EurekaHealthCheckTests {
@Test
void shouldRegisterService() {
System.setProperty("status", "UP");
InstanceInfo.InstanceStatus status = this.discoveryClient.getHealthCheckHandler()
.getStatus(InstanceInfo.InstanceStatus.UNKNOWN);
assertThat(status).isNotNull();
assertThat(status).isEqualTo(InstanceInfo.InstanceStatus.OUT_OF_SERVICE);
assertThat(status).isEqualTo(InstanceInfo.InstanceStatus.UP);
}
@Test
void shouldMapOutOfServiceToDown() {
System.setProperty("status", "OUT_OF_SERVICE");
InstanceInfo.InstanceStatus status = this.discoveryClient.getHealthCheckHandler()
.getStatus(InstanceInfo.InstanceStatus.UNKNOWN);
assertThat(status).isNotNull();
assertThat(status).isEqualTo(InstanceInfo.InstanceStatus.DOWN);
}
@Configuration(proxyBeanMethods = false)
@@ -61,7 +74,7 @@ class EurekaHealthCheckTests {
@Bean
public HealthIndicator healthIndicator() {
return () -> new Health.Builder().outOfService().build();
return () -> new Health.Builder().status(System.getProperty("status")).build();
}
}
@@ -54,7 +54,7 @@ import static com.netflix.appinfo.InstanceInfo.DEFAULT_SECURE_PORT;
import static org.springframework.util.Assert.isTrue;
/**
* Mocked Eureka Server
* Mocked Eureka Server.
*
* @author Daniel Lavoie
*/
@@ -0,0 +1,12 @@
spring:
config:
import: "optional:configserver:"
cloud:
config:
enabled: true
discovery:
service-id: eureka-configserver
enabled: true
service-registry:
auto-registration:
enabled: false
+5 -5
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>3.1.1</version>
<version>3.1.7</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-netflix-eureka-server</artifactId>
@@ -177,14 +177,14 @@
</configuration>
<dependencies>
<dependency>
<groupId>org.webjars</groupId>
<groupId>org.webjars.npm</groupId>
<artifactId>jquery</artifactId>
<version>3.5.1</version>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<groupId>org.webjars.npm</groupId>
<artifactId>bootstrap</artifactId>
<version>3.2.0</version>
<version>5.1.3</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
@@ -48,6 +48,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
/**
* @author Spencer Gibb
* @author Gang Li
* @author Weix Sun
*/
@Controller
@RequestMapping("${eureka.dashboard.path:/}")
@@ -58,8 +59,20 @@ public class EurekaController {
private ApplicationInfoManager applicationInfoManager;
private final EurekaProperties eurekaProperties;
/**
* @deprecated in favour of
* {@link EurekaController#EurekaController(ApplicationInfoManager, EurekaProperties)}
*/
@Deprecated
public EurekaController(ApplicationInfoManager applicationInfoManager) {
this(applicationInfoManager, null);
}
public EurekaController(ApplicationInfoManager applicationInfoManager, EurekaProperties eurekaProperties) {
this.applicationInfoManager = applicationInfoManager;
this.eurekaProperties = eurekaProperties;
}
@RequestMapping(method = RequestMethod.GET)
@@ -116,8 +129,14 @@ public class EurekaController {
private void populateHeader(Map<String, Object> model) {
model.put("currentTime", StatusResource.getCurrentTimeAsString());
model.put("upTime", StatusInfo.getUpTime());
model.put("environment", "N/A"); // FIXME:
model.put("datacenter", "N/A"); // FIXME:
if (eurekaProperties != null) {
model.put("environment", eurekaProperties.getEnvironment());
model.put("datacenter", eurekaProperties.getDatacenter());
}
else {
model.put("environment", "N/A");
model.put("datacenter", "N/A");
}
PeerAwareInstanceRegistry registry = getRegistry();
model.put("registry", registry);
model.put("isBelowRenewThreshold", registry.isBelowRenewThresold() == 1);
@@ -0,0 +1,83 @@
/*
* 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.netflix.eureka.server;
import java.util.Objects;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for the Eureka deployment.
*
* @author Weix Sun
*/
@ConfigurationProperties("eureka")
public class EurekaProperties {
/**
* Eureka environment. Defaults to "test".
*/
private String environment = "test";
/**
* Eureka datacenter. Defaults to "default".
*/
private String datacenter = "default";
public String getEnvironment() {
return environment;
}
public void setEnvironment(String environment) {
this.environment = environment;
}
public String getDatacenter() {
return datacenter;
}
public void setDatacenter(String datacenter) {
this.datacenter = datacenter;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
EurekaProperties that = (EurekaProperties) o;
return Objects.equals(datacenter, that.datacenter) && Objects.equals(environment, that.environment);
}
@Override
public int hashCode() {
return Objects.hash(environment, datacenter);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("EurekaProperties{");
sb.append("environment='").append(environment).append('\'');
sb.append(", datacenter=").append(datacenter);
sb.append('}');
return sb.toString();
}
}
@@ -73,11 +73,13 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
* @author Gunnar Hillert
* @author Biju Kunjummen
* @author Fahim Farook
* @author Weix Sun
*/
@Configuration(proxyBeanMethods = false)
@Import(EurekaServerInitializerConfiguration.class)
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
@EnableConfigurationProperties({ EurekaDashboardProperties.class, InstanceRegistryProperties.class })
@EnableConfigurationProperties({ EurekaDashboardProperties.class, InstanceRegistryProperties.class,
EurekaProperties.class })
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration implements WebMvcConfigurer {
@@ -113,8 +115,8 @@ public class EurekaServerAutoConfiguration implements WebMvcConfigurer {
@Bean
@ConditionalOnProperty(prefix = "eureka.dashboard", name = "enabled", matchIfMissing = true)
public EurekaController eurekaController() {
return new EurekaController(this.applicationInfoManager);
public EurekaController eurekaController(EurekaProperties eurekaProperties) {
return new EurekaController(this.applicationInfoManager, eurekaProperties);
}
static {
@@ -38,6 +38,7 @@ import org.apache.commons.logging.LogFactory;
/**
* @author Spencer Gibb
* @author Weix Sun
*/
public class EurekaServerBootstrap {
@@ -67,7 +68,6 @@ public class EurekaServerBootstrap {
public void contextInitialized(ServletContext context) {
try {
initEurekaEnvironment();
initEurekaServerContext();
context.setAttribute(EurekaServerContext.class.getName(), this.serverContext);
@@ -93,11 +93,6 @@ public class EurekaServerBootstrap {
log.info("Eureka Service is now shutdown...");
}
protected void initEurekaEnvironment() throws Exception {
log.info("Setting the eureka configuration..");
}
protected void initEurekaServerContext() throws Exception {
// For backward compatibility
JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), XStream.PRIORITY_VERY_HIGH);
@@ -16,8 +16,6 @@
package org.springframework.cloud.netflix.eureka.server;
import java.util.List;
import com.netflix.appinfo.ApplicationInfoManager;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.EurekaClient;
@@ -97,18 +95,11 @@ public class InstanceRegistry extends PeerAwareInstanceRegistryImpl implements A
@Override
public boolean renew(final String appName, final String serverId, boolean isReplication) {
log("renew " + appName + " serverId " + serverId + ", isReplication {}" + isReplication);
List<Application> applications = getSortedApplications();
for (Application input : applications) {
if (input.getName().equals(appName)) {
InstanceInfo instance = null;
for (InstanceInfo info : input.getInstances()) {
if (info.getId().equals(serverId)) {
instance = info;
break;
}
}
publishEvent(new EurekaInstanceRenewedEvent(this, appName, serverId, instance, isReplication));
break;
Application application = getApplication(appName);
if (application != null) {
InstanceInfo instanceInfo = application.getByInstanceId(serverId);
if (instanceInfo != null) {
publishEvent(new EurekaInstanceRenewedEvent(this, appName, serverId, instanceInfo, isReplication));
}
}
return super.renew(appName, serverId, isReplication);
@@ -1,22 +1,17 @@
<#import "/spring.ftl" as spring />
<nav class="navbar navbar-default" role="navigation">
<nav class="navbar navbar-expand-md navbar-dark" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="<@spring.url dashboardPath/>"><span></span></a>
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
<li>
<a href="<@spring.url dashboardPath/>">Home</a>
<a class="navbar-brand my-auto" href="<@spring.url dashboardPath/>"><span></span></a>
<button type="button" class="navbar-toggler" data-bs-toggle="collapse" data-bs-target="#navbarContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarContent">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link px-2" href="<@spring.url dashboardPath/>">Home</a>
</li>
<li>
<a href="<@spring.url dashboardPath/>/lastn">Last 1000 since startup</a>
<li class="nav-item">
<a class="nav-link px-2" href="<@spring.url dashboardPath/>/lastn">Last 1000 since startup</a>
</li>
</ul>
</div>
@@ -1,29 +1,27 @@
<#import "/spring.ftl" as spring />
<!doctype html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<html lang="en">
<head>
<base href="<@spring.url basePath/>">
<title>Eureka - Last N events</title>
<link rel="stylesheet" type="text/css" href="eureka/css/wro.css">
</head>
<body id="three">
<!--[if lt IE 7]>
<p>You are using an <strong>outdated</strong> browser. Please upgrade your browser to improve your experience.</p>
<![endif]-->
<script type="text/javascript">
if ( document.documentMode ) {
document.write('<p>You are using an <strong>outdated</strong> browser. Please upgrade your browser to improve your experience.</p>');
}
</script>
<#include "header.ftlh">
<div class="container-fluid xd-container">
<#include "navbar.ftlh">
<div id="xd-jobs" class="tab-pane active col-md-12">
<div id="xd-jobs" class="tab-pane col-md-12">
<ul class="nav nav-tabs" role="tablist" id="myTab">
<li class="active"><a data-toggle="tab" href="#cancelled">Last 1000 cancelled leases</a></li>
<li><a data-toggle="tab" href="#registered">Last 1000 newly registered leases</a></li>
<li class="nav-item"><a href="<@spring.url dashboardPath/>/lastn#cancelled" class="nav-link p-2" data-bs-toggle="tab">Last 1000 cancelled leases</a></li>
<li class="nav-item"><a href="<@spring.url dashboardPath/>/lastn#registered" class="nav-link p-2 active" data-bs-toggle="tab">Last 1000 newly registered leases</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane" id="cancelled">
@@ -42,7 +40,7 @@
<tbody>
</table>
</div>
<div class="tab-pane" id="registered">
<div class="tab-pane active" id="registered">
<table id='lastNRegistered' class="table table-striped table-hover">
<thead>
<tr><th>Timestamp</th><th>Lease</th></tr>
@@ -62,10 +60,5 @@
</div>
</div>
<script type="text/javascript" src="eureka/js/wro.js" ></script>
<script type="text/javascript">
$(function () {
$('#myTab a:last').tab('show')
})
</script>
</body>
</html>
@@ -63,7 +63,7 @@
</#if>
<h1>DS Replicas</h1>
<ul class="list-group">
<ul class="list-group mb-3">
<#list replicas as replica>
<li class="list-group-item"><a href="${replica.value}">${replica.key}</a></li>
</#list>
@@ -1,13 +1,9 @@
<#import "/spring.ftl" as spring />
<!doctype html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<html lang="en">
<head>
<base href="<@spring.url basePath/>">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Eureka</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
@@ -17,6 +13,12 @@
</head>
<body id="one">
<script type="text/javascript">
if ( document.documentMode ) {
document.write('<p>You are using an <strong>outdated</strong> browser. Please upgrade your browser to improve your experience.</p>');
}
</script>
<#include "header.ftlh">
<div class="container-fluid xd-container">
<#include "navbar.ftlh">
@@ -103,11 +105,5 @@
</table>
</div>
<script type="text/javascript" src="eureka/js/wro.js" ></script>
<script type="text/javascript">
$(document).ready(function() {
$('table.stripeable tr:odd').addClass('odd');
$('table.stripeable tr:even').addClass('even');
});
</script>
</body>
</html>
@@ -0,0 +1,56 @@
.navbar {
border-top: 4px solid #6db33f;
background-color: #34302d;
margin: 0;
padding: 0;
border-bottom: 0;
border-left: 0;
border-right: 0;
}
.navbar a.navbar-brand {
background: url("../images/spring-logo-eureka.png") -1px -1px no-repeat;
margin: 12px 0 6px;
width: 300px;
height: 46px;
display: inline-block;
text-decoration: none;
padding: 0;
}
.navbar a.navbar-brand span {
display: block;
width: 300px;
height: 46px;
background: url("../images/spring-logo-eureka.png") -1px -48px no-repeat;
opacity: 0;
transition: opacity 0.12s ease-in-out;
-moz-transition: opacity 0.12s ease-in-out;
-webkit-transition: opacity 0.12s ease-in-out;
}
.navbar a:hover.navbar-brand span {
opacity: 1;
}
.nav-link, .navbar-text {
color: #34302d;
font-family: "montserratregular", sans-serif;
text-shadow: none;
font-size: 14px;
padding: 28px 20px;
transition: all 0.15s;
-moz-transition: all 0.15s;
-webkit-transition: all 0.15s;
}
.navbar .nav-link:link {
color: #f1f1f1;
text-transform: uppercase;
}
.navbar .nav-item .nav-link:hover {
color: #f1f1f1;
background-color: #6db33f;
}
.navbar-toggler {
color: #f1f1f1;
border-width: 0;
}
table {
border-color: #34302D;
}
@@ -1,73 +0,0 @@
.navbar {
border-top: 4px solid #6db33f;
background-color: #34302d;
margin-bottom: 0px;
border-bottom: 0;
border-left: 0;
border-right: 0;
}
.navbar a.navbar-brand {
background: url("../images/spring-logo-eureka.png") -1px -1px no-repeat;
margin: 12px 0 6px;
width: 300px;
height: 46px;
display: inline-block;
text-decoration: none;
padding: 0;
}
.navbar a.navbar-brand span {
display: block;
width: 300px;
height: 46px;
background: url("../images/spring-logo-eureka.png") -1px -48px no-repeat;
opacity: 0;
-moz-transition: opacity 0.12s ease-in-out;
-webkit-transition: opacity 0.12s ease-in-out;
-o-transition: opacity 0.12s ease-in-out;
}
.navbar a:hover.navbar-brand span {
opacity: 1;
}
.navbar li > a, .navbar-text {
font-family: "montserratregular", sans-serif;
text-shadow: none;
font-size: 14px;
/* line-height: 14px; */
padding: 28px 20px;
transition: all 0.15s;
-webkit-transition: all 0.15s;
-moz-transition: all 0.15s;
-o-transition: all 0.15s;
-ms-transition: all 0.15s;
}
.navbar li > a {
text-transform: uppercase;
}
.navbar .navbar-text {
margin-top: 0;
margin-bottom: 0;
}
.navbar li:hover > a {
color: #eeeeee;
background-color: #6db33f;
}
.navbar-toggle {
border-width: 0;
.icon-bar + .icon-bar {
margin-top: 3px;
}
.icon-bar {
width: 19px;
height: 3px;
}
}
@@ -0,0 +1,102 @@
@import "typography.css";
@import "header.css";
@import "responsive.css";
a:link {
color: #5fa134;
}
a:hover {
color: #5fa134;
}
body {
background-color: #f1f1f1;
}
.table > thead > tr > th {
background-color: #34302D;
color: #f1f1f1;
}
.table-filter {
background-color: #34302D;
padding: 9px 12px;
}
.nav-tabs .nav-link {
color: #838789;
}
.nav-tabs .nav-link.active, .nav-tabs .nav-link.active:hover {
color: #f1f1f1;
background-color: #34302D;
border-color: #34302D;
}
.btn-default {
border-width: 2px;
transition: border 0.15s;
-webkit-transition: border 0.15s;
-moz-transition: border 0.15s;
-o-transition: border 0.15s;
-ms-transition: border 0.15s;
color: #f1f1f1;
background-color: #34302D;
border-color: #6db33f;
}
.btn-default:hover, .btn-default:focus, .btn-default:active, .btn-default.active, .open .dropdown-toggle.btn-default {
background-color: #34302D;
border-color: #34302D;
}
.container .text-muted {
margin: 20px 0;
}
code {
font-size: 80%;
}
.xd-container {
margin-top: 40px;
margin-bottom: 100px;
}
h1 {
margin-bottom: 15px;
}
.index-page--subtitle {
font-size: 16px;
line-height: 24px;
margin: 0 0 30px;
}
.form-horizontal button.btn-inverse {
margin-left: 32px;
}
#job-params-modal .modal-dialog {
width: 90%;
margin-left: auto;
margin-right: auto;
}
[ng-cloak].splash {
display: block !important;
}
[ng-cloak] {
display: none;
}
.splash {
background: #6db33f;
color: #34302D;
display: none;
}
.error-page {
margin-top: 100px;
text-align: center;
}
.error-page .error-title {
font-size: 24px;
line-height: 24px;
margin: 30px 0 0;
}
table td {
vertical-align: middle !important;
}
table td .progress {
margin-bottom: 0;
}
table td.action-column {
width: 1px;
}
.help-block {
color: #f1f1f1;
}
@@ -1,143 +0,0 @@
@spring-green: #6db33f;
@spring-dark-green: #5fa134;
@spring-brown: #34302D;
@spring-grey: #838789;
@spring-light-grey: #f1f1f1;
@body-bg: @spring-light-grey;
@text-color: @spring-light-grey;
@link-color: @spring-dark-green;
@link-hover-color: @spring-dark-green;
@navbar-default-link-color: @spring-light-grey;
@navbar-default-link-active-color: @spring-light-grey;
@navbar-default-link-hover-color: @spring-light-grey;
@navbar-default-link-hover-bg: @spring-green;
@navbar-default-toggle-icon-bar-bg: @spring-light-grey;
@navbar-default-toggle-hover-bg: transparent;
@navbar-default-link-active-bg: @spring-green;
@border-radius-base: 0;
@border-radius-large: 0;
@border-radius-small: 0;
@btn-default-color: @spring-light-grey;
@btn-default-bg: @spring-brown;
@btn-default-border: @spring-green;
@nav-tabs-active-link-hover-color: @spring-light-grey;
@nav-tabs-active-link-hover-bg: @spring-brown;
@nav-tabs-active-link-hover-border-color: @spring-brown;
@nav-tabs-border-color: @spring-brown;
@table-border-color: @spring-brown;
@import "typography.less";
@import "header.less";
.table > thead > tr > th {
background-color: lighten(@spring-brown, 3%);
color: @spring-light-grey;
}
.table-filter {
background-color: @spring-brown;
padding: 9px 12px;
}
.nav > li > a {
color: @spring-grey;
}
.btn-default {
border-width: 2px;
transition: border 0.15s;
-webkit-transition: border 0.15s;
-moz-transition: border 0.15s;
-o-transition: border 0.15s;
-ms-transition: border 0.15s;
&:hover,
&:focus,
&:active,
&.active,
.open .dropdown-toggle& {
background-color: @spring-brown;
border-color: @spring-brown;
}
}
.container .text-muted {
margin: 20px 0;
}
code {
font-size: 80%;
}
.xd-container {
margin-top: 40px;
margin-bottom: 100px;
}
h1 {
margin-bottom: 15px
}
.index-page--subtitle {
font-size: 16px;
line-height: 24px;
margin: 0 0 30px;
}
.form-horizontal button.btn-inverse {
margin-left: 32px;
}
#job-params-modal .modal-dialog {
width: 90%;
margin-left:auto;
margin-right:auto;
}
[ng-cloak].splash {
display: block !important;
}
[ng-cloak] {
display: none;
}
.splash {
background: @spring-green;
color: @spring-brown;
display: none;
}
.error-page {
margin-top: 100px;
text-align: center;
}
.error-page .error-title {
font-size: 24px;
line-height: 24px;
margin: 30px 0 0;
}
table td {
vertical-align: middle !important;
}
table td .progress {
margin-bottom: 0;
}
table td.action-column {
width: 1px;
}
.help-block {
color: lighten(@text-color, 0%); // lighten the text some for contrast
}
@import "responsive.less";
@@ -0,0 +1,23 @@
@media (max-width: 768px) {
.navbar a.navbar-brand {
background: url("../images/spring-logo-eureka-mobile.png") 0 center no-repeat;
}
.homepage-billboard .homepage-subtitle {
font-size: 21px;
line-height: 21px;
}
.navbar a.navbar-brand span {
display: none;
}
.navbar {
border-top-width: 0;
}
.xd-container {
margin-top: 20px;
margin-bottom: 30px;
}
.index-page--subtitle {
margin-top: 10px;
margin-bottom: 30px;
}
}
@@ -1,41 +0,0 @@
@media (max-width: 768px) {
.navbar-toggle {
position:absolute;
z-index: 9999;
left:0px;
top:0px;
}
.navbar a.navbar-brand {
display: block;
margin: 0 auto 0 auto;
width: 200px;
height: 50px;
float: none;
background: url("../images/spring-logo-eureka-mobile.png") 0 center no-repeat;
}
.homepage-billboard .homepage-subtitle {
font-size: 21px;
line-height: 21px;
}
.navbar a.navbar-brand span {
display: none;
}
.navbar {
border-top-width: 0;
}
.xd-container {
margin-top: 20px;
margin-bottom: 30px;
}
.index-page--subtitle {
margin-top: 10px;
margin-bottom: 30px;
}
}
@@ -0,0 +1,42 @@
@font-face {
font-family: 'varela_roundregular';
src: url('../fonts/varela_round-webfont.eot');
src: url('../fonts/varela_round-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/varela_round-webfont.woff') format('woff'), url('../fonts/varela_round-webfont.ttf') format('truetype'), url('../fonts/varela_round-webfont.svg#varela_roundregular') format('svg');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'montserratregular';
src: url('../fonts/montserrat-webfont.eot');
src: url('../fonts/montserrat-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/montserrat-webfont.woff') format('woff'), url('../fonts/montserrat-webfont.ttf') format('truetype'), url('../fonts/montserrat-webfont.svg#montserratregular') format('svg');
font-weight: normal;
font-style: normal;
}
body, h1, h2, h3, p, input {
margin: 0;
font-weight: 400;
font-family: "varela_roundregular", sans-serif;
color: #34302d;
}
h1 {
font-size: 24px;
line-height: 30px;
font-family: "montserratregular", sans-serif;
}
h2 {
font-size: 18px;
font-weight: 700;
line-height: 24px;
margin-bottom: 10px;
font-family: "montserratregular", sans-serif;
}
h3 {
font-size: 16px;
line-height: 24px;
margin-bottom: 10px;
font-weight: 700;
}
strong {
font-weight: 700;
font-family: "montserratregular", sans-serif;
}
@@ -1,59 +0,0 @@
@font-face {
font-family: 'varela_roundregular';
src: url('../fonts/varela_round-webfont.eot');
src: url('../fonts/varela_round-webfont.eot?#iefix') format('embedded-opentype'),
url('../fonts/varela_round-webfont.woff') format('woff'),
url('../fonts/varela_round-webfont.ttf') format('truetype'),
url('../fonts/varela_round-webfont.svg#varela_roundregular') format('svg');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'montserratregular';
src: url('../fonts/montserrat-webfont.eot');
src: url('../fonts/montserrat-webfont.eot?#iefix') format('embedded-opentype'),
url('../fonts/montserrat-webfont.woff') format('woff'),
url('../fonts/montserrat-webfont.ttf') format('truetype'),
url('../fonts/montserrat-webfont.svg#montserratregular') format('svg');
font-weight: normal;
font-style: normal;
}
body, h1, h2, h3, p, input {
margin: 0;
font-weight: 400;
font-family: "varela_roundregular", sans-serif;
color: #34302d;
}
h1 {
font-size: 24px;
line-height: 30px;
font-family: "montserratregular", sans-serif;
}
h2 {
font-size: 18px;
font-weight: 700;
line-height: 24px;
margin-bottom: 10px;
font-family: "montserratregular", sans-serif;
}
h3 {
font-size: 16px;
line-height: 24px;
margin-bottom: 10px;
font-weight: 700;
}
p {
//font-size: 15px;
//line-height: 24px;
}
strong {
font-weight: 700;
font-family: "montserratregular", sans-serif;
}
@@ -1,4 +1,5 @@
#List of preProcessors
preProcessors=cssImport
#List of postProcessors
postProcessors=less4j,jsMin
postProcessors=jsMin
@@ -1,8 +1,8 @@
<groups xmlns="http://www.isdc.ro/wro">
<group name="wro">
<css>webjar:bootstrap/3.2.0/less/bootstrap.less</css>
<css>file:@.project.basedir.@/src/main/wro/main.less</css>
<js>webjar:jquery/3.5.1/jquery.min.js</js>
<js>webjar:bootstrap/3.2.0/js/bootstrap.min.js</js>
<css>webjar:bootstrap/5.1.3/dist/css/bootstrap.min.css</css>
<css>file:@.project.basedir.@/src/main/wro/main.css</css>
<js>webjar:jquery/3.6.0/jquery.min.js</js>
<js>webjar:bootstrap/5.1.3/dist/js/bootstrap.min.js</js>
</group>
</groups>
@@ -57,9 +57,9 @@ class ApplicationDashboardPathTests {
// The "DS Replicas"
assertThat(body.contains("<h1>Instances currently registered with Eureka</h1>")).isTrue();
// The Home
assertThat(body.contains("<a href=\"/dashboard\">Home</a>")).isTrue();
assertThat(body.contains("<a class=\"nav-link px-2\" href=\"/dashboard\">Home</a>")).isTrue();
// The Lastn
assertThat(body.contains("<a href=\"/dashboard/lastn\">Last")).isTrue();
assertThat(body.contains("<a class=\"nav-link px-2\" href=\"/dashboard/lastn\">Last")).isTrue();
}
@Test
@@ -96,10 +96,13 @@ class EurekaControllerTests {
void testStatus() throws Exception {
Map<String, Object> model = new HashMap<>();
EurekaController controller = new EurekaController(infoManager);
EurekaController controller = new EurekaController(infoManager, new EurekaProperties());
controller.status(new MockHttpServletRequest("GET", "/"), model);
assertThat((String) model.get("environment")).isEqualTo("test");
assertThat((String) model.get("datacenter")).isEqualTo("default");
Map<String, Object> app = getFirst(model, "apps");
Map<String, Object> instanceInfo = getFirst(app, "instanceInfos");
Map<String, Object> instance = getFirst(instanceInfo, "instances");
@@ -16,7 +16,6 @@
package org.springframework.cloud.netflix.eureka.server;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
@@ -125,10 +124,8 @@ class InstanceRegistryTests {
final InstanceInfo instanceInfo2 = getInstanceInfo(APP_NAME, HOST_NAME, "my-host-name:8009", 8009, null);
// creating application list with an app having two instances
final Application application = new Application(APP_NAME, Arrays.asList(instanceInfo1, instanceInfo2));
final List<Application> applications = new ArrayList<>();
applications.add(application);
// stubbing applications list
doReturn(applications).when(instanceRegistry).getSortedApplications();
// stubbing application
doReturn(application).when(instanceRegistry).getApplication(APP_NAME);
// calling tested method
instanceRegistry.renew(APP_NAME, INSTANCE_ID, false);
instanceRegistry.renew(APP_NAME, "my-host-name:8009", false);
@@ -4,7 +4,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>3.1.1</version>
<version>3.1.7</version>
</parent>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<name>Spring Cloud Starter Netflix Eureka Client</name>
@@ -3,7 +3,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>3.1.1</version>
<version>3.1.7</version>
</parent>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<name>Spring Cloud Starter Netflix Eureka Server</name>
@@ -12,4 +12,5 @@
<suppress files=".*TestAutoConfiguration\.java" checks="HideUtilityClassConstructor"/>
<suppress files=".*TestAutoConfiguration\.java" checks="JavadocStyle"/>
<suppress files=".*TestAutoConfiguration\.java" checks="JavadocStyle"/>
<suppress files=".*Tests\.java" checks="JavadocVariable"/>
</suppressions>