Skip to content

Commit 8531e75

Browse files
committed
Improvements to the testsuite
1 parent 9d3dfd8 commit 8531e75

File tree

7 files changed

+329
-16
lines changed

7 files changed

+329
-16
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# End to end integration test which deploys the MySQL Schema Operator operator to a Kubernetes
2+
# (Minikube) cluster and creates custom resources to verify the operator's functionality
3+
name: MySQL Schema Operator New End to End test
4+
on:
5+
pull_request:
6+
branches: [ main ]
7+
push:
8+
branches:
9+
- main
10+
jobs:
11+
mysql_e2e_test:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v2
16+
17+
- name: Setup Minikube-Kubernetes
18+
uses: manusa/[email protected]
19+
with:
20+
minikube version: v1.24.0
21+
kubernetes version: v1.23.0
22+
github token: ${{ secrets.GITHUB_TOKEN }}
23+
driver: docker
24+
25+
- name: Set up Java and Maven
26+
uses: actions/setup-java@v2
27+
with:
28+
java-version: 11
29+
distribution: adopt-hotspot
30+
cache: 'maven'
31+
32+
- name: Build SDK
33+
run: mvn install -DskipTests
34+
35+
- name: Run E2E Tests in Local Mode
36+
working-directory: sample-operators/mysql-schema
37+
run: |
38+
mvn test -Dtest=MySQLSchemaOperatorNewE2E
39+
40+
- name: Run E2E Tests as a deployment
41+
working-directory: sample-operators/mysql-schema
42+
run: |
43+
eval $(minikube -p minikube docker-env)
44+
mvn jib:dockerBuild test -Dtest=MySQLSchemaOperatorNewE2E -Djosdk.it.localOperatorDeployment=false

operator-framework-core/pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
<dependency>
5757
<groupId>io.fabric8</groupId>
5858
<artifactId>openshift-client</artifactId>
59+
<version>${fabric8-client.version}</version>
5960
</dependency>
6061

6162
<dependency>
@@ -98,6 +99,7 @@
9899
<dependency>
99100
<groupId>io.fabric8</groupId>
100101
<artifactId>kubernetes-server-mock</artifactId>
102+
<version>${fabric8-client.version}</version>
101103
<scope>test</scope>
102104
</dependency>
103105
<dependency>

operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/OperatorExtension.java

Lines changed: 169 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package io.javaoperatorsdk.operator.junit;
22

33
import java.io.InputStream;
4+
import java.time.Duration;
45
import java.util.ArrayList;
56
import java.util.List;
67
import java.util.Locale;
8+
import java.util.UUID;
79
import java.util.concurrent.TimeUnit;
810
import java.util.stream.Collectors;
911

@@ -19,6 +21,7 @@
1921
import io.fabric8.kubernetes.api.model.HasMetadata;
2022
import io.fabric8.kubernetes.api.model.KubernetesResourceList;
2123
import io.fabric8.kubernetes.api.model.NamespaceBuilder;
24+
import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBinding;
2225
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
2326
import io.fabric8.kubernetes.client.KubernetesClient;
2427
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
@@ -47,24 +50,42 @@ public class OperatorExtension
4750
private final KubernetesClient kubernetesClient;
4851
private final ConfigurationService configurationService;
4952
private final Operator operator;
53+
private final List<HasMetadata> operatorDeployment;
54+
private final Duration deploymentTimeout;
5055
private final List<ReconcilerSpec> reconcilers;
56+
private final List<HasMetadata> infrastructure;
57+
private final Duration infrastructureTimeout;
5158
private final boolean preserveNamespaceOnError;
5259
private final boolean waitForNamespaceDeletion;
60+
private final boolean oneNamespacePerClass;
61+
private final boolean localOperatorDeployment;
5362

5463
private String namespace;
5564

5665
private OperatorExtension(
5766
ConfigurationService configurationService,
5867
List<ReconcilerSpec> reconcilers,
68+
List<HasMetadata> operatorDeployment,
69+
Duration deploymentTimeout,
70+
List<HasMetadata> infrastructure,
71+
Duration infrastructureTimeout,
5972
boolean preserveNamespaceOnError,
60-
boolean waitForNamespaceDeletion) {
73+
boolean waitForNamespaceDeletion,
74+
boolean oneNamespacePerClass,
75+
boolean localOperatorDeployment) {
6176

6277
this.kubernetesClient = new DefaultKubernetesClient();
6378
this.configurationService = configurationService;
6479
this.reconcilers = reconcilers;
80+
this.operatorDeployment = operatorDeployment;
81+
this.deploymentTimeout = deploymentTimeout;
82+
this.infrastructure = infrastructure;
83+
this.infrastructureTimeout = infrastructureTimeout;
6584
this.operator = new Operator(this.kubernetesClient, this.configurationService);
6685
this.preserveNamespaceOnError = preserveNamespaceOnError;
6786
this.waitForNamespaceDeletion = waitForNamespaceDeletion;
87+
this.oneNamespacePerClass = oneNamespacePerClass;
88+
this.localOperatorDeployment = localOperatorDeployment;
6889
}
6990

