Simplify integration tests (#1181)

This commit is contained in:
erabii
2023-01-04 22:13:04 +02:00
committed by GitHub
parent 20e87ce161
commit 66801c93d3
90 changed files with 2045 additions and 4419 deletions

View File

@@ -6,6 +6,11 @@ runs:
- name: run fabric8 istio integration test
shell: bash
run: |
cd spring-cloud-kubernetes-test-support
.././mvnw clean install
cd ..
docker load -i /tmp/docker/images/spring-cloud-kubernetes-fabric8-istio-it.tar
cd spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-istio-it/
../.././mvnw clean install -Dskip.build.image=true

View File

@@ -20,13 +20,9 @@ import java.time.Duration;
import java.util.List;
import java.util.Objects;
import io.kubernetes.client.openapi.apis.AppsV1Api;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.apis.NetworkingV1Api;
import io.kubernetes.client.openapi.models.V1Deployment;
import io.kubernetes.client.openapi.models.V1Ingress;
import io.kubernetes.client.openapi.models.V1Service;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
@@ -38,7 +34,8 @@ import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.commons.discovery.EndpointNameAndNamespace;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpMethod;
@@ -46,7 +43,6 @@ 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.K8SUtils.createApiClient;
/**
* @author wind57
@@ -59,47 +55,20 @@ class KubernetesClientCatalogWatchIT {
private static final K3sContainer K3S = Commons.container();
private static CoreV1Api api;
private static AppsV1Api appsApi;
private static NetworkingV1Api networkingApi;
private static K8SUtils k8SUtils;
private static String busyboxServiceName;
private static String busyboxDeploymentName;
private static String appDeploymentName;
private static String appServiceName;
private static String appIngressName;
private static Util util;
@BeforeAll
static void beforeAll() throws Exception {
K3S.start();
Commons.validateImage(APP_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(APP_NAME, K3S);
createApiClient(K3S.getKubeConfigYaml());
api = new CoreV1Api();
appsApi = new AppsV1Api();
networkingApi = new NetworkingV1Api();
k8SUtils = new K8SUtils(api, appsApi);
k8SUtils.setUp(NAMESPACE);
util = new Util(K3S);
util.setUp(NAMESPACE);
}
@BeforeEach
void beforeEach() throws Exception {
deployBusyboxManifests();
}
@AfterEach
void afterEach() throws Exception {
deleteApp();
void beforeEach() {
util.busybox(NAMESPACE, Phase.CREATE);
}
/**
@@ -112,16 +81,18 @@ class KubernetesClientCatalogWatchIT {
*/
@Test
void testCatalogWatchWithEndpoints() throws Exception {
deployApp(false);
app(false, Phase.CREATE);
assertLogStatement("stateGenerator is of type: KubernetesEndpointsCatalogWatch");
test();
app(false, Phase.DELETE);
}
@Test
void testCatalogWatchWithEndpointSlices() throws Exception {
deployApp(true);
app(true, Phase.CREATE);
assertLogStatement("stateGenerator is of type: KubernetesEndpointSlicesCatalogWatch");
test();
app(true, Phase.DELETE);
}
/**
@@ -140,8 +111,7 @@ class KubernetesClientCatalogWatchIT {
* the test is the same for both endpoints and endpoint slices, the set-up for them is
* different.
*/
@SuppressWarnings("unchecked")
private void test() throws Exception {
private void test() {
WebClient client = builder().baseUrl("localhost/result").build();
EndpointNameAndNamespace[] holder = new EndpointNameAndNamespace[2];
@@ -175,7 +145,7 @@ class KubernetesClientCatalogWatchIT {
Assertions.assertEquals("default", resultOne.namespace());
Assertions.assertEquals("default", resultTwo.namespace());
deleteBusyboxApp();
util.busybox(NAMESPACE, Phase.DELETE);
// what we get after delete
EndpointNameAndNamespace[] afterDelete = new EndpointNameAndNamespace[1];
@@ -208,89 +178,19 @@ class KubernetesClientCatalogWatchIT {
}
private void deployBusyboxManifests() throws Exception {
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");
V1Service service = (V1Service) util.yaml("app/watcher-service.yaml");
V1Ingress ingress = (V1Ingress) util.yaml("app/watcher-ingress.yaml");
V1Deployment busyboxDeployment = (V1Deployment) K8SUtils.readYamlFromClasspath(getBusyboxDeployment());
String[] image = K8SUtils.getImageFromDeployment(busyboxDeployment).split(":");
Commons.pullImage(image[0], image[1], K3S);
Commons.loadImage(image[0], image[1], "busybox", K3S);
appsApi.createNamespacedDeployment(NAMESPACE, busyboxDeployment, null, null, null, null);
busyboxDeploymentName = busyboxDeployment.getMetadata().getName();
V1Service busyboxService = (V1Service) K8SUtils.readYamlFromClasspath(getBusyboxService());
busyboxServiceName = busyboxService.getMetadata().getName();
api.createNamespacedService(NAMESPACE, busyboxService, null, null, null, null);
k8SUtils.waitForDeployment(busyboxDeploymentName, NAMESPACE);
}
private static void deployApp(boolean useEndpointSlices) throws Exception {
V1Deployment appDeployment = useEndpointSlices
? (V1Deployment) K8SUtils.readYamlFromClasspath(getEndpointSlicesAppDeployment())
: (V1Deployment) K8SUtils.readYamlFromClasspath(getEndpointsAppDeployment());
String version = K8SUtils.getPomVersion();
String currentImage = appDeployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
appDeployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(currentImage + ":" + version);
appsApi.createNamespacedDeployment(NAMESPACE, appDeployment, null, null, null, null);
appDeploymentName = appDeployment.getMetadata().getName();
V1Service appService = (V1Service) K8SUtils.readYamlFromClasspath(getAppService());
appServiceName = appService.getMetadata().getName();
api.createNamespacedService(NAMESPACE, appService, null, null, null, null);
k8SUtils.waitForDeployment(appDeploymentName, NAMESPACE);
V1Ingress appIngress = (V1Ingress) K8SUtils.readYamlFromClasspath(getAppIngress());
appIngressName = appIngress.getMetadata().getName();
networkingApi.createNamespacedIngress(NAMESPACE, appIngress, null, null, null, null);
k8SUtils.waitForIngress(appIngressName, NAMESPACE);
}
private void deleteBusyboxApp() throws Exception {
appsApi.deleteNamespacedDeployment(busyboxDeploymentName, NAMESPACE, null, null, null, null, null, null);
api.deleteNamespacedService(busyboxServiceName, NAMESPACE, null, null, null, null, null, null);
k8SUtils.waitForDeploymentToBeDeleted(busyboxDeploymentName, NAMESPACE);
}
private void deleteApp() throws Exception {
appsApi.deleteNamespacedDeployment(appDeploymentName, NAMESPACE, null, null, null, null, null, null);
api.deleteNamespacedService(appServiceName, NAMESPACE, null, null, null, null, null, null);
networkingApi.deleteNamespacedIngress(appIngressName, NAMESPACE, null, null, null, null, null, null);
k8SUtils.waitForDeploymentToBeDeleted(busyboxDeploymentName, NAMESPACE);
}
private static String getBusyboxService() {
return "busybox/service.yaml";
}
private static String getBusyboxDeployment() {
return "busybox/deployment.yaml";
}
/**
* deployment where support for endpoint slices is equal to false
*/
private static String getEndpointsAppDeployment() {
return "app/watcher-endpoints-deployment.yaml";
}
private static String getEndpointSlicesAppDeployment() {
return "app/watcher-endpoint-slices-deployment.yaml";
}
private static String getAppIngress() {
return "app/watcher-ingress.yaml";
}
private static String getAppService() {
return "app/watcher-service.yaml";
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
else if (phase.equals(Phase.DELETE)) {
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
private WebClient.Builder builder() {

View File

@@ -18,18 +18,16 @@ 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.apis.AppsV1Api;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.apis.NetworkingV1Api;
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.V1NamespaceBuilder;
import io.kubernetes.client.openapi.models.V1Service;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
@@ -43,7 +41,8 @@ import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.commons.discovery.EndpointNameAndNamespace;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpMethod;
@@ -51,7 +50,6 @@ 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.K8SUtils.createApiClient;
public class KubernetesClientCatalogWatchNamespacesIT {
@@ -65,58 +63,30 @@ public class KubernetesClientCatalogWatchNamespacesIT {
private static final K3sContainer K3S = Commons.container();
private static CoreV1Api api;
private static AppsV1Api appsApi;
private static NetworkingV1Api networkingApi;
private static K8SUtils k8SUtils;
private static String busyboxServiceNameA;
private static String busyboxServiceNameB;
private static String busyboxDeploymentNameA;
private static String busyboxDeploymentNameB;
private static String appDeploymentName;
private static String appServiceName;
private static String appIngressName;
private static Util util;
@BeforeAll
static void beforeAll() throws Exception {
K3S.start();
Commons.validateImage(APP_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(APP_NAME, K3S);
createApiClient(K3S.getKubeConfigYaml());
api = new CoreV1Api();
appsApi = new AppsV1Api();
networkingApi = new NetworkingV1Api();
k8SUtils = new K8SUtils(api, appsApi);
k8SUtils.setUp(NAMESPACE_DEFAULT);
util = new Util(K3S);
util.setUp(NAMESPACE_DEFAULT);
}
@BeforeEach
void beforeEach() throws Exception {
api.createNamespace(new V1NamespaceBuilder().withNewMetadata().withName(NAMESPACE_A).and().build(), null, null,
null, null);
api.createNamespace(new V1NamespaceBuilder().withNewMetadata().withName(NAMESPACE_B).and().build(), null, null,
null, null);
k8SUtils.setUpClusterWide(NAMESPACE_DEFAULT, Set.of(NAMESPACE_A, NAMESPACE_B));
deployBusyboxManifests();
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() throws Exception {
deleteApp();
k8SUtils.deleteNamespace(NAMESPACE_A);
k8SUtils.deleteNamespace(NAMESPACE_B);
void afterEach() {
util.deleteNamespace(NAMESPACE_A);
util.deleteNamespace(NAMESPACE_B);
}
/**
@@ -131,16 +101,18 @@ public class KubernetesClientCatalogWatchNamespacesIT {
*/
@Test
void testCatalogWatchWithEndpoints() throws Exception {
deployApp(false);
app(false, Phase.CREATE);
assertLogStatement("stateGenerator is of type: KubernetesEndpointsCatalogWatch");
test();
app(false, Phase.DELETE);
}
@Test
void testCatalogWatchWithEndpointSlices() throws Exception {
deployApp(true);
app(true, Phase.CREATE);
assertLogStatement("stateGenerator is of type: KubernetesEndpointSlicesCatalogWatch");
test();
app(true, Phase.DELETE);
}
/**
@@ -149,10 +121,8 @@ public class KubernetesClientCatalogWatchNamespacesIT {
* 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 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));
}
@@ -161,11 +131,10 @@ public class KubernetesClientCatalogWatchNamespacesIT {
* the test is the same for both endpoints and endpoint slices, the set-up for them is
* different.
*/
@SuppressWarnings("unchecked")
private void test() throws Exception {
private void test() {
WebClient client = builder().baseUrl("localhost/result").build();
EndpointNameAndNamespace[] holder = new EndpointNameAndNamespace[2];
EndpointNameAndNamespace[] holder = new EndpointNameAndNamespace[4];
ResolvableType resolvableType = ResolvableType.forClassWithGenerics(List.class, EndpointNameAndNamespace.class);
await().pollInterval(Duration.ofSeconds(1)).atMost(Duration.ofSeconds(240)).until(() -> {
@@ -173,12 +142,13 @@ public class KubernetesClientCatalogWatchNamespacesIT {
.retrieve().bodyToMono(ParameterizedTypeReference.forType(resolvableType.getType()))
.retryWhen(retrySpec()).block();
// 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) {
// 2 from namespace-a, 2 from namespace-b
Assertions.assertEquals(result.size(), 4);
holder[0] = result.get(0);
holder[1] = result.get(1);
holder[2] = result.get(2);
holder[3] = result.get(3);
return true;
}
@@ -187,159 +157,61 @@ public class KubernetesClientCatalogWatchNamespacesIT {
EndpointNameAndNamespace resultOne = holder[0];
EndpointNameAndNamespace resultTwo = holder[1];
Assertions.assertNotNull(resultOne);
Assertions.assertNotNull(resultTwo);
EndpointNameAndNamespace resultThree = holder[2];
EndpointNameAndNamespace resultFour = holder[3];
Assertions.assertTrue(resultOne.endpointName().contains("busybox"));
Assertions.assertTrue(resultTwo.endpointName().contains("busybox"));
Assertions.assertEquals(NAMESPACE_A, resultOne.namespace());
Assertions.assertEquals(NAMESPACE_A, resultTwo.namespace());
Assertions.assertTrue(resultThree.endpointName().contains("busybox"));
Assertions.assertTrue(resultFour.endpointName().contains("busybox"));
deleteBusyboxApp();
List<EndpointNameAndNamespace> sorted = Arrays.stream(holder)
.sorted(Comparator.comparing(EndpointNameAndNamespace::namespace)).toList();
// what we get after delete
EndpointNameAndNamespace[] afterDelete = new EndpointNameAndNamespace[1];
Assertions.assertEquals(NAMESPACE_A, sorted.get(0).namespace());
Assertions.assertEquals(NAMESPACE_A, sorted.get(1).namespace());
Assertions.assertEquals(NAMESPACE_B, sorted.get(2).namespace());
Assertions.assertEquals(NAMESPACE_B, sorted.get(3).namespace());
util.busybox(NAMESPACE_A, Phase.DELETE);
util.busybox(NAMESPACE_B, Phase.DELETE);
await().pollInterval(Duration.ofSeconds(1)).atMost(Duration.ofSeconds(240)).until(() -> {
List<EndpointNameAndNamespace> result = (List<EndpointNameAndNamespace>) client.method(HttpMethod.GET)
.retrieve().bodyToMono(ParameterizedTypeReference.forType(resolvableType.getType()))
.retryWhen(retrySpec()).block();
// we need to get the event from KubernetesCatalogWatch, but that happens
// on periodic bases. So in order to be sure we got the event we care about
// we wait until the result has a single entry, which means busybox was
// deleted
// + KubernetesCatalogWatch received the new update.
if (result != null && result.size() != 1) {
return false;
}
// we will only receive one pod here, our own
if (result != null) {
afterDelete[0] = result.get(0);
return true;
}
return false;
// there is no update to receive anymore, as there is nothing in namespacea
// and namespaceb
return result.size() == 0;
});
Assertions.assertTrue(afterDelete[0].endpointName().contains(APP_NAME));
Assertions.assertEquals("default", afterDelete[0].namespace());
}
private void deployBusyboxManifests() throws Exception {
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");
V1Deployment busyboxDeployment = (V1Deployment) K8SUtils.readYamlFromClasspath(getBusyboxDeployment());
if (phase.equals(Phase.CREATE)) {
V1EnvVar one = new V1EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0")
.withValue(NAMESPACE_A).build();
String[] image = K8SUtils.getImageFromDeployment(busyboxDeployment).split(":");
Commons.pullImage(image[0], image[1], K3S);
Commons.loadImage(image[0], image[1], "busybox", K3S);
V1EnvVar two = new V1EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1")
.withValue(NAMESPACE_B).build();
// namespace_a
appsApi.createNamespacedDeployment(NAMESPACE_A, busyboxDeployment, null, null, null, null);
busyboxDeploymentNameA = busyboxDeployment.getMetadata().getName();
V1Service busyboxServiceA = (V1Service) K8SUtils.readYamlFromClasspath(getBusyboxService());
busyboxServiceNameA = busyboxServiceA.getMetadata().getName();
api.createNamespacedService(NAMESPACE_A, busyboxServiceA, null, null, null, null);
k8SUtils.waitForDeployment(busyboxDeploymentNameA, NAMESPACE_A);
// namespace_b
appsApi.createNamespacedDeployment(NAMESPACE_B, busyboxDeployment, null, null, null, null);
busyboxDeploymentNameB = busyboxDeployment.getMetadata().getName();
V1Service busyboxServiceB = (V1Service) K8SUtils.readYamlFromClasspath(getBusyboxService());
busyboxServiceNameB = busyboxServiceB.getMetadata().getName();
api.createNamespacedService(NAMESPACE_B, busyboxServiceB, null, null, null, null);
k8SUtils.waitForDeployment(busyboxDeploymentNameA, NAMESPACE_A);
}
private static void deployApp(boolean useEndpointSlices) throws Exception {
V1Deployment appDeployment = useEndpointSlices
? (V1Deployment) K8SUtils.readYamlFromClasspath(getEndpointSlicesAppDeployment())
: (V1Deployment) K8SUtils.readYamlFromClasspath(getEndpointsAppDeployment());
String version = K8SUtils.getPomVersion();
String currentImage = appDeployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
appDeployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(currentImage + ":" + version);
List<V1EnvVar> envVars = new ArrayList<>(
appDeployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv());
V1EnvVar namespaceAEnvVar = new V1EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0")
.withValue(NAMESPACE_A).build();
V1EnvVar namespaceDefaultEnvVar = new V1EnvVarBuilder()
.withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1").withValue(NAMESPACE_DEFAULT).build();
envVars.add(namespaceAEnvVar);
envVars.add(namespaceDefaultEnvVar);
appDeployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars);
appsApi.createNamespacedDeployment(NAMESPACE_DEFAULT, appDeployment, null, null, null, null);
appDeploymentName = appDeployment.getMetadata().getName();
V1Service appService = (V1Service) K8SUtils.readYamlFromClasspath(getAppService());
appServiceName = appService.getMetadata().getName();
api.createNamespacedService(NAMESPACE_DEFAULT, appService, null, null, null, null);
k8SUtils.waitForDeployment(appDeploymentName, NAMESPACE_DEFAULT);
V1Ingress appIngress = (V1Ingress) K8SUtils.readYamlFromClasspath(getAppIngress());
appIngressName = appIngress.getMetadata().getName();
networkingApi.createNamespacedIngress(NAMESPACE_DEFAULT, appIngress, null, null, null, null);
k8SUtils.waitForIngress(appIngressName, NAMESPACE_DEFAULT);
}
private void deleteBusyboxApp() throws Exception {
// namespacea
appsApi.deleteNamespacedDeployment(busyboxDeploymentNameA, NAMESPACE_A, null, null, null, null, null, null);
api.deleteNamespacedService(busyboxServiceNameA, NAMESPACE_A, null, null, null, null, null, null);
k8SUtils.waitForDeploymentToBeDeleted(busyboxDeploymentNameA, NAMESPACE_A);
// namespaceb
appsApi.deleteNamespacedDeployment(busyboxDeploymentNameB, NAMESPACE_B, null, null, null, null, null, null);
api.deleteNamespacedService(busyboxServiceNameB, NAMESPACE_B, null, null, null, null, null, null);
k8SUtils.waitForDeploymentToBeDeleted(busyboxDeploymentNameB, NAMESPACE_B);
}
private void deleteApp() throws Exception {
appsApi.deleteNamespacedDeployment(appDeploymentName, NAMESPACE_DEFAULT, null, null, null, null, null, null);
api.deleteNamespacedService(appServiceName, NAMESPACE_DEFAULT, null, null, null, null, null, null);
networkingApi.deleteNamespacedIngress(appIngressName, NAMESPACE_DEFAULT, null, null, null, null, null, null);
}
private static String getBusyboxService() {
return "busybox/service.yaml";
}
private static String getBusyboxDeployment() {
return "busybox/deployment.yaml";
}
/**
* deployment where support for endpoint slices is equal to false
*/
private static String getEndpointsAppDeployment() {
return "app/watcher-endpoints-deployment.yaml";
}
private static String getEndpointSlicesAppDeployment() {
return "app/watcher-endpoint-slices-deployment.yaml";
}
private static String getAppIngress() {
return "app/watcher-ingress.yaml";
}
private static String getAppService() {
return "app/watcher-service.yaml";
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() {

View File

@@ -1,9 +0,0 @@
apiVersion: v1
data:
application.yaml: |-
my:
config:
myProperty: from-config-map
kind: ConfigMap
metadata:
name: spring-cloud-kubernetes-client-config-it

View File

@@ -1,29 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: spring-cloud-kubernetes-client-config-it
name: spring-cloud-kubernetes-client-config-it-deployment
spec:
replicas: 1
selector:
matchLabels:
app: spring-cloud-kubernetes-client-config-it
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: spring-cloud-kubernetes-client-config-it
spec:
serviceAccountName: spring-cloud-kubernetes-serviceaccount
containers:
- image: springcloud/spring-cloud-kubernetes-client-config-it:3.0.0-SNAPSHOT
imagePullPolicy: IfNotPresent
name: spring-cloud-kubernetes-client-config-it
resources: {}
env:
- name: SPRING_PROFILES_ACTIVE
value: kubernetes
status: {}

View File

@@ -1,31 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: spring-cloud-kubernetes-client-config-it
name: spring-cloud-kubernetes-client-config-it-deployment
spec:
replicas: 1
selector:
matchLabels:
app: spring-cloud-kubernetes-client-config-it
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: spring-cloud-kubernetes-client-config-it
spec:
serviceAccountName: spring-cloud-kubernetes-serviceaccount
containers:
- image: springcloud/spring-cloud-kubernetes-client-config-it:3.0.0-SNAPSHOT
imagePullPolicy: IfNotPresent
name: spring-cloud-kubernetes-client-config-it
resources: {}
env:
- name: SPRING_PROFILES_ACTIVE
value: kubernetes
- name: SPRING_CLOUD_KUBERNETES_RELOAD_MODE
value: polling
status: {}

View File

@@ -1,7 +0,0 @@
apiVersion: v1
data:
my.config.mySecret: cDQ1NXcwcmQ=
kind: Secret
metadata:
name: spring-cloud-kubernetes-client-config-it
type: Opaque

View File

@@ -1,18 +0,0 @@
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: spring-cloud-kubernetes-client-config-it
name: spring-cloud-kubernetes-client-config-it
spec:
ports:
- name: 80-8080
port: 80
protocol: TCP
targetPort: 8080
selector:
app: spring-cloud-kubernetes-client-config-it
type: ClusterIP
status:
loadBalancer: {}

View File

@@ -1,33 +0,0 @@
apiVersion: skaffold/v2alpha3
kind: Config
metadata:
name: spring-cloud-kubernetes-client-config-it
build:
artifacts:
- image: springcloud/spring-cloud-kubernetes-client-config-it
custom:
buildCommand: "../../mvnw clean install -Pskaffold"
dependencies:
paths:
- src
- pom.xml
profiles:
- name: polling
deploy:
kubectl:
manifests:
- k8s/deployment-polling-it.yaml
- k8s/service-it.yaml
- k8s/configmap.yaml
- k8s/secret.yaml
- ../permissions.yaml
deploy:
kubectl:
manifests:
- k8s/deployment-it.yaml
- k8s/service-it.yaml
- k8s/configmap.yaml
- k8s/secret.yaml
- ../permissions.yaml

View File

@@ -17,36 +17,38 @@
package org.springframework.cloud.kubernetes.client.config.it;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import io.kubernetes.client.openapi.apis.AppsV1Api;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.apis.NetworkingV1Api;
import io.kubernetes.client.openapi.models.V1ConfigMap;
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.V1Secret;
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.Test;
import org.testcontainers.k3s.K3sContainer;
import org.testcontainers.shaded.org.awaitility.Awaitility;
import reactor.netty.http.client.HttpClient;
import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.createApiClient;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.getPomVersion;
import static org.awaitility.Awaitility.await;
/**
* @author Ryan Baxter
@@ -57,37 +59,26 @@ class ConfigMapAndSecretIT {
private static final String SECRET_URL = "localhost:80/mySecret";
private static final String SPRING_CLOUD_CLIENT_CONFIG_IT_DEPLOYMENT_NAME = "spring-cloud-kubernetes-client-config-it-deployment";
private static final String K8S_CONFIG_CLIENT_IT_NAME = "spring-cloud-kubernetes-client-config-it-deployment";
private static final String K8S_CONFIG_CLIENT_IT_SERVICE_NAME = "spring-cloud-kubernetes-client-config-it";
private static final String NAMESPACE = "default";
private static final String APP_NAME = "spring-cloud-kubernetes-client-config-it";
private static CoreV1Api api;
private static AppsV1Api appsApi;
private static NetworkingV1Api networkingApi;
private static K8SUtils k8SUtils;
private static final K3sContainer K3S = Commons.container();
private static Util util;
private static CoreV1Api coreV1Api;
@BeforeAll
static void setup() throws Exception {
K3S.start();
Commons.validateImage(K8S_CONFIG_CLIENT_IT_SERVICE_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(K8S_CONFIG_CLIENT_IT_SERVICE_NAME, K3S);
createApiClient(K3S.getKubeConfigYaml());
api = new CoreV1Api();
appsApi = new AppsV1Api();
networkingApi = new NetworkingV1Api();
k8SUtils = new K8SUtils(api, appsApi);
k8SUtils.setUp(NAMESPACE);
util = new Util(K3S);
coreV1Api = new CoreV1Api();
util.setUp(NAMESPACE);
}
@AfterAll
@@ -96,117 +87,93 @@ class ConfigMapAndSecretIT {
}
@AfterEach
void after() throws Exception {
appsApi.deleteCollectionNamespacedDeployment(NAMESPACE, null, null, null,
"metadata.name=" + K8S_CONFIG_CLIENT_IT_NAME, null, null, null, null, null, null, null, null, null);
api.deleteNamespacedService(K8S_CONFIG_CLIENT_IT_SERVICE_NAME, NAMESPACE, null, null, null, null, null, null);
networkingApi.deleteNamespacedIngress("it-ingress", NAMESPACE, null, null, null, null, null, null);
api.deleteNamespacedConfigMap(APP_NAME, NAMESPACE, null, null, null, null, null, null);
api.deleteNamespacedSecret(APP_NAME, NAMESPACE, null, null, null, null, null, null);
void after() {
configK8sClientIt(false, Phase.DELETE);
}
@Test
void testConfigMapAndSecretWatchRefresh() throws Exception {
deployConfigK8sClientIt();
// Check to make sure the controller deployment is ready
k8SUtils.waitForDeployment(SPRING_CLOUD_CLIENT_CONFIG_IT_DEPLOYMENT_NAME, NAMESPACE);
void testConfigMapAndSecretWatchRefresh() {
configK8sClientIt(false, Phase.CREATE);
testConfigMapAndSecretRefresh();
}
@Test
void testConfigMapAndSecretPollingRefresh() throws Exception {
deployConfigK8sClientPollingIt();
// Check to make sure the controller deployment is ready
k8SUtils.waitForDeployment(SPRING_CLOUD_CLIENT_CONFIG_IT_DEPLOYMENT_NAME, NAMESPACE);
void testConfigMapAndSecretPollingRefresh() {
configK8sClientIt(true, Phase.CREATE);
testConfigMapAndSecretRefresh();
}
void testConfigMapAndSecretRefresh() throws Exception {
/**
* <pre>
* - read configmap/secrets the way we initially build them and assert their values
* - replace the above and assert we get the new values.
* </pre>
*/
void testConfigMapAndSecretRefresh() {
WebClient.Builder builder = builder();
WebClient propertyClient = builder.baseUrl(PROPERTY_URL).build();
String property = propertyClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class)
.retryWhen(retrySpec()).block();
assertThat(property).isEqualTo("from-config-map");
await().timeout(Duration.ofSeconds(60)).pollInterval(Duration.ofSeconds(2)).until(() -> propertyClient
.method(HttpMethod.GET).retrieve().bodyToMono(String.class).block().equals("from-config-map"));
WebClient secretClient = builder.baseUrl(SECRET_URL).build();
String secret = secretClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class).retryWhen(retrySpec())
.block();
assertThat(secret).isEqualTo("p455w0rd");
Assertions.assertEquals(secret, "p455w0rd");
V1ConfigMap configMap = getConfigK8sClientItConfigMap();
V1ConfigMap configMap = (V1ConfigMap) util.yaml("spring-cloud-kubernetes-client-config-it-configmap.yaml");
Map<String, String> data = configMap.getData();
data.replace("application.yaml", data.get("application.yaml").replace("from-config-map", "from-unit-test"));
configMap.data(data);
api.replaceNamespacedConfigMap(APP_NAME, NAMESPACE, configMap, null, null, null, null);
Awaitility.await().timeout(Duration.ofSeconds(60)).pollInterval(Duration.ofSeconds(2))
.until(() -> propertyClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class).block()
.equals("from-unit-test"));
V1Secret v1Secret = getConfigK8sClientItCSecret();
try {
coreV1Api.replaceNamespacedConfigMap(APP_NAME, NAMESPACE, configMap, null, null, null, null);
}
catch (ApiException e) {
throw new RuntimeException(e);
}
await().timeout(Duration.ofSeconds(60)).pollInterval(Duration.ofSeconds(2)).until(() -> propertyClient
.method(HttpMethod.GET).retrieve().bodyToMono(String.class).block().equals("from-unit-test"));
V1Secret v1Secret = (V1Secret) util.yaml("spring-cloud-kubernetes-client-config-it-secret.yaml");
Map<String, byte[]> secretData = v1Secret.getData();
secretData.replace("my.config.mySecret", "p455w1rd".getBytes());
v1Secret.setData(secretData);
api.replaceNamespacedSecret(APP_NAME, NAMESPACE, v1Secret, null, null, null, null);
Awaitility.await().timeout(Duration.ofSeconds(60)).pollInterval(Duration.ofSeconds(2)).until(() -> secretClient
try {
coreV1Api.replaceNamespacedSecret(APP_NAME, NAMESPACE, v1Secret, null, null, null, null);
}
catch (ApiException e) {
throw new RuntimeException(e);
}
await().timeout(Duration.ofSeconds(60)).pollInterval(Duration.ofSeconds(2)).until(() -> secretClient
.method(HttpMethod.GET).retrieve().bodyToMono(String.class).block().equals("p455w1rd"));
}
private static void deployConfigK8sClientIt() throws Exception {
k8SUtils.waitForDeploymentToBeDeleted(K8S_CONFIG_CLIENT_IT_NAME, NAMESPACE);
api.createNamespacedSecret(NAMESPACE, getConfigK8sClientItCSecret(), null, null, null, null);
api.createNamespacedConfigMap(NAMESPACE, getConfigK8sClientItConfigMap(), null, null, null, null);
appsApi.createNamespacedDeployment(NAMESPACE, getConfigK8sClientItDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getConfigK8sClientItService(), null, null, null, null);
private static void configK8sClientIt(boolean pooling, Phase phase) {
V1Deployment deployment = (V1Deployment) util.yaml("spring-cloud-kubernetes-client-config-it-deployment.yaml");
V1Ingress ingress = getConfigK8sClientItIngress();
networkingApi.createNamespacedIngress(NAMESPACE, ingress, null, null, null, null);
k8SUtils.waitForIngress(ingress.getMetadata().getName(), NAMESPACE);
}
if (pooling) {
V1EnvVar one = new V1EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_RELOAD_MODE").withValue("polling")
.build();
List<V1EnvVar> existing = new ArrayList<>(
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv());
existing.add(one);
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(existing);
}
private static void deployConfigK8sClientPollingIt() throws Exception {
k8SUtils.waitForDeploymentToBeDeleted(K8S_CONFIG_CLIENT_IT_NAME, NAMESPACE);
api.createNamespacedSecret(NAMESPACE, getConfigK8sClientItCSecret(), null, null, null, null);
api.createNamespacedConfigMap(NAMESPACE, getConfigK8sClientItConfigMap(), null, null, null, null);
appsApi.createNamespacedDeployment(NAMESPACE, getConfigK8sClientItPollingDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getConfigK8sClientItService(), null, null, null, null);
networkingApi.createNamespacedIngress(NAMESPACE, getConfigK8sClientItIngress(), null, null, null, null);
}
V1Service service = (V1Service) util.yaml("spring-cloud-kubernetes-client-config-it-service.yaml");
V1Ingress ingress = (V1Ingress) util.yaml("spring-cloud-kubernetes-client-config-it-ingress.yaml");
private static V1Deployment getConfigK8sClientItDeployment() throws Exception {
V1Deployment deployment = (V1Deployment) K8SUtils
.readYamlFromClasspath("spring-cloud-kubernetes-client-config-it-deployment.yaml");
String image = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage() + ":"
+ getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
V1ConfigMap configMap = (V1ConfigMap) util.yaml("spring-cloud-kubernetes-client-config-it-configmap.yaml");
V1Secret secret = (V1Secret) util.yaml("spring-cloud-kubernetes-client-config-it-secret.yaml");
private static V1Deployment getConfigK8sClientItPollingDeployment() throws Exception {
V1Deployment deployment = (V1Deployment) K8SUtils
.readYamlFromClasspath("spring-cloud-kubernetes-client-config-it-polling-deployment.yaml");
String image = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage() + ":"
+ getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
private static V1Service getConfigK8sClientItService() throws Exception {
return (V1Service) K8SUtils.readYamlFromClasspath("spring-cloud-kubernetes-client-config-it-service.yaml");
}
private static V1Ingress getConfigK8sClientItIngress() throws Exception {
return (V1Ingress) K8SUtils.readYamlFromClasspath("spring-cloud-kubernetes-client-config-it-ingress.yaml");
}
private static V1ConfigMap getConfigK8sClientItConfigMap() throws Exception {
return (V1ConfigMap) K8SUtils.readYamlFromClasspath("spring-cloud-kubernetes-client-config-it-configmap.yaml");
}
private static V1Secret getConfigK8sClientItCSecret() throws Exception {
return (V1Secret) K8SUtils.readYamlFromClasspath("spring-cloud-kubernetes-client-config-it-secret.yaml");
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
util.createAndWait(NAMESPACE, configMap, secret);
}
else if (phase.equals(Phase.DELETE)) {
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
util.deleteAndWait(NAMESPACE, configMap, secret);
}
}
private WebClient.Builder builder() {

View File

@@ -1,33 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-cloud-kubernetes-client-config-it-deployment
spec:
selector:
matchLabels:
app: spring-cloud-kubernetes-client-config-it
template:
metadata:
labels:
app: spring-cloud-kubernetes-client-config-it
spec:
serviceAccountName: spring-cloud-kubernetes-serviceaccount
containers:
- name: spring-cloud-kubernetes-client-config-it
image: docker.io/springcloud/spring-cloud-kubernetes-client-config-it
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet:
port: 8080
path: /actuator/health/readiness
livenessProbe:
httpGet:
port: 8080
path: /actuator/health/liveness
env:
- name: SPRING_PROFILES_ACTIVE
value: kubernetes
- name: SPRING_CLOUD_KUBERNETES_RELOAD_MODE
value: polling
ports:
- containerPort: 8080

View File

@@ -18,11 +18,6 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-test-support</artifactId>

View File

@@ -18,19 +18,15 @@ package org.springframework.cloud.kubernetes.configuration.watcher.multiple.apps
import java.time.Duration;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import io.kubernetes.client.openapi.apis.AppsV1Api;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.apis.NetworkingV1Api;
import io.kubernetes.client.openapi.models.V1ConfigMap;
import io.kubernetes.client.openapi.models.V1ConfigMapBuilder;
import io.kubernetes.client.openapi.models.V1Deployment;
import io.kubernetes.client.openapi.models.V1Ingress;
import io.kubernetes.client.openapi.models.V1Service;
import org.assertj.core.api.Assertions;
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;
@@ -40,14 +36,13 @@ import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util;
import org.springframework.http.HttpMethod;
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.K8SUtils.createApiClient;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.getPomVersion;
/**
* @author wind57
@@ -58,39 +53,20 @@ class ConfigurationWatcherMultipleAppsIT {
private static final String CONFIG_WATCHER_APP_B_IMAGE = "spring-cloud-kubernetes-client-configuration-watcher-configmap-app-b";
private static final String CONFIG_WATCHER_DEPLOYMENT_APP_A_NAME = "app-a-deployment";
private static final String CONFIG_WATCHER_DEPLOYMENT_APP_B_NAME = "app-b-deployment";
private static final String SPRING_CLOUD_K8S_CONFIG_WATCHER_DEPLOYMENT_NAME = "spring-cloud-kubernetes-configuration-watcher-deployment";
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 NAMESPACE = "default";
private static final String KAFKA_BROKER = "kafka-broker";
private static final String KAFKA_SERVICE = "kafka";
private static final String ZOOKEEPER_SERVICE = "zookeeper";
private static final String ZOOKEEPER_DEPLOYMENT = "zookeeper";
private static CoreV1Api api;
private static AppsV1Api appsApi;
private static NetworkingV1Api networkingApi;
private static K8SUtils k8SUtils;
private static final K3sContainer K3S = Commons.container();
private static Util util;
@BeforeAll
static void beforeAll() throws Exception {
K3S.start();
util = new Util(K3S);
Commons.validateImage(SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME, K3S);
@@ -101,12 +77,8 @@ class ConfigurationWatcherMultipleAppsIT {
Commons.validateImage(CONFIG_WATCHER_APP_B_IMAGE, K3S);
Commons.loadSpringCloudKubernetesImage(CONFIG_WATCHER_APP_B_IMAGE, K3S);
createApiClient(K3S.getKubeConfigYaml());
api = new CoreV1Api();
appsApi = new AppsV1Api();
k8SUtils = new K8SUtils(api, appsApi);
networkingApi = new NetworkingV1Api();
k8SUtils.setUp(NAMESPACE);
util = new Util(K3S);
util.setUp(NAMESPACE);
}
@AfterAll
@@ -117,41 +89,25 @@ class ConfigurationWatcherMultipleAppsIT {
}
@BeforeEach
void setup() throws Exception {
deployZookeeper();
deployKafka();
deployAppA();
deployAppB();
deployIngress();
deployConfigWatcher();
waitForDeployment(ZOOKEEPER_DEPLOYMENT);
waitForDeployment(KAFKA_BROKER);
waitForDeployment(CONFIG_WATCHER_DEPLOYMENT_APP_A_NAME);
waitForDeployment(CONFIG_WATCHER_DEPLOYMENT_APP_B_NAME);
waitForDeployment(SPRING_CLOUD_K8S_CONFIG_WATCHER_DEPLOYMENT_NAME);
void setup() {
util.zookeeper(NAMESPACE, Phase.CREATE);
util.kafka(NAMESPACE, Phase.CREATE);
appA(Phase.CREATE);
appB(Phase.CREATE);
configWatcher(Phase.CREATE);
}
@AfterEach
void after() throws Exception {
cleanUpKafka();
cleanUpZookeeper();
cleanUpServices();
cleanUpDeployments();
cleanUpIngress();
cleanUpConfigMaps();
k8SUtils.waitForDeploymentToBeDeleted(KAFKA_BROKER, NAMESPACE);
k8SUtils.waitForDeploymentToBeDeleted(ZOOKEEPER_DEPLOYMENT, NAMESPACE);
k8SUtils.waitForDeploymentToBeDeleted(SPRING_CLOUD_K8S_CONFIG_WATCHER_DEPLOYMENT_NAME, NAMESPACE);
k8SUtils.waitForDeploymentToBeDeleted(CONFIG_WATCHER_DEPLOYMENT_APP_A_NAME, NAMESPACE);
k8SUtils.waitForDeploymentToBeDeleted(CONFIG_WATCHER_DEPLOYMENT_APP_B_NAME, NAMESPACE);
void afterEach() {
util.zookeeper(NAMESPACE, Phase.DELETE);
util.kafka(NAMESPACE, Phase.DELETE);
appA(Phase.DELETE);
appB(Phase.DELETE);
configWatcher(Phase.DELETE);
}
@Test
void testRefresh() throws Exception {
void testRefresh() {
// configmap has one label, one that says that we should refresh
// and one annotation that says that we should refresh some specific services
@@ -161,7 +117,7 @@ class ConfigurationWatcherMultipleAppsIT {
"spring-cloud-kubernetes-client-configuration-watcher-configmap-app-a, "
+ "spring-cloud-kubernetes-client-configuration-watcher-configmap-app-b")
.endMetadata().addToData("foo", "hello world").build();
api.createNamespacedConfigMap(NAMESPACE, configMap, null, null, null, null);
util.createAndWait(NAMESPACE, configMap, null);
WebClient.Builder builderA = builder();
WebClient serviceClientA = builderA.baseUrl("http://localhost:80/app-a").build();
@@ -176,7 +132,7 @@ class ConfigurationWatcherMultipleAppsIT {
return valueA[0];
});
Assertions.assertThat(valueA[0]).isTrue();
Assertions.assertTrue(valueA[0]);
Boolean[] valueB = new Boolean[1];
await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(240)).until(() -> {
@@ -185,191 +141,48 @@ class ConfigurationWatcherMultipleAppsIT {
return valueB[0];
});
Assertions.assertThat(valueB[0]).isTrue();
Assertions.assertTrue(valueB[0]);
util.deleteAndWait(NAMESPACE, configMap, null);
}
/**
* <pre>
--------------------------------------------------- zookeeper ----------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
</pre>
*/
private void deployZookeeper() throws Exception {
api.createNamespacedService(NAMESPACE, getZookeeperService(), null, null, null, null);
V1Deployment deployment = getZookeeperDeployment();
String[] image = K8SUtils.getImageFromDeployment(deployment).split(":");
Commons.pullImage(image[0], image[1], K3S);
Commons.loadImage(image[0], image[1], "zookeeper", K3S);
appsApi.createNamespacedDeployment(NAMESPACE, deployment, null, null, null, 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");
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
else if (phase.equals(Phase.DELETE)) {
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
private V1Deployment getZookeeperDeployment() throws Exception {
return (V1Deployment) K8SUtils.readYamlFromClasspath("zookeeper/zookeeper-deployment.yaml");
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 V1Service getZookeeperService() throws Exception {
return (V1Service) K8SUtils.readYamlFromClasspath("zookeeper/zookeeper-service.yaml");
}
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");
/**
* <pre>
----------------------------------------------------- kafka ------------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
</pre>
*/
private void deployKafka() throws Exception {
api.createNamespacedService(NAMESPACE, getKafkaService(), null, null, null, null);
V1Deployment deployment = getKafkaDeployment();
String[] image = K8SUtils.getImageFromDeployment(deployment).split(":");
Commons.pullImage(image[0], image[1], K3S);
Commons.loadImage(image[0], image[1], "kafka", K3S);
appsApi.createNamespacedDeployment(NAMESPACE, getKafkaDeployment(), null, null, null, null);
}
private V1Deployment getKafkaDeployment() throws Exception {
return (V1Deployment) K8SUtils.readYamlFromClasspath("kafka/kafka-deployment.yaml");
}
private V1Service getKafkaService() throws Exception {
return (V1Service) K8SUtils.readYamlFromClasspath("kafka/kafka-service.yaml");
}
/**
* <pre>
----------------------------------------------------- app-a ------------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
</pre>
*/
private void deployAppA() throws Exception {
appsApi.createNamespacedDeployment(NAMESPACE, getAppADeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getAppAService(), null, null, null, null);
}
private V1Deployment getAppADeployment() throws Exception {
String urlString = "app-a/app-a-deployment.yaml";
V1Deployment deployment = (V1Deployment) K8SUtils.readYamlFromClasspath(urlString);
String image = K8SUtils.getImageFromDeployment(deployment) + ":" + getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
private V1Service getAppAService() throws Exception {
return (V1Service) K8SUtils.readYamlFromClasspath("app-a/app-a-service.yaml");
}
/**
* <pre>
--------------------------------------------------- app-b --------------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
</pre>
*/
private void deployAppB() throws Exception {
appsApi.createNamespacedDeployment(NAMESPACE, getAppBDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getAppBService(), null, null, null, null);
}
private V1Deployment getAppBDeployment() throws Exception {
String urlString = "app-b/app-b-deployment.yaml";
V1Deployment deployment = (V1Deployment) K8SUtils.readYamlFromClasspath(urlString);
String image = K8SUtils.getImageFromDeployment(deployment) + ":" + getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
private V1Service getAppBService() throws Exception {
return (V1Service) K8SUtils.readYamlFromClasspath("app-b/app-b-service.yaml");
}
/**
* <pre>
------------------------------------------------ config-watcher --------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
</pre>
*/
private void deployConfigWatcher() throws Exception {
appsApi.createNamespacedDeployment(NAMESPACE, getConfigWatcherDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getConfigWatcherService(), null, null, null, null);
}
private V1Deployment getConfigWatcherDeployment() throws Exception {
V1Deployment deployment = (V1Deployment) K8SUtils.readYamlFromClasspath(
"config-watcher/spring-cloud-kubernetes-configuration-watcher-bus-kafka-deployment.yaml");
String image = K8SUtils.getImageFromDeployment(deployment) + ":" + getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
private V1Service getConfigWatcherService() throws Exception {
return (V1Service) K8SUtils
.readYamlFromClasspath("config-watcher/spring-cloud-kubernetes-configuration-watcher-service.yaml");
}
/**
* <pre>
------------------------------------------------ common ----------------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
</pre>
*/
private void deployIngress() throws Exception {
V1Ingress ingress = (V1Ingress) K8SUtils.readYamlFromClasspath(
"ingress/spring-cloud-kubernetes-configuration-watcher-multiple-apps-ingress.yaml");
networkingApi.createNamespacedIngress(NAMESPACE, ingress, null, null, null, null);
k8SUtils.waitForIngress(ingress.getMetadata().getName(), NAMESPACE);
}
private void waitForDeployment(String deploymentName) {
await().pollInterval(Duration.ofSeconds(3)).atMost(600, TimeUnit.SECONDS)
.until(() -> k8SUtils.isDeploymentReady(deploymentName, NAMESPACE));
}
private void cleanUpKafka() throws Exception {
appsApi.deleteNamespacedDeployment(KAFKA_BROKER, NAMESPACE, null, null, null, null, null, null);
api.deleteNamespacedService(KAFKA_SERVICE, NAMESPACE, null, null, null, null, null, null);
}
private void cleanUpZookeeper() throws Exception {
appsApi.deleteNamespacedDeployment(ZOOKEEPER_DEPLOYMENT, NAMESPACE, null, null, null, null, null, null);
api.deleteNamespacedService(ZOOKEEPER_SERVICE, NAMESPACE, null, null, null, null, null, null);
}
private void cleanUpServices() throws Exception {
api.deleteNamespacedService("app-a", NAMESPACE, null, null, null, null, null, null);
api.deleteNamespacedService("app-b", NAMESPACE, null, null, null, null, null, null);
api.deleteNamespacedService(SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME, NAMESPACE, null, null, null, null, null,
null);
}
private void cleanUpDeployments() throws Exception {
appsApi.deleteNamespacedDeployment(SPRING_CLOUD_K8S_CONFIG_WATCHER_DEPLOYMENT_NAME, NAMESPACE, null, null, null,
null, null, null);
appsApi.deleteNamespacedDeployment(CONFIG_WATCHER_DEPLOYMENT_APP_A_NAME, NAMESPACE, null, null, null, null,
null, null);
appsApi.deleteNamespacedDeployment(CONFIG_WATCHER_DEPLOYMENT_APP_B_NAME, NAMESPACE, null, null, null, null,
null, null);
}
private void cleanUpConfigMaps() throws Exception {
api.deleteNamespacedConfigMap(CONFIG_MAP_NAME, NAMESPACE, null, null, null, null, null, null);
}
private void cleanUpIngress() throws Exception {
networkingApi.deleteNamespacedIngress("it-ingress-multiple-apps", NAMESPACE, null, null, null, null, null,
null);
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 WebClient.Builder builder() {

View File

@@ -24,9 +24,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
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.apis.NetworkingV1Api;
import io.kubernetes.client.openapi.models.V1ConfigMap;
import io.kubernetes.client.openapi.models.V1ConfigMapBuilder;
import io.kubernetes.client.openapi.models.V1Deployment;
@@ -34,6 +32,7 @@ import io.kubernetes.client.openapi.models.V1Ingress;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1Service;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.testcontainers.k3s.K3sContainer;
@@ -42,15 +41,13 @@ import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.processExecResult;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.createApiClient;
import static org.testcontainers.shaded.org.awaitility.Awaitility.await;
import static org.awaitility.Awaitility.await;
/**
* @author wind57
@@ -61,46 +58,28 @@ class ConfigMapEventReloadIT {
private static final String NAMESPACE = "default";
private static String deploymentName;
private static final K3sContainer K3S = Commons.container();
private static String serviceName;
private static String ingressName;
private static String leftConfigMapName;
private static String rightConfigMapName;
private static String rightWithLabelConfigMapName;
private static Util util;
private static CoreV1Api api;
private static AppsV1Api appsApi;
private static NetworkingV1Api networkingApi;
private static K8SUtils k8SUtils;
private static final K3sContainer K3S = Commons.container();
@BeforeAll
static void beforeAll() throws Exception {
K3S.start();
Commons.validateImage(IMAGE_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S);
createApiClient(K3S.getKubeConfigYaml());
createNamespaces();
util = new Util(K3S);
util.createNamespace("left");
util.createNamespace("right");
util.setUpClusterWide(NAMESPACE, Set.of("left", "right"));
api = new CoreV1Api();
appsApi = new AppsV1Api();
networkingApi = new NetworkingV1Api();
k8SUtils = new K8SUtils(api, appsApi);
k8SUtils.setUpClusterWide(NAMESPACE, Set.of("left", "right"));
}
@AfterAll
static void afterAll() throws Exception {
deleteNamespaces();
util.deleteNamespace("left");
util.deleteNamespace("right");
Commons.cleanUp(IMAGE_NAME, K3S);
}
@@ -114,19 +93,19 @@ class ConfigMapEventReloadIT {
*/
@Test
void testInformFromOneNamespaceEventNotTriggered() throws Exception {
deployManifests("one");
manifests("one", Phase.CREATE);
WebClient webClient = builder().baseUrl("localhost/left").build();
String result = webClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class).retryWhen(retrySpec())
.block();
// we first read the initial value from the left-configmap
assertThat("left-initial").isEqualTo(result);
Assertions.assertEquals("left-initial", result);
// then read the value from the right-configmap
webClient = builder().baseUrl("localhost/right").build();
result = webClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class).retryWhen(retrySpec()).block();
assertThat("right-initial").isEqualTo(result);
Assertions.assertEquals("right-initial", result);
// then deploy a new version of right-configmap
V1ConfigMap rightConfigMapAfterChange = new V1ConfigMapBuilder()
@@ -141,9 +120,9 @@ class ConfigMapEventReloadIT {
webClient = builder().baseUrl("localhost/left").build();
result = webClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class).retryWhen(retrySpec()).block();
// left configmap has not changed, no restart of app has happened
assertThat("left-initial").isEqualTo(result);
Assertions.assertEquals("left-initial", result);
deleteManifests();
manifests("one", Phase.DELETE);
}
/**
@@ -156,13 +135,13 @@ class ConfigMapEventReloadIT {
*/
@Test
void testInformFromOneNamespaceEventTriggered() throws Exception {
deployManifests("two");
manifests("two", Phase.CREATE);
// read the value from the right-configmap
WebClient webClient = builder().baseUrl("localhost/right").build();
String result = webClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class).retryWhen(retrySpec())
.block();
assertThat("right-initial").isEqualTo(result);
Assertions.assertEquals("right-initial", result);
// then deploy a new version of right-configmap
V1ConfigMap rightConfigMapAfterChange = new V1ConfigMapBuilder()
@@ -180,9 +159,9 @@ class ConfigMapEventReloadIT {
resultAfterChange[0] = innerResult;
return innerResult != null;
});
assertThat("right-after-change").isEqualTo(resultAfterChange[0]);
Assertions.assertEquals("right-after-change", resultAfterChange[0]);
deleteManifests();
manifests("two", Phase.DELETE);
}
/**
@@ -196,19 +175,19 @@ class ConfigMapEventReloadIT {
*/
@Test
void testInform() throws Exception {
deployManifests("three");
manifests("three", Phase.CREATE);
// read the initial value from the right-configmap
WebClient rightWebClient = builder().baseUrl("localhost/right").build();
String rightResult = rightWebClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class)
.retryWhen(retrySpec()).block();
assertThat("right-initial").isEqualTo(rightResult);
Assertions.assertEquals("right-initial", rightResult);
// then read the initial value from the right-with-label-configmap
WebClient rightWithLabelWebClient = builder().baseUrl("localhost/with-label").build();
String rightWithLabelResult = rightWithLabelWebClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class)
.retryWhen(retrySpec()).block();
assertThat("right-with-label-initial").isEqualTo(rightWithLabelResult);
Assertions.assertEquals("right-with-label-initial", rightWithLabelResult);
// then deploy a new version of right-configmap
V1ConfigMap rightConfigMapAfterChange = new V1ConfigMapBuilder()
@@ -223,7 +202,7 @@ class ConfigMapEventReloadIT {
// nothing changes in our app, because we are watching only labeled configmaps
rightResult = rightWebClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class).retryWhen(retrySpec())
.block();
assertThat("right-initial").isEqualTo(rightResult);
Assertions.assertEquals("right-initial", rightResult);
// then deploy a new version of right-with-label-configmap
V1ConfigMap rightWithLabelConfigMapAfterChange = new V1ConfigMapBuilder()
@@ -242,62 +221,47 @@ class ConfigMapEventReloadIT {
resultAfterChange[0] = innerResult;
return innerResult != null;
});
assertThat("right-with-label-after-change").isEqualTo(resultAfterChange[0]);
Assertions.assertEquals("right-with-label-after-change", resultAfterChange[0]);
// right-configmap now will see the new value also, but only because the other
// configmap has triggered the restart
rightResult = rightWebClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class).retryWhen(retrySpec())
.block();
assertThat("right-after-change").isEqualTo(rightResult);
Assertions.assertEquals("right-after-change", rightResult);
deleteManifests();
manifests("three", Phase.DELETE);
}
private static void createNamespaces() throws Exception {
processExecResult(K3S.execInContainer("sh", "-c", "kubectl create namespace left"));
processExecResult(K3S.execInContainer("sh", "-c", "kubectl create namespace right"));
}
private static void deleteNamespaces() throws Exception {
processExecResult(K3S.execInContainer("sh", "-c", "kubectl delete namespace left"));
processExecResult(K3S.execInContainer("sh", "-c", "kubectl delete namespace right"));
}
private static void deployManifests(String deploymentRoot) {
private static void manifests(String deploymentRoot, Phase phase) {
try {
V1ConfigMap leftConfigMap = leftConfigMap();
leftConfigMapName = leftConfigMap.getMetadata().getName();
api.createNamespacedConfigMap("left", leftConfigMap, null, null, null, null);
V1ConfigMap leftConfigMap = (V1ConfigMap) util.yaml("left-configmap.yaml");
V1ConfigMap rightConfigMap = (V1ConfigMap) util.yaml("right-configmap.yaml");
V1ConfigMap rightWithLabelConfigMap = (V1ConfigMap) util.yaml("right-configmap-with-label.yaml");
V1ConfigMap rightConfigMap = rightConfigMap();
rightConfigMapName = rightConfigMap.getMetadata().getName();
api.createNamespacedConfigMap("right", rightConfigMap, null, null, null, null);
V1Deployment deployment = (V1Deployment) util.yaml(deploymentRoot + "/deployment.yaml");
V1Service service = (V1Service) util.yaml("service.yaml");
V1Ingress ingress = (V1Ingress) util.yaml("ingress.yaml");
if ("three".equals(deploymentRoot)) {
V1ConfigMap rightWithLabelConfigMap = rightWithLabelConfigMap();
rightWithLabelConfigMapName = rightWithLabelConfigMap.getMetadata().getName();
api.createNamespacedConfigMap("right", rightWithLabelConfigMap, null, null, null, null);
if (phase.equals(Phase.CREATE)) {
util.createAndWait("left", leftConfigMap, null);
util.createAndWait("right", rightConfigMap, null);
if ("three".equals(deploymentRoot)) {
util.createAndWait("right", rightWithLabelConfigMap, null);
}
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
V1Deployment deployment = getDeployment(deploymentRoot);
String version = K8SUtils.getPomVersion();
String currentImage = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(currentImage + ":" + version);
deploymentName = deployment.getMetadata().getName();
appsApi.createNamespacedDeployment(NAMESPACE, deployment, null, null, null, null);
V1Service service = getService();
serviceName = service.getMetadata().getName();
api.createNamespacedService(NAMESPACE, service, null, null, null, null);
V1Ingress ingress = getIngress();
ingressName = ingress.getMetadata().getName();
networkingApi.createNamespacedIngress(NAMESPACE, ingress, null, null, null, null);
k8SUtils.waitForIngress(ingressName, NAMESPACE);
k8SUtils.waitForDeployment("spring-cloud-kubernetes-client-configmap-deployment-event-reload", NAMESPACE);
if (phase.equals(Phase.DELETE)) {
util.deleteAndWait("left", leftConfigMap, null);
util.deleteAndWait("right", rightConfigMap, null);
if ("three".equals(deploymentRoot)) {
util.deleteAndWait("right", rightWithLabelConfigMap, null);
}
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
catch (Exception e) {
@@ -306,52 +270,6 @@ class ConfigMapEventReloadIT {
}
private static void deleteManifests() {
try {
api.deleteNamespacedConfigMap(leftConfigMapName, "left", null, null, null, null, null, null);
api.deleteNamespacedConfigMap(rightConfigMapName, "right", null, null, null, null, null, null);
if (rightWithLabelConfigMapName != null) {
api.deleteNamespacedConfigMap(rightWithLabelConfigMapName, "right", null, null, null, null, null, null);
}
appsApi.deleteNamespacedDeployment(deploymentName, NAMESPACE, null, null, null, null, null, null);
api.deleteNamespacedService(serviceName, NAMESPACE, null, null, null, null, null, null);
networkingApi.deleteNamespacedIngress(ingressName, NAMESPACE, null, null, null, null, null, null);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static V1ConfigMap leftConfigMap() throws Exception {
return (V1ConfigMap) K8SUtils.readYamlFromClasspath("left-configmap.yaml");
}
private static V1ConfigMap rightConfigMap() throws Exception {
return (V1ConfigMap) K8SUtils.readYamlFromClasspath("right-configmap.yaml");
}
private static V1ConfigMap rightWithLabelConfigMap() throws Exception {
return (V1ConfigMap) K8SUtils.readYamlFromClasspath("right-configmap-with-label.yaml");
}
private static V1Deployment getDeployment(String root) throws Exception {
return (V1Deployment) K8SUtils.readYamlFromClasspath(root + "/deployment.yaml");
}
private static V1Service getService() throws Exception {
return (V1Service) K8SUtils.readYamlFromClasspath("service.yaml");
}
private static V1Ingress getIngress() throws Exception {
return (V1Ingress) K8SUtils.readYamlFromClasspath("ingress.yaml");
}
private WebClient.Builder builder() {
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
}

View File

@@ -1,26 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: spring-cloud-kubernetes-client-loadbalancer-it
name: spring-cloud-kubernetes-client-loadbalancer-it-deployment
spec:
replicas: 1
selector:
matchLabels:
app: spring-cloud-kubernetes-client-loadbalancer-it
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: spring-cloud-kubernetes-client-loadbalancer-it
spec:
serviceAccountName: spring-cloud-kubernetes-serviceaccount
containers:
- image: springcloud/spring-cloud-kubernetes-client-loadbalancer-it:3.0.0-SNAPSHOT
imagePullPolicy: IfNotPresent
name: spring-cloud-kubernetes-client-loadbalancer-it
resources: {}
status: {}

View File

@@ -1,18 +0,0 @@
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: spring-cloud-kubernetes-client-loadbalancer-it
name: spring-cloud-kubernetes-client-loadbalancer-it
spec:
ports:
- name: 80-8080
port: 80
protocol: TCP
targetPort: 8080
selector:
app: spring-cloud-kubernetes-client-loadbalancer-it
type: ClusterIP
status:
loadBalancer: {}

View File

@@ -1,19 +0,0 @@
apiVersion: skaffold/v2alpha3
kind: Config
metadata:
name: spring-cloud-kubernetes-client-loadbalancer-it
build:
artifacts:
- image: springcloud/spring-cloud-kubernetes-client-loadbalancer-it
custom:
buildCommand: "../../mvnw clean install -Pskaffold"
dependencies:
paths:
- src
- pom.xml
deploy:
kubectl:
manifests:
- k8s/deployment-it.yaml
- k8s/service-it.yaml
- ../permissions.yaml

View File

@@ -40,7 +40,7 @@ import org.springframework.web.reactive.function.client.WebClient;
@RestController
public class KubernetesClientLoadBalancerApplicationIt {
private static final String URL = "http://servicea-wiremock/__admin/mappings";
private static final String URL = "http://service-wiremock/__admin/mappings";
private final DiscoveryClient discoveryClient;
@@ -58,7 +58,7 @@ public class KubernetesClientLoadBalancerApplicationIt {
return WebClient.builder();
}
@GetMapping("/loadbalancer-it/servicea")
@GetMapping("/loadbalancer-it/service")
@SuppressWarnings("unchecked")
public Map<String, Object> greeting() {
return (Map<String, Object>) client().clientConnector(new ReactorClientHttpConnector(HttpClient.create()))

View File

@@ -20,16 +20,12 @@ import java.time.Duration;
import java.util.Map;
import java.util.Objects;
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.apis.NetworkingV1Api;
import io.kubernetes.client.openapi.models.V1Deployment;
import io.kubernetes.client.openapi.models.V1Ingress;
import io.kubernetes.client.openapi.models.V1Service;
import org.assertj.core.api.Assertions;
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;
@@ -39,93 +35,68 @@ import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.createApiClient;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.getPomVersion;
/**
* @author Ryan Baxter
*/
class LoadBalancerIT {
private static final String SERVICE_URL = "localhost:80/loadbalancer-it/servicea";
private static final String SPRING_CLOUD_K8S_LOADBALANCER_DEPLOYMENT_NAME = "spring-cloud-kubernetes-client-loadbalancer-it-deployment";
private static final String SERVICE_URL = "localhost:80/loadbalancer-it/service";
private static final String SPRING_CLOUD_K8S_LOADBALANCER_APP_NAME = "spring-cloud-kubernetes-client-loadbalancer-it";
private static final String NAMESPACE = "default";
private static CoreV1Api api;
private static AppsV1Api appsApi;
private static NetworkingV1Api networkingApi;
private static K8SUtils k8SUtils;
private static final K3sContainer K3S = Commons.container();
private static Util util;
@BeforeAll
static void beforeAll() throws Exception {
K3S.start();
Commons.validateImage(SPRING_CLOUD_K8S_LOADBALANCER_APP_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(SPRING_CLOUD_K8S_LOADBALANCER_APP_NAME, K3S);
createApiClient(K3S.getKubeConfigYaml());
api = new CoreV1Api();
appsApi = new AppsV1Api();
networkingApi = new NetworkingV1Api();
k8SUtils = new K8SUtils(api, appsApi);
k8SUtils.setUp(NAMESPACE);
util = new Util(K3S);
util.setUp(NAMESPACE);
}
@AfterAll
static void afterAll() throws Exception {
Commons.cleanUp(SPRING_CLOUD_K8S_LOADBALANCER_APP_NAME, K3S);
k8SUtils.removeWiremockImage();
}
@BeforeEach
void setup() throws Exception {
k8SUtils.deployWiremock(NAMESPACE, false, K3S);
void setup() {
util.wiremock(NAMESPACE, "/wiremock", Phase.CREATE);
}
@AfterEach
void afterEach() throws Exception {
cleanup();
k8SUtils.cleanUpWiremock(NAMESPACE);
void afterEach() {
util.wiremock(NAMESPACE, "/wiremock", Phase.DELETE);
}
@Test
void testLoadBalancerServiceMode() throws Exception {
deployLoadbalancerServiceIt();
void testLoadBalancerServiceMode() {
loadbalancerIt(false, Phase.CREATE);
testLoadBalancer();
loadbalancerIt(false, Phase.DELETE);
}
@Test
void testLoadBalancerPodMode() throws Exception {
deployLoadbalancerPodIt();
void testLoadBalancerPodMode() {
loadbalancerIt(true, Phase.CREATE);
testLoadBalancer();
}
private void cleanup() throws ApiException {
appsApi.deleteCollectionNamespacedDeployment(NAMESPACE, null, null, null,
"metadata.name=" + SPRING_CLOUD_K8S_LOADBALANCER_DEPLOYMENT_NAME, null, null, null, null, null, null,
null, null, null);
api.deleteNamespacedService(SPRING_CLOUD_K8S_LOADBALANCER_APP_NAME, NAMESPACE, null, null, null, null, null,
null);
networkingApi.deleteNamespacedIngress("it-ingress", NAMESPACE, null, null, null, null, null, null);
loadbalancerIt(true, Phase.DELETE);
}
private void testLoadBalancer() {
// Check to make sure the controller deployment is ready
k8SUtils.waitForDeployment(SPRING_CLOUD_K8S_LOADBALANCER_DEPLOYMENT_NAME, NAMESPACE);
WebClient.Builder builder = builder();
WebClient serviceClient = builder.baseUrl(SERVICE_URL).build();
@@ -136,55 +107,24 @@ class LoadBalancerIT {
.bodyToMono(ParameterizedTypeReference.forType(resolvableType.getType())).retryWhen(retrySpec())
.block();
Assertions.assertThat(result.containsKey("mappings")).isTrue();
Assertions.assertThat(result.containsKey("meta")).isTrue();
Assertions.assertTrue(result.containsKey("mappings"));
Assertions.assertTrue(result.containsKey("meta"));
}
private void deployLoadbalancerServiceIt() throws Exception {
appsApi.createNamespacedDeployment(NAMESPACE, getLoadbalancerServiceItDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getLoadbalancerItService(), null, null, null, null);
deployIngress();
}
private void loadbalancerIt(boolean podBased, Phase phase) {
V1Deployment deployment = podBased
? (V1Deployment) util.yaml("spring-cloud-kubernetes-client-loadbalancer-pod-it-deployment.yaml")
: (V1Deployment) util.yaml("spring-cloud-kubernetes-client-loadbalancer-service-it-deployment.yaml");
V1Service service = (V1Service) util.yaml("spring-cloud-kubernetes-client-loadbalancer-it-service.yaml");
V1Ingress ingress = (V1Ingress) util.yaml("spring-cloud-kubernetes-client-loadbalancer-it-ingress.yaml");
private void deployLoadbalancerPodIt() throws Exception {
appsApi.createNamespacedDeployment(NAMESPACE, getLoadbalancerPodItDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getLoadbalancerItService(), null, null, null, null);
deployIngress();
}
private void deployIngress() throws Exception {
V1Ingress ingress = getLoadbalancerItIngress();
networkingApi.createNamespacedIngress(NAMESPACE, ingress, null, null, null, null);
k8SUtils.waitForIngress(ingress.getMetadata().getName(), NAMESPACE);
}
private V1Deployment getLoadbalancerServiceItDeployment() throws Exception {
V1Deployment deployment = (V1Deployment) K8SUtils
.readYamlFromClasspath("spring-cloud-kubernetes-client-loadbalancer-service-it-deployment.yaml");
String image = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage() + ":"
+ getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
private V1Deployment getLoadbalancerPodItDeployment() throws Exception {
V1Deployment deployment = (V1Deployment) K8SUtils
.readYamlFromClasspath("spring-cloud-kubernetes-client-loadbalancer-service-it-deployment.yaml");
String image = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage() + ":"
+ getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
private V1Ingress getLoadbalancerItIngress() throws Exception {
return (V1Ingress) K8SUtils
.readYamlFromClasspath("spring-cloud-kubernetes-client-loadbalancer-it-ingress.yaml");
}
private V1Service getLoadbalancerItService() throws Exception {
return (V1Service) K8SUtils
.readYamlFromClasspath("spring-cloud-kubernetes-client-loadbalancer-it-service.yaml");
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
else if (phase.equals(Phase.DELETE)) {
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
private WebClient.Builder builder() {

View File

@@ -1,27 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: servicea-wiremock-deployment
spec:
selector:
matchLabels:
app: servicea-wiremock
template:
metadata:
labels:
app: servicea-wiremock
spec:
containers:
- name: servicea-wiremock
image: wiremock/wiremock:2.32.0
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet:
port: 8080
path: /__admin/mappings
livenessProbe:
httpGet:
port: 8080
path: /__admin/mappings
ports:
- containerPort: 8080

View File

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

View File

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

View File

@@ -1,26 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: spring-cloud-kubernetes-client-reactive-discovery-it
name: spring-cloud-kubernetes-client-reactive-discovery-it-deployment
spec:
replicas: 1
selector:
matchLabels:
app: spring-cloud-kubernetes-client-reactive-discovery-it
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: spring-cloud-kubernetes-client-reactive-discovery-it
spec:
serviceAccountName: spring-cloud-kubernetes-serviceaccount
containers:
- image: springcloud/spring-cloud-kubernetes-client-reactive-discovery-it:3.0.0-SNAPSHOT
imagePullPolicy: IfNotPresent
name: spring-cloud-kubernetes-client-reactive-discovery-it
resources: {}
status: {}

View File

@@ -1,18 +0,0 @@
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: spring-cloud-kubernetes-client-reactive-discovery-it
name: spring-cloud-kubernetes-client-reactive-discovery-it
spec:
ports:
- name: 80-8080
port: 80
protocol: TCP
targetPort: 8080
selector:
app: spring-cloud-kubernetes-client-reactive-discovery-it
type: ClusterIP
status:
loadBalancer: {}

View File

@@ -1,19 +0,0 @@
apiVersion: skaffold/v2alpha3
kind: Config
metadata:
name: spring-cloud-kubernetes-client-reactive-discovery-it
build:
artifacts:
- image: springcloud/spring-cloud-kubernetes-client-reactive-discovery-it
custom:
buildCommand: "../../mvnw clean install -Pskaffold"
dependencies:
paths:
- src
- pom.xml
deploy:
kubectl:
manifests:
- k8s/deployment-it.yaml
- k8s/service-it.yaml
- ../permissions.yaml

View File

@@ -21,16 +21,12 @@ import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
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.apis.NetworkingV1Api;
import io.kubernetes.client.openapi.models.V1Deployment;
import io.kubernetes.client.openapi.models.V1Ingress;
import io.kubernetes.client.openapi.models.V1Service;
import org.assertj.core.api.Assertions;
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;
@@ -40,17 +36,14 @@ import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.createApiClient;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.getPomVersion;
/**
* @author Ryan Baxter
*/
@@ -60,55 +53,42 @@ class ReactiveDiscoveryClientIT {
private static final String SERVICES_URL = "localhost:80/reactive-discovery-it/services";
private static final String SPRING_CLOUD_K8S_REACTIVE_DISCOVERY_DEPLOYMENT_NAME = "spring-cloud-kubernetes-client-reactive-discoveryclient-it-deployment";
private static final String SPRING_CLOUD_K8S_REACTIVE_DISCOVERY_APP_NAME = "spring-cloud-kubernetes-client-reactive-discoveryclient-it";
private static final String NAMESPACE = "default";
private static CoreV1Api api;
private static AppsV1Api appsApi;
private static NetworkingV1Api networkingApi;
private static K8SUtils k8SUtils;
private static final K3sContainer K3S = Commons.container();
private static Util util;
@BeforeAll
static void beforeAll() throws Exception {
K3S.start();
Commons.validateImage(SPRING_CLOUD_K8S_REACTIVE_DISCOVERY_APP_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(SPRING_CLOUD_K8S_REACTIVE_DISCOVERY_APP_NAME, K3S);
createApiClient(K3S.getKubeConfigYaml());
api = new CoreV1Api();
appsApi = new AppsV1Api();
networkingApi = new NetworkingV1Api();
k8SUtils = new K8SUtils(api, appsApi);
k8SUtils.setUp(NAMESPACE);
util = new Util(K3S);
util.setUp(NAMESPACE);
}
@AfterAll
static void afterAll() throws Exception {
Commons.cleanUp(SPRING_CLOUD_K8S_REACTIVE_DISCOVERY_APP_NAME, K3S);
k8SUtils.removeWiremockImage();
}
@BeforeEach
void setup() throws Exception {
k8SUtils.deployWiremock(NAMESPACE, false, K3S);
void setup() {
util.wiremock(NAMESPACE, "/wiremock", Phase.CREATE);
}
@AfterEach
void after() throws Exception {
k8SUtils.cleanUpWiremock(NAMESPACE);
cleanup();
void after() {
util.wiremock(NAMESPACE, "/wiremock", Phase.DELETE);
reactiveDiscoveryIt(Phase.DELETE);
}
@Test
void testReactiveDiscoveryClient() throws Exception {
deployReactiveDiscoveryIt();
void testReactiveDiscoveryClient() {
reactiveDiscoveryIt(Phase.CREATE);
testLoadBalancer();
testHealth();
}
@@ -126,64 +106,36 @@ class ReactiveDiscoveryClientIT {
Map<String, Object> components = (Map<String, Object>) health.get("components");
assertThat(components.containsKey("reactiveDiscoveryClients")).isTrue();
Assertions.assertTrue(components.containsKey("reactiveDiscoveryClients"));
Map<String, Object> discoveryComposite = (Map<String, Object>) components.get("discoveryComposite");
assertThat(discoveryComposite.get("status")).isEqualTo("UP");
}
private void cleanup() throws ApiException {
appsApi.deleteCollectionNamespacedDeployment(NAMESPACE, null, null, null,
"metadata.name=" + SPRING_CLOUD_K8S_REACTIVE_DISCOVERY_DEPLOYMENT_NAME, null, null, null, null, null,
null, null, null, null);
api.deleteNamespacedService(SPRING_CLOUD_K8S_REACTIVE_DISCOVERY_APP_NAME, NAMESPACE, null, null, null, null,
null, null);
networkingApi.deleteNamespacedIngress("it-ingress", NAMESPACE, null, null, null, null, null, null);
Assertions.assertEquals(discoveryComposite.get("status"), "UP");
}
private void testLoadBalancer() {
// Check to make sure the controller deployment is ready
k8SUtils.waitForDeployment(SPRING_CLOUD_K8S_REACTIVE_DISCOVERY_DEPLOYMENT_NAME, NAMESPACE);
WebClient.Builder builder = builder();
WebClient serviceClient = builder.baseUrl(SERVICES_URL).build();
String servicesResponse = serviceClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class)
.retryWhen(retrySpec()).block();
Assertions
.assertThat(Arrays.stream(servicesResponse.split(",")).anyMatch("servicea-wiremock"::equalsIgnoreCase))
.isTrue();
.assertTrue(Arrays.stream(servicesResponse.split(",")).anyMatch("service-wiremock"::equalsIgnoreCase));
}
private void deployIngress(V1Ingress ingress) throws Exception {
networkingApi.createNamespacedIngress(NAMESPACE, ingress, null, null, null, null);
k8SUtils.waitForIngress(ingress.getMetadata().getName(), NAMESPACE);
}
private void reactiveDiscoveryIt(Phase phase) {
V1Deployment deployment = (V1Deployment) util
.yaml("spring-cloud-kubernetes-client-reactive-discoveryclient-it-deployment.yaml");
V1Service service = (V1Service) util
.yaml("spring-cloud-kubernetes-client-reactive-discoveryclient-it-service.yaml");
V1Ingress ingress = (V1Ingress) util
.yaml("spring-cloud-kubernetes-client-reactive-discoveryclient-it-ingress.yaml");
private void deployReactiveDiscoveryIt() throws Exception {
appsApi.createNamespacedDeployment(NAMESPACE, getReactiveDiscoveryItDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getReactiveDiscoveryService(), null, null, null, null);
deployIngress(getReactiveDiscoveryItIngress());
}
private V1Deployment getReactiveDiscoveryItDeployment() throws Exception {
V1Deployment deployment = (V1Deployment) K8SUtils
.readYamlFromClasspath("spring-cloud-kubernetes-client-reactive-discoveryclient-it-deployment.yaml");
String image = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage() + ":"
+ getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
private V1Service getReactiveDiscoveryService() throws Exception {
return (V1Service) K8SUtils
.readYamlFromClasspath("spring-cloud-kubernetes-client-reactive-discoveryclient-it-service.yaml");
}
private V1Ingress getReactiveDiscoveryItIngress() throws Exception {
return (V1Ingress) K8SUtils
.readYamlFromClasspath("spring-cloud-kubernetes-client-reactive-discoveryclient-it-ingress.yaml");
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
else if (phase.equals(Phase.DELETE)) {
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
private WebClient.Builder builder() {

View File

@@ -18,11 +18,6 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-test-support</artifactId>

View File

@@ -18,20 +18,15 @@ package org.springframework.cloud.kubernetes.configuration.watcher.multiple.apps
import java.time.Duration;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import io.kubernetes.client.openapi.apis.AppsV1Api;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.apis.NetworkingV1Api;
import io.kubernetes.client.openapi.models.V1Deployment;
import io.kubernetes.client.openapi.models.V1Ingress;
import io.kubernetes.client.openapi.models.V1ReplicationController;
import io.kubernetes.client.openapi.models.V1Secret;
import io.kubernetes.client.openapi.models.V1SecretBuilder;
import io.kubernetes.client.openapi.models.V1Service;
import org.assertj.core.api.Assertions;
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;
@@ -41,14 +36,13 @@ import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util;
import org.springframework.http.HttpMethod;
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.K8SUtils.createApiClient;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.getPomVersion;
/**
* @author wind57
@@ -59,28 +53,16 @@ class ConfigurationWatcherMultipleAppIT {
private static final String CONFIG_WATCHER_APP_B_IMAGE = "spring-cloud-kubernetes-client-configuration-watcher-secrets-app-b";
private static final String CONFIG_WATCHER_DEPLOYMENT_APP_A_NAME = "app-a-deployment";
private static final String CONFIG_WATCHER_DEPLOYMENT_APP_B_NAME = "app-b-deployment";
private static final String SPRING_CLOUD_K8S_CONFIG_WATCHER_DEPLOYMENT_NAME = "spring-cloud-kubernetes-configuration-watcher-deployment";
private static final String SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME = "spring-cloud-kubernetes-configuration-watcher";
private static final String SECRET_NAME = "multiple-apps";
private static final String NAMESPACE = "default";
private static CoreV1Api api;
private static AppsV1Api appsApi;
private static NetworkingV1Api networkingApi;
private static K8SUtils k8SUtils;
private static final K3sContainer K3S = Commons.container();
private static Util util;
@BeforeAll
static void beforeAll() throws Exception {
K3S.start();
@@ -94,12 +76,8 @@ class ConfigurationWatcherMultipleAppIT {
Commons.validateImage(CONFIG_WATCHER_APP_B_IMAGE, K3S);
Commons.loadSpringCloudKubernetesImage(CONFIG_WATCHER_APP_B_IMAGE, K3S);
createApiClient(K3S.getKubeConfigYaml());
api = new CoreV1Api();
appsApi = new AppsV1Api();
k8SUtils = new K8SUtils(api, appsApi);
networkingApi = new NetworkingV1Api();
k8SUtils.setUp(NAMESPACE);
util = new Util(K3S);
util.setUp(NAMESPACE);
}
@AfterAll
@@ -110,36 +88,23 @@ class ConfigurationWatcherMultipleAppIT {
}
@BeforeEach
void setup() throws Exception {
deployRabbitMq();
deployAppA();
deployAppB();
deployIngress();
deployConfigWatcher();
k8SUtils.waitForReplicationController("rabbitmq-controller", NAMESPACE);
waitForDeployment(CONFIG_WATCHER_DEPLOYMENT_APP_A_NAME);
waitForDeployment(CONFIG_WATCHER_DEPLOYMENT_APP_B_NAME);
waitForDeployment(SPRING_CLOUD_K8S_CONFIG_WATCHER_DEPLOYMENT_NAME);
void setup() {
util.rabbitMq(NAMESPACE, Phase.CREATE);
appA(Phase.CREATE);
appB(Phase.CREATE);
configWatcher(Phase.CREATE);
}
@AfterEach
void after() throws Exception {
cleanRabbitMq();
cleanUpServices();
cleanUpDeployments();
cleanUpIngress();
cleanUpConfigMaps();
k8SUtils.waitForDeploymentToBeDeleted(SPRING_CLOUD_K8S_CONFIG_WATCHER_DEPLOYMENT_NAME, NAMESPACE);
k8SUtils.waitForDeploymentToBeDeleted(CONFIG_WATCHER_DEPLOYMENT_APP_A_NAME, NAMESPACE);
k8SUtils.waitForDeploymentToBeDeleted(CONFIG_WATCHER_DEPLOYMENT_APP_B_NAME, NAMESPACE);
void after() {
util.rabbitMq(NAMESPACE, Phase.DELETE);
appA(Phase.DELETE);
appB(Phase.DELETE);
configWatcher(Phase.DELETE);
}
@Test
void testRefresh() throws Exception {
void testRefresh() {
// secret has one label, one that says that we should refresh
// and one annotation that says that we should refresh some specific services
@@ -149,7 +114,7 @@ class ConfigurationWatcherMultipleAppIT {
"spring-cloud-kubernetes-client-configuration-watcher-secret-app-a, "
+ "spring-cloud-kubernetes-client-configuration-watcher-secret-app-b")
.endMetadata().build();
api.createNamespacedSecret(NAMESPACE, secret, null, null, null, null);
util.createAndWait(NAMESPACE, null, secret);
WebClient.Builder builderA = builder();
WebClient serviceClientA = builderA.baseUrl("http://localhost:80/app-a").build();
@@ -164,7 +129,7 @@ class ConfigurationWatcherMultipleAppIT {
return valueA[0];
});
Assertions.assertThat(valueA[0]).isTrue();
Assertions.assertTrue(valueA[0]);
Boolean[] valueB = new Boolean[1];
await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(240)).until(() -> {
@@ -173,170 +138,48 @@ class ConfigurationWatcherMultipleAppIT {
return valueB[0];
});
Assertions.assertThat(valueB[0]).isTrue();
Assertions.assertTrue(valueB[0]);
util.deleteAndWait(NAMESPACE, null, secret);
}
/**
* <pre>
--------------------------------------------------- rabbitmq -----------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
</pre>
*/
private void deployRabbitMq() throws Exception {
api.createNamespacedService(NAMESPACE, getRabbitMqService(), null, null, null, null);
String[] image = getRabbitMQReplicationController().getSpec().getTemplate().getSpec().getContainers().get(0)
.getImage().split(":");
Commons.pullImage(image[0], image[1], K3S);
Commons.loadImage(image[0], image[1], "rabbitmq", K3S);
api.createNamespacedReplicationController(NAMESPACE, getRabbitMQReplicationController(), null, null, null,
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 V1ReplicationController getRabbitMQReplicationController() throws Exception {
return (V1ReplicationController) K8SUtils.readYamlFromClasspath("rabbitmq/rabbitmq-controller.yaml");
}
private V1Service getRabbitMqService() throws Exception {
return (V1Service) K8SUtils.readYamlFromClasspath("rabbitmq/rabbitmq-service.yaml");
}
/**
* <pre>
----------------------------------------------------- app-a ------------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
</pre>
*/
private void deployAppA() throws Exception {
appsApi.createNamespacedDeployment(NAMESPACE, getAppADeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getAppAService(), null, null, null, null);
}
private V1Deployment getAppADeployment() throws Exception {
String urlString = "app-a/app-a-deployment.yaml";
V1Deployment deployment = (V1Deployment) K8SUtils.readYamlFromClasspath(urlString);
String image = K8SUtils.getImageFromDeployment(deployment) + ":" + getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
private V1Service getAppAService() throws Exception {
return (V1Service) K8SUtils.readYamlFromClasspath("app-a/app-a-service.yaml");
}
/**
* <pre>
--------------------------------------------------- app-b --------------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
</pre>
*/
private void deployAppB() throws Exception {
appsApi.createNamespacedDeployment(NAMESPACE, getAppBDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getAppBService(), null, null, null, null);
}
private V1Deployment getAppBDeployment() throws Exception {
String urlString = "app-b/app-b-deployment.yaml";
V1Deployment deployment = (V1Deployment) K8SUtils.readYamlFromClasspath(urlString);
String image = K8SUtils.getImageFromDeployment(deployment) + ":" + getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
private V1Service getAppBService() throws Exception {
return (V1Service) K8SUtils.readYamlFromClasspath("app-b/app-b-service.yaml");
}
/**
* <pre>
------------------------------------------------ config-watcher --------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
</pre>
*/
private void deployConfigWatcher() throws Exception {
appsApi.createNamespacedDeployment(NAMESPACE, getConfigWatcherDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getConfigWatcherService(), null, null, null, null);
}
private V1Deployment getConfigWatcherDeployment() throws Exception {
V1Deployment deployment = (V1Deployment) K8SUtils.readYamlFromClasspath(
"config-watcher/spring-cloud-kubernetes-configuration-watcher-it-bus-amqp-deployment.yaml");
String image = K8SUtils.getImageFromDeployment(deployment) + ":" + getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
private V1Service getConfigWatcherService() throws Exception {
return (V1Service) K8SUtils
.readYamlFromClasspath("config-watcher/spring-cloud-kubernetes-configuration-watcher-service.yaml");
}
/**
* <pre>
------------------------------------------------ common ----------------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
</pre>
*/
private void deployIngress() throws Exception {
V1Ingress ingress = (V1Ingress) K8SUtils.readYamlFromClasspath(
"ingress/spring-cloud-kubernetes-configuration-watcher-multiple-apps-ingress.yaml");
networkingApi.createNamespacedIngress(NAMESPACE, ingress, null, null, null, null);
k8SUtils.waitForIngress(ingress.getMetadata().getName(), NAMESPACE);
}
private void waitForDeployment(String deploymentName) {
await().pollInterval(Duration.ofSeconds(3)).atMost(600, TimeUnit.SECONDS)
.until(() -> k8SUtils.isDeploymentReady(deploymentName, NAMESPACE));
}
private void cleanRabbitMq() throws Exception {
api.deleteNamespacedService("rabbitmq-service", NAMESPACE, null, null, null, null, null, null);
try {
api.deleteNamespacedReplicationController("rabbitmq-controller", NAMESPACE, null, null, null, null, null,
null);
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
catch (Exception e) {
// swallowing this exception, delete does actually happen, it's a problem
// downstream from the k8s client; see:
// https://github.com/kubernetes-client/java/issues/86#issuecomment-411234259
else if (phase.equals(Phase.DELETE)) {
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
private void cleanUpServices() throws Exception {
api.deleteNamespacedService("app-a", NAMESPACE, null, null, null, null, null, null);
api.deleteNamespacedService("app-b", NAMESPACE, null, null, null, null, null, null);
api.deleteNamespacedService(SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME, NAMESPACE, null, null, null, null, null,
null);
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 cleanUpDeployments() throws Exception {
appsApi.deleteNamespacedDeployment(SPRING_CLOUD_K8S_CONFIG_WATCHER_DEPLOYMENT_NAME, NAMESPACE, null, null, null,
null, null, null);
appsApi.deleteNamespacedDeployment(CONFIG_WATCHER_DEPLOYMENT_APP_A_NAME, NAMESPACE, null, null, null, null,
null, null);
private void configWatcher(Phase phase) {
V1Deployment deployment = (V1Deployment) util
.yaml("config-watcher/spring-cloud-kubernetes-configuration-watcher-it-bus-amqp-deployment.yaml");
V1Service service = (V1Service) util
.yaml("config-watcher/spring-cloud-kubernetes-configuration-watcher-service.yaml");
appsApi.deleteNamespacedDeployment(CONFIG_WATCHER_DEPLOYMENT_APP_B_NAME, NAMESPACE, null, null, null, null,
null, null);
}
private void cleanUpConfigMaps() throws Exception {
api.deleteNamespacedSecret(SECRET_NAME, NAMESPACE, null, null, null, null, null, null);
}
private void cleanUpIngress() throws Exception {
networkingApi.deleteNamespacedIngress("it-ingress-multiple-apps", NAMESPACE, null, null, null, null, null,
null);
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 WebClient.Builder builder() {

View File

@@ -1,29 +0,0 @@
apiVersion: v1
kind: ReplicationController
metadata:
labels:
component: rabbitmq
name: rabbitmq-controller
spec:
replicas: 1
template:
metadata:
labels:
app: taskQueue
component: rabbitmq
spec:
containers:
- image: rabbitmq:3-management
name: rabbitmq
ports:
- name: amqp
containerPort: 5672
- name: http-stats
containerPort: 15672
readinessProbe:
httpGet:
port: 15672
path: /api/healthchecks/node
httpHeaders:
- name: Authorization
value: Basic Z3Vlc3Q6Z3Vlc3Q=

View File

@@ -20,9 +20,7 @@ import java.time.Duration;
import java.util.Map;
import java.util.Objects;
import io.kubernetes.client.openapi.apis.AppsV1Api;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.apis.NetworkingV1Api;
import io.kubernetes.client.openapi.models.V1Deployment;
import io.kubernetes.client.openapi.models.V1Ingress;
import io.kubernetes.client.openapi.models.V1Secret;
@@ -32,20 +30,18 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.testcontainers.k3s.K3sContainer;
import org.testcontainers.shaded.org.awaitility.Awaitility;
import reactor.netty.http.client.HttpClient;
import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.createApiClient;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.getPomVersion;
import static org.awaitility.Awaitility.await;
/**
* @author wind57
@@ -54,33 +50,24 @@ class SecretsEventReloadIT {
private static final String PROPERTY_URL = "localhost:80/key";
private static final String SPRING_CLOUD_CLIENT_CONFIG_IT_DEPLOYMENT_NAME = "spring-cloud-kubernetes-client-secrets-deployment-event-reload";
private static final String K8S_CONFIG_CLIENT_IT_SERVICE_NAME = "spring-cloud-kubernetes-client-secrets-event-reload";
private static final String NAMESPACE = "default";
private static CoreV1Api api;
private static AppsV1Api appsApi;
private static NetworkingV1Api networkingApi;
private static K8SUtils k8SUtils;
private static final K3sContainer K3S = Commons.container();
private static Util util;
private static CoreV1Api coreV1Api;
@BeforeAll
static void setup() throws Exception {
K3S.start();
Commons.validateImage(K8S_CONFIG_CLIENT_IT_SERVICE_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(K8S_CONFIG_CLIENT_IT_SERVICE_NAME, K3S);
createApiClient(K3S.getKubeConfigYaml());
api = new CoreV1Api();
appsApi = new AppsV1Api();
networkingApi = new NetworkingV1Api();
k8SUtils = new K8SUtils(api, appsApi);
k8SUtils.setUp(NAMESPACE);
util = new Util(K3S);
coreV1Api = new CoreV1Api();
util.setUp(NAMESPACE);
}
@AfterAll
@@ -89,22 +76,13 @@ class SecretsEventReloadIT {
}
@AfterEach
void after() throws Exception {
appsApi.deleteCollectionNamespacedDeployment(NAMESPACE, null, null, null,
"metadata.name=" + SPRING_CLOUD_CLIENT_CONFIG_IT_DEPLOYMENT_NAME, null, null, null, null, null, null,
null, null, null);
api.deleteNamespacedService(K8S_CONFIG_CLIENT_IT_SERVICE_NAME, NAMESPACE, null, null, null, null, null, null);
networkingApi.deleteNamespacedIngress("spring-cloud-kubernetes-client-secrets-ingress-event-reload", NAMESPACE,
null, null, null, null, null, null);
api.deleteNamespacedSecret("event-reload", NAMESPACE, null, null, null, null, null, null);
void after() {
configK8sClientIt(Phase.DELETE);
}
@Test
void testSecretReload() throws Exception {
deployConfigK8sClientIt();
// Check to make sure the controller deployment is ready
k8SUtils.waitForDeployment(SPRING_CLOUD_CLIENT_CONFIG_IT_DEPLOYMENT_NAME, NAMESPACE);
configK8sClientIt(Phase.CREATE);
testSecretEventReload();
}
@@ -112,50 +90,36 @@ class SecretsEventReloadIT {
WebClient.Builder builder = builder();
WebClient secretClient = builder.baseUrl(PROPERTY_URL).build();
String secret = secretClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class).retryWhen(retrySpec())
.block();
assertThat(secret).isEqualTo("initial");
V1Secret v1Secret = getConfigK8sClientItCSecret();
await().timeout(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(2))
.until(() -> secretClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class)
.retryWhen(retrySpec()).block().equals("initial"));
V1Secret v1Secret = (V1Secret) util.yaml("secret.yaml");
Map<String, byte[]> secretData = v1Secret.getData();
secretData.replace("application.properties", "from.properties.key: after-change".getBytes());
v1Secret.setData(secretData);
api.replaceNamespacedSecret("event-reload", NAMESPACE, v1Secret, null, null, null, null);
coreV1Api.replaceNamespacedSecret("event-reload", NAMESPACE, v1Secret, null, null, null, null);
Awaitility.await().timeout(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(2))
await().timeout(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(2))
.until(() -> secretClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class)
.retryWhen(retrySpec()).block().equals("after-change"));
}
private static void deployConfigK8sClientIt() throws Exception {
k8SUtils.waitForDeploymentToBeDeleted(SPRING_CLOUD_CLIENT_CONFIG_IT_DEPLOYMENT_NAME, NAMESPACE);
api.createNamespacedSecret(NAMESPACE, getConfigK8sClientItCSecret(), null, null, null, null);
appsApi.createNamespacedDeployment(NAMESPACE, getConfigK8sClientItDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getConfigK8sClientItService(), null, null, null, null);
private void configK8sClientIt(Phase phase) {
V1Deployment deployment = (V1Deployment) util.yaml("deployment.yaml");
V1Service service = (V1Service) util.yaml("service.yaml");
V1Ingress ingress = (V1Ingress) util.yaml("ingress.yaml");
V1Secret secret = (V1Secret) util.yaml("secret.yaml");
V1Ingress ingress = getConfigK8sClientItIngress();
networkingApi.createNamespacedIngress(NAMESPACE, ingress, null, null, null, null);
k8SUtils.waitForIngress(ingress.getMetadata().getName(), NAMESPACE);
}
private static V1Deployment getConfigK8sClientItDeployment() throws Exception {
V1Deployment deployment = (V1Deployment) K8SUtils.readYamlFromClasspath("deployment.yaml");
String image = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage() + ":"
+ getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
private static V1Service getConfigK8sClientItService() throws Exception {
return (V1Service) K8SUtils.readYamlFromClasspath("service.yaml");
}
private static V1Ingress getConfigK8sClientItIngress() throws Exception {
return (V1Ingress) K8SUtils.readYamlFromClasspath("ingress.yaml");
}
private static V1Secret getConfigK8sClientItCSecret() throws Exception {
return (V1Secret) K8SUtils.readYamlFromClasspath("secret.yaml");
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
util.createAndWait(NAMESPACE, null, secret);
}
else if (phase.equals(Phase.DELETE)) {
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
util.deleteAndWait(NAMESPACE, null, secret);
}
}
private WebClient.Builder builder() {

View File

@@ -38,11 +38,6 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-test-support</artifactId>

View File

@@ -20,8 +20,6 @@ import java.time.Duration;
import com.github.tomakehurst.wiremock.client.VerificationException;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.kubernetes.client.openapi.apis.AppsV1Api;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.V1ConfigMap;
import io.kubernetes.client.openapi.models.V1ConfigMapBuilder;
import io.kubernetes.client.openapi.models.V1Deployment;
@@ -34,30 +32,18 @@ import org.junit.jupiter.api.Test;
import org.testcontainers.k3s.K3sContainer;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.findAll;
import static com.github.tomakehurst.wiremock.client.WireMock.post;
import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.verify;
import static org.awaitility.Awaitility.await;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.createApiClient;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.getPomVersion;
/**
* @author Ryan Baxter
*/
class ActuatorRefreshIT {
private static final String SPRING_CLOUD_K8S_CONFIG_WATCHER_DEPLOYMENT_NAME = "spring-cloud-kubernetes-configuration-watcher-deployment";
private static final String SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME = "spring-cloud-kubernetes-configuration-watcher";
private String configWatcherConfigMapName;
private static final String WIREMOCK_HOST = "localhost";
private static final String WIREMOCK_PATH = "/";
@@ -66,105 +52,83 @@ class ActuatorRefreshIT {
private static final String NAMESPACE = "default";
private static CoreV1Api api;
private static AppsV1Api appsApi;
private static K8SUtils k8SUtils;
private static final K3sContainer K3S = Commons.container();
private static Util util;
@BeforeAll
static void beforeAll() throws Exception {
K3S.start();
Commons.validateImage(SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME, K3S);
createApiClient(K3S.getKubeConfigYaml());
api = new CoreV1Api();
appsApi = new AppsV1Api();
k8SUtils = new K8SUtils(api, appsApi);
k8SUtils.setUp(NAMESPACE);
util = new Util(K3S);
util.setUp(NAMESPACE);
}
@AfterAll
static void afterAll() throws Exception {
Commons.cleanUp(SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME, K3S);
k8SUtils.removeWiremockImage();
}
@BeforeEach
void setup() throws Exception {
deployConfigWatcher();
k8SUtils.deployWiremock(NAMESPACE, true, K3S);
void setup() {
configWatcher(Phase.CREATE);
util.wiremock(NAMESPACE, "/", Phase.CREATE);
}
@AfterEach
void after() throws Exception {
appsApi.deleteCollectionNamespacedDeployment(NAMESPACE, null, null, null,
"metadata.name=" + SPRING_CLOUD_K8S_CONFIG_WATCHER_DEPLOYMENT_NAME, null, null, null, null, null, null,
null, null, null);
api.deleteNamespacedService(SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME, NAMESPACE, null, null, null, null, null,
null);
api.deleteNamespacedConfigMap(configWatcherConfigMapName, NAMESPACE, null, null, null, null, null, null);
api.deleteNamespacedConfigMap("servicea-wiremock", NAMESPACE, null, null, null, null, null, null);
k8SUtils.cleanUpWiremock(NAMESPACE);
void after() {
configWatcher(Phase.DELETE);
util.wiremock(NAMESPACE, "/", Phase.DELETE);
}
/*
* this test loads uses two services: wiremock on port 8080 and configuration-watcher
* on port 8888. we deploy configuration-watcher first and configure it via a
* configmap with the same name. then, we mock the call to actuator/refresh endpoint
* and deploy a new configmap: "servicea-wiremock", this in turn will trigger that
* and deploy a new configmap: "service-wiremock", this in turn will trigger that
* refresh that we capture and assert for.
*/
// curl <WIREMOCK_POD_IP>:8080/__admin/mappings
@Test
void testActuatorRefresh() throws Exception {
void testActuatorRefresh() {
WireMock.configureFor(WIREMOCK_HOST, WIREMOCK_PORT, WIREMOCK_PATH);
await().timeout(Duration.ofSeconds(60)).ignoreException(VerificationException.class)
.until(() -> stubFor(post(urlEqualTo("/actuator/refresh")).willReturn(aResponse().withStatus(200)))
.until(() -> WireMock
.stubFor(WireMock.post(WireMock.urlEqualTo("/actuator/refresh"))
.willReturn(WireMock.aResponse().withBody("{}").withStatus(200)))
.getResponse().wasConfigured());
// Create new configmap to trigger controller to signal app to refresh
V1ConfigMap configMap = new V1ConfigMapBuilder().editOrNewMetadata().withName("servicea-wiremock")
V1ConfigMap configMap = new V1ConfigMapBuilder().editOrNewMetadata().withName("service-wiremock")
.addToLabels("spring.cloud.kubernetes.config", "true").endMetadata().addToData("foo", "bar").build();
api.createNamespacedConfigMap(NAMESPACE, configMap, null, null, null, null);
util.createAndWait(NAMESPACE, configMap, null);
// Wait a bit before we verify
await().atMost(Duration.ofSeconds(30))
.until(() -> !findAll(postRequestedFor(urlEqualTo("/actuator/refresh"))).isEmpty());
await().atMost(Duration.ofSeconds(30)).until(
() -> !WireMock.findAll(WireMock.postRequestedFor(WireMock.urlEqualTo("/actuator/refresh"))).isEmpty());
verify(postRequestedFor(urlEqualTo("/actuator/refresh")));
WireMock.verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/actuator/refresh")));
util.deleteAndWait(NAMESPACE, configMap, null);
}
private void deployConfigWatcher() throws Exception {
V1ConfigMap configMap = getConfigWatcherConfigMap();
configWatcherConfigMapName = configMap.getMetadata().getName();
api.createNamespacedConfigMap(NAMESPACE, configMap, null, null, null, null);
appsApi.createNamespacedDeployment(NAMESPACE, getConfigWatcherDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getConfigWatcherService(), null, null, null, null);
private void configWatcher(Phase phase) {
V1ConfigMap configMap = (V1ConfigMap) util
.yaml("config-watcher/spring-cloud-kubernetes-configuration-watcher-configmap.yaml");
V1Deployment deployment = (V1Deployment) util
.yaml("config-watcher/spring-cloud-kubernetes-configuration-watcher-http-deployment.yaml");
V1Service service = (V1Service) util
.yaml("config-watcher/spring-cloud-kubernetes-configuration-watcher-service.yaml");
// Check to make sure the controller deployment is ready
k8SUtils.waitForDeployment(SPRING_CLOUD_K8S_CONFIG_WATCHER_DEPLOYMENT_NAME, NAMESPACE);
}
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, configMap, null);
util.createAndWait(NAMESPACE, null, deployment, service, null, true);
}
else {
util.deleteAndWait(NAMESPACE, configMap, null);
util.deleteAndWait(NAMESPACE, deployment, service, null);
}
private V1Deployment getConfigWatcherDeployment() throws Exception {
V1Deployment deployment = (V1Deployment) K8SUtils.readYamlFromClasspath(
"config-watcher/spring-cloud-kubernetes-configuration-watcher-http-deployment.yaml");
String image = K8SUtils.getImageFromDeployment(deployment) + ":" + getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
private V1Service getConfigWatcherService() throws Exception {
return (V1Service) K8SUtils
.readYamlFromClasspath("config-watcher/spring-cloud-kubernetes-configuration-watcher-service.yaml");
}
private V1ConfigMap getConfigWatcherConfigMap() throws Exception {
return (V1ConfigMap) K8SUtils
.readYamlFromClasspath("config-watcher/spring-cloud-kubernetes-configuration-watcher-configmap.yaml");
}
}

View File

@@ -18,19 +18,15 @@ package org.springframework.cloud.kubernetes.configuration.watcher;
import java.time.Duration;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import io.kubernetes.client.openapi.apis.AppsV1Api;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.apis.NetworkingV1Api;
import io.kubernetes.client.openapi.models.V1ConfigMap;
import io.kubernetes.client.openapi.models.V1ConfigMapBuilder;
import io.kubernetes.client.openapi.models.V1Deployment;
import io.kubernetes.client.openapi.models.V1Ingress;
import io.kubernetes.client.openapi.models.V1Service;
import org.assertj.core.api.Assertions;
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;
@@ -40,14 +36,13 @@ import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util;
import org.springframework.http.HttpMethod;
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.K8SUtils.createApiClient;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.getPomVersion;
/**
* @author Kris Iyer
@@ -56,32 +51,14 @@ class ActuatorRefreshKafkaIT {
private static final String CONFIG_WATCHER_IT_IMAGE = "spring-cloud-kubernetes-configuration-watcher-it";
private static final String SPRING_CLOUD_K8S_CONFIG_WATCHER_DEPLOYMENT_NAME = "spring-cloud-kubernetes-configuration-watcher-deployment";
private static final String SPRING_CLOUD_K8S_CONFIG_WATCHER_IT_DEPLOYMENT_NAME = "spring-cloud-kubernetes-configuration-watcher-it-deployment";
private static final String SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME = "spring-cloud-kubernetes-configuration-watcher";
private static final String NAMESPACE = "default";
private static final String KAFKA_BROKER = "kafka-broker";
private static final String KAFKA_SERVICE = "kafka";
private static final String ZOOKEEPER_SERVICE = "zookeeper";
private static final String ZOOKEEPER_DEPLOYMENT = "zookeeper";
private static CoreV1Api api;
private static AppsV1Api appsApi;
private static NetworkingV1Api networkingApi;
private static K8SUtils k8SUtils;
private static final K3sContainer K3S = Commons.container();
private static Util util;
@BeforeAll
static void beforeAll() throws Exception {
K3S.start();
@@ -91,13 +68,8 @@ class ActuatorRefreshKafkaIT {
Commons.validateImage(CONFIG_WATCHER_IT_IMAGE, K3S);
Commons.loadSpringCloudKubernetesImage(CONFIG_WATCHER_IT_IMAGE, K3S);
createApiClient(K3S.getKubeConfigYaml());
api = new CoreV1Api();
appsApi = new AppsV1Api();
k8SUtils = new K8SUtils(api, appsApi);
networkingApi = new NetworkingV1Api();
k8SUtils.setUp(NAMESPACE);
util = new Util(K3S);
util.setUp(NAMESPACE);
}
@AfterAll
@@ -107,46 +79,28 @@ class ActuatorRefreshKafkaIT {
}
@BeforeEach
void setup() throws Exception {
deployZookeeper();
deployKafka();
deployTestApp();
deployConfigWatcher();
// Check to make sure the controller deployment is ready
waitForDeployment(ZOOKEEPER_DEPLOYMENT);
waitForDeployment(KAFKA_BROKER);
waitForDeployment(SPRING_CLOUD_K8S_CONFIG_WATCHER_IT_DEPLOYMENT_NAME);
waitForDeployment(SPRING_CLOUD_K8S_CONFIG_WATCHER_DEPLOYMENT_NAME);
void setup() {
util.zookeeper(NAMESPACE, Phase.CREATE);
util.kafka(NAMESPACE, Phase.CREATE);
testApp(Phase.CREATE);
configWatcher(Phase.CREATE);
}
@AfterEach
void after() throws Exception {
cleanUpKafka();
cleanUpZookeeper();
cleanUpServices();
cleanUpDeployments();
networkingApi.deleteNamespacedIngress("it-ingress", NAMESPACE, null, null, null, null, null, null);
cleanUpConfigMaps();
// Check to make sure the controller deployment is deleted
k8SUtils.waitForDeploymentToBeDeleted(KAFKA_BROKER, NAMESPACE);
k8SUtils.waitForDeploymentToBeDeleted(ZOOKEEPER_DEPLOYMENT, NAMESPACE);
k8SUtils.waitForDeploymentToBeDeleted(SPRING_CLOUD_K8S_CONFIG_WATCHER_DEPLOYMENT_NAME, NAMESPACE);
k8SUtils.waitForDeploymentToBeDeleted(SPRING_CLOUD_K8S_CONFIG_WATCHER_IT_DEPLOYMENT_NAME, NAMESPACE);
void after() {
util.zookeeper(NAMESPACE, Phase.DELETE);
util.kafka(NAMESPACE, Phase.DELETE);
testApp(Phase.DELETE);
configWatcher(Phase.DELETE);
}
@Test
void testRefresh() throws Exception {
void testRefresh() {
// Create new configmap to trigger controller to signal app to refresh
V1ConfigMap configMap = new V1ConfigMapBuilder().editOrNewMetadata().withName(CONFIG_WATCHER_IT_IMAGE)
.addToLabels("spring.cloud.kubernetes.config", "true").endMetadata().addToData("foo", "hello world")
.build();
api.createNamespacedConfigMap(NAMESPACE, configMap, null, null, null, null);
util.createAndWait(NAMESPACE, configMap, null);
WebClient.Builder builder = builder();
WebClient serviceClient = builder.baseUrl("http://localhost:80/it").build();
@@ -158,126 +112,42 @@ class ActuatorRefreshKafkaIT {
return value[0];
});
Assertions.assertThat(value[0]).isTrue();
Assertions.assertTrue(value[0]);
util.deleteAndWait(NAMESPACE, configMap, null);
}
private void waitForDeployment(String deploymentName) {
await().pollInterval(Duration.ofSeconds(3)).atMost(600, TimeUnit.SECONDS)
.until(() -> k8SUtils.isDeploymentReady(deploymentName, NAMESPACE));
private void testApp(Phase phase) {
V1Deployment deployment = (V1Deployment) util
.yaml("app/spring-cloud-kubernetes-configuration-watcher-it-bus-kafka-deployment.yaml");
V1Service service = (V1Service) util.yaml("app/spring-cloud-kubernetes-configuration-watcher-it-service.yaml");
V1Ingress ingress = (V1Ingress) util.yaml("app/spring-cloud-kubernetes-configuration-watcher-it-ingress.yaml");
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
else if (phase.equals(Phase.DELETE)) {
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
private void deployTestApp() throws Exception {
appsApi.createNamespacedDeployment(NAMESPACE, getItDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getItAppService(), null, null, null, null);
private void configWatcher(Phase phase) {
V1Deployment deployment = (V1Deployment) util
.yaml("app-watcher/spring-cloud-kubernetes-configuration-watcher-bus-kafka-deployment.yaml");
V1Service service = (V1Service) util
.yaml("config-watcher/spring-cloud-kubernetes-configuration-watcher-service.yaml");
V1Ingress ingress = getItIngress();
networkingApi.createNamespacedIngress(NAMESPACE, ingress, null, null, null, null);
k8SUtils.waitForIngress(ingress.getMetadata().getName(), NAMESPACE);
}
V1ConfigMap configMap = (V1ConfigMap) util
.yaml("config-watcher/spring-cloud-kubernetes-configuration-watcher-configmap.yaml");
private void deployConfigWatcher() throws Exception {
api.createNamespacedConfigMap(NAMESPACE, getConfigWatcherConfigMap(), null, null, null, null);
appsApi.createNamespacedDeployment(NAMESPACE, getConfigWatcherDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getConfigWatcherService(), null, null, null, null);
}
private void deployZookeeper() throws Exception {
api.createNamespacedService(NAMESPACE, getZookeeperService(), null, null, null, null);
V1Deployment deployment = getZookeeperDeployment();
String[] image = K8SUtils.getImageFromDeployment(deployment).split(":");
Commons.pullImage(image[0], image[1], K3S);
Commons.loadImage(image[0], image[1], "zookeeper", K3S);
appsApi.createNamespacedDeployment(NAMESPACE, deployment, null, null, null, null);
}
private void deployKafka() throws Exception {
api.createNamespacedService(NAMESPACE, getKafkaService(), null, null, null, null);
V1Deployment deployment = getKafkaDeployment();
String[] image = K8SUtils.getImageFromDeployment(deployment).split(":");
Commons.pullImage(image[0], image[1], K3S);
Commons.loadImage(image[0], image[1], "kafka", K3S);
appsApi.createNamespacedDeployment(NAMESPACE, getKafkaDeployment(), null, null, null, null);
}
private void cleanUpKafka() throws Exception {
appsApi.deleteNamespacedDeployment(KAFKA_BROKER, NAMESPACE, null, null, null, null, null, null);
api.deleteNamespacedService(KAFKA_SERVICE, NAMESPACE, null, null, null, null, null, null);
}
private void cleanUpZookeeper() throws Exception {
appsApi.deleteNamespacedDeployment(ZOOKEEPER_DEPLOYMENT, NAMESPACE, null, null, null, null, null, null);
api.deleteNamespacedService(ZOOKEEPER_SERVICE, NAMESPACE, null, null, null, null, null, null);
}
private V1Deployment getConfigWatcherDeployment() throws Exception {
V1Deployment deployment = (V1Deployment) K8SUtils.readYamlFromClasspath(
"app-watcher/spring-cloud-kubernetes-configuration-watcher-bus-kafka-deployment.yaml");
String image = K8SUtils.getImageFromDeployment(deployment) + ":" + getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
private V1Deployment getItDeployment() throws Exception {
String urlString = "app/spring-cloud-kubernetes-configuration-watcher-it-bus-kafka-deployment.yaml";
V1Deployment deployment = (V1Deployment) K8SUtils.readYamlFromClasspath(urlString);
String image = K8SUtils.getImageFromDeployment(deployment) + ":" + getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
private V1Service getConfigWatcherService() throws Exception {
return (V1Service) K8SUtils
.readYamlFromClasspath("config-watcher/spring-cloud-kubernetes-configuration-watcher-service.yaml");
}
private V1ConfigMap getConfigWatcherConfigMap() throws Exception {
return (V1ConfigMap) K8SUtils
.readYamlFromClasspath("config-watcher/spring-cloud-kubernetes-configuration-watcher-configmap.yaml");
}
private V1Service getItAppService() throws Exception {
return (V1Service) K8SUtils
.readYamlFromClasspath("app/spring-cloud-kubernetes-configuration-watcher-it-service.yaml");
}
private V1Ingress getItIngress() throws Exception {
return (V1Ingress) K8SUtils
.readYamlFromClasspath("app/spring-cloud-kubernetes-configuration-watcher-it-ingress.yaml");
}
private V1Deployment getKafkaDeployment() throws Exception {
return (V1Deployment) K8SUtils.readYamlFromClasspath("kafka/kafka-deployment.yaml");
}
private V1Service getKafkaService() throws Exception {
return (V1Service) K8SUtils.readYamlFromClasspath("kafka/kafka-service.yaml");
}
private V1Deployment getZookeeperDeployment() throws Exception {
return (V1Deployment) K8SUtils.readYamlFromClasspath("zookeeper/zookeeper-deployment.yaml");
}
private V1Service getZookeeperService() throws Exception {
return (V1Service) K8SUtils.readYamlFromClasspath("zookeeper/zookeeper-service.yaml");
}
private void cleanUpConfigMaps() throws Exception {
api.deleteNamespacedConfigMap(SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME, NAMESPACE, null, null, null, null, null,
null);
api.deleteNamespacedConfigMap(CONFIG_WATCHER_IT_IMAGE, NAMESPACE, null, null, null, null, null, null);
}
private void cleanUpDeployments() throws Exception {
appsApi.deleteNamespacedDeployment(SPRING_CLOUD_K8S_CONFIG_WATCHER_DEPLOYMENT_NAME, NAMESPACE, null, null, null,
null, null, null);
appsApi.deleteNamespacedDeployment(SPRING_CLOUD_K8S_CONFIG_WATCHER_IT_DEPLOYMENT_NAME, NAMESPACE, null, null,
null, null, null, null);
}
private void cleanUpServices() throws Exception {
api.deleteNamespacedService(CONFIG_WATCHER_IT_IMAGE, NAMESPACE, null, null, null, null, null, null);
api.deleteNamespacedService(SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME, NAMESPACE, null, null, null, null, null,
null);
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, null, true);
util.createAndWait(NAMESPACE, configMap, null);
}
else if (phase.equals(Phase.DELETE)) {
util.deleteAndWait(NAMESPACE, deployment, service, null);
util.deleteAndWait(NAMESPACE, configMap, null);
}
}
private WebClient.Builder builder() {

View File

@@ -18,20 +18,15 @@ package org.springframework.cloud.kubernetes.configuration.watcher;
import java.time.Duration;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import io.kubernetes.client.openapi.apis.AppsV1Api;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.apis.NetworkingV1Api;
import io.kubernetes.client.openapi.models.V1ConfigMap;
import io.kubernetes.client.openapi.models.V1ConfigMapBuilder;
import io.kubernetes.client.openapi.models.V1Deployment;
import io.kubernetes.client.openapi.models.V1Ingress;
import io.kubernetes.client.openapi.models.V1ReplicationController;
import io.kubernetes.client.openapi.models.V1Service;
import org.assertj.core.api.Assertions;
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;
@@ -41,14 +36,13 @@ import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util;
import org.springframework.http.HttpMethod;
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.K8SUtils.createApiClient;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.getPomVersion;
/**
* @author Ryan Baxter
@@ -57,26 +51,14 @@ class ActuatorRefreshRabbitMQIT {
private static final String CONFIG_WATCHER_IT_IMAGE = "spring-cloud-kubernetes-configuration-watcher-it";
private static final String SPRING_CLOUD_K8S_CONFIG_WATCHER_DEPLOYMENT_NAME = "spring-cloud-kubernetes-configuration-watcher-deployment";
private static final String SPRING_CLOUD_K8S_CONFIG_WATCHER_IT_DEPLOYMENT_NAME = "spring-cloud-kubernetes-configuration-watcher-it-deployment";
private static final String SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME = "spring-cloud-kubernetes-configuration-watcher";
private static final String NAMESPACE = "default";
private static final String RABBIT_MQ_CONTROLLER_NAME = "rabbitmq-controller";
private static CoreV1Api api;
private static AppsV1Api appsApi;
private static NetworkingV1Api networkingApi;
private static K8SUtils k8SUtils;
private static final K3sContainer K3S = Commons.container();
private static Util util;
@BeforeAll
static void beforeAll() throws Exception {
K3S.start();
@@ -86,13 +68,8 @@ class ActuatorRefreshRabbitMQIT {
Commons.validateImage(CONFIG_WATCHER_IT_IMAGE, K3S);
Commons.loadSpringCloudKubernetesImage(CONFIG_WATCHER_IT_IMAGE, K3S);
createApiClient(K3S.getKubeConfigYaml());
api = new CoreV1Api();
appsApi = new AppsV1Api();
k8SUtils = new K8SUtils(api, appsApi);
networkingApi = new NetworkingV1Api();
k8SUtils.setUp(NAMESPACE);
util = new Util(K3S);
util.setUp(NAMESPACE);
}
@AfterAll
@@ -102,25 +79,26 @@ class ActuatorRefreshRabbitMQIT {
}
@BeforeEach
void setup() throws Exception {
void setup() {
util.rabbitMq(NAMESPACE, Phase.CREATE);
app(Phase.CREATE);
configWatcher(Phase.CREATE);
}
deployRabbitMQ();
deployTestApp();
deployConfigWatcher();
// Check to make sure the controller deployment is ready
k8SUtils.waitForReplicationController(RABBIT_MQ_CONTROLLER_NAME, NAMESPACE);
waitForDeployment(SPRING_CLOUD_K8S_CONFIG_WATCHER_IT_DEPLOYMENT_NAME);
waitForDeployment(SPRING_CLOUD_K8S_CONFIG_WATCHER_DEPLOYMENT_NAME);
@AfterEach
void afterEach() {
util.rabbitMq(NAMESPACE, Phase.DELETE);
app(Phase.DELETE);
configWatcher(Phase.DELETE);
}
@Test
void testRefresh() throws Exception {
void testRefresh() {
// Create new configmap to trigger controller to signal app to refresh
V1ConfigMap configMap = new V1ConfigMapBuilder().editOrNewMetadata().withName(CONFIG_WATCHER_IT_IMAGE)
.addToLabels("spring.cloud.kubernetes.config", "true").endMetadata().addToData("foo", "hello world")
.build();
api.createNamespacedConfigMap(NAMESPACE, configMap, null, null, null, null);
util.createAndWait(NAMESPACE, configMap, null);
WebClient.Builder builder = builder();
WebClient serviceClient = builder.baseUrl("http://localhost:80/it").build();
@@ -132,115 +110,40 @@ class ActuatorRefreshRabbitMQIT {
return value[0];
});
Assertions.assertThat(value[0]).isTrue();
Assertions.assertTrue(value[0]);
util.deleteAndWait(NAMESPACE, configMap, null);
}
@AfterEach
void after() throws Exception {
api.deleteNamespacedService("rabbitmq-service", NAMESPACE, null, null, null, null, null, null);
api.deleteNamespacedService(CONFIG_WATCHER_IT_IMAGE, NAMESPACE, null, null, null, null, null, null);
api.deleteNamespacedService(SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME, NAMESPACE, null, null, null, null, null,
null);
private void app(Phase phase) {
V1Deployment deployment = (V1Deployment) util
.yaml("app-watcher/spring-cloud-kubernetes-configuration-watcher-it-bus-amqp-deployment.yaml");
V1Service service = (V1Service) util.yaml("app/spring-cloud-kubernetes-configuration-watcher-it-service.yaml");
V1Ingress ingress = (V1Ingress) util.yaml("app/spring-cloud-kubernetes-configuration-watcher-it-ingress.yaml");
appsApi.deleteNamespacedDeployment(SPRING_CLOUD_K8S_CONFIG_WATCHER_DEPLOYMENT_NAME, NAMESPACE, null, null, null,
null, null, null);
appsApi.deleteNamespacedDeployment(SPRING_CLOUD_K8S_CONFIG_WATCHER_IT_DEPLOYMENT_NAME, NAMESPACE, null, null,
null, null, null, null);
try {
api.deleteNamespacedReplicationController(RABBIT_MQ_CONTROLLER_NAME, NAMESPACE, null, null, null, null,
null, null);
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
catch (Exception e) {
// swallowing this exception, delete does actually happen, it's a problem
// downstream from the k8s client; see:
// https://github.com/kubernetes-client/java/issues/86#issuecomment-411234259
else if (phase.equals(Phase.DELETE)) {
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
networkingApi.deleteNamespacedIngress("it-ingress", NAMESPACE, null, null, null, null, null, null);
api.deleteNamespacedConfigMap(SPRING_CLOUD_K8S_CONFIG_WATCHER_APP_NAME, NAMESPACE, null, null, null, null, null,
null);
api.deleteNamespacedConfigMap(CONFIG_WATCHER_IT_IMAGE, NAMESPACE, null, null, null, null, null, null);
// Check to make sure the controller deployment is deleted
k8SUtils.waitForDeploymentToBeDeleted(SPRING_CLOUD_K8S_CONFIG_WATCHER_DEPLOYMENT_NAME, NAMESPACE);
k8SUtils.waitForDeploymentToBeDeleted(SPRING_CLOUD_K8S_CONFIG_WATCHER_IT_DEPLOYMENT_NAME, NAMESPACE);
}
private void deployTestApp() throws Exception {
appsApi.createNamespacedDeployment(NAMESPACE, getItDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getItAppService(), null, null, null, null);
private void configWatcher(Phase phase) {
V1Deployment deployment = (V1Deployment) util
.yaml("app-watcher/spring-cloud-kubernetes-configuration-watcher-bus-amqp-deployment.yaml");
V1Service service = (V1Service) util
.yaml("config-watcher/spring-cloud-kubernetes-configuration-watcher-service.yaml");
V1ConfigMap configMap = (V1ConfigMap) util
.yaml("config-watcher/spring-cloud-kubernetes-configuration-watcher-configmap.yaml");
V1Ingress ingress = getItIngress();
networkingApi.createNamespacedIngress(NAMESPACE, ingress, null, null, null, null);
k8SUtils.waitForIngress(ingress.getMetadata().getName(), NAMESPACE);
}
private void deployConfigWatcher() throws Exception {
api.createNamespacedConfigMap(NAMESPACE, getConfigWatcherConfigMap(), null, null, null, null);
appsApi.createNamespacedDeployment(NAMESPACE, getConfigWatcherDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getConfigWatcherService(), null, null, null, null);
}
private void deployRabbitMQ() throws Exception {
api.createNamespacedService(NAMESPACE, getRabbitMQService(), null, null, null, null);
String[] image = getRabbitMQReplicationController().getSpec().getTemplate().getSpec().getContainers().get(0)
.getImage().split(":");
Commons.pullImage(image[0], image[1], K3S);
Commons.loadImage(image[0], image[1], "rabbitmq", K3S);
api.createNamespacedReplicationController(NAMESPACE, getRabbitMQReplicationController(), null, null, null,
null);
}
private V1Deployment getConfigWatcherDeployment() throws Exception {
V1Deployment deployment = (V1Deployment) K8SUtils.readYamlFromClasspath(
"app-watcher/spring-cloud-kubernetes-configuration-watcher-bus-amqp-deployment.yaml");
String image = K8SUtils.getImageFromDeployment(deployment) + ":" + getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
private V1Deployment getItDeployment() throws Exception {
String urlString = "app-watcher/spring-cloud-kubernetes-configuration-watcher-it-bus-amqp-deployment.yaml";
V1Deployment deployment = (V1Deployment) K8SUtils.readYamlFromClasspath(urlString);
String image = K8SUtils.getImageFromDeployment(deployment) + ":" + getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
private V1Service getItAppService() throws Exception {
return (V1Service) K8SUtils
.readYamlFromClasspath("app/spring-cloud-kubernetes-configuration-watcher-it-service.yaml");
}
private V1Service getConfigWatcherService() throws Exception {
return (V1Service) K8SUtils
.readYamlFromClasspath("config-watcher/spring-cloud-kubernetes-configuration-watcher-service.yaml");
}
private V1ConfigMap getConfigWatcherConfigMap() throws Exception {
return (V1ConfigMap) K8SUtils
.readYamlFromClasspath("config-watcher/spring-cloud-kubernetes-configuration-watcher-configmap.yaml");
}
private V1Ingress getItIngress() throws Exception {
return (V1Ingress) K8SUtils
.readYamlFromClasspath("app/spring-cloud-kubernetes-configuration-watcher-it-ingress.yaml");
}
private V1ReplicationController getRabbitMQReplicationController() throws Exception {
return (V1ReplicationController) K8SUtils.readYamlFromClasspath("rabbitmq/rabbitmq-controller.yaml");
}
private V1Service getRabbitMQService() throws Exception {
return (V1Service) K8SUtils.readYamlFromClasspath("rabbitmq/rabbitmq-service.yaml");
}
private void waitForDeployment(String deploymentName) {
await().pollInterval(Duration.ofSeconds(3)).atMost(600, TimeUnit.SECONDS)
.until(() -> k8SUtils.isDeploymentReady(deploymentName, NAMESPACE));
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, null, true);
util.createAndWait(NAMESPACE, configMap, null);
}
else if (phase.equals(Phase.DELETE)) {
util.deleteAndWait(NAMESPACE, deployment, service, null);
util.deleteAndWait(NAMESPACE, configMap, null);
}
}
private WebClient.Builder builder() {

View File

@@ -1,54 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: kafka
component: kafka-broker
name: kafka-broker
spec:
replicas: 1
selector:
matchLabels:
app: kafka
component: kafka-broker
template:
metadata:
labels:
app: kafka
component: kafka-broker
spec:
# otherwise we will get an env var "KAFKA_PORT" (from service name: "kafka" and appended with "_PORT")
# and this will cause this problem: https://github.com/confluentinc/cp-docker-images/blob/master/debian/kafka/include/etc/confluent/docker/configure#L58-L62
# Another solution is to rename the service.
enableServiceLinks: false
containers:
- name: kafka
image: confluentinc/cp-kafka:7.2.1
ports:
- containerPort: 9092
env:
- name: KAFKA_LISTENERS
value: "INTERNAL://0.0.0.0:9092,OUTSIDE://0.0.0.0:9094"
- name: KAFKA_LISTENER_SECURITY_PROTOCOL_MAP
value: "INTERNAL:PLAINTEXT,OUTSIDE:PLAINTEXT"
- name: KAFKA_ADVERTISED_LISTENERS
value: "INTERNAL://kafka:9092,OUTSIDE://localhost:9094"
- name: KAFKA_INTER_BROKER_LISTENER_NAME
value: "INTERNAL"
- name: KAFKA_ADVERTISED_HOST_NAME
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: KAFKA_ZOOKEEPER_CONNECT
value: zookeeper:2181
# we have enabled auto creation of topics and when this happens there is a replication factor of 3
# that is set automatically. Since we don't have that many, producers will fail.
# This setting ensures that there is just one replication
- name: KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR
value: "1"

View File

@@ -1,16 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: kafka
labels:
app: kafka
component: kafka-broker
spec:
ports:
- port: 9092
name: kafka-port
targetPort: 9092
protocol: TCP
selector:
app: kafka
component: kafka-broker

View File

@@ -1,36 +0,0 @@
apiVersion: v1
kind: ReplicationController
metadata:
labels:
component: rabbitmq
name: rabbitmq-controller
spec:
replicas: 1
template:
metadata:
labels:
app: taskQueue
component: rabbitmq
spec:
containers:
- image: rabbitmq:3-management
name: rabbitmq
ports:
- name: amqp
containerPort: 5672
- name: http-stats
containerPort: 15672
readinessProbe:
httpGet:
port: 15672
path: /api/healthchecks/node
httpHeaders:
- name: Authorization
value: Basic Z3Vlc3Q6Z3Vlc3Q=
# livenessProbe:
# httpGet:
# port: 15672
# path: /api/healthchecks/node
# httpHeaders:
# - name: Authorization
# value: Basic Z3Vlc3Q6Z3Vlc3Q=

View File

@@ -1,17 +0,0 @@
apiVersion: v1
kind: Service
metadata:
labels:
component: rabbitmq
name: rabbitmq-service
spec:
ports:
- port: 5672
name: amqp
targetPort: 5672
- port: 15672
name: http-stats
targetPort: 15672
selector:
app: taskQueue
component: rabbitmq

View File

@@ -1,31 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: kafka
component: zookeeper
name: zookeeper
spec:
replicas: 1
selector:
matchLabels:
app: kafka
component: zookeeper
template:
metadata:
labels:
app: kafka
component: zookeeper
spec:
containers:
- name: zookeeper
image: confluentinc/cp-zookeeper:7.2.1
ports:
- containerPort: 2181
env:
- name: ZOOKEEPER_ID
value: "1"
- name: ZOOKEEPER_SERVER_1
value: zookeeper
- name: ZOOKEEPER_CLIENT_PORT
value: 2181

View File

@@ -1,16 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: zookeeper
labels:
app: kafka
component: zookeeper
spec:
ports:
- port: 2181
name: zookeeper-port
targetPort: 2181
protocol: TCP
selector:
app: kafka
component: zookeeper

View File

@@ -1,26 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: spring-cloud-kubernetes-core-k8s-client-it
name: spring-cloud-kubernetes-core-k8s-client-it-deployment
spec:
replicas: 1
selector:
matchLabels:
app: spring-cloud-kubernetes-core-k8s-client-it
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: spring-cloud-kubernetes-core-k8s-client-it
spec:
serviceAccountName: spring-cloud-kubernetes-serviceaccount
containers:
- image: springcloud/spring-cloud-kubernetes-core-k8s-client-it:3.0.0-SNAPSHOT
imagePullPolicy: IfNotPresent
name: spring-cloud-kubernetes-core-k8s-client-it
resources: {}
status: {}

View File

@@ -1,18 +0,0 @@
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: spring-cloud-kubernetes-core-k8s-client-it
name: spring-cloud-kubernetes-core-k8s-client-it
spec:
ports:
- name: 80-8080
port: 80
protocol: TCP
targetPort: 8080
selector:
app: spring-cloud-kubernetes-core-k8s-client-it
type: ClusterIP
status:
loadBalancer: {}

View File

@@ -1,19 +0,0 @@
apiVersion: skaffold/v2alpha3
kind: Config
metadata:
name: spring-cloud-kubernetes-core-k8s-client-it
build:
artifacts:
- image: springcloud/spring-cloud-kubernetes-core-k8s-client-it
custom:
buildCommand: "../../mvnw clean install -Pskaffold"
dependencies:
paths:
- src
- pom.xml
deploy:
kubectl:
manifests:
- k8s/deployment-it.yaml
- k8s/service-it.yaml
- ../permissions.yaml

View File

@@ -20,9 +20,6 @@ import java.time.Duration;
import java.util.Map;
import java.util.Objects;
import io.kubernetes.client.openapi.apis.AppsV1Api;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.apis.NetworkingV1Api;
import io.kubernetes.client.openapi.models.V1Deployment;
import io.kubernetes.client.openapi.models.V1Ingress;
import io.kubernetes.client.openapi.models.V1Service;
@@ -35,65 +32,42 @@ import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.createApiClient;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.getPomVersion;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
class ActuatorEndpointIT {
private static final String SPRING_CLOUD_K8S_CLIENT_IT_DEPLOYMENT_NAME = "spring-cloud-kubernetes-core-k8s-client-it-deployment";
private static final String K8S_CONFIG_CLIENT_IT_NAME = "spring-cloud-kubernetes-core-k8s-client-it-deployment";
private static final String K8S_CONFIG_CLIENT_IT_SERVICE_NAME = "spring-cloud-kubernetes-core-k8s-client-it";
private static final String NAMESPACE = "default";
private static CoreV1Api api;
private static AppsV1Api appsApi;
private static K8SUtils k8SUtils;
private static NetworkingV1Api networkingApi;
private static final K3sContainer K3S = Commons.container();
private static Util util;
@BeforeAll
static void beforeAll() throws Exception {
K3S.start();
Commons.validateImage(K8S_CONFIG_CLIENT_IT_SERVICE_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(K8S_CONFIG_CLIENT_IT_SERVICE_NAME, K3S);
createApiClient(K3S.getKubeConfigYaml());
api = new CoreV1Api();
appsApi = new AppsV1Api();
networkingApi = new NetworkingV1Api();
k8SUtils = new K8SUtils(api, appsApi);
k8SUtils.setUp(NAMESPACE);
deployCoreK8sClientIt();
// Check to make sure the controller deployment is ready
k8SUtils.waitForDeployment(SPRING_CLOUD_K8S_CLIENT_IT_DEPLOYMENT_NAME, NAMESPACE);
util = new Util(K3S);
util.setUp(NAMESPACE);
coreK8sClientIt(Phase.CREATE);
}
@AfterAll
static void after() throws Exception {
Commons.cleanUp(K8S_CONFIG_CLIENT_IT_SERVICE_NAME, K3S);
appsApi.deleteCollectionNamespacedDeployment(NAMESPACE, null, null, null,
"metadata.name=" + K8S_CONFIG_CLIENT_IT_NAME, null, null, null, null, null, null, null, null, null);
api.deleteNamespacedService(K8S_CONFIG_CLIENT_IT_SERVICE_NAME, NAMESPACE, null, null, null, null, null, null);
networkingApi.deleteNamespacedIngress("it-ingress", NAMESPACE, null, null, null, null, null, null);
static void afterAll() {
coreK8sClientIt(Phase.DELETE);
}
@Test
@@ -152,30 +126,18 @@ class ActuatorEndpointIT {
assertThat(kubernetes.containsKey("serviceAccount")).isTrue();
}
private static void deployCoreK8sClientIt() throws Exception {
appsApi.createNamespacedDeployment(NAMESPACE, getCoreK8sClientItDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getCoreK8sClientItService(), null, null, null, null);
private static void coreK8sClientIt(Phase phase) {
V1Deployment deployment = (V1Deployment) util
.yaml("spring-cloud-kubernetes-core-k8s-client-it-deployment.yaml");
V1Service service = (V1Service) util.yaml("spring-cloud-kubernetes-core-k8s-client-it-service.yaml");
V1Ingress ingress = (V1Ingress) util.yaml("spring-cloud-kubernetes-core-k8s-client-it-ingress.yaml");
V1Ingress ingress = getCoreK8sClientItIngress();
networkingApi.createNamespacedIngress(NAMESPACE, ingress, null, null, null, null);
k8SUtils.waitForIngress(ingress.getMetadata().getName(), NAMESPACE);
}
private static V1Deployment getCoreK8sClientItDeployment() throws Exception {
V1Deployment deployment = (V1Deployment) K8SUtils
.readYamlFromClasspath("spring-cloud-kubernetes-core-k8s-client-it-deployment.yaml");
String image = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage() + ":"
+ getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
private static V1Service getCoreK8sClientItService() throws Exception {
return (V1Service) K8SUtils.readYamlFromClasspath("spring-cloud-kubernetes-core-k8s-client-it-service.yaml");
}
private static V1Ingress getCoreK8sClientItIngress() throws Exception {
return (V1Ingress) K8SUtils.readYamlFromClasspath("spring-cloud-kubernetes-core-k8s-client-it-ingress.yaml");
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
else {
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
private WebClient.Builder builder() {

View File

@@ -1,26 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: spring-cloud-kubernetes-discoveryclient-it
name: spring-cloud-kubernetes-discoveryclient-it-deployment
spec:
replicas: 1
selector:
matchLabels:
app: spring-cloud-kubernetes-discoveryclient-it
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: spring-cloud-kubernetes-discoveryclient-it
spec:
serviceAccountName: spring-cloud-kubernetes-serviceaccount
containers:
- image: springcloud/spring-cloud-kubernetes-discoveryclient-it:3.0.0-SNAPSHOT
imagePullPolicy: IfNotPresent
name: spring-cloud-kubernetes-discoveryclient-it
resources: {}
status: {}

View File

@@ -1,18 +0,0 @@
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: spring-cloud-kubernetes-discoveryclient-it
name: spring-cloud-kubernetes-discoveryclient-it
spec:
ports:
- name: 80-8080
port: 80
protocol: TCP
targetPort: 8080
selector:
app: spring-cloud-kubernetes-discoveryclient-it
type: ClusterIP
status:
loadBalancer: {}

View File

@@ -17,15 +17,10 @@
package org.springframework.cloud.kubernetes.discoveryclient.it;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
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.apis.NetworkingV1Api;
import io.kubernetes.client.openapi.apis.RbacAuthorizationV1Api;
import io.kubernetes.client.openapi.models.V1ClusterRoleBinding;
import io.kubernetes.client.openapi.models.V1Container;
@@ -33,11 +28,7 @@ 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.V1Namespace;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1Service;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
@@ -49,7 +40,8 @@ import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.discovery.KubernetesServiceInstance;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpMethod;
@@ -57,48 +49,28 @@ import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.createApiClient;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.getPomVersion;
/**
* @author mbialkowski1
*/
class DiscoveryClientFilterNamespaceIT {
private static final Log LOG = LogFactory.getLog(DiscoveryClientFilterNamespaceIT.class);
private static final String DISCOVERY_SERVER_DEPLOYMENT_NAME = "spring-cloud-kubernetes-discoveryserver-deployment";
private static final String DISCOVERY_SERVER_APP_NAME = "spring-cloud-kubernetes-discoveryserver";
private static final String SPRING_CLOUD_K8S_DISCOVERY_CLIENT_DEPLOYMENT_NAME = "spring-cloud-kubernetes-discoveryclient-it-deployment";
private static final String SPRING_CLOUD_K8S_DISCOVERY_CLIENT_APP_NAME = "spring-cloud-kubernetes-discoveryclient-it";
private static final String MOCK_DEPLOYMENT_NAME = "wiremock-deployment";
private static final String MOCK_CLIENT_APP_NAME = "wiremock";
private static final String MOCK_IMAGE_NAME = "wiremock";
private static final String NAMESPACE = "default";
private static final String NAMESPACE_LEFT = "left-namespace-k8s-client";
private static final String NAMESPACE_LEFT = "left";
private static final String NAMESPACE_RIGHT = "right-namespace-k8s-client";
private static CoreV1Api api;
private static AppsV1Api appsApi;
private static NetworkingV1Api networkingApi;
private static RbacAuthorizationV1Api authApi;
private static K8SUtils k8SUtils;
private static final String NAMESPACE_RIGHT = "right";
private static final K3sContainer K3S = Commons.container();
private static Util util;
private static RbacAuthorizationV1Api rbacApi;
@BeforeAll
static void beforeAll() throws Exception {
K3S.start();
@@ -109,97 +81,56 @@ class DiscoveryClientFilterNamespaceIT {
Commons.validateImage(SPRING_CLOUD_K8S_DISCOVERY_CLIENT_APP_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(SPRING_CLOUD_K8S_DISCOVERY_CLIENT_APP_NAME, K3S);
String[] mockImage = K8SUtils.getImageFromDeployment(getMockServiceDeployment()).split(":");
Commons.pullImage(mockImage[0], mockImage[1], K3S);
Commons.loadImage(mockImage[0], mockImage[1], MOCK_IMAGE_NAME, K3S);
util = new Util(K3S);
rbacApi = new RbacAuthorizationV1Api();
util.createNamespace(NAMESPACE_LEFT);
util.createNamespace(NAMESPACE_RIGHT);
util.setUp(NAMESPACE);
createApiClient(K3S.getKubeConfigYaml());
api = new CoreV1Api();
appsApi = new AppsV1Api();
networkingApi = new NetworkingV1Api();
authApi = new RbacAuthorizationV1Api();
k8SUtils = new K8SUtils(api, appsApi);
k8SUtils.setUp(NAMESPACE);
deployDiscoveryServer();
// Check to make sure the discovery server deployment is ready
k8SUtils.waitForDeployment(DISCOVERY_SERVER_DEPLOYMENT_NAME, NAMESPACE);
// Check to see if endpoint is ready
k8SUtils.waitForEndpointReady(DISCOVERY_SERVER_APP_NAME, NAMESPACE);
V1ClusterRoleBinding clusterRole = (V1ClusterRoleBinding) util
.yaml("namespace-filter/cluster-admin-serviceaccount-role.yaml");
rbacApi.createClusterRoleBinding(clusterRole, null, null, null, null);
discoveryServer(Phase.CREATE);
}
@AfterAll
static void afterAll() throws Exception {
Commons.cleanUp(DISCOVERY_SERVER_APP_NAME, K3S);
Commons.cleanUp(SPRING_CLOUD_K8S_DISCOVERY_CLIENT_APP_NAME, K3S);
Commons.cleanUpDownloadedImage(MOCK_IMAGE_NAME);
appsApi.deleteCollectionNamespacedDeployment(NAMESPACE, null, null, null,
"metadata.name=" + DISCOVERY_SERVER_DEPLOYMENT_NAME, null, null, null, null, null, null, null, null,
null);
api.deleteNamespacedService(DISCOVERY_SERVER_APP_NAME, NAMESPACE, null, null, null, null, null, null);
networkingApi.deleteNamespacedIngress("discoveryserver-ingress", NAMESPACE, null, null, null, null, null, null);
discoveryServer(Phase.DELETE);
util.deleteNamespace(NAMESPACE_LEFT);
util.deleteNamespace(NAMESPACE_RIGHT);
}
@AfterEach
void afterEach() throws ApiException {
cleanup();
void afterEach() {
util.wiremock(NAMESPACE_LEFT, "/wiremock-" + NAMESPACE_LEFT, Phase.DELETE);
util.wiremock(NAMESPACE_RIGHT, "/wiremock-" + NAMESPACE_RIGHT, Phase.DELETE);
discoveryIt(Phase.DELETE);
}
@Test
void testDiscoveryClient() throws Exception {
deploySampleAppInNamespace(NAMESPACE_LEFT);
deploySampleAppInNamespace(NAMESPACE_RIGHT);
deployDiscoveryIt();
void testDiscoveryClient() {
util.wiremock(NAMESPACE_LEFT, "/wiremock-" + NAMESPACE_LEFT, Phase.CREATE);
util.wiremock(NAMESPACE_RIGHT, "/wiremock-" + NAMESPACE_RIGHT, Phase.CREATE);
discoveryIt(Phase.CREATE);
testLoadBalancer();
testHealth();
}
private void cleanup() throws ApiException {
appsApi.deleteCollectionNamespacedDeployment(NAMESPACE, null, null, null,
"metadata.name=" + SPRING_CLOUD_K8S_DISCOVERY_CLIENT_DEPLOYMENT_NAME, null, null, null, null, null,
null, null, null, null);
api.deleteNamespacedService(SPRING_CLOUD_K8S_DISCOVERY_CLIENT_APP_NAME, NAMESPACE, null, null, null, null, null,
null);
networkingApi.deleteNamespacedIngress("it-ingress", NAMESPACE, null, null, null, null, null, null);
appsApi.deleteCollectionNamespacedDeployment(NAMESPACE_LEFT, null, null, null,
"metadata.name=" + MOCK_DEPLOYMENT_NAME, null, null, null, null, null, null, null, null, null);
appsApi.deleteCollectionNamespacedDeployment(NAMESPACE_RIGHT, null, null, null,
"metadata.name=" + MOCK_DEPLOYMENT_NAME, null, null, null, null, null, null, null, null, null);
api.deleteNamespacedService(MOCK_CLIENT_APP_NAME, NAMESPACE_LEFT, null, null, null, null, null, null);
api.deleteNamespacedService(MOCK_CLIENT_APP_NAME, NAMESPACE_RIGHT, null, null, null, null, null, null);
networkingApi.deleteNamespacedIngress("wiremock-ingress", NAMESPACE_LEFT, null, null, null, null, null, null);
networkingApi.deleteNamespacedIngress("wiremock-ingress", NAMESPACE_RIGHT, null, null, null, null, null, null);
authApi.deleteClusterRoleBinding("admin-default-k8s-client", null, null, null, null, null, null);
api.deleteNamespace(NAMESPACE_LEFT, null, null, null, null, null, null);
api.deleteNamespace(NAMESPACE_RIGHT, null, null, null, null, null, null);
}
private void testLoadBalancer() {
// Check to make sure the controller deployment is ready
k8SUtils.waitForDeployment(SPRING_CLOUD_K8S_DISCOVERY_CLIENT_DEPLOYMENT_NAME, NAMESPACE);
WebClient.Builder builder = builder();
WebClient serviceClient = builder.baseUrl("http://localhost:80/discoveryclient-it/services").build();
String[] result = serviceClient.method(HttpMethod.GET).retrieve().bodyToMono(String[].class)
.retryWhen(retrySpec()).block();
LOG.info("Services: " + Arrays.toString(result));
assertThat(result).containsAnyOf("wiremock");
assertThat(result).containsAnyOf("service-wiremock");
// ServiceInstance
WebClient serviceInstanceClient = builder.baseUrl("http://localhost:80/discoveryclient-it/service/wiremock")
.build();
WebClient serviceInstanceClient = builder
.baseUrl("http://localhost:80/discoveryclient-it/service/service-wiremock").build();
List<KubernetesServiceInstance> serviceInstances = serviceInstanceClient.method(HttpMethod.GET).retrieve()
.bodyToMono(new ParameterizedTypeReference<List<KubernetesServiceInstance>>() {
}).retryWhen(retrySpec()).block();
@@ -227,119 +158,48 @@ class DiscoveryClientFilterNamespaceIT {
assertThat(discoveryComposite.get("status")).isEqualTo("UP");
}
private void deployDiscoveryIt() throws Exception {
appsApi.createNamespacedDeployment(NAMESPACE, getDiscoveryItDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getDiscoveryService(), null, null, null, null);
private void discoveryIt(Phase phase) {
V1Deployment deployment = (V1Deployment) util
.yaml("client/spring-cloud-kubernetes-discoveryclient-it-deployment.yaml");
V1Service service = (V1Service) util.yaml("client/spring-cloud-kubernetes-discoveryclient-it-service.yaml");
V1Ingress ingress = (V1Ingress) util.yaml("client/spring-cloud-kubernetes-discoveryclient-it-ingress.yaml");
if (phase.equals(Phase.CREATE)) {
// add namespaces filter property for left namespace
var env = new V1EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0")
.withValue(NAMESPACE_LEFT).build();
var container = deployment.getSpec().getTemplate().getSpec().getContainers().get(0);
container.setEnv(List.of(env));
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
else {
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
V1Ingress ingress = getDiscoveryItIngress();
networkingApi.createNamespacedIngress(NAMESPACE, ingress, null, null, null, null);
k8SUtils.waitForIngress(ingress.getMetadata().getName(), NAMESPACE);
}
private static V1Deployment getDiscoveryItDeployment() throws Exception {
V1Deployment deployment = (V1Deployment) K8SUtils
.readYamlFromClasspath("client/spring-cloud-kubernetes-discoveryclient-it-deployment.yaml");
private static void discoveryServer(Phase phase) {
// add namespaces filter property for left namespace
var env = new V1EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0")
.withValue(NAMESPACE_LEFT).build();
var container = deployment.getSpec().getTemplate().getSpec().getContainers().get(0);
container.setEnv(List.of(env));
V1Deployment deployment = (V1Deployment) util
.yaml("server/spring-cloud-kubernetes-discoveryserver-deployment.yaml");
V1Service service = (V1Service) util.yaml("server/spring-cloud-kubernetes-discoveryserver-service.yaml");
V1Ingress ingress = (V1Ingress) util.yaml("server/spring-cloud-kubernetes-discoveryserver-ingress.yaml");
String image = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage() + ":"
+ getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
if (phase.equals(Phase.CREATE)) {
// add namespaces filter property for left namespace
// setup all-namespaces property
V1EnvVar env = new V1EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_ALL_NAMESPACES")
.withValue("TRUE").build();
V1Container container = deployment.getSpec().getTemplate().getSpec().getContainers().get(0);
container.setEnv(List.of(env));
private static void deployDiscoveryServer() throws Exception {
V1ClusterRoleBinding clusterRoleBinding = getClusterRoleBinding();
authApi.createClusterRoleBinding(clusterRoleBinding, null, null, null, null);
appsApi.createNamespacedDeployment(NAMESPACE, getDiscoveryServerDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getDiscoveryServerService(), null, null, null, null);
V1Ingress ingress = getDiscoveryServerIngress();
networkingApi.createNamespacedIngress(NAMESPACE, ingress, null, null, null, null);
k8SUtils.waitForIngress(ingress.getMetadata().getName(), NAMESPACE);
}
private static void deploySampleAppInNamespace(final String namespace) throws Exception {
V1Namespace v1Namespace = new V1Namespace();
V1ObjectMeta meta = new V1ObjectMeta();
meta.setName(namespace);
v1Namespace.setMetadata(meta);
api.createNamespace(v1Namespace, null, null, null, null);
V1Deployment deployment = getMockServiceDeployment();
deployment.getMetadata().setNamespace(namespace);
appsApi.createNamespacedDeployment(namespace, deployment, null, null, null, null);
V1Service service = getMockServiceService();
service.getMetadata().setNamespace(namespace);
api.createNamespacedService(namespace, service, null, null, null, null);
V1Ingress ingress = getMockIngress();
ingress.getMetadata().setNamespace(namespace);
ingress.getSpec().getRules().get(0).getHttp().getPaths().get(0).setPath("/wiremock-" + namespace);
networkingApi.createNamespacedIngress(namespace, ingress, null, null, null, null);
k8SUtils.waitForIngress(ingress.getMetadata().getName(), namespace);
}
private static V1Deployment getDiscoveryServerDeployment() throws Exception {
V1Deployment deployment = (V1Deployment) K8SUtils
.readYamlFromClasspath("server/spring-cloud-kubernetes-discoveryserver-deployment.yaml");
String image = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage() + ":"
+ getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
// setup all-namespaces property
V1EnvVar env = new V1EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_ALL_NAMESPACES")
.withValue("TRUE").build();
V1Container container = deployment.getSpec().getTemplate().getSpec().getContainers().get(0);
container.setEnv(List.of(env));
return deployment;
}
private static V1Ingress getDiscoveryServerIngress() throws Exception {
return (V1Ingress) K8SUtils
.readYamlFromClasspath("server/spring-cloud-kubernetes-discoveryserver-ingress.yaml");
}
private static V1Service getDiscoveryServerService() throws Exception {
return (V1Service) K8SUtils
.readYamlFromClasspath("server/spring-cloud-kubernetes-discoveryserver-service.yaml");
}
private static V1Ingress getDiscoveryItIngress() throws Exception {
return (V1Ingress) K8SUtils
.readYamlFromClasspath("client/spring-cloud-kubernetes-discoveryclient-it-ingress.yaml");
}
private static V1Service getDiscoveryService() throws Exception {
return (V1Service) K8SUtils
.readYamlFromClasspath("client/spring-cloud-kubernetes-discoveryclient-it-service.yaml");
}
private static V1ClusterRoleBinding getClusterRoleBinding() throws Exception {
return (V1ClusterRoleBinding) K8SUtils
.readYamlFromClasspath("namespace-filter/cluster-admin-serviceaccount-role.yaml");
}
private static V1Deployment getMockServiceDeployment() throws Exception {
return (V1Deployment) K8SUtils.readYamlFromClasspath("wiremock/discovery-wiremock-deployment.yaml");
}
private static V1Service getMockServiceService() throws Exception {
return (V1Service) K8SUtils.readYamlFromClasspath("wiremock/discovery-wiremock-service.yaml");
}
private static V1Ingress getMockIngress() throws Exception {
return (V1Ingress) K8SUtils.readYamlFromClasspath("wiremock/discovery-wiremock-ingress.yaml");
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
else {
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
private WebClient.Builder builder() {

View File

@@ -21,17 +21,10 @@ import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
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.apis.NetworkingV1Api;
import io.kubernetes.client.openapi.models.V1Deployment;
import io.kubernetes.client.openapi.models.V1Ingress;
import io.kubernetes.client.openapi.models.V1Service;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.testcontainers.k3s.K3sContainer;
@@ -40,7 +33,8 @@ import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.native_client.Util;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpMethod;
@@ -48,36 +42,22 @@ import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.createApiClient;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.getPomVersion;
/**
* @author Ryan Baxter
*/
class DiscoveryClientIT {
private static final Log LOG = LogFactory.getLog(DiscoveryClientIT.class);
private static final String DISCOVERY_SERVER_DEPLOYMENT_NAME = "spring-cloud-kubernetes-discoveryserver-deployment";
private static final String DISCOVERY_SERVER_APP_NAME = "spring-cloud-kubernetes-discoveryserver";
private static final String SPRING_CLOUD_K8S_DISCOVERY_CLIENT_DEPLOYMENT_NAME = "spring-cloud-kubernetes-discoveryclient-it-deployment";
private static final String SPRING_CLOUD_K8S_DISCOVERY_CLIENT_APP_NAME = "spring-cloud-kubernetes-discoveryclient-it";
private static final String NAMESPACE = "default";
private static CoreV1Api api;
private static AppsV1Api appsApi;
private static NetworkingV1Api networkingApi;
private static K8SUtils k8SUtils;
private static final K3sContainer K3S = Commons.container();
private static Util util;
@BeforeAll
static void beforeAll() throws Exception {
K3S.start();
@@ -87,21 +67,10 @@ class DiscoveryClientIT {
Commons.validateImage(SPRING_CLOUD_K8S_DISCOVERY_CLIENT_APP_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(SPRING_CLOUD_K8S_DISCOVERY_CLIENT_APP_NAME, K3S);
util = new Util(K3S);
util.setUp(NAMESPACE);
discoveryServer(Phase.CREATE);
createApiClient(K3S.getKubeConfigYaml());
api = new CoreV1Api();
appsApi = new AppsV1Api();
networkingApi = new NetworkingV1Api();
k8SUtils = new K8SUtils(api, appsApi);
k8SUtils.setUp(NAMESPACE);
deployDiscoveryServer();
// Check to make sure the discovery server deployment is ready
k8SUtils.waitForDeployment(DISCOVERY_SERVER_DEPLOYMENT_NAME, NAMESPACE);
// Check to see if endpoint is ready
k8SUtils.waitForEndpointReady(DISCOVERY_SERVER_APP_NAME, NAMESPACE);
}
@AfterAll
@@ -109,45 +78,23 @@ class DiscoveryClientIT {
Commons.cleanUp(DISCOVERY_SERVER_APP_NAME, K3S);
Commons.cleanUp(SPRING_CLOUD_K8S_DISCOVERY_CLIENT_APP_NAME, K3S);
appsApi.deleteCollectionNamespacedDeployment(NAMESPACE, null, null, null,
"metadata.name=" + DISCOVERY_SERVER_DEPLOYMENT_NAME, null, null, null, null, null, null, null, null,
null);
api.deleteNamespacedService(DISCOVERY_SERVER_APP_NAME, NAMESPACE, null, null, null, null, null, null);
networkingApi.deleteNamespacedIngress("discoveryserver-ingress", NAMESPACE, null, null, null, null, null, null);
}
@AfterEach
void afterEach() throws ApiException {
cleanup();
discoveryServer(Phase.DELETE);
discoveryIt(Phase.DELETE);
}
@Test
void testDiscoveryClient() throws Exception {
deployDiscoveryIt();
void testDiscoveryClient() {
discoveryIt(Phase.CREATE);
testLoadBalancer();
testHealth();
}
private void cleanup() throws ApiException {
appsApi.deleteCollectionNamespacedDeployment(NAMESPACE, null, null, null,
"metadata.name=" + SPRING_CLOUD_K8S_DISCOVERY_CLIENT_DEPLOYMENT_NAME, null, null, null, null, null,
null, null, null, null);
api.deleteNamespacedService(SPRING_CLOUD_K8S_DISCOVERY_CLIENT_APP_NAME, NAMESPACE, null, null, null, null, null,
null);
networkingApi.deleteNamespacedIngress("it-ingress", NAMESPACE, null, null, null, null, null, null);
}
private void testLoadBalancer() {
// Check to make sure the controller deployment is ready
k8SUtils.waitForDeployment(SPRING_CLOUD_K8S_DISCOVERY_CLIENT_DEPLOYMENT_NAME, NAMESPACE);
WebClient.Builder builder = builder();
WebClient serviceClient = builder.baseUrl("http://localhost:80/discoveryclient-it/services").build();
String[] result = serviceClient.method(HttpMethod.GET).retrieve().bodyToMono(String[].class)
.retryWhen(retrySpec()).block();
LOG.info("Services: " + Arrays.toString(result));
assertThat(Arrays.stream(result).anyMatch("spring-cloud-kubernetes-discoveryserver"::equalsIgnoreCase))
.isTrue();
@@ -170,60 +117,32 @@ class DiscoveryClientIT {
assertThat(discoveryComposite.get("status")).isEqualTo("UP");
}
private void deployDiscoveryIt() throws Exception {
appsApi.createNamespacedDeployment(NAMESPACE, getDiscoveryItDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getDiscoveryService(), null, null, null, null);
private static void discoveryIt(Phase phase) {
V1Deployment deployment = (V1Deployment) util
.yaml("client/spring-cloud-kubernetes-discoveryclient-it-deployment.yaml");
V1Service service = (V1Service) util.yaml("client/spring-cloud-kubernetes-discoveryclient-it-service.yaml");
V1Ingress ingress = (V1Ingress) util.yaml("client/spring-cloud-kubernetes-discoveryclient-it-ingress.yaml");
V1Ingress ingress = getDiscoveryItIngress();
networkingApi.createNamespacedIngress(NAMESPACE, ingress, null, null, null, null);
k8SUtils.waitForIngress(ingress.getMetadata().getName(), NAMESPACE);
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
else {
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
private V1Deployment getDiscoveryItDeployment() throws Exception {
V1Deployment deployment = (V1Deployment) k8SUtils
.readYamlFromClasspath("client/spring-cloud-kubernetes-discoveryclient-it-deployment.yaml");
String image = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage() + ":"
+ getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
private static void discoveryServer(Phase phase) {
V1Deployment deployment = (V1Deployment) util
.yaml("server/spring-cloud-kubernetes-discoveryserver-deployment.yaml");
V1Service service = (V1Service) util.yaml("server/spring-cloud-kubernetes-discoveryserver-service.yaml");
V1Ingress ingress = (V1Ingress) util.yaml("server/spring-cloud-kubernetes-discoveryserver-ingress.yaml");
private static void deployDiscoveryServer() throws Exception {
appsApi.createNamespacedDeployment(NAMESPACE, getDiscoveryServerDeployment(), null, null, null, null);
api.createNamespacedService(NAMESPACE, getDiscoveryServerService(), null, null, null, null);
V1Ingress ingress = getDiscoveryServerIngress();
networkingApi.createNamespacedIngress(NAMESPACE, ingress, null, null, null, null);
k8SUtils.waitForIngress(ingress.getMetadata().getName(), NAMESPACE);
}
private static V1Deployment getDiscoveryServerDeployment() throws Exception {
V1Deployment deployment = (V1Deployment) k8SUtils
.readYamlFromClasspath("server/spring-cloud-kubernetes-discoveryserver-deployment.yaml");
String image = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage() + ":"
+ getPomVersion();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(image);
return deployment;
}
private static V1Ingress getDiscoveryServerIngress() throws Exception {
return (V1Ingress) K8SUtils
.readYamlFromClasspath("server/spring-cloud-kubernetes-discoveryserver-ingress.yaml");
}
private static V1Service getDiscoveryServerService() throws Exception {
return (V1Service) K8SUtils
.readYamlFromClasspath("server/spring-cloud-kubernetes-discoveryserver-service.yaml");
}
private V1Ingress getDiscoveryItIngress() throws Exception {
return (V1Ingress) K8SUtils
.readYamlFromClasspath("client/spring-cloud-kubernetes-discoveryclient-it-ingress.yaml");
}
private V1Service getDiscoveryService() throws Exception {
return (V1Service) K8SUtils
.readYamlFromClasspath("client/spring-cloud-kubernetes-discoveryclient-it-service.yaml");
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
else {
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
private WebClient.Builder builder() {

View File

@@ -1,27 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: wiremock-deployment
spec:
selector:
matchLabels:
app: wiremock
template:
metadata:
labels:
app: wiremock
spec:
containers:
- name: wiremock
image: wiremock/wiremock:2.32.0
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet:
port: 8080
path: /__admin/mappings
livenessProbe:
httpGet:
port: 8080
path: /__admin/mappings
ports:
- containerPort: 8080

View File

@@ -1,15 +0,0 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: wiremock-ingress
spec:
rules:
- http:
paths:
- path: /wiremock
pathType: Prefix
backend:
service:
name: wiremock
port:
number: 8080

View File

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

View File

@@ -24,10 +24,7 @@ import java.util.Objects;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
@@ -39,8 +36,8 @@ import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.commons.discovery.EndpointNameAndNamespace;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.Fabric8Utils;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpMethod;
@@ -62,36 +59,23 @@ class Fabric8CatalogWatchIT {
private static KubernetesClient client;
private static String busyboxServiceName;
private static String busyboxDeploymentName;
private static String appDeploymentName;
private static String appServiceName;
private static String appIngressName;
private static Util util;
@BeforeAll
static void beforeAll() throws Exception {
K3S.start();
Config config = Config.fromKubeconfig(K3S.getKubeConfigYaml());
client = new KubernetesClientBuilder().withConfig(config).build();
util = new Util(K3S);
client = util.client();
Commons.validateImage(APP_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(APP_NAME, K3S);
Fabric8Utils.setUp(client, "default");
util.setUp(NAMESPACE);
}
@BeforeEach
void beforeEach() throws Exception {
deployBusyboxManifests();
}
@AfterEach
void afterEach() {
deleteApp();
void beforeEach() {
util.busybox(NAMESPACE, Phase.CREATE);
}
/**
@@ -104,16 +88,18 @@ class Fabric8CatalogWatchIT {
*/
@Test
void testCatalogWatchWithEndpoints() throws Exception {
deployApp(false);
app(false, Phase.CREATE);
assertLogStatement("stateGenerator is of type: Fabric8EndpointsCatalogWatch");
test();
app(false, Phase.DELETE);
}
@Test
void testCatalogWatchWithEndpointSlices() throws Exception {
deployApp(true);
app(true, Phase.CREATE);
assertLogStatement("stateGenerator is of type: Fabric8EndpointSliceV1CatalogWatch");
test();
app(true, Phase.DELETE);
}
/**
@@ -169,7 +155,7 @@ class Fabric8CatalogWatchIT {
Assertions.assertEquals("default", resultOne.namespace());
Assertions.assertEquals("default", resultTwo.namespace());
deleteBusyboxApp();
util.busybox(NAMESPACE, Phase.DELETE);
// what we get after delete
EndpointNameAndNamespace[] afterDelete = new EndpointNameAndNamespace[1];
@@ -202,87 +188,26 @@ class Fabric8CatalogWatchIT {
}
private void deployBusyboxManifests() throws Exception {
private static void app(boolean useEndpointSlices, Phase phase) {
Deployment deployment = client.apps().deployments().load(getBusyboxDeployment()).get();
InputStream endpointsDeploymentStream = util.inputStream("app/watcher-endpoints-deployment.yaml");
InputStream endpointSlicesDeploymentStream = util.inputStream("app/watcher-endpoint-slices-deployment.yaml");
InputStream serviceStream = util.inputStream("app/watcher-service.yaml");
InputStream ingressStream = util.inputStream("app/watcher-ingress.yaml");
String[] image = K8SUtils.getImageFromDeployment(deployment).split(":");
Commons.pullImage(image[0], image[1], K3S);
Commons.loadImage(image[0], image[1], "busybox", K3S);
Deployment deployment = useEndpointSlices
? client.apps().deployments().load(endpointSlicesDeploymentStream).get()
: client.apps().deployments().load(endpointsDeploymentStream).get();
Service service = client.services().load(serviceStream).get();
Ingress ingress = client.network().v1().ingresses().load(ingressStream).get();
client.apps().deployments().inNamespace(NAMESPACE).resource(deployment).create();
busyboxDeploymentName = deployment.getMetadata().getName();
if (phase.equals(Phase.CREATE)) {
util.createAndWait(Fabric8CatalogWatchIT.NAMESPACE, null, deployment, service, ingress, true);
}
else {
util.deleteAndWait(Fabric8CatalogWatchIT.NAMESPACE, deployment, service, ingress);
}
Service busyboxService = client.services().load(getBusyboxService()).get();
busyboxServiceName = busyboxService.getMetadata().getName();
client.services().inNamespace(NAMESPACE).resource(busyboxService).create();
Fabric8Utils.waitForDeployment(client, busyboxDeploymentName, NAMESPACE, 2, 600);
}
private static void deployApp(boolean useEndpointSlices) {
InputStream deployment = useEndpointSlices ? getEndpointSlicesAppDeployment() : getEndpointsAppDeployment();
Deployment appDeployment = client.apps().deployments().load(deployment).get();
String version = K8SUtils.getPomVersion();
String currentImage = appDeployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
appDeployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(currentImage + ":" + version);
client.apps().deployments().inNamespace(NAMESPACE).resource(appDeployment).create();
appDeploymentName = appDeployment.getMetadata().getName();
Service appService = client.services().load(getAppService()).get();
appServiceName = appService.getMetadata().getName();
client.services().inNamespace(NAMESPACE).resource(appService).create();
Fabric8Utils.waitForDeployment(client, appDeploymentName, NAMESPACE, 2, 600);
Ingress appIngress = client.network().v1().ingresses().load(getAppIngress()).get();
appIngressName = appIngress.getMetadata().getName();
client.network().v1().ingresses().inNamespace(NAMESPACE).resource(appIngress).create();
Fabric8Utils.waitForIngress(client, appIngressName, NAMESPACE);
}
private void deleteBusyboxApp() {
Fabric8Utils.deleteDeployment(client, NAMESPACE, busyboxDeploymentName);
Fabric8Utils.deleteService(client, NAMESPACE, busyboxServiceName);
}
private void deleteApp() {
Fabric8Utils.deleteDeployment(client, NAMESPACE, appDeploymentName);
Fabric8Utils.deleteService(client, NAMESPACE, appServiceName);
Fabric8Utils.deleteIngress(client, NAMESPACE, appIngressName);
}
private static InputStream getBusyboxService() {
return Fabric8Utils.inputStream("busybox/service.yaml");
}
private static InputStream getBusyboxDeployment() {
return Fabric8Utils.inputStream("busybox/deployment.yaml");
}
/**
* deployment where support for endpoint slices is equal to false
*/
private static InputStream getEndpointsAppDeployment() {
return Fabric8Utils.inputStream("app/watcher-endpoints-deployment.yaml");
}
private static InputStream getEndpointSlicesAppDeployment() {
return Fabric8Utils.inputStream("app/watcher-endpoint-slices-deployment.yaml");
}
private static InputStream getAppIngress() {
return Fabric8Utils.inputStream("app/watcher-ingress.yaml");
}
private static InputStream getAppService() {
return Fabric8Utils.inputStream("app/watcher-service.yaml");
}
private WebClient.Builder builder() {

View File

@@ -25,14 +25,11 @@ import java.util.Set;
import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.EnvVarBuilder;
import io.fabric8.kubernetes.api.model.NamespaceBuilder;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
@@ -44,8 +41,8 @@ import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.commons.discovery.EndpointNameAndNamespace;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.Fabric8Utils;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpMethod;
@@ -71,46 +68,33 @@ class Fabric8CatalogWatchWithNamespacesIT {
private static KubernetesClient client;
private static String busyboxServiceNameA;
private static String busyboxServiceNameB;
private static String busyboxDeploymentNameA;
private static String busyboxDeploymentNameB;
private static String appDeploymentName;
private static String appServiceName;
private static String appIngressName;
private static Util util;
@BeforeAll
static void beforeAll() throws Exception {
K3S.start();
Config config = Config.fromKubeconfig(K3S.getKubeConfigYaml());
client = new KubernetesClientBuilder().withConfig(config).build();
util = new Util(K3S);
client = util.client();
Commons.validateImage(APP_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(APP_NAME, K3S);
util.createNamespace(NAMESPACE_A);
util.createNamespace(NAMESPACE_B);
util.setUpClusterWide(NAMESPACE_DEFAULT, Set.of(NAMESPACE_DEFAULT, NAMESPACE_A, NAMESPACE_B));
}
@BeforeEach
void beforeEach() throws Exception {
client.namespaces().resource(new NamespaceBuilder().withNewMetadata().withName(NAMESPACE_A).and().build())
.create();
client.namespaces().resource(new NamespaceBuilder().withNewMetadata().withName(NAMESPACE_B).and().build())
.create();
Fabric8Utils.setUpClusterWide(client, NAMESPACE_DEFAULT, Set.of(NAMESPACE_DEFAULT, NAMESPACE_A, NAMESPACE_B));
deployBusyboxManifests();
void beforeEach() {
util.busybox(NAMESPACE_A, Phase.CREATE);
util.busybox(NAMESPACE_B, Phase.CREATE);
}
@AfterEach
void afterEach() {
Fabric8Utils.cleanUpClusterWide(client, NAMESPACE_DEFAULT, Set.of(NAMESPACE_DEFAULT, NAMESPACE_A, NAMESPACE_B));
Fabric8Utils.deleteNamespace(client, NAMESPACE_A);
Fabric8Utils.deleteNamespace(client, NAMESPACE_B);
deleteApp();
@AfterAll
static void afterAll() {
util.deleteNamespace(NAMESPACE_A);
util.deleteNamespace(NAMESPACE_B);
}
/**
@@ -125,16 +109,18 @@ class Fabric8CatalogWatchWithNamespacesIT {
*/
@Test
void testCatalogWatchWithEndpoints() throws Exception {
deployApp(false);
app(false, Phase.CREATE);
assertLogStatement("stateGenerator is of type: Fabric8EndpointsCatalogWatch");
test();
app(false, Phase.DELETE);
}
@Test
void testCatalogWatchWithEndpointSlices() throws Exception {
deployApp(true);
app(true, Phase.CREATE);
assertLogStatement("stateGenerator is of type: Fabric8EndpointSliceV1CatalogWatch");
test();
app(true, Phase.DELETE);
}
/**
@@ -190,7 +176,8 @@ class Fabric8CatalogWatchWithNamespacesIT {
Assertions.assertEquals(NAMESPACE_A, resultOne.namespace());
Assertions.assertEquals(NAMESPACE_A, resultTwo.namespace());
deleteBusyboxApp();
util.busybox(NAMESPACE_A, Phase.DELETE);
util.busybox(NAMESPACE_B, Phase.DELETE);
// what we get after delete
EndpointNameAndNamespace[] afterDelete = new EndpointNameAndNamespace[1];
@@ -223,43 +210,19 @@ class Fabric8CatalogWatchWithNamespacesIT {
}
private void deployBusyboxManifests() throws Exception {
private static void app(boolean useEndpointSlices, Phase phase) {
Deployment deployment = client.apps().deployments().load(getBusyboxDeployment()).get();
InputStream endpointsDeploymentStream = util.inputStream("app/watcher-endpoints-deployment.yaml");
InputStream endpointSlicesDeploymentStream = util.inputStream("app/watcher-endpoint-slices-deployment.yaml");
InputStream serviceStream = util.inputStream("app/watcher-service.yaml");
InputStream ingressStream = util.inputStream("app/watcher-ingress.yaml");
String[] image = K8SUtils.getImageFromDeployment(deployment).split(":");
Commons.pullImage(image[0], image[1], K3S);
Commons.loadImage(image[0], image[1], "busybox", K3S);
// namespace_a
client.apps().deployments().inNamespace(NAMESPACE_A).resource(deployment).create();
busyboxDeploymentNameA = deployment.getMetadata().getName();
Service busyboxServiceA = client.services().load(getBusyboxService()).get();
busyboxServiceNameA = busyboxServiceA.getMetadata().getName();
client.services().inNamespace(NAMESPACE_A).resource(busyboxServiceA).create();
Fabric8Utils.waitForDeployment(client, busyboxDeploymentNameA, NAMESPACE_A, 2, 600);
// namespace_b
client.apps().deployments().inNamespace(NAMESPACE_B).resource(deployment).create();
busyboxDeploymentNameB = deployment.getMetadata().getName();
Service busyboxServiceB = client.services().load(getBusyboxService()).get();
busyboxServiceNameB = busyboxServiceB.getMetadata().getName();
client.services().inNamespace(NAMESPACE_B).resource(busyboxServiceB).create();
Fabric8Utils.waitForDeployment(client, busyboxDeploymentNameB, NAMESPACE_B, 2, 600);
}
private static void deployApp(boolean useEndpointSlices) {
InputStream deployment = useEndpointSlices ? getEndpointSlicesAppDeployment() : getEndpointsAppDeployment();
Deployment appDeployment = client.apps().deployments().load(deployment).get();
Deployment deployment = useEndpointSlices
? client.apps().deployments().load(endpointSlicesDeploymentStream).get()
: client.apps().deployments().load(endpointsDeploymentStream).get();
List<EnvVar> envVars = new ArrayList<>(
appDeployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv());
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv());
EnvVar namespaceAEnvVar = new EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0")
.withValue(NAMESPACE_A).build();
EnvVar namespaceDefaultEnvVar = new EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1")
@@ -267,70 +230,19 @@ class Fabric8CatalogWatchWithNamespacesIT {
envVars.add(namespaceAEnvVar);
envVars.add(namespaceDefaultEnvVar);
appDeployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars);
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars);
String version = K8SUtils.getPomVersion();
String currentImage = appDeployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
appDeployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(currentImage + ":" + version);
Service service = client.services().load(serviceStream).get();
Ingress ingress = client.network().v1().ingresses().load(ingressStream).get();
client.apps().deployments().inNamespace(NAMESPACE_DEFAULT).resource(appDeployment).create();
appDeploymentName = appDeployment.getMetadata().getName();
if (phase.equals(Phase.CREATE)) {
util.createAndWait(Fabric8CatalogWatchWithNamespacesIT.NAMESPACE_DEFAULT, null, deployment, service,
ingress, true);
}
else {
util.deleteAndWait(Fabric8CatalogWatchWithNamespacesIT.NAMESPACE_DEFAULT, deployment, service, ingress);
}
Service appService = client.services().load(getAppService()).get();
appServiceName = appService.getMetadata().getName();
client.services().inNamespace(NAMESPACE_DEFAULT).resource(appService).create();
Fabric8Utils.waitForDeployment(client, appDeploymentName, NAMESPACE_DEFAULT, 2, 600);
Ingress appIngress = client.network().v1().ingresses().load(getAppIngress()).get();
appIngressName = appIngress.getMetadata().getName();
client.network().v1().ingresses().inNamespace(NAMESPACE_DEFAULT).resource(appIngress).create();
Fabric8Utils.waitForIngress(client, appIngressName, NAMESPACE_DEFAULT);
}
private void deleteBusyboxApp() {
// namespacea
Fabric8Utils.deleteDeployment(client, NAMESPACE_A, busyboxDeploymentNameA);
Fabric8Utils.deleteService(client, NAMESPACE_A, busyboxServiceNameA);
// namespaceb
Fabric8Utils.deleteDeployment(client, NAMESPACE_B, busyboxDeploymentNameB);
Fabric8Utils.deleteService(client, NAMESPACE_B, busyboxServiceNameB);
}
private void deleteApp() {
Fabric8Utils.deleteDeployment(client, NAMESPACE_DEFAULT, appDeploymentName);
Fabric8Utils.deleteService(client, NAMESPACE_DEFAULT, appServiceName);
Fabric8Utils.deleteIngress(client, NAMESPACE_DEFAULT, appIngressName);
}
private static InputStream getBusyboxService() {
return Fabric8Utils.inputStream("busybox/service.yaml");
}
private static InputStream getBusyboxDeployment() {
return Fabric8Utils.inputStream("busybox/deployment.yaml");
}
/**
* deployment where support for endpoint slices is equal to false
*/
private static InputStream getEndpointsAppDeployment() {
return Fabric8Utils.inputStream("app/watcher-endpoints-deployment.yaml");
}
private static InputStream getEndpointSlicesAppDeployment() {
return Fabric8Utils.inputStream("app/watcher-endpoint-slices-deployment.yaml");
}
private static InputStream getAppIngress() {
return Fabric8Utils.inputStream("app/watcher-ingress.yaml");
}
private static InputStream getAppService() {
return Fabric8Utils.inputStream("app/watcher-service.yaml");
}
private WebClient.Builder builder() {

View File

@@ -18,6 +18,8 @@ package org.springframework.cloud.kubernetes.fabric8.configmap.event.reload;
import java.io.InputStream;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -26,14 +28,13 @@ import java.util.concurrent.locks.LockSupport;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.EnvVarBuilder;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import io.fabric8.kubernetes.client.dsl.internal.HasMetadataOperation;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
@@ -44,14 +45,13 @@ import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.Fabric8Utils;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util;
import org.springframework.http.HttpMethod;
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.processExecResult;
/**
* @author wind57
@@ -62,37 +62,30 @@ class ConfigMapEventReloadIT {
private static final String NAMESPACE = "default";
private static KubernetesClient client;
private static String deploymentName;
private static String serviceName;
private static String ingressName;
private static String leftConfigMapName;
private static String rightConfigMapName;
private static String rightWithLabelConfigMapName;
private static final K3sContainer K3S = Commons.container();
private static Util util;
private static KubernetesClient client;
@BeforeAll
static void beforeAll() throws Exception {
K3S.start();
Commons.validateImage(IMAGE_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S);
Config config = Config.fromKubeconfig(K3S.getKubeConfigYaml());
client = new KubernetesClientBuilder().withConfig(config).build();
createNamespaces();
Fabric8Utils.setUpClusterWide(client, "default", Set.of("left", "right"));
util = new Util(K3S);
client = util.client();
util.createNamespace("left");
util.createNamespace("right");
util.setUpClusterWide(NAMESPACE, Set.of("left", "right"));
}
@AfterAll
static void afterAll() throws Exception {
deleteNamespaces();
util.deleteNamespace("left");
util.deleteNamespace("right");
Commons.cleanUp(IMAGE_NAME, K3S);
}
@@ -106,7 +99,7 @@ class ConfigMapEventReloadIT {
*/
@Test
void testInformFromOneNamespaceEventNotTriggered() {
deployManifests("one");
manifests("one", Phase.CREATE);
WebClient webClient = builder().baseUrl("localhost/left").build();
String result = webClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class).retryWhen(retrySpec())
@@ -125,7 +118,7 @@ class ConfigMapEventReloadIT {
.withMetadata(new ObjectMetaBuilder().withNamespace("right").withName("right-configmap").build())
.withData(Map.of("right.value", "right-after-change")).build();
replaceConfigMap(rightConfigMapAfterChange, "right-configmap");
replaceConfigMap(rightConfigMapAfterChange);
// wait dummy for 5 seconds
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(5));
@@ -135,7 +128,7 @@ class ConfigMapEventReloadIT {
// left configmap has not changed, no restart of app has happened
Assertions.assertEquals("left-initial", result);
deleteManifests();
manifests("one", Phase.DELETE);
}
/**
@@ -148,7 +141,7 @@ class ConfigMapEventReloadIT {
*/
@Test
void testInformFromOneNamespaceEventTriggered() {
deployManifests("two");
manifests("two", Phase.CREATE);
// read the value from the right-configmap
WebClient webClient = builder().baseUrl("localhost/right").build();
@@ -161,7 +154,7 @@ class ConfigMapEventReloadIT {
.withMetadata(new ObjectMetaBuilder().withNamespace("right").withName("right-configmap").build())
.withData(Map.of("right.value", "right-after-change")).build();
replaceConfigMap(rightConfigMapAfterChange, "right-configmap");
replaceConfigMap(rightConfigMapAfterChange);
String[] resultAfterChange = new String[1];
await().pollInterval(Duration.ofSeconds(3)).atMost(Duration.ofSeconds(90)).until(() -> {
@@ -173,7 +166,7 @@ class ConfigMapEventReloadIT {
});
Assertions.assertEquals("right-after-change", resultAfterChange[0]);
deleteManifests();
manifests("two", Phase.DELETE);
}
/**
@@ -187,7 +180,7 @@ class ConfigMapEventReloadIT {
*/
@Test
void testInform() {
deployManifests("three");
manifests("three", Phase.CREATE);
// read the initial value from the right-configmap
WebClient rightWebClient = builder().baseUrl("localhost/right").build();
@@ -206,7 +199,7 @@ class ConfigMapEventReloadIT {
.withMetadata(new ObjectMetaBuilder().withNamespace("right").withName("right-configmap").build())
.withData(Map.of("right.value", "right-after-change")).build();
replaceConfigMap(rightConfigMapAfterChange, "right-configmap");
replaceConfigMap(rightConfigMapAfterChange);
// sleep for 5 seconds
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(5));
@@ -222,7 +215,7 @@ class ConfigMapEventReloadIT {
new ObjectMetaBuilder().withNamespace("right").withName("right-configmap-with-label").build())
.withData(Map.of("right.with.label.value", "right-with-label-after-change")).build();
replaceConfigMap(rightWithLabelConfigMapAfterChange, "right-configmap-with-label");
replaceConfigMap(rightWithLabelConfigMapAfterChange);
// since we have changed a labeled configmap, app will restart and pick up the new
// value
@@ -242,111 +235,53 @@ class ConfigMapEventReloadIT {
.block();
Assertions.assertEquals("right-after-change", rightResult);
deleteManifests();
manifests("three", Phase.DELETE);
}
private static void createNamespaces() throws Exception {
processExecResult(K3S.execInContainer("sh", "-c", "kubectl create namespace left"));
processExecResult(K3S.execInContainer("sh", "-c", "kubectl create namespace right"));
}
private static void manifests(String activeProfile, Phase phase) {
private static void deleteNamespaces() throws Exception {
processExecResult(K3S.execInContainer("sh", "-c", "kubectl delete namespace left"));
processExecResult(K3S.execInContainer("sh", "-c", "kubectl delete namespace right"));
}
InputStream deploymentStream = util.inputStream("deployment.yaml");
InputStream serviceStream = util.inputStream("service.yaml");
InputStream ingressStream = util.inputStream("ingress.yaml");
InputStream leftConfigMapStream = util.inputStream("left-configmap.yaml");
InputStream rightConfigMapStream = util.inputStream("right-configmap.yaml");
InputStream rightWithLabelConfigMapStream = util.inputStream("right-configmap-with-label.yaml");
private static void deployManifests(String deploymentRoot) {
Deployment deployment = client.apps().deployments().load(deploymentStream).get();
try {
List<EnvVar> envVars = new ArrayList<>(
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv());
EnvVar activeProfileProperty = new EnvVarBuilder().withName("SPRING_PROFILES_ACTIVE").withValue(activeProfile)
.build();
envVars.add(activeProfileProperty);
ConfigMap leftConfigMap = client.configMaps().load(leftConfigMap()).get();
leftConfigMapName = leftConfigMap.getMetadata().getName();
client.configMaps().create(leftConfigMap);
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars);
ConfigMap rightConfigMap = client.configMaps().load(rightConfigMap()).get();
rightConfigMapName = rightConfigMap.getMetadata().getName();
client.configMaps().create(rightConfigMap);
Service service = client.services().load(serviceStream).get();
Ingress ingress = client.network().v1().ingresses().load(ingressStream).get();
ConfigMap leftConfigMap = client.configMaps().load(leftConfigMapStream).get();
ConfigMap rightConfigMap = client.configMaps().load(rightConfigMapStream).get();
ConfigMap rightWithLabelConfigMap = client.configMaps().load(rightWithLabelConfigMapStream).get();
if ("three".equals(deploymentRoot)) {
ConfigMap rightWithLabelConfigMap = client.configMaps().load(rightWithLabelConfigMap()).get();
rightWithLabelConfigMapName = rightWithLabelConfigMap.getMetadata().getName();
client.configMaps().create(rightWithLabelConfigMap);
if (phase.equals(Phase.CREATE)) {
util.createAndWait("left", leftConfigMap, null);
util.createAndWait("right", rightConfigMap, null);
if ("three".equals(activeProfile)) {
util.createAndWait("right", rightWithLabelConfigMap, null);
}
Deployment deployment = client.apps().deployments().load(getDeployment(deploymentRoot)).get();
String version = K8SUtils.getPomVersion();
String currentImage = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(currentImage + ":" + version);
client.apps().deployments().inNamespace(NAMESPACE).create(deployment);
deploymentName = deployment.getMetadata().getName();
Service service = client.services().load(getService()).get();
serviceName = service.getMetadata().getName();
client.services().inNamespace(NAMESPACE).create(service);
Ingress ingress = client.network().v1().ingresses().load(getIngress()).get();
ingressName = ingress.getMetadata().getName();
client.network().v1().ingresses().inNamespace(NAMESPACE).create(ingress);
Fabric8Utils.waitForDeployment(client,
"spring-cloud-kubernetes-fabric8-client-configmap-deployment-event-reload", NAMESPACE, 2, 600);
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void deleteManifests() {
try {
client.configMaps().inNamespace("left").withName(leftConfigMapName).delete();
Fabric8Utils.waitForConfigMapDelete(client, "left", leftConfigMapName);
client.configMaps().inNamespace("right").withName(rightConfigMapName).delete();
Fabric8Utils.waitForConfigMapDelete(client, "right", rightConfigMapName);
if (rightWithLabelConfigMapName != null) {
client.configMaps().inNamespace("right").withName(rightWithLabelConfigMapName).delete();
Fabric8Utils.waitForConfigMapDelete(client, "right", rightWithLabelConfigMapName);
else {
util.deleteAndWait("left", leftConfigMap, null);
util.deleteAndWait("right", rightConfigMap, null);
if ("three".equals(activeProfile)) {
util.deleteAndWait("right", rightWithLabelConfigMap, null);
}
client.apps().deployments().inNamespace(NAMESPACE).withName(deploymentName).delete();
client.services().inNamespace(NAMESPACE).withName(serviceName).delete();
client.network().v1().ingresses().inNamespace(NAMESPACE).withName(ingressName).delete();
}
catch (Exception e) {
throw new RuntimeException(e);
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
private static InputStream leftConfigMap() {
return Fabric8Utils.inputStream("left-configmap.yaml");
}
private static InputStream rightConfigMap() {
return Fabric8Utils.inputStream("right-configmap.yaml");
}
private static InputStream rightWithLabelConfigMap() {
return Fabric8Utils.inputStream("right-configmap-with-label.yaml");
}
private static InputStream getDeployment(String root) {
return Fabric8Utils.inputStream(root + "/deployment.yaml");
}
private static InputStream getService() {
return Fabric8Utils.inputStream("service.yaml");
}
private static InputStream getIngress() {
return Fabric8Utils.inputStream("ingress.yaml");
}
private WebClient.Builder builder() {
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
}
@@ -355,11 +290,8 @@ class ConfigMapEventReloadIT {
return Retry.fixedDelay(120, Duration.ofSeconds(2)).filter(Objects::nonNull);
}
// the weird cast comes from :
// https://github.com/fabric8io/kubernetes-client/issues/2445
@SuppressWarnings({ "unchecked", "raw" })
private static void replaceConfigMap(ConfigMap configMap, String name) {
((HasMetadataOperation) client.configMaps().inNamespace("right").withName(name)).createOrReplace(configMap);
private static void replaceConfigMap(ConfigMap configMap) {
client.configMaps().inNamespace("right").resource(configMap).createOrReplace();
}
}

View File

@@ -1,31 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-cloud-kubernetes-fabric8-client-configmap-deployment-event-reload
spec:
selector:
matchLabels:
app: spring-cloud-kubernetes-fabric8-client-configmap-event-reload
template:
metadata:
labels:
app: spring-cloud-kubernetes-fabric8-client-configmap-event-reload
spec:
serviceAccountName: spring-cloud-kubernetes-serviceaccount
containers:
- name: spring-cloud-kubernetes-fabric8-client-configmap-event-reload
image: docker.io/springcloud/spring-cloud-kubernetes-fabric8-client-configmap-event-reload
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet:
port: 8080
path: /actuator/health/readiness
livenessProbe:
httpGet:
port: 8080
path: /actuator/health/liveness
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: one

View File

@@ -1,31 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-cloud-kubernetes-fabric8-client-configmap-deployment-event-reload
spec:
selector:
matchLabels:
app: spring-cloud-kubernetes-fabric8-client-configmap-event-reload
template:
metadata:
labels:
app: spring-cloud-kubernetes-fabric8-client-configmap-event-reload
spec:
serviceAccountName: spring-cloud-kubernetes-serviceaccount
containers:
- name: spring-cloud-kubernetes-fabric8-client-configmap-event-reload
image: docker.io/springcloud/spring-cloud-kubernetes-fabric8-client-configmap-event-reload
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet:
port: 8080
path: /actuator/health/readiness
livenessProbe:
httpGet:
port: 8080
path: /actuator/health/liveness
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: three

View File

@@ -27,10 +27,7 @@ import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import io.fabric8.kubernetes.client.dsl.internal.HasMetadataOperation;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
@@ -41,8 +38,8 @@ import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.Fabric8Utils;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
@@ -58,16 +55,10 @@ class ConfigMapPollingReloadIT {
private static final String NAMESPACE = "default";
private static Util util;
private static KubernetesClient client;
private static String deploymentName;
private static String serviceName;
private static String ingressName;
private static String configMapName;
private static final K3sContainer K3S = Commons.container();
@BeforeAll
@@ -75,19 +66,18 @@ class ConfigMapPollingReloadIT {
K3S.start();
Commons.validateImage(IMAGE_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S);
Config config = Config.fromKubeconfig(K3S.getKubeConfigYaml());
client = new KubernetesClientBuilder().withConfig(config).build();
Fabric8Utils.setUp(client, NAMESPACE);
deployManifests();
util = new Util(K3S);
client = util.client();
util.setUp(NAMESPACE);
manifests(Phase.CREATE);
}
@AfterAll
static void after() throws Exception {
deleteManifests();
manifests(Phase.DELETE);
Commons.cleanUp(IMAGE_NAME, K3S);
}
@SuppressWarnings({ "raw", "unchecked" })
@Test
void test() {
WebClient webClient = builder().baseUrl("localhost/key").build();
@@ -103,83 +93,36 @@ class ConfigMapPollingReloadIT {
.withMetadata(new ObjectMetaBuilder().withNamespace("default").withName("poll-reload").build())
.withData(Map.of("application.properties", "from.properties.key=after-change")).build();
// the weird cast comes from :
// https://github.com/fabric8io/kubernetes-client/issues/2445
((HasMetadataOperation) client.configMaps().inNamespace("default").withName("poll-reload")).resource(map)
.createOrReplace();
client.configMaps().inNamespace("default").resource(map).createOrReplace();
await().timeout(Duration.ofSeconds(60)).until(() -> webClient.method(HttpMethod.GET).retrieve()
.bodyToMono(String.class).retryWhen(retrySpec()).block().equals("after-change"));
}
private static void deleteManifests() {
private static void manifests(Phase phase) {
try {
InputStream deploymentStream = util.inputStream("deployment.yaml");
InputStream serviceStream = util.inputStream("service.yaml");
InputStream ingressStream = util.inputStream("ingress.yaml");
InputStream configMapStream = util.inputStream("configmap.yaml");
client.configMaps().inNamespace(NAMESPACE).withName(configMapName).delete();
client.apps().deployments().inNamespace(NAMESPACE).withName(deploymentName).delete();
client.services().inNamespace(NAMESPACE).withName(serviceName).delete();
client.network().v1().ingresses().inNamespace(NAMESPACE).withName(ingressName).delete();
Deployment deployment = client.apps().deployments().load(deploymentStream).get();
Service service = client.services().load(serviceStream).get();
Ingress ingress = client.network().v1().ingresses().load(ingressStream).get();
ConfigMap configMap = client.configMaps().load(configMapStream).get();
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, configMap, null);
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
catch (Exception e) {
throw new RuntimeException(e);
else {
util.deleteAndWait(NAMESPACE, configMap, null);
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
private static void deployManifests() {
try {
ConfigMap configMap = client.configMaps().load(getConfigMap()).get();
configMapName = configMap.getMetadata().getName();
client.configMaps().resource(configMap).create();
Deployment deployment = client.apps().deployments().load(getDeployment()).get();
String version = K8SUtils.getPomVersion();
String currentImage = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(currentImage + ":" + version);
client.apps().deployments().inNamespace(NAMESPACE).create(deployment);
deploymentName = deployment.getMetadata().getName();
Service service = client.services().load(getService()).get();
serviceName = service.getMetadata().getName();
client.services().inNamespace(NAMESPACE).resource(service).create();
Ingress ingress = client.network().v1().ingresses().load(getIngress()).get();
ingressName = ingress.getMetadata().getName();
client.network().v1().ingresses().inNamespace(NAMESPACE).resource(ingress).create();
Fabric8Utils.waitForDeployment(client,
"spring-cloud-kubernetes-fabric8-client-configmap-deployment-polling-reload", NAMESPACE, 2, 600);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static InputStream getService() {
return Fabric8Utils.inputStream("service.yaml");
}
private static InputStream getDeployment() {
return Fabric8Utils.inputStream("deployment.yaml");
}
private static InputStream getIngress() {
return Fabric8Utils.inputStream("ingress.yaml");
}
private static InputStream getConfigMap() {
return Fabric8Utils.inputStream("configmap.yaml");
}
private WebClient.Builder builder() {
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
}

View File

@@ -24,9 +24,7 @@ import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
@@ -37,8 +35,8 @@ import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.Fabric8Utils;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
@@ -51,30 +49,26 @@ class Fabric8ConfigMapIT {
private static KubernetesClient client;
private static String deploymentName;
private static String serviceName;
private static String ingressName;
private static String configMapName;
private static final K3sContainer K3S = Commons.container();
private static Util util;
@BeforeAll
static void beforeAll() throws Exception {
K3S.start();
util = new Util(K3S);
client = util.client();
Commons.validateImage(IMAGE_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S);
Config config = Config.fromKubeconfig(K3S.getKubeConfigYaml());
client = new KubernetesClientBuilder().withConfig(config).build();
Fabric8Utils.setUp(client, NAMESPACE);
deployManifests();
util.setUp(NAMESPACE);
manifests(Phase.CREATE);
}
@AfterAll
static void after() throws Exception {
deleteManifests();
static void afterAll() throws Exception {
manifests(Phase.DELETE);
Commons.cleanUp(IMAGE_NAME, K3S);
}
@@ -88,73 +82,29 @@ class Fabric8ConfigMapIT {
Assertions.assertEquals("value1", result);
}
private static void deleteManifests() {
private static void manifests(Phase phase) {
try {
InputStream deploymentStream = util.inputStream("fabric8-deployment.yaml");
InputStream serviceStream = util.inputStream("fabric8-service.yaml");
InputStream ingressStream = util.inputStream("fabric8-ingress.yaml");
InputStream configMapStream = util.inputStream("fabric8-configmap.yaml");
client.configMaps().inNamespace(NAMESPACE).withName(configMapName).delete();
client.apps().deployments().inNamespace(NAMESPACE).withName(deploymentName).delete();
client.services().inNamespace(NAMESPACE).withName(serviceName).delete();
client.network().v1().ingresses().inNamespace(NAMESPACE).withName(ingressName).delete();
Deployment deployment = client.apps().deployments().load(deploymentStream).get();
Service service = client.services().load(serviceStream).get();
Ingress ingress = client.network().v1().ingresses().load(ingressStream).get();
ConfigMap configMap = client.configMaps().load(configMapStream).get();
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, configMap, null);
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
catch (Exception e) {
throw new RuntimeException(e);
else {
util.deleteAndWait(NAMESPACE, configMap, null);
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
private static void deployManifests() {
try {
ConfigMap configMap = client.configMaps().load(getConfigMap()).get();
configMapName = configMap.getMetadata().getName();
client.configMaps().resource(configMap).create();
Deployment deployment = client.apps().deployments().load(getDeployment()).get();
String version = K8SUtils.getPomVersion();
String currentImage = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(currentImage + ":" + version);
client.apps().deployments().inNamespace(NAMESPACE).resource(deployment).create();
deploymentName = deployment.getMetadata().getName();
Service service = client.services().load(getService()).get();
serviceName = service.getMetadata().getName();
client.services().inNamespace(NAMESPACE).resource(service).create();
Ingress ingress = client.network().v1().ingresses().load(getIngress()).get();
ingressName = ingress.getMetadata().getName();
client.network().v1().ingresses().inNamespace(NAMESPACE).resource(ingress).create();
Fabric8Utils.waitForDeployment(client, "spring-cloud-kubernetes-fabric8-client-configmap-deployment",
NAMESPACE, 2, 600);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static InputStream getService() {
return Fabric8Utils.inputStream("fabric8-service.yaml");
}
private static InputStream getDeployment() {
return Fabric8Utils.inputStream("fabric8-deployment.yaml");
}
private static InputStream getIngress() {
return Fabric8Utils.inputStream("fabric8-ingress.yaml");
}
private static InputStream getConfigMap() {
return Fabric8Utils.inputStream("fabric8-configmap.yaml");
}
private WebClient.Builder builder() {
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
}

View File

@@ -24,9 +24,7 @@ import java.util.Objects;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
@@ -37,8 +35,8 @@ import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.Fabric8Utils;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
@@ -54,17 +52,7 @@ class Fabric8DiscoveryIT {
private static KubernetesClient client;
private static String deploymentName;
private static String serviceName;
private static String ingressName;
private static String mockServiceName;
private static String mockDeploymentName;
private static String mockDeploymentImage;
private static Util util;
private static final K3sContainer K3S = Commons.container();
@@ -74,19 +62,20 @@ class Fabric8DiscoveryIT {
Commons.validateImage(IMAGE_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S);
Config config = Config.fromKubeconfig(K3S.getKubeConfigYaml());
client = new KubernetesClientBuilder().withConfig(config).build();
Fabric8Utils.setUp(client, NAMESPACE);
util = new Util(K3S);
client = util.client();
deployManifests();
deployMockManifests();
util.setUp(NAMESPACE);
manifests(Phase.CREATE);
util.wiremock(NAMESPACE, "/wiremock", Phase.CREATE);
}
@AfterAll
static void after() throws Exception {
deleteManifests();
util.wiremock(NAMESPACE, "/wiremock", Phase.DELETE);
manifests(Phase.DELETE);
Commons.cleanUp(IMAGE_NAME, K3S);
Commons.cleanUpDownloadedImage(mockDeploymentImage);
}
@Test
@@ -100,103 +89,28 @@ class Fabric8DiscoveryIT {
Assertions.assertEquals(result.size(), 3);
Assertions.assertTrue(result.contains("kubernetes"));
Assertions.assertTrue(result.contains("spring-cloud-kubernetes-fabric8-client-discovery"));
Assertions.assertTrue(result.contains("wiremock"));
Assertions.assertTrue(result.contains("service-wiremock"));
}
private static void deleteManifests() {
private static void manifests(Phase phase) {
try {
InputStream deploymentStream = util.inputStream("fabric8-discovery-deployment.yaml");
InputStream serviceStream = util.inputStream("fabric8-discovery-service.yaml");
InputStream ingressStream = util.inputStream("fabric8-discovery-ingress.yaml");
client.apps().deployments().inNamespace(NAMESPACE).withName(deploymentName).delete();
client.services().inNamespace(NAMESPACE).withName(serviceName).delete();
client.network().v1().ingresses().inNamespace(NAMESPACE).withName(ingressName).delete();
client.services().inNamespace(NAMESPACE).withName(mockServiceName).delete();
client.apps().deployments().inNamespace(NAMESPACE).withName(mockDeploymentName).delete();
Deployment deployment = client.apps().deployments().load(deploymentStream).get();
Service service = client.services().load(serviceStream).get();
Ingress ingress = client.network().v1().ingresses().load(ingressStream).get();
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
catch (Exception e) {
throw new RuntimeException(e);
else {
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
private static void deployManifests() {
try {
Deployment deployment = client.apps().deployments().load(getDeployment()).get();
String version = K8SUtils.getPomVersion();
String currentImage = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(currentImage + ":" + version);
client.apps().deployments().inNamespace(NAMESPACE).resource(deployment).create();
deploymentName = deployment.getMetadata().getName();
Service service = client.services().load(getService()).get();
serviceName = service.getMetadata().getName();
client.services().inNamespace(NAMESPACE).resource(service).create();
Ingress ingress = client.network().v1().ingresses().load(getIngress()).get();
ingressName = ingress.getMetadata().getName();
client.network().v1().ingresses().inNamespace(NAMESPACE).resource(ingress).create();
Fabric8Utils.waitForDeployment(client, "spring-cloud-kubernetes-fabric8-client-discovery-deployment",
NAMESPACE, 2, 600);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void deployMockManifests() {
try {
Deployment deployment = client.apps().deployments().load(getMockDeployment()).get();
String[] image = K8SUtils.getImageFromDeployment(deployment).split(":");
Commons.pullImage(image[0], image[1], K3S);
Commons.loadImage(image[0], image[1], "wiremock", K3S);
client.apps().deployments().inNamespace(NAMESPACE).resource(deployment).create();
mockDeploymentName = deployment.getMetadata().getName();
mockDeploymentImage = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
Service service = client.services().load(getMockService()).get();
mockServiceName = service.getMetadata().getName();
client.services().inNamespace(NAMESPACE).resource(service).create();
Fabric8Utils.waitForDeployment(client, "wiremock-deployment", NAMESPACE, 2, 600);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static InputStream getService() {
return Fabric8Utils.inputStream("fabric8-discovery-service.yaml");
}
private static InputStream getDeployment() {
return Fabric8Utils.inputStream("fabric8-discovery-deployment.yaml");
}
private static InputStream getIngress() {
return Fabric8Utils.inputStream("fabric8-discovery-ingress.yaml");
}
private static InputStream getMockService() {
return Fabric8Utils.inputStream("wiremock/wiremock-service.yaml");
}
private static InputStream getMockDeployment() {
return Fabric8Utils.inputStream("wiremock/wiremock-deployment.yaml");
}
private WebClient.Builder builder() {
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
}

View File

@@ -18,19 +18,17 @@ package org.springframework.cloud.kubernetes.fabric8.configmap;
import java.io.InputStream;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import io.fabric8.kubernetes.api.model.Endpoints;
import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.Namespace;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.EnvVarBuilder;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
@@ -41,8 +39,8 @@ import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.Fabric8Utils;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
@@ -61,20 +59,10 @@ class Fabric8DiscoveryNamespaceFilterIT {
private static final String IMAGE_NAME = "spring-cloud-kubernetes-fabric8-client-discovery";
private static Util util;
private static KubernetesClient client;
private static String deploymentName;
private static String serviceName;
private static String ingressName;
private static String mockServiceName;
private static String mockDeploymentName;
private static String mockDeploymentImage;
private static final K3sContainer K3S = Commons.container();
@BeforeAll
@@ -83,19 +71,25 @@ class Fabric8DiscoveryNamespaceFilterIT {
Commons.validateImage(IMAGE_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S);
Config config = Config.fromKubeconfig(K3S.getKubeConfigYaml());
client = new KubernetesClientBuilder().withConfig(config).build();
Fabric8Utils.setUp(client, NAMESPACE);
util = new Util(K3S);
client = util.client();
util.setUp(NAMESPACE);
deployManifests();
deployMockManifests();
manifests(Phase.CREATE);
util.createNamespace(NAMESPACE_LEFT);
util.createNamespace(NAMESPACE_RIGHT);
util.wiremock(NAMESPACE_LEFT, "/wiremock", Phase.CREATE);
util.wiremock(NAMESPACE_RIGHT, "/wiremock", Phase.CREATE);
}
@AfterAll
static void after() throws Exception {
deleteManifests();
manifests(Phase.DELETE);
util.wiremock(NAMESPACE_LEFT, "/wiremock", Phase.DELETE);
util.wiremock(NAMESPACE_RIGHT, "/wiremock", Phase.DELETE);
util.deleteNamespace(NAMESPACE_LEFT);
util.deleteNamespace(NAMESPACE_RIGHT);
Commons.cleanUp(IMAGE_NAME, K3S);
Commons.cleanUpDownloadedImage(mockDeploymentImage);
}
@Test
@@ -107,9 +101,9 @@ class Fabric8DiscoveryNamespaceFilterIT {
.retryWhen(retrySpec()).block();
Assertions.assertEquals(services.size(), 1);
Assertions.assertTrue(services.contains("wiremock"));
Assertions.assertTrue(services.contains("service-wiremock"));
WebClient clientEndpoints = builder().baseUrl("localhost/endpoints/wiremock").build();
WebClient clientEndpoints = builder().baseUrl("localhost/endpoints/service-wiremock").build();
List<Endpoints> endpoints = clientEndpoints.method(HttpMethod.GET).retrieve()
.bodyToMono(new ParameterizedTypeReference<List<Endpoints>>() {
@@ -120,127 +114,36 @@ class Fabric8DiscoveryNamespaceFilterIT {
}
private static void deleteManifests() {
private static void manifests(Phase phase) {
try {
InputStream deploymentStream = util.inputStream("fabric8-discovery-deployment.yaml");
InputStream serviceStream = util.inputStream("fabric8-discovery-service.yaml");
InputStream ingressStream = util.inputStream("fabric8-discovery-ingress.yaml");
client.apps().deployments().inNamespace(NAMESPACE).withName(deploymentName).delete();
client.services().inNamespace(NAMESPACE).withName(serviceName).delete();
client.network().v1().ingresses().inNamespace(NAMESPACE).withName(ingressName).delete();
Deployment deployment = client.apps().deployments().load(deploymentStream).get();
List<EnvVar> envVars = new ArrayList<>(
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv());
EnvVar activeProfileProperty = new EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0")
.withValue(NAMESPACE_LEFT).build();
envVars.add(activeProfileProperty);
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars);
client.services().inNamespace(NAMESPACE_LEFT).withName(mockServiceName).delete();
client.apps().deployments().inNamespace(NAMESPACE_LEFT).withName(mockDeploymentName).delete();
client.services().inNamespace(NAMESPACE_RIGHT).withName(mockServiceName).delete();
client.apps().deployments().inNamespace(NAMESPACE_RIGHT).withName(mockDeploymentName).delete();
client.rbac().clusterRoleBindings().withName("admin-default").delete();
client.namespaces().withName(NAMESPACE_LEFT).delete();
client.namespaces().withName(NAMESPACE_RIGHT).delete();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void deployManifests() {
try {
Deployment deployment = client.apps().deployments().load(getDeployment()).get();
String version = K8SUtils.getPomVersion();
String currentImage = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(currentImage + ":" + version);
List<EnvVar> env = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv();
env.add(new EnvVar("SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0", NAMESPACE_LEFT, null));
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(env);
client.apps().deployments().inNamespace(NAMESPACE).resource(deployment).create();
deploymentName = deployment.getMetadata().getName();
Service service = client.services().load(getService()).get();
serviceName = service.getMetadata().getName();
client.services().inNamespace(NAMESPACE).resource(service).create();
Ingress ingress = client.network().v1().ingresses().load(getIngress()).get();
ingressName = ingress.getMetadata().getName();
client.network().v1().ingresses().inNamespace(NAMESPACE).resource(ingress).create();
Service service = client.services().load(serviceStream).get();
Ingress ingress = client.network().v1().ingresses().load(ingressStream).get();
if (phase.equals(Phase.CREATE)) {
client.rbac().clusterRoleBindings().resource(client.rbac().clusterRoleBindings().load(getAdminRole()).get())
.create();
Fabric8Utils.waitForDeployment(client, "spring-cloud-kubernetes-fabric8-client-discovery-deployment",
NAMESPACE, 2, 600);
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
catch (Exception e) {
throw new RuntimeException(e);
else {
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
private static void deployMockManifests() {
try {
deployInMockInNamespace(NAMESPACE_LEFT);
deployInMockInNamespace(NAMESPACE_RIGHT);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void deployInMockInNamespace(String namespace) throws Exception {
Namespace namespaceDef = new Namespace();
ObjectMeta meta = new ObjectMeta();
meta.setName(namespace);
meta.setNamespace(namespace);
namespaceDef.setMetadata(meta);
client.namespaces().resource(namespaceDef).create();
Deployment deployment = client.apps().deployments().load(getMockDeployment()).get();
String[] image = K8SUtils.getImageFromDeployment(deployment).split(":");
Commons.pullImage(image[0], image[1], K3S);
Commons.loadImage(image[0], image[1], "wiremock", K3S);
client.apps().deployments().inNamespace(namespace).resource(deployment).create();
mockDeploymentName = deployment.getMetadata().getName();
mockDeploymentImage = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
Service service = client.services().load(getMockService()).get();
mockServiceName = service.getMetadata().getName();
client.services().inNamespace(namespace).resource(service).create();
Fabric8Utils.waitForDeployment(client, "wiremock-deployment", namespace, 2, 600);
}
private static InputStream getService() {
return Fabric8Utils.inputStream("fabric8-discovery-service.yaml");
}
private static InputStream getDeployment() {
return Fabric8Utils.inputStream("fabric8-discovery-deployment.yaml");
}
private static InputStream getAdminRole() {
return Fabric8Utils.inputStream("namespace-filter/fabric8-cluster-admin-serviceaccount-role.yaml");
}
private static InputStream getIngress() {
return Fabric8Utils.inputStream("fabric8-discovery-ingress.yaml");
}
private static InputStream getMockService() {
return Fabric8Utils.inputStream("wiremock/wiremock-service.yaml");
}
private static InputStream getMockDeployment() {
return Fabric8Utils.inputStream("wiremock/wiremock-deployment.yaml");
return util.inputStream("namespace-filter/fabric8-cluster-admin-serviceaccount-role.yaml");
}
private WebClient.Builder builder() {

View File

@@ -1,27 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: wiremock-deployment
spec:
selector:
matchLabels:
app: wiremock
template:
metadata:
labels:
app: wiremock
spec:
containers:
- name: wiremock
image: wiremock/wiremock:2.32.0
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet:
port: 8080
path: /__admin/mappings
livenessProbe:
httpGet:
port: 8080
path: /__admin/mappings
ports:
- containerPort: 8080

View File

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

View File

@@ -54,7 +54,7 @@ public class Fabric8ClientLoadbalancerApp {
@GetMapping("/servicea")
public Mono<Map> greeting() {
return builder().build().method(HttpMethod.GET).uri(URI.create("http://servicea-wiremock/__admin/mappings"))
return builder().build().method(HttpMethod.GET).uri(URI.create("http://service-wiremock/__admin/mappings"))
.retrieve().bodyToMono(Map.class);
}

View File

@@ -18,17 +18,20 @@ package org.springframework.cloud.kubernetes.client.loadbalancer.it;
import java.io.InputStream;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.EnvVarBuilder;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
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;
@@ -38,14 +41,12 @@ import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.Fabric8Utils;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@@ -57,17 +58,7 @@ public class Fabric8ClientLoadbalancerIT {
private static KubernetesClient client;
private static String deploymentName;
private static String serviceName;
private static String ingressName;
private static String mockServiceName;
private static String mockDeploymentName;
private static String mockIngressName;
private static Util util;
private static final K3sContainer K3S = Commons.container();
@@ -77,9 +68,9 @@ public class Fabric8ClientLoadbalancerIT {
Commons.validateImage(IMAGE_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S);
Config config = Config.fromKubeconfig(K3S.getKubeConfigYaml());
client = new KubernetesClientBuilder().withConfig(config).build();
Fabric8Utils.setUp(client, NAMESPACE);
util = new Util(K3S);
client = util.client();
util.setUp(NAMESPACE);
}
@AfterAll
@@ -89,18 +80,18 @@ public class Fabric8ClientLoadbalancerIT {
@BeforeEach
void beforeEach() {
deployMockManifests();
util.wiremock(NAMESPACE, "/", Phase.CREATE);
}
@AfterEach
void after() {
deleteManifests();
void afterEach() {
util.wiremock(NAMESPACE, "/", Phase.DELETE);
}
@Test
void testLoadBalancerServiceMode() {
deployServiceManifests();
manifests("SERVICE", Phase.CREATE);
WebClient client = builder().baseUrl("localhost/loadbalancer-it/servicea").build();
@@ -108,15 +99,17 @@ public class Fabric8ClientLoadbalancerIT {
Map<String, String> mapResult = (Map<String, String>) client.method(HttpMethod.GET).retrieve()
.bodyToMono(Map.class).retryWhen(retrySpec()).block();
assertThat(mapResult.containsKey("mappings")).isTrue();
assertThat(mapResult.containsKey("meta")).isTrue();
Assertions.assertTrue(mapResult.containsKey("mappings"));
Assertions.assertTrue(mapResult.containsKey("meta"));
manifests("SERVICE", Phase.DELETE);
}
@Test
public void testLoadBalancerPodMode() {
deployPodManifests();
manifests("POD", Phase.CREATE);
WebClient client = builder().baseUrl("localhost/loadbalancer-it/servicea").build();
@@ -124,148 +117,42 @@ public class Fabric8ClientLoadbalancerIT {
Map<String, String> mapResult = (Map<String, String>) client.method(HttpMethod.GET).retrieve()
.bodyToMono(Map.class).retryWhen(retrySpec()).block();
assertThat(mapResult.containsKey("mappings")).isTrue();
assertThat(mapResult.containsKey("meta")).isTrue();
Assertions.assertTrue(mapResult.containsKey("mappings"));
Assertions.assertTrue(mapResult.containsKey("meta"));
manifests("SERVICE", Phase.DELETE);
}
private static void deleteManifests() {
private static void manifests(String type, Phase phase) {
try {
InputStream deploymentStream = util
.inputStream("spring-cloud-kubernetes-fabric8-client-loadbalancer-deployment.yaml");
InputStream serviceStream = util
.inputStream("spring-cloud-kubernetes-fabric8-client-loadbalancer-service.yaml");
InputStream ingressStream = util
.inputStream("spring-cloud-kubernetes-fabric8-client-loadbalancer-ingress.yaml");
client.apps().deployments().inNamespace(NAMESPACE).withName(deploymentName).delete();
client.services().inNamespace(NAMESPACE).withName(serviceName).delete();
client.network().v1().ingresses().inNamespace(NAMESPACE).withName(ingressName).delete();
Deployment deployment = client.apps().deployments().load(deploymentStream).get();
List<EnvVar> envVars = new ArrayList<>(
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv());
EnvVar activeProfileProperty = new EnvVarBuilder().withName("SPRING_CLOUD_KUBERNETES_LOADBALANCER_MODE")
.withValue(type).build();
envVars.add(activeProfileProperty);
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setEnv(envVars);
client.services().inNamespace(NAMESPACE).withName(mockServiceName).delete();
client.apps().deployments().inNamespace(NAMESPACE).withName(mockDeploymentName).delete();
client.network().v1().ingresses().inNamespace(NAMESPACE).withName(mockIngressName).delete();
Service service = client.services().load(serviceStream).get();
Ingress ingress = client.network().v1().ingresses().load(ingressStream).get();
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
catch (Exception e) {
throw new RuntimeException(e);
else {
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
private static void deployServiceManifests() {
try {
Deployment deployment = client.apps().deployments().load(getServiceDeployment()).get();
String version = K8SUtils.getPomVersion();
String currentImage = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(currentImage + ":" + version);
client.apps().deployments().inNamespace(NAMESPACE).resource(deployment).create();
deploymentName = deployment.getMetadata().getName();
Service service = client.services().load(getService()).get();
serviceName = service.getMetadata().getName();
client.services().inNamespace(NAMESPACE).resource(service).create();
Ingress ingress = client.network().v1().ingresses().load(getIngress()).get();
ingressName = ingress.getMetadata().getName();
client.network().v1().ingresses().inNamespace(NAMESPACE).resource(ingress).create();
Fabric8Utils.waitForDeployment(client, "spring-cloud-kubernetes-fabric8-client-loadbalancer-deployment",
NAMESPACE, 2, 600);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void deployPodManifests() {
try {
Deployment deployment = client.apps().deployments().load(getPodDeployment()).get();
String version = K8SUtils.getPomVersion();
String currentImage = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(currentImage + ":" + version);
client.apps().deployments().inNamespace(NAMESPACE).resource(deployment).create();
deploymentName = deployment.getMetadata().getName();
Service service = client.services().load(getService()).get();
serviceName = service.getMetadata().getName();
client.services().inNamespace(NAMESPACE).resource(service).create();
Ingress ingress = client.network().v1().ingresses().load(getIngress()).get();
ingressName = ingress.getMetadata().getName();
client.network().v1().ingresses().inNamespace(NAMESPACE).resource(ingress).create();
Fabric8Utils.waitForDeployment(client, "spring-cloud-kubernetes-fabric8-client-loadbalancer-deployment",
NAMESPACE, 2, 600);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void deployMockManifests() {
try {
Deployment deployment = client.apps().deployments().load(getMockDeployment()).get();
String[] image = K8SUtils.getImageFromDeployment(deployment).split(":");
Commons.pullImage(image[0], image[1], K3S);
Commons.loadImage(image[0], image[1], "wiremock", K3S);
client.apps().deployments().inNamespace(NAMESPACE).resource(deployment).create();
mockDeploymentName = deployment.getMetadata().getName();
Service service = client.services().load(getMockService()).get();
mockServiceName = service.getMetadata().getName();
client.services().inNamespace(NAMESPACE).resource(service).create();
Ingress ingress = client.network().v1().ingresses().load(getMockIngress()).get();
mockIngressName = ingress.getMetadata().getName();
Fabric8Utils.waitForDeployment(client, "servicea-wiremock-deployment", NAMESPACE, 2, 600);
Fabric8Utils.waitForEndpoint(client, "servicea-wiremock", NAMESPACE, 2, 600);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static InputStream getIngress() {
return Fabric8Utils.inputStream("spring-cloud-kubernetes-fabric8-client-loadbalancer-ingress.yaml");
}
private static InputStream getService() {
return Fabric8Utils.inputStream("spring-cloud-kubernetes-fabric8-client-loadbalancer-service.yaml");
}
private static InputStream getPodDeployment() {
return Fabric8Utils.inputStream("spring-cloud-kubernetes-fabric8-client-loadbalancer-pod-deployment.yaml");
}
private static InputStream getServiceDeployment() {
return Fabric8Utils.inputStream("spring-cloud-kubernetes-fabric8-client-loadbalancer-service-deployment.yaml");
}
private static InputStream getMockIngress() {
return Fabric8Utils.inputStream("wiremock/wiremock-ingress.yaml");
}
private static InputStream getMockService() {
return Fabric8Utils.inputStream("wiremock/wiremock-service.yaml");
}
private static InputStream getMockDeployment() {
return Fabric8Utils.inputStream("wiremock/wiremock-deployment.yaml");
}
private WebClient.Builder builder() {
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
}

View File

@@ -14,9 +14,6 @@ spec:
serviceAccountName: spring-cloud-kubernetes-serviceaccount
containers:
- name: spring-cloud-kubernetes-fabric8-client-loadbalancer
env:
- name: SPRING_CLOUD_KUBERNETES_LOADBALANCER_MODE
value: POD
image: docker.io/springcloud/spring-cloud-kubernetes-fabric8-client-loadbalancer
imagePullPolicy: IfNotPresent
readinessProbe:

View File

@@ -1,31 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-cloud-kubernetes-fabric8-client-loadbalancer-deployment
spec:
selector:
matchLabels:
app: spring-cloud-kubernetes-fabric8-client-loadbalancer
template:
metadata:
labels:
app: spring-cloud-kubernetes-fabric8-client-loadbalancer
spec:
serviceAccountName: spring-cloud-kubernetes-serviceaccount
containers:
- name: spring-cloud-kubernetes-fabric8-client-loadbalancer
env:
- name: SPRING_CLOUD_KUBERNETES_LOADBALANCER_MODE
value: SERVICE
image: docker.io/springcloud/spring-cloud-kubernetes-fabric8-client-loadbalancer
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet:
port: 8080
path: /loadbalancer-it/actuator/health/readiness
livenessProbe:
httpGet:
port: 8080
path: /loadbalancer-it/actuator/health/liveness
ports:
- containerPort: 8080

View File

@@ -1,27 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: servicea-wiremock-deployment
spec:
selector:
matchLabels:
app: servicea-wiremock
template:
metadata:
labels:
app: servicea-wiremock
spec:
containers:
- name: servicea-wiremock
image: wiremock/wiremock:2.32.0
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet:
port: 8080
path: /__admin/mappings
livenessProbe:
httpGet:
port: 8080
path: /__admin/mappings
ports:
- containerPort: 8080

View File

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

View File

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

View File

@@ -28,10 +28,7 @@ import io.fabric8.kubernetes.api.model.SecretBuilder;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import io.fabric8.kubernetes.client.dsl.internal.HasMetadataOperation;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
@@ -42,8 +39,8 @@ import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.Fabric8Utils;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
@@ -61,13 +58,7 @@ class SecretsEventsReloadIT {
private static KubernetesClient client;
private static String deploymentName;
private static String serviceName;
private static String ingressName;
private static String secretName;
private static Util util;
private static final K3sContainer K3S = Commons.container();
@@ -76,19 +67,19 @@ class SecretsEventsReloadIT {
K3S.start();
Commons.validateImage(IMAGE_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S);
Config config = Config.fromKubeconfig(K3S.getKubeConfigYaml());
client = new KubernetesClientBuilder().withConfig(config).build();
Fabric8Utils.setUp(client, NAMESPACE);
deployManifests();
util = new Util(K3S);
client = util.client();
util.setUp(NAMESPACE);
manifests(Phase.CREATE);
}
@AfterAll
static void after() throws Exception {
deleteManifests();
manifests(Phase.DELETE);
Commons.cleanUp(IMAGE_NAME, K3S);
}
@SuppressWarnings({ "raw", "unchecked" })
@Test
void test() {
WebClient webClient = builder().baseUrl("localhost/key").build();
@@ -106,83 +97,36 @@ class SecretsEventsReloadIT {
Base64.getEncoder().encodeToString("from.properties.key=after-change".getBytes())))
.build();
// the weird cast comes from :
// https://github.com/fabric8io/kubernetes-client/issues/2445
((HasMetadataOperation) client.secrets().inNamespace("default").withName("event-reload"))
.createOrReplace(secret);
client.secrets().inNamespace("default").resource(secret).createOrReplace();
await().timeout(Duration.ofSeconds(120)).until(() -> webClient.method(HttpMethod.GET).retrieve()
.bodyToMono(String.class).retryWhen(retrySpec()).block().equals("after-change"));
}
private static void deleteManifests() {
private static void manifests(Phase phase) {
try {
InputStream deploymentStream = util.inputStream("deployment.yaml");
InputStream serviceStream = util.inputStream("service.yaml");
InputStream ingressStream = util.inputStream("ingress.yaml");
InputStream secretStream = util.inputStream("secret.yaml");
client.secrets().inNamespace(NAMESPACE).withName(secretName).delete();
client.apps().deployments().inNamespace(NAMESPACE).withName(deploymentName).delete();
client.services().inNamespace(NAMESPACE).withName(serviceName).delete();
client.network().v1().ingresses().inNamespace(NAMESPACE).withName(ingressName).delete();
Deployment deployment = client.apps().deployments().load(deploymentStream).get();
Service service = client.services().load(serviceStream).get();
Ingress ingress = client.network().v1().ingresses().load(ingressStream).get();
Secret secret = client.secrets().load(secretStream).get();
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, secret);
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
catch (Exception e) {
throw new RuntimeException(e);
else {
util.deleteAndWait(NAMESPACE, null, secret);
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
private static void deployManifests() {
try {
Secret configMap = client.secrets().load(getSecret()).get();
secretName = configMap.getMetadata().getName();
client.secrets().resource(configMap).create();
Deployment deployment = client.apps().deployments().load(getDeployment()).get();
String version = K8SUtils.getPomVersion();
String currentImage = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(currentImage + ":" + version);
client.apps().deployments().inNamespace(NAMESPACE).resource(deployment).create();
deploymentName = deployment.getMetadata().getName();
Service service = client.services().load(getService()).get();
serviceName = service.getMetadata().getName();
client.services().inNamespace(NAMESPACE).resource(service).create();
Ingress ingress = client.network().v1().ingresses().load(getIngress()).get();
ingressName = ingress.getMetadata().getName();
client.network().v1().ingresses().inNamespace(NAMESPACE).resource(ingress).create();
Fabric8Utils.waitForDeployment(client,
"spring-cloud-kubernetes-fabric8-client-secrets-deployment-event-reload", NAMESPACE, 2, 600);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static InputStream getService() {
return Fabric8Utils.inputStream("service.yaml");
}
private static InputStream getDeployment() {
return Fabric8Utils.inputStream("deployment.yaml");
}
private static InputStream getIngress() {
return Fabric8Utils.inputStream("ingress.yaml");
}
private static InputStream getSecret() {
return Fabric8Utils.inputStream("secret.yaml");
}
private WebClient.Builder builder() {
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
}

View File

@@ -23,9 +23,7 @@ import java.util.Objects;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
@@ -36,8 +34,8 @@ import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.Fabric8Utils;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
@@ -53,11 +51,7 @@ class SimpleCoreIT {
private static KubernetesClient client;
private static String deploymentName;
private static String serviceName;
private static String ingressName;
private static Util util;
private static final K3sContainer K3S = Commons.container();
@@ -67,16 +61,16 @@ class SimpleCoreIT {
Commons.validateImage(IMAGE_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S);
Config config = Config.fromKubeconfig(K3S.getKubeConfigYaml());
client = new KubernetesClientBuilder().withConfig(config).build();
Fabric8Utils.setUp(client, NAMESPACE);
util = new Util(K3S);
client = util.client();
util.setUp(NAMESPACE);
deployManifests();
manifests(Phase.CREATE);
}
@AfterAll
static void after() throws Exception {
deleteManifests();
manifests(Phase.DELETE);
Commons.cleanUp(IMAGE_NAME, K3S);
}
@@ -91,64 +85,25 @@ class SimpleCoreIT {
Assertions.assertEquals("Hello from k8s profile", result);
}
private static void deleteManifests() {
private static void manifests(Phase phase) {
try {
InputStream deploymentStream = util.inputStream("simple-core-deployment.yaml");
InputStream serviceStream = util.inputStream("simple-core-service.yaml");
InputStream ingressStream = util.inputStream("simple-core-ingress.yaml");
client.apps().deployments().inNamespace(NAMESPACE).withName(deploymentName).delete();
client.services().inNamespace(NAMESPACE).withName(serviceName).delete();
client.network().v1().ingresses().inNamespace(NAMESPACE).withName(ingressName).delete();
Deployment deployment = client.apps().deployments().load(deploymentStream).get();
Service service = client.services().load(serviceStream).get();
Ingress ingress = client.network().v1().ingresses().load(ingressStream).get();
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
catch (Exception e) {
throw new RuntimeException(e);
else {
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
private static void deployManifests() {
try {
Deployment deployment = client.apps().deployments().load(getDeployment()).get();
String version = K8SUtils.getPomVersion();
String currentImage = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(currentImage + ":" + version);
client.apps().deployments().inNamespace(NAMESPACE).resource(deployment).create();
deploymentName = deployment.getMetadata().getName();
Service service = client.services().load(getService()).get();
serviceName = service.getMetadata().getName();
client.services().inNamespace(NAMESPACE).resource(service).create();
Ingress ingress = client.network().v1().ingresses().load(getIngress()).get();
ingressName = ingress.getMetadata().getName();
client.network().v1().ingresses().inNamespace(NAMESPACE).resource(ingress).create();
Fabric8Utils.waitForDeployment(client, "spring-cloud-kubernetes-fabric8-client-simple-core-deployment",
NAMESPACE, 2, 600);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static InputStream getService() {
return Fabric8Utils.inputStream("simple-core-service.yaml");
}
private static InputStream getDeployment() {
return Fabric8Utils.inputStream("simple-core-deployment.yaml");
}
private static InputStream getIngress() {
return Fabric8Utils.inputStream("simple-core-ingress.yaml");
}
private WebClient.Builder builder() {
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
}

View File

@@ -25,9 +25,7 @@ import java.util.Objects;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
@@ -38,8 +36,8 @@ import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
import org.springframework.cloud.kubernetes.integration.tests.commons.Fabric8Utils;
import org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
@@ -67,11 +65,7 @@ class Fabric8IstioIT {
private static KubernetesClient client;
private static String deploymentName;
private static String serviceName;
private static String ingressName;
private static Util util;
private static K3sContainer K3S;
@@ -81,6 +75,8 @@ class Fabric8IstioIT {
String absolutePath = new File(LOCAL_ISTIO_BIN_PATH).getAbsolutePath();
K3S = Commons.container().withFileSystemBind(absolutePath, CONTAINER_ISTIO_BIN_PATH);
K3S.start();
util = new Util(K3S);
client = util.client();
Commons.validateImage(IMAGE_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S);
@@ -93,21 +89,12 @@ class Fabric8IstioIT {
processExecResult(
K3S.execInContainer("sh", "-c", "kubectl label namespace istio-test istio-injection=enabled"));
// for Mac M1 with aarch64
if (System.getProperty("os.arch").equals("aarch64")) {
processExecResult(K3S.execInContainer("sh", "-c", CONTAINER_ISTIO_BIN_PATH + "istioctl"
+ " --kubeconfig=/etc/rancher/k3s/k3s.yaml install --set hub=docker.io/querycapistio --set profile=minimal -y"));
}
else {
processExecResult(K3S.execInContainer("sh", "-c", CONTAINER_ISTIO_BIN_PATH + "istioctl"
+ " --kubeconfig=/etc/rancher/k3s/k3s.yaml install --set profile=minimal -y"));
}
processExecResult(K3S.execInContainer("sh", "-c", CONTAINER_ISTIO_BIN_PATH + "istioctl"
+ " --kubeconfig=/etc/rancher/k3s/k3s.yaml install --set profile=minimal -y"));
Config config = Config.fromKubeconfig(K3S.getKubeConfigYaml());
client = new KubernetesClientBuilder().withConfig(config).build();
Fabric8Utils.setUpIstio(client, NAMESPACE);
util.setUpIstio(NAMESPACE);
deployManifests();
manifests(Phase.CREATE);
}
@AfterAll
@@ -117,7 +104,7 @@ class Fabric8IstioIT {
@AfterAll
static void after() {
deleteManifests();
manifests(Phase.DELETE);
}
@Test
@@ -132,67 +119,25 @@ class Fabric8IstioIT {
Assertions.assertTrue(result.contains("istio"));
}
private static void deleteManifests() {
private static void manifests(Phase phase) {
try {
InputStream deploymentStream = util.inputStream("istio-deployment.yaml");
InputStream serviceStream = util.inputStream("istio-service.yaml");
InputStream ingressStream = util.inputStream("istio-ingress.yaml");
client.apps().deployments().inNamespace(NAMESPACE).withName(deploymentName).delete();
client.services().inNamespace(NAMESPACE).withName(serviceName).delete();
client.network().v1().ingresses().inNamespace(NAMESPACE).withName(ingressName).delete();
Deployment deployment = client.apps().deployments().load(deploymentStream).get();
Service service = client.services().load(serviceStream).get();
Ingress ingress = client.network().v1().ingresses().load(ingressStream).get();
if (phase.equals(Phase.CREATE)) {
util.createAndWait(NAMESPACE, null, deployment, service, ingress, true);
}
catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
else {
util.deleteAndWait(NAMESPACE, deployment, service, ingress);
}
}
private static void deployManifests() {
try {
Deployment deployment = client.apps().deployments().load(getDeployment()).get();
String version = K8SUtils.getPomVersion();
String currentImage = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage(currentImage + ":" + version);
client.apps().deployments().inNamespace(NAMESPACE).resource(deployment).create();
deploymentName = deployment.getMetadata().getName();
Service service = client.services().load(getService()).get();
serviceName = service.getMetadata().getName();
client.services().inNamespace(NAMESPACE).resource(service).create();
Ingress ingress = client.network().v1().ingresses().load(getIngress()).get();
ingressName = ingress.getMetadata().getName();
client.network().v1().ingresses().inNamespace(NAMESPACE).resource(ingress).create();
Fabric8Utils.waitForIngress(client, ingressName, NAMESPACE);
Fabric8Utils.waitForDeployment(client, "spring-cloud-kubernetes-fabric8-istio-it-deployment", NAMESPACE, 2,
600);
}
catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private static InputStream getService() {
return Fabric8Utils.inputStream("istio-service.yaml");
}
private static InputStream getDeployment() {
return Fabric8Utils.inputStream("istio-deployment.yaml");
}
private static InputStream getIngress() {
return Fabric8Utils.inputStream("istio-ingress.yaml");
}
private WebClient.Builder builder() {
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
}

View File

@@ -17,7 +17,9 @@
package org.springframework.cloud.kubernetes.integration.tests.commons;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -32,7 +34,10 @@ import org.testcontainers.containers.Container;
import org.testcontainers.k3s.K3sContainer;
import org.testcontainers.utility.DockerImageName;
import static org.springframework.cloud.kubernetes.integration.tests.commons.K8SUtils.getPomVersion;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
/**
* A few commons things that can be re-used across clients. This is meant to be used for
@@ -46,6 +51,8 @@ public final class Commons {
throw new AssertionError("No instance provided");
}
private static final String KUBERNETES_VERSION_FILE = "META-INF/springcloudkubernetes-version.txt";
/**
* Rancher version to use for test-containers.
*/
@@ -76,7 +83,7 @@ public final class Commons {
}
public static void loadSpringCloudKubernetesImage(String project, K3sContainer container) throws Exception {
loadImage("springcloud/" + project, getPomVersion(), project, container);
loadImage("springcloud/" + project, pomVersion(), project, container);
}
public static void loadImage(String image, String tag, String tarName, K3sContainer container) throws Exception {
@@ -95,7 +102,7 @@ public final class Commons {
}
public static void cleanUp(String image, K3sContainer container) throws Exception {
container.execInContainer("crictl", "rmi", "docker.io/springcloud/" + image + ":" + getPomVersion());
container.execInContainer("crictl", "rmi", "docker.io/springcloud/" + image + ":" + pomVersion());
container.execInContainer("rm", TEMP_FOLDER + "/" + image + ".tar");
}
@@ -132,6 +139,21 @@ public final class Commons {
return execResult.getStdout();
}
public static String pomVersion() {
try (InputStream in = new ClassPathResource(KUBERNETES_VERSION_FILE).getInputStream()) {
String version = StreamUtils.copyToString(in, StandardCharsets.UTF_8);
if (StringUtils.hasText(version)) {
version = version.trim();
}
return version;
}
catch (IOException e) {
ReflectionUtils.rethrowRuntimeException(e);
}
// not reachable since exception rethrown at runtime
return null;
}
/**
* A K3sContainer, but with fixed port mappings. This is needed because of the nature
* of some integration tests.

View File

@@ -1,297 +0,0 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.integration.tests.commons;
import java.io.InputStream;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.Endpoints;
import io.fabric8.kubernetes.api.model.LoadBalancerIngress;
import io.fabric8.kubernetes.api.model.Namespace;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.ServiceAccount;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import io.fabric8.kubernetes.api.model.rbac.ClusterRole;
import io.fabric8.kubernetes.api.model.rbac.Role;
import io.fabric8.kubernetes.api.model.rbac.RoleBinding;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import static org.awaitility.Awaitility.await;
import static org.junit.Assert.fail;
/**
* @author wind57
*/
public final class Fabric8Utils {
private static final Log LOG = LogFactory.getLog(Fabric8Utils.class);
private Fabric8Utils() {
throw new AssertionError("no instance provided");
}
public static InputStream inputStream(String fileName) {
return Fabric8Utils.class.getClassLoader().getResourceAsStream(fileName);
}
public static void waitForDeployment(KubernetesClient client, String deploymentName, String namespace,
int pollSeconds, int maxSeconds) {
await().pollInterval(Duration.ofSeconds(pollSeconds)).atMost(maxSeconds, TimeUnit.SECONDS)
.until(() -> isDeploymentReady(client, deploymentName, namespace));
}
public static void waitForEndpoint(KubernetesClient client, String endpointName, String namespace, int pollSeconds,
int maxSeconds) {
await().pollInterval(Duration.ofSeconds(pollSeconds)).atMost(maxSeconds, TimeUnit.SECONDS)
.until(() -> isEndpointReady(client, endpointName, namespace));
}
private static boolean isDeploymentReady(KubernetesClient client, String deploymentName, String namespace) {
Deployment deployment = client.apps().deployments().inNamespace(namespace).withName(deploymentName).get();
Integer availableReplicas = deployment.getStatus().getAvailableReplicas();
LOG.info("Available replicas for " + deploymentName + ": " + ((availableReplicas == null) ? 0 : 1));
return availableReplicas != null && availableReplicas >= 1;
}
private static boolean isEndpointReady(KubernetesClient client, String endpointName, String namespace) {
Endpoints endpoint = client.endpoints().inNamespace(namespace).withName(endpointName).get();
if (endpoint.getSubsets().isEmpty()) {
fail("no endpoints for " + endpointName);
}
return endpoint.getSubsets().get(0).getAddresses().size() >= 1;
}
public static void setUp(KubernetesClient client, String namespace) throws Exception {
InputStream serviceAccountAsStream = inputStream("setup/service-account.yaml");
InputStream roleBindingAsStream = inputStream("setup/role-binding.yaml");
InputStream roleAsStream = inputStream("setup/role.yaml");
innerSetup(client, namespace, serviceAccountAsStream, roleBindingAsStream, roleAsStream);
}
public static void setUpClusterWide(KubernetesClient client, String serviceAccountNamespace,
Set<String> namespaces) {
InputStream clusterRoleBindingAsStream = inputStream("cluster/cluster-role.yaml");
InputStream serviceAccountAsStream = inputStream("cluster/service-account.yaml");
InputStream roleBindingAsStream = inputStream("cluster/role-binding.yaml");
ClusterRole clusterRole = client.rbac().clusterRoles().load(clusterRoleBindingAsStream).get();
if (client.rbac().clusterRoles().withName(clusterRole.getMetadata().getName()).get() == null) {
client.rbac().clusterRoles().resource(clusterRole).create();
}
ServiceAccount serviceAccountFromStream = client.serviceAccounts().load(serviceAccountAsStream).get();
serviceAccountFromStream.getMetadata().setNamespace(serviceAccountNamespace);
if (client.serviceAccounts().inNamespace(serviceAccountNamespace)
.withName(serviceAccountFromStream.getMetadata().getName()).get() == null) {
client.serviceAccounts().inNamespace(serviceAccountNamespace).resource(serviceAccountFromStream).create();
}
RoleBinding roleBindingFromStream = client.rbac().roleBindings().load(roleBindingAsStream).get();
namespaces.forEach(namespace -> {
roleBindingFromStream.getMetadata().setNamespace(namespace);
if (client.rbac().roleBindings().inNamespace(namespace)
.withName(roleBindingFromStream.getMetadata().getName()).get() == null) {
client.rbac().roleBindings().inNamespace(namespace).resource(roleBindingFromStream).create();
}
});
}
public static void cleanUpClusterWide(KubernetesClient client, String serviceAccountNamespace,
Set<String> namespaces) {
InputStream clusterRoleBindingAsStream = inputStream("cluster/cluster-role.yaml");
InputStream serviceAccountAsStream = inputStream("cluster/service-account.yaml");
InputStream roleBindingAsStream = inputStream("cluster/role-binding.yaml");
ClusterRole clusterRole = client.rbac().clusterRoles().load(clusterRoleBindingAsStream).get();
client.rbac().clusterRoles().withName(clusterRole.getMetadata().getName()).delete();
await().pollInterval(Duration.ofSeconds(1)).atMost(30, TimeUnit.SECONDS).until(() -> {
ClusterRole innerClusterRole = client.rbac().clusterRoles().withName(clusterRole.getMetadata().getName())
.get();
return innerClusterRole == null;
});
ServiceAccount serviceAccount = client.serviceAccounts().load(serviceAccountAsStream).get();
client.serviceAccounts().inNamespace(serviceAccountNamespace).withName(serviceAccount.getMetadata().getName())
.delete();
await().pollInterval(Duration.ofSeconds(1)).atMost(30, TimeUnit.SECONDS).until(() -> {
ServiceAccount innerServiceAccount = client.serviceAccounts().inNamespace(serviceAccountNamespace)
.withName(serviceAccount.getMetadata().getName()).get();
return innerServiceAccount == null;
});
RoleBinding roleBinding = client.rbac().roleBindings().load(roleBindingAsStream).get();
namespaces.forEach(namespace -> {
client.rbac().roleBindings().inNamespace(namespace).withName(roleBinding.getMetadata().getName()).delete();
await().pollInterval(Duration.ofSeconds(1)).atMost(30, TimeUnit.SECONDS).until(() -> {
RoleBinding innerRoleBinding = client.rbac().roleBindings().inNamespace(namespace)
.withName(roleBinding.getMetadata().getName()).get();
return innerRoleBinding == null;
});
});
}
public static void setUpIstio(KubernetesClient client, String namespace) {
InputStream serviceAccountAsStream = inputStream("istio/service-account.yaml");
InputStream roleBindingAsStream = inputStream("istio/role-binding.yaml");
InputStream roleAsStream = inputStream("istio/role.yaml");
innerSetup(client, namespace, serviceAccountAsStream, roleBindingAsStream, roleAsStream);
}
public static void waitForIngress(KubernetesClient client, String ingressName, String namespace) {
try {
await().pollInterval(Duration.ofSeconds(2)).atMost(180, TimeUnit.SECONDS).until(() -> {
Ingress ingress = client.network().v1().ingresses().inNamespace(namespace).withName(ingressName).get();
if (ingress == null) {
System.out.println("ingress : " + ingressName + " not ready yet present");
return false;
}
List<LoadBalancerIngress> loadBalancerIngress = ingress.getStatus().getLoadBalancer().getIngress();
if (loadBalancerIngress == null || loadBalancerIngress.isEmpty()) {
System.out.println(
"ingress : " + ingressName + " not ready yet (loadbalancer ingress not yet present)");
return false;
}
String ip = loadBalancerIngress.get(0).getIp();
if (ip == null) {
System.out.println("ingress : " + ingressName + " not ready yet");
return false;
}
System.out.println("ingress : " + ingressName + " ready with ip : " + ip);
return true;
});
}
catch (Exception e) {
System.out.println("Error waiting for ingress");
e.printStackTrace();
}
}
public static void waitForConfigMapDelete(KubernetesClient client, String namespace, String name) {
await().pollInterval(Duration.ofSeconds(1)).atMost(30, TimeUnit.SECONDS).until(() -> {
ConfigMap configMap = client.configMaps().inNamespace(namespace).withName(name).get();
return configMap == null;
});
}
/**
* delete a deployment and every pod by spec.select.matchLabels, waits until
* everything is deleted.
*/
public static void deleteDeployment(KubernetesClient client, String namespace, String name) {
Deployment deployment = client.apps().deployments().inNamespace(namespace).withName(name).get();
Map<String, String> matchLabels = deployment.getSpec().getSelector().getMatchLabels();
client.apps().deployments().inNamespace(namespace).resource(deployment).delete();
await().pollInterval(Duration.ofSeconds(1)).atMost(30, TimeUnit.SECONDS).until(() -> {
Deployment inner = client.apps().deployments().inNamespace(namespace).withName(name).get();
return inner == null;
});
await().pollInterval(Duration.ofSeconds(1)).atMost(60, TimeUnit.SECONDS).until(() -> {
List<Pod> podList = client.pods().inNamespace(namespace).withLabels(matchLabels).list().getItems();
return podList == null || podList.isEmpty();
});
}
/**
* delete the service and wait for it to be deleted.
*/
public static void deleteService(KubernetesClient client, String namespace, String name) {
client.services().inNamespace(namespace).withName(name).delete();
await().pollInterval(Duration.ofSeconds(1)).atMost(30, TimeUnit.SECONDS).until(() -> {
Service service = client.services().inNamespace(namespace).withName(name).get();
return service == null;
});
}
/**
* delete ingress and wait for it to be deleted.
*/
public static void deleteIngress(KubernetesClient client, String namespace, String name) {
client.network().v1().ingresses().inNamespace(namespace).withName(name).delete();
await().pollInterval(Duration.ofSeconds(1)).atMost(30, TimeUnit.SECONDS).until(() -> {
Ingress ingress = client.network().v1().ingresses().inNamespace(namespace).withName(name).get();
return ingress == null;
});
}
public static void deleteNamespace(KubernetesClient client, String name) {
client.namespaces().withName(name).delete();
await().pollInterval(Duration.ofSeconds(1)).atMost(30, TimeUnit.SECONDS).until(() -> {
Namespace namespace = client.namespaces().withName(name).get();
return namespace == null;
});
}
private static void innerSetup(KubernetesClient client, String namespace, InputStream serviceAccountAsStream,
InputStream roleBindingAsStream, InputStream roleAsStream) {
ServiceAccount serviceAccountFromStream = client.serviceAccounts().load(serviceAccountAsStream).get();
if (client.serviceAccounts().inNamespace(namespace).withName(serviceAccountFromStream.getMetadata().getName())
.get() == null) {
client.serviceAccounts().inNamespace(namespace).resource(serviceAccountFromStream).create();
}
RoleBinding roleBindingFromStream = client.rbac().roleBindings().load(roleBindingAsStream).get();
if (client.rbac().roleBindings().inNamespace(namespace).withName(roleBindingFromStream.getMetadata().getName())
.get() == null) {
client.rbac().roleBindings().inNamespace(namespace).resource(roleBindingFromStream).create();
}
Role roleFromStream = client.rbac().roles().load(roleAsStream).get();
if (client.rbac().roles().inNamespace(namespace).withName(roleFromStream.getMetadata().getName())
.get() == null) {
client.rbac().roles().inNamespace(namespace).resource(roleFromStream).create();
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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.integration.tests.commons;
/**
* @author wind57
*/
public enum Phase {
/**
* Apply the manifests.
*/
CREATE,
/**
* Deleted the manifests.
*/
DELETE
}

View File

@@ -0,0 +1,400 @@
/*
* 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.integration.tests.commons.fabric8_client;
import java.io.InputStream;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.LoadBalancerIngress;
import io.fabric8.kubernetes.api.model.NamespaceBuilder;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.ServiceAccount;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import io.fabric8.kubernetes.api.model.rbac.ClusterRole;
import io.fabric8.kubernetes.api.model.rbac.Role;
import io.fabric8.kubernetes.api.model.rbac.RoleBinding;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import jakarta.annotation.Nullable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.testcontainers.k3s.K3sContainer;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import static org.awaitility.Awaitility.await;
import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.loadImage;
import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.pomVersion;
import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.pullImage;
/**
* @author wind57
*/
public final class Util {
private static final Log LOG = LogFactory.getLog(Util.class);
private final K3sContainer container;
private final KubernetesClient client;
public Util(K3sContainer container) {
this.container = container;
this.client = new KubernetesClientBuilder().withConfig(Config.fromKubeconfig(container.getKubeConfigYaml()))
.build();
}
/**
* This is the preferred method to use when creating a deployment alongside with a
* service. It creates the given resources as-well as waits for them to be created.
* The delay check is intentionally not taken as an argument, so that it stays as
* tight as possible, providing reasonable defaults.
*
*/
public void createAndWait(String namespace, String name, Deployment deployment, Service service,
@Nullable Ingress ingress, boolean changeVersion) {
try {
String imageFromDeployment = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
if (changeVersion) {
deployment.getSpec().getTemplate().getSpec().getContainers().get(0)
.setImage(imageFromDeployment + ":" + pomVersion());
}
else {
String[] image = imageFromDeployment.split(":", 2);
pullImage(image[0], image[1], container);
loadImage(image[0], image[1], name, container);
}
client.apps().deployments().inNamespace(namespace).resource(deployment).create();
client.services().inNamespace(namespace).resource(service).create();
waitForDeployment(namespace, deployment);
if (ingress != null) {
client.network().v1().ingresses().inNamespace(namespace).resource(ingress).create();
waitForIngress(namespace, ingress);
}
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
public void busybox(String namespace, Phase phase) {
InputStream deploymentStream = inputStream("busybox/deployment.yaml");
InputStream serviceStream = inputStream("busybox/service.yaml");
Deployment deployment = client.apps().deployments().load(deploymentStream).get();
Service service = client.services().load(serviceStream).get();
if (phase.equals(Phase.CREATE)) {
createAndWait(namespace, "busybox", deployment, service, null, false);
}
else if (phase.equals(Phase.DELETE)) {
deleteAndWait(namespace, deployment, service, null);
}
}
public void deleteAndWait(String namespace, Deployment deployment, Service service, @Nullable Ingress ingress) {
try {
client.apps().deployments().inNamespace(namespace).resource(deployment).delete();
client.services().inNamespace(namespace).resource(service).delete();
waitForDeploymentToBeDeleted(namespace, deployment);
if (ingress != null) {
client.network().v1().ingresses().inNamespace(namespace).resource(ingress).delete();
waitForIngressToBeDeleted(namespace, ingress);
}
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
public void setUp(String namespace) throws Exception {
InputStream serviceAccountAsStream = inputStream("setup/service-account.yaml");
InputStream roleBindingAsStream = inputStream("setup/role-binding.yaml");
InputStream roleAsStream = inputStream("setup/role.yaml");
innerSetup(namespace, serviceAccountAsStream, roleBindingAsStream, roleAsStream);
}
public InputStream inputStream(String fileName) {
return Util.class.getClassLoader().getResourceAsStream(fileName);
}
public void createNamespace(String name) {
try {
client.namespaces().resource(new NamespaceBuilder().withNewMetadata().withName(name).and().build())
.create();
await().pollInterval(Duration.ofSeconds(1)).atMost(30, TimeUnit.SECONDS).until(() -> client.namespaces()
.list().getItems().stream().anyMatch(x -> x.getMetadata().getName().equals(name)));
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
public void deleteNamespace(String name) {
try {
client.namespaces().resource(new NamespaceBuilder().withNewMetadata().withName(name).and().build())
.delete();
await().pollInterval(Duration.ofSeconds(1)).atMost(30, TimeUnit.SECONDS).until(() -> client.namespaces()
.list().getItems().stream().noneMatch(x -> x.getMetadata().getName().equals(name)));
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
public void setUpClusterWide(String serviceAccountNamespace, Set<String> namespaces) {
InputStream clusterRoleBindingAsStream = inputStream("cluster/cluster-role.yaml");
InputStream serviceAccountAsStream = inputStream("cluster/service-account.yaml");
InputStream roleBindingAsStream = inputStream("cluster/role-binding.yaml");
ClusterRole clusterRole = client.rbac().clusterRoles().load(clusterRoleBindingAsStream).get();
if (client.rbac().clusterRoles().withName(clusterRole.getMetadata().getName()).get() == null) {
client.rbac().clusterRoles().resource(clusterRole).create();
}
ServiceAccount serviceAccountFromStream = client.serviceAccounts().load(serviceAccountAsStream).get();
serviceAccountFromStream.getMetadata().setNamespace(serviceAccountNamespace);
if (client.serviceAccounts().inNamespace(serviceAccountNamespace)
.withName(serviceAccountFromStream.getMetadata().getName()).get() == null) {
client.serviceAccounts().inNamespace(serviceAccountNamespace).resource(serviceAccountFromStream).create();
}
RoleBinding roleBindingFromStream = client.rbac().roleBindings().load(roleBindingAsStream).get();
namespaces.forEach(namespace -> {
roleBindingFromStream.getMetadata().setNamespace(namespace);
if (client.rbac().roleBindings().inNamespace(namespace)
.withName(roleBindingFromStream.getMetadata().getName()).get() == null) {
client.rbac().roleBindings().inNamespace(namespace).resource(roleBindingFromStream).create();
}
});
}
public void createAndWait(String namespace, @Nullable ConfigMap configMap, @Nullable Secret secret) {
if (configMap != null) {
client.configMaps().resource(configMap).create();
waitForConfigMap(namespace, configMap, Phase.CREATE);
}
if (secret != null) {
client.secrets().resource(secret).create();
waitForSecret(namespace, secret, Phase.CREATE);
}
}
public void deleteAndWait(String namespace, @Nullable ConfigMap configMap, @Nullable Secret secret) {
if (configMap != null) {
client.configMaps().resource(configMap).delete();
waitForConfigMap(namespace, configMap, Phase.DELETE);
}
if (secret != null) {
client.secrets().resource(secret).delete();
waitForSecret(namespace, secret, Phase.DELETE);
}
}
public void setUpIstio(String namespace) {
InputStream serviceAccountAsStream = inputStream("istio/service-account.yaml");
InputStream roleBindingAsStream = inputStream("istio/role-binding.yaml");
InputStream roleAsStream = inputStream("istio/role.yaml");
innerSetup(namespace, serviceAccountAsStream, roleBindingAsStream, roleAsStream);
}
private void waitForConfigMap(String namespace, ConfigMap configMap, Phase phase) {
String configMapName = configMapName(configMap);
await().pollInterval(Duration.ofSeconds(1)).atMost(600, TimeUnit.SECONDS).until(() -> {
int size = (int) client.configMaps().inNamespace(namespace).list().getItems().stream()
.filter(x -> x.getMetadata().getName().equals(configMapName)).count();
if (size == 0) {
return !phase.equals(Phase.CREATE);
}
return phase.equals(Phase.CREATE);
});
}
public void wiremock(String namespace, String path, Phase phase) {
InputStream deploymentStream = inputStream("wiremock/wiremock-deployment.yaml");
InputStream serviceStream = inputStream("wiremock/wiremock-service.yaml");
InputStream ingressStream = inputStream("wiremock/wiremock-ingress.yaml");
Deployment deployment = client.apps().deployments().load(deploymentStream).get();
Service service = client.services().load(serviceStream).get();
Ingress ingress = client.network().v1().ingresses().load(ingressStream).get();
if (phase.equals(Phase.CREATE)) {
deployment.getMetadata().setNamespace(namespace);
service.getMetadata().setNamespace(namespace);
ingress.getMetadata().setNamespace(namespace);
ingress.getSpec().getRules().get(0).getHttp().getPaths().get(0).setPath(path);
createAndWait(namespace, "wiremock", deployment, service, ingress, false);
}
else {
deleteAndWait(namespace, deployment, service, ingress);
}
}
private void waitForSecret(String namespace, Secret secret, Phase phase) {
String secretName = secretName(secret);
await().pollInterval(Duration.ofSeconds(1)).atMost(600, TimeUnit.SECONDS).until(() -> {
int size = (int) client.secrets().inNamespace(namespace).list().getItems().stream()
.filter(x -> x.getMetadata().getName().equals(secretName)).count();
if (size == 0) {
return !phase.equals(Phase.CREATE);
}
return phase.equals(Phase.CREATE);
});
}
private void waitForIngressToBeDeleted(String namespace, Ingress ingress) {
String ingressName = ingressName(ingress);
await().pollInterval(Duration.ofSeconds(1)).atMost(30, TimeUnit.SECONDS).until(() -> {
Ingress inner = client.network().v1().ingresses().inNamespace(namespace).withName(ingressName).get();
return inner == null;
});
}
private void waitForDeploymentToBeDeleted(String namespace, Deployment deployment) {
String deploymentName = deploymentName(deployment);
Map<String, String> matchLabels = deployment.getSpec().getSelector().getMatchLabels();
await().pollInterval(Duration.ofSeconds(1)).atMost(30, TimeUnit.SECONDS).until(() -> {
Deployment inner = client.apps().deployments().inNamespace(namespace).withName(deploymentName).get();
return inner == null;
});
await().pollInterval(Duration.ofSeconds(1)).atMost(60, TimeUnit.SECONDS).until(() -> {
List<Pod> podList = client.pods().inNamespace(namespace).withLabels(matchLabels).list().getItems();
return podList == null || podList.isEmpty();
});
}
private void waitForDeployment(String namespace, Deployment deployment) {
String deploymentName = deploymentName(deployment);
await().pollInterval(Duration.ofSeconds(2)).atMost(600, TimeUnit.SECONDS)
.until(() -> isDeploymentReady(namespace, deploymentName));
}
private boolean isDeploymentReady(String namespace, String deploymentName) {
Deployment deployment = client.apps().deployments().inNamespace(namespace).withName(deploymentName).get();
Integer availableReplicas = deployment.getStatus().getAvailableReplicas();
LOG.info("Available replicas for " + deploymentName + ": " + ((availableReplicas == null) ? 0 : 1));
return availableReplicas != null && availableReplicas >= 1;
}
public void waitForIngress(String namespace, Ingress ingress) {
String ingressName = ingressName(ingress);
try {
await().pollInterval(Duration.ofSeconds(2)).atMost(180, TimeUnit.SECONDS).until(() -> {
Ingress inner = client.network().v1().ingresses().inNamespace(namespace).withName(ingressName).get();
if (inner == null) {
LOG.info("ingress : " + ingressName + " not ready yet present");
return false;
}
List<LoadBalancerIngress> loadBalancerIngress = inner.getStatus().getLoadBalancer().getIngress();
if (loadBalancerIngress == null || loadBalancerIngress.isEmpty()) {
LOG.info("ingress : " + ingressName + " not ready yet (loadbalancer ingress not yet present)");
return false;
}
String ip = loadBalancerIngress.get(0).getIp();
if (ip == null) {
LOG.info("ingress : " + ingressName + " not ready yet");
return false;
}
LOG.info("ingress : " + ingressName + " ready with ip : " + ip);
return true;
});
}
catch (Exception e) {
LOG.error("Error waiting for ingress");
e.printStackTrace();
}
}
private void innerSetup(String namespace, InputStream serviceAccountAsStream, InputStream roleBindingAsStream,
InputStream roleAsStream) {
ServiceAccount serviceAccountFromStream = client.serviceAccounts().load(serviceAccountAsStream).get();
if (client.serviceAccounts().inNamespace(namespace).withName(serviceAccountFromStream.getMetadata().getName())
.get() == null) {
client.serviceAccounts().inNamespace(namespace).resource(serviceAccountFromStream).create();
}
RoleBinding roleBindingFromStream = client.rbac().roleBindings().load(roleBindingAsStream).get();
if (client.rbac().roleBindings().inNamespace(namespace).withName(roleBindingFromStream.getMetadata().getName())
.get() == null) {
client.rbac().roleBindings().inNamespace(namespace).resource(roleBindingFromStream).create();
}
Role roleFromStream = client.rbac().roles().load(roleAsStream).get();
if (client.rbac().roles().inNamespace(namespace).withName(roleFromStream.getMetadata().getName())
.get() == null) {
client.rbac().roles().inNamespace(namespace).resource(roleFromStream).create();
}
}
private String deploymentName(Deployment deployment) {
return deployment.getMetadata().getName();
}
private String ingressName(Ingress ingress) {
return ingress.getMetadata().getName();
}
private String configMapName(ConfigMap configMap) {
return configMap.getMetadata().getName();
}
private String secretName(Secret secret) {
return secret.getMetadata().getName();
}
public KubernetesClient client() {
return client;
}
}

View File

@@ -0,0 +1,517 @@
/*
* 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.integration.tests.commons.native_client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.time.Duration;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.openapi.apis.AppsV1Api;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.apis.NetworkingV1Api;
import io.kubernetes.client.openapi.apis.RbacAuthorizationV1Api;
import io.kubernetes.client.openapi.models.V1ClusterRole;
import io.kubernetes.client.openapi.models.V1ConfigMap;
import io.kubernetes.client.openapi.models.V1Deployment;
import io.kubernetes.client.openapi.models.V1DeploymentList;
import io.kubernetes.client.openapi.models.V1Ingress;
import io.kubernetes.client.openapi.models.V1LoadBalancerIngress;
import io.kubernetes.client.openapi.models.V1LoadBalancerStatus;
import io.kubernetes.client.openapi.models.V1NamespaceBuilder;
import io.kubernetes.client.openapi.models.V1Role;
import io.kubernetes.client.openapi.models.V1RoleBinding;
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.Yaml;
import jakarta.annotation.Nullable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.testcontainers.k3s.K3sContainer;
import org.springframework.cloud.kubernetes.integration.tests.commons.Phase;
import static org.awaitility.Awaitility.await;
import static org.junit.Assert.fail;
import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.loadImage;
import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.pomVersion;
import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.pullImage;
/**
* @author wind57
*/
public final class Util {
private static final Log LOG = LogFactory.getLog(Util.class);
private final CoreV1Api coreV1Api;
private final AppsV1Api appsV1Api;
private final NetworkingV1Api networkingV1Api;
private final RbacAuthorizationV1Api rbacApi;
private final K3sContainer container;
public Util(K3sContainer container) {
ApiClient client;
try {
client = Config.fromConfig(new StringReader(container.getKubeConfigYaml()));
}
catch (IOException e) {
throw new RuntimeException(e);
}
client.setHttpClient(client.getHttpClient().newBuilder().readTimeout(Duration.ofSeconds(15)).build());
client.setDebugging(false);
Configuration.setDefaultApiClient(client);
this.container = container;
this.coreV1Api = new CoreV1Api();
this.appsV1Api = new AppsV1Api();
this.networkingV1Api = new NetworkingV1Api();
rbacApi = new RbacAuthorizationV1Api();
}
/**
* This is the preferred method to use when creating a deployment alongside with a
* service. It creates the given resources as-well as waits for them to be created.
* The delay check is intentionally not taken as an argument, so that it stays as
* tight as possible, providing reasonable defaults.
*
*/
public void createAndWait(String namespace, String name, V1Deployment deployment, V1Service service,
@Nullable V1Ingress ingress, boolean changeVersion) {
try {
String imageFromDeployment = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage();
if (changeVersion) {
deployment.getSpec().getTemplate().getSpec().getContainers().get(0)
.setImage(imageFromDeployment + ":" + pomVersion());
}
else {
String[] image = imageFromDeployment.split(":", 2);
pullImage(image[0], image[1], container);
loadImage(image[0], image[1], name, container);
}
appsV1Api.createNamespacedDeployment(namespace, deployment, null, null, null, null);
coreV1Api.createNamespacedService(namespace, service, null, null, null, null);
waitForDeployment(namespace, deployment);
if (ingress != null) {
networkingV1Api.createNamespacedIngress(namespace, ingress, null, null, null, null);
waitForIngress(namespace, ingress);
}
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
public void createAndWait(String namespace, @Nullable V1ConfigMap configMap, @Nullable V1Secret secret) {
try {
if (configMap != null) {
coreV1Api.createNamespacedConfigMap(namespace, configMap, null, null, null, null);
waitForConfigMap(namespace, configMap, Phase.CREATE);
}
if (secret != null) {
coreV1Api.createNamespacedSecret(namespace, secret, null, null, null, null);
waitForSecret(namespace, secret, Phase.CREATE);
}
}
catch (ApiException e) {
throw new RuntimeException(e);
}
}
public void deleteAndWait(String namespace, @Nullable V1ConfigMap configMap, @Nullable V1Secret secret) {
try {
if (configMap != null) {
String configMapName = configMapName(configMap);
coreV1Api.deleteNamespacedConfigMap(configMapName, namespace, null, null, null, null, null, null);
waitForConfigMap(namespace, configMap, Phase.DELETE);
}
if (secret != null) {
String secretName = secretName(secret);
coreV1Api.deleteNamespacedSecret(secretName, namespace, null, null, null, null, null, null);
waitForSecret(namespace, secret, Phase.DELETE);
}
}
catch (ApiException e) {
throw new RuntimeException(e);
}
}
public void createNamespace(String name) {
try {
coreV1Api.createNamespace(new V1NamespaceBuilder().withNewMetadata().withName(name).and().build(), null,
null, null, null);
}
catch (ApiException e) {
throw new RuntimeException(e);
}
}
public void deleteAndWait(String namespace, V1Deployment deployment, V1Service service,
@Nullable V1Ingress ingress) {
String deploymentName = deploymentName(deployment);
String serviceName = serviceName(service);
try {
appsV1Api.deleteNamespacedDeployment(deploymentName, namespace, null, null, null, null, null, null);
coreV1Api.deleteNamespacedService(serviceName, namespace, null, null, null, null, null, null);
waitForDeploymentToBeDeleted(deploymentName, namespace);
if (ingress != null) {
String ingressName = ingressName(ingress);
networkingV1Api.deleteNamespacedIngress(ingressName, namespace, null, null, null, null, null, null);
waitForIngressToBeDeleted(ingressName, namespace);
}
}
catch (ApiException e) {
throw new RuntimeException(e);
}
}
public void busybox(String namespace, Phase phase) {
V1Deployment deployment = (V1Deployment) yaml("busybox/deployment.yaml");
V1Service service = (V1Service) yaml("busybox/service.yaml");
if (phase.equals(Phase.CREATE)) {
createAndWait(namespace, "busybox", deployment, service, null, false);
}
else if (phase.equals(Phase.DELETE)) {
deleteAndWait(namespace, deployment, service, null);
}
}
public void kafka(String namespace, Phase phase) {
V1Deployment deployment = (V1Deployment) yaml("kafka/kafka-deployment.yaml");
V1Service service = (V1Service) yaml("kafka/kafka-service.yaml");
if (phase.equals(Phase.CREATE)) {
createAndWait(namespace, "kafka", deployment, service, null, false);
}
else if (phase.equals(Phase.DELETE)) {
deleteAndWait(namespace, deployment, service, null);
}
}
public void rabbitMq(String namespace, Phase phase) {
V1Deployment deployment = (V1Deployment) yaml("rabbitmq/rabbitmq-deployment.yaml");
V1Service service = (V1Service) yaml("rabbitmq/rabbitmq-service.yaml");
if (phase.equals(Phase.CREATE)) {
createAndWait(namespace, "rabbitmq", deployment, service, null, false);
}
else if (phase.equals(Phase.DELETE)) {
deleteAndWait(namespace, deployment, service, null);
}
}
public void zookeeper(String namespace, Phase phase) {
V1Deployment deployment = (V1Deployment) yaml("zookeeper/zookeeper-deployment.yaml");
V1Service service = (V1Service) yaml("zookeeper/zookeeper-service.yaml");
if (phase.equals(Phase.CREATE)) {
createAndWait(namespace, "zookeeper", deployment, service, null, false);
}
else if (phase.equals(Phase.DELETE)) {
deleteAndWait(namespace, deployment, service, null);
}
}
/**
* reads a yaml from classpath, fails if not found.
*/
public Object yaml(String fileName) {
ClassLoader classLoader = Util.class.getClassLoader();
String file = new BufferedReader(new InputStreamReader(classLoader.getResourceAsStream(fileName))).lines()
.collect(Collectors.joining("\n"));
try {
return Yaml.load(file);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
public void setUp(String namespace) {
try {
V1ServiceAccount serviceAccount = (V1ServiceAccount) yaml("setup/service-account.yaml");
CheckedSupplier<V1ServiceAccount> accountSupplier = () -> coreV1Api
.readNamespacedServiceAccount(serviceAccount.getMetadata().getName(), namespace, null);
CheckedSupplier<V1ServiceAccount> accountDefaulter = () -> coreV1Api
.createNamespacedServiceAccount(namespace, serviceAccount, null, null, null, null);
notExistsHandler(accountSupplier, accountDefaulter);
V1RoleBinding roleBinding = (V1RoleBinding) yaml("setup/role-binding.yaml");
notExistsHandler(
() -> rbacApi.readNamespacedRoleBinding(roleBinding.getMetadata().getName(), namespace, null),
() -> rbacApi.createNamespacedRoleBinding(namespace, roleBinding, null, null, null, null));
V1Role role = (V1Role) yaml("setup/role.yaml");
notExistsHandler(() -> rbacApi.readNamespacedRole(role.getMetadata().getName(), namespace, null),
() -> rbacApi.createNamespacedRole(namespace, role, null, null, null, null));
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
public void setUpClusterWide(String serviceAccountNamespace, Set<String> namespaces) {
try {
V1ServiceAccount serviceAccount = (V1ServiceAccount) yaml("cluster/service-account.yaml");
CheckedSupplier<V1ServiceAccount> accountSupplier = () -> coreV1Api.readNamespacedServiceAccount(
serviceAccount.getMetadata().getName(), serviceAccountNamespace, null);
CheckedSupplier<V1ServiceAccount> accountDefaulter = () -> coreV1Api
.createNamespacedServiceAccount(serviceAccountNamespace, serviceAccount, null, null, null, null);
notExistsHandler(accountSupplier, accountDefaulter);
V1ClusterRole clusterRole = (V1ClusterRole) yaml("cluster/cluster-role.yaml");
notExistsHandler(() -> rbacApi.readClusterRole(clusterRole.getMetadata().getName(), null),
() -> rbacApi.createClusterRole(clusterRole, null, null, null, null));
V1RoleBinding roleBinding = (V1RoleBinding) yaml("cluster/role-binding.yaml");
namespaces.forEach(namespace -> {
roleBinding.getMetadata().setNamespace(namespace);
try {
notExistsHandler(
() -> rbacApi.readNamespacedRoleBinding(roleBinding.getMetadata().getName(), namespace,
null),
() -> rbacApi.createNamespacedRoleBinding(namespace, roleBinding, null, null, null, null));
}
catch (Exception e) {
throw new RuntimeException(e);
}
});
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
public void deleteNamespace(String name) {
try {
coreV1Api.deleteNamespace(name, null, null, null, null, null, null);
}
catch (ApiException e) {
throw new RuntimeException(e);
}
await().pollInterval(Duration.ofSeconds(1)).atMost(30, TimeUnit.SECONDS)
.until(() -> coreV1Api.listNamespace(null, null, null, null, null, null, null, null, null, null)
.getItems().stream().noneMatch(x -> x.getMetadata().getName().equals(name)));
}
public void wiremock(String namespace, String path, Phase phase) {
V1Deployment deployment = (V1Deployment) yaml("wiremock/wiremock-deployment.yaml");
V1Service service = (V1Service) yaml("wiremock/wiremock-service.yaml");
V1Ingress ingress = (V1Ingress) yaml("wiremock/wiremock-ingress.yaml");
if (phase.equals(Phase.CREATE)) {
deployment.getMetadata().setNamespace(namespace);
service.getMetadata().setNamespace(namespace);
ingress.getMetadata().setNamespace(namespace);
ingress.getSpec().getRules().get(0).getHttp().getPaths().get(0).setPath(path);
createAndWait(namespace, "wiremock", deployment, service, ingress, false);
}
else {
deleteAndWait(namespace, deployment, service, ingress);
}
}
private String deploymentName(V1Deployment deployment) {
return deployment.getMetadata().getName();
}
private String serviceName(V1Service service) {
return service.getMetadata().getName();
}
private String ingressName(V1Ingress ingress) {
return ingress.getMetadata().getName();
}
private String configMapName(V1ConfigMap configMap) {
return configMap.getMetadata().getName();
}
private String secretName(V1Secret secret) {
return secret.getMetadata().getName();
}
private void waitForDeployment(String namespace, V1Deployment deployment) {
String deploymentName = deploymentName(deployment);
await().pollInterval(Duration.ofSeconds(1)).atMost(600, TimeUnit.SECONDS)
.until(() -> isDeploymentReady(deploymentName, namespace));
}
private void waitForConfigMap(String namespace, V1ConfigMap configMap, Phase phase) {
String configMapName = configMapName(configMap);
await().pollInterval(Duration.ofSeconds(1)).atMost(600, TimeUnit.SECONDS).until(() -> {
try {
coreV1Api.readNamespacedConfigMap(configMapName, namespace, null);
return phase.equals(Phase.CREATE);
}
catch (ApiException e) {
if (e.getCode() == HttpURLConnection.HTTP_NOT_FOUND) {
return !phase.equals(Phase.CREATE);
}
throw new RuntimeException(e);
}
});
}
private void waitForSecret(String namespace, V1Secret secret, Phase phase) {
String secretName = secretName(secret);
await().pollInterval(Duration.ofSeconds(1)).atMost(600, TimeUnit.SECONDS).until(() -> {
try {
coreV1Api.readNamespacedSecret(secretName, namespace, null);
return phase.equals(Phase.CREATE);
}
catch (ApiException e) {
if (e.getCode() == HttpURLConnection.HTTP_NOT_FOUND) {
return !phase.equals(Phase.CREATE);
}
throw new RuntimeException(e);
}
});
}
private void waitForIngress(String namespace, V1Ingress ingress) {
String ingressName = ingressName(ingress);
await().timeout(Duration.ofSeconds(90)).pollInterval(Duration.ofSeconds(3)).until(() -> {
try {
V1LoadBalancerStatus status = networkingV1Api.readNamespacedIngress(ingressName, namespace, null)
.getStatus().getLoadBalancer();
if (status == null) {
LOG.info("ingress : " + ingressName + " not ready yet (loadbalancer not yet present)");
return false;
}
List<V1LoadBalancerIngress> loadBalancerIngress = status.getIngress();
if (loadBalancerIngress == null) {
LOG.info("ingress : " + ingressName + " not ready yet (loadbalancer ingress not yet present)");
return false;
}
String ip = loadBalancerIngress.get(0).getIp();
if (ip == null) {
LOG.info("ingress : " + ingressName + " not ready yet");
return false;
}
LOG.info("ingress : " + ingressName + " ready with ip : " + ip);
return true;
}
catch (ApiException e) {
if (e.getCode() == HttpURLConnection.HTTP_NOT_FOUND) {
return false;
}
throw new RuntimeException(e);
}
});
}
private void waitForDeploymentToBeDeleted(String deploymentName, String namespace) {
await().timeout(Duration.ofSeconds(90)).until(() -> {
try {
appsV1Api.readNamespacedDeployment(deploymentName, namespace, null);
return false;
}
catch (ApiException e) {
if (e.getCode() == HttpURLConnection.HTTP_NOT_FOUND) {
return true;
}
throw new RuntimeException(e);
}
});
}
private void waitForIngressToBeDeleted(String ingressName, String namespace) {
await().timeout(Duration.ofSeconds(90)).until(() -> {
try {
networkingV1Api.readNamespacedIngress(ingressName, namespace, null);
return false;
}
catch (ApiException e) {
if (e.getCode() == HttpURLConnection.HTTP_NOT_FOUND) {
return true;
}
throw new RuntimeException(e);
}
});
}
private boolean isDeploymentReady(String deploymentName, String namespace) throws ApiException {
V1DeploymentList deployments = appsV1Api.listNamespacedDeployment(namespace, null, null, null,
"metadata.name=" + deploymentName, null, null, null, null, null, null);
if (deployments.getItems().size() < 1) {
fail("No deployments with the name " + deploymentName);
}
V1Deployment deployment = deployments.getItems().get(0);
Integer availableReplicas = deployment.getStatus().getAvailableReplicas();
LOG.info("Available replicas for " + deploymentName + ": "
+ (availableReplicas == null ? 0 : availableReplicas));
return availableReplicas != null && availableReplicas >= 1;
}
private static <T> void notExistsHandler(CheckedSupplier<T> callee, CheckedSupplier<T> defaulter) throws Exception {
try {
callee.get();
}
catch (Exception exception) {
if (exception instanceof ApiException apiException) {
if (apiException.getCode() == 404) {
defaulter.get();
return;
}
}
throw new RuntimeException(exception);
}
}
private interface CheckedSupplier<T> {
T get() throws Exception;
}
}

View File

@@ -0,0 +1,24 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: rabbitmq
labels:
app: taskqueue
component: rabbitmq
spec:
selector:
matchLabels:
app: taskqueue
component: rabbitmq
template:
metadata:
labels:
app: taskqueue
component: rabbitmq
spec:
containers:
- name: taskqueue
image: rabbitmq:3-management
imagePullPolicy: IfNotPresent

View File

@@ -1,19 +1,19 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: servicea-wiremock-deployment
name: service-wiremock-deployment
spec:
selector:
matchLabels:
app: servicea-wiremock
app: service-wiremock
template:
metadata:
labels:
app: servicea-wiremock
app: service-wiremock
spec:
containers:
- name: servicea-wiremock
image: wiremock/wiremock:2.32.0
- name: service-wiremock
image: wiremock/wiremock:2.35.0
args: ["--verbose"]
imagePullPolicy: IfNotPresent
readinessProbe:

View File

@@ -11,7 +11,7 @@ spec:
pathType: Prefix
backend:
service:
name: servicea-wiremock
name: service-wiremock
port:
number: 8080

View File

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

View File

@@ -2,13 +2,13 @@ apiVersion: v1
kind: Service
metadata:
labels:
app: servicea-wiremock
name: servicea-wiremock
app: service-wiremock
name: service-wiremock
spec:
ports:
- name: http
port: 8080
targetPort: 8080
selector:
app: servicea-wiremock
app: service-wiremock
type: ClusterIP