Merge branch 'master' into transformer-no-create-arrays

This commit is contained in:
Tom Wieczorek
2019-05-29 12:18:59 +02:00
30 changed files with 435 additions and 479 deletions

View File

@@ -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:

View File

@@ -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

View File

@@ -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==

67
build/localbuild.sh Executable file
View File

@@ -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 <<EOF >/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

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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)
}

View File

@@ -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) {

View File

@@ -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

View File

@@ -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
}

View File

@@ -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)
}

52
pkg/hasher/hasher.go Normal file
View 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
View 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)
}
}

View File

@@ -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

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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
`)
}

View File

@@ -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 {

View File

@@ -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:

View File

@@ -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

View File

@@ -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,

View File

@@ -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