mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-05-17 18:25:26 +00:00
Move hashing code out of k8sdeps.
This commit is contained in:
@@ -4,13 +4,12 @@
|
||||
package hash
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"sigs.k8s.io/kustomize/pkg/hasher"
|
||||
)
|
||||
|
||||
// kustHash computes a hash of an unstructured object.
|
||||
@@ -54,7 +53,7 @@ func configMapHash(cm *v1.ConfigMap) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
h, err := encodeHash(hash(encoded))
|
||||
h, err := hasher.Encode(hasher.Hash(encoded))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -68,22 +67,7 @@ func secretHash(sec *v1.Secret) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
h, err := encodeHash(hash(encoded))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// SortArrayAndComputeHash sorts a string array and
|
||||
// returns a hash for it
|
||||
func SortArrayAndComputeHash(s []string) (string, error) {
|
||||
sort.Strings(s)
|
||||
data, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
h, err := encodeHash(hash(string(data)))
|
||||
h, err := hasher.Encode(hasher.Hash(encoded))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -116,41 +100,6 @@ func encodeSecret(sec *v1.Secret) (string, error) {
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
// encodeHash extracts the first 40 bits of the hash from the hex string
|
||||
// (1 hex char represents 4 bits), and then maps vowels and vowel-like hex
|
||||
// characters to consonants to prevent bad words from being formed (the theory
|
||||
// is that no vowels makes it really hard to make bad words). Since the string
|
||||
// is hex, the only vowels it can contain are 'a' and 'e'.
|
||||
// We picked some arbitrary consonants to map to from the same character set as GenerateName.
|
||||
// See: https://github.com/kubernetes/apimachinery/blob/dc1f89aff9a7509782bde3b68824c8043a3e58cc/pkg/util/rand/rand.go#L75
|
||||
// If the hex string contains fewer than ten characters, returns an error.
|
||||
func encodeHash(hex string) (string, error) {
|
||||
if len(hex) < 10 {
|
||||
return "", fmt.Errorf("the hex string must contain at least 10 characters")
|
||||
}
|
||||
enc := []rune(hex[:10])
|
||||
for i := range enc {
|
||||
switch enc[i] {
|
||||
case '0':
|
||||
enc[i] = 'g'
|
||||
case '1':
|
||||
enc[i] = 'h'
|
||||
case '3':
|
||||
enc[i] = 'k'
|
||||
case 'a':
|
||||
enc[i] = 'm'
|
||||
case 'e':
|
||||
enc[i] = 't'
|
||||
}
|
||||
}
|
||||
return string(enc), nil
|
||||
}
|
||||
|
||||
// hash hashes `data` with sha256 and returns the hex string
|
||||
func hash(data string) string {
|
||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(data)))
|
||||
}
|
||||
|
||||
func unstructuredToConfigmap(u unstructured.Unstructured) (*v1.ConfigMap, error) {
|
||||
marshaled, err := json.Marshal(u.Object)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package hash
|
||||
|
||||
@@ -90,28 +77,6 @@ func TestSecretHash(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestArrayHash(t *testing.T) {
|
||||
array1 := []string{"a", "b", "c"}
|
||||
array2 := []string{"c", "b", "a"}
|
||||
h1, err := SortArrayAndComputeHash(array1)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
if h1 == "" {
|
||||
t.Errorf("failed to hash %v", array1)
|
||||
}
|
||||
h2, err := SortArrayAndComputeHash(array2)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
if h2 == "" {
|
||||
t.Errorf("failed to hash %v", array2)
|
||||
}
|
||||
if h1 != h2 {
|
||||
t.Errorf("hash is not consistent with reordered list: %s %s", h1, h2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeConfigMap(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
@@ -178,15 +143,6 @@ func TestEncodeSecret(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHash(t *testing.T) {
|
||||
// hash the empty string to be sure that sha256 is being used
|
||||
expect := "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
sum := hash("")
|
||||
if expect != sum {
|
||||
t.Errorf("expected hash %q but got %q", expect, sum)
|
||||
}
|
||||
}
|
||||
|
||||
// warn devs who change types that they might have to update a hash function
|
||||
// not perfect, as it only checks the number of top-level fields
|
||||
func TestTypeStability(t *testing.T) {
|
||||
|
||||
@@ -5,9 +5,10 @@ package inventory
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
|
||||
"sigs.k8s.io/kustomize/k8sdeps/transformer/hash"
|
||||
"sigs.k8s.io/kustomize/pkg/gvk"
|
||||
"sigs.k8s.io/kustomize/pkg/hasher"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
"sigs.k8s.io/kustomize/pkg/inventory"
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
@@ -68,7 +69,7 @@ func (tf *transformer) Transform(m resmap.ResMap) error {
|
||||
invty.Current[item] = refs
|
||||
keys = append(keys, item.String())
|
||||
}
|
||||
h, err := hash.SortArrayAndComputeHash(keys)
|
||||
h, err := hasher.SortArrayAndComputeHash(keys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
52
pkg/hasher/hasher.go
Normal file
52
pkg/hasher/hasher.go
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package hasher
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// SortArrayAndComputeHash sorts a string array and
|
||||
// returns a hash for it
|
||||
func SortArrayAndComputeHash(s []string) (string, error) {
|
||||
sort.Strings(s)
|
||||
data, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return Encode(Hash(string(data)))
|
||||
}
|
||||
|
||||
// Copied from https://github.com/kubernetes/kubernetes
|
||||
// /blob/master/pkg/kubectl/util/hash/hash.go
|
||||
func Encode(hex string) (string, error) {
|
||||
if len(hex) < 10 {
|
||||
return "", fmt.Errorf(
|
||||
"input length must be at least 10.")
|
||||
}
|
||||
enc := []rune(hex[:10])
|
||||
for i := range enc {
|
||||
switch enc[i] {
|
||||
case '0':
|
||||
enc[i] = 'g'
|
||||
case '1':
|
||||
enc[i] = 'h'
|
||||
case '3':
|
||||
enc[i] = 'k'
|
||||
case 'a':
|
||||
enc[i] = 'm'
|
||||
case 'e':
|
||||
enc[i] = 't'
|
||||
}
|
||||
}
|
||||
return string(enc), nil
|
||||
}
|
||||
|
||||
// Hash returns the hex form of the sha256 of the argument.
|
||||
func Hash(data string) string {
|
||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(data)))
|
||||
}
|
||||
41
pkg/hasher/hasher_test.go
Normal file
41
pkg/hasher/hasher_test.go
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package hasher_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "sigs.k8s.io/kustomize/pkg/hasher"
|
||||
)
|
||||
|
||||
func TestSortArrayAndComputeHash(t *testing.T) {
|
||||
array1 := []string{"a", "b", "c", "d"}
|
||||
array2 := []string{"c", "b", "d", "a"}
|
||||
h1, err := SortArrayAndComputeHash(array1)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
if h1 == "" {
|
||||
t.Errorf("failed to hash %v", array1)
|
||||
}
|
||||
h2, err := SortArrayAndComputeHash(array2)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
if h2 == "" {
|
||||
t.Errorf("failed to hash %v", array2)
|
||||
}
|
||||
if h1 != h2 {
|
||||
t.Errorf("hash is not consistent with reordered list: %s %s", h1, h2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHash(t *testing.T) {
|
||||
// hash the empty string to be sure that sha256 is being used
|
||||
expect := "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
sum := Hash("")
|
||||
if expect != sum {
|
||||
t.Errorf("expected hash %q but got %q", expect, sum)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user