Reduce k8ds deps

This commit is contained in:
jregan
2019-05-27 08:21:10 -07:00
parent 3af0f9776f
commit 47c965481f
39 changed files with 563 additions and 698 deletions

View File

@@ -6,7 +6,6 @@
这些示例通过了 [pre-commit](../../bin/pre-commit.sh) 测试,并且应该与 HEAD 一起使用。
<!-- @installkustomize @test -->
```
go get sigs.k8s.io/kustomize
```
@@ -26,7 +25,7 @@ go get sigs.k8s.io/kustomize
* [configGenerations](../configGeneration.md) - 当 ConfigMapGenerator 修改时进行滚动更新。
* [secret generation](../kvSourceGoPlugin.md) - 生成 Secret。
* [secret generation](../secretGeneratorPlugin.md) - 生成 Secret。
* [generatorOptions](../generatorOptions.md) -修改所有 ConfigMapGenerator 和 SecretGenerator 的行为。

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 loadertest holds a fake for the Loader interface.
package loadertest
@@ -22,6 +9,8 @@ import (
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/types"
"sigs.k8s.io/kustomize/pkg/validators"
)
// FakeLoader encapsulates the delegate Loader and the fake file system.
@@ -46,7 +35,8 @@ func NewFakeLoaderWithRestrictor(
// Create fake filesystem and inject it into initial Loader.
fSys := fs.MakeFakeFS()
fSys.Mkdir(initialDir)
ldr, err := loader.NewLoader(lr, initialDir, fSys)
ldr, err := loader.NewLoader(
lr, validators.MakeFakeValidator(), initialDir, fSys)
if err != nil {
log.Fatalf("Unable to make loader: %v", err)
}
@@ -63,7 +53,7 @@ func (f FakeLoader) AddDirectory(fullDirPath string) error {
return f.fs.Mkdir(fullDirPath)
}
// Root returns root.
// Root delegates.
func (f FakeLoader) Root() string {
return f.delegate.Root()
}
@@ -77,12 +67,22 @@ func (f FakeLoader) New(newRoot string) (ifc.Loader, error) {
return FakeLoader{fs: f.fs, delegate: l}, nil
}
// Load performs load from a given location.
// Load delegates.
func (f FakeLoader) Load(location string) ([]byte, error) {
return f.delegate.Load(location)
}
// Cleanup does nothing
// Cleanup delegates.
func (f FakeLoader) Cleanup() error {
return nil
return f.delegate.Cleanup()
}
// Validator delegates.
func (f FakeLoader) Validator() ifc.Validator {
return f.delegate.Validator()
}
// LoadKvPairs delegates.
func (f FakeLoader) LoadKvPairs(args types.GeneratorArgs) ([]types.Pair, error) {
return f.delegate.LoadKvPairs(args)
}

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 configmapandsecret generates configmaps and secrets per generator rules.
package configmapandsecret
@@ -39,13 +26,13 @@ func makeFreshConfigMap(
// MakeConfigMap returns a new ConfigMap, or nil and an error.
func (f *Factory) MakeConfigMap(
args *types.ConfigMapArgs) (*v1.ConfigMap, error) {
all, err := f.loadKvPairs(args.GeneratorArgs)
all, err := f.ldr.LoadKvPairs(args.GeneratorArgs)
if err != nil {
return nil, err
}
cm := makeFreshConfigMap(args)
for _, p := range all {
err = addKvToConfigMap(cm, p.Key, p.Value)
err = f.addKvToConfigMap(cm, p)
if err != nil {
return nil, err
}
@@ -59,26 +46,26 @@ func (f *Factory) MakeConfigMap(
// addKvToConfigMap adds the given key and data to the given config map.
// Error if key invalid, or already exists.
func addKvToConfigMap(configMap *v1.ConfigMap, keyName, data string) error {
if err := errIfInvalidKey(keyName); err != nil {
func (f *Factory) addKvToConfigMap(configMap *v1.ConfigMap, p types.Pair) error {
if err := f.ldr.Validator().ErrIfInvalidKey(p.Key); err != nil {
return err
}
// If the configmap data contains byte sequences that are all in the UTF-8
// range, we will write it to .Data
if utf8.Valid([]byte(data)) {
if _, entryExists := configMap.Data[keyName]; entryExists {
return fmt.Errorf(keyExistsErrorMsg, keyName, configMap.Data)
if utf8.Valid([]byte(p.Value)) {
if _, entryExists := configMap.Data[p.Key]; entryExists {
return fmt.Errorf(keyExistsErrorMsg, p.Key, configMap.Data)
}
configMap.Data[keyName] = data
configMap.Data[p.Key] = p.Value
return nil
}
// otherwise, it's BinaryData
if configMap.BinaryData == nil {
configMap.BinaryData = map[string][]byte{}
}
if _, entryExists := configMap.BinaryData[keyName]; entryExists {
return fmt.Errorf(keyExistsErrorMsg, keyName, configMap.BinaryData)
if _, entryExists := configMap.BinaryData[p.Key]; entryExists {
return fmt.Errorf(keyExistsErrorMsg, p.Key, configMap.BinaryData)
}
configMap.BinaryData[keyName] = []byte(data)
configMap.BinaryData[p.Key] = []byte(p.Value)
return nil
}

View File

@@ -12,6 +12,7 @@ import (
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/types"
"sigs.k8s.io/kustomize/pkg/validators"
)
func makeEnvConfigMap(name string) *corev1.ConfigMap {
@@ -128,7 +129,7 @@ func TestConstructConfigMap(t *testing.T) {
fSys.WriteFile("/configmap/app.env", []byte("DB_USERNAME=admin\nDB_PASSWORD=somepw\n"))
fSys.WriteFile("/configmap/app-init.ini", []byte("FOO=bar\nBAR=baz\n"))
fSys.WriteFile("/configmap/app.bin", []byte{0xff, 0xfd})
ldr := loader.NewFileLoaderAtRoot(fSys)
ldr := loader.NewFileLoaderAtRoot(validators.MakeFakeValidator(), fSys)
for _, tc := range testCases {
f := NewFactory(ldr, tc.options)
cm, err := f.MakeConfigMap(&tc.input)

View File

@@ -4,12 +4,6 @@
package configmapandsecret
import (
"fmt"
"strings"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/util/validation"
"sigs.k8s.io/kustomize/k8sdeps/kv"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/types"
)
@@ -20,87 +14,10 @@ type Factory struct {
options *types.GeneratorOptions
}
// NewFactory returns a new Factory.
// NewFactory returns a new factory that makes ConfigMaps and Secrets.
func NewFactory(
l ifc.Loader, o *types.GeneratorOptions) *Factory {
return &Factory{ldr: l, options: o}
}
func (f *Factory) loadKvPairs(
args types.GeneratorArgs) (all []kv.Pair, err error) {
pairs, err := f.keyValuesFromEnvFiles(args.EnvSources)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf(
"env source files: %v",
args.EnvSources))
}
all = append(all, pairs...)
pairs, err = keyValuesFromLiteralSources(args.LiteralSources)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf(
"literal sources %v", args.LiteralSources))
}
all = append(all, pairs...)
pairs, err = f.keyValuesFromFileSources(args.FileSources)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf(
"file sources: %v", args.FileSources))
}
return append(all, pairs...), nil
ldr ifc.Loader, o *types.GeneratorOptions) *Factory {
return &Factory{ldr: ldr, options: o}
}
const keyExistsErrorMsg = "cannot add key %s, another key by that name already exists: %v"
func errIfInvalidKey(keyName string) error {
if errs := validation.IsConfigMapKey(keyName); len(errs) != 0 {
return fmt.Errorf("%q is not a valid key name: %s",
keyName, strings.Join(errs, ";"))
}
return nil
}
func keyValuesFromLiteralSources(sources []string) ([]kv.Pair, error) {
var kvs []kv.Pair
for _, s := range sources {
k, v, err := kv.ParseLiteralSource(s)
if err != nil {
return nil, err
}
kvs = append(kvs, kv.Pair{Key: k, Value: v})
}
return kvs, nil
}
func (f *Factory) keyValuesFromFileSources(sources []string) ([]kv.Pair, error) {
var kvs []kv.Pair
for _, s := range sources {
k, fPath, err := kv.ParseFileSource(s)
if err != nil {
return nil, err
}
content, err := f.ldr.Load(fPath)
if err != nil {
return nil, err
}
kvs = append(kvs, kv.Pair{Key: k, Value: string(content)})
}
return kvs, nil
}
func (f *Factory) keyValuesFromEnvFiles(paths []string) ([]kv.Pair, error) {
var kvs []kv.Pair
for _, path := range paths {
content, err := f.ldr.Load(path)
if err != nil {
return nil, err
}
more, err := kv.KeyValuesFromLines(content)
if err != nil {
return nil, err
}
kvs = append(kvs, more...)
}
return kvs, nil
}

View File

@@ -1,45 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package configmapandsecret
import (
"reflect"
"testing"
"sigs.k8s.io/kustomize/k8sdeps/kv"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/loader"
)
func TestKeyValuesFromFileSources(t *testing.T) {
tests := []struct {
description string
sources []string
expected []kv.Pair
}{
{
description: "create kvs from file sources",
sources: []string{"files/app-init.ini"},
expected: []kv.Pair{
{
Key: "app-init.ini",
Value: "FOO=bar",
},
},
},
}
fSys := fs.MakeFakeFS()
fSys.WriteFile("/files/app-init.ini", []byte("FOO=bar"))
bf := NewFactory(loader.NewFileLoaderAtRoot(fSys), nil)
for _, tc := range tests {
kvs, err := bf.keyValuesFromFileSources(tc.sources)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(kvs, tc.expected) {
t.Fatalf("in testcase: %q updated:\n%#v\ndoesn't match expected:\n%#v\n", tc.description, kvs, tc.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 configmapandsecret
@@ -41,13 +28,13 @@ func makeFreshSecret(
// MakeSecret returns a new secret.
func (f *Factory) MakeSecret(
args *types.SecretArgs) (*corev1.Secret, error) {
all, err := f.loadKvPairs(args.GeneratorArgs)
all, err := f.ldr.LoadKvPairs(args.GeneratorArgs)
if err != nil {
return nil, err
}
s := makeFreshSecret(args)
for _, p := range all {
err = addKvToSecret(s, p.Key, p.Value)
err = f.addKvToSecret(s, p.Key, p.Value)
if err != nil {
return nil, err
}
@@ -59,8 +46,8 @@ func (f *Factory) MakeSecret(
return s, nil
}
func addKvToSecret(secret *corev1.Secret, keyName, data string) error {
if err := errIfInvalidKey(keyName); err != nil {
func (f *Factory) addKvToSecret(secret *corev1.Secret, keyName, data string) error {
if err := f.ldr.Validator().ErrIfInvalidKey(keyName); err != nil {
return err
}
if _, entryExists := secret.Data[keyName]; entryExists {

View File

@@ -12,6 +12,7 @@ import (
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/types"
"sigs.k8s.io/kustomize/pkg/validators"
)
func makeEnvSecret(name string) *corev1.Secret {
@@ -125,7 +126,7 @@ func TestConstructSecret(t *testing.T) {
fSys := fs.MakeFakeFS()
fSys.WriteFile("/secret/app.env", []byte("DB_USERNAME=admin\nDB_PASSWORD=somepw\n"))
fSys.WriteFile("/secret/app-init.ini", []byte("FOO=bar\nBAR=baz\n"))
ldr := loader.NewFileLoaderAtRoot(fSys)
ldr := loader.NewFileLoaderAtRoot(validators.MakeFakeValidator(), fSys)
for _, tc := range testCases {
f := NewFactory(ldr, tc.options)
cm, err := f.MakeSecret(&tc.input)

View File

@@ -1,101 +0,0 @@
/*
Copyright 2019 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 kv
import (
"bufio"
"bytes"
"fmt"
"os"
"strings"
"unicode"
"unicode/utf8"
"k8s.io/apimachinery/pkg/util/validation"
)
type Pair struct {
Key string
Value string
}
var utf8bom = []byte{0xEF, 0xBB, 0xBF}
// KeyValuesFromLines parses given content in to a list of key-value pairs.
func KeyValuesFromLines(content []byte) ([]Pair, error) {
var kvs []Pair
scanner := bufio.NewScanner(bytes.NewReader(content))
currentLine := 0
for scanner.Scan() {
// Process the current line, retrieving a key/value pair if
// possible.
scannedBytes := scanner.Bytes()
kv, err := KeyValuesFromLine(scannedBytes, currentLine)
if err != nil {
return nil, err
}
currentLine++
if len(kv.Key) == 0 {
// no key means line was empty or a comment
continue
}
kvs = append(kvs, kv)
}
return kvs, nil
}
// KeyValuesFromLine returns a kv with blank key if the line is empty or a comment.
// The value will be retrieved from the environment if necessary.
func KeyValuesFromLine(line []byte, currentLine int) (Pair, error) {
kv := Pair{}
if !utf8.Valid(line) {
return kv, fmt.Errorf("line %d has invalid utf8 bytes : %v", line, string(line))
}
// We trim UTF8 BOM from the first line of the file but no others
if currentLine == 0 {
line = bytes.TrimPrefix(line, utf8bom)
}
// trim the line from all leading whitespace first
line = bytes.TrimLeftFunc(line, unicode.IsSpace)
// If the line is empty or a comment, we return a blank key/value pair.
if len(line) == 0 || line[0] == '#' {
return kv, nil
}
data := strings.SplitN(string(line), "=", 2)
key := data[0]
if errs := validation.IsEnvVarName(key); len(errs) != 0 {
return kv, fmt.Errorf("%q is not a valid key name: %s", key, strings.Join(errs, ";"))
}
if len(data) == 2 {
kv.Value = data[1]
} else {
// No value (no `=` in the line) is a signal to obtain the value
// from the environment.
kv.Value = os.Getenv(key)
}
kv.Key = key
return kv, nil
}

View File

@@ -1,68 +0,0 @@
/*
Copyright 2019 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 kv
import (
"reflect"
"testing"
)
func TestKeyValuesFromLines(t *testing.T) {
tests := []struct {
desc string
content string
expectedPairs []Pair
expectedErr bool
}{
{
desc: "valid kv content parse",
content: `
k1=v1
k2=v2
`,
expectedPairs: []Pair{
{Key: "k1", Value: "v1"},
{Key: "k2", Value: "v2"},
},
expectedErr: false,
},
{
desc: "content with comments",
content: `
k1=v1
#k2=v2
`,
expectedPairs: []Pair{
{Key: "k1", Value: "v1"},
},
expectedErr: false,
},
// TODO: add negative testcases
}
for _, test := range tests {
pairs, err := KeyValuesFromLines([]byte(test.content))
if test.expectedErr && err == nil {
t.Fatalf("%s should not return error", test.desc)
}
if !reflect.DeepEqual(pairs, test.expectedPairs) {
t.Errorf("%s should succeed, got:%v exptected:%v", test.desc, pairs, test.expectedPairs)
}
}
}

View File

@@ -1,65 +0,0 @@
/*
Copyright 2019 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 kv
import (
"errors"
"fmt"
"path"
"strings"
)
// ParseFileSource parses the source given.
//
// Acceptable formats include:
// 1. source-path: the basename will become the key name
// 2. source-name=source-path: the source-name will become the key name and
// source-path is the path to the key file.
//
// Key names cannot include '='.
func ParseFileSource(source string) (keyName, filePath string, err error) {
numSeparators := strings.Count(source, "=")
switch {
case numSeparators == 0:
return path.Base(source), source, nil
case numSeparators == 1 && strings.HasPrefix(source, "="):
return "", "", fmt.Errorf("key name for file path %v missing", strings.TrimPrefix(source, "="))
case numSeparators == 1 && strings.HasSuffix(source, "="):
return "", "", fmt.Errorf("file path for key name %v missing", strings.TrimSuffix(source, "="))
case numSeparators > 1:
return "", "", errors.New("key names or file paths cannot contain '='")
default:
components := strings.Split(source, "=")
return components[0], components[1], nil
}
}
// ParseLiteralSource parses the source key=val pair into its component pieces.
// This functionality is distinguished from strings.SplitN(source, "=", 2) since
// it returns an error in the case of empty keys, values, or a missing equals sign.
func ParseLiteralSource(source string) (keyName, value string, err error) {
// leading equal is invalid
if strings.Index(source, "=") == 0 {
return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source)
}
// split after the first equal (so values can have the = character)
items := strings.SplitN(source, "=", 2)
if len(items) != 2 {
return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source)
}
return items[0], strings.Trim(items[1], "\"'"), 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 transformer provides transformer factory
package transformer
@@ -21,6 +8,7 @@ import (
"sigs.k8s.io/kustomize/k8sdeps/transformer/hash"
"sigs.k8s.io/kustomize/k8sdeps/transformer/inventory"
"sigs.k8s.io/kustomize/k8sdeps/transformer/patch"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/transformers"
"sigs.k8s.io/kustomize/pkg/types"
@@ -48,7 +36,8 @@ func (p *FactoryImpl) MakeHashTransformer() transformers.Transformer {
func (p *FactoryImpl) MakeInventoryTransformer(
arg *types.Inventory,
ldr ifc.Loader,
namespace string,
gp types.GarbagePolicy) transformers.Transformer {
return inventory.NewInventoryTransformer(arg, namespace, gp)
return inventory.NewInventoryTransformer(arg, ldr, namespace, gp)
}

View File

@@ -8,6 +8,7 @@ import (
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/k8sdeps/transformer/hash"
"sigs.k8s.io/kustomize/pkg/gvk"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/inventory"
"sigs.k8s.io/kustomize/pkg/resid"
"sigs.k8s.io/kustomize/pkg/resmap"
@@ -19,6 +20,7 @@ import (
// inventoryTransformer compute the inventory object used in prune
type inventoryTransformer struct {
garbagePolicy types.GarbagePolicy
ldr ifc.Loader
cmName string
cmNamespace string
}
@@ -28,6 +30,7 @@ var _ transformers.Transformer = &inventoryTransformer{}
// NewInventoryTransformer makes a inventoryTransformer.
func NewInventoryTransformer(
p *types.Inventory,
ldr ifc.Loader,
namespace string,
gp types.GarbagePolicy) transformers.Transformer {
if p == nil || p.Type != "ConfigMap" || p.ConfigMap.Namespace != namespace {
@@ -35,6 +38,7 @@ func NewInventoryTransformer(
}
return &inventoryTransformer{
garbagePolicy: gp,
ldr: ldr,
cmName: p.ConfigMap.Name,
cmNamespace: p.ConfigMap.Namespace,
}
@@ -82,7 +86,7 @@ func (o *inventoryTransformer) Transform(m resmap.ResMap) error {
}
kf := kunstruct.NewKunstructuredFactoryImpl()
k, err := kf.MakeConfigMap(nil, opts, args)
k, err := kf.MakeConfigMap(o.ldr, opts, args)
if err != nil {
return err
}

View File

@@ -1,18 +1,5 @@
/*
Copyright 2019 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 inventory
@@ -21,11 +8,14 @@ import (
"testing"
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/gvk"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/resid"
"sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/types"
"sigs.k8s.io/kustomize/pkg/validators"
)
var secret = gvk.Gvk{Version: "v1", Kind: "Secret"}
@@ -106,6 +96,7 @@ func makeResMap() resmap.ResMap {
func TestInventoryTransformer(t *testing.T) {
rf := resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl())
ldr := loader.NewFileLoaderAtCwd(validators.MakeFakeValidator(), fs.MakeFakeFS())
// hash is derived based on all keys in the Inventory
// It is added to annotations as
@@ -148,7 +139,7 @@ func TestInventoryTransformer(t *testing.T) {
objs := makeResMap()
// include the original resmap; only return the ConfigMap for pruning
tran := NewInventoryTransformer(p, "default", types.GarbageCollect)
tran := NewInventoryTransformer(p, ldr, "default", types.GarbageCollect)
tran.Transform(objs)
if !reflect.DeepEqual(objs, expected) {
@@ -160,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, "default", types.GarbageIgnore)
tran = NewInventoryTransformer(p, ldr, "default", types.GarbageIgnore)
tran.Transform(objs)
if !reflect.DeepEqual(objs, expected) {

View File

@@ -1,24 +1,15 @@
/*
Copyright 2018 The Kubernetes Authors.
// Copyright 2019 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 validator provides functions to validate labels, annotations, namespace using apimachinery
// Package validator provides functions to validate labels, annotations,
// namespaces and configmap/secret keys using apimachinery functions.
package validator
import (
"errors"
"fmt"
"strings"
apivalidation "k8s.io/apimachinery/pkg/api/validation"
v1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/util/validation"
@@ -33,6 +24,24 @@ func NewKustValidator() *KustValidator {
return &KustValidator{}
}
func (v *KustValidator) ErrIfInvalidKey(k string) error {
if errs := validation.IsConfigMapKey(k); len(errs) != 0 {
return fmt.Errorf(
"%q is not a valid key name: %s",
k, strings.Join(errs, ";"))
}
return nil
}
func (v *KustValidator) IsEnvVarName(k string) error {
if errs := validation.IsEnvVarName(k); len(errs) != 0 {
return fmt.Errorf(
"%q is not a valid key name: %s",
k, strings.Join(errs, ";"))
}
return nil
}
// MakeAnnotationValidator returns a MapValidatorFunc using apimachinery.
func (v *KustValidator) MakeAnnotationValidator() func(map[string]string) error {
return func(x map[string]string) error {

View File

@@ -9,6 +9,8 @@ import (
"path/filepath"
"strings"
"sigs.k8s.io/kustomize/pkg/ifc"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/pkg/fs"
@@ -54,8 +56,8 @@ url examples:
// NewCmdBuild creates a new build command.
func NewCmdBuild(
out io.Writer, fs fs.FileSystem,
rf *resmap.Factory,
out io.Writer, fSys fs.FileSystem,
v ifc.Validator, rf *resmap.Factory,
ptf transformer.Factory) *cobra.Command {
var o Options
@@ -72,7 +74,7 @@ func NewCmdBuild(
if err != nil {
return err
}
return o.RunBuild(out, fs, rf, ptf, pl)
return o.RunBuild(out, v, fSys, rf, ptf, pl)
},
}
@@ -84,7 +86,7 @@ func NewCmdBuild(
plugins.AddEnablePluginsFlag(
cmd.Flags(), &pluginConfig.Enabled)
cmd.AddCommand(NewCmdBuildPrune(out, fs, rf, ptf, pl))
cmd.AddCommand(NewCmdBuildPrune(out, v, fSys, rf, ptf, pl))
return cmd
}
@@ -105,11 +107,11 @@ func (o *Options) Validate(args []string) (err error) {
// RunBuild runs build command.
func (o *Options) RunBuild(
out io.Writer, fSys fs.FileSystem,
out io.Writer, v ifc.Validator, fSys fs.FileSystem,
rf *resmap.Factory, ptf transformer.Factory,
pl *plugins.Loader) error {
ldr, err := loader.NewLoader(
o.loadRestrictor, o.kustomizationPath, fSys)
o.loadRestrictor, v, o.kustomizationPath, fSys)
if err != nil {
return err
}
@@ -126,11 +128,11 @@ func (o *Options) RunBuild(
}
func (o *Options) RunBuildPrune(
out io.Writer, fSys fs.FileSystem,
out io.Writer, v ifc.Validator, fSys fs.FileSystem,
rf *resmap.Factory, ptf transformer.Factory,
pl *plugins.Loader) error {
ldr, err := loader.NewLoader(
o.loadRestrictor, o.kustomizationPath, fSys)
o.loadRestrictor, v, o.kustomizationPath, fSys)
if err != nil {
return err
}
@@ -163,9 +165,8 @@ func (o *Options) emitResources(
}
func NewCmdBuildPrune(
out io.Writer, fs fs.FileSystem,
rf *resmap.Factory,
ptf transformer.Factory,
out io.Writer, v ifc.Validator, fSys fs.FileSystem,
rf *resmap.Factory, ptf transformer.Factory,
pl *plugins.Loader) *cobra.Command {
var o Options
@@ -179,7 +180,7 @@ func NewCmdBuildPrune(
if err != nil {
return err
}
return o.RunBuildPrune(out, fs, rf, ptf, pl)
return o.RunBuildPrune(out, v, fSys, rf, ptf, pl)
},
}
return cmd

View File

@@ -37,11 +37,12 @@ See https://sigs.k8s.io/kustomize
uf := kunstruct.NewKunstructuredFactoryImpl()
rf := resmap.NewFactory(resource.NewFactory(uf))
v := validator.NewKustValidator()
c.AddCommand(
build.NewCmdBuild(
stdOut, fSys,
stdOut, fSys, v,
rf, transformer.NewFactoryImpl()),
edit.NewCmdEdit(fSys, validator.NewKustValidator(), uf),
edit.NewCmdEdit(fSys, v, uf),
misc.NewCmdConfig(fSys),
misc.NewCmdVersion(stdOut),
)

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 add
@@ -23,7 +10,10 @@ import (
)
// NewCmdAdd returns an instance of 'add' subcommand.
func NewCmdAdd(fsys fs.FileSystem, v ifc.Validator, kf ifc.KunstructuredFactory) *cobra.Command {
func NewCmdAdd(
fSys fs.FileSystem,
ldr ifc.Loader,
kf ifc.KunstructuredFactory) *cobra.Command {
c := &cobra.Command{
Use: "add",
Short: "Adds an item to the kustomization file.",
@@ -54,13 +44,13 @@ func NewCmdAdd(fsys fs.FileSystem, v ifc.Validator, kf ifc.KunstructuredFactory)
Args: cobra.MinimumNArgs(1),
}
c.AddCommand(
newCmdAddResource(fsys),
newCmdAddPatch(fsys),
newCmdAddSecret(fsys, kf),
newCmdAddConfigMap(fsys, kf),
newCmdAddBase(fsys),
newCmdAddLabel(fsys, v.MakeLabelValidator()),
newCmdAddAnnotation(fsys, v.MakeAnnotationValidator()),
newCmdAddResource(fSys),
newCmdAddPatch(fSys),
newCmdAddSecret(fSys, ldr, kf),
newCmdAddConfigMap(fSys, ldr, kf),
newCmdAddBase(fSys),
newCmdAddLabel(fSys, ldr.Validator().MakeLabelValidator()),
newCmdAddAnnotation(fSys, ldr.Validator().MakeAnnotationValidator()),
)
return c
}

View File

@@ -8,12 +8,14 @@ import (
"sigs.k8s.io/kustomize/pkg/commands/kustfile"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/types"
)
// newCmdAddConfigMap returns a new command.
func newCmdAddConfigMap(fSys fs.FileSystem, kf ifc.KunstructuredFactory) *cobra.Command {
func newCmdAddConfigMap(
fSys fs.FileSystem,
ldr ifc.Loader,
kf ifc.KunstructuredFactory) *cobra.Command {
var flags flagsAndArgs
cmd := &cobra.Command{
Use: "configmap NAME [--from-file=[key=]source] [--from-literal=key1=value1]",
@@ -52,8 +54,7 @@ func newCmdAddConfigMap(fSys fs.FileSystem, kf ifc.KunstructuredFactory) *cobra.
}
// Add the flagsAndArgs map to the kustomization file.
err = addConfigMap(
loader.NewFileLoaderAtCwd(fSys), kustomization, flags, kf)
err = addConfigMap(ldr, kustomization, flags, kf)
if err != nil {
return err
}

View File

@@ -7,11 +7,15 @@ import (
"testing"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/types"
"sigs.k8s.io/kustomize/pkg/validators"
)
func TestNewAddConfigMapIsNotNil(t *testing.T) {
if newCmdAddConfigMap(fs.MakeFakeFS(), nil) == nil {
fSys := fs.MakeFakeFS()
ldr := loader.NewFileLoaderAtCwd(validators.MakeFakeValidator(), fSys)
if newCmdAddConfigMap(fSys, ldr, nil) == nil {
t.Fatal("newCmdAddConfigMap shouldn't be nil")
}
}

View File

@@ -8,12 +8,14 @@ import (
"sigs.k8s.io/kustomize/pkg/commands/kustfile"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/types"
)
// newCmdAddSecret returns a new command.
func newCmdAddSecret(fSys fs.FileSystem, kf ifc.KunstructuredFactory) *cobra.Command {
func newCmdAddSecret(
fSys fs.FileSystem,
ldr ifc.Loader,
kf ifc.KunstructuredFactory) *cobra.Command {
var flags flagsAndArgs
cmd := &cobra.Command{
Use: "secret NAME [--from-file=[key=]source] [--from-literal=key1=value1] [--type=Opaque|kubernetes.io/tls]",
@@ -52,8 +54,7 @@ func newCmdAddSecret(fSys fs.FileSystem, kf ifc.KunstructuredFactory) *cobra.Com
}
// Add the flagsAndArgs map to the kustomization file.
err = addSecret(
loader.NewFileLoaderAtCwd(fSys), kustomization, flags, kf)
err = addSecret(ldr, kustomization, flags, kf)
if err != nil {
return err
}

View File

@@ -7,11 +7,15 @@ import (
"testing"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/types"
"sigs.k8s.io/kustomize/pkg/validators"
)
func TestNewCmdAddSecretIsNotNil(t *testing.T) {
if newCmdAddSecret(fs.MakeFakeFS(), nil) == nil {
fSys := fs.MakeFakeFS()
ldr := loader.NewFileLoaderAtCwd(validators.MakeFakeValidator(), fSys)
if newCmdAddSecret(fSys, ldr, nil) == nil {
t.Fatal("newCmdAddSecret shouldn't be nil")
}
}

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 edit
@@ -24,10 +11,12 @@ import (
"sigs.k8s.io/kustomize/pkg/commands/edit/set"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/loader"
)
// NewCmdEdit returns an instance of 'edit' subcommand.
func NewCmdEdit(fsys fs.FileSystem, v ifc.Validator, kf ifc.KunstructuredFactory) *cobra.Command {
func NewCmdEdit(
fSys fs.FileSystem, v ifc.Validator, kf ifc.KunstructuredFactory) *cobra.Command {
c := &cobra.Command{
Use: "edit",
Short: "Edits a kustomization file",
@@ -44,11 +33,12 @@ func NewCmdEdit(fsys fs.FileSystem, v ifc.Validator, kf ifc.KunstructuredFactory
`,
Args: cobra.MinimumNArgs(1),
}
c.AddCommand(
add.NewCmdAdd(fsys, v, kf),
set.NewCmdSet(fsys, v),
fix.NewCmdFix(fsys),
remove.NewCmdRemove(fsys),
add.NewCmdAdd(fSys, loader.NewFileLoaderAtCwd(v, fSys), kf),
set.NewCmdSet(fSys, v),
fix.NewCmdFix(fSys),
remove.NewCmdRemove(fSys),
)
return c
}

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 ifc holds miscellaneous interfaces used by kustomize.
package ifc
@@ -27,6 +14,8 @@ type Validator interface {
MakeAnnotationValidator() func(map[string]string) error
MakeLabelValidator() func(map[string]string) error
ValidateNamespace(string) []string
ErrIfInvalidKey(string) error
IsEnvVarName(k string) error
}
// Loader interface exposes methods to read bytes.
@@ -39,6 +28,10 @@ type Loader interface {
Load(location string) ([]byte, error)
// Cleanup cleans the loader
Cleanup() error
// Validator validates data for use in various k8s fields.
Validator() Validator
// Loads pairs.
LoadKvPairs(args types.GeneratorArgs) ([]types.Pair, error)
}
// Kunstructured allows manipulation of k8s objects

View File

@@ -1,23 +1,11 @@
/*
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 holds miscellaneous interfaces used by kustomize.
package transformer
import (
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/transformers"
"sigs.k8s.io/kustomize/pkg/types"
@@ -29,6 +17,7 @@ type Factory interface {
MakeHashTransformer() transformers.Transformer
MakeInventoryTransformer(
p *types.Inventory,
ldr ifc.Loader,
namespace string,
gp types.GarbagePolicy) transformers.Transformer
}

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 loader
@@ -92,6 +79,9 @@ type fileLoader struct {
// Restricts behavior of Load function.
loadRestrictor LoadRestrictorFunc
// Used to validate various k8s data fields.
validator ifc.Validator
// If this is non-nil, the files were
// obtained from the given repository.
repoSpec *git.RepoSpec
@@ -110,41 +100,44 @@ const CWD = "."
// NewFileLoaderAtCwd returns a loader that loads from ".".
// A convenience for kustomize edit commands.
func NewFileLoaderAtCwd(fSys fs.FileSystem) *fileLoader {
func NewFileLoaderAtCwd(v ifc.Validator, fSys fs.FileSystem) *fileLoader {
return newLoaderOrDie(
RestrictionRootOnly, fSys, CWD)
RestrictionRootOnly, v, fSys, CWD)
}
// NewFileLoaderAtRoot returns a loader that loads from "/".
// A convenience for tests.
func NewFileLoaderAtRoot(fSys fs.FileSystem) *fileLoader {
func NewFileLoaderAtRoot(v ifc.Validator, fSys fs.FileSystem) *fileLoader {
return newLoaderOrDie(
RestrictionRootOnly, fSys, string(filepath.Separator))
RestrictionRootOnly, v, fSys, string(filepath.Separator))
}
// Root returns the absolute path that is prepended to any
// relative paths used in Load.
func (l *fileLoader) Root() string {
return l.root.String()
func (fl *fileLoader) Root() string {
return fl.root.String()
}
func newLoaderOrDie(
lr LoadRestrictorFunc, fSys fs.FileSystem, path string) *fileLoader {
lr LoadRestrictorFunc, v ifc.Validator,
fSys fs.FileSystem, path string) *fileLoader {
root, err := demandDirectoryRoot(fSys, path)
if err != nil {
log.Fatalf("unable to make loader at '%s'; %v", path, err)
}
return newLoaderAtConfirmedDir(
lr, root, fSys, nil, git.ClonerUsingGitExec)
lr, v, root, fSys, nil, git.ClonerUsingGitExec)
}
// newLoaderAtConfirmedDir returns a new fileLoader with given root.
func newLoaderAtConfirmedDir(
lr LoadRestrictorFunc,
v ifc.Validator,
root fs.ConfirmedDir, fSys fs.FileSystem,
referrer *fileLoader, cloner git.Cloner) *fileLoader {
return &fileLoader{
loadRestrictor: lr,
validator: v,
root: root,
referrer: referrer,
fSys: fSys,
@@ -175,39 +168,41 @@ func demandDirectoryRoot(
// New returns a new Loader, rooted relative to current loader,
// or rooted in a temp directory holding a git repo clone.
func (l *fileLoader) New(path string) (ifc.Loader, error) {
func (fl *fileLoader) New(path string) (ifc.Loader, error) {
if path == "" {
return nil, fmt.Errorf("new root cannot be empty")
}
repoSpec, err := git.NewRepoSpecFromUrl(path)
if err == nil {
// Treat this as git repo clone request.
if err := l.errIfRepoCycle(repoSpec); err != nil {
if err := fl.errIfRepoCycle(repoSpec); err != nil {
return nil, err
}
return newLoaderAtGitClone(repoSpec, l.fSys, l.referrer, l.cloner)
return newLoaderAtGitClone(
repoSpec, fl.validator, fl.fSys, fl.referrer, fl.cloner)
}
if filepath.IsAbs(path) {
return nil, fmt.Errorf("new root '%s' cannot be absolute", path)
}
root, err := demandDirectoryRoot(l.fSys, l.root.Join(path))
root, err := demandDirectoryRoot(fl.fSys, fl.root.Join(path))
if err != nil {
return nil, err
}
if err := l.errIfGitContainmentViolation(root); err != nil {
if err := fl.errIfGitContainmentViolation(root); err != nil {
return nil, err
}
if err := l.errIfArgEqualOrHigher(root); err != nil {
if err := fl.errIfArgEqualOrHigher(root); err != nil {
return nil, err
}
return newLoaderAtConfirmedDir(
l.loadRestrictor, root, l.fSys, l, l.cloner), nil
fl.loadRestrictor, fl.validator, root, fl.fSys, fl, fl.cloner), nil
}
// newLoaderAtGitClone returns a new Loader pinned to a temporary
// directory holding a cloned git repo.
func newLoaderAtGitClone(
repoSpec *git.RepoSpec, fSys fs.FileSystem,
repoSpec *git.RepoSpec,
v ifc.Validator, fSys fs.FileSystem,
referrer *fileLoader, cloner git.Cloner) (ifc.Loader, error) {
err := cloner(repoSpec)
if err != nil {
@@ -229,6 +224,7 @@ func newLoaderAtGitClone(
return &fileLoader{
// Clones never allowed to escape root.
loadRestrictor: RestrictionRootOnly,
validator: v,
root: root,
referrer: referrer,
repoSpec: repoSpec,
@@ -238,9 +234,9 @@ func newLoaderAtGitClone(
}, nil
}
func (l *fileLoader) errIfGitContainmentViolation(
func (fl *fileLoader) errIfGitContainmentViolation(
base fs.ConfirmedDir) error {
containingRepo := l.containingRepo()
containingRepo := fl.containingRepo()
if containingRepo == nil {
return nil
}
@@ -256,64 +252,64 @@ func (l *fileLoader) errIfGitContainmentViolation(
// Looks back through referrers for a git repo, returning nil
// if none found.
func (l *fileLoader) containingRepo() *git.RepoSpec {
if l.repoSpec != nil {
return l.repoSpec
func (fl *fileLoader) containingRepo() *git.RepoSpec {
if fl.repoSpec != nil {
return fl.repoSpec
}
if l.referrer == nil {
if fl.referrer == nil {
return nil
}
return l.referrer.containingRepo()
return fl.referrer.containingRepo()
}
// errIfArgEqualOrHigher tests whether the argument,
// is equal to or above the root of any ancestor.
func (l *fileLoader) errIfArgEqualOrHigher(
func (fl *fileLoader) errIfArgEqualOrHigher(
candidateRoot fs.ConfirmedDir) error {
if l.root.HasPrefix(candidateRoot) {
if fl.root.HasPrefix(candidateRoot) {
return fmt.Errorf(
"cycle detected: candidate root '%s' contains visited root '%s'",
candidateRoot, l.root)
candidateRoot, fl.root)
}
if l.referrer == nil {
if fl.referrer == nil {
return nil
}
return l.referrer.errIfArgEqualOrHigher(candidateRoot)
return fl.referrer.errIfArgEqualOrHigher(candidateRoot)
}
// TODO(monopole): Distinguish branches?
// I.e. Allow a distinction between git URI with
// path foo and tag bar and a git URI with the same
// path but a different tag?
func (l *fileLoader) errIfRepoCycle(newRepoSpec *git.RepoSpec) error {
func (fl *fileLoader) errIfRepoCycle(newRepoSpec *git.RepoSpec) error {
// TODO(monopole): Use parsed data instead of Raw().
if l.repoSpec != nil &&
strings.HasPrefix(l.repoSpec.Raw(), newRepoSpec.Raw()) {
if fl.repoSpec != nil &&
strings.HasPrefix(fl.repoSpec.Raw(), newRepoSpec.Raw()) {
return fmt.Errorf(
"cycle detected: URI '%s' referenced by previous URI '%s'",
newRepoSpec.Raw(), l.repoSpec.Raw())
newRepoSpec.Raw(), fl.repoSpec.Raw())
}
if l.referrer == nil {
if fl.referrer == nil {
return nil
}
return l.referrer.errIfRepoCycle(newRepoSpec)
return fl.referrer.errIfRepoCycle(newRepoSpec)
}
// Load returns the content of file at the given path,
// else an error. Relative paths are taken relative
// to the root.
func (l *fileLoader) Load(path string) ([]byte, error) {
func (fl *fileLoader) Load(path string) ([]byte, error) {
if !filepath.IsAbs(path) {
path = l.root.Join(path)
path = fl.root.Join(path)
}
path, err := l.loadRestrictor(l.fSys, l.root, path)
path, err := fl.loadRestrictor(fl.fSys, fl.root, path)
if err != nil {
return nil, err
}
return l.fSys.ReadFile(path)
return fl.fSys.ReadFile(path)
}
// Cleanup runs the cleaner.
func (l *fileLoader) Cleanup() error {
return l.cleaner()
func (fl *fileLoader) Cleanup() error {
return fl.cleaner()
}

View File

@@ -29,6 +29,7 @@ import (
"sigs.k8s.io/kustomize/pkg/git"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/pgmconfig"
"sigs.k8s.io/kustomize/pkg/validators"
)
type testData struct {
@@ -63,8 +64,12 @@ func MakeFakeFs(td []testData) fs.FileSystem {
return fSys
}
func makeLoader() *fileLoader {
return NewFileLoaderAtRoot(validators.MakeFakeValidator(), MakeFakeFs(testCases))
}
func TestLoaderLoad(t *testing.T) {
l1 := NewFileLoaderAtRoot(MakeFakeFs(testCases))
l1 := makeLoader()
if "/" != l1.Root() {
t.Fatalf("incorrect root: '%s'\n", l1.Root())
}
@@ -103,7 +108,7 @@ func TestLoaderLoad(t *testing.T) {
}
func TestLoaderNewSubDir(t *testing.T) {
l1, err := NewFileLoaderAtRoot(MakeFakeFs(testCases)).New("foo/project")
l1, err := makeLoader().New("foo/project")
if err != nil {
t.Fatalf("unexpected err: %v\n", err)
}
@@ -125,7 +130,7 @@ func TestLoaderNewSubDir(t *testing.T) {
}
func TestLoaderBadRelative(t *testing.T) {
l1, err := NewFileLoaderAtRoot(MakeFakeFs(testCases)).New("foo/project/subdir1")
l1, err := makeLoader().New("foo/project/subdir1")
if err != nil {
t.Fatalf("unexpected err: %v\n", err)
}
@@ -195,7 +200,7 @@ func TestLoaderBadRelative(t *testing.T) {
}
func TestLoaderMisc(t *testing.T) {
l := NewFileLoaderAtRoot(MakeFakeFs(testCases))
l := makeLoader()
_, err := l.New("")
if err == nil {
t.Fatalf("Expected error for empty root location not returned")
@@ -297,7 +302,8 @@ func TestRestrictionRootOnlyInRealLoader(t *testing.T) {
var l ifc.Loader
l = newLoaderOrDie(RestrictionRootOnly, fSys, dir)
l = newLoaderOrDie(
RestrictionRootOnly, validators.MakeFakeValidator(), fSys, dir)
l = doSanityChecksAndDropIntoBase(t, l)
@@ -330,7 +336,8 @@ func TestRestrictionNoneInRealLoader(t *testing.T) {
var l ifc.Loader
l = newLoaderOrDie(RestrictionNone, fSys, dir)
l = newLoaderOrDie(
RestrictionNone, validators.MakeFakeValidator(), fSys, dir)
l = doSanityChecksAndDropIntoBase(t, l)
@@ -392,7 +399,7 @@ whatever
t.Fatalf("unexpected err: %v\n", err)
}
l, err := newLoaderAtGitClone(
repoSpec, fSys, nil,
repoSpec, validators.MakeFakeValidator(), fSys, nil,
git.DoNothingCloner(fs.ConfirmedDir(coRoot)))
if err != nil {
t.Fatalf("unexpected err: %v\n", err)
@@ -434,7 +441,8 @@ func TestLoaderDisallowsLocalBaseFromRemoteOverlay(t *testing.T) {
// Establish that a local overlay can navigate
// to the local bases.
l1 = newLoaderOrDie(RestrictionRootOnly, fSys, cloneRoot+"/foo/overlay")
l1 = newLoaderOrDie(
RestrictionRootOnly, validators.MakeFakeValidator(), fSys, cloneRoot+"/foo/overlay")
if l1.Root() != cloneRoot+"/foo/overlay" {
t.Fatalf("unexpected root %s", l1.Root())
}
@@ -470,7 +478,7 @@ func TestLoaderDisallowsLocalBaseFromRemoteOverlay(t *testing.T) {
t.Fatalf("unexpected err: %v\n", err)
}
l1, err = newLoaderAtGitClone(
repoSpec, fSys, nil,
repoSpec, validators.MakeFakeValidator(), fSys, nil,
git.DoNothingCloner(fs.ConfirmedDir(cloneRoot)))
if err != nil {
t.Fatalf("unexpected err: %v\n", err)
@@ -509,7 +517,7 @@ func TestLocalLoaderReferencingGitBase(t *testing.T) {
t.Fatalf("unexpected err: %v\n", err)
}
l1 := newLoaderAtConfirmedDir(
RestrictionRootOnly, root, fSys, nil,
RestrictionRootOnly, validators.MakeFakeValidator(), root, fSys, nil,
git.DoNothingCloner(fs.ConfirmedDir(cloneRoot)))
if l1.Root() != topDir {
t.Fatalf("unexpected root %s", l1.Root())

200
pkg/loader/kv.go Normal file
View File

@@ -0,0 +1,200 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package loader
import (
"bufio"
"bytes"
"fmt"
"os"
"path"
"strings"
"unicode"
"unicode/utf8"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/types"
)
var utf8bom = []byte{0xEF, 0xBB, 0xBF}
func (fl *fileLoader) Validator() ifc.Validator {
return fl.validator
}
func (fl *fileLoader) LoadKvPairs(
args types.GeneratorArgs) (all []types.Pair, err error) {
pairs, err := fl.keyValuesFromEnvFiles(args.EnvSources)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf(
"env source files: %v",
args.EnvSources))
}
all = append(all, pairs...)
pairs, err = keyValuesFromLiteralSources(args.LiteralSources)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf(
"literal sources %v", args.LiteralSources))
}
all = append(all, pairs...)
pairs, err = fl.keyValuesFromFileSources(args.FileSources)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf(
"file sources: %v", args.FileSources))
}
return append(all, pairs...), nil
}
func keyValuesFromLiteralSources(sources []string) ([]types.Pair, error) {
var kvs []types.Pair
for _, s := range sources {
k, v, err := parseLiteralSource(s)
if err != nil {
return nil, err
}
kvs = append(kvs, types.Pair{Key: k, Value: v})
}
return kvs, nil
}
func (fl *fileLoader) keyValuesFromFileSources(sources []string) ([]types.Pair, error) {
var kvs []types.Pair
for _, s := range sources {
k, fPath, err := parseFileSource(s)
if err != nil {
return nil, err
}
content, err := fl.Load(fPath)
if err != nil {
return nil, err
}
kvs = append(kvs, types.Pair{Key: k, Value: string(content)})
}
return kvs, nil
}
func (fl *fileLoader) keyValuesFromEnvFiles(paths []string) ([]types.Pair, error) {
var kvs []types.Pair
for _, p := range paths {
content, err := fl.Load(p)
if err != nil {
return nil, err
}
more, err := fl.keyValuesFromLines(content)
if err != nil {
return nil, err
}
kvs = append(kvs, more...)
}
return kvs, nil
}
// keyValuesFromLines parses given content in to a list of key-value pairs.
func (fl *fileLoader) keyValuesFromLines(content []byte) ([]types.Pair, error) {
var kvs []types.Pair
scanner := bufio.NewScanner(bytes.NewReader(content))
currentLine := 0
for scanner.Scan() {
// Process the current line, retrieving a key/value pair if
// possible.
scannedBytes := scanner.Bytes()
kv, err := fl.keyValuesFromLine(scannedBytes, currentLine)
if err != nil {
return nil, err
}
currentLine++
if len(kv.Key) == 0 {
// no key means line was empty or a comment
continue
}
kvs = append(kvs, kv)
}
return kvs, nil
}
// KeyValuesFromLine returns a kv with blank key if the line is empty or a comment.
// The value will be retrieved from the environment if necessary.
func (fl *fileLoader) keyValuesFromLine(line []byte, currentLine int) (types.Pair, error) {
kv := types.Pair{}
if !utf8.Valid(line) {
return kv, fmt.Errorf("line %d has invalid utf8 bytes : %v", line, string(line))
}
// We trim UTF8 BOM from the first line of the file but no others
if currentLine == 0 {
line = bytes.TrimPrefix(line, utf8bom)
}
// trim the line from all leading whitespace first
line = bytes.TrimLeftFunc(line, unicode.IsSpace)
// If the line is empty or a comment, we return a blank key/value pair.
if len(line) == 0 || line[0] == '#' {
return kv, nil
}
data := strings.SplitN(string(line), "=", 2)
key := data[0]
if err := fl.validator.IsEnvVarName(key); err != nil {
return kv, err
}
if len(data) == 2 {
kv.Value = data[1]
} else {
// No value (no `=` in the line) is a signal to obtain the value
// from the environment.
kv.Value = os.Getenv(key)
}
kv.Key = key
return kv, nil
}
// ParseFileSource parses the source given.
//
// Acceptable formats include:
// 1. source-path: the basename will become the key name
// 2. source-name=source-path: the source-name will become the key name and
// source-path is the path to the key file.
//
// Key names cannot include '='.
func parseFileSource(source string) (keyName, filePath string, err error) {
numSeparators := strings.Count(source, "=")
switch {
case numSeparators == 0:
return path.Base(source), source, nil
case numSeparators == 1 && strings.HasPrefix(source, "="):
return "", "", fmt.Errorf("key name for file path %v missing", strings.TrimPrefix(source, "="))
case numSeparators == 1 && strings.HasSuffix(source, "="):
return "", "", fmt.Errorf("file path for key name %v missing", strings.TrimSuffix(source, "="))
case numSeparators > 1:
return "", "", errors.New("key names or file paths cannot contain '='")
default:
components := strings.Split(source, "=")
return components[0], components[1], nil
}
}
// ParseLiteralSource parses the source key=val pair into its component pieces.
// This functionality is distinguished from strings.SplitN(source, "=", 2) since
// it returns an error in the case of empty keys, values, or a missing equals sign.
func parseLiteralSource(source string) (keyName, value string, err error) {
// leading equal is invalid
if strings.Index(source, "=") == 0 {
return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source)
}
// split after the first equal (so values can have the = character)
items := strings.SplitN(source, "=", 2)
if len(items) != 2 {
return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source)
}
return items[0], strings.Trim(items[1], "\"'"), nil
}

91
pkg/loader/kv_test.go Normal file
View File

@@ -0,0 +1,91 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package loader
import (
"reflect"
"testing"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/types"
"sigs.k8s.io/kustomize/pkg/validators"
)
func TestKeyValuesFromLines(t *testing.T) {
tests := []struct {
desc string
content string
expectedPairs []types.Pair
expectedErr bool
}{
{
desc: "valid kv content parse",
content: `
k1=v1
k2=v2
`,
expectedPairs: []types.Pair{
{Key: "k1", Value: "v1"},
{Key: "k2", Value: "v2"},
},
expectedErr: false,
},
{
desc: "content with comments",
content: `
k1=v1
#k2=v2
`,
expectedPairs: []types.Pair{
{Key: "k1", Value: "v1"},
},
expectedErr: false,
},
// TODO: add negative testcases
}
l := NewFileLoaderAtRoot(
validators.MakeFakeValidator(), fs.MakeFakeFS())
for _, test := range tests {
pairs, err := l.keyValuesFromLines([]byte(test.content))
if test.expectedErr && err == nil {
t.Fatalf("%s should not return error", test.desc)
}
if !reflect.DeepEqual(pairs, test.expectedPairs) {
t.Errorf("%s should succeed, got:%v exptected:%v", test.desc, pairs, test.expectedPairs)
}
}
}
func TestKeyValuesFromFileSources(t *testing.T) {
tests := []struct {
description string
sources []string
expected []types.Pair
}{
{
description: "create kvs from file sources",
sources: []string{"files/app-init.ini"},
expected: []types.Pair{
{
Key: "app-init.ini",
Value: "FOO=bar",
},
},
},
}
fSys := fs.MakeFakeFS()
fSys.WriteFile("/files/app-init.ini", []byte("FOO=bar"))
l := NewFileLoaderAtRoot(validators.MakeFakeValidator(), fSys)
for _, tc := range tests {
kvs, err := l.keyValuesFromFileSources(tc.sources)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(kvs, tc.expected) {
t.Fatalf("in testcase: %q updated:\n%#v\ndoesn't match expected:\n%#v\n", tc.description, kvs, tc.expected)
}
}
}

View File

@@ -31,17 +31,18 @@ import (
// the remote bases will all be root-only restricted.
func NewLoader(
lr LoadRestrictorFunc,
v ifc.Validator,
target string, fSys fs.FileSystem) (ifc.Loader, error) {
repoSpec, err := git.NewRepoSpecFromUrl(target)
if err == nil {
// The target qualifies as a remote git target.
return newLoaderAtGitClone(
repoSpec, fSys, nil, git.ClonerUsingGitExec)
repoSpec, v, fSys, nil, git.ClonerUsingGitExec)
}
root, err := demandDirectoryRoot(fSys, target)
if err != nil {
return nil, err
}
return newLoaderAtConfirmedDir(
lr, root, fSys, nil, git.ClonerUsingGitExec), nil
lr, v, root, fSys, nil, git.ClonerUsingGitExec), nil
}

View File

@@ -38,7 +38,7 @@ func DefaultPluginConfig() *types.PluginConfig {
}
}
func PluginsNotEnabledErr(name string) error {
func NotEnabledErr(name string) error {
return fmt.Errorf(
flagErrorFmt,
name,

View File

@@ -103,7 +103,7 @@ func (l *Loader) absolutePluginPath(id resid.ResId) string {
func (l *Loader) loadAndConfigurePlugin(
ldr ifc.Loader, res *resource.Resource) (c Configurable, err error) {
if !l.pc.Enabled {
return nil, PluginsNotEnabledErr(res.Id().Gvk().Kind)
return nil, NotEnabledErr(res.Id().Gvk().Kind)
}
if p := NewExecPlugin(
l.absolutePluginPath(res.Id())); p.isAvailable() {

View File

@@ -29,6 +29,7 @@ import (
"sigs.k8s.io/kustomize/pkg/resid"
. "sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/types"
"sigs.k8s.io/kustomize/pkg/validators"
)
func TestFromFile(t *testing.T) {
@@ -52,7 +53,6 @@ metadata:
namespace: test
---
`
l := loadertest.NewFakeLoader("/whatever/project")
if ferr := l.AddFile("/whatever/project/deployment.yaml", []byte(resourceStr)); ferr != nil {
t.Fatalf("Error adding fake file: %v\n", ferr)
@@ -173,6 +173,7 @@ func TestNewFromConfigMaps(t *testing.T) {
}, &types.GeneratorArgs{}, nil),
},
},
{
description: "construct config map from file",
input: []types.ConfigMapArgs{{
@@ -231,6 +232,7 @@ BAR=baz
}, &types.GeneratorArgs{}, nil),
},
},
// TODO: add testcase for data coming from multiple sources like
// files/literal/env etc.
}
@@ -268,7 +270,7 @@ func TestNewResMapFromSecretArgs(t *testing.T) {
fakeFs := fs.MakeFakeFS()
fakeFs.Mkdir(".")
actual, err := rmF.NewResMapFromSecretArgs(
loader.NewFileLoaderAtRoot(fakeFs), nil, secrets)
loader.NewFileLoaderAtRoot(validators.MakeFakeValidator(), fakeFs), nil, secrets)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

View File

@@ -144,7 +144,7 @@ func (kt *KustTarget) makeCustomizedResMap(
rm := ra.ResMap()
pt := kt.tFactory.MakeInventoryTransformer(
kt.kustomization.Inventory,
kt.kustomization.Inventory, kt.ldr,
kt.kustomization.Namespace,
garbagePolicy)
err = pt.Transform(rm)

View File

@@ -18,6 +18,7 @@ import (
"sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/target"
"sigs.k8s.io/kustomize/pkg/validators"
"sigs.k8s.io/kustomize/plugin"
)
@@ -56,7 +57,8 @@ metadata:
t.Fatalf("err %v", err)
}
ldr, err := loader.NewLoader(loader.RestrictionRootOnly, dir, fSys)
ldr, err := loader.NewLoader(
loader.RestrictionRootOnly, validators.MakeFakeValidator(), dir, fSys)
if err != nil {
t.Fatalf("Err: %v", err)
}

View File

@@ -1,28 +1,14 @@
/*
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 config
import (
"reflect"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/gvk"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/loader"
"testing"
"sigs.k8s.io/kustomize/internal/loadertest"
"sigs.k8s.io/kustomize/pkg/gvk"
)
func TestMakeDefaultConfig(t *testing.T) {
@@ -30,20 +16,15 @@ func TestMakeDefaultConfig(t *testing.T) {
_ = MakeDefaultConfig()
}
func makeTestLoader(path, content string) ifc.Loader {
fSys := fs.MakeFakeFS()
fSys.WriteFile(path, []byte(content))
return loader.NewFileLoaderAtRoot(fSys)
}
func TestFromFiles(t *testing.T) {
path := "/transformerconfig/test/config.yaml"
ldr := makeTestLoader(path, `
ldr := loadertest.NewFakeLoader("/app")
ldr.AddFile("/app/config.yaml", []byte(`
namePrefix:
- path: nameprefix/path
kind: SomeKind
`)
tcfg, err := NewFactory(ldr).FromFiles([]string{"transformerconfig/test/config.yaml"})
`))
tcfg, err := NewFactory(ldr).FromFiles([]string{"/app/config.yaml"})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

View File

@@ -242,6 +242,12 @@ type ConfigMapArgs struct {
GeneratorArgs `json:",inline,omitempty" yaml:",inline,omitempty"`
}
// Pair is a key value pair.
type Pair struct {
Key string
Value string
}
// SecretArgs contains the metadata of how to generate a secret.
type SecretArgs struct {
// GeneratorArgs for the secret.

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 validators defines a FakeValidator that can be used in tests
package validators
@@ -48,6 +35,16 @@ func MakeFakeValidator() *FakeValidator {
return &FakeValidator{}
}
// ErrIfInvalidKey returns nil
func (v *FakeValidator) ErrIfInvalidKey(k string) error {
return nil
}
// IsEnvVarName returns nil
func (v *FakeValidator) IsEnvVarName(k string) error {
return nil
}
// MakeAnnotationValidator returns a nil function
func (v *FakeValidator) MakeAnnotationValidator() func(map[string]string) error {
return nil

View File

@@ -34,6 +34,7 @@ var database = map[string]string{
func (p *plugin) Config(
ldr ifc.Loader, rf *resmap.Factory, c []byte) error {
p.rf = rf
p.ldr = ldr
return yaml.Unmarshal(c, p)
}