7091
/**
@@ -78,22 +99,22 @@ public static Builder builder() {
7899

79100
@Override
80101
public void beforeAll(ExtensionContext context) throws Exception {
81-
before(context);
102+
beforeAllImpl(context);
82103
}
83104

84105
@Override
85106
public void beforeEach(ExtensionContext context) throws Exception {
86-
before(context);
107+
beforeEachImpl(context);
87108
}
88109

89110
@Override
90111
public void afterAll(ExtensionContext context) throws Exception {
91-
after(context);
112+
afterAllImpl(context);
92113
}
93114

94115
@Override
95116
public void afterEach(ExtensionContext context) throws Exception {
96-
after(context);
117+
afterEachImpl(context);
97118
}
98119

99120
@Override
@@ -118,6 +139,10 @@ public Reconciler getFirstReconciler() {
118139
.findFirst().orElseThrow();
119140
}
120141

142+
public boolean isLocal() {
143+
return this.localOperatorDeployment;
144+
}
145+
121146
@SuppressWarnings({"rawtypes"})
122147
public <T extends Reconciler> T getControllerOfType(Class<T> type) {
123148
return operator.getControllers().stream()
@@ -151,19 +176,47 @@ public <T extends HasMetadata> boolean delete(Class<T> type, T resource) {
151176
}
152177

153178
@SuppressWarnings("unchecked")
154-
protected void before(ExtensionContext context) {
155-
namespace = context.getRequiredTestClass().getSimpleName();
156-
namespace += "-";
157-
namespace += context.getRequiredTestMethod().getName();
158-
namespace = KubernetesResourceUtil.sanitizeName(namespace).toLowerCase(Locale.US);
159-
namespace = namespace.substring(0, Math.min(namespace.length(), 63));
179+
protected void beforeAllImpl(ExtensionContext context) {
180+
if (oneNamespacePerClass) {
181+
namespace = context.getRequiredTestClass().getSimpleName();
182+
namespace += "-";
183+
namespace += UUID.randomUUID();
184+
namespace = KubernetesResourceUtil.sanitizeName(namespace).toLowerCase(Locale.US);
185+
namespace = namespace.substring(0, Math.min(namespace.length(), 63));
186+
187+
before(context);
188+
}
189+
}
160190

191+
@SuppressWarnings("unchecked")
192+
protected void beforeEachImpl(ExtensionContext context) {
193+
if (!oneNamespacePerClass) {
194+
namespace = context.getRequiredTestClass().getSimpleName();
195+
namespace += "-";
196+
namespace += context.getRequiredTestMethod().getName();
197+
namespace += "-";
198+
namespace += UUID.randomUUID();
199+
namespace = KubernetesResourceUtil.sanitizeName(namespace).toLowerCase(Locale.US);
200+
namespace = namespace.substring(0, Math.min(namespace.length(), 63));
201+
202+
before(context);
203+
}
204+
}
205+
206+
@SuppressWarnings("unchecked")
207+
protected void before(ExtensionContext context) {
161208
LOGGER.info("Initializing integration test in namespace {}", namespace);
162209

163210
kubernetesClient
164211
.namespaces()
165212
.create(new NamespaceBuilder().withNewMetadata().withName(namespace).endMetadata().build());
166213

214+
kubernetesClient
215+
.resourceList(infrastructure)
216+
.createOrReplace();
217+
kubernetesClient
218+
.resourceList(infrastructure)
219+
.waitUntilReady(infrastructureTimeout.toMillis(), TimeUnit.MILLISECONDS);
167220

168221
for (var ref : reconcilers) {
169222
final var config = configurationService.getConfigurationFor(ref.reconciler);
@@ -191,14 +244,50 @@ protected void before(ExtensionContext context) {
191244
this.operator.register(ref.reconciler, oconfig.build());
192245
}
193246

194-
this.operator.start();
247+
if (isLocal()) {
248+
LOGGER.debug("Starting the operator locally");
249+
this.operator.start();
250+
} else {
251+
LOGGER.debug("Deploying the operator into Kubernetes");
252+
operatorDeployment.stream().forEach(hm -> {
253+
hm.getMetadata().setNamespace(namespace);
254+
if (hm.getKind().toLowerCase(Locale.ROOT).equals("clusterrolebinding")) {
255+
var crb = (ClusterRoleBinding) hm;
256+
for (var subject : crb.getSubjects()) {
257+
subject.setNamespace(namespace);
258+
}
259+
}
260+
});
261+
262+
kubernetesClient
263+
.resourceList(operatorDeployment)
264+
.inNamespace(namespace)
265+
.createOrReplace();
266+
kubernetesClient
267+
.resourceList(operatorDeployment)
268+
.waitUntilReady(deploymentTimeout.toMillis(), TimeUnit.MILLISECONDS);
269+
}
270+
}
271+
272+
protected void afterAllImpl(ExtensionContext context) {
273+
if (oneNamespacePerClass) {
274+
after(context);
275+
}
276+
}
277+
278+
protected void afterEachImpl(ExtensionContext context) {
279+
if (!oneNamespacePerClass) {
280+
after(context);
281+
}
195282
}
196283

197284
protected void after(ExtensionContext context) {
198285
if (namespace != null) {
199286
if (preserveNamespaceOnError && context.getExecutionException().isPresent()) {
200287
LOGGER.info("Preserving namespace {}", namespace);
201288
} else {
289+
kubernetesClient.resourceList(infrastructure).delete();
290+
kubernetesClient.resourceList(operatorDeployment).inNamespace(namespace).delete();
202291
LOGGER.info("Deleting namespace {} and stopping operator", namespace);
203292
kubernetesClient.namespaces().withName(namespace).delete();
204293
if (waitForNamespaceDeletion) {
@@ -212,7 +301,9 @@ protected void after(ExtensionContext context) {
212301
}
213302

214303
try {
215-
this.operator.stop();
304+
if (isLocal()) {
305+
this.operator.stop();
306+
}
216307
} catch (Exception e) {
217308
// ignored
218309
}
@@ -221,13 +312,23 @@ protected void after(ExtensionContext context) {
221312
@SuppressWarnings("rawtypes")
222313
public static class Builder {
223314
private final List<ReconcilerSpec> reconcilers;
315+
private final List<HasMetadata> operatorDeployment;
316+
private Duration deploymentTimeout;
317+
private boolean localOperatorDeployment;
318+
private final List<HasMetadata> infrastructure;
319+
private Duration infrastructureTimeout;
224320
private ConfigurationService configurationService;
225321
private boolean preserveNamespaceOnError;
226322
private boolean waitForNamespaceDeletion;
323+
private boolean oneNamespacePerClass;
227324

228325
protected Builder() {
229326
this.configurationService = new BaseConfigurationService(Version.UNKNOWN);
230327
this.reconcilers = new ArrayList<>();
328+
this.operatorDeployment = new ArrayList<>();
329+
this.infrastructure = new ArrayList<>();
330+
this.deploymentTimeout = Duration.ofMinutes(1);
331+
this.infrastructureTimeout = Duration.ofMinutes(1);
231332

232333
this.preserveNamespaceOnError = Utils.getSystemPropertyOrEnvVar(
233334
"josdk.it.preserveNamespaceOnError",
@@ -236,6 +337,14 @@ protected Builder() {
236337
this.waitForNamespaceDeletion = Utils.getSystemPropertyOrEnvVar(
237338
"josdk.it.waitForNamespaceDeletion",
238339
true);
340+
341+
this.oneNamespacePerClass = Utils.getSystemPropertyOrEnvVar(
342+
"josdk.it.oneNamespacePerClass",
343+
false);
344+
345+
this.localOperatorDeployment = Utils.getSystemPropertyOrEnvVar(
346+
"josdk.it.localOperatorDeployment",
347+
true);
239348
}
240349

241350
public Builder preserveNamespaceOnError(boolean value) {
@@ -248,11 +357,31 @@ public Builder waitForNamespaceDeletion(boolean value) {
248357
return this;
249358
}
250359

360+
public Builder oneNamespacePerClass(boolean value) {
361+
this.oneNamespacePerClass = value;
362+
return this;
363+
}
364+
365+
public Builder localOperatorDeployment(boolean value) {
366+
this.localOperatorDeployment = value;
367+
return this;
368+
}
369+
251370
public Builder withConfigurationService(ConfigurationService value) {
252371
configurationService = value;
253372
return this;
254373
}
255374

375+
public Builder withDeploymentTimeout(Duration value) {
376+
deploymentTimeout = value;
377+
return this;
378+
}
379+
380+
public Builder withInfrastructureTimeout(Duration value) {
381+
infrastructureTimeout = value;
382+
return this;
383+
}
384+
256385
@SuppressWarnings("rawtypes")
257386
public Builder withReconciler(Reconciler value) {
258387
reconcilers.add(new ReconcilerSpec(value, null));
@@ -275,12 +404,38 @@ public Builder withReconciler(Class<? extends Reconciler> value) {
275404
return this;
276405
}
277406

407+
public Builder withInfrastructure(List<HasMetadata> hm) {
408+
infrastructure.addAll(hm);
409+
return this;
410+
}
411+
412+
public Builder withInfrastructure(HasMetadata hm) {
413+
infrastructure.add(hm);
414+
return this;
415+
}
416+
417+
public Builder withDeployment(List<HasMetadata> hm) {
418+
operatorDeployment.addAll(hm);
419+
return this;
420+
}
421+
422+
public Builder withDeployment(HasMetadata hm) {
423+
operatorDeployment.add(hm);
424+
return this;
425+
}
426+
278427
public OperatorExtension build() {
279428
return new OperatorExtension(
280429
configurationService,
281430
reconcilers,
431+
operatorDeployment,
432+
deploymentTimeout,
433+
infrastructure,
434+
infrastructureTimeout,
282435
preserveNamespaceOnError,
283-
waitForNamespaceDeletion);
436+
waitForNamespaceDeletion,
437+
oneNamespacePerClass,
438+
localOperatorDeployment);
284439
}
285440
}
286441

0 commit comments

Comments
 (0)