-
Notifications
You must be signed in to change notification settings - Fork 5k
/
managed_fields.go
123 lines (111 loc) · 3.9 KB
/
managed_fields.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package managedfields
import (
"bytes"
"fmt"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
"sigs.k8s.io/structured-merge-diff/v4/typed"
)
// Normalize will compare the live and config states. If config mutates
// a field that belongs to one of the trustedManagers it will remove
// that field from both live and config objects and return the normalized
// objects in this order. This function won't modify the live and config
// parameters. If pt is nil, the normalization will use a deduced parseable
// type which means that lists and maps are manipulated atomically.
// It is a no-op if no trustedManagers is provided. It is also a no-op if
// live or config are nil.
func Normalize(live, config *unstructured.Unstructured, trustedManagers []string, pt *typed.ParseableType) (*unstructured.Unstructured, *unstructured.Unstructured, error) {
if len(trustedManagers) == 0 {
return nil, nil, nil
}
if live == nil || config == nil {
return nil, nil, nil
}
liveCopy := live.DeepCopy()
configCopy := config.DeepCopy()
results, err := newTypedResults(liveCopy, configCopy, pt)
if err != nil {
return nil, nil, fmt.Errorf("error building typed results: %s", err)
}
normalized := false
for _, mf := range live.GetManagedFields() {
if trustedManager(mf.Manager, trustedManagers) {
err := normalize(mf, results)
if err != nil {
return nil, nil, fmt.Errorf("error normalizing manager %s: %s", mf.Manager, err)
}
normalized = true
}
}
if !normalized {
return liveCopy, configCopy, nil
}
lvu := results.live.AsValue().Unstructured()
l, ok := lvu.(map[string]interface{})
if !ok {
return nil, nil, fmt.Errorf("error converting live typedValue: expected map got %T", lvu)
}
normLive := &unstructured.Unstructured{Object: l}
cvu := results.config.AsValue().Unstructured()
c, ok := cvu.(map[string]interface{})
if !ok {
return nil, nil, fmt.Errorf("error converting config typedValue: expected map got %T", cvu)
}
normConfig := &unstructured.Unstructured{Object: c}
return normLive, normConfig, nil
}
// normalize will check if the modified set has fields that are present
// in the managed fields entry. If so, it will remove the fields from
// the live and config objects so it is ignored in diffs.
func normalize(mf v1.ManagedFieldsEntry, tr *typedResults) error {
mfs := &fieldpath.Set{}
err := mfs.FromJSON(bytes.NewReader(mf.FieldsV1.Raw))
if err != nil {
return err
}
intersect := mfs.Intersection(tr.comparison.Modified)
if intersect.Empty() {
return nil
}
tr.live = tr.live.RemoveItems(intersect)
tr.config = tr.config.RemoveItems(intersect)
return nil
}
type typedResults struct {
live *typed.TypedValue
config *typed.TypedValue
comparison *typed.Comparison
}
// newTypedResults will convert live and config into a TypedValue using the given pt
// and compare them. Returns a typedResults with the coverted types and the comparison.
// If pt is nil, will use the DeducedParseableType.
func newTypedResults(live, config *unstructured.Unstructured, pt *typed.ParseableType) (*typedResults, error) {
typedLive, err := pt.FromUnstructured(live.Object)
if err != nil {
return nil, fmt.Errorf("error creating typedLive: %s", err)
}
typedConfig, err := pt.FromUnstructured(config.Object)
if err != nil {
return nil, fmt.Errorf("error creating typedConfig: %s", err)
}
comparison, err := typedLive.Compare(typedConfig)
if err != nil {
return nil, fmt.Errorf("error comparing typed resources: %s", err)
}
return &typedResults{
live: typedLive,
config: typedConfig,
comparison: comparison,
}, nil
}
// trustedManager will return true if trustedManagers contains curManager.
// Returns false otherwise.
func trustedManager(curManager string, trustedManagers []string) bool {
for _, m := range trustedManagers {
if m == curManager {
return true
}
}
return false
}