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

View File

@@ -1,18 +1,5 @@
/* // Copyright 2019 The Kubernetes Authors.
Copyright 2018 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 loadertest holds a fake for the Loader interface. // Package loadertest holds a fake for the Loader interface.
package loadertest package loadertest
@@ -22,6 +9,8 @@ import (
"sigs.k8s.io/kustomize/pkg/fs" "sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/ifc" "sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/loader" "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. // 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. // Create fake filesystem and inject it into initial Loader.
fSys := fs.MakeFakeFS() fSys := fs.MakeFakeFS()
fSys.Mkdir(initialDir) fSys.Mkdir(initialDir)
ldr, err := loader.NewLoader(lr, initialDir, fSys) ldr, err := loader.NewLoader(
lr, validators.MakeFakeValidator(), initialDir, fSys)
if err != nil { if err != nil {
log.Fatalf("Unable to make loader: %v", err) log.Fatalf("Unable to make loader: %v", err)
} }
@@ -63,7 +53,7 @@ func (f FakeLoader) AddDirectory(fullDirPath string) error {
return f.fs.Mkdir(fullDirPath) return f.fs.Mkdir(fullDirPath)
} }
// Root returns root. // Root delegates.
func (f FakeLoader) Root() string { func (f FakeLoader) Root() string {
return f.delegate.Root() 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 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) { func (f FakeLoader) Load(location string) ([]byte, error) {
return f.delegate.Load(location) return f.delegate.Load(location)
} }
// Cleanup does nothing // Cleanup delegates.
func (f FakeLoader) Cleanup() error { 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 2019 The Kubernetes Authors.
Copyright 2018 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 configmapandsecret generates configmaps and secrets per generator rules. // Package configmapandsecret generates configmaps and secrets per generator rules.
package configmapandsecret package configmapandsecret
@@ -39,13 +26,13 @@ func makeFreshConfigMap(
// MakeConfigMap returns a new ConfigMap, or nil and an error. // MakeConfigMap returns a new ConfigMap, or nil and an error.
func (f *Factory) MakeConfigMap( func (f *Factory) MakeConfigMap(
args *types.ConfigMapArgs) (*v1.ConfigMap, error) { args *types.ConfigMapArgs) (*v1.ConfigMap, error) {
all, err := f.loadKvPairs(args.GeneratorArgs) all, err := f.ldr.LoadKvPairs(args.GeneratorArgs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
cm := makeFreshConfigMap(args) cm := makeFreshConfigMap(args)
for _, p := range all { for _, p := range all {
err = addKvToConfigMap(cm, p.Key, p.Value) err = f.addKvToConfigMap(cm, p)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -59,26 +46,26 @@ func (f *Factory) MakeConfigMap(
// addKvToConfigMap adds the given key and data to the given config map. // addKvToConfigMap adds the given key and data to the given config map.
// Error if key invalid, or already exists. // Error if key invalid, or already exists.
func addKvToConfigMap(configMap *v1.ConfigMap, keyName, data string) error { func (f *Factory) addKvToConfigMap(configMap *v1.ConfigMap, p types.Pair) error {
if err := errIfInvalidKey(keyName); err != nil { if err := f.ldr.Validator().ErrIfInvalidKey(p.Key); err != nil {
return err return err
} }
// If the configmap data contains byte sequences that are all in the UTF-8 // If the configmap data contains byte sequences that are all in the UTF-8
// range, we will write it to .Data // range, we will write it to .Data
if utf8.Valid([]byte(data)) { if utf8.Valid([]byte(p.Value)) {
if _, entryExists := configMap.Data[keyName]; entryExists { if _, entryExists := configMap.Data[p.Key]; entryExists {
return fmt.Errorf(keyExistsErrorMsg, keyName, configMap.Data) return fmt.Errorf(keyExistsErrorMsg, p.Key, configMap.Data)
} }
configMap.Data[keyName] = data configMap.Data[p.Key] = p.Value
return nil return nil
} }
// otherwise, it's BinaryData // otherwise, it's BinaryData
if configMap.BinaryData == nil { if configMap.BinaryData == nil {
configMap.BinaryData = map[string][]byte{} configMap.BinaryData = map[string][]byte{}
} }
if _, entryExists := configMap.BinaryData[keyName]; entryExists { if _, entryExists := configMap.BinaryData[p.Key]; entryExists {
return fmt.Errorf(keyExistsErrorMsg, keyName, configMap.BinaryData) return fmt.Errorf(keyExistsErrorMsg, p.Key, configMap.BinaryData)
} }
configMap.BinaryData[keyName] = []byte(data) configMap.BinaryData[p.Key] = []byte(p.Value)
return nil return nil
} }

View File

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

View File

@@ -4,12 +4,6 @@
package configmapandsecret package configmapandsecret
import ( 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/ifc"
"sigs.k8s.io/kustomize/pkg/types" "sigs.k8s.io/kustomize/pkg/types"
) )
@@ -20,87 +14,10 @@ type Factory struct {
options *types.GeneratorOptions options *types.GeneratorOptions
} }
// NewFactory returns a new Factory. // NewFactory returns a new factory that makes ConfigMaps and Secrets.
func NewFactory( func NewFactory(
l ifc.Loader, o *types.GeneratorOptions) *Factory { ldr ifc.Loader, o *types.GeneratorOptions) *Factory {
return &Factory{ldr: l, options: o} return &Factory{ldr: ldr, 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
} }
const keyExistsErrorMsg = "cannot add key %s, another key by that name already exists: %v" 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 2019 The Kubernetes Authors.
Copyright 2018 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 configmapandsecret package configmapandsecret
@@ -41,13 +28,13 @@ func makeFreshSecret(
// MakeSecret returns a new secret. // MakeSecret returns a new secret.
func (f *Factory) MakeSecret( func (f *Factory) MakeSecret(
args *types.SecretArgs) (*corev1.Secret, error) { args *types.SecretArgs) (*corev1.Secret, error) {
all, err := f.loadKvPairs(args.GeneratorArgs) all, err := f.ldr.LoadKvPairs(args.GeneratorArgs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
s := makeFreshSecret(args) s := makeFreshSecret(args)
for _, p := range all { for _, p := range all {
err = addKvToSecret(s, p.Key, p.Value) err = f.addKvToSecret(s, p.Key, p.Value)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -59,8 +46,8 @@ func (f *Factory) MakeSecret(
return s, nil return s, nil
} }
func addKvToSecret(secret *corev1.Secret, keyName, data string) error { func (f *Factory) addKvToSecret(secret *corev1.Secret, keyName, data string) error {
if err := errIfInvalidKey(keyName); err != nil { if err := f.ldr.Validator().ErrIfInvalidKey(keyName); err != nil {
return err return err
} }
if _, entryExists := secret.Data[keyName]; entryExists { if _, entryExists := secret.Data[keyName]; entryExists {

View File

@@ -12,6 +12,7 @@ import (
"sigs.k8s.io/kustomize/pkg/fs" "sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/loader" "sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/types" "sigs.k8s.io/kustomize/pkg/types"
"sigs.k8s.io/kustomize/pkg/validators"
) )
func makeEnvSecret(name string) *corev1.Secret { func makeEnvSecret(name string) *corev1.Secret {
@@ -125,7 +126,7 @@ func TestConstructSecret(t *testing.T) {
fSys := fs.MakeFakeFS() fSys := fs.MakeFakeFS()
fSys.WriteFile("/secret/app.env", []byte("DB_USERNAME=admin\nDB_PASSWORD=somepw\n")) 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")) 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 { for _, tc := range testCases {
f := NewFactory(ldr, tc.options) f := NewFactory(ldr, tc.options)
cm, err := f.MakeSecret(&tc.input) 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 2019 The Kubernetes Authors.
Copyright 2018 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 transformer provides transformer factory // Package transformer provides transformer factory
package transformer package transformer
@@ -21,6 +8,7 @@ import (
"sigs.k8s.io/kustomize/k8sdeps/transformer/hash" "sigs.k8s.io/kustomize/k8sdeps/transformer/hash"
"sigs.k8s.io/kustomize/k8sdeps/transformer/inventory" "sigs.k8s.io/kustomize/k8sdeps/transformer/inventory"
"sigs.k8s.io/kustomize/k8sdeps/transformer/patch" "sigs.k8s.io/kustomize/k8sdeps/transformer/patch"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/resource" "sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/transformers" "sigs.k8s.io/kustomize/pkg/transformers"
"sigs.k8s.io/kustomize/pkg/types" "sigs.k8s.io/kustomize/pkg/types"
@@ -48,7 +36,8 @@ func (p *FactoryImpl) MakeHashTransformer() transformers.Transformer {
func (p *FactoryImpl) MakeInventoryTransformer( func (p *FactoryImpl) MakeInventoryTransformer(
arg *types.Inventory, arg *types.Inventory,
ldr ifc.Loader,
namespace string, namespace string,
gp types.GarbagePolicy) transformers.Transformer { 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/kunstruct"
"sigs.k8s.io/kustomize/k8sdeps/transformer/hash" "sigs.k8s.io/kustomize/k8sdeps/transformer/hash"
"sigs.k8s.io/kustomize/pkg/gvk" "sigs.k8s.io/kustomize/pkg/gvk"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/inventory" "sigs.k8s.io/kustomize/pkg/inventory"
"sigs.k8s.io/kustomize/pkg/resid" "sigs.k8s.io/kustomize/pkg/resid"
"sigs.k8s.io/kustomize/pkg/resmap" "sigs.k8s.io/kustomize/pkg/resmap"
@@ -19,6 +20,7 @@ import (
// inventoryTransformer compute the inventory object used in prune // inventoryTransformer compute the inventory object used in prune
type inventoryTransformer struct { type inventoryTransformer struct {
garbagePolicy types.GarbagePolicy garbagePolicy types.GarbagePolicy
ldr ifc.Loader
cmName string cmName string
cmNamespace string cmNamespace string
} }
@@ -28,6 +30,7 @@ var _ transformers.Transformer = &inventoryTransformer{}
// NewInventoryTransformer makes a inventoryTransformer. // NewInventoryTransformer makes a inventoryTransformer.
func NewInventoryTransformer( func NewInventoryTransformer(
p *types.Inventory, p *types.Inventory,
ldr ifc.Loader,
namespace string, namespace string,
gp types.GarbagePolicy) transformers.Transformer { gp types.GarbagePolicy) transformers.Transformer {
if p == nil || p.Type != "ConfigMap" || p.ConfigMap.Namespace != namespace { if p == nil || p.Type != "ConfigMap" || p.ConfigMap.Namespace != namespace {
@@ -35,6 +38,7 @@ func NewInventoryTransformer(
} }
return &inventoryTransformer{ return &inventoryTransformer{
garbagePolicy: gp, garbagePolicy: gp,
ldr: ldr,
cmName: p.ConfigMap.Name, cmName: p.ConfigMap.Name,
cmNamespace: p.ConfigMap.Namespace, cmNamespace: p.ConfigMap.Namespace,
} }
@@ -82,7 +86,7 @@ func (o *inventoryTransformer) Transform(m resmap.ResMap) error {
} }
kf := kunstruct.NewKunstructuredFactoryImpl() kf := kunstruct.NewKunstructuredFactoryImpl()
k, err := kf.MakeConfigMap(nil, opts, args) k, err := kf.MakeConfigMap(o.ldr, opts, args)
if err != nil { if err != nil {
return err return err
} }

View File

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

View File

@@ -1,24 +1,15 @@
/* // Copyright 2019 The Kubernetes Authors.
Copyright 2018 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); // Package validator provides functions to validate labels, annotations,
you may not use this file except in compliance with the License. // namespaces and configmap/secret keys using apimachinery functions.
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 package validator
import ( import (
"errors" "errors"
"fmt"
"strings"
apivalidation "k8s.io/apimachinery/pkg/api/validation" apivalidation "k8s.io/apimachinery/pkg/api/validation"
v1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation" v1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation"
@@ -33,6 +24,24 @@ func NewKustValidator() *KustValidator {
return &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. // MakeAnnotationValidator returns a MapValidatorFunc using apimachinery.
func (v *KustValidator) MakeAnnotationValidator() func(map[string]string) error { func (v *KustValidator) MakeAnnotationValidator() func(map[string]string) error {
return func(x map[string]string) error { return func(x map[string]string) error {

View File

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

View File

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

View File

@@ -1,18 +1,5 @@
/* // Copyright 2019 The Kubernetes Authors.
Copyright 2017 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 add package add
@@ -23,7 +10,10 @@ import (
) )
// NewCmdAdd returns an instance of 'add' subcommand. // 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{ c := &cobra.Command{
Use: "add", Use: "add",
Short: "Adds an item to the kustomization file.", 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), Args: cobra.MinimumNArgs(1),
} }
c.AddCommand( c.AddCommand(
newCmdAddResource(fsys), newCmdAddResource(fSys),
newCmdAddPatch(fsys), newCmdAddPatch(fSys),
newCmdAddSecret(fsys, kf), newCmdAddSecret(fSys, ldr, kf),
newCmdAddConfigMap(fsys, kf), newCmdAddConfigMap(fSys, ldr, kf),
newCmdAddBase(fsys), newCmdAddBase(fSys),
newCmdAddLabel(fsys, v.MakeLabelValidator()), newCmdAddLabel(fSys, ldr.Validator().MakeLabelValidator()),
newCmdAddAnnotation(fsys, v.MakeAnnotationValidator()), newCmdAddAnnotation(fSys, ldr.Validator().MakeAnnotationValidator()),
) )
return c return c
} }

View File

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

View File

@@ -7,11 +7,15 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/pkg/fs" "sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/types" "sigs.k8s.io/kustomize/pkg/types"
"sigs.k8s.io/kustomize/pkg/validators"
) )
func TestNewAddConfigMapIsNotNil(t *testing.T) { 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") 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/commands/kustfile"
"sigs.k8s.io/kustomize/pkg/fs" "sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/ifc" "sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/types" "sigs.k8s.io/kustomize/pkg/types"
) )
// newCmdAddSecret returns a new command. // 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 var flags flagsAndArgs
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "secret NAME [--from-file=[key=]source] [--from-literal=key1=value1] [--type=Opaque|kubernetes.io/tls]", 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. // Add the flagsAndArgs map to the kustomization file.
err = addSecret( err = addSecret(ldr, kustomization, flags, kf)
loader.NewFileLoaderAtCwd(fSys), kustomization, flags, kf)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -7,11 +7,15 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/pkg/fs" "sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/types" "sigs.k8s.io/kustomize/pkg/types"
"sigs.k8s.io/kustomize/pkg/validators"
) )
func TestNewCmdAddSecretIsNotNil(t *testing.T) { 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") t.Fatal("newCmdAddSecret shouldn't be nil")
} }
} }

View File

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

View File

@@ -1,18 +1,5 @@
/* // Copyright 2019 The Kubernetes Authors.
Copyright 2018 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 ifc holds miscellaneous interfaces used by kustomize. // Package ifc holds miscellaneous interfaces used by kustomize.
package ifc package ifc
@@ -27,6 +14,8 @@ type Validator interface {
MakeAnnotationValidator() func(map[string]string) error MakeAnnotationValidator() func(map[string]string) error
MakeLabelValidator() func(map[string]string) error MakeLabelValidator() func(map[string]string) error
ValidateNamespace(string) []string ValidateNamespace(string) []string
ErrIfInvalidKey(string) error
IsEnvVarName(k string) error
} }
// Loader interface exposes methods to read bytes. // Loader interface exposes methods to read bytes.
@@ -39,6 +28,10 @@ type Loader interface {
Load(location string) ([]byte, error) Load(location string) ([]byte, error)
// Cleanup cleans the loader // Cleanup cleans the loader
Cleanup() error 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 // Kunstructured allows manipulation of k8s objects

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -18,6 +18,7 @@ import (
"sigs.k8s.io/kustomize/pkg/resmap" "sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/resource" "sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/target" "sigs.k8s.io/kustomize/pkg/target"
"sigs.k8s.io/kustomize/pkg/validators"
"sigs.k8s.io/kustomize/plugin" "sigs.k8s.io/kustomize/plugin"
) )
@@ -56,7 +57,8 @@ metadata:
t.Fatalf("err %v", err) 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 { if err != nil {
t.Fatalf("Err: %v", err) t.Fatalf("Err: %v", err)
} }

View File

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

View File

@@ -242,6 +242,12 @@ type ConfigMapArgs struct {
GeneratorArgs `json:",inline,omitempty" yaml:",inline,omitempty"` 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. // SecretArgs contains the metadata of how to generate a secret.
type SecretArgs struct { type SecretArgs struct {
// GeneratorArgs for the secret. // GeneratorArgs for the secret.

View File

@@ -1,18 +1,5 @@
/* // Copyright 2019 The Kubernetes Authors.
Copyright 2018 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 validators defines a FakeValidator that can be used in tests // Package validators defines a FakeValidator that can be used in tests
package validators package validators
@@ -48,6 +35,16 @@ func MakeFakeValidator() *FakeValidator {
return &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 // MakeAnnotationValidator returns a nil function
func (v *FakeValidator) MakeAnnotationValidator() func(map[string]string) error { func (v *FakeValidator) MakeAnnotationValidator() func(map[string]string) error {
return nil return nil

View File

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