Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor fabric8 catalog watch integration test using PATCH #1450

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
bbc75ba
started work
wind57 Sep 22, 2023
4db16c2
dirty
wind57 Sep 25, 2023
ab5796b
test
wind57 Sep 26, 2023
80c100c
placeholder commit
wind57 Sep 26, 2023
4763bdc
test
wind57 Sep 26, 2023
970c875
test
wind57 Sep 26, 2023
edcc996
test
wind57 Sep 26, 2023
82d5f2f
test
wind57 Sep 26, 2023
2b56337
test
wind57 Sep 26, 2023
dcbc5de
test
wind57 Sep 26, 2023
d48d61f
test
wind57 Sep 26, 2023
db8d950
test
wind57 Sep 26, 2023
ae31faf
test
wind57 Sep 26, 2023
dfca45b
test
wind57 Sep 26, 2023
34648eb
placeholder commit
wind57 Sep 26, 2023
1eacf6c
test
wind57 Sep 26, 2023
c77b86a
test
wind57 Sep 26, 2023
694f50c
dirty
wind57 Sep 26, 2023
5c244f5
review comments
wind57 Sep 26, 2023
6810e8f
test
wind57 Sep 26, 2023
dc62bbc
test
wind57 Sep 26, 2023
5d5ea38
test
wind57 Sep 26, 2023
f31d741
test
wind57 Sep 26, 2023
8de4b9c
test
wind57 Sep 26, 2023
7715ca5
test
wind57 Sep 26, 2023
40c4514
test
wind57 Sep 26, 2023
67d1cc4
test
wind57 Sep 26, 2023
622cc94
test
wind57 Sep 26, 2023
78734c1
test
wind57 Sep 26, 2023
c2ad21e
test
wind57 Sep 26, 2023
1d853d2
test
wind57 Sep 26, 2023
5e985c5
test
wind57 Sep 26, 2023
c982060
test
wind57 Sep 26, 2023
8a42c15
fix build
wind57 Sep 26, 2023
6a07b13
placeholder commit
wind57 Sep 26, 2023
e9077e2
review comments
wind57 Sep 27, 2023
9346620
test
wind57 Sep 27, 2023
fb064d6
Merge branch 'improve-build-times' into refactor-fabric8-catalog-watc…
wind57 Sep 27, 2023
bd77fde
merge 3.0.x
wind57 Sep 28, 2023
d5ba80e
placeholder commit
wind57 Sep 28, 2023
6c0de29
merge 3.0.x
wind57 Oct 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,17 @@
import java.io.InputStream;
import java.time.Duration;
import java.util.List;
import java.util.Objects;
import java.util.Set;

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.KubernetesClient;
import io.fabric8.kubernetes.client.utils.Serialization;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.testcontainers.k3s.K3sContainer;
import reactor.netty.http.client.HttpClient;
import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;

import org.springframework.cloud.kubernetes.commons.discovery.EndpointNameAndNamespace;
import org.springframework.cloud.kubernetes.integration.tests.commons.Commons;
Expand All @@ -43,46 +38,61 @@
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.awaitility.Awaitility.await;
import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchUtil.builder;
import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchUtil.patchForEndpointSlices;
import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchUtil.patchForNamespaceFilterAndEndpointSlices;
import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchUtil.patchForNamespaceFilterAndEndpoints;
import static org.springframework.cloud.kubernetes.fabric8.catalog.watch.Fabric8CatalogWatchUtil.retrySpec;
import static org.springframework.cloud.kubernetes.integration.tests.commons.Commons.pomVersion;

