Skip to content

Commit b379178

Browse files
committed
covering v: values blindly, refactor
1 parent 9364a33 commit b379178

File tree

1 file changed

+135
-69
lines changed

1 file changed

+135
-69
lines changed

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

+135-69
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
// https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#fieldsv1-v1-meta
1818
// https://github.com/kubernetes-sigs/structured-merge-diff
1919
// https://docs.aws.amazon.com/eks/latest/userguide/kubernetes-field-management.html
20+
// see also: https://kubernetes.slack.com/archives/C0123CNN8F3/p1686141087220719
2021
public class SSABasedGenericKubernetesResourceMatcher<R extends HasMetadata> {
2122

2223
@SuppressWarnings("rawtypes")
@@ -34,51 +35,45 @@ public static <L extends HasMetadata> SSABasedGenericKubernetesResourceMatcher<L
3435

3536
private static final String F_PREFIX = "f:";
3637
private static final String K_PREFIX = "k:";
38+
private static final String V_PREFIX = "v:";
3739
public static final String METADATA_KEY = "metadata";
3840

3941
private static final Logger log =
4042
LoggerFactory.getLogger(SSABasedGenericKubernetesResourceMatcher.class);
4143

42-
// todo list orders should be irrelevant, change lists to sets?
43-
// todo owner reference removal?
44-
public boolean matches(R actual, R desired, Context<?> context) {
4544

46-
var optionalManagedFieldsEntry =
47-
checkIfFieldManagerExists(actual,
48-
context.getControllerConfiguration().fieldManager());
49-
// the update will add the field manager
50-
// todo check if it's an apply, since if not it could be a different field manager?
51-
if (optionalManagedFieldsEntry.isEmpty()) {
52-
return false;
53-
}
45+
public boolean matches(R actual, R desired, Context<?> context) {
46+
try {
47+
var optionalManagedFieldsEntry =
48+
checkIfFieldManagerExists(actual,
49+
context.getControllerConfiguration().fieldManager());
50+
// the results of this is that it will add the field manager; it's important from migration
51+
// aspect
52+
if (optionalManagedFieldsEntry.isEmpty()) {
53+
return false;
54+
}
5455

55-
var managedFieldsEntry = optionalManagedFieldsEntry.orElseThrow();
56+
var managedFieldsEntry = optionalManagedFieldsEntry.orElseThrow();
5657

57-
var objectMapper =
58-
context.getControllerConfiguration().getConfigurationService().getObjectMapper();
58+
var objectMapper =
59+
context.getControllerConfiguration().getConfigurationService().getObjectMapper();
5960

60-
var actualMap = objectMapper.convertValue(actual, typeRef);
61-
var desiredMap = objectMapper.convertValue(desired, typeRef);
61+
var actualMap = objectMapper.convertValue(actual, typeRef);
62+
var desiredMap = objectMapper.convertValue(desired, typeRef);
6263

63-
log.trace("Original actual: \n {} \n original desired: \n {} ", actual, desiredMap);
64+
log.trace("Original actual: \n {} \n original desired: \n {} ", actual, desiredMap);
6465

65-
var prunedActual = new HashMap<String, Object>();
66-
pruneActualAccordingManagedFields(prunedActual, actualMap,
67-
managedFieldsEntry.getFieldsV1().getAdditionalProperties(), objectMapper);
68-
removeIrrelevantValues(desiredMap);
66+
var prunedActual = new HashMap<String, Object>();
67+
pruneActualAccordingManagedFields(prunedActual, actualMap,
68+
managedFieldsEntry.getFieldsV1().getAdditionalProperties(), objectMapper);
6969

70-
log.trace("Pruned actual: \n {} \n desired: \n {} ", prunedActual, desiredMap);
70+
removeIrrelevantValues(desiredMap);
7171

72-
return prunedActual.equals(desiredMap);
73-
}
72+
log.trace("Pruned actual: \n {} \n desired: \n {} ", prunedActual, desiredMap);
7473

75-
private void removeOwnerRefernces(HashMap<String, Object> resourceMap) {
76-
var metadata = (Map<String, Object>) resourceMap.get(METADATA_KEY);
77-
if (metadata != null) {
78-
metadata.remove("ownerReferences");
79-
if (metadata.isEmpty()) {
80-
resourceMap.remove(METADATA_KEY);
81-
}
74+
return prunedActual.equals(desiredMap);
75+
} catch (JsonProcessingException e) {
76+
throw new IllegalStateException(e);
8277
}
8378
}
8479

@@ -95,66 +90,137 @@ private void removeIrrelevantValues(HashMap<String, Object> desiredMap) {
9590

9691
private void pruneActualAccordingManagedFields(Map<String, Object> result,
9792
Map<String, Object> actualMap,
98-
Map<String, Object> managedFields, ObjectMapper objectMapper) {
93+
Map<String, Object> managedFields, ObjectMapper objectMapper) throws JsonProcessingException {
94+
9995
if (managedFields.isEmpty()) {
10096
result.putAll(actualMap);
10197
return;
10298
}
10399
for (Map.Entry<String, Object> entry : managedFields.entrySet()) {
104100
String key = entry.getKey();
105101
if (key.startsWith(F_PREFIX)) {
106-
String targetKey = key.substring(2);
102+
String keyInActual = key.substring(2);
107103
var managedFieldValue = entry.getValue();
108-
if (managedFieldValue instanceof Map && (!((Map) managedFieldValue).isEmpty())) {
104+
// basically if we should travers further
105+
if (isNestedValue(managedFieldValue)) {
109106
var managedEntrySet = ((Map<String, Object>) managedFieldValue).entrySet();
110107

111-
if (isListEntrySet(managedEntrySet)) {
112-
var valueList = new ArrayList<>();
113-
result.put(targetKey, valueList);
114-
for (Map.Entry<String, Object> listEntry : managedEntrySet) {
115-
var actualListEntry = selectListEntryBasedOnKey(listEntry.getKey().substring(2),
116-
(List<Map<String, Object>>) actualMap.get(targetKey), objectMapper);
117-
118-
if (listEntry.getValue() instanceof Map
119-
&& ((Map<String, Object>) listEntry.getValue()).isEmpty()) {
120-
var map = new HashMap<String, Object>();
121-
valueList.add(map);
122-
pruneActualAccordingManagedFields(map, actualListEntry,
123-
(Map<String, Object>) listEntry.getValue(), objectMapper);
124-
continue;
125-
}
126-
127-
if (DOT_KEY.equals(listEntry.getKey())) {
128-
continue;
129-
}
130-
var emptyResMapValue = new HashMap<String, Object>();
131-
valueList.add(emptyResMapValue);
132-
pruneActualAccordingManagedFields(emptyResMapValue, actualListEntry,
133-
(Map<String, Object>) listEntry.getValue(), objectMapper);
134-
}
108+
if (isListKeyEntrySet(managedEntrySet)) {
109+
handleListKeyEntrySet(result, actualMap, objectMapper, keyInActual, managedEntrySet);
110+
} else if (isSetValueField(managedEntrySet)) {
111+
handleSetValues(result, actualMap, objectMapper, keyInActual, managedEntrySet);
135112
} else {
136-
var emptyMapValue = new HashMap<String, Object>();
137-
result.put(targetKey, emptyMapValue);
138-
var actualMapValue = actualMap.get(targetKey);
139-
log.debug("key: {} actual map value: {} managedFieldValue: {}", targetKey,
140-
actualMapValue, managedFieldValue);
141-
142-
pruneActualAccordingManagedFields(emptyMapValue, (Map<String, Object>) actualMapValue,
143-
(Map<String, Object>) managedFields.get(key), objectMapper);
113+
fillResultsAndTraversFurther(result, actualMap, managedFields, objectMapper, key,
114+
keyInActual, managedFieldValue);
144115
}
145116
} else {
146-
result.put(targetKey, actualMap.get(targetKey));
117+
// this should handle the case when the value is complex in the actual map (not just a simple value).
118+
result.put(keyInActual, actualMap.get(keyInActual));
147119
}
148120
} else {
149-
if (!".".equals(key)) {
121+
// .:{} is ignored, other should not be present
122+
if (!DOT_KEY.equals(key)) {
150123
throw new IllegalStateException("Key: " + key + " has no prefix: " + F_PREFIX);
151124
}
152125
}
153126
}
154127

155128
}
156129

157-
private boolean isListEntrySet(Set<Map.Entry<String, Object>> managedEntrySet) {
130+
private void fillResultsAndTraversFurther(Map<String, Object> result,
131+
Map<String, Object> actualMap, Map<String, Object> managedFields, ObjectMapper objectMapper,
132+
String key, String keyInActual, Object managedFieldValue) throws JsonProcessingException {
133+
var emptyMapValue = new HashMap<String, Object>();
134+
result.put(keyInActual, emptyMapValue);
135+
var actualMapValue = actualMap.get(keyInActual);
136+
log.debug("key: {} actual map value: {} managedFieldValue: {}", keyInActual,
137+
actualMapValue, managedFieldValue);
138+
139+
pruneActualAccordingManagedFields(emptyMapValue, (Map<String, Object>) actualMapValue,
140+
(Map<String, Object>) managedFields.get(key), objectMapper);
141+
}
142+
143+
private static boolean isNestedValue(Object managedFieldValue) {
144+
return managedFieldValue instanceof Map && (!((Map) managedFieldValue).isEmpty());
145+
}
146+
147+
// list entries referenced by key, or when "k:" prefix is used
148+
private void handleListKeyEntrySet(Map<String, Object> result, Map<String, Object> actualMap,
149+
ObjectMapper objectMapper, String keyInActual, Set<Map.Entry<String, Object>> managedEntrySet)
150+
throws JsonProcessingException {
151+
var valueList = new ArrayList<>();
152+
result.put(keyInActual, valueList);
153+
for (Map.Entry<String, Object> listEntry : managedEntrySet) {
154+
var actualListEntry = selectListEntryBasedOnKey(listEntry.getKey().substring(2),
155+
(List<Map<String, Object>>) actualMap.get(keyInActual), objectMapper);
156+
157+
if (listEntry.getValue() instanceof Map
158+
&& ((Map<String, Object>) listEntry.getValue()).isEmpty()) {
159+
var map = new HashMap<String, Object>();
160+
valueList.add(map);
161+
pruneActualAccordingManagedFields(map, actualListEntry,
162+
(Map<String, Object>) listEntry.getValue(), objectMapper);
163+
continue;
164+
}
165+
166+
if (DOT_KEY.equals(listEntry.getKey())) {
167+
continue;
168+
}
169+
var emptyResMapValue = new HashMap<String, Object>();
170+
valueList.add(emptyResMapValue);
171+
pruneActualAccordingManagedFields(emptyResMapValue, actualListEntry,
172+
(Map<String, Object>) listEntry.getValue(), objectMapper);
173+
}
174+
}
175+
176+
// set values, the "v:" prefix
177+
private static void handleSetValues(Map<String, Object> result, Map<String, Object> actualMap,
178+
ObjectMapper objectMapper, String keyInActual,
179+
Set<Map.Entry<String, Object>> managedEntrySet) {
180+
var valueList = new ArrayList<>();
181+
result.put(keyInActual, valueList);
182+
for (Map.Entry<String, Object> valueEntry : managedEntrySet) {
183+
// not clear if this can happen
184+
if (DOT_KEY.equals(valueEntry.getKey())) {
185+
continue;
186+
}
187+
188+
Class<?> targetClass = null;
189+
List values = (List) actualMap.get(keyInActual);
190+
if (!(values.get(0) instanceof Map)) {
191+
targetClass = values.get(0).getClass();
192+
}
193+
194+
var value =
195+
parseKeyValue(valueEntry.getKey().substring(2), targetClass, objectMapper);
196+
valueList.add(value);
197+
}
198+
}
199+
200+
public static Object parseKeyValue(String stringValue, Class<?> targetClass,
201+
ObjectMapper objectMapper) {
202+
try {
203+
stringValue = stringValue.trim();
204+
if (targetClass != null) {
205+
return objectMapper.readValue(stringValue, targetClass);
206+
} else {
207+
return objectMapper.readValue(stringValue, typeRef);
208+
}
209+
} catch (JsonProcessingException e) {
210+
throw new IllegalStateException(e);
211+
}
212+
}
213+
214+
private boolean isSetValueField(Set<Map.Entry<String, Object>> managedEntrySet) {
215+
var iterator = managedEntrySet.iterator();
216+
var managedFieldEntry = iterator.next();
217+
if (managedFieldEntry.getKey().equals(DOT_KEY)) {
218+
managedFieldEntry = iterator.next();
219+
}
220+
return managedFieldEntry.getKey().startsWith(V_PREFIX);
221+
}
222+
223+
private boolean isListKeyEntrySet(Set<Map.Entry<String, Object>> managedEntrySet) {
158224
var iterator = managedEntrySet.iterator();
159225
var managedFieldEntry = iterator.next();
160226
// todo unit test

0 commit comments

Comments
 (0)