Skip to content

Commit ff1a44d

Browse files
authored
feat: enable easy E2E & improvements to the integration test suite (#891)
1 parent 6f8d38d commit ff1a44d

File tree

12 files changed

+638
-350
lines changed

12 files changed

+638
-350
lines changed

.github/workflows/e2e-test-mysql.yml

-83
This file was deleted.

.github/workflows/e2e-test-tomcat.yml

-79
This file was deleted.

.github/workflows/e2e-test.yml

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Integration and end to end tests which runs locally and deploys the Operator to a Kubernetes
2+
# (Minikube) cluster and creates custom resources to verify the operator's functionality
3+
name: Integration & End to End tests
4+
on:
5+
pull_request:
6+
branches: [ main ]
7+
push:
8+
branches:
9+
- main
10+
11+
jobs:
12+
sample_operators_tests:
13+
strategy:
14+
matrix:
15+
sample_dir:
16+
- "sample-operators/mysql-schema"
17+
- "sample-operators/tomcat-operator"
18+
runs-on: ubuntu-latest
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v2
22+
23+
- name: Setup Minikube-Kubernetes
24+
uses: manusa/[email protected]
25+
with:
26+
minikube version: v1.24.0
27+
kubernetes version: v1.23.0
28+
github token: ${{ secrets.GITHUB_TOKEN }}
29+
driver: docker
30+
31+
- name: Set up Java and Maven
32+
uses: actions/setup-java@v2
33+
with:
34+
java-version: 17
35+
distribution: temurin
36+
cache: 'maven'
37+
38+
- name: Build SDK
39+
run: mvn install -DskipTests
40+
41+
- name: Run integration tests in local mode
42+
working-directory: ${{ matrix.sample_dir }}
43+
run: |
44+
mvn test -P end-to-end-tests
45+
46+
- name: Run E2E tests as a deployment
47+
working-directory: ${{ matrix.sample_dir }}
48+
run: |
49+
eval $(minikube -p minikube docker-env)
50+
mvn jib:dockerBuild test -P end-to-end-tests -Dtest.deployment=remote
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package io.javaoperatorsdk.operator.junit;
2+
3+
import java.time.Duration;
4+
import java.util.ArrayList;
5+
import java.util.List;
6+
import java.util.Locale;
7+
import java.util.UUID;
8+
9+
import org.junit.jupiter.api.extension.*;
10+
11+
import io.fabric8.kubernetes.api.model.HasMetadata;
12+
import io.fabric8.kubernetes.api.model.KubernetesResourceList;
13+
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
14+
import io.fabric8.kubernetes.client.KubernetesClient;
15+
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
16+
import io.fabric8.kubernetes.client.dsl.Resource;
17+
import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil;
18+
import io.fabric8.kubernetes.client.utils.Utils;
19+
import io.javaoperatorsdk.operator.api.config.BaseConfigurationService;
20+
import io.javaoperatorsdk.operator.api.config.ConfigurationService;
21+
import io.javaoperatorsdk.operator.api.config.Version;
22+
23+
public abstract class AbstractOperatorExtension implements HasKubernetesClient,
24+
BeforeAllCallback,
25+
BeforeEachCallback,
26+
AfterAllCallback,
27+
AfterEachCallback {
28+
29+
protected final KubernetesClient kubernetesClient;
30+
protected final ConfigurationService configurationService;
31+
protected final List<HasMetadata> infrastructure;
32+
protected Duration infrastructureTimeout;
33+
protected final boolean oneNamespacePerClass;
34+
protected final boolean preserveNamespaceOnError;
35+
protected final boolean waitForNamespaceDeletion;
36+
37+
protected String namespace;
38+
39+
protected AbstractOperatorExtension(
40+
ConfigurationService configurationService,
41+
List<HasMetadata> infrastructure,
42+
Duration infrastructureTimeout,
43+
boolean oneNamespacePerClass,
44+
boolean preserveNamespaceOnError,
45+
boolean waitForNamespaceDeletion) {
46+
47+
this.kubernetesClient = new DefaultKubernetesClient();
48+
this.configurationService = configurationService;
49+
this.infrastructure = infrastructure;
50+
this.infrastructureTimeout = infrastructureTimeout;
51+
this.oneNamespacePerClass = oneNamespacePerClass;
52+
this.preserveNamespaceOnError = preserveNamespaceOnError;
53+
this.waitForNamespaceDeletion = waitForNamespaceDeletion;
54+
}
55+
56+
57+
@Override
58+
public void beforeAll(ExtensionContext context) throws Exception {
59+
beforeAllImpl(context);
60+
}
61+
62+
@Override
63+
public void beforeEach(ExtensionContext context) throws Exception {
64+
beforeEachImpl(context);
65+
}
66+
67+
@Override
68+
public void afterAll(ExtensionContext context) throws Exception {
69+
afterAllImpl(context);
70+
}
71+
72+
@Override
73+
public void afterEach(ExtensionContext context) throws Exception {
74+
afterEachImpl(context);
75+
}
76+
77+
@Override
78+
public KubernetesClient getKubernetesClient() {
79+
return kubernetesClient;
80+
}
81+
82+
public String getNamespace() {
83+
return namespace;
84+
}
85+
86+
public <T extends HasMetadata> NonNamespaceOperation<T, KubernetesResourceList<T>, Resource<T>> resources(
87+
Class<T> type) {
88+
return kubernetesClient.resources(type).inNamespace(namespace);
89+
}
90+
91+
public <T extends HasMetadata> T get(Class<T> type, String name) {
92+
return kubernetesClient.resources(type).inNamespace(namespace).withName(name).get();
93+
}
94+
95+
public <T extends HasMetadata> T create(Class<T> type, T resource) {
96+
return kubernetesClient.resources(type).inNamespace(namespace).create(resource);
97+
}
98+
99+
public <T extends HasMetadata> T replace(Class<T> type, T resource) {
100+
return kubernetesClient.resources(type).inNamespace(namespace).replace(resource);
101+
}
102+
103+
public <T extends HasMetadata> boolean delete(Class<T> type, T resource) {
104+
return kubernetesClient.resources(type).inNamespace(namespace).delete(resource);
105+
}
106+
107+
protected void beforeAllImpl(ExtensionContext context) {
108+
if (oneNamespacePerClass) {
109+
namespace = context.getRequiredTestClass().getSimpleName();
110+
namespace += "-";
111+
namespace += UUID.randomUUID();
112+
namespace = KubernetesResourceUtil.sanitizeName(namespace).toLowerCase(Locale.US);
113+
namespace = namespace.substring(0, Math.min(namespace.length(), 63));
114+
115+
before(context);
116+
}
117+
}
118+
119+
protected void beforeEachImpl(ExtensionContext context) {
120+
if (!oneNamespacePerClass) {
121+
namespace = context.getRequiredTestClass().getSimpleName();
122+
namespace += "-";
123+
namespace += context.getRequiredTestMethod().getName();
124+
namespace += "-";
125+
namespace += UUID.randomUUID();
126+
namespace = KubernetesResourceUtil.sanitizeName(namespace).toLowerCase(Locale.US);
127+
namespace = namespace.substring(0, Math.min(namespace.length(), 63));
128+
129+
before(context);
130+
}
131+
}
132+
133+
protected abstract void before(ExtensionContext context);
134+
135+
protected void afterAllImpl(ExtensionContext context) {
136+
if (oneNamespacePerClass) {
137+
after(context);
138+
}
139+
}
140+
141+
protected void afterEachImpl(ExtensionContext context) {
142+
if (!oneNamespacePerClass) {
143+
after(context);
144+
}
145+
}
146+
147+
protected abstract void after(ExtensionContext context);
148+
149+
public static abstract class AbstractBuilder {
150+
protected ConfigurationService configurationService;
151+
protected final List<HasMetadata> infrastructure;
152+
protected Duration infrastructureTimeout;
153+
protected boolean preserveNamespaceOnError;
154+
protected boolean waitForNamespaceDeletion;
155+
protected boolean oneNamespacePerClass;
156+
157+
protected AbstractBuilder() {
158+
this.configurationService = new BaseConfigurationService(Version.UNKNOWN);
159+
160+
this.infrastructure = new ArrayList<>();
161+
this.infrastructureTimeout = Duration.ofMinutes(1);
162+
163+
this.preserveNamespaceOnError = Utils.getSystemPropertyOrEnvVar(
164+
"josdk.it.preserveNamespaceOnError",
165+
false);
166+
167+
this.waitForNamespaceDeletion = Utils.getSystemPropertyOrEnvVar(
168+
"josdk.it.waitForNamespaceDeletion",
169+
true);
170+
171+
this.oneNamespacePerClass = Utils.getSystemPropertyOrEnvVar(
172+
"josdk.it.oneNamespacePerClass",
173+
false);
174+
}
175+
}
176+
}

0 commit comments

Comments
 (0)