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