Skip to content

Commit 8c0b66b

Browse files
committed
integration test
Signed-off-by: Attila Mészáros <[email protected]>
1 parent 38b268b commit 8c0b66b

File tree

7 files changed

+176
-6
lines changed

7 files changed

+176
-6
lines changed

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/CRDPresentActivationCondition.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public boolean isMet(DependentResource<HasMetadata, HasMetadata> dependentResour
5252
crdInformer = (InformerEventSource<CustomResourceDefinition, HasMetadata>) context
5353
.eventSourceRetriever().getResourceEventSourceFor(CustomResourceDefinition.class);
5454
} catch (IllegalArgumentException e) {
55-
log.debug("Error when finding event source for CustomResourceDefinitions", e);
55+
log.trace("Error when finding event source for CustomResourceDefinitions", e);
5656
}
5757

5858
if (crdInformer != null) {

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

+31-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.javaoperatorsdk.operator.junit;
22

33
import java.io.ByteArrayInputStream;
4+
import java.io.IOException;
45
import java.io.InputStream;
56
import java.nio.charset.StandardCharsets;
67
import java.time.Duration;
@@ -19,9 +20,11 @@
1920

2021
import io.fabric8.kubernetes.api.model.HasMetadata;
2122
import io.fabric8.kubernetes.api.model.Namespaced;
23+
import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinition;
2224
import io.fabric8.kubernetes.client.CustomResource;
2325
import io.fabric8.kubernetes.client.KubernetesClient;
2426
import io.fabric8.kubernetes.client.LocalPortForward;
27+
import io.fabric8.kubernetes.client.dsl.NonDeletingOperation;
2528
import io.javaoperatorsdk.operator.Operator;
2629
import io.javaoperatorsdk.operator.ReconcilerUtils;
2730
import io.javaoperatorsdk.operator.RegisteredController;
@@ -173,23 +176,46 @@ public static void applyCrd(Class<? extends HasMetadata> resourceClass, Kubernet
173176
applyCrd(ReconcilerUtils.getResourceTypeName(resourceClass), client);
174177
}
175178

176-
public static void applyCrd(String resourceTypeName, KubernetesClient client) {
179+
public static void deleteCrd(Class<? extends HasMetadata> resourceClass,
180+
KubernetesClient client) {
181+
try {
182+
var crd = loadCRD(ReconcilerUtils.getResourceTypeName(resourceClass), client);
183+
client.resource(crd).delete();
184+
185+
Thread.sleep(CRD_READY_WAIT);
186+
} catch (InterruptedException e) {
187+
throw new RuntimeException(e);
188+
}
189+
}
190+
191+
192+
private static CustomResourceDefinition loadCRD(String resourceTypeName,
193+
KubernetesClient client) {
177194
String path = "/META-INF/fabric8/" + resourceTypeName + "-v1.yml";
178195
try (InputStream is = LocallyRunOperatorExtension.class.getResourceAsStream(path)) {
179196
if (is == null) {
180197
throw new IllegalStateException("Cannot find CRD at " + path);
181198
}
182199
var crdString = new String(is.readAllBytes(), StandardCharsets.UTF_8);
183200
LOGGER.debug("Applying CRD: {}", crdString);
184-
final var crd = client.load(new ByteArrayInputStream(crdString.getBytes()));
185-
crd.createOrReplace();
201+
final var resources = client.load(new ByteArrayInputStream(crdString.getBytes()));
202+
return (CustomResourceDefinition) resources.items().get(0);
203+
} catch (IOException e) {
204+
throw new IllegalStateException(e);
205+
}
206+
}
207+
208+
public static void applyCrd(String resourceTypeName, KubernetesClient client) {
209+
try {
210+
var crd = loadCRD(resourceTypeName, client);
211+
client.resource(crd).createOr(NonDeletingOperation::update);
186212
Thread.sleep(CRD_READY_WAIT); // readiness is not applicable for CRD, just wait a little
187-
LOGGER.debug("Applied CRD with path: {}", path);
213+
LOGGER.debug("Applied CRD for type {}", resourceTypeName);
188214
} catch (InterruptedException ex) {
189215
LOGGER.error("Interrupted.", ex);
190216
Thread.currentThread().interrupt();
191217
} catch (Exception ex) {
192-
throw new IllegalStateException("Cannot apply CRD yaml: " + path, ex);
218+
throw new IllegalStateException("Cannot apply CRD for type: " + resourceTypeName, ex);
193219
}
194220
}
195221

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package io.javaoperatorsdk.operator;
2+
3+
import java.time.Duration;
4+
5+
import org.junit.jupiter.api.AfterEach;
6+
import org.junit.jupiter.api.Test;
7+
import org.junit.jupiter.api.extension.RegisterExtension;
8+
9+
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
10+
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;
11+
import io.javaoperatorsdk.operator.sample.optionaldependent.OptionalDependentCustomResource;
12+
import io.javaoperatorsdk.operator.sample.optionaldependent.OptionalDependentReconciler;
13+
import io.javaoperatorsdk.operator.sample.optionaldependent.OptionalDependentSecondaryCustomResource;
14+
15+
import static org.assertj.core.api.Assertions.assertThat;
16+
import static org.awaitility.Awaitility.await;
17+
18+
public class OptionalDependentIT {
19+
20+
@RegisterExtension
21+
LocallyRunOperatorExtension extension =
22+
LocallyRunOperatorExtension.builder().withReconciler(new OptionalDependentReconciler())
23+
.build();
24+
25+
@AfterEach
26+
void cleanup() {
27+
LocallyRunOperatorExtension.deleteCrd(OptionalDependentSecondaryCustomResource.class,
28+
extension.getKubernetesClient());
29+
}
30+
31+
@Test
32+
void activatesResourceAfterCRDApplied() {
33+
var r = extension.create(testResource());
34+
35+
await().pollDelay(Duration.ofMillis(200)).untilAsserted(() -> {
36+
var secondary =
37+
extension.get(OptionalDependentSecondaryCustomResource.class, r.getMetadata().getName());
38+
assertThat(secondary).isNull();
39+
});
40+
LocallyRunOperatorExtension.applyCrd(OptionalDependentSecondaryCustomResource.class,
41+
extension.getKubernetesClient());
42+
43+
// triggering reconciliation explicitly
44+
r.getMetadata().getAnnotations().put("trigger", "true");
45+
extension.replace(r);
46+
47+
await().untilAsserted(() -> {
48+
var secondary =
49+
extension.get(OptionalDependentSecondaryCustomResource.class, r.getMetadata().getName());
50+
assertThat(secondary).isNotNull();
51+
});
52+
}
53+
54+
private OptionalDependentCustomResource testResource() {
55+
var res = new OptionalDependentCustomResource();
56+
res.setMetadata(new ObjectMetaBuilder()
57+
.withName("test1")
58+
.build());
59+
return res;
60+
}
61+
62+
}
63+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.javaoperatorsdk.operator.sample.optionaldependent;
2+
3+
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
4+
import io.javaoperatorsdk.operator.api.reconciler.Context;
5+
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
6+
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
7+
8+
@KubernetesDependent
9+
public class OptionalDependent extends
10+
CRUDKubernetesDependentResource<OptionalDependentSecondaryCustomResource, OptionalDependentCustomResource> {
11+
12+
public OptionalDependent() {
13+
super(OptionalDependentSecondaryCustomResource.class);
14+
}
15+
16+
@Override
17+
protected OptionalDependentSecondaryCustomResource desired(
18+
OptionalDependentCustomResource primary,
19+
Context<OptionalDependentCustomResource> context) {
20+
var res = new OptionalDependentSecondaryCustomResource();
21+
res.setMetadata(new ObjectMetaBuilder()
22+
.withName(primary.getMetadata().getName())
23+
.withNamespace(primary.getMetadata().getNamespace())
24+
.build());
25+
return res;
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.javaoperatorsdk.operator.sample.optionaldependent;
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.ShortNames;
7+
import io.fabric8.kubernetes.model.annotation.Version;
8+
9+
@Group("sample.javaoperatorsdk")
10+
@Version("v1")
11+
@ShortNames("od")
12+
public class OptionalDependentCustomResource
13+
extends CustomResource<Void, Void>
14+
implements Namespaced {
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package io.javaoperatorsdk.operator.sample.optionaldependent;
2+
3+
import io.javaoperatorsdk.operator.api.reconciler.*;
4+
import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent;
5+
6+
@ControllerConfiguration(
7+
8+
generationAwareEventProcessing = false, // to easily trigger reconciliation with metadata update
9+
namespaces = Constants.WATCH_CURRENT_NAMESPACE,
10+
dependents = {
11+
@Dependent(type = OptionalDependent.class, optional = true),
12+
})
13+
public class OptionalDependentReconciler
14+
implements Reconciler<OptionalDependentCustomResource> {
15+
16+
@Override
17+
public UpdateControl<OptionalDependentCustomResource> reconcile(
18+
OptionalDependentCustomResource resource,
19+
Context<OptionalDependentCustomResource> context) {
20+
return UpdateControl.noUpdate();
21+
}
22+
23+
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.javaoperatorsdk.operator.sample.optionaldependent;
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.ShortNames;
7+
import io.fabric8.kubernetes.model.annotation.Version;
8+
9+
@Group("sample.javaoperatorsdk")
10+
@Version("v1")
11+
@ShortNames("ods")
12+
public class OptionalDependentSecondaryCustomResource
13+
extends CustomResource<Void, Void>
14+
implements Namespaced {
15+
}

0 commit comments

Comments
 (0)