mirror of
https://github.com/spring-cloud/spring-cloud-netflix.git
synced 2026-09-19 16:49:01 +00:00
Compare commits
44
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ceaf159142 | ||
|
|
fcadd450be | ||
|
|
125926ab14 | ||
|
|
554299750a | ||
|
|
12554cbd60 | ||
|
|
4112daa5f1 | ||
|
|
5c40f13f70 | ||
|
|
564ac7a1a5 | ||
|
|
a76ddd526a | ||
|
|
fdc37f6d54 | ||
|
|
28ba6079f0 | ||
|
|
9a3e622742 | ||
|
|
c6ed320cb0 | ||
|
|
0053b77e2c | ||
|
|
9a8a542c32 | ||
|
|
dd3c86e5ec | ||
|
|
0e5748f505 | ||
|
|
0eb26f03dd | ||
|
|
23259330c9 | ||
|
|
064197a724 | ||
|
|
ecfab055f9 | ||
|
|
8094476bca | ||
|
|
b5b0f67ada | ||
|
|
209ed2d41c | ||
|
|
3d27038710 | ||
|
|
221eb0937c | ||
|
|
c2dcdacaa2 | ||
|
|
7fbf5a51de | ||
|
|
078db236b8 | ||
|
|
132cf9e4f7 | ||
|
|
147d50f2c2 | ||
|
|
ded713ecc1 | ||
|
|
890f1ef4d6 | ||
|
|
25796a27f0 | ||
|
|
327b126207 | ||
|
|
5298970764 | ||
|
|
6fef357742 | ||
|
|
5844550f9a | ||
|
|
c44426f579 | ||
|
|
b467a2f462 | ||
|
|
97ee8144be | ||
|
|
4ba8c4ae53 | ||
|
|
695787a326 | ||
|
|
e32db575b8 |
@@ -1,41 +0,0 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: springcloud/pipeline-base
|
||||
user: appuser
|
||||
environment:
|
||||
_JAVA_OPTIONS: "-Xms1024m -Xmx2048m"
|
||||
TERM: dumb
|
||||
branches:
|
||||
ignore:
|
||||
- gh-pages # list of branches to ignore
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: sc-netflix-{{ .Branch }}
|
||||
- run:
|
||||
name: "Download dependencies"
|
||||
command: |
|
||||
cd spring-cloud-netflix-hystrix-contract && ../mvnw clean install && \
|
||||
./mvnw -s .settings.xml -U --fail-never dependency:go-offline || true
|
||||
- save_cache:
|
||||
key: sc-netflix-{{ .Branch }}
|
||||
paths:
|
||||
- ~/.m2
|
||||
- run:
|
||||
name: "Running build"
|
||||
command: ./mvnw -s .settings.xml clean org.jacoco:jacoco-maven-plugin:prepare-agent install -U -P sonar -nsu --batch-mode -Dmaven.test.redirectTestOutputToFile=true -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
|
||||
- run:
|
||||
name: "Aggregate test results"
|
||||
when: always
|
||||
command: |
|
||||
mkdir -p ~/junit/
|
||||
find . -type f -regex ".*/target/.*-reports/.*" -exec cp {} ~/junit/ \;
|
||||
bash <(curl -s https://codecov.io/bash)
|
||||
- store_artifacts:
|
||||
path: ~/junit/
|
||||
destination: artifacts
|
||||
- store_test_results:
|
||||
path: ~/junit/
|
||||
destination: testartifacts
|
||||
@@ -0,0 +1,33 @@
|
||||
# This workflow will build a Java project with Maven
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
||||
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ 3.1.x ]
|
||||
pull_request:
|
||||
branches: [ 3.1.x ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
java: ["8"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK ${{ matrix.java }}
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: ${{ matrix.java }}
|
||||
cache: 'maven'
|
||||
- name: Build with Maven
|
||||
run: ./mvnw clean install -B -U -P sonar
|
||||
- uses: codecov/codecov-action@v3
|
||||
with:
|
||||
fail_ci_if_error: false
|
||||
+11
-12
@@ -21,9 +21,9 @@ Edit the files in the src/main/asciidoc/ directory instead.
|
||||
:sc-ext: java
|
||||
:project-full-name: Spring Cloud Netflix
|
||||
|
||||
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://github.com/spring-cloud/spring-cloud-netflix/actions/workflows/maven.yml/badge.svg?branch=3.1.x&style=svg["Build",link="https://github.com/spring-cloud/spring-cloud-netflix/actions/workflows/maven.yml"]
|
||||
|
||||
image:https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/3.1.x/graph/badge.svg["Codecov", link="https://app.codecov.io/gh/spring-cloud/spring-cloud-netflix/tree/3.1.x"]
|
||||
|
||||
:doctype: book
|
||||
:idprefix:
|
||||
@@ -56,8 +56,7 @@ patterns provided include Service Discovery (Eureka).
|
||||
|
||||
== Building
|
||||
|
||||
|
||||
:jdkversion: 17
|
||||
:jdkversion: 1.8
|
||||
|
||||
=== Basic Compile and Test
|
||||
|
||||
@@ -143,7 +142,7 @@ NOTE: To build the module `spring-cloud-netflix-hystrix-contract` along with th
|
||||
|
||||
== Contributing
|
||||
|
||||
:spring-cloud-build-branch: master
|
||||
:spring-cloud-build-branch: 3.1.x
|
||||
|
||||
Spring Cloud is released under the non-restrictive Apache 2.0 license,
|
||||
and follows a very standard Github development process, using Github
|
||||
@@ -160,7 +159,7 @@ author credit if we do. Active contributors might be asked to join the core tea
|
||||
given the ability to merge pull requests.
|
||||
|
||||
=== Code of Conduct
|
||||
This project adheres to the Contributor Covenant https://github.com/spring-cloud/spring-cloud-build/blob/master/docs/src/main/asciidoc/code-of-conduct.adoc[code of
|
||||
This project adheres to the Contributor Covenant https://github.com/spring-cloud/spring-cloud-build/blob/3.1.x/docs/src/main/asciidoc/code-of-conduct.adoc[code of
|
||||
conduct]. By participating, you are expected to uphold this code. Please report
|
||||
unacceptable behavior to spring-code-of-conduct@pivotal.io.
|
||||
|
||||
@@ -171,7 +170,7 @@ added after the original pull request but before a merge.
|
||||
* Use the Spring Framework code format conventions. If you use Eclipse
|
||||
you can import formatter settings using the
|
||||
`eclipse-code-formatter.xml` file from the
|
||||
https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-dependencies-parent/eclipse-code-formatter.xml[Spring
|
||||
https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/3.1.x/spring-cloud-dependencies-parent/eclipse-code-formatter.xml[Spring
|
||||
Cloud Build] project. If using IntelliJ, you can use the
|
||||
https://plugins.jetbrains.com/plugin/6546[Eclipse Code Formatter
|
||||
Plugin] to import the same file.
|
||||
@@ -267,7 +266,7 @@ If you need to suppress some rules (e.g. line length needs to be longer), then i
|
||||
It's advisable to copy the `${spring-cloud-build.rootFolder}/.editorconfig` and `${spring-cloud-build.rootFolder}/.springformat` to your project. That way, some default formatting rules will be applied. You can do so by running this script:
|
||||
|
||||
```bash
|
||||
$ curl https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/.editorconfig -o .editorconfig
|
||||
$ curl https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/3.1.x/.editorconfig -o .editorconfig
|
||||
$ touch .springformat
|
||||
```
|
||||
|
||||
@@ -276,7 +275,7 @@ $ touch .springformat
|
||||
==== Intellij IDEA
|
||||
|
||||
In order to setup Intellij you should import our coding conventions, inspection profiles and set up the checkstyle plugin.
|
||||
The following files can be found in the https://github.com/spring-cloud/spring-cloud-build/tree/master/spring-cloud-build-tools[Spring Cloud Build] project.
|
||||
The following files can be found in the https://github.com/spring-cloud/spring-cloud-build/tree/3.1.x/spring-cloud-build-tools[Spring Cloud Build] project.
|
||||
|
||||
.spring-cloud-build-tools/
|
||||
----
|
||||
@@ -315,10 +314,10 @@ To have Intellij work with Checkstyle, you have to install the `Checkstyle` plug
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/{spring-cloud-build-branch}/docs/src/main/asciidoc/images/intellij-checkstyle.png[Checkstyle]
|
||||
|
||||
Go to `File` -> `Settings` -> `Other settings` -> `Checkstyle`. There click on the `+` icon in the `Configuration file` section. There, you'll have to define where the checkstyle rules should be picked from. In the image above, we've picked the rules from the cloned Spring Cloud Build repository. However, you can point to the Spring Cloud Build's GitHub repository (e.g. for the `checkstyle.xml` : `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/main/resources/checkstyle.xml`). We need to provide the following variables:
|
||||
Go to `File` -> `Settings` -> `Other settings` -> `Checkstyle`. There click on the `+` icon in the `Configuration file` section. There, you'll have to define where the checkstyle rules should be picked from. In the image above, we've picked the rules from the cloned Spring Cloud Build repository. However, you can point to the Spring Cloud Build's GitHub repository (e.g. for the `checkstyle.xml` : `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/3.1.x/spring-cloud-build-tools/src/main/resources/checkstyle.xml`). We need to provide the following variables:
|
||||
|
||||
- `checkstyle.header.file` - please point it to the Spring Cloud Build's, `spring-cloud-build-tools/src/main/resources/checkstyle-header.txt` file either in your cloned repo or via the `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/main/resources/checkstyle-header.txt` URL.
|
||||
- `checkstyle.suppressions.file` - default suppressions. Please point it to the Spring Cloud Build's, `spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` file either in your cloned repo or via the `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` URL.
|
||||
- `checkstyle.header.file` - please point it to the Spring Cloud Build's, `spring-cloud-build-tools/src/main/resources/checkstyle-header.txt` file either in your cloned repo or via the `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/3.1.x/spring-cloud-build-tools/src/main/resources/checkstyle-header.txt` URL.
|
||||
- `checkstyle.suppressions.file` - default suppressions. Please point it to the Spring Cloud Build's, `spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` file either in your cloned repo or via the `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/3.1.x/spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` URL.
|
||||
- `checkstyle.additional.suppressions.file` - this variable corresponds to suppressions in your local project. E.g. you're working on `spring-cloud-contract`. Then point to the `project-root/src/checkstyle/checkstyle-suppressions.xml` folder. Example for `spring-cloud-contract` would be: `/home/username/spring-cloud-contract/src/checkstyle/checkstyle-suppressions.xml`.
|
||||
|
||||
IMPORTANT: Remember to set the `Scan Scope` to `All sources` since we apply checkstyle rules for production and test sources.
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>3.1.4-SNAPSHOT</version>
|
||||
<version>3.1.9-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-docs</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
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://github.com/spring-cloud/spring-cloud-netflix/actions/workflows/maven.yml/badge.svg?branch=3.1.x&style=svg["Build",link="https://github.com/spring-cloud/spring-cloud-netflix/actions/workflows/maven.yml"]
|
||||
|
||||
image:https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/3.1.x/graph/badge.svg["Codecov", link="https://app.codecov.io/gh/spring-cloud/spring-cloud-netflix/tree/3.1.x"]
|
||||
|
||||
include::intro.adoc[]
|
||||
|
||||
@@ -14,14 +14,14 @@ include::intro.adoc[]
|
||||
|
||||
== Building
|
||||
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/src/main/asciidoc/building-jdk8.adoc[]
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/3.1.x/docs/src/main/asciidoc/building-jdk8.adoc[]
|
||||
|
||||
NOTE: To build the module `spring-cloud-netflix-hystrix-contract` along with the entire Netflix project run the
|
||||
`build.sh` script in the `scripts` directory.
|
||||
|
||||
== Contributing
|
||||
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/src/main/asciidoc/contributing.adoc[]
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/3.1.x/docs/src/main/asciidoc/contributing.adoc[]
|
||||
|
||||
== License
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|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.
|
||||
|
||||
|===
|
||||
@@ -112,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.
|
||||
@@ -294,7 +307,7 @@ The following example shows the dependencies you need to add:
|
||||
|
||||
You need not use the raw Netflix `EurekaClient`.
|
||||
Also, it is usually more convenient to use it behind a wrapper of some sort.
|
||||
Spring Cloud has support for <<spring-cloud-feign, Feign>> (a REST client builder) and <<spring-cloud-ribbon, Spring `RestTemplate`>> through the logical Eureka service identifiers (VIPs) instead of physical URLs.
|
||||
Spring Cloud has support for <<spring-cloud-feign, Feign>> (a REST client builder) and https://docs.spring.io/spring-cloud-commons/docs/3.1.7/reference/html/#spring-cloud-loadbalancer[Spring Cloud LoadBalancer] through the logical Eureka service identifiers (VIPs) instead of physical URLs.
|
||||
|
||||
You can also use the `org.springframework.cloud.client.discovery.DiscoveryClient`, which provides a simple API (not specific to Netflix) for discovery clients, as shown in the following example:
|
||||
|
||||
|
||||
@@ -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.4-SNAPSHOT</version>
|
||||
<version>3.1.9-SNAPSHOT</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.4-SNAPSHOT</version>
|
||||
<version>3.1.10-SNAPSHOT</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
<scm>
|
||||
@@ -21,8 +21,10 @@
|
||||
</scm>
|
||||
<properties>
|
||||
<bintray.package>netflix</bintray.package>
|
||||
<spring-cloud-commons.version>3.1.4-SNAPSHOT</spring-cloud-commons.version>
|
||||
<spring-cloud-config.version>3.1.4-SNAPSHOT</spring-cloud-config.version>
|
||||
<spring-cloud-commons.version>3.1.9-SNAPSHOT</spring-cloud-commons.version>
|
||||
<spring-cloud-config.version>3.1.10-SNAPSHOT</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>
|
||||
@@ -147,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>
|
||||
|
||||
@@ -5,16 +5,16 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-dependencies-parent</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>3.1.4-SNAPSHOT</version>
|
||||
<version>3.1.10-SNAPSHOT</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-dependencies</artifactId>
|
||||
<version>3.1.4-SNAPSHOT</version>
|
||||
<version>3.1.9-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>spring-cloud-netflix-dependencies</name>
|
||||
<description>Spring Cloud Netflix Dependencies</description>
|
||||
<properties>
|
||||
<eureka.version>1.10.17</eureka.version>
|
||||
<eureka.version>1.10.18</eureka.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>3.1.4-SNAPSHOT</version>
|
||||
<version>3.1.9-SNAPSHOT</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>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>3.1.4-SNAPSHOT</version>
|
||||
<version>3.1.9-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
|
||||
@@ -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>
|
||||
|
||||
+96
@@ -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 + '}';
|
||||
}
|
||||
|
||||
}
|
||||
+6
-2
@@ -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
|
||||
|
||||
+46
-23
@@ -17,12 +17,17 @@
|
||||
package org.springframework.cloud.netflix.eureka.config;
|
||||
|
||||
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;
|
||||
@@ -39,35 +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;
|
||||
}
|
||||
|
||||
return 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 (id) -> Collections.emptyList();
|
||||
}
|
||||
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;
|
||||
});
|
||||
// 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 Boolean getDiscoveryEnabled(Binder binder) {
|
||||
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();
|
||||
}
|
||||
|
||||
EurekaClientConfigBean config = binder.bind(EurekaClientConfigBean.PREFIX, EurekaClientConfigBean.class)
|
||||
.orElseGet(EurekaClientConfigBean::new);
|
||||
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(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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+28
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-18
@@ -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,
|
||||
|
||||
+40
-45
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
-6
@@ -12,12 +12,6 @@
|
||||
"description": "Determines whether the EurekaClient instance can be refreshed or not(If disabled none of the Eureka client properties will be refreshable).",
|
||||
"type": "java.lang.Boolean"
|
||||
},
|
||||
{
|
||||
"defaultValue": true,
|
||||
"name": "ribbon.eureka.enabled",
|
||||
"description": "Enables the use of Eureka with Ribbon.",
|
||||
"type": "java.lang.Boolean"
|
||||
},
|
||||
{
|
||||
"defaultValue": false,
|
||||
"name": "eureka.client.webclient.enabled",
|
||||
|
||||
+155
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+19
-5
@@ -16,16 +16,21 @@
|
||||
|
||||
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 {
|
||||
|
||||
@@ -48,7 +53,10 @@ class EurekaConfigServerBootstrapperTests {
|
||||
.addBootstrapRegistryInitializer(registry -> registry.addCloseListener(event -> {
|
||||
ConfigServerInstanceProvider.Function providerFn = event.getBootstrapContext()
|
||||
.get(ConfigServerInstanceProvider.Function.class);
|
||||
assertThat(providerFn.apply("id")).as("Should return empty list").isEmpty();
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -60,19 +68,25 @@ class EurekaConfigServerBootstrapperTests {
|
||||
.addBootstrapRegistryInitializer(registry -> registry.addCloseListener(event -> {
|
||||
ConfigServerInstanceProvider.Function providerFn = event.getBootstrapContext()
|
||||
.get(ConfigServerInstanceProvider.Function.class);
|
||||
assertThat(providerFn.apply("id")).as("Should return empty list").isEmpty();
|
||||
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);
|
||||
assertThatThrownBy(() -> providerFn.apply("id")).isInstanceOf(ResourceAccessException.class)
|
||||
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();
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
# for EurekaRibbonClientPropertyOverrideIntegrationTests
|
||||
foo3:
|
||||
ribbon:
|
||||
NFLoadBalancerPingClassName: com.netflix.loadbalancer.DummyPing
|
||||
NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
|
||||
|
||||
|
||||
security:
|
||||
basic:
|
||||
enabled: false
|
||||
|
||||
@@ -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,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>3.1.4-SNAPSHOT</version>
|
||||
<version>3.1.9-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-eureka-server</artifactId>
|
||||
@@ -189,7 +189,7 @@
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<version>3.6.28</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>3.1.4-SNAPSHOT</version>
|
||||
<version>3.1.9-SNAPSHOT</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.4-SNAPSHOT</version>
|
||||
<version>3.1.9-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
|
||||
<name>Spring Cloud Starter Netflix Eureka Server</name>
|
||||
|
||||
@@ -13,4 +13,5 @@
|
||||
<suppress files=".*TestAutoConfiguration\.java" checks="JavadocStyle"/>
|
||||
<suppress files=".*TestAutoConfiguration\.java" checks="JavadocStyle"/>
|
||||
<suppress files=".*Tests\.java" checks="JavadocVariable"/>
|
||||
<suppress files=".*EurekaConfigServerBootstrapperIT\.java" checks="JavadocVariable"/>
|
||||
</suppressions>
|
||||
Reference in New Issue
Block a user