Merge branch '3.1.x' into 3.2.x

This commit is contained in:
Ryan Baxter
2025-03-08 03:51:20 -05:00
19 changed files with 119 additions and 338 deletions

View File

@@ -110,7 +110,7 @@
<module>spring-cloud-kubernetes-k8s-client-configuration-watcher</module>
<!-- config watcher reload, using kafka and configmap -->
<module>spring-cloud-kubernetes-k8s-client-kafka-configmap-reload-multiple-apps</module>
<module>spring-cloud-kubernetes-k8s-client-kafka-configmap-reload</module>
<!-- config watcher reload, using rabbitmq and secret -->
<module>spring-cloud-kubernetes-k8s-client-rabbitmq-secret-reload-multiple-apps</module>

View File

@@ -1,57 +0,0 @@
/*
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.configuration.watcher.appA;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author wind57
*/
@SpringBootApplication
@RestController
public class AppATestApplication implements ApplicationListener<RefreshRemoteApplicationEvent> {
private final Log LOG = LogFactory.getLog(getClass());
private boolean value = false;
public static void main(String[] args) {
SpringApplication.run(AppATestApplication.class, args);
}
@GetMapping("/app-a")
public boolean index() {
LOG.info("Current value: " + value);
return value;
}
@Override
public void onApplicationEvent(RefreshRemoteApplicationEvent refreshRemoteApplicationEvent) {
LOG.info("Received remote refresh event from origin: " + refreshRemoteApplicationEvent.getOriginService()
+ " to destination : " + refreshRemoteApplicationEvent.getDestinationService());
this.value = true;
}
}

View File

@@ -1,22 +0,0 @@
#logging:
# level:
# org.springframework: DEBUG
#
spring:
application:
name: spring-cloud-kubernetes-client-configuration-watcher-configmap-app-a
cloud:
bus:
refresh:
enabled: false
enabled: true
destination: multiple-apps
stream:
default-binder: kafka
management:
endpoint:
health:
probes:
enabled: true
server:
port: 8080

View File

@@ -1,69 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-kubernetes-k8s-client-kafka-configmap-reload-multiple-apps</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>3.2.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>kafka-configmap-test-app</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-test-support</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>../../src/main/resources</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<!-- no need to build image for this module -->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>build-image</id>
<configuration>
<skip>true</skip>
</configuration>
</execution>
<execution>
<id>repackage</id>
<configuration>
<skip>true</skip>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,42 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-a-deployment
spec:
selector:
matchLabels:
app: app-a
template:
metadata:
labels:
app: app-a
spec:
containers:
- name: app-a
image: docker.io/springcloud/kafka-configmap-app-a
imagePullPolicy: IfNotPresent
env:
- name: SPRING_PROFILES_ACTIVE
value: bus-kafka
- name: spring.kafka.bootstrap-servers
value: kafka:9092
readinessProbe:
httpGet:
port: 8080
path: /actuator/health/readiness
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
successThreshold: 1
livenessProbe:
httpGet:
port: 8080
path: /actuator/health/liveness
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
successThreshold: 1
ports:
- containerPort: 8080

View File

@@ -1,14 +0,0 @@
apiVersion: v1
kind: Service
metadata:
labels:
app: app-a
name: app-a
spec:
ports:
- name: http
port: 8080
targetPort: 8080
selector:
app: app-a
type: ClusterIP

View File

@@ -1,26 +0,0 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: it-ingress-multiple-apps
namespace: default
spec:
rules:
- http:
paths:
- path: /app-a
pathType: Prefix
backend:
service:
name: app-a
port:
number: 8080
- http:
paths:
- path: /app-b
pathType: Prefix
backend:
service:
name: app-b
port:
number: 8081

View File

