Skip to content

Commit ac815fd

Browse files
committed
machinesetsync: refactor to a generalized differ which can work independent of types
1 parent 4a81309 commit ac815fd

File tree

3 files changed

+118
-33
lines changed

3 files changed

+118
-33
lines changed

pkg/controllers/machinesetsync/machineset_sync_controller.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -787,7 +787,10 @@ func (r *MachineSetSyncReconciler) createOrUpdateCAPIMachineSet(ctx context.Cont
787787
// If there is an existing CAPI machine set, work out if it needs updating.
788788

789789
// Compare the existing CAPI machine set with the converted CAPI machine set to check for changes.
790-
capiMachineSetsDiff := compareCAPIMachineSets(existingCAPIMachineSet, convertedCAPIMachineSet)
790+
capiMachineSetsDiff, err := compareCAPIMachineSets(existingCAPIMachineSet, convertedCAPIMachineSet)
791+
if err != nil {
792+
return fmt.Errorf("failed to compare CAPI machine sets: %w", err)
793+
}
791794

792795
// Make a deep copy of the converted CAPI machine set to avoid modifying the original.
793796
updatedOrCreatedCAPIMachineSet := convertedCAPIMachineSet.DeepCopy()
@@ -1397,7 +1400,7 @@ func compareCAPIInfraMachineTemplates(platform configv1.PlatformType, infraMachi
13971400
}
13981401

13991402
// compareCAPIMachineSets compares CAPI machineSets a and b, and returns a list of differences, or none if there are none.
1400-
func compareCAPIMachineSets(capiMachineSet1, capiMachineSet2 *clusterv1.MachineSet) map[string]any {
1403+
func compareCAPIMachineSets(capiMachineSet1, capiMachineSet2 *clusterv1.MachineSet) (map[string]any, error) {
14011404
diff := make(map[string]any)
14021405

14031406
if diffSpec := deep.Equal(capiMachineSet1.Spec, capiMachineSet2.Spec); len(diffSpec) > 0 {
@@ -1408,11 +1411,13 @@ func compareCAPIMachineSets(capiMachineSet1, capiMachineSet2 *clusterv1.MachineS
14081411
diff[".metadata"] = diffObjectMeta
14091412
}
14101413

1411-
if diffStatus := util.CAPIMachineSetStatusEqual(capiMachineSet1.Status, capiMachineSet2.Status); len(diffStatus) > 0 {
1414+
if diffStatus, err := util.CAPIMachineSetStatusEqual(capiMachineSet1.Status, capiMachineSet2.Status); err != nil {
1415+
return nil, fmt.Errorf("failed to compare CAPI machine set status: %w", err)
1416+
} else if len(diffStatus) > 0 {
14121417
diff[".status"] = diffStatus
14131418
}
14141419

1415-
return diff
1420+
return diff, nil
14161421
}
14171422

14181423
// compareMAPIMachineSets compares MAPI machineSets a and b, and returns a list of differences, or none if there are none.

pkg/util/diff.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
Copyright 2025 Red Hat, Inc.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
package util
17+
18+
import (
19+
"fmt"
20+
21+
"github.com/go-test/deep"
22+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
23+
"k8s.io/apimachinery/pkg/runtime"
24+
)
25+
26+
type UnstructuredDiffer[T any] struct {
27+
customDiff []func(a, b T) ([]string, error)
28+
ignoreFields [][]string
29+
}
30+
31+
func (d *UnstructuredDiffer[T]) Diff(a, b T) (map[string]any, error) {
32+
diffs := map[string]any{}
33+
34+
for i, customDiff := range d.customDiff {
35+
diff, err := customDiff(a, b)
36+
if err != nil {
37+
return nil, err
38+
}
39+
40+
if len(diff) > 0 {
41+
diffs[fmt.Sprintf("customDiff.%d", i)] = diff
42+
}
43+
}
44+
45+
unstructuredA, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&a)
46+
if err != nil {
47+
return nil, fmt.Errorf("failed to convert b to unstructured: %w", err)
48+
}
49+
50+
unstructuredB, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&b)
51+
if err != nil {
52+
return nil, fmt.Errorf("failed to convert b to unstructured: %w", err)
53+
}
54+
55+
for _, nestedField := range d.ignoreFields {
56+
unstructured.RemoveNestedField(unstructuredA, nestedField...)
57+
unstructured.RemoveNestedField(unstructuredB, nestedField...)
58+
}
59+
60+
diff := deep.Equal(unstructuredA, unstructuredB)
61+
if len(diff) > 0 {
62+
diffs["deep.Equal"] = diff
63+
}
64+
65+
return diffs, nil
66+
}
67+
68+
type diffopts[T any] func(*UnstructuredDiffer[T])
69+
70+
func NewUnstructuredDiffer[T any](opts ...diffopts[T]) *UnstructuredDiffer[T] {
71+
d := &UnstructuredDiffer[T]{}
72+
for _, opt := range opts {
73+
opt(d)
74+
}
75+
76+
return d
77+
}
78+
79+
func WithIgnoreField[T any](path ...string) diffopts[T] {
80+
return func(d *UnstructuredDiffer[T]) {
81+
d.ignoreFields = append(d.ignoreFields, path)
82+
}
83+
}
84+
85+
func WithCustomDiff[T any](diff func(a, b T) ([]string, error)) diffopts[T] {
86+
return func(d *UnstructuredDiffer[T]) {
87+
d.customDiff = append(d.customDiff, diff)
88+
}
89+
}

pkg/util/sync.go

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -81,34 +81,25 @@ func ObjectMetaEqual(a, b metav1.ObjectMeta) map[string]any {
8181
// CAPIMachineSetStatusEqual compares variables a and b,
8282
// and returns a list of differences, or nil if there are none,
8383
// for the fields we care about when synchronising MAPI and CAPI Machines.
84-
func CAPIMachineSetStatusEqual(a, b clusterv1.MachineSetStatus) map[string]any { //nolint:dupl
85-
diff := map[string]any{}
86-
87-
if diffConditions := compareCAPIMachineSetConditions(a.Conditions, b.Conditions); len(diffConditions) > 0 {
88-
diff[".conditions"] = diffConditions
89-
}
90-
91-
if diffReadyReplicas := deep.Equal(a.ReadyReplicas, b.ReadyReplicas); len(diffReadyReplicas) > 0 {
92-
diff[".readyReplicas"] = diffReadyReplicas
93-
}
94-
95-
if diffAvailableReplicas := deep.Equal(a.AvailableReplicas, b.AvailableReplicas); len(diffAvailableReplicas) > 0 {
96-
diff[".availableReplicas"] = diffAvailableReplicas
97-
}
98-
99-
if diffFullyLabeledReplicas := deep.Equal(a.FullyLabeledReplicas, b.FullyLabeledReplicas); len(diffFullyLabeledReplicas) > 0 {
100-
diff[".fullyLabeledReplicas"] = diffFullyLabeledReplicas
101-
}
102-
103-
if diffFailureReason := deep.Equal(a.FailureReason, b.FailureReason); len(diffFailureReason) > 0 {
104-
diff[".failureReason"] = diffFailureReason
105-
}
106-
107-
if diffFailureMessage := deep.Equal(a.FailureMessage, b.FailureMessage); len(diffFailureMessage) > 0 {
108-
diff[".failureMessage"] = diffFailureMessage
109-
}
110-
111-
return diff
84+
func CAPIMachineSetStatusEqual(a, b clusterv1.MachineSetStatus) (map[string]any, error) {
85+
// Maybe we can also have it this way:
86+
// differ := NewUnstructuredDiffer(
87+
// WithIgnoreField[clusterv1.MachineSetStatus]("conditions"),
88+
// WithCustomDiff(func(a, b clusterv1.MachineSetStatus) ([]string, error) {
89+
// return compareCAPIMachineSetConditions(a.Conditions, b.Conditions), nil
90+
// }),
91+
// )
92+
93+
differ := UnstructuredDiffer[clusterv1.MachineSetStatus]{
94+
customDiff: []func(a clusterv1.MachineSetStatus, b clusterv1.MachineSetStatus) ([]string, error){
95+
func(a, b clusterv1.MachineSetStatus) ([]string, error) {
96+
return compareCAPIMachineSetConditions(a.Conditions, b.Conditions), nil
97+
},
98+
},
99+
ignoreFields: [][]string{{"conditions"}},
100+
}
101+
102+
return differ.Diff(a, b)
112103
}
113104

114105
func compareCAPIMachineSetConditions(a, b []clusterv1.Condition) []string {
@@ -158,7 +149,7 @@ func compareMAPIMachineSetConditions(a, b []mapiv1beta1.Condition) []string {
158149
// MAPIMachineSetStatusEqual compares variables a and b,
159150
// and returns a list of differences, or nil if there are none,
160151
// for the fields we care about when synchronising MAPI and CAPI Machines.
161-
func MAPIMachineSetStatusEqual(a, b mapiv1beta1.MachineSetStatus) map[string]any { //nolint:dupl
152+
func MAPIMachineSetStatusEqual(a, b mapiv1beta1.MachineSetStatus) map[string]any {
162153
diff := map[string]any{}
163154

164155
if diffConditions := compareMAPIMachineSetConditions(a.Conditions, b.Conditions); len(diffConditions) > 0 {

0 commit comments

Comments
 (0)