Skip to content

Commit d763fcb

Browse files
authored
improve: matcher for config maps (#2121)
Signed-off-by: Attila Mészáros <csviri@gmail.com>
1 parent 4cc151e commit d763fcb

File tree

2 files changed

+50
-36
lines changed

2 files changed

+50
-36
lines changed

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

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -73,26 +73,26 @@ public Result<R> match(R actualResource, P primary, Context<P> context) {
7373
* @param labelsAndAnnotationsEquality if true labels and annotation match exactly in the actual
7474
* and desired state if false, additional elements are allowed in actual annotations.
7575
* Considered only if considerLabelsAndAnnotations is true.
76-
* @param specEquality if {@code false}, the algorithm checks if the properties in the desired
77-
* resource spec are same as in the actual resource spec. The reason is that admission
78-
* controllers and default Kubernetes controllers might add default values to some
79-
* properties which are not set in the desired resources' spec and comparing it with simple
80-
* equality check would mean that such resource will not match (while conceptually should).
81-
* However, there is an issue with this for example if desired spec contains a list of
82-
* values and a value is removed, this still will match the actual state from previous
83-
* reconciliation. Setting this parameter to {@code true}, will match the resources only if
84-
* all properties and values are equal. This could be implemented also by overriding equals
85-
* method of spec, should be done as an optimization - this implementation does not require
86-
* that.
76+
* @param valuesEquality if {@code false}, the algorithm checks if the properties in the desired
77+
* resource spec (or other non metadata value) are same as in the actual resource spec. The
78+
* reason is that admission controllers and default Kubernetes controllers might add
79+
* default values to some properties which are not set in the desired resources' spec and
80+
* comparing it with simple equality check would mean that such resource will not match
81+
* (while conceptually should). However, there is an issue with this for example if desired
82+
* spec contains a list of values and a value is removed, this still will match the actual
83+
* state from previous reconciliation. Setting this parameter to {@code true}, will match
84+
* the resources only if all properties and values are equal. This could be implemented
85+
* also by overriding equals method of spec, should be done as an optimization - this
86+
* implementation does not require that.
8787
* @param <R> resource
8888
* @return results of matching
8989
*/
9090
public static <R extends HasMetadata, P extends HasMetadata> Result<R> match(R desired,
9191
R actualResource,
9292
boolean considerLabelsAndAnnotations, boolean labelsAndAnnotationsEquality,
93-
boolean specEquality, Context<P> context) {
93+
boolean valuesEquality, Context<P> context) {
9494
return match(desired, actualResource, considerLabelsAndAnnotations,
95-
labelsAndAnnotationsEquality, specEquality, context, EMPTY_ARRAY);
95+
labelsAndAnnotationsEquality, valuesEquality, context, EMPTY_ARRAY);
9696
}
9797

9898
/**
@@ -171,14 +171,14 @@ public static <R extends HasMetadata, P extends HasMetadata> Result<R> match(
171171

172172
public static <R extends HasMetadata, P extends HasMetadata> Result<R> match(R desired,
173173
R actualResource,
174-
boolean considerMetadata, boolean labelsAndAnnotationsEquality, boolean specEquality,
174+
boolean considerMetadata, boolean labelsAndAnnotationsEquality, boolean valuesEquality,
175175
Context<P> context,
176176
String... ignoredPaths) {
177177
final List<String> ignoreList =
178178
ignoredPaths != null && ignoredPaths.length > 0 ? Arrays.asList(ignoredPaths)
179179
: Collections.emptyList();
180180

181-
if (specEquality && !ignoreList.isEmpty()) {
181+
if (valuesEquality && !ignoreList.isEmpty()) {
182182
throw new IllegalArgumentException(
183183
"Equality should be false in case of ignore list provided");
184184
}
@@ -192,15 +192,15 @@ public static <R extends HasMetadata, P extends HasMetadata> Result<R> match(R d
192192
for (int i = 0; i < wholeDiffJsonPatch.size() && matched; i++) {
193193
var node = wholeDiffJsonPatch.get(i);
194194
if (nodeIsChildOf(node, List.of(SPEC))) {
195-
matched = match(specEquality, node, ignoreList);
195+
matched = match(valuesEquality, node, ignoreList);
196196
} else if (nodeIsChildOf(node, List.of(METADATA))) {
197197
// conditionally consider labels and annotations
198198
if (considerMetadata
199199
&& nodeIsChildOf(node, List.of(METADATA_LABELS, METADATA_ANNOTATIONS))) {
200200
matched = match(labelsAndAnnotationsEquality, node, Collections.emptyList());
201201
}
202202
} else if (!nodeIsChildOf(node, IGNORED_FIELDS)) {
203-
matched = match(true, node, ignoreList);
203+
matched = match(valuesEquality, node, ignoreList);
204204
}
205205
}
206206

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

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package io.javaoperatorsdk.operator.processing.dependent.kubernetes;
22

3+
import java.util.Map;
34
import java.util.Optional;
45

56
import org.junit.jupiter.api.BeforeAll;
67
import org.junit.jupiter.api.Test;
78

8-
import io.fabric8.kubernetes.api.model.HasMetadata;
9-
import io.fabric8.kubernetes.api.model.ServiceAccount;
10-
import io.fabric8.kubernetes.api.model.ServiceAccountBuilder;
9+
import io.fabric8.kubernetes.api.model.*;
1110
import io.fabric8.kubernetes.api.model.apps.Deployment;
1211
import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
1312
import io.fabric8.kubernetes.api.model.apps.DeploymentStatusBuilder;
@@ -17,6 +16,7 @@
1716
import io.javaoperatorsdk.operator.processing.dependent.Matcher;
1817
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.updatermatcher.GenericResourceUpdaterMatcher;
1918

19+
import static io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericKubernetesResourceMatcher.match;
2020
import static org.assertj.core.api.Assertions.assertThat;
2121
import static org.mockito.Mockito.mock;
2222
import static org.mockito.Mockito.when;
@@ -55,9 +55,8 @@ void matchesAdditiveOnlyChanges() {
5555
@Test
5656
void matchesWithStrongSpecEquality() {
5757
actual.getSpec().getTemplate().getMetadata().getLabels().put("new-key", "val");
58-
assertThat(GenericKubernetesResourceMatcher
59-
.match(dependentResource, actual, null, context, true, true,
60-
true)
58+
assertThat(match(dependentResource, actual, null, context, true, true,
59+
true)
6160
.matched())
6261
.withFailMessage("Adding values should fail matching when strong equality is required")
6362
.isFalse();
@@ -93,8 +92,7 @@ void ignoreStatus() {
9392
void doesNotMatchChangedValuesWhenNoIgnoredPathsAreProvided() {
9493
actual = createDeployment();
9594
actual.getSpec().setReplicas(2);
96-
assertThat(GenericKubernetesResourceMatcher
97-
.match(dependentResource, actual, null, context, true).matched())
95+
assertThat(match(dependentResource, actual, null, context, true).matched())
9896
.withFailMessage(
9997
"Should not have matched because values have changed and no ignored path is provided")
10098
.isFalse();
@@ -104,8 +102,7 @@ void doesNotMatchChangedValuesWhenNoIgnoredPathsAreProvided() {
104102
void doesNotAttemptToMatchIgnoredPaths() {
105103
actual = createDeployment();
106104
actual.getSpec().setReplicas(2);
107-
assertThat(GenericKubernetesResourceMatcher
108-
.match(dependentResource, actual, null, context, false, "/spec/replicas").matched())
105+
assertThat(match(dependentResource, actual, null, context, false, "/spec/replicas").matched())
109106
.withFailMessage("Should not have compared ignored paths")
110107
.isTrue();
111108
}
@@ -114,8 +111,7 @@ void doesNotAttemptToMatchIgnoredPaths() {
114111
void ignoresWholeSubPath() {
115112
actual = createDeployment();
116113
actual.getSpec().getTemplate().getMetadata().getLabels().put("additional-key", "val");
117-
assertThat(GenericKubernetesResourceMatcher
118-
.match(dependentResource, actual, null, context, false, "/spec/template").matched())
114+
assertThat(match(dependentResource, actual, null, context, false, "/spec/template").matched())
119115
.withFailMessage("Should match when only changes impact ignored sub-paths")
120116
.isTrue();
121117
}
@@ -127,18 +123,15 @@ void matchesMetadata() {
127123
.addToAnnotations("test", "value")
128124
.endMetadata()
129125
.build();
130-
assertThat(GenericKubernetesResourceMatcher
131-
.match(dependentResource, actual, null, context, false).matched())
126+
assertThat(match(dependentResource, actual, null, context, false).matched())
132127
.withFailMessage("Annotations shouldn't matter when metadata is not considered")
133128
.isTrue();
134129

135-
assertThat(GenericKubernetesResourceMatcher
136-
.match(dependentResource, actual, null, context, true, true, true).matched())
130+
assertThat(match(dependentResource, actual, null, context, true, true, true).matched())
137131
.withFailMessage("Annotations should matter when metadata is considered")
138132
.isFalse();
139133

140-
assertThat(GenericKubernetesResourceMatcher
141-
.match(dependentResource, actual, null, context, true, false).matched())
134+
assertThat(match(dependentResource, actual, null, context, true, false).matched())
142135
.withFailMessage(
143136
"Should match when strong equality is not considered and only additive changes are made")
144137
.isTrue();
@@ -155,7 +148,28 @@ void checkServiceAccount() {
155148
.build();
156149

157150
final var matcher = GenericResourceUpdaterMatcher.updaterMatcherFor(ServiceAccount.class);
158-
assertThat(matcher.matches(actual, desired, context)).isFalse();
151+
assertThat(matcher.matches(actual, desired, context)).isTrue();
152+
}
153+
154+
@Test
155+
void matchConfigMap() {
156+
var desired = createConfigMap();
157+
var actual = createConfigMap();
158+
actual.getData().put("key2", "val2");
159+
160+
var match = GenericKubernetesResourceMatcher.match(desired, actual, true,
161+
true, false, context);
162+
assertThat(match.matched()).isTrue();
163+
}
164+
165+
ConfigMap createConfigMap() {
166+
return new ConfigMapBuilder()
167+
.withMetadata(new ObjectMetaBuilder()
168+
.withName("tes1")
169+
.withNamespace("default")
170+
.build())
171+
.withData(Map.of("key1", "val1"))
172+
.build();
159173
}
160174

161175
Deployment createDeployment() {

0 commit comments

Comments
 (0)