diff --git a/k8sdeps/transformer/hash/kusthash.go b/k8sdeps/transformer/hash/kusthash.go index 26c647d83..c755698d6 100644 --- a/k8sdeps/transformer/hash/kusthash.go +++ b/k8sdeps/transformer/hash/kusthash.go @@ -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 { diff --git a/k8sdeps/transformer/hash/kusthash_test.go b/k8sdeps/transformer/hash/kusthash_test.go index 47d783498..006de9f8d 100644 --- a/k8sdeps/transformer/hash/kusthash_test.go +++ b/k8sdeps/transformer/hash/kusthash_test.go @@ -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) { diff --git a/k8sdeps/transformer/inventory/transformer.go b/k8sdeps/transformer/inventory/transformer.go index 431c8c280..5b3009831 100644 --- a/k8sdeps/transformer/inventory/transformer.go +++ b/k8sdeps/transformer/inventory/transformer.go @@ -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 } diff --git a/pkg/hasher/hasher.go b/pkg/hasher/hasher.go new file mode 100644 index 000000000..77ded7fdf --- /dev/null +++ b/pkg/hasher/hasher.go @@ -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))) +} diff --git a/pkg/hasher/hasher_test.go b/pkg/hasher/hasher_test.go new file mode 100644 index 000000000..970fb4968 --- /dev/null +++ b/pkg/hasher/hasher_test.go @@ -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) + } +}