/
match.go
78 lines (67 loc) · 2.08 KB
/
match.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
package openapi
import (
"bytes"
"encoding/json"
"fmt"
jsonpatch "github.com/evanphx/json-patch"
"k8s.io/client-go/util/jsonpath"
)
// PropertiesChanged compares two versions of an object to see if any path specified in `paths` has
// been changed. Paths are specified as JSONPaths, e.g., `.spec.accessModes` refers to `{spec:
// {accessModes: {}}}`.
func PropertiesChanged(oldObj, newObj map[string]interface{}, paths []string) ([]string, error) {
patch, err := mergePatchObj(oldObj, newObj)
if err != nil {
return nil, err
}
return PatchPropertiesChanged(patch, paths)
}
// PatchPropertiesChanged scrapes the given patch object to see if any path specified in `paths` has
// been changed. Paths are specified as JSONPaths, e.g., `.spec.accessModes` refers to `{spec:
// {accessModes: {}}}`.
func PatchPropertiesChanged(patch map[string]interface{}, paths []string) ([]string, error) {
j := jsonpath.New("")
matches := []string{}
for _, path := range paths {
j.AllowMissingKeys(true)
err := j.Parse(fmt.Sprintf("{%s}", path))
if err != nil {
return nil, err
}
buf := new(bytes.Buffer)
err = j.Execute(buf, patch)
if err != nil {
continue
}
if len(buf.String()) > 0 {
matches = append(matches, path)
}
}
return matches, nil
}
// mergePatchObj takes a two objects and returns an object that is the union of all
// fields that were changed (e.g., were deleted, were added, and so on) between the two.
//
// For example, say we have {a: 1, c:3} and {a:1, b:2}. This function would then return {b:2, c:3}.
//
// This is useful so that we can (e.g.) use jsonpath to see which fields were altered.
func mergePatchObj(oldObj, newObj map[string]interface{}) (map[string]interface{}, error) {
oldJSON, err := json.Marshal(oldObj)
if err != nil {
return nil, err
}
newJSON, err := json.Marshal(newObj)
if err != nil {
return nil, err
}
patchBytes, err := jsonpatch.CreateMergePatch(oldJSON, newJSON)
if err != nil {
return nil, err
}
patch := map[string]interface{}{}
err = json.Unmarshal(patchBytes, &patch)
if err != nil {
return nil, err
}
return patch, nil
}