Skip to content

Commit d0f1be5

Browse files
bachmanity1metacosm
authored andcommitted
feat: improve diff logging (#2544)
* imporve diff logging Signed-off-by: bachmanity1 <[email protected]> * compute diff only when actual doesn't match desired Signed-off-by: bachmanity1 <[email protected]> * slight improvements Signed-off-by: bachmanity1 <[email protected]> * increase context size Signed-off-by: bachmanity1 <[email protected]> * fix style Signed-off-by: bachmanity1 <[email protected]> * calculate diff only if debug is enabled Signed-off-by: bachmanity1 <[email protected]> * print actual resources when trace is enabled Signed-off-by: bachmanity1 <[email protected]> * use java-diff-utils Signed-off-by: bachmanity1 <[email protected]> * add unit tests --------- Signed-off-by: bachmanity1 <[email protected]>
1 parent 9b093b6 commit d0f1be5

File tree

4 files changed

+115
-8
lines changed

4 files changed

+115
-8
lines changed

Diff for: operator-framework-core/pom.xml

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
<description>Core framework for implementing Kubernetes operators</description>
1515

1616
<dependencies>
17+
<dependency>
18+
<groupId>io.github.java-diff-utils</groupId>
19+
<artifactId>java-diff-utils</artifactId>
20+
</dependency>
1721
<dependency>
1822
<groupId>io.fabric8</groupId>
1923
<artifactId>kubernetes-client</artifactId>

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

+58-8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.ArrayList;
55
import java.util.Collections;
66
import java.util.HashMap;
7+
import java.util.LinkedHashMap;
78
import java.util.List;
89
import java.util.Map;
910
import java.util.Map.Entry;
@@ -33,6 +34,9 @@
3334

3435
import static io.javaoperatorsdk.operator.processing.dependent.kubernetes.ResourceRequirementsSanitizer.sanitizeResourceRequirements;
3536

37+
import com.github.difflib.DiffUtils;
38+
import com.github.difflib.UnifiedDiffUtils;
39+
3640
/**
3741
* Matches the actual state on the server vs the desired state. Based on the managedFields of SSA.
3842
* <p>
@@ -107,20 +111,66 @@ public boolean matches(R actual, R desired, Context<?> context) {
107111

108112
removeIrrelevantValues(desiredMap);
109113

110-
if (LoggingUtils.isNotSensitiveResource(desired)) {
111-
logDiff(prunedActual, desiredMap, objectMapper);
114+
var matches = prunedActual.equals(desiredMap);
115+
116+
if (!matches && log.isDebugEnabled() && LoggingUtils.isNotSensitiveResource(desired)) {
117+
var diff = getDiff(prunedActual, desiredMap, objectMapper);
118+
log.debug(
119+
"Diff between actual and desired state for resource: {} with name: {} in namespace: {} is: \n{}",
120+
actual.getKind(), actual.getMetadata().getName(), actual.getMetadata().getNamespace(),
121+
diff);
112122
}
113123

114-
return prunedActual.equals(desiredMap);
124+
return matches;
115125
}
116126

117-
private void logDiff(Map<String, Object> prunedActualMap, Map<String, Object> desiredMap,
127+
private String getDiff(Map<String, Object> prunedActualMap, Map<String, Object> desiredMap,
118128
KubernetesSerialization serialization) {
119-
if (log.isDebugEnabled()) {
120-
var actualYaml = serialization.asYaml(prunedActualMap);
121-
var desiredYaml = serialization.asYaml(desiredMap);
122-
log.debug("Pruned actual yaml: \n {} \n desired yaml: \n {} ", actualYaml, desiredYaml);
129+
var actualYaml = serialization.asYaml(sortMap(prunedActualMap));
130+
var desiredYaml = serialization.asYaml(sortMap(desiredMap));
131+
if (log.isTraceEnabled()) {
132+
log.trace("Pruned actual resource: \n {} \ndesired resource: \n {} ", actualYaml,
133+
desiredYaml);
134+
}
135+
136+
var patch = DiffUtils.diff(actualYaml.lines().toList(), desiredYaml.lines().toList());
137+
List<String> unifiedDiff =
138+
UnifiedDiffUtils.generateUnifiedDiff("", "", actualYaml.lines().toList(), patch, 1);
139+
return String.join("\n", unifiedDiff);
140+
}
141+
142+
@SuppressWarnings("unchecked")
143+
Map<String, Object> sortMap(Map<String, Object> map) {
144+
List<String> sortedKeys = new ArrayList<>(map.keySet());
145+
Collections.sort(sortedKeys);
146+
147+
Map<String, Object> sortedMap = new LinkedHashMap<>();
148+
for (String key : sortedKeys) {
149+
Object value = map.get(key);
150+
if (value instanceof Map) {
151+
sortedMap.put(key, sortMap((Map<String, Object>) value));
152+
} else if (value instanceof List) {
153+
sortedMap.put(key, sortListItems((List<Object>) value));
154+
} else {
155+
sortedMap.put(key, value);
156+
}
157+
}
158+
return sortedMap;
159+
}
160+
161+
@SuppressWarnings("unchecked")
162+
List<Object> sortListItems(List<Object> list) {
163+
List<Object> sortedList = new ArrayList<>();
164+
for (Object item : list) {
165+
if (item instanceof Map) {
166+
sortedList.add(sortMap((Map<String, Object>) item));
167+
} else if (item instanceof List) {
168+
sortedList.add(sortListItems((List<Object>) item));
169+
} else {
170+
sortedList.add(item);
171+
}
123172
}
173+
return sortedList;
124174
}
125175

126176
/**

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

+47
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package io.javaoperatorsdk.operator.processing.dependent.kubernetes;
22

3+
import java.util.Arrays;
4+
import java.util.HashMap;
5+
import java.util.List;
36
import java.util.Map;
47

58
import org.junit.jupiter.api.BeforeEach;
@@ -206,4 +209,48 @@ private static <R> R loadResource(String fileName, Class<R> clazz) {
206209
return ReconcilerUtils.loadYaml(clazz, SSABasedGenericKubernetesResourceMatcherTest.class,
207210
fileName);
208211
}
212+
213+
@Test
214+
@SuppressWarnings("unchecked")
215+
void sortListItemsTest() {
216+
Map<String, Object> nestedMap1 = new HashMap<>();
217+
nestedMap1.put("z", 26);
218+
nestedMap1.put("y", 25);
219+
220+
Map<String, Object> nestedMap2 = new HashMap<>();
221+
nestedMap2.put("b", 26);
222+
nestedMap2.put("c", 25);
223+
nestedMap2.put("a", 24);
224+
225+
List<Object> unsortedListItems = Arrays.asList(1, nestedMap1, nestedMap2);
226+
List<Object> sortedListItems = matcher.sortListItems(unsortedListItems);
227+
228+
assertThat(sortedListItems).element(0).isEqualTo(1);
229+
230+
Map<String, Object> sortedNestedMap1 = (Map<String, Object>) sortedListItems.get(1);
231+
assertThat(sortedNestedMap1.keySet()).containsExactly("y", "z");
232+
233+
Map<String, Object> sortedNestedMap2 = (Map<String, Object>) sortedListItems.get(2);
234+
assertThat(sortedNestedMap2.keySet()).containsExactly("a", "b", "c");
235+
}
236+
237+
@Test
238+
@SuppressWarnings("unchecked")
239+
void testSortMapWithNestedMap() {
240+
Map<String, Object> nestedMap = new HashMap<>();
241+
nestedMap.put("z", 26);
242+
nestedMap.put("y", 25);
243+
244+
Map<String, Object> unsortedMap = new HashMap<>();
245+
unsortedMap.put("b", nestedMap);
246+
unsortedMap.put("a", 1);
247+
unsortedMap.put("c", 2);
248+
249+
Map<String, Object> sortedMap = matcher.sortMap(unsortedMap);
250+
251+
assertThat(sortedMap.keySet()).containsExactly("a", "b", "c");
252+
253+
Map<String, Object> sortedNestedMap = (Map<String, Object>) sortedMap.get("b");
254+
assertThat(sortedNestedMap.keySet()).containsExactly("y", "z");
255+
}
209256
}

Diff for: pom.xml

+6
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
<caffeine.version>3.1.8</caffeine.version>
7676
<mustache.version>0.9.11</mustache.version>
7777
<commons.io.version>2.16.1</commons.io.version>
78+
<java.diff.version>4.12</java.diff.version>
7879

7980
<fmt-maven-plugin.version>2.11</fmt-maven-plugin.version>
8081
<maven-compiler-plugin.version>3.12.1</maven-compiler-plugin.version>
@@ -161,6 +162,11 @@
161162
<version>${mokito.version}</version>
162163
</dependency>
163164

165+
<dependency>
166+
<groupId>io.github.java-diff-utils</groupId>
167+
<artifactId>java-diff-utils</artifactId>
168+
<version>${java.diff.version}</version>
169+
</dependency>
164170
<dependency>
165171
<groupId>org.slf4j</groupId>
166172
<artifactId>slf4j-api</artifactId>

0 commit comments

Comments
 (0)