Skip to content

Commit

Permalink
Merge branch 'main' into major-changes-in-fabric8-discovery-implement…
Browse files Browse the repository at this point in the history
…ation
  • Loading branch information
wind57 committed Oct 2, 2023
2 parents 0a97238 + 2925ee1 commit a439758
Show file tree
Hide file tree
Showing 8 changed files with 377 additions and 338 deletions.
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

0 comments on commit a439758

Please sign in to comment.