/**
* @author wind57
*/
class Fabric8CatalogWatchIT {

private static final String APP_NAME = "spring-cloud-kubernetes-fabric8-client-catalog-watcher";

private static final String NAMESPACE = "default";

private static final K3sContainer K3S = Commons.container();
public static final String NAMESPACE_A = "namespacea";

public static final String NAMESPACE_B = "namespaceb";

private static final String IMAGE_NAME = "spring-cloud-kubernetes-fabric8-client-catalog-watcher";

private static KubernetesClient client;
private static final String DOCKER_IMAGE = "docker.io/springcloud/" + IMAGE_NAME + ":" + pomVersion();

private static final K3sContainer K3S = Commons.container();

private static Util util;

@BeforeAll
static void beforeAll() throws Exception {
K3S.start();
Commons.validateImage(IMAGE_NAME, K3S);
Commons.loadSpringCloudKubernetesImage(IMAGE_NAME, K3S);

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.setUp(NAMESPACE);
util.setUpClusterWide(NAMESPACE, Set.of(NAMESPACE, NAMESPACE_A, NAMESPACE_B));
util.busybox(NAMESPACE, Phase.CREATE);

app(Phase.CREATE);
}

@AfterAll
static void afterAll() {
Commons.systemPrune();
}

@BeforeEach
void beforeEach() {
util.busybox(NAMESPACE, Phase.CREATE);
util.deleteNamespace(NAMESPACE_A);
util.deleteNamespace(NAMESPACE_B);

app(Phase.DELETE);
Commons.systemPrune();
}

/**
Expand All @@ -95,32 +105,49 @@ void beforeEach() {
*/
@Test
void testCatalogWatchWithEndpoints() throws Exception {
app(false, Phase.CREATE);
assertLogStatement("stateGenerator is of type: Fabric8EndpointsCatalogWatch");
assertLogStatement();
test();
app(false, Phase.DELETE);

testCatalogWatchWithEndpointSlices();
testCatalogWatchWithNamespaceFilterAndEndpoints();
testCatalogWatchWithNamespaceFilterAndEndpointSlices();
}

@Test
void testCatalogWatchWithEndpointSlices() throws Exception {
app(true, Phase.CREATE);
assertLogStatement("stateGenerator is of type: Fabric8EndpointSliceV1CatalogWatch");
void testCatalogWatchWithEndpointSlices() {
util.busybox(NAMESPACE, Phase.CREATE);
patchForEndpointSlices(util, DOCKER_IMAGE, IMAGE_NAME, NAMESPACE);
Commons.waitForLogStatement("stateGenerator is of type: Fabric8EndpointSliceV1CatalogWatch", K3S, IMAGE_NAME);
test();
app(true, Phase.DELETE);
}

void testCatalogWatchWithNamespaceFilterAndEndpoints() {
util.busybox(NAMESPACE_A, Phase.CREATE);
util.busybox(NAMESPACE_B, Phase.CREATE);
patchForNamespaceFilterAndEndpoints(util, DOCKER_IMAGE, IMAGE_NAME, NAMESPACE);
Fabric8CatalogWatchWithNamespacesDelegate.testCatalogWatchWithNamespaceFilterAndEndpoints(K3S, IMAGE_NAME,
util);
}

void testCatalogWatchWithNamespaceFilterAndEndpointSlices() {
util.busybox(NAMESPACE_A, Phase.CREATE);
util.busybox(NAMESPACE_B, Phase.CREATE);
patchForNamespaceFilterAndEndpointSlices(util, DOCKER_IMAGE, IMAGE_NAME, NAMESPACE);
Fabric8CatalogWatchWithNamespacesDelegate.testCatalogWatchWithNamespaceFilterAndEndpointSlices(K3S, IMAGE_NAME,
util);
}

/**
* we log in debug mode the type of the StateGenerator we use, be that Endpoints or
* EndpointSlices. Here we make sure that in the test we actually use the correct
* type.
*/
private void assertLogStatement(String log) throws Exception {
private void assertLogStatement() throws Exception {
String appPodName = K3S
.execInContainer("kubectl", "get", "pods", "-l",
"app=spring-cloud-kubernetes-fabric8-client-catalog-watcher", "-o=name", "--no-headers")
.getStdout();
String allLogs = K3S.execInContainer("kubectl", "logs", appPodName.trim()).getStdout();
Assertions.assertTrue(allLogs.contains(log));
Assertions.assertTrue(allLogs.contains("stateGenerator is of type: Fabric8EndpointsCatalogWatch"));
}

/**
Expand All @@ -143,6 +170,9 @@ private void test() {
// watcher implementation
// we will get the first busybox instances here.
if (result != null) {
if (result.size() != 3) {
return false;
}
holder[0] = result.get(0);
holder[1] = result.get(1);
return true;
Expand Down Expand Up @@ -190,21 +220,18 @@ private void test() {
return false;
});

Assertions.assertTrue(afterDelete[0].endpointName().contains(APP_NAME));
Assertions.assertTrue(afterDelete[0].endpointName().contains(IMAGE_NAME));
Assertions.assertEquals("default", afterDelete[0].namespace());

}

private static void app(boolean useEndpointSlices, Phase phase) {
private static void app(Phase phase) {

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");

Deployment deployment = useEndpointSlices
? Serialization.unmarshal(endpointSlicesDeploymentStream, Deployment.class)
: Serialization.unmarshal(endpointsDeploymentStream, Deployment.class);
Deployment deployment = Serialization.unmarshal(endpointsDeploymentStream, Deployment.class);
Service service = Serialization.unmarshal(serviceStream, Service.class);
Ingress ingress = Serialization.unmarshal(ingressStream, Ingress.class);

Expand All @@ -217,12 +244,4 @@ private static void app(boolean useEndpointSlices, Phase phase) {

}

private WebClient.Builder builder() {
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
}

private RetryBackoffSpec retrySpec() {
return Retry.fixedDelay(15, Duration.ofSeconds(1)).filter(Objects::nonNull);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* Copyright 2013-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.cloud.kubernetes.fabric8.catalog.watch;

import java.time.Duration;
import java.util.Map;
import java.util.Objects;

import reactor.netty.http.client.HttpClient;
import reactor.util.retry.Retry;
import reactor.util.retry.RetryBackoffSpec;

import org.springframework.cloud.kubernetes.integration.tests.commons.fabric8_client.Util;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;

/**
* @author wind57
*/
final class Fabric8CatalogWatchUtil {

private static final Map<String, String> POD_LABELS = Map.of("app",
"spring-cloud-kubernetes-fabric8-client-catalog-watcher");

private Fabric8CatalogWatchUtil() {

}

static final String BODY_ONE = """
{
"spec": {
"template": {
"spec": {
"containers": [{
"name": "spring-cloud-kubernetes-fabric8-client-catalog-watcher",
"image": "image_name_here",
"env": [
{
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY",
"value": "DEBUG"
},
{
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_USE_ENDPOINT_SLICES",
"value": "TRUE"
}
]
}]
}
}
}
}
""";

static final String BODY_TWO = """
{
"spec": {
"template": {
"spec": {
"containers": [{
"name": "spring-cloud-kubernetes-fabric8-client-catalog-watcher",
"image": "image_name_here",
"env": [
{
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY",
"value": "DEBUG"
},
{
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_USE_ENDPOINT_SLICES",
"value": "FALSE"
},
{
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0",
"value": "namespacea"
},
{
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1",
"value": "default"
}
]
}]
}
}
}
}
""";

static final String BODY_THREE = """
{
"spec": {
"template": {
"spec": {
"containers": [{
"name": "spring-cloud-kubernetes-fabric8-client-catalog-watcher",
"image": "image_name_here",
"env": [
{
"name": "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_DISCOVERY",
"value": "DEBUG"
},
{
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_USE_ENDPOINT_SLICES",
"value": "TRUE"
},
{
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_0",
"value": "namespacea"
},
{
"name": "SPRING_CLOUD_KUBERNETES_DISCOVERY_NAMESPACES_1",
"value": "default"
}
]
}]
}
}
}
}
""";

static void patchForEndpointSlices(Util util, String dockerImage, String deploymentName, String namespace) {
util.patchWithReplace(dockerImage, deploymentName, namespace, BODY_ONE, POD_LABELS);
}

static void patchForNamespaceFilterAndEndpoints(Util util, String dockerImage, String deploymentName,
String namespace) {
util.patchWithReplace(dockerImage, deploymentName, namespace, BODY_TWO, POD_LABELS);
}

static void patchForNamespaceFilterAndEndpointSlices(Util util, String dockerImage, String deploymentName,
String namespace) {
util.patchWithReplace(dockerImage, deploymentName, namespace, BODY_THREE, POD_LABELS);
}

static WebClient.Builder builder() {
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create()));
}

static RetryBackoffSpec retrySpec() {
return Retry.fixedDelay(15, Duration.ofSeconds(1)).filter(Objects::nonNull);
}

}
Loading