1
2
3
4 package hasher
5
6 import (
7 "crypto/sha256"
8 "encoding/json"
9 "fmt"
10 "sort"
11
12 "sigs.k8s.io/kustomize/kyaml/yaml"
13 )
14
15
16
17 func SortArrayAndComputeHash(s []string) (string, error) {
18 sort.Strings(s)
19 data, err := json.Marshal(s)
20 if err != nil {
21 return "", err
22 }
23 return encode(hex256(string(data)))
24 }
25
26
27
28 func encode(hex string) (string, error) {
29 if len(hex) < 10 {
30 return "", fmt.Errorf(
31 "input length must be at least 10")
32 }
33 enc := []rune(hex[:10])
34 for i := range enc {
35 switch enc[i] {
36 case '0':
37 enc[i] = 'g'
38 case '1':
39 enc[i] = 'h'
40 case '3':
41 enc[i] = 'k'
42 case 'a':
43 enc[i] = 'm'
44 case 'e':
45 enc[i] = 't'
46 }
47 }
48 return string(enc), nil
49 }
50
51
52 func hex256(data string) string {
53 return fmt.Sprintf("%x", sha256.Sum256([]byte(data)))
54 }
55
56
57 type Hasher struct{}
58
59
60 func (h *Hasher) Hash(node *yaml.RNode) (r string, err error) {
61 var encoded string
62 switch node.GetKind() {
63 case "ConfigMap":
64 encoded, err = encodeConfigMap(node)
65 case "Secret":
66 encoded, err = encodeSecret(node)
67 default:
68 var encodedBytes []byte
69 encodedBytes, err = json.Marshal(node.YNode())
70 encoded = string(encodedBytes)
71 }
72 if err != nil {
73 return "", err
74 }
75 return encode(hex256(encoded))
76 }
77
78 func getNodeValues(
79 node *yaml.RNode, paths []string) (map[string]interface{}, error) {
80 values := make(map[string]interface{})
81 for _, p := range paths {
82 vn, err := node.Pipe(yaml.Lookup(p))
83 if err != nil {
84 return map[string]interface{}{}, err
85 }
86 if vn == nil {
87 values[p] = ""
88 continue
89 }
90 if vn.YNode().Kind != yaml.ScalarNode {
91 vs, err := vn.MarshalJSON()
92 if err != nil {
93 return map[string]interface{}{}, err
94 }
95
96 var v map[string]interface{}
97 json.Unmarshal(vs, &v)
98 values[p] = v
99 } else {
100 values[p] = vn.YNode().Value
101 }
102 }
103 return values, nil
104 }
105
106
107
108
109 func encodeConfigMap(node *yaml.RNode) (string, error) {
110
111 paths := []string{"metadata/name", "data", "binaryData"}
112 values, err := getNodeValues(node, paths)
113 if err != nil {
114 return "", err
115 }
116 m := map[string]interface{}{
117 "kind": "ConfigMap",
118 "name": values["metadata/name"],
119 "data": values["data"],
120 }
121 if _, ok := values["binaryData"].(map[string]interface{}); ok {
122 m["binaryData"] = values["binaryData"]
123 }
124
125
126 data, err := json.Marshal(m)
127 if err != nil {
128 return "", err
129 }
130 return string(data), nil
131 }
132
133
134
135
136 func encodeSecret(node *yaml.RNode) (string, error) {
137
138 paths := []string{"type", "metadata/name", "data", "stringData"}
139 values, err := getNodeValues(node, paths)
140 if err != nil {
141 return "", err
142 }
143 m := map[string]interface{}{"kind": "Secret", "type": values["type"],
144 "name": values["metadata/name"], "data": values["data"]}
145 if _, ok := values["stringData"].(map[string]interface{}); ok {
146 m["stringData"] = values["stringData"]
147 }
148
149
150 data, err := json.Marshal(m)
151 if err != nil {
152 return "", err
153 }
154 return string(data), nil
155 }
156
View as plain text