Skip to content

Commit 74e8028

Browse files
authored
WebPage E2E Test (#68)
Signed-off-by: Attila Mészáros <[email protected]>
1 parent d8eab2b commit 74e8028

File tree

12 files changed

+294
-49
lines changed

12 files changed

+294
-49
lines changed

.github/workflows/pr.yml

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
name: Verify Pull Request
22

3-
env:
4-
MAVEN_ARGS: -V -ntp -e
5-
63
concurrency:
74
group: ${{ github.ref }}-${{ github.workflow }}
85
cancel-in-progress: true
@@ -25,9 +22,9 @@ jobs:
2522
cache: 'maven'
2623
- name: Check code format
2724
run: |
28-
./mvnw ${MAVEN_ARGS} spotless:check --file pom.xml
25+
./mvnw spotless:check --file pom.xml
2926
30-
build_with_tests:
27+
build_and_run_unit_and_integration_tests:
3128
runs-on: ubuntu-latest
3229
steps:
3330
- uses: actions/checkout@v4
@@ -38,4 +35,26 @@ jobs:
3835
java-version: 21
3936
cache: 'maven'
4037
- name: Run integration tests
41-
run: ./mvnw ${MAVEN_ARGS} clean install
38+
run: ./mvnw clean install
39+
run_e2e_tests:
40+
runs-on: ubuntu-latest
41+
steps:
42+
- uses: actions/checkout@v4
43+
- name: Set up Java and Maven
44+
uses: actions/setup-java@v4
45+
with:
46+
distribution: temurin
47+
java-version: 21
48+
cache: 'maven'
49+
- name: Setup minikube for E2E tests
50+
uses: manusa/[email protected]
51+
with:
52+
minikube version: v1.32.0
53+
kubernetes version: v1.29.1
54+
github token: ${{ secrets.GITHUB_TOKEN }}
55+
driver: docker
56+
- name: Run E2E tests
57+
run: |
58+
eval $(minikube -p minikube docker-env)
59+
./mvnw clean install -DskipTests -Dquarkus.container-image.build=true -Dquarkus.kubernetes.namespace=default
60+
./mvnw test -Pe2e-tests

pom.xml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,12 @@
152152
<artifactId>maven-surefire-plugin</artifactId>
153153
<version>${surefire-plugin.version}</version>
154154
<configuration>
155+
<includes>
156+
<include>**/*Test.java</include>
157+
</includes>
158+
<excludes>
159+
<exclude>**/*E2E.java</exclude>
160+
</excludes>
155161
<systemPropertyVariables>
156162
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
157163
<maven.home>${maven.home}</maven.home>
@@ -204,7 +210,34 @@
204210
</java>
205211
</configuration>
206212
</plugin>
213+
207214
</plugins>
208215
</build>
209216

217+
<profiles>
218+
<profile>
219+
<id>e2e-tests</id>
220+
<build>
221+
<plugins>
222+
<plugin>
223+
<artifactId>maven-surefire-plugin</artifactId>
224+
<version>${surefire-plugin.version}</version>
225+
<configuration>
226+
<includes>
227+
<include>**/*E2E.java</include>
228+
</includes>
229+
<excludes>
230+
<exclude>**/*Test.java</exclude>
231+
</excludes>
232+
<systemPropertyVariables>
233+
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
234+
<maven.home>${maven.home}</maven.home>
235+
</systemPropertyVariables>
236+
</configuration>
237+
</plugin>
238+
</plugins>
239+
</build>
240+
</profile>
241+
</profiles>
242+
210243
</project>

src/main/java/io/csviri/operator/resourceglue/Utils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public static Map<String, GenericKubernetesResource> getRelatedResources(Glue gl
6565
Context<?> context) {
6666
var gvk =
6767
new GroupVersionKind(relatedResourceSpec.getApiVersion(), relatedResourceSpec.getKind());
68-
log.debug("Getting event source for gvk: {}", gvk);
68+
log.trace("Getting event source for gvk: {}", gvk);
6969
var es =
7070
(InformerEventSource<GenericKubernetesResource, Glue>) context
7171
.eventSourceRetriever()

src/test/java/io/csviri/operator/resourceglue/GlueOperatorTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@ class GlueOperatorTest extends TestBase {
3131

3232
@BeforeEach
3333
void applyCRD() {
34-
TestUtils.applyCrd(TestCustomResource.class, client);
35-
TestUtils.applyCrd(TestCustomResource2.class, client);
34+
TestUtils.applyTestCrd(client, TestCustomResource.class, TestCustomResource2.class);
3635
}
3736

3837
@Test

src/test/java/io/csviri/operator/resourceglue/GlueRelatedResourcesTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public class GlueRelatedResourcesTest extends TestBase {
2727

2828
@BeforeEach
2929
void applyCRD() {
30-
TestUtils.applyCrd(ClusterScopeTestCustomResource.class, client);
30+
TestUtils.applyTestCrd(client, ClusterScopeTestCustomResource.class);
3131
}
3232

3333
@Test

src/test/java/io/csviri/operator/resourceglue/TestUtils.java

Lines changed: 84 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package io.csviri.operator.resourceglue;
22

3-
import java.io.ByteArrayInputStream;
4-
import java.io.IOException;
5-
import java.io.InputStream;
3+
import java.io.*;
64
import java.nio.charset.StandardCharsets;
5+
import java.util.Arrays;
6+
import java.util.List;
7+
import java.util.concurrent.TimeUnit;
8+
import java.util.function.UnaryOperator;
9+
import java.util.stream.Collectors;
710

811
import org.slf4j.Logger;
912
import org.slf4j.LoggerFactory;
@@ -17,6 +20,8 @@
1720
import io.fabric8.kubernetes.client.utils.Serialization;
1821
import io.javaoperatorsdk.operator.ReconcilerUtils;
1922

23+
import static com.oracle.truffle.js.builtins.ObjectFunctionBuiltins.ObjectFunction.is;
24+
2025
public class TestUtils {
2126

2227
public static final int CRD_READY_WAIT = 1000;
@@ -39,40 +44,104 @@ public static GlueOperator loadResourceFlowOperator(String path) {
3944
}
4045
}
4146

42-
public static GenericKubernetesResource load(String path) {
47+
public static <T extends HasMetadata> T load(String path, Class<T> clazz) {
4348
try (InputStream is = TestUtils.class.getResourceAsStream(path)) {
44-
return Serialization.unmarshal(is, GenericKubernetesResource.class);
49+
return Serialization.unmarshal(is, clazz);
4550
} catch (IOException e) {
4651
throw new RuntimeException(e);
4752
}
4853
}
4954

55+
public static GenericKubernetesResource load(String path) {
56+
return load(path, GenericKubernetesResource.class);
57+
}
58+
5059
public static GenericKubernetesResource createOrUpdate(KubernetesClient client, String path) {
5160
return client.resource(load(path)).createOr(NonDeletingOperation::update);
5261
}
5362

54-
public static void applyCrd(Class<? extends HasMetadata> resourceClass, KubernetesClient client) {
55-
applyCrd(ReconcilerUtils.getResourceTypeName(resourceClass), client);
63+
public static void applyCrd(Class<? extends HasMetadata> resourceClass, KubernetesClient client,
64+
boolean test) {
65+
applyCrd(ReconcilerUtils.getResourceTypeName(resourceClass), client, test);
5666
}
5767

58-
public static void applyCrd(String resourceTypeName, KubernetesClient client) {
59-
String path = "/META-INF/fabric8/" + resourceTypeName + "-v1.yml";
60-
try (InputStream is = TestUtils.class.getResourceAsStream(path)) {
61-
if (is == null) {
62-
throw new IllegalStateException("Cannot find CRD at " + path);
63-
}
68+
@SafeVarargs
69+
public static void applyTestCrd(KubernetesClient client,
70+
Class<? extends HasMetadata>... resourceClasses) {
71+
Arrays.stream(resourceClasses).forEach(c -> applyCrd(c, client, true));
72+
}
73+
74+
@SafeVarargs
75+
public static void applyCrd(KubernetesClient client,
76+
Class<? extends HasMetadata>... resourceClasses) {
77+
Arrays.stream(resourceClasses).forEach(c -> applyCrd(c, client, false));
78+
}
79+
80+
public static void applyCrd(String resourceTypeName, KubernetesClient client, boolean test) {
81+
82+
try (InputStream is = createInputStream(resourceTypeName, test)) {
83+
6484
var crdString = new String(is.readAllBytes(), StandardCharsets.UTF_8);
6585
log.debug("Applying CRD: {}", crdString);
6686
final var crd = client.load(new ByteArrayInputStream(crdString.getBytes()));
6787
crd.createOrReplace();
6888
Thread.sleep(CRD_READY_WAIT); // readiness is not applicable for CRD, just wait a little
69-
log.debug("Applied CRD with path: {}", path);
89+
log.debug("Applied CRD for resource: {}", resourceTypeName);
7090
} catch (InterruptedException ex) {
7191
log.error("Interrupted.", ex);
7292
Thread.currentThread().interrupt();
7393
} catch (Exception ex) {
74-
throw new IllegalStateException("Cannot apply CRD yaml: " + path, ex);
94+
throw new IllegalStateException("Cannot apply CRD for: " + resourceTypeName, ex);
7595
}
7696
}
7797

98+
private static InputStream createInputStream(String resourceTypeName, boolean test)
99+
throws FileNotFoundException {
100+
if (test) {
101+
String path = "/META-INF/fabric8/" + resourceTypeName + "-v1.yml";
102+
return TestUtils.class.getResourceAsStream(path);
103+
} else {
104+
String path = "target/kubernetes/" + resourceTypeName + "-v1.yml";
105+
File file = new File(path);
106+
107+
var res = new FileInputStream(path);
108+
if (!file.exists()) {
109+
throw new IllegalStateException("Cannot find CRD at " + file.getAbsolutePath());
110+
}
111+
return res;
112+
}
113+
}
114+
115+
public static void applyAndWait(KubernetesClient client, String path) {
116+
applyAndWait(client, path, null);
117+
}
118+
119+
public static void applyAndWait(KubernetesClient client, String path,
120+
UnaryOperator<HasMetadata> transform) {
121+
try (FileInputStream fileInputStream = new FileInputStream(path)) {
122+
applyAndWait(client, fileInputStream, transform);
123+
} catch (IOException e) {
124+
throw new IllegalStateException(e);
125+
}
126+
}
127+
128+
public static void applyAndWait(KubernetesClient client, InputStream is) {
129+
applyAndWait(client, is, null);
130+
}
131+
132+
public static void applyAndWait(KubernetesClient client, List<HasMetadata> resources,
133+
UnaryOperator<HasMetadata> transformer) {
134+
if (transformer != null) {
135+
resources = resources.stream().map(transformer).collect(Collectors.toList());
136+
}
137+
client.resourceList(resources).createOrReplace();
138+
client.resourceList(resources).waitUntilReady(3, TimeUnit.MINUTES);
139+
}
140+
141+
public static void applyAndWait(KubernetesClient client, InputStream is,
142+
UnaryOperator<HasMetadata> transformer) {
143+
var resources = client.load(is).items();
144+
applyAndWait(client, resources, transformer);
145+
}
146+
78147
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package io.csviri.operator.resourceglue.sample.webpage;
2+
3+
import io.fabric8.kubernetes.api.model.Namespaced;
4+
import io.fabric8.kubernetes.client.CustomResource;
5+
import io.fabric8.kubernetes.model.annotation.Group;
6+
import io.fabric8.kubernetes.model.annotation.Plural;
7+
import io.fabric8.kubernetes.model.annotation.Singular;
8+
import io.fabric8.kubernetes.model.annotation.Version;
9+
10+
@Version(value = "v1")
11+
@Group("resourceglueoperator.sample")
12+
@Singular("webpage")
13+
@Plural("webpages")
14+
public class WebPage extends CustomResource<WebPageSpec, WebPageStatus>
15+
implements Namespaced {
16+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package io.csviri.operator.resourceglue;
2+
3+
4+
import org.junit.jupiter.api.BeforeEach;
5+
import org.junit.jupiter.api.Test;
6+
7+
import io.csviri.operator.resourceglue.customresource.glue.Glue;
8+
import io.csviri.operator.resourceglue.customresource.operator.GlueOperator;
9+
import io.csviri.operator.resourceglue.sample.webpage.WebPage;
10+
import io.fabric8.kubernetes.api.model.ConfigMap;
11+
import io.fabric8.kubernetes.api.model.Service;
12+
import io.fabric8.kubernetes.api.model.apps.Deployment;
13+
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
14+
import io.fabric8.kubernetes.client.KubernetesClient;
15+
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
16+
import io.fabric8.kubernetes.client.dsl.NonDeletingOperation;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.awaitility.Awaitility.await;
20+
21+
public class WebPageE2E {
22+
23+
private KubernetesClient client = new KubernetesClientBuilder().build();
24+
25+
@BeforeEach
26+
void applyCRDs() {
27+
TestUtils.applyCrd(client, Glue.class, GlueOperator.class);
28+
TestUtils.applyTestCrd(client, WebPage.class);
29+
TestUtils.applyAndWait(client, "target/kubernetes/kubernetes.yml");
30+
}
31+
32+
@Test
33+
void testDeployment() {
34+
client.resource(TestUtils.load("/sample/webpage/webpage.operator.yaml"))
35+
.createOr(NonDeletingOperation::update);
36+
var webPage = TestUtils.load("/sample/webpage/webpage.sample.yaml", WebPage.class);
37+
var createdWebPage = client.resource(webPage).createOr(NonDeletingOperation::update);
38+
39+
await().untilAsserted(() -> {
40+
var deployment =
41+
client.resources(Deployment.class).withName(webPage.getMetadata().getName()).get();
42+
var configMap =
43+
client.resources(ConfigMap.class).withName(webPage.getMetadata().getName()).get();
44+
var service = client.resources(Service.class).withName(webPage.getMetadata().getName()).get();
45+
var ingress = client.resources(Ingress.class).withName(webPage.getMetadata().getName()).get();
46+
47+
assertThat(deployment).isNotNull();
48+
assertThat(configMap).isNotNull();
49+
assertThat(service).isNotNull();
50+
assertThat(ingress).isNull();
51+
});
52+
53+
createdWebPage.getMetadata().setResourceVersion(null);
54+
createdWebPage.getSpec().setExposed(true);
55+
createdWebPage = client.resource(createdWebPage).update();
56+
57+
await().untilAsserted(() -> {
58+
var ingress = client.resources(Ingress.class).withName(webPage.getMetadata().getName()).get();
59+
assertThat(ingress).isNotNull();
60+
});
61+
62+
client.resource(createdWebPage).delete();
63+
64+
await().untilAsserted(() -> {
65+
var deployment =
66+
client.resources(Deployment.class).withName(webPage.getMetadata().getName()).get();
67+
var configMap =
68+
client.resources(ConfigMap.class).withName(webPage.getMetadata().getName()).get();
69+
var service = client.resources(Service.class).withName(webPage.getMetadata().getName()).get();
70+
var ingress = client.resources(Ingress.class).withName(webPage.getMetadata().getName()).get();
71+
72+
assertThat(deployment).isNull();
73+
assertThat(configMap).isNull();
74+
assertThat(service).isNull();
75+
assertThat(ingress).isNull();
76+
});
77+
}
78+
79+
}

0 commit comments

Comments
 (0)