Skip to content
This repository was archived by the owner on Apr 10, 2024. It is now read-only.

Commit aedd944

Browse files
authored
feat: support for injecting fabric8 client in unit tests (#73)
1 parent fa42821 commit aedd944

18 files changed

+323
-119
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package io.javaoperatorsdk.jenvtest.junit;
2+
3+
import org.junit.jupiter.api.extension.ExtensionContext;
4+
5+
import io.javaoperatorsdk.jenvtest.KubeAPIServer;
6+
7+
public interface ClientInjectionHandler {
8+
9+
boolean isTargetFieldAvailable(ExtensionContext extensionContext,
10+
boolean staticField);
11+
12+
void inject(ExtensionContext extensionContext,
13+
boolean staticField, KubeAPIServer kubeApiServer);
14+
}

core/src/main/java/io/javaoperatorsdk/jenvtest/junit/KubeAPIServerExtension.java

Lines changed: 13 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package io.javaoperatorsdk.jenvtest.junit;
22

33
import java.lang.reflect.AnnotatedElement;
4-
import java.lang.reflect.Field;
54
import java.util.Arrays;
65
import java.util.List;
76
import java.util.Optional;
7+
import java.util.ServiceLoader;
88
import java.util.stream.Collectors;
99

1010
import org.junit.jupiter.api.extension.*;
@@ -24,6 +24,13 @@ public class KubeAPIServerExtension
2424
private static final Logger log = LoggerFactory.getLogger(KubeAPIServerExtension.class);
2525

2626
private KubeAPIServer kubeApiServer;
27+
private List<ClientInjectionHandler> clientInjectionHandlers;
28+
29+
public KubeAPIServerExtension() {
30+
var loader = ServiceLoader.load(ClientInjectionHandler.class);
31+
clientInjectionHandlers =
32+
loader.stream().map(ServiceLoader.Provider::get).collect(Collectors.toList());
33+
}
2734

2835
@Override
2936
public void beforeAll(ExtensionContext extensionContext) {
@@ -47,47 +54,13 @@ public void afterEach(ExtensionContext extensionContext) {
4754

4855

4956
private void initialize(ExtensionContext extensionContext, boolean staticContext) {
50-
var kubeConfigField = getFieldForKubeConfigInjection(extensionContext, staticContext);
51-
startIfAnnotationPresent(extensionContext, kubeConfigField.isEmpty());
52-
kubeConfigField.ifPresent(f -> setKubeConfigYamlToField(extensionContext, f));
53-
}
54-
55-
private void setKubeConfigYamlToField(ExtensionContext extensionContext, Field kubeConfigField) {
56-
try {
57-
var target = extensionContext.getTestInstance()
58-
.orElseGet(() -> extensionContext.getTestClass().orElseThrow());
59-
kubeConfigField.setAccessible(true);
60-
kubeConfigField.set(target,
61-
kubeApiServer.getKubeConfigYaml());
62-
} catch (IllegalAccessException e) {
63-
throw new JenvtestException(e);
64-
}
65-
}
66-
67-
private Optional<Field> getFieldForKubeConfigInjection(ExtensionContext extensionContext,
68-
boolean findStatic) {
69-
Class<?> clazz = extensionContext.getTestClass().orElseThrow();
70-
var kubeConfigFields = Arrays.stream(clazz.getDeclaredFields())
71-
.filter(f -> f.getAnnotationsByType(KubeConfig.class).length > 0)
57+
var targetInjectors = clientInjectionHandlers.stream()
58+
.filter(h -> h.isTargetFieldAvailable(extensionContext, staticContext))
7259
.collect(Collectors.toList());
73-
if (kubeConfigFields.isEmpty()) {
74-
return Optional.empty();
75-
}
76-
if (kubeConfigFields.size() > 1) {
77-
throw new JenvtestException(
78-
"More fields annotation with @" + KubeConfig.class.getSimpleName() + " annotation");
79-
}
80-
var field = kubeConfigFields.get(0);
81-
if (!field.getType().equals(String.class)) {
82-
throw new JenvtestException(
83-
"Field annotated with @" + KubeConfig.class.getSimpleName() + " is not a String");
84-
}
8560

86-
if (java.lang.reflect.Modifier.isStatic(field.getModifiers()) != findStatic) {
87-
return Optional.empty();
88-
} else {
89-
return Optional.of(field);
90-
}
61+
startIfAnnotationPresent(extensionContext, targetInjectors.isEmpty());
62+
63+
targetInjectors.forEach(i -> i.inject(extensionContext, staticContext, kubeApiServer));
9164
}
9265

9366
private void startIfAnnotationPresent(ExtensionContext extensionContext,
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package io.javaoperatorsdk.jenvtest.junit;
2+
3+
import java.lang.reflect.Field;
4+
import java.util.Arrays;
5+
import java.util.Optional;
6+
import java.util.stream.Collectors;
7+
8+
import org.junit.jupiter.api.extension.ExtensionContext;
9+
10+
import io.javaoperatorsdk.jenvtest.JenvtestException;
11+
import io.javaoperatorsdk.jenvtest.KubeAPIServer;
12+
13+
public class KubeConfigInjectionHandler implements ClientInjectionHandler {
14+
15+
public boolean isTargetFieldAvailable(ExtensionContext extensionContext,
16+
boolean staticField) {
17+
return getFieldForKubeConfigInjection(extensionContext, staticField).isPresent();
18+
}
19+
20+
public void inject(ExtensionContext extensionContext,
21+
boolean staticField, KubeAPIServer kubeApiServer) {
22+
var field = getFieldForKubeConfigInjection(extensionContext, staticField).orElseThrow();
23+
setKubeConfigYamlToField(extensionContext, field, kubeApiServer);
24+
}
25+
26+
private void setKubeConfigYamlToField(ExtensionContext extensionContext,
27+
Field kubeConfigField, KubeAPIServer kubeApiServer) {
28+
try {
29+
var target = extensionContext.getTestInstance()
30+
.orElseGet(() -> extensionContext.getTestClass().orElseThrow());
31+
kubeConfigField.setAccessible(true);
32+
kubeConfigField.set(target,
33+
kubeApiServer.getKubeConfigYaml());
34+
} catch (IllegalAccessException e) {
35+
throw new JenvtestException(e);
36+
}
37+
}
38+
39+
public Optional<Field> getFieldForKubeConfigInjection(ExtensionContext extensionContext,
40+
boolean staticField) {
41+
Class<?> clazz = extensionContext.getTestClass().orElseThrow();
42+
var kubeConfigFields = Arrays.stream(clazz.getDeclaredFields())
43+
.filter(f -> f.getAnnotationsByType(KubeConfig.class).length > 0)
44+
.collect(Collectors.toList());
45+
if (kubeConfigFields.isEmpty()) {
46+
return Optional.empty();
47+
}
48+
if (kubeConfigFields.size() > 1) {
49+
throw new JenvtestException(
50+
"More fields annotation with @" + KubeConfig.class.getSimpleName() + " annotation");
51+
}
52+
var field = kubeConfigFields.get(0);
53+
if (!field.getType().equals(String.class)) {
54+
throw new JenvtestException(
55+
"Field annotated with @" + KubeConfig.class.getSimpleName() + " is not a String");
56+
}
57+
58+
if (java.lang.reflect.Modifier.isStatic(field.getModifiers()) != staticField) {
59+
return Optional.empty();
60+
} else {
61+
return Optional.of(field);
62+
}
63+
}
64+
65+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
io.javaoperatorsdk.jenvtest.junit.KubeConfigInjectionHandler

fabric8/pom.xml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<parent>
7+
<groupId>io.javaoperatorsdk</groupId>
8+
<artifactId>jenvtest-project</artifactId>
9+
<version>0.5.1-SNAPSHOT</version>
10+
</parent>
11+
12+
<artifactId>jenvtest-fabric8-client</artifactId>
13+
<packaging>jar</packaging>
14+
<name>JEnvTest Fabric8 Client Support</name>
15+
16+
<dependencies>
17+
<dependency>
18+
<groupId>io.javaoperatorsdk</groupId>
19+
<artifactId>jenvtest</artifactId>
20+
<version>${project.version}</version>
21+
</dependency>
22+
<dependency>
23+
<groupId>org.junit.jupiter</groupId>
24+
<artifactId>junit-jupiter-api</artifactId>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.junit.jupiter</groupId>
28+
<artifactId>junit-jupiter-engine</artifactId>
29+
</dependency>
30+
<dependency>
31+
<groupId>org.slf4j</groupId>
32+
<artifactId>slf4j-api</artifactId>
33+
</dependency>
34+
<dependency>
35+
<groupId>io.fabric8</groupId>
36+
<artifactId>kubernetes-client</artifactId>
37+
<scope>compile</scope>
38+
</dependency>
39+
<dependency>
40+
<groupId>org.assertj</groupId>
41+
<artifactId>assertj-core</artifactId>
42+
<scope>test</scope>
43+
</dependency>
44+
<dependency>
45+
<groupId>org.apache.logging.log4j</groupId>
46+
<artifactId>log4j-slf4j2-impl</artifactId>
47+
<scope>test</scope>
48+
</dependency>
49+
<dependency>
50+
<groupId>org.apache.logging.log4j</groupId>
51+
<artifactId>log4j-core</artifactId>
52+
<scope>test</scope>
53+
</dependency>
54+
</dependencies>
55+
</project>
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package io.javaoperatorsdk.jenvtest.junit;
2+
3+
import java.lang.reflect.Field;
4+
import java.util.Arrays;
5+
import java.util.Optional;
6+
import java.util.stream.Collectors;
7+
8+
import org.junit.jupiter.api.extension.ExtensionContext;
9+
10+
import io.fabric8.kubernetes.client.Config;
11+
import io.fabric8.kubernetes.client.KubernetesClient;
12+
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
13+
import io.javaoperatorsdk.jenvtest.JenvtestException;
14+
import io.javaoperatorsdk.jenvtest.KubeAPIServer;
15+
16+
public class Fabric8ClientInjectionHandler implements ClientInjectionHandler {
17+
18+
public boolean isTargetFieldAvailable(ExtensionContext extensionContext,
19+
boolean staticField) {
20+
return getFieldForKubeConfigInjection(extensionContext, staticField).isPresent();
21+
}
22+
23+
public void inject(ExtensionContext extensionContext,
24+
boolean staticField, KubeAPIServer kubeApiServer) {
25+
var field = getFieldForKubeConfigInjection(extensionContext, staticField).orElseThrow();
26+
setKubeConfigYamlToField(extensionContext, field, kubeApiServer);
27+
}
28+
29+
private void setKubeConfigYamlToField(ExtensionContext extensionContext,
30+
Field kubeConfigField, KubeAPIServer kubeApiServer) {
31+
try {
32+
var target = extensionContext.getTestInstance()
33+
.orElseGet(() -> extensionContext.getTestClass().orElseThrow());
34+
kubeConfigField.setAccessible(true);
35+
kubeConfigField.set(target, new KubernetesClientBuilder()
36+
.withConfig(Config.fromKubeconfig(kubeApiServer.getKubeConfigYaml()))
37+
.build());
38+
} catch (IllegalAccessException e) {
39+
throw new JenvtestException(e);
40+
}
41+
}
42+
43+
@SuppressWarnings("rawtypes")
44+
public static Optional<Field> getFieldForKubeConfigInjection(ExtensionContext extensionContext,
45+
boolean staticField) {
46+
Class<?> clazz = extensionContext.getTestClass().orElseThrow();
47+
var kubeConfigFields = Arrays.stream(clazz.getDeclaredFields())
48+
.filter(f -> KubernetesClient.class.isAssignableFrom(f.getType()))
49+
.collect(Collectors.toList());
50+
if (kubeConfigFields.isEmpty()) {
51+
return Optional.empty();
52+
}
53+
if (kubeConfigFields.size() > 1) {
54+
throw new JenvtestException(
55+
"More fields type KubernetesClient found");
56+
}
57+
var field = kubeConfigFields.get(0);
58+
if (java.lang.reflect.Modifier.isStatic(field.getModifiers()) != staticField) {
59+
return Optional.empty();
60+
} else {
61+
return Optional.of(field);
62+
}
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
io.javaoperatorsdk.jenvtest.junit.Fabric8ClientInjectionHandler
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package io.javaoperatorsdk.jenvtest.junit;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import io.fabric8.kubernetes.client.Config;
6+
import io.fabric8.kubernetes.client.KubernetesClient;
7+
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
8+
9+
import static io.javaoperatorsdk.jenvtest.junit.TestUtils.simpleTest;
10+
11+
@EnableKubeAPIServer
12+
class JUnitFabric8ClientInjectionTest {
13+
14+
static KubernetesClient client;
15+
16+
@KubeConfig
17+
static String configYaml;
18+
19+
@Test
20+
void testClientInjection() {
21+
simpleTest(client);
22+
}
23+
24+
@Test
25+
void testKubeConfigInjectionAlsoWorks() {
26+
simpleTest(new KubernetesClientBuilder().withConfig(Config.fromKubeconfig(configYaml)).build());
27+
}
28+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package io.javaoperatorsdk.jenvtest.junit;
2+
3+
import java.util.Map;
4+
5+
import org.assertj.core.api.Assertions;
6+
7+
import io.fabric8.kubernetes.api.model.ConfigMap;
8+
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
9+
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
10+
import io.fabric8.kubernetes.client.KubernetesClient;
11+
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
12+
13+
public class TestUtils {
14+
15+
public static ConfigMap testConfigMap() {
16+
return new ConfigMapBuilder()
17+
.withMetadata(new ObjectMetaBuilder()
18+
.withName("test1")
19+
.withNamespace("default")
20+
.build())
21+
.withData(Map.of("key", "data"))
22+
.build();
23+
}
24+
25+
public static void simpleTest() {
26+
simpleTest(new KubernetesClientBuilder().build());
27+
}
28+
29+
public static void simpleTest(KubernetesClient client) {
30+
client.resource(TestUtils.testConfigMap()).create();
31+
var cm = client.resource(TestUtils.testConfigMap()).get();
32+
33+
Assertions.assertThat(cm).isNotNull();
34+
35+
client.resource(cm).delete();
36+
}
37+
38+
}

fabric8/src/test/resources/log4j2.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Configuration status="WARN">
3+
<Appenders>
4+
<Console name="Console" target="SYSTEM_OUT">
5+
<PatternLayout pattern="%d %threadId %-30c{1.} [%-5level] %msg%n%throwable"/>
6+
</Console>
7+
</Appenders>
8+
<Loggers>
9+
<Logger name="io.javaoperatorsdk.jenvtest" level="debug">
10+
</Logger>
11+
<Root level="error">
12+
<AppenderRef ref="Console"/>
13+
</Root>
14+
</Loggers>
15+
</Configuration>

0 commit comments

Comments
 (0)