github.com/GoogleContainerTools/kpt@v1.0.0-beta.50.0.20240520170205-c25345ffcbee/commands/alpha/live/plan/diff.go (about) 1 // Copyright 2022 The kpt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package plan 16 17 import ( 18 "reflect" 19 "strconv" 20 21 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 22 ) 23 24 type Diff struct { 25 Type string 26 Left interface{} 27 Right interface{} 28 Path string 29 } 30 31 func diffObjects(b, a *unstructured.Unstructured) ([]Diff, error) { 32 diffs, err := diffMaps("", b.Object, a.Object) 33 if err != nil { 34 return nil, err 35 } 36 return diffs, nil 37 } 38 39 func diffMaps(prefix string, l, r map[string]interface{}) ([]Diff, error) { 40 var diffs []Diff 41 for k, lv := range l { 42 childPrefix := prefix + "." + k 43 44 rv, ok := r[k] 45 if !ok { 46 diffs = append(diffs, Diff{Type: "LeftAdd", Path: childPrefix, Left: lv, Right: rv}) 47 continue 48 } 49 childDiffs, err := diffValue(childPrefix, lv, rv) 50 if err != nil { 51 return nil, err 52 } 53 diffs = append(diffs, childDiffs...) 54 } 55 56 for k, rv := range r { 57 childPrefix := prefix + "." + k 58 59 lv, ok := l[k] 60 if !ok { 61 diffs = append(diffs, Diff{Type: "RightAdd", Path: childPrefix, Left: lv, Right: rv}) 62 continue 63 } 64 } 65 66 return diffs, nil 67 } 68 69 func diffSlices(prefix string, l, r []interface{}) ([]Diff, error) { 70 var diffs []Diff 71 for i, lv := range l { 72 childPrefix := prefix + "." + strconv.Itoa(i) 73 74 if len(r) <= i { 75 diffs = append(diffs, Diff{Type: "LeftAdd", Path: childPrefix, Left: l[i], Right: nil}) 76 continue 77 } 78 rv := r[i] 79 childDiffs, err := diffValue(childPrefix, lv, rv) 80 if err != nil { 81 return nil, err 82 } 83 diffs = append(diffs, childDiffs...) 84 } 85 86 for i, rv := range r { 87 childPrefix := prefix + "." + strconv.Itoa(i) 88 89 if len(l) <= i { 90 diffs = append(diffs, Diff{Type: "RightAdd", Path: childPrefix, Left: nil, Right: rv}) 91 continue 92 } 93 } 94 95 return diffs, nil 96 } 97 98 func diffValue(path string, lv, rv interface{}) ([]Diff, error) { 99 switch lv := lv.(type) { 100 // case string: 101 // rvString, ok := rv.(string) 102 // if !ok || lv != rvString { 103 // diffs = append(diffs, Diff{Type: "Change", Path: childPrefix, Left: lv, Right: rv}) 104 // } 105 // case int64: 106 // rvInt64, ok := rv.(int64) 107 // if !ok || lv != rvInt64 { 108 // diffs = append(diffs, Diff{Type: "Change", Path: childPrefix, Left: lv, Right: rv}) 109 // } 110 case map[string]interface{}: 111 rvMap, ok := rv.(map[string]interface{}) 112 if !ok { 113 return []Diff{{Type: "Change", Path: path, Left: lv, Right: rv}}, nil 114 } 115 return diffMaps(path, lv, rvMap) 116 117 case []interface{}: 118 rvSlice, ok := rv.([]interface{}) 119 if !ok { 120 return []Diff{{Type: "Change", Path: path, Left: lv, Right: rv}}, nil 121 } 122 return diffSlices(path, lv, rvSlice) 123 124 default: 125 if !reflect.DeepEqual(lv, rv) { 126 return []Diff{{Type: "Change", Path: path, Left: lv, Right: rv}}, nil 127 } 128 return nil, nil 129 } 130 }