@@ -17,6 +17,8 @@ limitations under the License.
17
17
package ssa
18
18
19
19
import (
20
+ "reflect"
21
+
20
22
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
21
23
22
24
"sigs.k8s.io/cluster-api/internal/contract"
@@ -32,26 +34,43 @@ type FilterObjectInput struct {
32
34
// spec.ControlPlaneEndpoint.
33
35
// NOTE: ignore paths which point to an array are not supported by the current implementation.
34
36
IgnorePaths []contract.Path
37
+
38
+ // DropEmptyStruct instruct the Helper to drop all fields with values equals to empty struct.
39
+ // NOTE: This is required when using typed objects, because the DefaultUnstructuredConverter does
40
+ // not handle omitzero (yet).
41
+ DropEmptyStruct bool
35
42
}
36
43
37
44
// FilterObject filter out changes not relevant for the controller.
38
45
func FilterObject (obj * unstructured.Unstructured , input * FilterObjectInput ) {
39
46
// filter out changes not in the allowed paths (fields to not consider, e.g. status);
47
+ // also drop empty struct if required.
40
48
if len (input .AllowedPaths ) > 0 {
41
49
FilterIntent (& FilterIntentInput {
42
- Path : contract.Path {},
43
- Value : obj .Object ,
44
- ShouldFilter : IsPathNotAllowed (input .AllowedPaths ),
50
+ Path : contract.Path {},
51
+ Value : obj .Object ,
52
+ ShouldFilter : IsPathNotAllowed (input .AllowedPaths ),
53
+ DropEmptyStruct : input .DropEmptyStruct ,
45
54
})
46
55
}
47
56
48
57
// filter out changes for ignore paths (well known fields owned by other controllers, e.g.
49
- // spec.controlPlaneEndpoint in the InfrastructureCluster object);
58
+ // spec.controlPlaneEndpoint in the InfrastructureCluster object); also drop empty struct if required.
50
59
if len (input .IgnorePaths ) > 0 {
51
60
FilterIntent (& FilterIntentInput {
52
- Path : contract.Path {},
53
- Value : obj .Object ,
54
- ShouldFilter : IsPathIgnored (input .IgnorePaths ),
61
+ Path : contract.Path {},
62
+ Value : obj .Object ,
63
+ ShouldFilter : IsPathIgnored (input .IgnorePaths ),
64
+ DropEmptyStruct : input .DropEmptyStruct ,
65
+ })
66
+ }
67
+
68
+ // DropEmptyStruct if not already done above.
69
+ if input .DropEmptyStruct && len (input .AllowedPaths ) == 0 && len (input .IgnorePaths ) == 0 {
70
+ FilterIntent (& FilterIntentInput {
71
+ Path : contract.Path {},
72
+ Value : obj .Object ,
73
+ DropEmptyStruct : input .DropEmptyStruct ,
55
74
})
56
75
}
57
76
}
@@ -76,11 +95,19 @@ func FilterIntent(ctx *FilterIntentInput) bool {
76
95
// Gets the original and the modified Value for the field.
77
96
Value : value [field ],
78
97
// Carry over global values from the context.
79
- ShouldFilter : ctx .ShouldFilter ,
98
+ ShouldFilter : ctx .ShouldFilter ,
99
+ DropEmptyStruct : ctx .DropEmptyStruct ,
80
100
}
81
101
82
102
// If the field should be filtered out, delete it from the modified object.
83
- if fieldCtx .ShouldFilter (fieldCtx .Path ) {
103
+ if fieldCtx .ShouldFilter != nil && fieldCtx .ShouldFilter (fieldCtx .Path ) {
104
+ delete (value , field )
105
+ gotDeletions = true
106
+ continue
107
+ }
108
+
109
+ // If empty struct should be dropped and the value is a empty struct, delete it from the modified object.
110
+ if fieldCtx .DropEmptyStruct && reflect .DeepEqual (fieldCtx .Value , map [string ]interface {}{}) {
84
111
delete (value , field )
85
112
gotDeletions = true
86
113
continue
@@ -109,6 +136,8 @@ type FilterIntentInput struct {
109
136
110
137
// ShouldFilter handle the func that determine if the current Path should be dropped or not.
111
138
ShouldFilter func (path contract.Path ) bool
139
+
140
+ DropEmptyStruct bool
112
141
}
113
142
114
143
// IsPathAllowed returns true when the Path is one of the AllowedPaths.
0 commit comments