diff --git a/.travis.yml b/.travis.yml index 6867c1279..656650745 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,7 @@ env: - GOLANGCI_RELEASE="v1.12" before_install: - - source ./bin/consider-early-travis-exit.sh + - source ./travis/consider-early-travis-exit.sh - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $GOPATH/bin ${GOLANGCI_RELEASE} - go get -u github.com/monopole/mdrip # The following would install Helm if needed for some reason. @@ -39,7 +39,7 @@ before_install: install: true script: - - ./bin/pre-commit.sh + - ./travis/pre-commit.sh # TBD. Suppressing for now. notifications: diff --git a/build/build.sh b/build/cloudbuild.sh similarity index 100% rename from build/build.sh rename to build/cloudbuild.sh diff --git a/build/cloudbuild.yaml b/build/cloudbuild.yaml index c843ecadb..d2233d75e 100644 --- a/build/cloudbuild.yaml +++ b/build/cloudbuild.yaml @@ -1,23 +1,8 @@ -# Copyright 2018 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. - -# TODO(droot): add instructions for running in production. steps: - name: "gcr.io/cloud-builders/git" args: [fetch, --tags, --depth=100] - name: "gcr.io/kustomize-199618/golang_with_goreleaser:1.10-stretch" - args: ["bash", "build/build.sh"] + args: ["bash", "build/cloudbuild.sh"] secretEnv: ['GITHUB_TOKEN'] secrets: - kmsKeyName: projects/kustomize-199618/locations/global/keyRings/github-tokens/cryptoKeys/gh-release-token diff --git a/build/cloudbuild_local.yaml b/build/cloudbuild_local.yaml deleted file mode 100644 index f1eb5bb2c..000000000 --- a/build/cloudbuild_local.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2018 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. - -# Instructions to run locally: -# Download google container builder: https://github.com/kubernetes-sigs/container-builder-local -# Set you GOOS and GOARCH vars to match your system -# Set OUTPUT to the location to write the directory containing the tar.gz -# $ container-builder-local --config=build/cloudbuild_local.yaml --dryrun=false \ -# --substitutions=_GOOS=$GOOS,_GOARCH=$GOARCH --write-workspace=$OUTPUT . -# Release tar will be in $OUTPUT - -steps: -- name: "gcr.io/kustomize-199618/golang_with_goreleaser:1.10-stretch" - args: ["bash", "build/build.sh", "--snapshot"] - secretEnv: ['GITHUB_TOKEN'] -secrets: -- kmsKeyName: projects/kustomize-199618/locations/global/keyRings/github-tokens/cryptoKeys/gh-release-token - secretEnv: - GITHUB_TOKEN: CiQAyrREbPgXJOeT7M3t+WlxkhXwlMPudixBeiyWTjmLOMLqdK4SUQA0W+xUmDJKAhyfHCcwqSEzUn9OwKC7XAYcmwe0CCKTCbPbDgmioDK24q3LVapndXNvnnHvCjhOJNEr1o+P1DCF+LlzYV2YL8lP09rrKrslPg== diff --git a/build/localbuild.sh b/build/localbuild.sh new file mode 100755 index 000000000..c0f7d770b --- /dev/null +++ b/build/localbuild.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +# Usage +# +# ./build/localbuild.sh +# +# The script attempts to use cloudbuild configuration +# to create a release "locally". +# +# At the time of writing, +# +# https://pantheon.corp.google.com/cloud-build/triggers?project=kustomize-199618 +# +# has a trigger such that whenever a git tag is +# applied to the kustomize repo, the cloud builder +# reads the repository-relative file +# +# build/cloudbuild.yaml +# +# Inside this yaml file is a reference to the script +# +# build/cloudbuild.sh +# +# The script you are reading now does something +# analogous via docker tricks. + +set -e +# set -x + +if [ -z ${GOPATH+x} ]; then + echo GOPATH is unset; cannot proceed. + exit 1 +fi + +WORK=$(mktemp -d) + +pushd $GOPATH/src/sigs.k8s.io/kustomize +pwd + +echo "Building in $WORK" + +cat </tmp/localbuild.yaml +steps: +- name: "gcr.io/kustomize-199618/golang_with_goreleaser:1.10-stretch" + args: ["bash", "build/cloudbuild.sh", "--snapshot"] + secretEnv: ['GITHUB_TOKEN'] +secrets: +- kmsKeyName: projects/kustomize-199618/locations/global/keyRings/github-tokens/cryptoKeys/gh-release-token + secretEnv: + GITHUB_TOKEN: CiQAyrREbPgXJOeT7M3t+WlxkhXwlMPudixBeiyWTjmLOMLqdK4SUQA0W+xUmDJKAhyfHCcwqSEzUn9OwKC7XAYcmwe0CCKTCbPbDgmioDK24q3LVapndXNvnnHvCjhOJNEr1o+P1DCF+LlzYV2YL8lP09rrKrslPg== +EOF + +# --substitutions=_GOOS=linux,_GOARCH=amd64 + +config=build/cloudbuild.yaml +config=/tmp/localbuild.yaml + +# See https://cloud.google.com/cloud-build/docs/build-debug-locally +cloud-build-local \ + --config=$config \ + --dryrun=false \ + --write-workspace=$WORK \ + . + +tree $WORK + +popd diff --git a/k8sdeps/transformer/factory.go b/k8sdeps/transformer/factory.go index 61dff0ab5..5a6a0b8d3 100644 --- a/k8sdeps/transformer/factory.go +++ b/k8sdeps/transformer/factory.go @@ -26,12 +26,12 @@ func NewFactoryImpl() *FactoryImpl { func (p *FactoryImpl) MakePatchTransformer( slice []*resource.Resource, rf *resource.Factory) (transformers.Transformer, error) { - return patch.NewPatchTransformer(slice, rf) + return patch.NewTransformer(slice, rf) } // MakeHashTransformer makes a new name hash transformer func (p *FactoryImpl) MakeHashTransformer() transformers.Transformer { - return hash.NewNameHashTransformer() + return hash.NewTransformer() } func (p *FactoryImpl) MakeInventoryTransformer( @@ -39,5 +39,5 @@ func (p *FactoryImpl) MakeInventoryTransformer( ldr ifc.Loader, namespace string, gp types.GarbagePolicy) transformers.Transformer { - return inventory.NewInventoryTransformer(arg, ldr, namespace, gp) + return inventory.NewTransformer(arg, ldr, namespace, gp) } diff --git a/k8sdeps/transformer/hash/hash.go b/k8sdeps/transformer/hash/hash.go deleted file mode 100644 index cdab7244b..000000000 --- a/k8sdeps/transformer/hash/hash.go +++ /dev/null @@ -1,184 +0,0 @@ -/* -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. -*/ - -package hash - -import ( - "crypto/sha256" - "encoding/json" - "fmt" - "sort" - - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" -) - -// KustHash compute hash for unstructured objects -type KustHash struct{} - -// NewKustHash returns a KustHash object -func NewKustHash() *KustHash { - return &KustHash{} -} - -// Hash returns a hash of either a ConfigMap or a Secret -func (h *KustHash) Hash(m map[string]interface{}) (string, error) { - u := unstructured.Unstructured{ - Object: m, - } - kind := u.GetKind() - switch kind { - case "ConfigMap": - cm, err := unstructuredToConfigmap(u) - if err != nil { - return "", err - } - return ConfigMapHash(cm) - case "Secret": - sec, err := unstructuredToSecret(u) - - if err != nil { - return "", err - } - return SecretHash(sec) - default: - return "", fmt.Errorf("type %s is supported for hashing in %v", kind, m) - } -} - -// ConfigMapHash returns a hash of the ConfigMap. -// The Data, Kind, and Name are taken into account. -func ConfigMapHash(cm *v1.ConfigMap) (string, error) { - encoded, err := encodeConfigMap(cm) - if err != nil { - return "", err - } - h, err := encodeHash(hash(encoded)) - if err != nil { - return "", err - } - return h, nil -} - -// SecretHash returns a hash of the Secret. -// The Data, Kind, Name, and Type are taken into account. -func SecretHash(sec *v1.Secret) (string, error) { - encoded, err := encodeSecret(sec) - 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))) - if err != nil { - return "", err - } - return h, nil -} - -// encodeConfigMap encodes a ConfigMap. -// Data, Kind, and Name are taken into account. -func encodeConfigMap(cm *v1.ConfigMap) (string, error) { - // json.Marshal sorts the keys in a stable order in the encoding - m := map[string]interface{}{"kind": "ConfigMap", "name": cm.Name, "data": cm.Data} - if len(cm.BinaryData) > 0 { - m["binaryData"] = cm.BinaryData - } - data, err := json.Marshal(m) - if err != nil { - return "", err - } - return string(data), nil -} - -// encodeSecret encodes a Secret. -// Data, Kind, Name, and Type are taken into account. -func encodeSecret(sec *v1.Secret) (string, error) { - // json.Marshal sorts the keys in a stable order in the encoding - data, err := json.Marshal(map[string]interface{}{"kind": "Secret", "type": sec.Type, "name": sec.Name, "data": sec.Data}) - if err != nil { - return "", err - } - 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 { - return nil, err - } - var out v1.ConfigMap - err = json.Unmarshal(marshaled, &out) - return &out, err -} - -func unstructuredToSecret(u unstructured.Unstructured) (*v1.Secret, error) { - marshaled, err := json.Marshal(u.Object) - if err != nil { - return nil, err - } - var out v1.Secret - err = json.Unmarshal(marshaled, &out) - return &out, err -} diff --git a/k8sdeps/transformer/hash/kusthash.go b/k8sdeps/transformer/hash/kusthash.go new file mode 100644 index 000000000..c755698d6 --- /dev/null +++ b/k8sdeps/transformer/hash/kusthash.go @@ -0,0 +1,121 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package hash + +import ( + "encoding/json" + "fmt" + + "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. +type kustHash struct{} + +// NewKustHash returns a kustHash object +func NewKustHash() *kustHash { + return &kustHash{} +} + +// Hash returns a hash of either a ConfigMap or a Secret +func (h *kustHash) Hash(m map[string]interface{}) (string, error) { + u := unstructured.Unstructured{ + Object: m, + } + kind := u.GetKind() + switch kind { + case "ConfigMap": + cm, err := unstructuredToConfigmap(u) + if err != nil { + return "", err + } + 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) + } +} + +// configMapHash returns a hash of the ConfigMap. +// The Data, Kind, and Name are taken into account. +func configMapHash(cm *v1.ConfigMap) (string, error) { + encoded, err := encodeConfigMap(cm) + if err != nil { + return "", err + } + h, err := hasher.Encode(hasher.Hash(encoded)) + if err != nil { + return "", err + } + return h, nil +} + +// SecretHash returns a hash of the Secret. +// The Data, Kind, Name, and Type are taken into account. +func secretHash(sec *v1.Secret) (string, error) { + encoded, err := encodeSecret(sec) + if err != nil { + return "", err + } + h, err := hasher.Encode(hasher.Hash(encoded)) + if err != nil { + return "", err + } + return h, nil +} + +// encodeConfigMap encodes a ConfigMap. +// Data, Kind, and Name are taken into account. +func encodeConfigMap(cm *v1.ConfigMap) (string, error) { + // json.Marshal sorts the keys in a stable order in the encoding + m := map[string]interface{}{"kind": "ConfigMap", "name": cm.Name, "data": cm.Data} + if len(cm.BinaryData) > 0 { + m["binaryData"] = cm.BinaryData + } + data, err := json.Marshal(m) + if err != nil { + return "", err + } + return string(data), nil +} + +// encodeSecret encodes a Secret. +// Data, Kind, Name, and Type are taken into account. +func encodeSecret(sec *v1.Secret) (string, error) { + // json.Marshal sorts the keys in a stable order in the encoding + data, err := json.Marshal(map[string]interface{}{"kind": "Secret", "type": sec.Type, "name": sec.Name, "data": sec.Data}) + if err != nil { + return "", err + } + return string(data), nil +} + +func unstructuredToConfigmap(u unstructured.Unstructured) (*v1.ConfigMap, error) { + marshaled, err := json.Marshal(u.Object) + if err != nil { + return nil, err + } + var out v1.ConfigMap + err = json.Unmarshal(marshaled, &out) + return &out, err +} + +func unstructuredToSecret(u unstructured.Unstructured) (*v1.Secret, error) { + marshaled, err := json.Marshal(u.Object) + if err != nil { + return nil, err + } + var out v1.Secret + err = json.Unmarshal(marshaled, &out) + return &out, err +} diff --git a/k8sdeps/transformer/hash/hash_test.go b/k8sdeps/transformer/hash/kusthash_test.go similarity index 82% rename from k8sdeps/transformer/hash/hash_test.go rename to k8sdeps/transformer/hash/kusthash_test.go index 4a3c9365c..006de9f8d 100644 --- a/k8sdeps/transformer/hash/hash_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 @@ -54,7 +41,7 @@ func TestConfigMapHash(t *testing.T) { } for _, c := range cases { - h, err := ConfigMapHash(c.cm) + h, err := configMapHash(c.cm) if SkipRest(t, c.desc, err, c.err) { continue } @@ -80,7 +67,7 @@ func TestSecretHash(t *testing.T) { } for _, c := range cases { - h, err := SecretHash(c.secret) + h, err := secretHash(c.secret) if SkipRest(t, c.desc, err, c.err) { continue } @@ -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/hash/namehash.go b/k8sdeps/transformer/hash/namehash.go deleted file mode 100644 index a52072e8a..000000000 --- a/k8sdeps/transformer/hash/namehash.go +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2018 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. -*/ - -package hash - -import ( - "fmt" - - "sigs.k8s.io/kustomize/pkg/resmap" - "sigs.k8s.io/kustomize/pkg/transformers" -) - -type nameHashTransformer struct{} - -var _ transformers.Transformer = &nameHashTransformer{} - -// NewNameHashTransformer construct a nameHashTransformer. -func NewNameHashTransformer() transformers.Transformer { - return &nameHashTransformer{} -} - -// Transform appends hash to generated resources. -func (o *nameHashTransformer) Transform(m resmap.ResMap) error { - for _, res := range m { - if res.NeedHashSuffix() { - h, err := NewKustHash().Hash(res.Map()) - if err != nil { - return err - } - res.SetName(fmt.Sprintf("%s-%s", res.GetName(), h)) - } - } - return nil -} diff --git a/k8sdeps/transformer/hash/transformer.go b/k8sdeps/transformer/hash/transformer.go new file mode 100644 index 000000000..3e0e3123a --- /dev/null +++ b/k8sdeps/transformer/hash/transformer.go @@ -0,0 +1,34 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package hash + +import ( + "fmt" + + "sigs.k8s.io/kustomize/pkg/resmap" + "sigs.k8s.io/kustomize/pkg/transformers" +) + +type transformer struct{} + +var _ transformers.Transformer = &transformer{} + +// NewTransformer make a hash transformer. +func NewTransformer() transformers.Transformer { + return &transformer{} +} + +// Transform appends hash to generated resources. +func (tf *transformer) Transform(m resmap.ResMap) error { + for _, res := range m { + if res.NeedHashSuffix() { + h, err := NewKustHash().Hash(res.Map()) + if err != nil { + return err + } + res.SetName(fmt.Sprintf("%s-%s", res.GetName(), h)) + } + } + return nil +} diff --git a/k8sdeps/transformer/hash/namehash_test.go b/k8sdeps/transformer/hash/transformer_test.go similarity index 85% rename from k8sdeps/transformer/hash/namehash_test.go rename to k8sdeps/transformer/hash/transformer_test.go index 7ca724511..cf182671e 100644 --- a/k8sdeps/transformer/hash/namehash_test.go +++ b/k8sdeps/transformer/hash/transformer_test.go @@ -1,18 +1,5 @@ -/* -Copyright 2018 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 @@ -152,7 +139,7 @@ func TestNameHashTransformer(t *testing.T) { }, &types.GeneratorArgs{Behavior: "create"}, &types.GeneratorOptions{DisableNameSuffixHash: false}), } - tran := NewNameHashTransformer() + tran := NewTransformer() tran.Transform(objs) if !reflect.DeepEqual(objs, expected) { diff --git a/k8sdeps/transformer/inventory/inventory.go b/k8sdeps/transformer/inventory/transformer.go similarity index 81% rename from k8sdeps/transformer/inventory/inventory.go rename to k8sdeps/transformer/inventory/transformer.go index 61fe389a6..5b3009831 100644 --- a/k8sdeps/transformer/inventory/inventory.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" @@ -17,18 +18,18 @@ import ( "sigs.k8s.io/kustomize/pkg/types" ) -// inventoryTransformer compute the inventory object used in prune -type inventoryTransformer struct { +// transformer compute the inventory object used in prune +type transformer struct { garbagePolicy types.GarbagePolicy ldr ifc.Loader cmName string cmNamespace string } -var _ transformers.Transformer = &inventoryTransformer{} +var _ transformers.Transformer = &transformer{} -// NewInventoryTransformer makes a inventoryTransformer. -func NewInventoryTransformer( +// NewTransformer makes a new inventory transformer. +func NewTransformer( p *types.Inventory, ldr ifc.Loader, namespace string, @@ -36,7 +37,7 @@ func NewInventoryTransformer( if p == nil || p.Type != "ConfigMap" || p.ConfigMap.Namespace != namespace { return transformers.NewNoOpTransformer() } - return &inventoryTransformer{ + return &transformer{ garbagePolicy: gp, ldr: ldr, cmName: p.ConfigMap.Name, @@ -52,7 +53,7 @@ func NewInventoryTransformer( // The inventory data is written to annotation since // 1. The key in data field is constrained and couldn't include arbitrary letters // 2. The annotation can be put into any kind of objects -func (o *inventoryTransformer) Transform(m resmap.ResMap) error { +func (tf *transformer) Transform(m resmap.ResMap) error { invty := inventory.NewInventory() var keys []string for _, r := range m { @@ -68,14 +69,14 @@ func (o *inventoryTransformer) 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 } args := &types.ConfigMapArgs{} - args.Name = o.cmName - args.Namespace = o.cmNamespace + args.Name = tf.cmName + args.Namespace = tf.cmNamespace opts := &types.GeneratorOptions{ Annotations: make(map[string]string), } @@ -86,12 +87,12 @@ func (o *inventoryTransformer) Transform(m resmap.ResMap) error { } kf := kunstruct.NewKunstructuredFactoryImpl() - k, err := kf.MakeConfigMap(o.ldr, opts, args) + k, err := kf.MakeConfigMap(tf.ldr, opts, args) if err != nil { return err } - if o.garbagePolicy == types.GarbageCollect { + if tf.garbagePolicy == types.GarbageCollect { for k := range m { delete(m, k) } @@ -102,8 +103,8 @@ func (o *inventoryTransformer) Transform(m resmap.ResMap) error { Version: "v1", Kind: "ConfigMap", }, - o.cmName, - "", o.cmNamespace) + tf.cmName, + "", tf.cmNamespace) if _, ok := m[id]; ok { return fmt.Errorf("id %v is already used, please use a different name in the prune field", id) } diff --git a/k8sdeps/transformer/inventory/inventory_test.go b/k8sdeps/transformer/inventory/transformer_test.go similarity index 97% rename from k8sdeps/transformer/inventory/inventory_test.go rename to k8sdeps/transformer/inventory/transformer_test.go index b2fa210c4..0c503d396 100644 --- a/k8sdeps/transformer/inventory/inventory_test.go +++ b/k8sdeps/transformer/inventory/transformer_test.go @@ -139,7 +139,7 @@ func TestInventoryTransformer(t *testing.T) { objs := makeResMap() // include the original resmap; only return the ConfigMap for pruning - tran := NewInventoryTransformer(p, ldr, "default", types.GarbageCollect) + tran := NewTransformer(p, ldr, "default", types.GarbageCollect) tran.Transform(objs) if !reflect.DeepEqual(objs, expected) { @@ -151,7 +151,7 @@ func TestInventoryTransformer(t *testing.T) { expected = objs.DeepCopy(rf) expected[resid.NewResIdWithPrefixNamespace(cmap, "pruneCM", "", "default")] = pruneMap // append the ConfigMap for pruning to the original resmap - tran = NewInventoryTransformer(p, ldr, "default", types.GarbageIgnore) + tran = NewTransformer(p, ldr, "default", types.GarbageIgnore) tran.Transform(objs) if !reflect.DeepEqual(objs, expected) { diff --git a/k8sdeps/transformer/patch/patchconflictdetector.go b/k8sdeps/transformer/patch/conflictdetector.go similarity index 85% rename from k8sdeps/transformer/patch/patchconflictdetector.go rename to k8sdeps/transformer/patch/conflictdetector.go index 10353c77f..3d8851505 100644 --- a/k8sdeps/transformer/patch/patchconflictdetector.go +++ b/k8sdeps/transformer/patch/conflictdetector.go @@ -1,18 +1,5 @@ -/* -Copyright 2018 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 patch diff --git a/k8sdeps/transformer/patch/patch.go b/k8sdeps/transformer/patch/transformer.go similarity index 75% rename from k8sdeps/transformer/patch/patch.go rename to k8sdeps/transformer/patch/transformer.go index 357f3daba..a0dcff6b2 100644 --- a/k8sdeps/transformer/patch/patch.go +++ b/k8sdeps/transformer/patch/transformer.go @@ -1,18 +1,5 @@ -/* -Copyright 2018 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 patch @@ -31,27 +18,27 @@ import ( "sigs.k8s.io/kustomize/pkg/transformers" ) -// patchTransformer applies patches. -type patchTransformer struct { +// transformer applies strategic merge patches. +type transformer struct { patches []*resource.Resource rf *resource.Factory } -var _ transformers.Transformer = &patchTransformer{} +var _ transformers.Transformer = &transformer{} -// NewPatchTransformer constructs a patchTransformer. -func NewPatchTransformer( +// NewTransformer constructs a strategic merge patch transformer. +func NewTransformer( slice []*resource.Resource, rf *resource.Factory) (transformers.Transformer, error) { if len(slice) == 0 { return transformers.NewNoOpTransformer(), nil } - return &patchTransformer{patches: slice, rf: rf}, nil + return &transformer{patches: slice, rf: rf}, nil } // Transform apply the patches on top of the base resources. -func (pt *patchTransformer) Transform(baseResourceMap resmap.ResMap) error { +func (tf *transformer) Transform(baseResourceMap resmap.ResMap) error { // Merge and then index the patches by Id. - patches, err := pt.mergePatches() + patches, err := tf.mergePatches() if err != nil { return err } @@ -118,9 +105,9 @@ func (pt *patchTransformer) Transform(baseResourceMap resmap.ResMap) error { // mergePatches merge and index patches by Id. // It errors out if there is conflict between patches. -func (pt *patchTransformer) mergePatches() (resmap.ResMap, error) { +func (tf *transformer) mergePatches() (resmap.ResMap, error) { rc := resmap.ResMap{} - for ix, patch := range pt.patches { + for ix, patch := range tf.patches { id := patch.Id() existing, found := rc[id] if !found { @@ -134,9 +121,9 @@ func (pt *patchTransformer) mergePatches() (resmap.ResMap, error) { } var cd conflictDetector if err != nil { - cd = newJMPConflictDetector(pt.rf) + cd = newJMPConflictDetector(tf.rf) } else { - cd, err = newSMPConflictDetector(versionedObj, pt.rf) + cd, err = newSMPConflictDetector(versionedObj, tf.rf) if err != nil { return nil, err } @@ -147,7 +134,7 @@ func (pt *patchTransformer) mergePatches() (resmap.ResMap, error) { return nil, err } if conflict { - conflictingPatch, err := cd.findConflict(ix, pt.patches) + conflictingPatch, err := cd.findConflict(ix, tf.patches) if err != nil { return nil, err } diff --git a/k8sdeps/transformer/patch/patch_test.go b/k8sdeps/transformer/patch/transformer_test.go similarity index 93% rename from k8sdeps/transformer/patch/patch_test.go rename to k8sdeps/transformer/patch/transformer_test.go index d6955a6ee..a47d2d25d 100644 --- a/k8sdeps/transformer/patch/patch_test.go +++ b/k8sdeps/transformer/patch/transformer_test.go @@ -1,18 +1,5 @@ -/* -Copyright 2018 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 patch @@ -128,7 +115,7 @@ func TestOverlayRun(t *testing.T) { }, }), } - lt, err := NewPatchTransformer(patch, rf) + lt, err := NewTransformer(patch, rf) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -258,7 +245,7 @@ func TestMultiplePatches(t *testing.T) { }, }), } - lt, err := NewPatchTransformer(patch, rf) + lt, err := NewTransformer(patch, rf) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -344,7 +331,7 @@ func TestMultiplePatchesWithConflict(t *testing.T) { ), } - lt, err := NewPatchTransformer(patch, rf) + lt, err := NewTransformer(patch, rf) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -407,7 +394,7 @@ func TestNoSchemaOverlayRun(t *testing.T) { }), } - lt, err := NewPatchTransformer(patch, rf) + lt, err := NewTransformer(patch, rf) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -491,7 +478,7 @@ func TestNoSchemaMultiplePatches(t *testing.T) { }), } - lt, err := NewPatchTransformer(patch, rf) + lt, err := NewTransformer(patch, rf) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -549,7 +536,7 @@ func TestNoSchemaMultiplePatchesWithConflict(t *testing.T) { }), } - lt, err := NewPatchTransformer(patch, rf) + lt, err := NewTransformer(patch, rf) if err != nil { t.Fatalf("unexpected error: %v", 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) + } +} diff --git a/pkg/target/chartinflatorplugin_test.go b/pkg/target/chartinflatorplugin_test.go index 4112cfac7..580eefd08 100644 --- a/pkg/target/chartinflatorplugin_test.go +++ b/pkg/target/chartinflatorplugin_test.go @@ -61,7 +61,7 @@ kind: Secret metadata: labels: app: release-name-minecraft - chart: minecraft-1.0.0 + chart: minecraft-1.0.1 heritage: Tiller release: release-name name: LOOOOOOOONG-release-name-minecraft @@ -72,7 +72,7 @@ kind: Service metadata: labels: app: release-name-minecraft - chart: minecraft-1.0.0 + chart: minecraft-1.0.1 heritage: Tiller release: release-name name: LOOOOOOOONG-release-name-minecraft @@ -93,7 +93,7 @@ metadata: volume.alpha.kubernetes.io/storage-class: default labels: app: release-name-minecraft - chart: minecraft-1.0.0 + chart: minecraft-1.0.1 heritage: Tiller release: release-name name: LOOOOOOOONG-release-name-minecraft-datadir diff --git a/pkg/target/configmaps_test.go b/pkg/target/configmaps_test.go index 2a7dc7738..edc275e2b 100644 --- a/pkg/target/configmaps_test.go +++ b/pkg/target/configmaps_test.go @@ -31,24 +31,26 @@ namePrefix: blah- configMapGenerator: - name: bob literals: - - fruit=apple - - vegetable=broccoli - env: foo.env + - fruit=apple + - vegetable=broccoli + envs: + - foo.env files: - - passphrase=phrase.dat - - forces.txt + - passphrase=phrase.dat + - forces.txt - name: json literals: - - 'v2=[{"path": "var/druid/segment-cache"}]' + - 'v2=[{"path": "var/druid/segment-cache"}]' secretGenerator: - name: bob literals: - - fruit=apple - - vegetable=broccoli - env: foo.env + - fruit=apple + - vegetable=broccoli + envs: + - foo.env files: - - passphrase=phrase.dat - - forces.txt + - passphrase=phrase.dat + - forces.txt `) th.WriteF("/app/foo.env", ` MOUNTAIN=everest @@ -122,15 +124,15 @@ configMapGenerator: - name: bob behavior: create literals: - - bean=pinto - - star=wolf-rayet + - bean=pinto + - star=wolf-rayet literals: - - fruit=apple - - vegetable=broccoli + - fruit=apple + - vegetable=broccoli files: - - forces.txt + - forces.txt files: - - nobles=nobility.txt + - nobles=nobility.txt `) th.WriteF("/app/forces.txt", ` gravitational @@ -177,7 +179,7 @@ configMapGenerator: - name: com1 behavior: create literals: - - from=base + - from=base `) th.WriteK("/app/base2", ` namePrefix: p2- @@ -185,7 +187,7 @@ configMapGenerator: - name: com2 behavior: create literals: - - from=base + - from=base `) th.WriteK("/app/overlay/o1", ` bases: @@ -194,7 +196,7 @@ configMapGenerator: - name: com1 behavior: merge literals: - - from=overlay + - from=overlay `) th.WriteK("/app/overlay/o2", ` bases: @@ -203,7 +205,7 @@ configMapGenerator: - name: com2 behavior: merge literals: - - from=overlay + - from=overlay `) th.WriteK("/app/overlay", ` bases: @@ -213,8 +215,8 @@ configMapGenerator: - name: com1 behavior: merge literals: - - foo=bar - - baz=qux + - foo=bar + - baz=qux `) m, err := th.MakeKustTarget().MakeCustomizedResMap() if err != nil { diff --git a/pkg/target/generatormergeandreplace_test.go b/pkg/target/generatormergeandreplace_test.go index 0bc66fe55..fa946223c 100644 --- a/pkg/target/generatormergeandreplace_test.go +++ b/pkg/target/generatormergeandreplace_test.go @@ -178,14 +178,14 @@ resources: - deployment.yaml - service.yaml configMapGenerator: - - name: configmap-in-base - literals: - - foo=bar +- name: configmap-in-base + literals: + - foo=bar secretGenerator: - name: secret-in-base literals: - - username=admin - - password=somepw + - username=admin + - password=somepw `) th.WriteF("/app/deployment.yaml", ` apiVersion: apps/v1beta2 @@ -354,18 +354,18 @@ patchesStrategicMerge: bases: - ../app configMapGenerator: - - name: configmap-in-overlay - literals: - - hello=world - - name: configmap-in-base - behavior: replace - literals: - - foo=override-bar +- name: configmap-in-overlay + literals: + - hello=world +- name: configmap-in-base + behavior: replace + literals: + - foo=override-bar secretGenerator: - name: secret-in-base behavior: merge literals: - - proxy=haproxy + - proxy=haproxy `) m, err := th.MakeKustTarget().MakeCustomizedResMap() if err != nil { diff --git a/pkg/target/multiplepatch_test.go b/pkg/target/multiplepatch_test.go index 7d75e7eb8..5fce30b1f 100644 --- a/pkg/target/multiplepatch_test.go +++ b/pkg/target/multiplepatch_test.go @@ -38,9 +38,9 @@ resources: - deployment.yaml - service.yaml configMapGenerator: - - name: configmap-in-base - literals: - - foo=bar +- name: configmap-in-base + literals: + - foo=bar `) th.WriteF("/app/base/deployment.yaml", ` apiVersion: apps/v1beta2 @@ -93,9 +93,9 @@ patchesStrategicMerge: bases: - ../../base configMapGenerator: - - name: configmap-in-overlay - literals: - - hello=world +- name: configmap-in-overlay + literals: + - hello=world `) } diff --git a/pkg/target/namespacedgenerators_test.go b/pkg/target/namespacedgenerators_test.go index 3d598ab4e..f62325f44 100644 --- a/pkg/target/namespacedgenerators_test.go +++ b/pkg/target/namespacedgenerators_test.go @@ -42,10 +42,10 @@ secretGenerator: - name: the-non-default-namespace-secret namespace: non-default literals: - - password.txt=verySecret + - password.txt=verySecret - name: the-secret literals: - - password.txt=anotherSecret + - password.txt=anotherSecret `) m, err := th.MakeKustTarget().MakeCustomizedResMap() if err != nil { diff --git a/pkg/target/variableref_test.go b/pkg/target/variableref_test.go index b664e860c..74b647110 100644 --- a/pkg/target/variableref_test.go +++ b/pkg/target/variableref_test.go @@ -32,8 +32,8 @@ resources: configMapGenerator: - name: test-config-map literals: - - foo=bar - - baz=qux + - foo=bar + - baz=qux vars: - name: CDB_PUBLIC_SVC objref: diff --git a/pkg/transformers/config/defaultconfig/namereference.go b/pkg/transformers/config/defaultconfig/namereference.go index 0a3562a5e..524a914fc 100644 --- a/pkg/transformers/config/defaultconfig/namereference.go +++ b/pkg/transformers/config/defaultconfig/namereference.go @@ -252,6 +252,12 @@ nameReference: - path: spec/service/name kind: APIService group: apiregistration.k8s.io + - path: webhooks/clientConfig/service/name + kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + - path: webhooks/clientConfig/service/name + kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io - kind: Role group: rbac.authorization.k8s.io diff --git a/plugin/someteam.example.com/v1/chartinflator/ChartInflator b/plugin/someteam.example.com/v1/chartinflator/ChartInflator index 127b371b2..fde9032d4 100755 --- a/plugin/someteam.example.com/v1/chartinflator/ChartInflator +++ b/plugin/someteam.example.com/v1/chartinflator/ChartInflator @@ -24,6 +24,10 @@ # Example execution: # ./plugin/someteam.example.com/v1/ChartInflator configFile.yaml +# TODO: allow specification of a specific chart VERSION +# so this test doesn't break every time minecraft is upgraded :P +# See https://github.com/helm/helm/issues/4008 + set -e # Yaml parsing is a ridiculous thing to do in bash, diff --git a/plugin/someteam.example.com/v1/chartinflator/ChartInflator_test.go b/plugin/someteam.example.com/v1/chartinflator/ChartInflator_test.go index 0fca6b53d..8765cbcb8 100644 --- a/plugin/someteam.example.com/v1/chartinflator/ChartInflator_test.go +++ b/plugin/someteam.example.com/v1/chartinflator/ChartInflator_test.go @@ -42,7 +42,7 @@ kind: Secret metadata: labels: app: release-name-minecraft - chart: minecraft-1.0.0 + chart: minecraft-1.0.1 heritage: Tiller release: release-name name: release-name-minecraft @@ -53,7 +53,7 @@ kind: Service metadata: labels: app: release-name-minecraft - chart: minecraft-1.0.0 + chart: minecraft-1.0.1 heritage: Tiller release: release-name name: release-name-minecraft @@ -74,7 +74,7 @@ metadata: volume.alpha.kubernetes.io/storage-class: default labels: app: release-name-minecraft - chart: minecraft-1.0.0 + chart: minecraft-1.0.1 heritage: Tiller release: release-name name: release-name-minecraft-datadir diff --git a/bin/consider-early-travis-exit.sh b/travis/consider-early-travis-exit.sh similarity index 100% rename from bin/consider-early-travis-exit.sh rename to travis/consider-early-travis-exit.sh diff --git a/bin/pre-commit.sh b/travis/pre-commit.sh similarity index 100% rename from bin/pre-commit.sh rename to travis/pre-commit.sh