Merge branch '3.0.x'
This commit is contained in:
@@ -19,6 +19,7 @@ package org.springframework.cloud.kubernetes.client.catalog;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import io.kubernetes.client.openapi.models.V1Deployment;
|
||||
import io.kubernetes.client.openapi.models.V1Ingress;
|
||||
@@ -27,7 +28,10 @@ import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.testcontainers.k3s.K3sContainer;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
import reactor.util.retry.Retry;
|
||||
@@ -44,18 +48,29 @@ import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.awaitility.Awaitility.await;
|
||||
import static org.springframework.cloud.kubernetes.client.catalog.KubernetesClientCatalogWatchUtils.patchForEndpointSlices;
|
||||
import static org.springframework.cloud.kubernetes.client.catalog.KubernetesClientCatalogWatchUtils.patchForEndpointSlicesNamespaces;
|
||||
import static org.springframework.cloud.kubernetes.client.catalog.KubernetesClientCatalogWatchUtils.patchForEndpointsNamespaces;
|
||||
import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.waitForLogStatement;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
class KubernetesClientCatalogWatchIT {
|
||||
|
||||
private static final String APP_NAME = "spring-cloud-kubernetes-client-catalog-watcher";
|
||||
|
||||
private static final String NAMESPACE = "default";
|
||||
|
||||
private static final String NAMESPACE_A = "namespacea";
|
||||
|
||||
private static final String NAMESPACE_B = "namespaceb";
|
||||
|
||||
private static final K3sContainer K3S = Commons.container();
|
||||
|
||||
private static final String DOCKER_IMAGE = "docker.io/springcloud/" + APP_NAME + ":" + Commons.pomVersion();
|
||||
|
||||
private static Util util;
|
||||
|
||||
@BeforeAll
|
||||
@@ -65,10 +80,15 @@ class KubernetesClientCatalogWatchIT {
|
||||
Commons.loadSpringCloudKubernetesImage(APP_NAME, K3S);
|
||||
util = new Util(K3S);
|
||||
util.setUp(NAMESPACE);
|
||||
app(Phase.CREATE);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void afterAll() {
|
||||
util.deleteClusterWide(NAMESPACE, Set.of(NAMESPACE_A, NAMESPACE_B));
|
||||
util.deleteNamespace(NAMESPACE_A);
|
||||
util.deleteNamespace(NAMESPACE_B);
|
||||
app(Phase.DELETE);
|
||||
Commons.systemPrune();
|
||||
}
|
||||
|
||||
@@ -86,31 +106,35 @@ class KubernetesClientCatalogWatchIT {
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
void testCatalogWatchWithEndpoints() throws Exception {
|
||||
app(false, Phase.CREATE);
|
||||
assertLogStatement("stateGenerator is of type: KubernetesEndpointsCatalogWatch");
|
||||
@Order(1)
|
||||
void testCatalogWatchWithEndpoints() {
|
||||
waitForLogStatement("stateGenerator is of type: KubernetesEndpointsCatalogWatch", K3S, APP_NAME);
|
||||
test();
|
||||
app(false, Phase.DELETE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCatalogWatchWithEndpointSlices() throws Exception {
|
||||
app(true, Phase.CREATE);
|
||||
assertLogStatement("stateGenerator is of type: KubernetesEndpointSlicesCatalogWatch");
|
||||
@Order(2)
|
||||
void testCatalogWatchWithEndpointSlices() {
|
||||
patchForEndpointSlices(APP_NAME, NAMESPACE, DOCKER_IMAGE);
|
||||
waitForLogStatement("stateGenerator is of type: KubernetesEndpointSlicesCatalogWatch", K3S, APP_NAME);
|
||||
test();
|
||||
app(true, Phase.DELETE);
|
||||
|
||||
testCatalogWatchWithEndpointsNamespaces();
|
||||
}
|
||||
|
||||
/**
|
||||
* we log in debug mode the type of the StateGenerator we use, be that Endpoints or
|
||||
* EndpointSlices. Here we make sure that in the test we actually use the correct
|
||||
* type.
|
||||
*/
|
||||
private void assertLogStatement(String log) throws Exception {
|
||||
String appPodName = K3S.execInContainer("kubectl", "get", "pods", "-l",
|
||||
"app=spring-cloud-kubernetes-client-catalog-watcher", "-o=name", "--no-headers").getStdout();
|
||||
String allLogs = K3S.execInContainer("kubectl", "logs", appPodName.trim()).getStdout();
|
||||
Assertions.assertTrue(allLogs.contains(log));
|
||||
void testCatalogWatchWithEndpointsNamespaces() {
|
||||
util.createNamespace(NAMESPACE_A);
|
||||
util.createNamespace(NAMESPACE_B);
|
||||
util.setUpClusterWide(NAMESPACE, Set.of(NAMESPACE_A, NAMESPACE_B));
|
||||
util.busybox(NAMESPACE_A, Phase.CREATE);
|
||||
util.busybox(NAMESPACE_B, Phase.CREATE);
|
||||
patchForEndpointsNamespaces(APP_NAME, NAMESPACE, DOCKER_IMAGE);
|
||||
KubernetesClientCatalogWatchNamespacesDelegate.testCatalogWatchWithEndpointsNamespaces();
|
||||
|
||||
util.busybox(NAMESPACE_A, Phase.CREATE);
|
||||
util.busybox(NAMESPACE_B, Phase.CREATE);
|
||||
patchForEndpointSlicesNamespaces(APP_NAME, NAMESPACE, DOCKER_IMAGE);
|
||||
KubernetesClientCatalogWatchNamespacesDelegate.testCatalogWatchWithEndpointSlicesNamespaces();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,7 +155,11 @@ class KubernetesClientCatalogWatchIT {
|
||||
// we get 3 pods as input, but because they are sorted by name in the catalog
|
||||
// watcher implementation
|
||||
// we will get the first busybox instances here.
|
||||
|
||||
if (result != null) {
|
||||
if (result.size() != 3) {
|
||||
return false;
|
||||
}
|
||||
holder[0] = result.get(0);
|
||||
holder[1] = result.get(1);
|
||||
return true;
|
||||
@@ -184,10 +212,8 @@ class KubernetesClientCatalogWatchIT {
|
||||
|
||||
}
|
||||
|
||||
private static void app(boolean useEndpointSlices, Phase phase) {
|
||||
V1Deployment deployment = useEndpointSlices
|
||||
? (V1Deployment) util.yaml("app/watcher-endpoint-slices-deployment.yaml")
|
||||
: (V1Deployment) util.yaml("app/watcher-endpoints-deployment.yaml");
|
||||
private static void app(Phase phase) {
|
||||
V1Deployment deployment = (V1Deployment) util.yaml("app/watcher-deployment.yaml");
|
||||
V1Service service = (V1Service) util.yaml("app/watcher-service.yaml");
|
||||
V1Ingress ingress = (V1Ingress) util.yaml("app/watcher-ingress.yaml");
|
||||
|
||||
|
||||
@@ -17,24 +17,12 @@
|
||||
package org.springframework.cloud.kubernetes.client.catalog;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import io.kubernetes.client.openapi.models.V1Deployment;
|
||||
import io.kubernetes.client.openapi.models.V1EnvVar;
|
||||
import io.kubernetes.client.openapi.models.V1EnvVarBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1Ingress;
|
||||
import io.kubernetes.client.openapi.models.V1Service;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.k3s.K3sContainer;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
import reactor.util.retry.Retry;
|
||||
@@ -51,8 +39,13 @@ import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.awaitility.Awaitility.await;
|
||||
import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.waitForLogStatement;
|
||||
|
||||
public class KubernetesClientCatalogWatchNamespacesIT {
|
||||
final class KubernetesClientCatalogWatchNamespacesDelegate {
|
||||
|
||||
private KubernetesClientCatalogWatchNamespacesDelegate() {
|
||||
|
||||
}
|
||||
|
||||
private static final String APP_NAME = "spring-cloud-kubernetes-client-catalog-watcher";
|
||||
|
||||
@@ -60,42 +53,10 @@ public class KubernetesClientCatalogWatchNamespacesIT {
|
||||
|
||||
private static final String NAMESPACE_B = "namespaceb";
|
||||
|
||||
private static final String NAMESPACE_DEFAULT = "default";
|
||||
|
||||
private static final K3sContainer K3S = Commons.container();
|
||||
|
||||
private static Util util;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() throws Exception {
|
||||
K3S.start();
|
||||
Commons.validateImage(APP_NAME, K3S);
|
||||
Commons.loadSpringCloudKubernetesImage(APP_NAME, K3S);
|
||||
util = new Util(K3S);
|
||||
util.setUp(NAMESPACE_DEFAULT);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void afterAll() {
|
||||
Commons.systemPrune();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
util.createNamespace(NAMESPACE_A);
|
||||
util.createNamespace(NAMESPACE_B);
|
||||
util.setUpClusterWide(NAMESPACE_DEFAULT, Set.of(NAMESPACE_A, NAMESPACE_B));
|
||||
util.busybox(NAMESPACE_A, Phase.CREATE);
|
||||
util.busybox(NAMESPACE_B, Phase.CREATE);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterEach() {
|
||||
util.deleteClusterWide(NAMESPACE_DEFAULT, Set.of(NAMESPACE_A, NAMESPACE_B));
|
||||
util.deleteNamespace(NAMESPACE_A);
|
||||
util.deleteNamespace(NAMESPACE_B);
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* - we deploy one busybox service with 2 replica pods in namespace namespacea
|
||||
@@ -106,39 +67,21 @@ public class KubernetesClientCatalogWatchNamespacesIT {
|
||||
* - assert that we receive only spring-cloud-kubernetes-client-catalog-watcher pod
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
void testCatalogWatchWithEndpoints() throws Exception {
|
||||
app(false, Phase.CREATE);
|
||||
assertLogStatement("stateGenerator is of type: KubernetesEndpointsCatalogWatch");
|
||||
test();
|
||||
app(false, Phase.DELETE);
|
||||
static void testCatalogWatchWithEndpointsNamespaces() {
|
||||
waitForLogStatement("stateGenerator is of type: KubernetesEndpointsCatalogWatch", K3S, APP_NAME);
|
||||
testForNamespacesFilter();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCatalogWatchWithEndpointSlices() throws Exception {
|
||||
app(true, Phase.CREATE);
|
||||
assertLogStatement("stateGenerator is of type: KubernetesEndpointSlicesCatalogWatch");
|
||||
test();
|
||||
app(true, Phase.DELETE);
|
||||
}
|
||||
|
||||
/**
|
||||
* we log in debug mode the type of the StateGenerator we use, be that Endpoints or
|
||||
* EndpointSlices. Here we make sure that in the test we actually use the correct
|
||||
* type.
|
||||
*/
|
||||
private void assertLogStatement(String log) throws Exception {
|
||||
String appPodName = K3S.execInContainer("kubectl", "get", "pods", "-l",
|
||||
"app=spring-cloud-kubernetes-client-catalog-watcher", "-o=name", "--no-headers").getStdout();
|
||||
String allLogs = K3S.execInContainer("kubectl", "logs", appPodName.trim()).getStdout();
|
||||
Assertions.assertTrue(allLogs.contains(log));
|
||||
static void testCatalogWatchWithEndpointSlicesNamespaces() {
|
||||
waitForLogStatement("stateGenerator is of type: KubernetesEndpointSlicesCatalogWatch", K3S, APP_NAME);
|
||||
testForNamespacesFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* the test is the same for both endpoints and endpoint slices, the set-up for them is
|
||||
* different.
|
||||
*/
|
||||
private void test() {
|
||||
private static void testForNamespacesFilter() {
|
||||
|
||||
WebClient client = builder().baseUrl("http://localhost/result").build();
|
||||
EndpointNameAndNamespace[] holder = new EndpointNameAndNamespace[4];
|
||||
@@ -180,6 +123,7 @@ public class KubernetesClientCatalogWatchNamespacesIT {
|
||||
Assertions.assertEquals(NAMESPACE_B, sorted.get(2).namespace());
|
||||
Assertions.assertEquals(NAMESPACE_B, sorted.get(3).namespace());
|
||||
|
||||
util = new Util(K3S);
|
||||
util.busybox(NAMESPACE_A, Phase.DELETE);
|
||||
util.busybox(NAMESPACE_B, Phase.DELETE);
|
||||
|
||||
@@ -195,37 +139,11 @@ public class KubernetesClientCatalogWatchNamespacesIT {
|
||||
|
||||
}
|
||||
|
||||
private void app(boolean useEndpointSlices, Phase phase) {
|
||||
V1Deployment deployment = useEndpointSlices
|
||||
? (V1Deployment) util.yaml("app/watcher-endpoint-slices-deployment.yaml")
|
||||
: (V1Deployment) util.yaml("app/watcher-endpoints-deployment.yaml");
|
||||
V1Service service = (V1Service) util.yaml("app/watcher-service.yaml");
|
||||
V1Ingress ingress = (V1Ingress) util.yaml("app/watcher-ingress.yaml");
|
||||
|
||||
if (phase.equals(Phase.CREATE)) {
|
||||
V1EnvVar one = new V1EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0")
|
||||
.withValue(NAMESPACE_A).build();
|
||||
|
||||
V1EnvVar two = new V1EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1")
|
||||
.withValue(NAMESPACE_B).build();
|
||||
|
||||
List<V1EnvVar> existing = new ArrayList<>(
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv());
|
||||
existing.add(one);
|
||||
existing.add(two);
|
||||
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(existing);
|
||||
util.createAndWait(NAMESPACE_DEFAULT, null, deployment, service, ingress, true);
|
||||
}
|
||||
else if (phase.equals(Phase.DELETE)) {
|
||||
util.deleteAndWait(NAMESPACE_DEFAULT, deployment, service, ingress);
|
||||
}
|
||||
}
|
||||
|
||||
private WebClient.Builder builder() {
|
||||
private static WebClient.Builder builder() {
|
||||
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
|
||||
}
|
||||
|
||||
private RetryBackoffSpec retrySpec() {
|
||||
private static RetryBackoffSpec retrySpec() {
|
||||
return Retry.fixedDelay(15, Duration.ofSeconds(1)).filter(Objects::nonNull);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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.client.catalog;
|
||||
|
||||
import static org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util.patchWithReplace;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
final class KubernetesClientCatalogWatchUtils {
|
||||
|
||||
private KubernetesClientCatalogWatchUtils() {
|
||||
|
||||
}
|
||||
|
||||
private static final String BODY_ONE = """
|
||||
{
|
||||
"spec": {
|
||||
"template": {
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "spring-cloud-kubernetes-client-catalog-watcher",
|
||||
"image": "image_name_here",
|
||||
"env": [
|
||||
{
|
||||
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_USE_ENDPOINT_SLICES",
|
||||
"value": "TRUE"
|
||||
},
|
||||
{
|
||||
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY_CATALOG",
|
||||
"value": "DEBUG"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String BODY_TWO = """
|
||||
{
|
||||
"spec": {
|
||||
"template": {
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "spring-cloud-kubernetes-client-catalog-watcher",
|
||||
"image": "image_name_here",
|
||||
"env": [
|
||||
{
|
||||
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_USE_ENDPOINT_SLICES",
|
||||
"value": "FALSE"
|
||||
},
|
||||
{
|
||||
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY_CATALOG",
|
||||
"value": "DEBUG"
|
||||
},
|
||||
{
|
||||
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0",
|
||||
"value": "namespacea"
|
||||
},
|
||||
{
|
||||
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1",
|
||||
"value": "namespaceb"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String BODY_THREE = """
|
||||
{
|
||||
"spec": {
|
||||
"template": {
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "spring-cloud-kubernetes-client-catalog-watcher",
|
||||
"image": "image_name_here",
|
||||
"env": [
|
||||
{
|
||||
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_USE_ENDPOINT_SLICES",
|
||||
"value": "TRUE"
|
||||
},
|
||||
{
|
||||
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY_CATALOG",
|
||||
"value": "DEBUG"
|
||||
},
|
||||
{
|
||||
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0",
|
||||
"value": "namespacea"
|
||||
},
|
||||
{
|
||||
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1",
|
||||
"value": "namespaceb"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
static void patchForEndpointSlices(String deploymentName, String namespace, String imageName) {
|
||||
patchWithReplace(imageName, deploymentName, namespace, BODY_ONE);
|
||||
}
|
||||
|
||||
static void patchForEndpointsNamespaces(String deploymentName, String namespace, String imageName) {
|
||||
patchWithReplace(imageName, deploymentName, namespace, BODY_TWO);
|
||||
}
|
||||
|
||||
static void patchForEndpointSlicesNamespaces(String deploymentName, String namespace, String imageName) {
|
||||
patchWithReplace(imageName, deploymentName, namespace, BODY_THREE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,6 +28,6 @@ spec:
|
||||
- containerPort: 8080
|
||||
env:
|
||||
- name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY_CATALOG
|
||||
value: DEBUG
|
||||
value: "DEBUG"
|
||||
- name: SPRING_CLOUD_KUBERNETES_DISCOVERY_USE_ENDPOINT_SLICES
|
||||
value: true
|
||||
value: "FALSE"
|
||||
@@ -1,33 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: spring-cloud-kubernetes-client-catalog-watcher
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: spring-cloud-kubernetes-client-catalog-watcher
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: spring-cloud-kubernetes-client-catalog-watcher
|
||||
spec:
|
||||
serviceAccountName: spring-cloud-kubernetes-serviceaccount
|
||||
containers:
|
||||
- name: spring-cloud-kubernetes-client-catalog-watcher
|
||||
image: docker.io/springcloud/spring-cloud-kubernetes-client-catalog-watcher
|
||||
imagePullPolicy: IfNotPresent
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
port: 8080
|
||||
path: /actuator/health/readiness
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
port: 8080
|
||||
path: /actuator/health/liveness
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
env:
|
||||
- name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_DISCOVERY_CATALOG
|
||||
value: DEBUG
|
||||
- name: SPRING_CLOUD_KUBERNETES_DISCOVERY_USE_ENDPOINT_SLICES
|
||||
value: false
|
||||
@@ -35,7 +35,6 @@ import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.testcontainers.containers.Container;
|
||||
import org.testcontainers.k3s.K3sContainer;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
import reactor.util.retry.Retry;
|
||||
@@ -103,7 +102,7 @@ class KubernetesClientDiscoveryClientIT {
|
||||
|
||||
util.busybox(NAMESPACE, Phase.CREATE);
|
||||
|
||||
Assertions.assertTrue(logs().contains("serviceSharedInformer will use namespace : default"));
|
||||
Commons.waitForLogStatement("serviceSharedInformer will use namespace : default", K3S, IMAGE_NAME);
|
||||
|
||||
WebClient servicesClient = builder().baseUrl("http://localhost/services").build();
|
||||
|
||||
@@ -180,10 +179,8 @@ class KubernetesClientDiscoveryClientIT {
|
||||
util.busybox(NAMESPACE_B, Phase.CREATE);
|
||||
|
||||
KubernetesClientDiscoveryClientUtils.patchForAllNamespaces(DEPLOYMENT_NAME, NAMESPACE);
|
||||
util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE,
|
||||
Map.of("app", "spring-cloud-kubernetes-client-discovery-it"));
|
||||
|
||||
Assertions.assertTrue(logs().contains("serviceSharedInformer will use all-namespaces"));
|
||||
Commons.waitForLogStatement("serviceSharedInformer will use all-namespaces", K3S, IMAGE_NAME);
|
||||
|
||||
WebClient servicesClient = builder().baseUrl("http://localhost/services").build();
|
||||
List<String> servicesResult = servicesClient.method(HttpMethod.GET).retrieve()
|
||||
@@ -228,16 +225,14 @@ class KubernetesClientDiscoveryClientIT {
|
||||
util.wiremock(NAMESPACE_B, "/wiremock", Phase.CREATE);
|
||||
|
||||
KubernetesClientDiscoveryClientUtils.patchForSingleNamespace(DEPLOYMENT_NAME, NAMESPACE);
|
||||
util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE,
|
||||
Map.of("app", "spring-cloud-kubernetes-client-discovery-it"));
|
||||
|
||||
// first check that wiremock service is present in both namespaces a and b
|
||||
assertServicePresentInNamespaces(List.of("a", "b"), "service-wiremock", "service-wiremock");
|
||||
String logs = logs();
|
||||
Assertions.assertTrue(logs.contains("using selective namespaces : [a]"));
|
||||
Assertions.assertTrue(logs.contains("reading pod in namespace : default"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a"));
|
||||
|
||||
Commons.waitForLogStatement("using selective namespaces : [a]", K3S, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("reading pod in namespace : default", K3S, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for services) in namespace : a", K3S, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for endpoints) in namespace : a", K3S, IMAGE_NAME);
|
||||
|
||||
WebClient servicesClient = builder().baseUrl("http://localhost/services").build();
|
||||
List<String> servicesResult = servicesClient.method(HttpMethod.GET).retrieve()
|
||||
@@ -284,8 +279,6 @@ class KubernetesClientDiscoveryClientIT {
|
||||
util.setUp(NAMESPACE);
|
||||
String imageName = "docker.io/springcloud/spring-cloud-kubernetes-client-discovery-it:" + Commons.pomVersion();
|
||||
KubernetesClientDiscoveryClientUtils.patchForPodMetadata(imageName, DEPLOYMENT_NAME, NAMESPACE);
|
||||
util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE,
|
||||
Map.of("app", "spring-cloud-kubernetes-client-discovery-it"));
|
||||
new KubernetesClientDiscoveryPodMetadataITDelegate().testSimple();
|
||||
}
|
||||
|
||||
@@ -294,8 +287,6 @@ class KubernetesClientDiscoveryClientIT {
|
||||
void filterMatchesOneNamespaceViaThePredicate() {
|
||||
String imageName = "docker.io/springcloud/spring-cloud-kubernetes-client-discovery-it:" + Commons.pomVersion();
|
||||
KubernetesClientDiscoveryClientUtils.patchForUATNamespacesTests(imageName, DEPLOYMENT_NAME, NAMESPACE);
|
||||
util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE,
|
||||
Map.of("app", "spring-cloud-kubernetes-client-discovery-it"));
|
||||
new KubernetesClientDiscoveryFilterITDelegate().filterMatchesOneNamespaceViaThePredicate(util);
|
||||
|
||||
}
|
||||
@@ -316,8 +307,6 @@ class KubernetesClientDiscoveryClientIT {
|
||||
|
||||
// patch the deployment to change what namespaces are take into account
|
||||
KubernetesClientDiscoveryClientUtils.patchForTwoNamespacesMatchViaThePredicate(DEPLOYMENT_NAME, NAMESPACE);
|
||||
util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE,
|
||||
Map.of("app", "spring-cloud-kubernetes-client-discovery-it"));
|
||||
|
||||
new KubernetesClientDiscoveryFilterITDelegate().filterMatchesBothNamespacesViaThePredicate(util);
|
||||
}
|
||||
@@ -331,8 +320,6 @@ class KubernetesClientDiscoveryClientIT {
|
||||
|
||||
String imageName = "docker.io/springcloud/spring-cloud-kubernetes-client-discovery-it:" + Commons.pomVersion();
|
||||
KubernetesClientDiscoveryClientUtils.patchForBlockingHealth(imageName, DEPLOYMENT_NAME, NAMESPACE);
|
||||
util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE,
|
||||
Map.of("app", "spring-cloud-kubernetes-client-discovery-it"));
|
||||
|
||||
new KubernetesClientDiscoveryHealthITDelegate().testBlockingConfiguration(K3S);
|
||||
}
|
||||
@@ -342,8 +329,6 @@ class KubernetesClientDiscoveryClientIT {
|
||||
void testReactiveConfiguration() {
|
||||
|
||||
KubernetesClientDiscoveryClientUtils.patchForReactiveHealth(DEPLOYMENT_NAME, NAMESPACE);
|
||||
util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE,
|
||||
Map.of("app", "spring-cloud-kubernetes-client-discovery-it"));
|
||||
|
||||
new KubernetesClientDiscoveryHealthITDelegate().testReactiveConfiguration(util, K3S);
|
||||
}
|
||||
@@ -353,8 +338,6 @@ class KubernetesClientDiscoveryClientIT {
|
||||
void testDefaultConfiguration() {
|
||||
|
||||
KubernetesClientDiscoveryClientUtils.patchForBlockingAndReactiveHealth(DEPLOYMENT_NAME, NAMESPACE);
|
||||
util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE,
|
||||
Map.of("app", "spring-cloud-kubernetes-client-discovery-it"));
|
||||
|
||||
new KubernetesClientDiscoveryHealthITDelegate().testDefaultConfiguration(util, K3S);
|
||||
}
|
||||
@@ -404,21 +387,6 @@ class KubernetesClientDiscoveryClientIT {
|
||||
return Retry.fixedDelay(15, Duration.ofSeconds(1)).filter(Objects::nonNull);
|
||||
}
|
||||
|
||||
private String logs() {
|
||||
try {
|
||||
String appPodName = K3S.execInContainer("sh", "-c",
|
||||
"kubectl get pods -l app=" + IMAGE_NAME + " -o=name --no-headers | tr -d '\n'").getStdout();
|
||||
|
||||
Container.ExecResult execResult = K3S.execInContainer("sh", "-c", "kubectl logs " + appPodName.trim());
|
||||
return execResult.getStdout();
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void assertServicePresentInNamespaces(List<String> namespaces, String value, String serviceName) {
|
||||
namespaces.forEach(x -> {
|
||||
try {
|
||||
|
||||
@@ -16,16 +16,13 @@
|
||||
|
||||
package org.springframework.cloud.kubernetes.client.discovery.it;
|
||||
|
||||
import io.kubernetes.client.custom.V1Patch;
|
||||
import io.kubernetes.client.openapi.ApiException;
|
||||
import io.kubernetes.client.openapi.apis.AppsV1Api;
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import io.kubernetes.client.openapi.models.V1Deployment;
|
||||
import io.kubernetes.client.util.PatchUtils;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.log.LogAccessor;
|
||||
|
||||
import static org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util.patchWithMerge;
|
||||
import static org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util.patchWithReplace;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
@@ -411,32 +408,4 @@ final class KubernetesClientDiscoveryClientUtils {
|
||||
patchWithReplace(image, deploymentName, namespace, BODY_TWELVE);
|
||||
}
|
||||
|
||||
private static void patchWithMerge(String deploymentName, String namespace, String patchBody) {
|
||||
try {
|
||||
PatchUtils.patch(V1Deployment.class,
|
||||
() -> new AppsV1Api().patchNamespacedDeploymentCall(deploymentName, namespace,
|
||||
new V1Patch(patchBody), null, null, null, null, null, null),
|
||||
V1Patch.PATCH_FORMAT_STRATEGIC_MERGE_PATCH, new CoreV1Api().getApiClient());
|
||||
}
|
||||
catch (ApiException e) {
|
||||
LOG.error(() -> "error : " + e.getResponseBody());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void patchWithReplace(String imageName, String deploymentName, String namespace, String patchBody) {
|
||||
String body = patchBody.replace("image_name_here", imageName);
|
||||
|
||||
try {
|
||||
PatchUtils.patch(V1Deployment.class,
|
||||
() -> new AppsV1Api().patchNamespacedDeploymentCall(deploymentName, namespace, new V1Patch(body),
|
||||
null, null, null, null, null, null),
|
||||
V1Patch.PATCH_FORMAT_JSON_MERGE_PATCH, new CoreV1Api().getApiClient());
|
||||
}
|
||||
catch (ApiException e) {
|
||||
LOG.error(() -> "error : " + e.getResponseBody());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -99,8 +99,6 @@ class KubernetesClientDiscoveryFilterITDelegate {
|
||||
|
||||
// patch the deployment to change what namespaces are take into account
|
||||
KubernetesClientDiscoveryClientUtils.patchForTwoNamespacesMatchViaThePredicate(DEPLOYMENT_NAME, NAMESPACE);
|
||||
util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE,
|
||||
Map.of("app", "spring-cloud-kubernetes-client-discovery-it"));
|
||||
|
||||
WebClient clientServices = builder().baseUrl("http://localhost/services").build();
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.cloud.kubernetes.client.discovery.it;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -103,8 +102,6 @@ class KubernetesClientDiscoveryHealthITDelegate {
|
||||
void testReactiveConfiguration(Util util, K3sContainer container) {
|
||||
|
||||
KubernetesClientDiscoveryClientUtils.patchForReactiveHealth(DEPLOYMENT_NAME, NAMESPACE);
|
||||
util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE,
|
||||
Map.of("app", "spring-cloud-kubernetes-client-discovery-it"));
|
||||
|
||||
assertLogStatement(container, "Will publish InstanceRegisteredEvent from reactive implementation");
|
||||
assertLogStatement(container, "publishing InstanceRegisteredEvent");
|
||||
@@ -148,8 +145,6 @@ class KubernetesClientDiscoveryHealthITDelegate {
|
||||
void testDefaultConfiguration(Util util, K3sContainer container) {
|
||||
|
||||
KubernetesClientDiscoveryClientUtils.patchForBlockingAndReactiveHealth(DEPLOYMENT_NAME, NAMESPACE);
|
||||
util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE,
|
||||
Map.of("app", "spring-cloud-kubernetes-client-discovery-it"));
|
||||
|
||||
assertLogStatement(container, "Will publish InstanceRegisteredEvent from blocking implementation");
|
||||
assertLogStatement(container, "publishing InstanceRegisteredEvent");
|
||||
|
||||
@@ -29,6 +29,7 @@ import reactor.util.retry.Retry;
|
||||
import reactor.util.retry.RetryBackoffSpec;
|
||||
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
|
||||
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
@@ -52,20 +53,21 @@ class KubernetesClientDiscoveryMultipleSelectiveNamespacesITDelegate {
|
||||
*/
|
||||
void testTwoNamespacesBlockingOnly(K3sContainer container) {
|
||||
|
||||
String logs = logs(container);
|
||||
Assertions.assertTrue(logs.contains("using selective namespaces : [a, b]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a, b]"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : b"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : b"));
|
||||
Commons.waitForLogStatement("using selective namespaces : [a, b]", container, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]",
|
||||
container, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]",
|
||||
container, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a, b]",
|
||||
container, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for services) in namespace : a", container, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for services) in namespace : b", container, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for endpoints) in namespace : a", container, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for endpoints) in namespace : b", container, IMAGE_NAME);
|
||||
|
||||
// this tiny checks makes sure that blocking is enabled and reactive is disabled.
|
||||
Assertions.assertTrue(logs.contains(BLOCKING_PUBLISH));
|
||||
Assertions.assertFalse(logs.contains(REACTIVE_PUBLISH));
|
||||
Commons.waitForLogStatement(BLOCKING_PUBLISH, container, IMAGE_NAME);
|
||||
Assertions.assertFalse(logs(container).contains(REACTIVE_PUBLISH));
|
||||
|
||||
blockingCheck();
|
||||
|
||||
@@ -78,20 +80,19 @@ class KubernetesClientDiscoveryMultipleSelectiveNamespacesITDelegate {
|
||||
*/
|
||||
void testTwoNamespaceReactiveOnly(K3sContainer container) {
|
||||
|
||||
String logs = logs(container);
|
||||
Assertions.assertTrue(logs.contains("using selective namespaces : [a, b]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a, b]"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : b"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : b"));
|
||||
Commons.waitForLogStatement("using selective namespaces : [a, b]", container, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]",
|
||||
container, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a, b]",
|
||||
container, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for services) in namespace : a", container, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for services) in namespace : b", container, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for endpoints) in namespace : a", container, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for endpoints) in namespace : b", container, IMAGE_NAME);
|
||||
|
||||
// this tiny checks makes sure that blocking is disabled and reactive is enabled.
|
||||
Assertions.assertFalse(logs.contains(BLOCKING_PUBLISH));
|
||||
Assertions.assertTrue(logs.contains(REACTIVE_PUBLISH));
|
||||
Commons.waitForLogStatement(REACTIVE_PUBLISH, container, IMAGE_NAME);
|
||||
Assertions.assertFalse(logs(container).contains(BLOCKING_PUBLISH));
|
||||
|
||||
reactiveCheck();
|
||||
|
||||
@@ -104,20 +105,19 @@ class KubernetesClientDiscoveryMultipleSelectiveNamespacesITDelegate {
|
||||
*/
|
||||
void testTwoNamespacesBothBlockingAndReactive(K3sContainer container) {
|
||||
|
||||
String logs = logs(container);
|
||||
Assertions.assertTrue(logs.contains("using selective namespaces : [a, b]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a, b]"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : b"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : b"));
|
||||
Commons.waitForLogStatement("using selective namespaces : [a, b]", container, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a, b]",
|
||||
container, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a, b]",
|
||||
container, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for services) in namespace : a", container, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for services) in namespace : b", container, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for endpoints) in namespace : a", container, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for endpoints) in namespace : b", container, IMAGE_NAME);
|
||||
|
||||
// this tiny checks makes sure that blocking is enabled and reactive is enabled.
|
||||
Assertions.assertTrue(logs.contains(BLOCKING_PUBLISH));
|
||||
Assertions.assertTrue(logs.contains(REACTIVE_PUBLISH));
|
||||
Commons.waitForLogStatement(BLOCKING_PUBLISH, container, IMAGE_NAME);
|
||||
Assertions.assertTrue(logs(container).contains(REACTIVE_PUBLISH));
|
||||
|
||||
blockingCheck();
|
||||
reactiveCheck();
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.springframework.cloud.kubernetes.client.discovery.it;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -114,18 +113,17 @@ class KubernetesClientDiscoverySelectiveNamespacesIT {
|
||||
@Order(1)
|
||||
void testOneNamespaceBlockingOnly() {
|
||||
|
||||
String logs = logs();
|
||||
Assertions.assertTrue(logs.contains("using selective namespaces : [a]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a]"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a"));
|
||||
Commons.waitForLogStatement("using selective namespaces : [a]", K3S, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a]", K3S,
|
||||
IMAGE_NAME);
|
||||
Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a]", K3S,
|
||||
IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for services) in namespace : a", K3S, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for endpoints) in namespace : a", K3S, IMAGE_NAME);
|
||||
|
||||
// this tiny checks makes sure that blocking is enabled and reactive is disabled.
|
||||
Assertions.assertTrue(logs.contains(BLOCKING_PUBLISH));
|
||||
Assertions.assertFalse(logs.contains(REACTIVE_PUBLISH));
|
||||
Commons.waitForLogStatement(BLOCKING_PUBLISH, K3S, IMAGE_NAME);
|
||||
Assertions.assertFalse(logs().contains(REACTIVE_PUBLISH));
|
||||
|
||||
blockingCheck();
|
||||
|
||||
@@ -141,21 +139,16 @@ class KubernetesClientDiscoverySelectiveNamespacesIT {
|
||||
void testOneNamespaceReactiveOnly() {
|
||||
|
||||
KubernetesClientDiscoveryClientUtils.patchForReactiveOnly(DEPLOYMENT_NAME, NAMESPACE);
|
||||
util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE,
|
||||
Map.of("app", "spring-cloud-kubernetes-client-discovery-it"));
|
||||
|
||||
String logs = logs();
|
||||
Assertions.assertTrue(logs.contains("using selective namespaces : [a]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a]"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a"));
|
||||
Commons.waitForLogStatement("using selective namespaces : [a]", K3S, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a]", K3S,
|
||||
IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for services) in namespace : a", K3S, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for endpoints) in namespace : a", K3S, IMAGE_NAME);
|
||||
|
||||
// this tiny checks makes sure that reactive is enabled and blocking is disabled.
|
||||
Assertions.assertFalse(logs.contains(BLOCKING_PUBLISH));
|
||||
Assertions.assertTrue(logs.contains(REACTIVE_PUBLISH));
|
||||
Commons.waitForLogStatement(REACTIVE_PUBLISH, K3S, IMAGE_NAME);
|
||||
Assertions.assertFalse(logs().contains(BLOCKING_PUBLISH));
|
||||
|
||||
reactiveCheck();
|
||||
|
||||
@@ -171,21 +164,18 @@ class KubernetesClientDiscoverySelectiveNamespacesIT {
|
||||
void testOneNamespaceBothBlockingAndReactive() {
|
||||
|
||||
KubernetesClientDiscoveryClientUtils.patchForBlockingAndReactive(DEPLOYMENT_NAME, NAMESPACE);
|
||||
util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE,
|
||||
Map.of("app", "spring-cloud-kubernetes-client-discovery-it"));
|
||||
|
||||
String logs = logs();
|
||||
Assertions.assertTrue(logs.contains("using selective namespaces : [a]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a]"));
|
||||
Assertions.assertTrue(
|
||||
logs.contains("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a]"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for services) in namespace : a"));
|
||||
Assertions.assertTrue(logs.contains("registering lister (for endpoints) in namespace : a"));
|
||||
Commons.waitForLogStatement("using selective namespaces : [a]", K3S, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesMissing : found selective namespaces : [a]", K3S,
|
||||
IMAGE_NAME);
|
||||
Commons.waitForLogStatement("ConditionalOnSelectiveNamespacesPresent : found selective namespaces : [a]", K3S,
|
||||
IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for services) in namespace : a", K3S, IMAGE_NAME);
|
||||
Commons.waitForLogStatement("registering lister (for endpoints) in namespace : a", K3S, IMAGE_NAME);
|
||||
|
||||
// this tiny checks makes sure that blocking and reactive is enabled.
|
||||
Assertions.assertTrue(logs.contains(BLOCKING_PUBLISH));
|
||||
Assertions.assertTrue(logs.contains(REACTIVE_PUBLISH));
|
||||
Commons.waitForLogStatement(BLOCKING_PUBLISH, K3S, IMAGE_NAME);
|
||||
Commons.waitForLogStatement(REACTIVE_PUBLISH, K3S, IMAGE_NAME);
|
||||
|
||||
blockingCheck();
|
||||
reactiveCheck();
|
||||
@@ -209,8 +199,6 @@ class KubernetesClientDiscoverySelectiveNamespacesIT {
|
||||
@Order(4)
|
||||
void testTwoNamespacesBlockingOnly() {
|
||||
KubernetesClientDiscoveryClientUtils.patchForTwoNamespacesBlockingOnly(DEPLOYMENT_NAME, NAMESPACE);
|
||||
util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE,
|
||||
Map.of("app", "spring-cloud-kubernetes-client-discovery-it"));
|
||||
new KubernetesClientDiscoveryMultipleSelectiveNamespacesITDelegate().testTwoNamespacesBlockingOnly(K3S);
|
||||
}
|
||||
|
||||
@@ -230,8 +218,6 @@ class KubernetesClientDiscoverySelectiveNamespacesIT {
|
||||
@Order(5)
|
||||
void testTwoNamespacesReactiveOnly() {
|
||||
KubernetesClientDiscoveryClientUtils.patchForReactiveOnly(DEPLOYMENT_NAME, NAMESPACE);
|
||||
util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE,
|
||||
Map.of("app", "spring-cloud-kubernetes-client-discovery-it"));
|
||||
new KubernetesClientDiscoveryMultipleSelectiveNamespacesITDelegate().testTwoNamespaceReactiveOnly(K3S);
|
||||
}
|
||||
|
||||
@@ -251,8 +237,6 @@ class KubernetesClientDiscoverySelectiveNamespacesIT {
|
||||
@Order(6)
|
||||
void testTwoNamespacesBothBlockingAndReactive() {
|
||||
KubernetesClientDiscoveryClientUtils.patchToAddBlockingSupport(DEPLOYMENT_NAME, NAMESPACE);
|
||||
util.waitForDeploymentAfterPatch(DEPLOYMENT_NAME, NAMESPACE,
|
||||
Map.of("app", "spring-cloud-kubernetes-client-discovery-it"));
|
||||
new KubernetesClientDiscoveryMultipleSelectiveNamespacesITDelegate()
|
||||
.testTwoNamespacesBothBlockingAndReactive(K3S);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.builder;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.retrySpec;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientUtil.waitForLogStatement;
|
||||
import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.waitForLogStatement;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.springframework.cloud.kubernetes.fabric8.discovery;
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.testcontainers.k3s.K3sContainer;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
import reactor.util.retry.Retry;
|
||||
import reactor.util.retry.RetryBackoffSpec;
|
||||
@@ -27,8 +26,6 @@ import reactor.util.retry.RetryBackoffSpec;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static org.awaitility.Awaitility.await;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
@@ -269,21 +266,4 @@ final class Fabric8DiscoveryClientUtil {
|
||||
return Retry.fixedDelay(15, Duration.ofSeconds(1)).filter(Objects::nonNull);
|
||||
}
|
||||
|
||||
static void waitForLogStatement(String message, K3sContainer k3sContainer, String imageName) {
|
||||
try {
|
||||
String appPodName = k3sContainer.execInContainer("sh", "-c",
|
||||
"kubectl get pods -l app=" + imageName + " -o=name --no-headers | tr -d '\n'").getStdout();
|
||||
|
||||
await().atMost(Duration.ofMinutes(2)).pollInterval(Duration.ofSeconds(2)).until(() -> {
|
||||
String execResult = k3sContainer.execInContainer("sh", "-c", "kubectl logs " + appPodName.trim())
|
||||
.getStdout();
|
||||
return execResult.contains(message);
|
||||
});
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -229,6 +229,31 @@ public final class Commons {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* the assumption is that there is only a single pod that is 'Running'.
|
||||
*/
|
||||
public static void waitForLogStatement(String message, K3sContainer k3sContainer, String imageName) {
|
||||
try {
|
||||
|
||||
await().atMost(Duration.ofMinutes(2)).pollInterval(Duration.ofSeconds(4)).until(() -> {
|
||||
|
||||
String appPodName = k3sContainer.execInContainer("sh", "-c",
|
||||
"kubectl get pods -l app=" + imageName
|
||||
+ " -o custom-columns=POD:metadata.name,STATUS:status.phase"
|
||||
+ " | grep -i 'running' | awk '{print $1}' | tr -d '\n' ")
|
||||
.getStdout();
|
||||
|
||||
String execResult = k3sContainer.execInContainer("sh", "-c", "kubectl logs " + appPodName.trim())
|
||||
.getStdout();
|
||||
return execResult.contains(message);
|
||||
});
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A K3sContainer, but with fixed port mappings. This is needed because of the nature
|
||||
* of some integration tests.
|
||||
|
||||
@@ -29,6 +29,7 @@ import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.kubernetes.client.custom.V1Patch;
|
||||
import io.kubernetes.client.openapi.ApiClient;
|
||||
import io.kubernetes.client.openapi.ApiException;
|
||||
import io.kubernetes.client.openapi.Configuration;
|
||||
@@ -51,6 +52,7 @@ import io.kubernetes.client.openapi.models.V1Secret;
|
||||
import io.kubernetes.client.openapi.models.V1Service;
|
||||
import io.kubernetes.client.openapi.models.V1ServiceAccount;
|
||||
import io.kubernetes.client.util.Config;
|
||||
import io.kubernetes.client.util.PatchUtils;
|
||||
import io.kubernetes.client.util.Yaml;
|
||||
import jakarta.annotation.Nullable;
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -436,6 +438,39 @@ public final class Util {
|
||||
|
||||
}
|
||||
|
||||
public static void patchWithMerge(String deploymentName, String namespace, String patchBody) {
|
||||
try {
|
||||
PatchUtils.patch(V1Deployment.class,
|
||||
() -> new AppsV1Api().patchNamespacedDeploymentCall(deploymentName, namespace,
|
||||
new V1Patch(patchBody), null, null, null, null, null, null),
|
||||
V1Patch.PATCH_FORMAT_STRATEGIC_MERGE_PATCH, new CoreV1Api().getApiClient());
|
||||
}
|
||||
catch (ApiException e) {
|
||||
LOG.error("error : " + e.getResponseBody());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
waitForDeploymentAfterPatch(deploymentName, namespace);
|
||||
}
|
||||
|
||||
public static void patchWithReplace(String imageName, String deploymentName, String namespace, String patchBody) {
|
||||
String body = patchBody.replace("image_name_here", imageName);
|
||||
|
||||
try {
|
||||
PatchUtils.patch(V1Deployment.class,
|
||||
() -> new AppsV1Api().patchNamespacedDeploymentCall(deploymentName, namespace, new V1Patch(body),
|
||||
null, null, null, null, null, null),
|
||||
V1Patch.PATCH_FORMAT_JSON_MERGE_PATCH, new CoreV1Api().getApiClient());
|
||||
}
|
||||
catch (ApiException e) {
|
||||
LOG.error("error : " + e.getResponseBody());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
waitForDeploymentAfterPatch(deploymentName, namespace);
|
||||
|
||||
}
|
||||
|
||||
private String deploymentName(V1Deployment deployment) {
|
||||
return deployment.getMetadata().getName();
|
||||
}
|
||||
@@ -590,10 +625,10 @@ public final class Util {
|
||||
return availableReplicas != null && availableReplicas >= 1;
|
||||
}
|
||||
|
||||
public void waitForDeploymentAfterPatch(String deploymentName, String namespace, Map<String, String> labels) {
|
||||
private static void waitForDeploymentAfterPatch(String deploymentName, String namespace) {
|
||||
try {
|
||||
await().pollDelay(Duration.ofSeconds(4)).pollInterval(Duration.ofSeconds(3)).atMost(60, TimeUnit.SECONDS)
|
||||
.until(() -> isDeploymentReadyAfterPatch(deploymentName, namespace, labels));
|
||||
.until(() -> isDeploymentReadyAfterPatch(deploymentName, namespace));
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (e instanceof ApiException apiException) {
|
||||
@@ -605,10 +640,9 @@ public final class Util {
|
||||
|
||||
}
|
||||
|
||||
private boolean isDeploymentReadyAfterPatch(String deploymentName, String namespace, Map<String, String> labels)
|
||||
throws ApiException {
|
||||
private static boolean isDeploymentReadyAfterPatch(String deploymentName, String namespace) throws ApiException {
|
||||
|
||||
V1DeploymentList deployments = appsV1Api.listNamespacedDeployment(namespace, null, null, null,
|
||||
V1DeploymentList deployments = new AppsV1Api().listNamespacedDeployment(namespace, null, null, null,
|
||||
"metadata.name=" + deploymentName, null, null, null, null, null, null);
|
||||
if (deployments.getItems().isEmpty()) {
|
||||
fail("No deployment with name " + deploymentName);
|
||||
@@ -617,17 +651,14 @@ public final class Util {
|
||||
V1Deployment deployment = deployments.getItems().get(0);
|
||||
// if no replicas are defined, it means only 1 is needed
|
||||
int replicas = Optional.ofNullable(deployment.getSpec().getReplicas()).orElse(1);
|
||||
int readyReplicas = Optional.ofNullable(deployment.getStatus().getReadyReplicas()).orElse(0);
|
||||
|
||||
int numberOfPods = coreV1Api.listNamespacedPod(namespace, null, null, null, null, labelSelector(labels), null,
|
||||
null, null, null, null).getItems().size();
|
||||
|
||||
if (numberOfPods != replicas) {
|
||||
if (readyReplicas != replicas) {
|
||||
LOG.info("number of pods not yet stabilized");
|
||||
return false;
|
||||
}
|
||||
|
||||
return replicas == Optional.ofNullable(deployment.getStatus().getAvailableReplicas()).orElse(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static <T> void notExistsHandler(CheckedSupplier<T> callee, CheckedSupplier<T> defaulter) throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user