Skip to content

Commit 2910c6a

Browse files
committed
Add integration test
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
1 parent 53081c4 commit 2910c6a

File tree

6 files changed

+208
-1
lines changed

6 files changed

+208
-1
lines changed

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcher.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ List<Object> sortListItems(List<Object> list) {
198198
}
199199

200200
/** Correct for known issue with SSA */
201-
private void sanitizeState(R actual, R desired, Map<String, Object> actualMap) {
201+
protected void sanitizeState(R actual, R desired, Map<String, Object> actualMap) {
202202
if (actual instanceof StatefulSet actualStatefulSet
203203
&& desired instanceof StatefulSet desiredStatefulSet) {
204204
var actualSpec = actualStatefulSet.getSpec();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package io.javaoperatorsdk.operator.dependent.prevblocklist;
2+
3+
import java.util.Map;
4+
5+
import io.fabric8.kubernetes.api.model.ContainerBuilder;
6+
import io.fabric8.kubernetes.api.model.HasMetadata;
7+
import io.fabric8.kubernetes.api.model.LabelSelectorBuilder;
8+
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
9+
import io.fabric8.kubernetes.api.model.PodSpecBuilder;
10+
import io.fabric8.kubernetes.api.model.PodTemplateSpecBuilder;
11+
import io.fabric8.kubernetes.api.model.Quantity;
12+
import io.fabric8.kubernetes.api.model.ResourceRequirementsBuilder;
13+
import io.fabric8.kubernetes.api.model.apps.Deployment;
14+
import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
15+
import io.fabric8.kubernetes.api.model.apps.DeploymentSpecBuilder;
16+
import io.javaoperatorsdk.operator.api.reconciler.Context;
17+
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
18+
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericKubernetesResourceMatcher;
19+
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
20+
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.SSABasedGenericKubernetesResourceMatcher;
21+
22+
@KubernetesDependent
23+
public class DeploymentDependent
24+
extends CRUDKubernetesDependentResource<Deployment, PrevAnnotationBlockCustomResource> {
25+
26+
public static final String RESOURCE_NAME = "test1";
27+
28+
public DeploymentDependent() {
29+
super(Deployment.class);
30+
}
31+
32+
@Override
33+
protected Deployment desired(
34+
PrevAnnotationBlockCustomResource primary,
35+
Context<PrevAnnotationBlockCustomResource> context) {
36+
37+
return new DeploymentBuilder()
38+
.withMetadata(
39+
new ObjectMetaBuilder()
40+
.withName(primary.getMetadata().getName())
41+
.withNamespace(primary.getMetadata().getNamespace())
42+
.build())
43+
.withSpec(
44+
new DeploymentSpecBuilder()
45+
.withReplicas(1)
46+
.withSelector(
47+
new LabelSelectorBuilder().withMatchLabels(Map.of("app", "nginx")).build())
48+
.withTemplate(
49+
new PodTemplateSpecBuilder()
50+
.withMetadata(
51+
new ObjectMetaBuilder().withLabels(Map.of("app", "nginx")).build())
52+
.withSpec(
53+
new PodSpecBuilder()
54+
.withContainers(
55+
new ContainerBuilder()
56+
.withName("nginx")
57+
.withImage("nginx:1.14.2")
58+
.withResources(
59+
new ResourceRequirementsBuilder()
60+
.withLimits(Map.of("cpu", new Quantity("2000m")))
61+
.build())
62+
.build())
63+
.build())
64+
.build())
65+
.build())
66+
.build();
67+
}
68+
69+
// for testing purposes replicating the matching logic but with the special matcher
70+
@Override
71+
public Result<Deployment> match(
72+
Deployment actualResource,
73+
Deployment desired,
74+
PrevAnnotationBlockCustomResource primary,
75+
Context<PrevAnnotationBlockCustomResource> context) {
76+
final boolean matches;
77+
addMetadata(true, actualResource, desired, primary, context);
78+
if (useSSA(context)) {
79+
matches = new SSAMatcherWithoutSanitization().matches(actualResource, desired, context);
80+
} else {
81+
matches =
82+
GenericKubernetesResourceMatcher.match(desired, actualResource, false, false, context)
83+
.matched();
84+
}
85+
return Result.computed(matches, desired);
86+
}
87+
88+
// using this matcher, so we are able to reproduce issue with resource limits
89+
static class SSAMatcherWithoutSanitization<R extends HasMetadata>
90+
extends SSABasedGenericKubernetesResourceMatcher<R> {
91+
92+
@Override
93+
protected void sanitizeState(R actual, R desired, Map<String, Object> actualMap) {}
94+
}
95+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.javaoperatorsdk.operator.dependent.prevblocklist;
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("pabc")
12+
public class PrevAnnotationBlockCustomResource extends CustomResource<PrevAnnotationBlockSpec, Void>
13+
implements Namespaced {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.javaoperatorsdk.operator.dependent.prevblocklist;
2+
3+
import java.util.concurrent.atomic.AtomicInteger;
4+
5+
import io.javaoperatorsdk.operator.api.reconciler.Context;
6+
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
7+
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
8+
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;
9+
import io.javaoperatorsdk.operator.api.reconciler.Workflow;
10+
import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent;
11+
import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider;
12+
13+
@Workflow(dependents = {@Dependent(type = DeploymentDependent.class)})
14+
@ControllerConfiguration()
15+
public class PrevAnnotationBlockReconciler
16+
implements Reconciler<PrevAnnotationBlockCustomResource>, TestExecutionInfoProvider {
17+
18+
private final AtomicInteger numberOfExecutions = new AtomicInteger(0);
19+
20+
public PrevAnnotationBlockReconciler() {}
21+
22+
@Override
23+
public UpdateControl<PrevAnnotationBlockCustomResource> reconcile(
24+
PrevAnnotationBlockCustomResource resource,
25+
Context<PrevAnnotationBlockCustomResource> context) {
26+
numberOfExecutions.getAndIncrement();
27+
28+
return UpdateControl.noUpdate();
29+
}
30+
31+
public int getNumberOfExecutions() {
32+
return numberOfExecutions.get();
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package io.javaoperatorsdk.operator.dependent.prevblocklist;
2+
3+
import java.time.Duration;
4+
5+
import org.junit.jupiter.api.Test;
6+
import org.junit.jupiter.api.extension.RegisterExtension;
7+
8+
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
9+
import io.fabric8.kubernetes.api.model.apps.Deployment;
10+
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;
11+
12+
import static org.assertj.core.api.Assertions.assertThat;
13+
import static org.awaitility.Awaitility.await;
14+
15+
class PrevAnnotationBlockReconcilerIT {
16+
17+
public static final String TEST_1 = "test1";
18+
19+
@RegisterExtension
20+
LocallyRunOperatorExtension extension =
21+
LocallyRunOperatorExtension.builder()
22+
// Removing resource from blocklist List would result in test failure
23+
// .withConfigurationService(
24+
// o -> o.previousAnnotationUsageBlocklist(Collections.emptyList()))
25+
.withReconciler(PrevAnnotationBlockReconciler.class)
26+
.build();
27+
28+
@Test
29+
void doNotUsePrevAnnotationForDeploymentDependent() {
30+
extension.create(testResource(TEST_1));
31+
32+
var reconciler = extension.getReconcilerOfType(PrevAnnotationBlockReconciler.class);
33+
await()
34+
.pollDelay(Duration.ofMillis(200))
35+
.untilAsserted(
36+
() -> {
37+
var deployment = extension.get(Deployment.class, TEST_1);
38+
assertThat(deployment).isNotNull();
39+
assertThat(reconciler.getNumberOfExecutions()).isGreaterThan(0).isLessThan(10);
40+
});
41+
}
42+
43+
PrevAnnotationBlockCustomResource testResource(String name) {
44+
var res = new PrevAnnotationBlockCustomResource();
45+
res.setMetadata(new ObjectMetaBuilder().withName(name).build());
46+
res.setSpec(new PrevAnnotationBlockSpec());
47+
res.getSpec().setValue("value");
48+
return res;
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.javaoperatorsdk.operator.dependent.prevblocklist;
2+
3+
public class PrevAnnotationBlockSpec {
4+
5+
private String value;
6+
7+
public String getValue() {
8+
return value;
9+
}
10+
11+
public PrevAnnotationBlockSpec setValue(String value) {
12+
this.value = value;
13+
return this;
14+
}
15+
}

0 commit comments

Comments
 (0)