...

Source file src/sigs.k8s.io/kustomize/api/hasher/hasher.go

Documentation: sigs.k8s.io/kustomize/api/hasher

     1  // Copyright 2019 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     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  // SortArrayAndComputeHash sorts a string array and
    16  // returns a hash for it
    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  // Copied from https://github.com/kubernetes/kubernetes
    27  // /blob/master/pkg/kubectl/util/hash/hash.go
    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  // hex256 returns the hex form of the sha256 of the argument.
    52  func hex256(data string) string {
    53  	return fmt.Sprintf("%x", sha256.Sum256([]byte(data)))
    54  }
    55  
    56  // Hasher computes the hash of an RNode.
    57  type Hasher struct{}
    58  
    59  // Hash returns a hash of the argument.
    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  			// data, binaryData and stringData are all maps
    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  // encodeConfigMap encodes a ConfigMap.
   107  // Data, Kind, and Name are taken into account.
   108  // BinaryData is included if it's not empty to avoid useless key in output.
   109  func encodeConfigMap(node *yaml.RNode) (string, error) {
   110  	// get fields
   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  	// json.Marshal sorts the keys in a stable order in the encoding
   126  	data, err := json.Marshal(m)
   127  	if err != nil {
   128  		return "", err
   129  	}
   130  	return string(data), nil
   131  }
   132  
   133  // encodeSecret encodes a Secret.
   134  // Data, Kind, Name, and Type are taken into account.
   135  // StringData is included if it's not empty to avoid useless key in output.
   136  func encodeSecret(node *yaml.RNode) (string, error) {
   137  	// get fields
   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  	// json.Marshal sorts the keys in a stable order in the encoding
   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