Skip to content

Commit f0322f3

Browse files
committed
refactor: update matcher removal
Signed-off-by: Attila Mészáros <[email protected]>
1 parent 5337e79 commit f0322f3

File tree

5 files changed

+47
-108
lines changed

5 files changed

+47
-108
lines changed

Diff for: operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java

+13-43
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,14 @@
44
import java.util.Collections;
55
import java.util.List;
66

7-
import io.fabric8.kubernetes.api.model.ConfigMap;
87
import io.fabric8.kubernetes.api.model.HasMetadata;
9-
import io.fabric8.kubernetes.api.model.Secret;
108
import io.fabric8.zjsonpatch.JsonDiff;
119
import io.javaoperatorsdk.operator.api.reconciler.Context;
1210
import io.javaoperatorsdk.operator.processing.dependent.Matcher;
1311

1412
import com.fasterxml.jackson.databind.JsonNode;
1513

16-
public class GenericKubernetesResourceMatcher<R extends HasMetadata, P extends HasMetadata>
17-
implements Matcher<R, P> {
14+
public class GenericKubernetesResourceMatcher<R extends HasMetadata, P extends HasMetadata> {
1815

1916
private static final String SPEC = "/spec";
2017
private static final String METADATA = "/metadata";
@@ -26,40 +23,7 @@ public class GenericKubernetesResourceMatcher<R extends HasMetadata, P extends H
2623

2724
private static final String PATH = "path";
2825
private static final String[] EMPTY_ARRAY = {};
29-
private final KubernetesDependentResource<R, P> dependentResource;
3026

31-
private GenericKubernetesResourceMatcher(KubernetesDependentResource<R, P> dependentResource) {
32-
this.dependentResource = dependentResource;
33-
}
34-
35-
@SuppressWarnings({"unchecked", "rawtypes", "unused"})
36-
static <R extends HasMetadata, P extends HasMetadata> Matcher<R, P> matcherFor(
37-
KubernetesDependentResource<R, P> dependentResource) {
38-
return new GenericKubernetesResourceMatcher(dependentResource);
39-
}
40-
41-
/**
42-
* {@inheritDoc}
43-
* <p>
44-
* This implementation attempts to cover most common cases out of the box by considering
45-
* non-additive changes to the resource's spec (if the resource in question has a {@code spec}
46-
* field), making special provisions for {@link ConfigMap} and {@link Secret} resources. Additive
47-
* changes (i.e. a field is added that previously didn't exist) are not considered as triggering a
48-
* mismatch by default to account for validating webhooks that might add default values
49-
* automatically when not present or some other controller adding labels and/or annotations.
50-
* </p>
51-
* <p>
52-
* It should be noted that this implementation is potentially intensive because it generically
53-
* attempts to cover common use cases by performing diffs on the JSON representation of objects.
54-
* If performance is a concern, it might be easier / simpler to provide a {@link Matcher}
55-
* implementation optimized for your use case.
56-
* </p>
57-
*/
58-
@Override
59-
public Result<R> match(R actualResource, P primary, Context<P> context) {
60-
var desired = dependentResource.desired(primary, context);
61-
return match(desired, actualResource, false, false, context);
62-
}
6327

6428
/**
6529
* Determines whether the specified actual resource matches the specified desired resource,
@@ -84,14 +48,20 @@ public Result<R> match(R actualResource, P primary, Context<P> context) {
8448
* @param <R> resource
8549
* @return results of matching
8650
*/
87-
public static <R extends HasMetadata, P extends HasMetadata> Result<R> match(R desired,
51+
public static <R extends HasMetadata, P extends HasMetadata> Matcher.Result<R> match(R desired,
8852
R actualResource,
8953
boolean labelsAndAnnotationsEquality,
9054
boolean valuesEquality, Context<P> context) {
9155
return match(desired, actualResource,
9256
labelsAndAnnotationsEquality, valuesEquality, context, EMPTY_ARRAY);
9357
}
9458

59+
public static <R extends HasMetadata, P extends HasMetadata> Matcher.Result<R> match(R desired,
60+
R actualResource, Context<P> context) {
61+
return match(desired, actualResource,
62+
false, false, context, EMPTY_ARRAY);
63+
}
64+
9565
/**
9666
* Determines whether the specified actual resource matches the specified desired resource,
9767
* possibly considering metadata and deeper equality checks.
@@ -108,7 +78,7 @@ public static <R extends HasMetadata, P extends HasMetadata> Result<R> match(R d
10878
* @param <R> resource
10979
* @return results of matching
11080
*/
111-
public static <R extends HasMetadata, P extends HasMetadata> Result<R> match(R desired,
81+
public static <R extends HasMetadata, P extends HasMetadata> Matcher.Result<R> match(R desired,
11282
R actualResource,
11383
boolean labelsAndAnnotationsEquality,
11484
Context<P> context, String... ignorePaths) {
@@ -139,7 +109,7 @@ public static <R extends HasMetadata, P extends HasMetadata> Result<R> match(R d
139109
* match fails.
140110
* @return a {@link io.javaoperatorsdk.operator.processing.dependent.Matcher.Result} object
141111
*/
142-
public static <R extends HasMetadata, P extends HasMetadata> Result<R> match(
112+
public static <R extends HasMetadata, P extends HasMetadata> Matcher.Result<R> match(
143113
KubernetesDependentResource<R, P> dependentResource, R actualResource, P primary,
144114
Context<P> context,
145115
boolean labelsAndAnnotationsEquality,
@@ -150,7 +120,7 @@ public static <R extends HasMetadata, P extends HasMetadata> Result<R> match(
150120
ignorePaths);
151121
}
152122

153-
public static <R extends HasMetadata, P extends HasMetadata> Result<R> match(
123+
public static <R extends HasMetadata, P extends HasMetadata> Matcher.Result<R> match(
154124
KubernetesDependentResource<R, P> dependentResource, R actualResource, P primary,
155125
Context<P> context,
156126
boolean specEquality,
@@ -161,7 +131,7 @@ public static <R extends HasMetadata, P extends HasMetadata> Result<R> match(
161131
labelsAndAnnotationsEquality, specEquality, context, ignorePaths);
162132
}
163133

164-
public static <R extends HasMetadata, P extends HasMetadata> Result<R> match(R desired,
134+
public static <R extends HasMetadata, P extends HasMetadata> Matcher.Result<R> match(R desired,
165135
R actualResource, boolean labelsAndAnnotationsEquality, boolean valuesEquality,
166136
Context<P> context,
167137
String... ignoredPaths) {
@@ -194,7 +164,7 @@ public static <R extends HasMetadata, P extends HasMetadata> Result<R> match(R d
194164
}
195165
}
196166

197-
return Result.computed(matched, desired);
167+
return Matcher.Result.computed(matched, desired);
198168
}
199169

200170
private static boolean match(boolean equality, JsonNode diff,
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,17 @@
1-
package io.javaoperatorsdk.operator.processing.dependent.kubernetes.updatermatcher;
1+
package io.javaoperatorsdk.operator.processing.dependent.kubernetes;
22

33
import java.util.Map;
44

55
import io.fabric8.kubernetes.api.model.HasMetadata;
66
import io.fabric8.kubernetes.client.utils.KubernetesSerialization;
77
import io.javaoperatorsdk.operator.api.reconciler.Context;
8-
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericKubernetesResourceMatcher;
9-
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.ResourceUpdaterMatcher;
108

11-
public class GenericResourceUpdaterMatcher<R extends HasMetadata> implements
12-
ResourceUpdaterMatcher<R> {
9+
public class GenericResourceUpdater {
1310

1411
private static final String METADATA = "metadata";
15-
private static final ResourceUpdaterMatcher<?> INSTANCE = new GenericResourceUpdaterMatcher<>();
16-
17-
protected GenericResourceUpdaterMatcher() {}
18-
19-
@SuppressWarnings("unchecked")
20-
public static <R extends HasMetadata> ResourceUpdaterMatcher<R> updaterMatcherFor(
21-
Class<R> resourceType) {
22-
return (ResourceUpdaterMatcher<R>) INSTANCE;
23-
}
2412

2513
@SuppressWarnings("unchecked")
26-
@Override
27-
public R updateResource(R actual, R desired, Context<?> context) {
14+
public static <R extends HasMetadata> R updateResource(R actual, R desired, Context<?> context) {
2815
KubernetesSerialization kubernetesSerialization =
2916
context.getClient().getKubernetesSerialization();
3017
Map<String, Object> actualMap = kubernetesSerialization.convertValue(actual, Map.class);
@@ -41,12 +28,6 @@ public R updateResource(R actual, R desired, Context<?> context) {
4128
return clonedActual;
4229
}
4330

44-
@Override
45-
public boolean matches(R actual, R desired, Context<?> context) {
46-
return GenericKubernetesResourceMatcher.match(desired, actual,
47-
false, false, context).matched();
48-
}
49-
5031
public static <K extends HasMetadata> void updateLabelsAndAnnotation(K actual, K desired) {
5132
actual.getMetadata().getLabels().putAll(desired.getMetadata().getLabels());
5233
actual.getMetadata().getAnnotations().putAll(desired.getMetadata().getAnnotations());

Diff for: operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java

+7-16
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DependentResourceConfigurator;
2525
import io.javaoperatorsdk.operator.processing.dependent.AbstractEventSourceHolderDependentResource;
2626
import io.javaoperatorsdk.operator.processing.dependent.Matcher.Result;
27-
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.updatermatcher.GenericResourceUpdaterMatcher;
2827
import io.javaoperatorsdk.operator.processing.event.ResourceID;
2928
import io.javaoperatorsdk.operator.processing.event.source.SecondaryToPrimaryMapper;
3029
import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
@@ -40,10 +39,7 @@ public abstract class KubernetesDependentResource<R extends HasMetadata, P exten
4039
private static final Logger log = LoggerFactory.getLogger(KubernetesDependentResource.class);
4140
private final boolean garbageCollected = this instanceof GarbageCollected;
4241
private final boolean usingCustomResourceUpdateMatcher = this instanceof ResourceUpdaterMatcher;
43-
@SuppressWarnings("unchecked")
44-
private final ResourceUpdaterMatcher<R> updaterMatcher = usingCustomResourceUpdateMatcher
45-
? (ResourceUpdaterMatcher<R>) this
46-
: GenericResourceUpdaterMatcher.updaterMatcherFor(resourceType());
42+
4743
private final boolean clustered;
4844
private KubernetesDependentResourceConfig<R> kubernetesDependentResourceConfig;
4945

@@ -150,7 +146,7 @@ public R update(R actual, R desired, P primary, Context<P> context) {
150146
.fieldManager(context.getControllerConfiguration().fieldManager())
151147
.forceConflicts().serverSideApply();
152148
} else {
153-
var updatedActual = updaterMatcher.updateResource(actual, desired, context);
149+
var updatedActual = GenericResourceUpdater.updateResource(actual, desired, context);
154150
updatedResource = prepare(context, updatedActual, primary, "Updating").update();
155151
}
156152
log.debug("Resource version after update: {}",
@@ -161,26 +157,21 @@ public R update(R actual, R desired, P primary, Context<P> context) {
161157
@Override
162158
public Result<R> match(R actualResource, P primary, Context<P> context) {
163159
final var desired = desired(primary, context);
164-
return match(actualResource, desired, primary, updaterMatcher, context);
160+
return match(actualResource, desired, primary, context);
165161
}
166162

167-
@SuppressWarnings({"unused", "unchecked"})
168-
public Result<R> match(R actualResource, R desired, P primary, Context<P> context) {
169-
return match(actualResource, desired, primary,
170-
(ResourceUpdaterMatcher<R>) GenericResourceUpdaterMatcher
171-
.updaterMatcherFor(actualResource.getClass()),
172-
context);
173-
}
174163

175-
public Result<R> match(R actualResource, R desired, P primary, ResourceUpdaterMatcher<R> matcher,
164+
165+
public Result<R> match(R actualResource, R desired, P primary,
176166
Context<P> context) {
177167
final boolean matches;
178168
addMetadata(true, actualResource, desired, primary, context);
179169
if (useSSA(context)) {
180170
matches = SSABasedGenericKubernetesResourceMatcher.getInstance()
181171
.matches(actualResource, desired, context);
182172
} else {
183-
matches = matcher.matches(actualResource, desired, context);
173+
matches = GenericKubernetesResourceMatcher.match(desired, actualResource,
174+
false, false, context).matched();
184175
}
185176
return Result.computed(matches, desired);
186177
}

Diff for: operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcherTest.java

+15-13
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,21 @@
1313
import io.javaoperatorsdk.operator.MockKubernetesClient;
1414
import io.javaoperatorsdk.operator.ReconcilerUtils;
1515
import io.javaoperatorsdk.operator.api.reconciler.Context;
16-
import io.javaoperatorsdk.operator.processing.dependent.Matcher;
17-
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.updatermatcher.GenericResourceUpdaterMatcher;
1816

1917
import static io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericKubernetesResourceMatcher.match;
2018
import static org.assertj.core.api.Assertions.assertThat;
2119
import static org.mockito.Mockito.mock;
2220
import static org.mockito.Mockito.when;
2321

24-
@SuppressWarnings({"unchecked", "rawtypes"})
22+
@SuppressWarnings({"unchecked"})
2523
class GenericKubernetesResourceMatcherTest {
2624

2725
private static final Context context = mock(Context.class);
2826

2927
Deployment actual = createDeployment();
3028
Deployment desired = createDeployment();
3129
TestDependentResource dependentResource = new TestDependentResource(desired);
32-
Matcher matcher = GenericKubernetesResourceMatcher.matcherFor(dependentResource);
30+
3331

3432
@BeforeAll
3533
static void setUp() {
@@ -39,15 +37,17 @@ static void setUp() {
3937

4038
@Test
4139
void matchesTrivialCases() {
42-
assertThat(matcher.match(actual, null, context).matched()).isTrue();
43-
assertThat(matcher.match(actual, null, context).computedDesired()).isPresent();
44-
assertThat(matcher.match(actual, null, context).computedDesired()).contains(desired);
40+
assertThat(GenericKubernetesResourceMatcher.match(desired, actual, context).matched()).isTrue();
41+
assertThat(GenericKubernetesResourceMatcher.match(desired, actual, context).computedDesired())
42+
.isPresent();
43+
assertThat(GenericKubernetesResourceMatcher.match(desired, actual, context).computedDesired())
44+
.contains(desired);
4545
}
4646

4747
@Test
4848
void matchesAdditiveOnlyChanges() {
4949
actual.getSpec().getTemplate().getMetadata().getLabels().put("new-key", "val");
50-
assertThat(matcher.match(actual, null, context).matched())
50+
assertThat(GenericKubernetesResourceMatcher.match(desired, actual, context).matched())
5151
.withFailMessage("Additive changes should not cause a mismatch by default")
5252
.isTrue();
5353
}
@@ -64,7 +64,9 @@ void matchesWithStrongSpecEquality() {
6464
@Test
6565
void doesNotMatchRemovedValues() {
6666
actual = createDeployment();
67-
assertThat(matcher.match(actual, createPrimary("removed"), context).matched())
67+
assertThat(GenericKubernetesResourceMatcher
68+
.match(dependentResource.desired(createPrimary("removed"), null), actual, context)
69+
.matched())
6870
.withFailMessage("Removing values in metadata should lead to a mismatch")
6971
.isFalse();
7072
}
@@ -73,7 +75,7 @@ void doesNotMatchRemovedValues() {
7375
void doesNotMatchChangedValues() {
7476
actual = createDeployment();
7577
actual.getSpec().setReplicas(2);
76-
assertThat(matcher.match(actual, null, context).matched())
78+
assertThat(GenericKubernetesResourceMatcher.match(desired, actual, context).matched())
7779
.withFailMessage("Should not have matched because values have changed")
7880
.isFalse();
7981
}
@@ -82,7 +84,7 @@ void doesNotMatchChangedValues() {
8284
void ignoreStatus() {
8385
actual = createDeployment();
8486
actual.setStatus(new DeploymentStatusBuilder().withReadyReplicas(1).build());
85-
assertThat(matcher.match(actual, null, context).matched())
87+
assertThat(GenericKubernetesResourceMatcher.match(desired, actual, context).matched())
8688
.withFailMessage("Should ignore status in actual")
8789
.isTrue();
8890
}
@@ -146,8 +148,8 @@ void checkServiceAccount() {
146148
.addNewImagePullSecret("imagePullSecret3")
147149
.build();
148150

149-
final var matcher = GenericResourceUpdaterMatcher.updaterMatcherFor(ServiceAccount.class);
150-
assertThat(matcher.matches(actual, desired, context)).isTrue();
151+
assertThat(GenericKubernetesResourceMatcher.match(desired, actual, false, false, context)
152+
.matched()).isTrue();
151153
}
152154

153155
@Test

0 commit comments

Comments
 (0)