@@ -3,14 +3,14 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-kubernetes-k8s-client-kafka-configmap-reload-multiple-apps</artifactId>
<artifactId>spring-cloud-kubernetes-k8s-client-kafka-configmap-reload</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>3.2.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>kafka-configmap-app-a</artifactId>
<artifactId>kafka-configmap-app</artifactId>
<dependencies>
<dependency>
@@ -25,21 +25,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>../src/main/resources</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>

View File

@@ -31,17 +31,17 @@ import org.springframework.web.bind.annotation.RestController;
*/
@SpringBootApplication
@RestController
public class AppBTestApplication implements ApplicationListener<RefreshRemoteApplicationEvent> {
public class AppTestApplication implements ApplicationListener<RefreshRemoteApplicationEvent> {
private final Log LOG = LogFactory.getLog(getClass());
private boolean value = false;
public static void main(String[] args) {
SpringApplication.run(AppBTestApplication.class, args);
SpringApplication.run(AppTestApplication.class, args);
}
@GetMapping("/app-b")
@GetMapping("/app")
public boolean index() {
LOG.info("Current value: " + value);
return value;

View File

@@ -1,16 +1,13 @@
#logging:
# level:
# org.springframework: DEBUG
#
spring:
application:
name: spring-cloud-kubernetes-client-configuration-watcher-configmap-app-b
name: non-app
cloud:
bus:
refresh:
enabled: false
enabled: true
destination: multiple-apps
destination: app
id: app
stream:
default-binder: kafka
management:

View File

@@ -3,28 +3,32 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-kubernetes-k8s-client-kafka-configmap-reload-multiple-apps</artifactId>
<artifactId>spring-cloud-kubernetes-k8s-client-kafka-configmap-reload</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>3.2.1-SNAPSHOT</version>
<relativePath>../../spring-cloud-kubernetes-k8s-client-kafka-configmap-reload-multiple-apps</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>kafka-configmap-app-b</artifactId>
<artifactId>kafka-configmap-test-app</artifactId>
<properties>
<spring-boot.repackage.skip>true</spring-boot.repackage.skip>
<spring-boot.build-image.skip>true</spring-boot.build-image.skip>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-kafka</artifactId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-test-support</artifactId>
</dependency>
</dependencies>

View File

@@ -47,15 +47,13 @@ import static org.awaitility.Awaitility.await;
/**
* @author wind57
*/
class ConfigurationWatcherMultipleAppsIT {
class ConfigurationWatcherBuKafkaIT {
private static final String CONFIG_WATCHER_APP_A_IMAGE = "kafka-configmap-app-a";
private static final String CONFIG_WATCHER_APP_B_IMAGE = "kafka-configmap-app-b";
private static final String CONFIG_WATCHER_APP_IMAGE = "kafka-configmap-app";
private static final String SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME = "spring-cloud-kubernetes-configuration-watcher";
private static final String CONFIG_MAP_NAME = "multiple-apps";
private static final String CONFIG_MAP_NAME = "apps";
private static final String NAMESPACE = "default";
@@ -71,11 +69,8 @@ class ConfigurationWatcherMultipleAppsIT {
Commons.validateImage(SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME, K3S);
Commons.validateImage(CONFIG_WATCHER_APP_A_IMAGE, K3S);
Commons.loadSpringCloudKubernetesImage(CONFIG_WATCHER_APP_A_IMAGE, K3S);
Commons.validateImage(CONFIG_WATCHER_APP_B_IMAGE, K3S);
Commons.loadSpringCloudKubernetesImage(CONFIG_WATCHER_APP_B_IMAGE, K3S);
Commons.validateImage(CONFIG_WATCHER_APP_IMAGE, K3S);
Commons.loadSpringCloudKubernetesImage(CONFIG_WATCHER_APP_IMAGE, K3S);
Images.loadKafka(K3S);
@@ -86,19 +81,64 @@ class ConfigurationWatcherMultipleAppsIT {
@BeforeEach
void setup() {
util.kafka(NAMESPACE, Phase.CREATE);
appA(Phase.CREATE);
appB(Phase.CREATE);
app(Phase.CREATE);
configWatcher(Phase.CREATE);
}
@AfterEach
void afterEach() {
util.kafka(NAMESPACE, Phase.DELETE);
appA(Phase.DELETE);
appB(Phase.DELETE);
app(Phase.DELETE);
configWatcher(Phase.DELETE);
}
/**
* <pre>
*
* To run locally and debug.
*
* - I've enabled "Enable host networking" on docker for mac settings
* - docker run -p 9092:9092 confluentinc/confluent-local:7.9.0
* - docker run --network=host -e 'KAFKA_BROKERS=localhost:9092' redpandadata/console:v2.8.2
* - run 'ConfigurationWatcherApplication' with :
* SPRING_CLOUD_KUBERNETES_CLIENT_NAMESPACE=default
* SPRING_CLOUD_KUBERNETES_CONFIGURATION_WATCHER_REFRESHDELAY=1
* SPRING_MAIN_CLOUDPLATFORM=kubernetes
* SPRING_PROFILES_ACTIVE=bus-kafka
* SPRING_CLOUD_BUS_DESTINATION=app-bus-destination
*
*
* How does this test work:
*
* - 'spring.cloud.kubernetes.configmap.apps = app' will tell configuration watcher
* to find a service called 'app'.
* If such a service is found, we will call : BusRefreshTrigger::triggerRefresh
*
* - This, in turn, will publish a RefreshRemoteApplicationEvent to a randomly named topic.
* In this test case in kafka such a message will be present
*
* {
* "type": "RefreshRemoteApplicationEvent",
* "timestamp": 1741365164981,
* "originService": "spring-cloud-kubernetes-configuration-watcher:bus-kafka:8888:24d2ff91d0af65bb17b551465573db4c",
* "destinationService": "app:**",
* "id": "309dcdc5-d876-4213-bc84-198d47d2988a"
* }
*
* - our app under test has such a configuration:
*
* spring:
* application:
* name: non-app
* cloud:
* bus:
* id: app
*
* which means we "listen" on the same topic, and we are the 'destinationService'.
*
*
* </pre>
*/
@Test
void testRefresh() {
@@ -107,51 +147,33 @@ class ConfigurationWatcherMultipleAppsIT {
V1ConfigMap configMap = new V1ConfigMapBuilder().editOrNewMetadata()
.withName(CONFIG_MAP_NAME)
.addToLabels("spring.cloud.kubernetes.config", "true")
.addToAnnotations("spring.cloud.kubernetes.configmap.apps",
"spring-cloud-kubernetes-client-configuration-watcher-configmap-app-a, "
+ "spring-cloud-kubernetes-client-configuration-watcher-configmap-app-b")
.addToAnnotations("spring.cloud.kubernetes.configmap.apps", "app")
.endMetadata()
.addToData("foo", "hello world")
.build();
util.createAndWait(NAMESPACE, configMap, null);
WebClient.Builder builderA = builder();
WebClient serviceClientA = builderA.baseUrl("http://localhost:80/app-a").build();
WebClient.Builder builder = builder();
WebClient serviceClient = builder.baseUrl("http://localhost:80/app").build();
WebClient.Builder builderB = builder();
WebClient serviceClientB = builderB.baseUrl("http://localhost:80/app-b").build();
Boolean[] valueA = new Boolean[1];
Boolean[] value = new Boolean[1];
await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(240)).until(() -> {
valueA[0] = serviceClientA.method(HttpMethod.GET)
value[0] = serviceClient.method(HttpMethod.GET)
.retrieve()
.bodyToMono(Boolean.class)
.retryWhen(retrySpec())
.block();
return valueA[0];
return value[0];
});
Assertions.assertTrue(valueA[0]);
Assertions.assertTrue(value[0]);
Boolean[] valueB = new Boolean[1];
await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(240)).until(() -> {
valueB[0] = serviceClientB.method(HttpMethod.GET)
.retrieve()
.bodyToMono(Boolean.class)
.retryWhen(retrySpec())
.block();
return valueB[0];
});
Assertions.assertTrue(valueB[0]);
util.deleteAndWait(NAMESPACE, configMap, null);
}
private void appA(Phase phase) {
V1Deployment deployment = (V1Deployment) util.yaml("app-a/app-a-deployment.yaml");
V1Service service = (V1Service) util.yaml("app-a/app-a-service.yaml");
V1Ingress ingress = (V1Ingress) util
.yaml("ingress/spring-cloud-kubernetes-configuration-watcher-multiple-apps-ingress.yaml");
private void app(Phase phase) {
V1Deployment deployment = (V1Deployment) util.yaml("app/app-deployment.yaml");
V1Service service = (V1Service) util.yaml("app/app-service.yaml");
V1Ingress ingress = (V1Ingress) util.yaml("ingress/ingress.yaml");
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
@@ -161,23 +183,9 @@ class ConfigurationWatcherMultipleAppsIT {
}
}
private void appB(Phase phase) {
V1Deployment deployment = (V1Deployment) util.yaml("app-b/app-b-deployment.yaml");
V1Service service = (V1Service) util.yaml("app-b/app-b-service.yaml");
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, null, true);
}
else if (phase.equals(Phase.DELETE)) {
util.deleteAndWait(NAMESPACE, deployment, service, null);
}
}
private void configWatcher(Phase phase) {
V1Deployment deployment = (V1Deployment) util
.yaml("config-watcher/spring-cloud-kubernetes-configuration-watcher-bus-kafka-deployment.yaml");
V1Service service = (V1Service) util
.yaml("config-watcher/spring-cloud-kubernetes-configuration-watcher-service.yaml");
V1Deployment deployment = (V1Deployment) util.yaml("config-watcher/watcher-bus-kafka-deployment.yaml");
V1Service service = (V1Service) util.yaml("config-watcher/watcher-kus-kafka-service.yaml");
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, null, true);

View File

@@ -1,19 +1,19 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-b-deployment
name: app-deployment
spec:
selector:
matchLabels:
app: app-b
app: app
template:
metadata:
labels:
app: app-b
app: app
spec:
containers:
- name: app-b
image: docker.io/springcloud/kafka-configmap-app-b
- name: app
image: docker.io/springcloud/kafka-configmap-app
imagePullPolicy: IfNotPresent
env:
- name: SPRING_PROFILES_ACTIVE

View File

@@ -20,11 +20,13 @@ spec:
- name: SPRING_PROFILES_ACTIVE
value: bus-kafka
- name: SPRING_CLOUD_BUS_DESTINATION
value: multiple-apps
value: app
- name: spring.kafka.bootstrap-servers
value: kafka:9092
- name: SPRING_CLOUD_KUBERNETES_CONFIGURATION_WATCHER_REFRESHDELAY
value: 1
value: "1"
- name: LOGGING_LEVEL_ORG_SPRING_CLOUD_KUBERNETES_CONFIGURATION_WATCHER
value: DEBUG
readinessProbe:
httpGet:
port: 8888

View File

@@ -0,0 +1,16 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: it-ingress-app
namespace: default
spec:
rules:
- http:
paths:
- path: /app
pathType: Prefix
backend:
service:
name: app
port:
number: 8081

View File

@@ -8,12 +8,11 @@
<version>3.2.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-kubernetes-k8s-client-kafka-configmap-reload-multiple-apps</artifactId>
<artifactId>spring-cloud-kubernetes-k8s-client-kafka-configmap-reload</artifactId>
<packaging>pom</packaging>
<modules>
<module>kafka-configmap-app-a</module>
<module>kafka-configmap-app-b</module>
<module>kafka-configmap-app</module>
<module>kafka-configmap-test-app</module>
</modules>