mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-09-15 12:18:57 +00:00
refactor hasher
This commit is contained in:
@@ -8,6 +8,8 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// SortArrayAndComputeHash sorts a string array and
|
||||
@@ -50,3 +52,141 @@ func Encode(hex string) (string, error) {
|
||||
func Hash(data string) string {
|
||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(data)))
|
||||
}
|
||||
|
||||
// HashRNode returns the hash value of input RNode
|
||||
func HashRNode(node *yaml.RNode) (string, error) {
|
||||
// get node kind
|
||||
kindNode, err := node.Pipe(yaml.FieldMatcher{Name: "kind"})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
kind := kindNode.YNode().Value
|
||||
|
||||
// calculate hash for different kinds
|
||||
switch kind {
|
||||
case "ConfigMap":
|
||||
return configMapHash(node)
|
||||
case "Secret":
|
||||
return secretHash(node)
|
||||
default:
|
||||
return defaultHash(node)
|
||||
}
|
||||
}
|
||||
|
||||
// configMapHash returns a hash of the ConfigMap.
|
||||
// The Data, Kind, and Name are taken into account.
|
||||
func configMapHash(node *yaml.RNode) (string, error) {
|
||||
encoded, err := encodeConfigMap(node)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
h, err := Encode(Hash(encoded))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// SecretHash returns a hash of the Secret.
|
||||
// The Data, Kind, Name, and Type are taken into account.
|
||||
func secretHash(node *yaml.RNode) (string, error) {
|
||||
encoded, err := encodeSecret(node)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
h, err := Encode(Hash(encoded))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// unstructuredHash creates a hash for an arbitrary type.
|
||||
// All fields of the object are taken into account when generating the hash.
|
||||
// This is a fallback for when a specalised hash for the type is unavailable.
|
||||
func defaultHash(node *yaml.RNode) (string, error) {
|
||||
encoded, err := json.Marshal(node.YNode())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
h, err := Encode(Hash(string(encoded)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func getNodeValues(node *yaml.RNode, paths []string) (map[string]interface{}, error) {
|
||||
values := make(map[string]interface{})
|
||||
for _, p := range paths {
|
||||
vn, err := node.Pipe(yaml.Lookup(p))
|
||||
if err != nil {
|
||||
return map[string]interface{}{}, err
|
||||
}
|
||||
if vn == nil {
|
||||
values[p] = ""
|
||||
continue
|
||||
}
|
||||
if vn.YNode().Kind != yaml.ScalarNode {
|
||||
vs, err := vn.MarshalJSON()
|
||||
if err != nil {
|
||||
return map[string]interface{}{}, err
|
||||
}
|
||||
// data, binaryData and stringData are all maps
|
||||
var v map[string]interface{}
|
||||
json.Unmarshal(vs, &v)
|
||||
values[p] = v
|
||||
} else {
|
||||
values[p] = vn.YNode().Value
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// encodeConfigMap encodes a ConfigMap.
|
||||
// Data, Kind, and Name are taken into account.
|
||||
// BinaryData is included if it's not empty to avoid useless key in output.
|
||||
func encodeConfigMap(node *yaml.RNode) (string, error) {
|
||||
// get fields
|
||||
paths := []string{"metadata/name", "data", "binaryData"}
|
||||
values, err := getNodeValues(node, paths)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
m := map[string]interface{}{"kind": "ConfigMap", "name": values["metadata/name"],
|
||||
"data": values["data"]}
|
||||
if _, ok := values["binaryData"].(map[string]interface{}); ok {
|
||||
m["binaryData"] = values["binaryData"]
|
||||
}
|
||||
|
||||
// json.Marshal sorts the keys in a stable order in the encoding
|
||||
data, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
// encodeSecret encodes a Secret.
|
||||
// Data, Kind, Name, and Type are taken into account.
|
||||
// StringData is included if it's not empty to avoid useless key in output.
|
||||
func encodeSecret(node *yaml.RNode) (string, error) {
|
||||
// get fields
|
||||
paths := []string{"type", "metadata/name", "data", "stringData"}
|
||||
values, err := getNodeValues(node, paths)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
m := map[string]interface{}{"kind": "Secret", "type": values["type"],
|
||||
"name": values["metadata/name"], "data": values["data"]}
|
||||
if _, ok := values["stringData"].(map[string]interface{}); ok {
|
||||
m["stringData"] = values["stringData"]
|
||||
}
|
||||
|
||||
// json.Marshal sorts the keys in a stable order in the encoding
|
||||
data, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user