Skip to content

Commit

Permalink
Merge pull request #2737 from tinselspoon/hash-arbitrary-objects
Browse files Browse the repository at this point in the history
Allow hash suffixing of arbitrary types
  • Loading branch information
monopole authored Jul 22, 2020
2 parents 508d193 + 4fbe565 commit 2114b97
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 7 deletions.
23 changes: 17 additions & 6 deletions api/k8sdeps/kunstruct/hasher.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package kunstruct

import (
"encoding/json"
"fmt"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
Expand All @@ -21,7 +20,7 @@ func NewKustHash() *kustHash {
return &kustHash{}
}

// Hash returns a hash of either a ConfigMap or a Secret
// Hash returns a hash of the given object
func (h *kustHash) Hash(m ifc.Kunstructured) (string, error) {
u := unstructured.Unstructured{
Object: m.Map(),
Expand All @@ -36,15 +35,12 @@ func (h *kustHash) Hash(m ifc.Kunstructured) (string, error) {
return configMapHash(cm)
case "Secret":
sec, err := unstructuredToSecret(u)

if err != nil {
return "", err
}
return secretHash(sec)
default:
return "", fmt.Errorf(
"type %s is not supported for hashing in %v",
kind, m.Map())
return unstructuredHash(&u)
}
}

Expand Down Expand Up @@ -76,6 +72,21 @@ func secretHash(sec *corev1.Secret) (string, error) {
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 unstructuredHash(u *unstructured.Unstructured) (string, error) {
encoded, err := json.Marshal(u.Object)
if err != nil {
return "", err
}
h, err := hasher.Encode(hasher.Hash(string(encoded)))
if err != nil {
return "", err
}
return h, 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.
Expand Down
34 changes: 34 additions & 0 deletions api/k8sdeps/kunstruct/hasher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"testing"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

func TestConfigMapHash(t *testing.T) {
Expand Down Expand Up @@ -75,6 +76,39 @@ func TestSecretHash(t *testing.T) {
}
}

func TestUnstructuredHash(t *testing.T) {
cases := []struct {
desc string
unstructured *unstructured.Unstructured
hash string
err string
}{
{"minimal", &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "test/v1",
"kind": "TestResource",
"metadata": map[string]string{"name": "my-resource"}},
}, "2tt46d7f79", ""},
{"with spec", &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "test/v1",
"kind": "TestResource",
"metadata": map[string]string{"name": "my-resource"},
"spec": map[string]interface{}{"foo": 1, "bar": "abc"}},
}, "6gc62g4m6k", ""},
}

for _, c := range cases {
h, err := unstructuredHash(c.unstructured)
if SkipRest(t, c.desc, err, c.err) {
continue
}
if c.hash != h {
t.Errorf("case %q, expect hash %q but got %q", c.desc, c.hash, h)
}
}
}

func TestEncodeConfigMap(t *testing.T) {
cases := []struct {
desc string
Expand Down
6 changes: 5 additions & 1 deletion site/content/en/guides/plugins/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,11 @@ A generator exec plugin can adjust the generator options for the resources it em
Resources can be marked as needing to be processed by the internal hash transformer by including the `needs-hash` annotation. When set valid values for the annotation are `"true"` and `"false"` which respectively enable or disable hash suffixing for the resource. Omitting the annotation is equivalent to setting the value `"false"`.
If this annotation is set on a resource not supported by the hash transformer the build will fail.
Hashes are determined as follows:
* For `ConfigMap` resources, hashes are based on the values of the `name`, `data`, and `binaryData` fields.
* For `Secret` resources, hashes are based on the values of the `name`, `type`, `data`, and `stringData` fields.
* For any other object type, hashes are based on the entire object content (i.e. all fields).
Example:
Expand Down

0 comments on commit 2114b97

Please sign in to comment.