mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-11 17:12:51 +00:00
Move hashing code out of k8sdeps.
This commit is contained in:
@@ -4,13 +4,12 @@
|
|||||||
package hash
|
package hash
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"sigs.k8s.io/kustomize/pkg/hasher"
|
||||||
)
|
)
|
||||||
|
|
||||||
// kustHash computes a hash of an unstructured object.
|
// kustHash computes a hash of an unstructured object.
|
||||||
@@ -54,7 +53,7 @@ func configMapHash(cm *v1.ConfigMap) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
h, err := encodeHash(hash(encoded))
|
h, err := hasher.Encode(hasher.Hash(encoded))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -68,22 +67,7 @@ func secretHash(sec *v1.Secret) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
h, err := encodeHash(hash(encoded))
|
h, err := hasher.Encode(hasher.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)))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -116,41 +100,6 @@ func encodeSecret(sec *v1.Secret) (string, error) {
|
|||||||
return string(data), nil
|
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) {
|
func unstructuredToConfigmap(u unstructured.Unstructured) (*v1.ConfigMap, error) {
|
||||||
marshaled, err := json.Marshal(u.Object)
|
marshaled, err := json.Marshal(u.Object)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,18 +1,5 @@
|
|||||||
/*
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
Copyright 2017 The Kubernetes Authors.
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package hash
|
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) {
|
func TestEncodeConfigMap(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
desc string
|
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
|
// 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
|
// not perfect, as it only checks the number of top-level fields
|
||||||
func TestTypeStability(t *testing.T) {
|
func TestTypeStability(t *testing.T) {
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ package inventory
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
|
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
|
||||||
"sigs.k8s.io/kustomize/k8sdeps/transformer/hash"
|
|
||||||
"sigs.k8s.io/kustomize/pkg/gvk"
|
"sigs.k8s.io/kustomize/pkg/gvk"
|
||||||
|
"sigs.k8s.io/kustomize/pkg/hasher"
|
||||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||||
"sigs.k8s.io/kustomize/pkg/inventory"
|
"sigs.k8s.io/kustomize/pkg/inventory"
|
||||||
"sigs.k8s.io/kustomize/pkg/resid"
|
"sigs.k8s.io/kustomize/pkg/resid"
|
||||||
@@ -68,7 +69,7 @@ func (tf *transformer) Transform(m resmap.ResMap) error {
|
|||||||
invty.Current[item] = refs
|
invty.Current[item] = refs
|
||||||
keys = append(keys, item.String())
|
keys = append(keys, item.String())
|
||||||
}
|
}
|
||||||
h, err := hash.SortArrayAndComputeHash(keys)
|
h, err := hasher.SortArrayAndComputeHash(keys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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