Merge remote-tracking branch 'upstream/master'

This commit is contained in:
郭旭东
2019-05-29 14:08:40 +08:00
88 changed files with 1322 additions and 2265 deletions

View File

@@ -27,7 +27,7 @@ env:
- GOLANGCI_RELEASE="v1.12"
before_install:
- source ./bin/consider-early-travis-exit.sh
- source ./travis/consider-early-travis-exit.sh
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $GOPATH/bin ${GOLANGCI_RELEASE}
- go get -u github.com/monopole/mdrip
# The following would install Helm if needed for some reason.
@@ -39,7 +39,7 @@ before_install:
install: true
script:
- ./bin/pre-commit.sh
- ./travis/pre-commit.sh
# TBD. Suppressing for now.
notifications:

View File

@@ -1,23 +1,8 @@
# Copyright 2018 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# TODO(droot): add instructions for running in production.
steps:
- name: "gcr.io/cloud-builders/git"
args: [fetch, --tags, --depth=100]
- name: "gcr.io/kustomize-199618/golang_with_goreleaser:1.10-stretch"
args: ["bash", "build/build.sh"]
args: ["bash", "build/cloudbuild.sh"]
secretEnv: ['GITHUB_TOKEN']
secrets:
- kmsKeyName: projects/kustomize-199618/locations/global/keyRings/github-tokens/cryptoKeys/gh-release-token

View File

@@ -1,30 +0,0 @@
# Copyright 2018 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Instructions to run locally:
# Download google container builder: https://github.com/kubernetes-sigs/container-builder-local
# Set you GOOS and GOARCH vars to match your system
# Set OUTPUT to the location to write the directory containing the tar.gz
# $ container-builder-local --config=build/cloudbuild_local.yaml --dryrun=false \
# --substitutions=_GOOS=$GOOS,_GOARCH=$GOARCH --write-workspace=$OUTPUT .
# Release tar will be in $OUTPUT
steps:
- name: "gcr.io/kustomize-199618/golang_with_goreleaser:1.10-stretch"
args: ["bash", "build/build.sh", "--snapshot"]
secretEnv: ['GITHUB_TOKEN']
secrets:
- kmsKeyName: projects/kustomize-199618/locations/global/keyRings/github-tokens/cryptoKeys/gh-release-token
secretEnv:
GITHUB_TOKEN: CiQAyrREbPgXJOeT7M3t+WlxkhXwlMPudixBeiyWTjmLOMLqdK4SUQA0W+xUmDJKAhyfHCcwqSEzUn9OwKC7XAYcmwe0CCKTCbPbDgmioDK24q3LVapndXNvnnHvCjhOJNEr1o+P1DCF+LlzYV2YL8lP09rrKrslPg==

67
build/localbuild.sh Executable file
View File

@@ -0,0 +1,67 @@
#!/bin/bash
# Usage
#
# ./build/localbuild.sh
#
# The script attempts to use cloudbuild configuration
# to create a release "locally".
#
# At the time of writing,
#
# https://pantheon.corp.google.com/cloud-build/triggers?project=kustomize-199618
#
# has a trigger such that whenever a git tag is
# applied to the kustomize repo, the cloud builder
# reads the repository-relative file
#
# build/cloudbuild.yaml
#
# Inside this yaml file is a reference to the script
#
# build/cloudbuild.sh
#
# The script you are reading now does something
# analogous via docker tricks.
set -e
# set -x
if [ -z ${GOPATH+x} ]; then
echo GOPATH is unset; cannot proceed.
exit 1
fi
WORK=$(mktemp -d)
pushd $GOPATH/src/sigs.k8s.io/kustomize
pwd
echo "Building in $WORK"
cat <<EOF >/tmp/localbuild.yaml
steps:
- name: "gcr.io/kustomize-199618/golang_with_goreleaser:1.10-stretch"
args: ["bash", "build/cloudbuild.sh", "--snapshot"]
secretEnv: ['GITHUB_TOKEN']
secrets:
- kmsKeyName: projects/kustomize-199618/locations/global/keyRings/github-tokens/cryptoKeys/gh-release-token
secretEnv:
GITHUB_TOKEN: CiQAyrREbPgXJOeT7M3t+WlxkhXwlMPudixBeiyWTjmLOMLqdK4SUQA0W+xUmDJKAhyfHCcwqSEzUn9OwKC7XAYcmwe0CCKTCbPbDgmioDK24q3LVapndXNvnnHvCjhOJNEr1o+P1DCF+LlzYV2YL8lP09rrKrslPg==
EOF
# --substitutions=_GOOS=linux,_GOARCH=amd64
config=build/cloudbuild.yaml
config=/tmp/localbuild.yaml
# See https://cloud.google.com/cloud-build/docs/build-debug-locally
cloud-build-local \
--config=$config \
--dryrun=false \
--write-workspace=$WORK \
.
tree $WORK
popd

View File

@@ -173,13 +173,10 @@ transformers cannot be expected to be commutative.
A `kustomize build` that tries to use plugins but
omits the flag
_TODO: Change flag_
> `--enable_alpha_goplugins_accept_panic_risk`
> `--enable_alpha_plugins`
will fail with a warning about plugin use.
Flag use is an opt-in acknowledging the absence of
plugin provenance. It's meant to give pause to
someone who blindly downloads a kustomization from

View File

@@ -12,46 +12,57 @@ tests, and should work with HEAD
go get sigs.k8s.io/kustomize
```
* [hello world](helloWorld/README.md) - Deploy multiple
(differently configured) variants of a simple Hello
World server.
Basic Usage
* [last mile helm](chart.md) - Make last mile modifications to
a helm chart.
* [LDAP](ldap/README.md) - Deploy multiple
(differently configured) variants of a LDAP server.
* [mySql](mySql/README.md) - Create a MySQL production
configuration from scratch.
* [springboot](springboot/README.md) - Create a Spring Boot
application production configuration from scratch.
* [combineConfigs](combineConfigs.md) -
* [configGenerations](configGeneration.md) -
Rolling update when ConfigMapGenerator changes.
* [combineConfigs](combineConfigs.md) -
Mixing configuration data from different owners
(e.g. devops/SRE and developers).
* [generatorOptions](generatorOptions.md) -
Modifying behavior of all ConfigMap and Secret generators.
* [configGenerations](configGeneration.md) -
Rolling update when ConfigMapGenerator changes.
* [vars](wordpress/README.md) - Injecting k8s runtime data into
container arguments (e.g. to point wordpress to a SQL service) by vars.
* [image names and tags](image.md) - Updating image names and tags without applying a patch.
* [remote target](remoteBuild.md) - Building a kustomization from a github URL
* [json patch](jsonpatch.md) - Apply a json patch in a kustomization
* [secret generation](secretGeneratorPlugin.md) - Generating secrets from a plugin.
Advanced Usage
- generator plugins:
* [last mile helm](chart.md) - Make last mile modifications to
a helm chart.
* [secret generation](secretGeneratorPlugin.md) - Generating secrets from a plugin.
- customize builtin transformer configurations
* [transformer configs](transformerconfigs/README.md) - Customize transformer configurations
* [generatorOptions](generatorOptions.md) -
Modifying behavior of all ConfigMap and Secret generators.
Multi Variant Examples
* [breakfast](breakfast.md) - Customize breakfast for
Alice and Bob.
* [hello world](helloWorld/README.md) - Deploy multiple
(differently configured) variants of a simple Hello
World server.
* [LDAP](ldap/README.md) - Deploy multiple
(differently configured) variants of a LDAP server.
* [springboot](springboot/README.md) - Create a Spring Boot
application production configuration from scratch.
* [vars](wordpress/README.md) - Injecting k8s runtime data into
container arguments (e.g. to point wordpress to a SQL service) by vars.
* [image names and tags](image.md) - Updating image names and tags without applying a patch.
* [multibases](multibases/README.md) - Composing three variants (dev, staging, production) with a common base.
* [remote target](remoteBuild.md) - Building a kustomization from a github URL
* [json patch](jsonpatch.md) - Apply a json patch in a kustomization
* [transformer configs](transformerconfigs/README.md) - Customize transformer configurations
* [mySql](mySql/README.md) - Create a MySQL production
configuration from scratch.
* [breakfast](breakfast.md) - Customize breakfast for
Alice and Bob.
* [multibases](multibases/README.md) - Composing three variants (dev, staging, production) with a common base.

View File

@@ -150,8 +150,7 @@ correct environment and flags for plugins:
```
function kustomizeIt {
XDG_CONFIG_HOME=$DEMO_HOME \
kustomize build \
--enable_alpha_goplugins_accept_panic_risk \
kustomize build --enable_alpha_plugins \
$DEMO_HOME/$1
}
```

View File

@@ -201,9 +201,7 @@ can be found under `$DEMO_HOME`:
```
result=$( \
XDG_CONFIG_HOME=$DEMO_HOME \
kustomize \
--enable_alpha_goplugins_accept_panic_risk \
build $DEMO_HOME )
kustomize build --enable_alpha_plugins $DEMO_HOME )
echo "$result"
# Spot check the result:
test 1 == $(echo "$result" | grep -c "FRUIT: YXBwbGU=")

View File

@@ -165,4 +165,14 @@ In addition to the default transformers, you can create custom transformer confi
- [support a CRD type](crd/README.md)
- add extra fields for variable substitution
- add extra fields for name reference
- add extra fields for name reference
## Supporting escape characters in CRD path
```yaml
metadata:
annotations:
foo.k8s.io/bar: baz
```
Kustomize supports escaping special characters in path, e.g `matadata/annotations/foo.k8s.io\/bar`

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

@@ -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
@@ -22,10 +9,10 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/kustomize/k8sdeps/kv/plugin"
"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 {
@@ -142,10 +129,9 @@ 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)
reg := plugin.NewRegistry(ldr)
ldr := loader.NewFileLoaderAtRoot(validators.MakeFakeValidator(), fSys)
for _, tc := range testCases {
f := NewFactory(ldr, tc.options, reg)
f := NewFactory(ldr, tc.options)
cm, err := f.MakeConfigMap(&tc.input)
if err != nil {
t.Fatalf("unexpected error: %v", err)

View File

@@ -1,30 +1,9 @@
/*
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 configmapandsecret
import (
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/util/validation"
"sigs.k8s.io/kustomize/k8sdeps/kv"
"sigs.k8s.io/kustomize/k8sdeps/kv/plugin"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/types"
)
@@ -33,126 +12,12 @@ import (
type Factory struct {
ldr ifc.Loader
options *types.GeneratorOptions
reg plugin.Registry
}
// NewFactory returns a new Factory.
// NewFactory returns a new factory that makes ConfigMaps and Secrets.
func NewFactory(
l ifc.Loader, o *types.GeneratorOptions, reg plugin.Registry) *Factory {
return &Factory{ldr: l, options: o, reg: reg}
}
func (f *Factory) loadKvPairs(
args types.GeneratorArgs) (all []kv.Pair, err error) {
pairs, err := f.keyValuesFromPlugins(args.KVSources)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf(
"plugins: %s",
args.KVSources))
}
all = append(all, pairs...)
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) keyValuesFromPlugins(sources []types.KVSource) ([]kv.Pair, error) {
var result []kv.Pair
for _, s := range sources {
plug, err := f.reg.Load(s.PluginType, s.Name)
if err != nil {
return nil, err
}
kvs, err := plug.Get(f.reg.Root(), s.Args)
if err != nil {
return nil, err
}
for _, k := range sortedKeys(kvs) {
result = append(result, kv.Pair{Key: k, Value: kvs[k]})
}
}
return result, nil
}
func sortedKeys(m map[string]string) []string {
keys := make([]string, len(m))
i := 0
for k := range m {
keys[i] = k
i++
}
sort.Strings(keys)
return keys
}
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,139 +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 configmapandsecret
import (
"reflect"
"testing"
"sigs.k8s.io/kustomize/k8sdeps/kv"
"sigs.k8s.io/kustomize/k8sdeps/kv/plugin"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/types"
)
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"))
ldr := loader.NewFileLoaderAtRoot(fSys)
reg := plugin.NewRegistry(ldr)
bf := NewFactory(loader.NewFileLoaderAtRoot(fSys), nil, reg)
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)
}
}
}
func TestKeyValuesFromPlugins(t *testing.T) {
tests := []struct {
description string
sources []types.KVSource
expected []kv.Pair
}{
{
description: "Create kv.Pairs from builtin literals plugin",
sources: []types.KVSource{
{
PluginType: "builtin",
Name: "literals",
Args: []string{"FOO=bar", "BAR=baz"},
},
},
expected: []kv.Pair{
{
Key: "BAR",
Value: "baz",
},
{
Key: "FOO",
Value: "bar",
},
},
},
{
description: "Create kv.Pairs from builtin files plugin",
sources: []types.KVSource{
{
PluginType: "builtin",
Name: "files",
Args: []string{"files/app-init.ini"},
},
},
expected: []kv.Pair{
{
Key: "app-init.ini",
Value: "FOO=bar",
},
},
},
{
description: "Create kv.Pairs from builtin envfiles plugin",
sources: []types.KVSource{
{
PluginType: "builtin",
Name: "envfiles",
Args: []string{"files/app-init.ini"},
},
},
expected: []kv.Pair{
{
Key: "FOO",
Value: "bar",
},
},
},
}
fSys := fs.MakeFakeFS()
fSys.WriteFile("/files/app-init.ini", []byte("FOO=bar"))
ldr := loader.NewFileLoaderAtRoot(fSys)
reg := plugin.NewRegistry(ldr)
bf := NewFactory(ldr, nil, reg)
for _, tc := range tests {
kvs, err := bf.keyValuesFromPlugins(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

@@ -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
@@ -22,10 +9,10 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/kustomize/k8sdeps/kv/plugin"
"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 {
@@ -139,10 +126,9 @@ 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)
reg := plugin.NewRegistry(ldr)
ldr := loader.NewFileLoaderAtRoot(validators.MakeFakeValidator(), fSys)
for _, tc := range testCases {
f := NewFactory(ldr, tc.options, reg)
f := NewFactory(ldr, tc.options)
cm, err := f.MakeSecret(&tc.input)
if err != nil {
t.Fatalf("unexpected error: %v", err)

View File

@@ -1,18 +1,5 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package kunstruct
@@ -25,28 +12,19 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/yaml"
"sigs.k8s.io/kustomize/k8sdeps/configmapandsecret"
"sigs.k8s.io/kustomize/k8sdeps/kv/plugin"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/types"
)
// KunstructuredFactoryImpl hides construction using apimachinery types.
type KunstructuredFactoryImpl struct {
generatorMetaArgs *types.GeneratorMetaArgs
}
var _ ifc.KunstructuredFactory = &KunstructuredFactoryImpl{}
// NewKunstructuredFactoryImpl returns a factory.
func NewKunstructuredFactoryImpl() ifc.KunstructuredFactory {
return NewKunstructuredFactoryWithGeneratorArgs(
&types.GeneratorMetaArgs{})
}
// NewKunstructuredFactoryWithGeneratorArgs returns a factory.
func NewKunstructuredFactoryWithGeneratorArgs(
gma *types.GeneratorMetaArgs) ifc.KunstructuredFactory {
return &KunstructuredFactoryImpl{gma}
return &KunstructuredFactoryImpl{}
}
// SliceFromBytes returns a slice of Kunstructured.
@@ -91,9 +69,7 @@ func (kf *KunstructuredFactoryImpl) MakeConfigMap(
options *types.GeneratorOptions,
args *types.ConfigMapArgs) (ifc.Kunstructured, error) {
o, err := configmapandsecret.NewFactory(
ldr, options,
plugin.NewConfiguredRegistry(
ldr, kf.generatorMetaArgs.PluginConfig)).MakeConfigMap(args)
ldr, options).MakeConfigMap(args)
if err != nil {
return nil, err
}
@@ -106,9 +82,7 @@ func (kf *KunstructuredFactoryImpl) MakeSecret(
options *types.GeneratorOptions,
args *types.SecretArgs) (ifc.Kunstructured, error) {
o, err := configmapandsecret.NewFactory(
ldr, options,
plugin.NewConfiguredRegistry(
ldr, kf.generatorMetaArgs.PluginConfig)).MakeSecret(args)
ldr, options).MakeSecret(args)
if err != nil {
return nil, err
}

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,51 +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 builtin
import (
"sigs.k8s.io/kustomize/k8sdeps/kv"
"sigs.k8s.io/kustomize/pkg/ifc"
)
// EnvFiles format should be a path to a file to read lines of key=val
// pairs to create a configmap.
// i.e. a Docker .env file or a .ini file.
type EnvFiles struct {
Ldr ifc.Loader
}
// Get implements the interface for kv plugins.
func (p EnvFiles) Get(root string, args []string) (map[string]string, error) {
all := make(map[string]string)
for _, path := range args {
if path == "" {
return nil, nil
}
content, err := p.Ldr.Load(path)
if err != nil {
return nil, err
}
kvs, err := kv.KeyValuesFromLines(content)
if err != nil {
return nil, err
}
for _, pair := range kvs {
all[pair.Key] = pair.Value
}
}
return all, nil
}

View File

@@ -1,49 +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 builtin
import (
"sigs.k8s.io/kustomize/k8sdeps/kv"
"sigs.k8s.io/kustomize/pkg/ifc"
)
// Files is a list of file sources.
// Each file source can be specified using its file path, in which case file
// basename will be used as configmap key, or optionally with a key and file
// path, in which case the given key will be used.
// Specifying a directory will iterate each named file in the directory
// whose basename is a valid configmap key.
type Files struct {
Ldr ifc.Loader
}
// Get implements the interface for kv plugins.
func (p Files) Get(root string, args []string) (map[string]string, error) {
kvs := make(map[string]string)
for _, s := range args {
k, fPath, err := kv.ParseFileSource(s)
if err != nil {
return nil, err
}
content, err := p.Ldr.Load(fPath)
if err != nil {
return nil, err
}
kvs[k] = string(content)
}
return kvs, nil
}

View File

@@ -1,39 +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 builtin
import (
"sigs.k8s.io/kustomize/k8sdeps/kv"
)
// Literals takes a list of literals.
// Each literal source should be a key and literal value,
// e.g. `somekey=somevalue`
type Literals struct{}
// Get implements the interface for kv plugins.
func (p Literals) Get(root string, args []string) (map[string]string, error) {
kvs := make(map[string]string)
for _, s := range args {
k, v, err := kv.ParseLiteralSource(s)
if err != nil {
return nil, err
}
kvs[k] = v
}
return kvs, nil
}

View File

@@ -1,47 +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 plugin
import (
"fmt"
"sigs.k8s.io/kustomize/k8sdeps/kv/plugin/builtin"
"sigs.k8s.io/kustomize/pkg/ifc"
)
var _ Factory = &builtinFactory{}
type builtinFactory struct {
plugins map[string]KVSource
}
func newBuiltinFactory(ldr ifc.Loader) *builtinFactory {
return &builtinFactory{
plugins: map[string]KVSource{
"literals": builtin.Literals{},
"files": builtin.Files{Ldr: ldr},
"envfiles": builtin.EnvFiles{Ldr: ldr},
},
}
}
func (p *builtinFactory) load(name string) (KVSource, error) {
if plug, ok := p.plugins[name]; ok {
return plug, nil
}
return nil, fmt.Errorf("plugin %s not found", name)
}

View File

@@ -1,94 +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 plugin
import (
"fmt"
"path/filepath"
"plugin"
"sigs.k8s.io/kustomize/pkg/types"
)
var _ Factory = &goFactory{}
const (
kvSourcesDir = "kvSources"
EnableGoPluginsFlagName = "enable_alpha_goplugins_accept_panic_risk"
EnableGoPluginsFlagHelp = `The main program may panic and exit on an attempt
to use a goplugin that was compiled under conditions
differing from the those in effect when main was
compiled. It's safest to use this flag in the
context of a container image holding both the main
and the goplugins it needs, all built on the same
machine, with the same transitive libs and the same
compiler version.`
errorFmt = `
enable go plugins by specifying flag
--%s
Place .so files in
%s
%s`
)
func newGoFactory(c *types.PluginConfig) *goFactory {
return &goFactory{
config: c,
plugins: make(map[string]KVSource),
}
}
type goFactory struct {
config *types.PluginConfig
plugins map[string]KVSource
}
func (p *goFactory) load(name string) (KVSource, error) {
if plug, ok := p.plugins[name]; ok {
return plug, nil
}
dir := filepath.Join(
p.config.DirectoryPath,
kvSourcesDir)
if !p.config.GoEnabled {
return nil, fmt.Errorf(
errorFmt,
EnableGoPluginsFlagName,
dir,
EnableGoPluginsFlagHelp)
}
goPlugin, err := plugin.Open(
filepath.Join(dir, name+".so"))
if err != nil {
return nil, err
}
symbol, err := goPlugin.Lookup("KVSource")
if err != nil {
return nil, err
}
plug, ok := symbol.(KVSource)
if !ok {
return nil, fmt.Errorf("plugin %s not found", name)
}
p.plugins[name] = plug
return plug, nil
}

View File

@@ -1,28 +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 plugin provides a plugin abstraction layer.
package plugin
// KVSource is the interface for kv source plugins.
type KVSource interface {
Get(root string, args []string) (map[string]string, error)
}
// Factory is the interface for new kv source plugin implementations.
type Factory interface {
load(string) (KVSource, error)
}

View File

@@ -1,87 +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 plugin
import (
"fmt"
"path/filepath"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/pgmconfig"
"sigs.k8s.io/kustomize/pkg/types"
)
// Registry holds all the plugin factories.
type Registry struct {
factories map[types.PluginType]Factory
ldr ifc.Loader
}
const (
pluginTypeGo = types.PluginType("go")
pluginTypeBuiltIn = types.PluginType("builtin")
)
func ActivePluginConfig() *types.PluginConfig {
pc := DefaultPluginConfig()
pc.GoEnabled = true
return pc
}
func DefaultPluginConfig() *types.PluginConfig {
return &types.PluginConfig{
GoEnabled: false,
DirectoryPath: filepath.Join(
pgmconfig.ConfigRoot(), pgmconfig.PluginRoot),
}
}
// NewConfiguredRegistry returns a new Registry loaded
// with all the factories and a custom PluginConfig.
func NewConfiguredRegistry(
ldr ifc.Loader, pc *types.PluginConfig) Registry {
return Registry{
ldr: ldr,
factories: map[types.PluginType]Factory{
pluginTypeGo: newGoFactory(pc),
pluginTypeBuiltIn: newBuiltinFactory(ldr),
},
}
}
// NewRegistry returns a new Registry with default config.
func NewRegistry(ldr ifc.Loader) Registry {
return NewConfiguredRegistry(ldr, &types.PluginConfig{})
}
// Load returns a plugin by type and name.
func (r *Registry) Load(
pt types.PluginType, name string) (KVSource, error) {
if pt.IsUndefined() {
pt = pluginTypeBuiltIn
}
factory, exists := r.factories[pt]
if !exists {
return nil, fmt.Errorf("%s is not a valid plugin type", pt)
}
return factory.load(name)
}
// Root returns the root of the plugins kustomization file.
func (r *Registry) Root() string {
return r.ldr.Root()
}

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"
@@ -38,17 +26,18 @@ func NewFactoryImpl() *FactoryImpl {
func (p *FactoryImpl) MakePatchTransformer(
slice []*resource.Resource,
rf *resource.Factory) (transformers.Transformer, error) {
return patch.NewPatchTransformer(slice, rf)
return patch.NewTransformer(slice, rf)
}
// MakeHashTransformer makes a new name hash transformer
func (p *FactoryImpl) MakeHashTransformer() transformers.Transformer {
return hash.NewNameHashTransformer()
return hash.NewTransformer()
}
func (p *FactoryImpl) MakeInventoryTransformer(
arg *types.Inventory,
ldr ifc.Loader,
namespace string,
gp types.GarbagePolicy) transformers.Transformer {
return inventory.NewInventoryTransformer(arg, namespace, gp)
return inventory.NewTransformer(arg, ldr, namespace, gp)
}

View File

@@ -1,184 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package hash
import (
"crypto/sha256"
"encoding/json"
"fmt"
"sort"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
// KustHash compute hash for unstructured objects
type KustHash struct{}
// NewKustHash returns a KustHash object
func NewKustHash() *KustHash {
return &KustHash{}
}
// Hash returns a hash of either a ConfigMap or a Secret
func (h *KustHash) Hash(m map[string]interface{}) (string, error) {
u := unstructured.Unstructured{
Object: m,
}
kind := u.GetKind()
switch kind {
case "ConfigMap":
cm, err := unstructuredToConfigmap(u)
if err != nil {
return "", err
}
return ConfigMapHash(cm)
case "Secret":
sec, err := unstructuredToSecret(u)
if err != nil {
return "", err
}
return SecretHash(sec)
default:
return "", fmt.Errorf("type %s is supported for hashing in %v", kind, m)
}
}
// ConfigMapHash returns a hash of the ConfigMap.
// The Data, Kind, and Name are taken into account.
func ConfigMapHash(cm *v1.ConfigMap) (string, error) {
encoded, err := encodeConfigMap(cm)
if err != nil {
return "", err
}
h, err := encodeHash(hash(encoded))
if err != nil {
return "", err
}
return h, nil
}
// SecretHash returns a hash of the Secret.
// The Data, Kind, Name, and Type are taken into account.
func SecretHash(sec *v1.Secret) (string, error) {
encoded, err := encodeSecret(sec)
if err != nil {
return "", err
}
h, err := encodeHash(hash(encoded))
if err != nil {
return "", err
}
return h, nil
}
// SortArrayAndComputeHash sorts a string array and
// returns a hash for it
func SortArrayAndComputeHash(s []string) (string, error) {
sort.Strings(s)
data, err := json.Marshal(s)
if err != nil {
return "", err
}
h, err := encodeHash(hash(string(data)))
if err != nil {
return "", err
}
return h, nil
}
// encodeConfigMap encodes a ConfigMap.
// Data, Kind, and Name are taken into account.
func encodeConfigMap(cm *v1.ConfigMap) (string, error) {
// json.Marshal sorts the keys in a stable order in the encoding
m := map[string]interface{}{"kind": "ConfigMap", "name": cm.Name, "data": cm.Data}
if len(cm.BinaryData) > 0 {
m["binaryData"] = cm.BinaryData
}
data, err := json.Marshal(m)
if err != nil {
return "", err
}
return string(data), nil
}
// encodeSecret encodes a Secret.
// Data, Kind, Name, and Type are taken into account.
func encodeSecret(sec *v1.Secret) (string, error) {
// json.Marshal sorts the keys in a stable order in the encoding
data, err := json.Marshal(map[string]interface{}{"kind": "Secret", "type": sec.Type, "name": sec.Name, "data": sec.Data})
if err != nil {
return "", err
}
return string(data), nil
}
// encodeHash extracts the first 40 bits of the hash from the hex string
// (1 hex char represents 4 bits), and then maps vowels and vowel-like hex
// characters to consonants to prevent bad words from being formed (the theory
// is that no vowels makes it really hard to make bad words). Since the string
// is hex, the only vowels it can contain are 'a' and 'e'.
// We picked some arbitrary consonants to map to from the same character set as GenerateName.
// See: https://github.com/kubernetes/apimachinery/blob/dc1f89aff9a7509782bde3b68824c8043a3e58cc/pkg/util/rand/rand.go#L75
// If the hex string contains fewer than ten characters, returns an error.
func encodeHash(hex string) (string, error) {
if len(hex) < 10 {
return "", fmt.Errorf("the hex string must contain at least 10 characters")
}
enc := []rune(hex[:10])
for i := range enc {
switch enc[i] {
case '0':
enc[i] = 'g'
case '1':
enc[i] = 'h'
case '3':
enc[i] = 'k'
case 'a':
enc[i] = 'm'
case 'e':
enc[i] = 't'
}
}
return string(enc), nil
}
// hash hashes `data` with sha256 and returns the hex string
func hash(data string) string {
return fmt.Sprintf("%x", sha256.Sum256([]byte(data)))
}
func unstructuredToConfigmap(u unstructured.Unstructured) (*v1.ConfigMap, error) {
marshaled, err := json.Marshal(u.Object)
if err != nil {
return nil, err
}
var out v1.ConfigMap
err = json.Unmarshal(marshaled, &out)
return &out, err
}
func unstructuredToSecret(u unstructured.Unstructured) (*v1.Secret, error) {
marshaled, err := json.Marshal(u.Object)
if err != nil {
return nil, err
}
var out v1.Secret
err = json.Unmarshal(marshaled, &out)
return &out, err
}

View File

@@ -0,0 +1,121 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package hash
import (
"encoding/json"
"fmt"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"sigs.k8s.io/kustomize/pkg/hasher"
)
// kustHash computes a hash of an unstructured object.
type kustHash struct{}
// NewKustHash returns a kustHash object
func NewKustHash() *kustHash {
return &kustHash{}
}
// Hash returns a hash of either a ConfigMap or a Secret
func (h *kustHash) Hash(m map[string]interface{}) (string, error) {
u := unstructured.Unstructured{
Object: m,
}
kind := u.GetKind()
switch kind {
case "ConfigMap":
cm, err := unstructuredToConfigmap(u)
if err != nil {
return "", err
}
return configMapHash(cm)
case "Secret":
sec, err := unstructuredToSecret(u)
if err != nil {
return "", err
}
return secretHash(sec)
default:
return "", fmt.Errorf(
"type %s is not supported for hashing in %v", kind, m)
}
}
// configMapHash returns a hash of the ConfigMap.
// The Data, Kind, and Name are taken into account.
func configMapHash(cm *v1.ConfigMap) (string, error) {
encoded, err := encodeConfigMap(cm)
if err != nil {
return "", err
}
h, err := hasher.Encode(hasher.Hash(encoded))
if err != nil {
return "", err
}
return h, nil
}
// SecretHash returns a hash of the Secret.
// The Data, Kind, Name, and Type are taken into account.
func secretHash(sec *v1.Secret) (string, error) {
encoded, err := encodeSecret(sec)
if err != nil {
return "", err
}
h, err := hasher.Encode(hasher.Hash(encoded))
if err != nil {
return "", err
}
return h, nil
}
// encodeConfigMap encodes a ConfigMap.
// Data, Kind, and Name are taken into account.
func encodeConfigMap(cm *v1.ConfigMap) (string, error) {
// json.Marshal sorts the keys in a stable order in the encoding
m := map[string]interface{}{"kind": "ConfigMap", "name": cm.Name, "data": cm.Data}
if len(cm.BinaryData) > 0 {
m["binaryData"] = cm.BinaryData
}
data, err := json.Marshal(m)
if err != nil {
return "", err
}
return string(data), nil
}
// encodeSecret encodes a Secret.
// Data, Kind, Name, and Type are taken into account.
func encodeSecret(sec *v1.Secret) (string, error) {
// json.Marshal sorts the keys in a stable order in the encoding
data, err := json.Marshal(map[string]interface{}{"kind": "Secret", "type": sec.Type, "name": sec.Name, "data": sec.Data})
if err != nil {
return "", err
}
return string(data), nil
}
func unstructuredToConfigmap(u unstructured.Unstructured) (*v1.ConfigMap, error) {
marshaled, err := json.Marshal(u.Object)
if err != nil {
return nil, err
}
var out v1.ConfigMap
err = json.Unmarshal(marshaled, &out)
return &out, err
}
func unstructuredToSecret(u unstructured.Unstructured) (*v1.Secret, error) {
marshaled, err := json.Marshal(u.Object)
if err != nil {
return nil, err
}
var out v1.Secret
err = json.Unmarshal(marshaled, &out)
return &out, err
}

View File

@@ -1,18 +1,5 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package hash
@@ -54,7 +41,7 @@ func TestConfigMapHash(t *testing.T) {
}
for _, c := range cases {
h, err := ConfigMapHash(c.cm)
h, err := configMapHash(c.cm)
if SkipRest(t, c.desc, err, c.err) {
continue
}
@@ -80,7 +67,7 @@ func TestSecretHash(t *testing.T) {
}
for _, c := range cases {
h, err := SecretHash(c.secret)
h, err := secretHash(c.secret)
if SkipRest(t, c.desc, err, c.err) {
continue
}
@@ -90,28 +77,6 @@ func TestSecretHash(t *testing.T) {
}
}
func TestArrayHash(t *testing.T) {
array1 := []string{"a", "b", "c"}
array2 := []string{"c", "b", "a"}
h1, err := SortArrayAndComputeHash(array1)
if err != nil {
t.Errorf("unexpected error %v", err)
}
if h1 == "" {
t.Errorf("failed to hash %v", array1)
}
h2, err := SortArrayAndComputeHash(array2)
if err != nil {
t.Errorf("unexpected error %v", err)
}
if h2 == "" {
t.Errorf("failed to hash %v", array2)
}
if h1 != h2 {
t.Errorf("hash is not consistent with reordered list: %s %s", h1, h2)
}
}
func TestEncodeConfigMap(t *testing.T) {
cases := []struct {
desc string
@@ -178,15 +143,6 @@ func TestEncodeSecret(t *testing.T) {
}
}
func TestHash(t *testing.T) {
// hash the empty string to be sure that sha256 is being used
expect := "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
sum := hash("")
if expect != sum {
t.Errorf("expected hash %q but got %q", expect, sum)
}
}
// warn devs who change types that they might have to update a hash function
// not perfect, as it only checks the number of top-level fields
func TestTypeStability(t *testing.T) {

View File

@@ -1,47 +0,0 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package hash
import (
"fmt"
"sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/transformers"
)
type nameHashTransformer struct{}
var _ transformers.Transformer = &nameHashTransformer{}
// NewNameHashTransformer construct a nameHashTransformer.
func NewNameHashTransformer() transformers.Transformer {
return &nameHashTransformer{}
}
// Transform appends hash to generated resources.
func (o *nameHashTransformer) Transform(m resmap.ResMap) error {
for _, res := range m {
if res.NeedHashSuffix() {
h, err := NewKustHash().Hash(res.Map())
if err != nil {
return err
}
res.SetName(fmt.Sprintf("%s-%s", res.GetName(), h))
}
}
return nil
}

View File

@@ -0,0 +1,34 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package hash
import (
"fmt"
"sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/transformers"
)
type transformer struct{}
var _ transformers.Transformer = &transformer{}
// NewTransformer make a hash transformer.
func NewTransformer() transformers.Transformer {
return &transformer{}
}
// Transform appends hash to generated resources.
func (tf *transformer) Transform(m resmap.ResMap) error {
for _, res := range m {
if res.NeedHashSuffix() {
h, err := NewKustHash().Hash(res.Map())
if err != nil {
return err
}
res.SetName(fmt.Sprintf("%s-%s", res.GetName(), h))
}
}
return nil
}

View File

@@ -1,18 +1,5 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package hash
@@ -152,7 +139,7 @@ func TestNameHashTransformer(t *testing.T) {
}, &types.GeneratorArgs{Behavior: "create"}, &types.GeneratorOptions{DisableNameSuffixHash: false}),
}
tran := NewNameHashTransformer()
tran := NewTransformer()
tran.Transform(objs)
if !reflect.DeepEqual(objs, expected) {

View File

@@ -5,9 +5,11 @@ package inventory
import (
"fmt"
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/k8sdeps/transformer/hash"
"sigs.k8s.io/kustomize/pkg/gvk"
"sigs.k8s.io/kustomize/pkg/hasher"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/inventory"
"sigs.k8s.io/kustomize/pkg/resid"
"sigs.k8s.io/kustomize/pkg/resmap"
@@ -16,25 +18,28 @@ import (
"sigs.k8s.io/kustomize/pkg/types"
)
// inventoryTransformer compute the inventory object used in prune
type inventoryTransformer struct {
// transformer compute the inventory object used in prune
type transformer struct {
garbagePolicy types.GarbagePolicy
ldr ifc.Loader
cmName string
cmNamespace string
}
var _ transformers.Transformer = &inventoryTransformer{}
var _ transformers.Transformer = &transformer{}
// NewInventoryTransformer makes a inventoryTransformer.
func NewInventoryTransformer(
// NewTransformer makes a new inventory transformer.
func NewTransformer(
p *types.Inventory,
ldr ifc.Loader,
namespace string,
gp types.GarbagePolicy) transformers.Transformer {
if p == nil || p.Type != "ConfigMap" || p.ConfigMap.Namespace != namespace {
return transformers.NewNoOpTransformer()
}
return &inventoryTransformer{
return &transformer{
garbagePolicy: gp,
ldr: ldr,
cmName: p.ConfigMap.Name,
cmNamespace: p.ConfigMap.Namespace,
}
@@ -48,7 +53,7 @@ func NewInventoryTransformer(
// The inventory data is written to annotation since
// 1. The key in data field is constrained and couldn't include arbitrary letters
// 2. The annotation can be put into any kind of objects
func (o *inventoryTransformer) Transform(m resmap.ResMap) error {
func (tf *transformer) Transform(m resmap.ResMap) error {
invty := inventory.NewInventory()
var keys []string
for _, r := range m {
@@ -64,14 +69,14 @@ func (o *inventoryTransformer) Transform(m resmap.ResMap) error {
invty.Current[item] = refs
keys = append(keys, item.String())
}
h, err := hash.SortArrayAndComputeHash(keys)
h, err := hasher.SortArrayAndComputeHash(keys)
if err != nil {
return err
}
args := &types.ConfigMapArgs{}
args.Name = o.cmName
args.Namespace = o.cmNamespace
args.Name = tf.cmName
args.Namespace = tf.cmNamespace
opts := &types.GeneratorOptions{
Annotations: make(map[string]string),
}
@@ -82,12 +87,12 @@ func (o *inventoryTransformer) Transform(m resmap.ResMap) error {
}
kf := kunstruct.NewKunstructuredFactoryImpl()
k, err := kf.MakeConfigMap(nil, opts, args)
k, err := kf.MakeConfigMap(tf.ldr, opts, args)
if err != nil {
return err
}
if o.garbagePolicy == types.GarbageCollect {
if tf.garbagePolicy == types.GarbageCollect {
for k := range m {
delete(m, k)
}
@@ -98,8 +103,8 @@ func (o *inventoryTransformer) Transform(m resmap.ResMap) error {
Version: "v1",
Kind: "ConfigMap",
},
o.cmName,
"", o.cmNamespace)
tf.cmName,
"", tf.cmNamespace)
if _, ok := m[id]; ok {
return fmt.Errorf("id %v is already used, please use a different name in the prune field", id)
}

View File

@@ -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 := NewTransformer(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 = NewTransformer(p, ldr, "default", types.GarbageIgnore)
tran.Transform(objs)
if !reflect.DeepEqual(objs, expected) {

View File

@@ -1,18 +1,5 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package patch

View File

@@ -1,18 +1,5 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package patch
@@ -31,27 +18,27 @@ import (
"sigs.k8s.io/kustomize/pkg/transformers"
)
// patchTransformer applies patches.
type patchTransformer struct {
// transformer applies strategic merge patches.
type transformer struct {
patches []*resource.Resource
rf *resource.Factory
}
var _ transformers.Transformer = &patchTransformer{}
var _ transformers.Transformer = &transformer{}
// NewPatchTransformer constructs a patchTransformer.
func NewPatchTransformer(
// NewTransformer constructs a strategic merge patch transformer.
func NewTransformer(
slice []*resource.Resource, rf *resource.Factory) (transformers.Transformer, error) {
if len(slice) == 0 {
return transformers.NewNoOpTransformer(), nil
}
return &patchTransformer{patches: slice, rf: rf}, nil
return &transformer{patches: slice, rf: rf}, nil
}
// Transform apply the patches on top of the base resources.
func (pt *patchTransformer) Transform(baseResourceMap resmap.ResMap) error {
func (tf *transformer) Transform(baseResourceMap resmap.ResMap) error {
// Merge and then index the patches by Id.
patches, err := pt.mergePatches()
patches, err := tf.mergePatches()
if err != nil {
return err
}
@@ -118,9 +105,9 @@ func (pt *patchTransformer) Transform(baseResourceMap resmap.ResMap) error {
// mergePatches merge and index patches by Id.
// It errors out if there is conflict between patches.
func (pt *patchTransformer) mergePatches() (resmap.ResMap, error) {
func (tf *transformer) mergePatches() (resmap.ResMap, error) {
rc := resmap.ResMap{}
for ix, patch := range pt.patches {
for ix, patch := range tf.patches {
id := patch.Id()
existing, found := rc[id]
if !found {
@@ -134,9 +121,9 @@ func (pt *patchTransformer) mergePatches() (resmap.ResMap, error) {
}
var cd conflictDetector
if err != nil {
cd = newJMPConflictDetector(pt.rf)
cd = newJMPConflictDetector(tf.rf)
} else {
cd, err = newSMPConflictDetector(versionedObj, pt.rf)
cd, err = newSMPConflictDetector(versionedObj, tf.rf)
if err != nil {
return nil, err
}
@@ -147,7 +134,7 @@ func (pt *patchTransformer) mergePatches() (resmap.ResMap, error) {
return nil, err
}
if conflict {
conflictingPatch, err := cd.findConflict(ix, pt.patches)
conflictingPatch, err := cd.findConflict(ix, tf.patches)
if err != nil {
return nil, err
}

View File

@@ -1,18 +1,5 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package patch
@@ -128,7 +115,7 @@ func TestOverlayRun(t *testing.T) {
},
}),
}
lt, err := NewPatchTransformer(patch, rf)
lt, err := NewTransformer(patch, rf)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -258,7 +245,7 @@ func TestMultiplePatches(t *testing.T) {
},
}),
}
lt, err := NewPatchTransformer(patch, rf)
lt, err := NewTransformer(patch, rf)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -344,7 +331,7 @@ func TestMultiplePatchesWithConflict(t *testing.T) {
),
}
lt, err := NewPatchTransformer(patch, rf)
lt, err := NewTransformer(patch, rf)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -407,7 +394,7 @@ func TestNoSchemaOverlayRun(t *testing.T) {
}),
}
lt, err := NewPatchTransformer(patch, rf)
lt, err := NewTransformer(patch, rf)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -491,7 +478,7 @@ func TestNoSchemaMultiplePatches(t *testing.T) {
}),
}
lt, err := NewPatchTransformer(patch, rf)
lt, err := NewTransformer(patch, rf)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -549,7 +536,7 @@ func TestNoSchemaMultiplePatchesWithConflict(t *testing.T) {
}),
}
lt, err := NewPatchTransformer(patch, rf)
lt, err := NewTransformer(patch, rf)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

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

@@ -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 build
@@ -22,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"
@@ -67,12 +56,14 @@ url examples:
// NewCmdBuild creates a new build command.
func NewCmdBuild(
out io.Writer, fs fs.FileSystem,
rf *resmap.Factory,
ptf transformer.Factory,
pl *plugins.Loader) *cobra.Command {
out io.Writer, fSys fs.FileSystem,
v ifc.Validator, rf *resmap.Factory,
ptf transformer.Factory) *cobra.Command {
var o Options
pluginConfig := plugins.DefaultPluginConfig()
pl := plugins.NewLoader(pluginConfig, rf)
cmd := &cobra.Command{
Use: "build [path]",
Short: "Print current configuration per contents of " + pgmconfig.KustomizationFileNames[0],
@@ -83,16 +74,19 @@ func NewCmdBuild(
if err != nil {
return err
}
return o.RunBuild(out, fs, rf, ptf, pl)
return o.RunBuild(out, v, fSys, rf, ptf, pl)
},
}
cmd.Flags().StringVarP(
&o.outputPath,
"output", "o", "",
"If specified, write the build output to this path.")
loader.AddLoadRestrictionsFlag(cmd.Flags())
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
}
@@ -113,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
}
@@ -134,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
}
@@ -171,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
@@ -187,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

@@ -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 commands holds the CLI glue mapping textual commands/args to method calls.
package commands
@@ -23,7 +10,6 @@ import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/k8sdeps/kv/plugin"
"sigs.k8s.io/kustomize/k8sdeps/transformer"
"sigs.k8s.io/kustomize/k8sdeps/validator"
"sigs.k8s.io/kustomize/pkg/commands/build"
@@ -31,10 +17,8 @@ import (
"sigs.k8s.io/kustomize/pkg/commands/misc"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/pgmconfig"
"sigs.k8s.io/kustomize/pkg/plugins"
"sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/types"
)
// NewDefaultCommand returns the default (aka root) command for kustomize command.
@@ -51,28 +35,14 @@ See https://sigs.k8s.io/kustomize
`,
}
pluginConfig := plugin.DefaultPluginConfig()
c.PersistentFlags().BoolVar(
&pluginConfig.GoEnabled,
plugin.EnableGoPluginsFlagName,
false, plugin.EnableGoPluginsFlagHelp)
// Not advertising this alpha feature.
c.PersistentFlags().MarkHidden(plugin.EnableGoPluginsFlagName)
// Configuration for ConfigMap and Secret generators.
genMetaArgs := types.GeneratorMetaArgs{
PluginConfig: pluginConfig,
}
uf := kunstruct.NewKunstructuredFactoryWithGeneratorArgs(&genMetaArgs)
uf := kunstruct.NewKunstructuredFactoryImpl()
rf := resmap.NewFactory(resource.NewFactory(uf))
v := validator.NewKustValidator()
c.AddCommand(
build.NewCmdBuild(
stdOut, fSys,
rf,
transformer.NewFactoryImpl(),
plugins.NewLoader(pluginConfig, rf)),
edit.NewCmdEdit(fSys, validator.NewKustValidator(), uf),
stdOut, fSys, v,
rf, transformer.NewFactoryImpl()),
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

@@ -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
@@ -21,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]",
@@ -65,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
}
@@ -104,17 +92,17 @@ func addConfigMap(
ldr ifc.Loader,
k *types.Kustomization,
flags flagsAndArgs, kf ifc.KunstructuredFactory) error {
cmArgs := makeConfigMapArgs(k, flags.Name)
mergeFlagsIntoCmArgs(&cmArgs.KVSources, flags)
args := findOrMakeConfigMapArgs(k, flags.Name)
mergeFlagsIntoCmArgs(args, flags)
// Validate by trying to create corev1.configmap.
_, err := kf.MakeConfigMap(ldr, k.GeneratorOptions, cmArgs)
_, err := kf.MakeConfigMap(ldr, k.GeneratorOptions, args)
if err != nil {
return err
}
return nil
}
func makeConfigMapArgs(m *types.Kustomization, name string) *types.ConfigMapArgs {
func findOrMakeConfigMapArgs(m *types.Kustomization, name string) *types.ConfigMapArgs {
for i, v := range m.ConfigMapGenerator {
if name == v.Name {
return &m.ConfigMapGenerator[i]
@@ -126,23 +114,17 @@ func makeConfigMapArgs(m *types.Kustomization, name string) *types.ConfigMapArgs
return &m.ConfigMapGenerator[len(m.ConfigMapGenerator)-1]
}
func mergeFlagsIntoCmArgs(src *[]types.KVSource, flags flagsAndArgs) {
func mergeFlagsIntoCmArgs(args *types.ConfigMapArgs, flags flagsAndArgs) {
if len(flags.LiteralSources) > 0 {
*src = append(*src, types.KVSource{
Name: "literals",
Args: flags.LiteralSources,
})
args.LiteralSources = append(
args.LiteralSources, flags.LiteralSources...)
}
if len(flags.FileSources) > 0 {
*src = append(*src, types.KVSource{
Name: "files",
Args: flags.FileSources,
})
args.FileSources = append(
args.FileSources, flags.FileSources...)
}
if flags.EnvFileSource != "" {
*src = append(*src, types.KVSource{
Name: "envfiles",
Args: []string{flags.EnvFileSource},
})
args.EnvSources = append(
args.EnvSources, flags.EnvFileSource)
}
}

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
@@ -20,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")
}
}
@@ -39,7 +30,7 @@ func TestMakeConfigMapArgs(t *testing.T) {
if len(kustomization.ConfigMapGenerator) != 0 {
t.Fatal("Initial kustomization should not have any configmaps")
}
args := makeConfigMapArgs(kustomization, cmName)
args := findOrMakeConfigMapArgs(kustomization, cmName)
if args == nil {
t.Fatalf("args should always be non-nil")
@@ -53,7 +44,7 @@ func TestMakeConfigMapArgs(t *testing.T) {
t.Fatalf("Pointer address for newly inserted configmap generator should be same")
}
args2 := makeConfigMapArgs(kustomization, cmName)
args2 := findOrMakeConfigMapArgs(kustomization, cmName)
if args2 != args {
t.Fatalf("should have returned an existing args with name: %v", cmName)
@@ -64,51 +55,53 @@ func TestMakeConfigMapArgs(t *testing.T) {
}
}
func TestMergeFlagsIntoCmArgs_LiteralSources(t *testing.T) {
var kv []types.KVSource
mergeFlagsIntoCmArgs(&kv, flagsAndArgs{LiteralSources: []string{"k1=v1"}})
if len(kv) != 1 {
t.Fatalf("Initial literal source should have been added")
func TestMergeFlagsIntoConfigMapArgs_LiteralSources(t *testing.T) {
k := &types.Kustomization{}
args := findOrMakeConfigMapArgs(k, "foo")
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{LiteralSources: []string{"k1=v1"}})
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{LiteralSources: []string{"k2=v2"}})
if k.ConfigMapGenerator[0].LiteralSources[0] != "k1=v1" {
t.Fatalf("expected v1")
}
mergeFlagsIntoCmArgs(&kv, flagsAndArgs{LiteralSources: []string{"k2=v2"}})
if len(kv) != 2 {
t.Fatalf("Second literal source should have been added")
if k.ConfigMapGenerator[0].LiteralSources[1] != "k2=v2" {
t.Fatalf("expected v2")
}
}
func TestMergeFlagsIntoCmArgs_FileSources(t *testing.T) {
var kv []types.KVSource
mergeFlagsIntoCmArgs(&kv, flagsAndArgs{FileSources: []string{"file1"}})
if len(kv) != 1 {
t.Fatalf("Initial file source should have been added")
func TestMergeFlagsIntoConfigMapArgs_FileSources(t *testing.T) {
k := &types.Kustomization{}
args := findOrMakeConfigMapArgs(k, "foo")
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{FileSources: []string{"file1"}})
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{FileSources: []string{"file2"}})
if k.ConfigMapGenerator[0].FileSources[0] != "file1" {
t.Fatalf("expected file1")
}
mergeFlagsIntoCmArgs(&kv, flagsAndArgs{FileSources: []string{"file2"}})
if len(kv) != 2 {
t.Fatalf("Second file source should have been added")
if k.ConfigMapGenerator[0].FileSources[1] != "file2" {
t.Fatalf("expected file2")
}
}
func TestMergeFlagsIntoCmArgs_EnvSource(t *testing.T) {
envFileName := "env1"
envFileName2 := "env2"
var kv []types.KVSource
mergeFlagsIntoCmArgs(&kv, flagsAndArgs{EnvFileSource: envFileName})
if len(kv) != 1 {
t.Fatalf("Initial env source should have been added")
func TestMergeFlagsIntoConfigMapArgs_EnvSource(t *testing.T) {
k := &types.Kustomization{}
args := findOrMakeConfigMapArgs(k, "foo")
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{EnvFileSource: "env1"})
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{EnvFileSource: "env2"})
if k.ConfigMapGenerator[0].EnvSources[0] != "env1" {
t.Fatalf("expected env1")
}
mergeFlagsIntoCmArgs(&kv, flagsAndArgs{EnvFileSource: envFileName2})
if len(kv) != 2 {
t.Fatalf("Second env source should have been added")
if k.ConfigMapGenerator[0].EnvSources[1] != "env2" {
t.Fatalf("expected env2")
}
}

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 add
@@ -21,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]",
@@ -65,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
}
@@ -109,45 +97,41 @@ func addSecret(
ldr ifc.Loader,
k *types.Kustomization,
flags flagsAndArgs, kf ifc.KunstructuredFactory) error {
secretArgs := makeSecretArgs(k, flags.Name, flags.Type)
mergeFlagsIntoSecretArgs(&secretArgs.KVSources, flags)
args := findOrMakeSecretArgs(k, flags.Name, flags.Type)
mergeFlagsIntoGeneratorArgs(&args.GeneratorArgs, flags)
// Validate by trying to create corev1.secret.
_, err := kf.MakeSecret(ldr, k.GeneratorOptions, secretArgs)
_, err := kf.MakeSecret(ldr, k.GeneratorOptions, args)
if err != nil {
return err
}
return nil
}
func makeSecretArgs(m *types.Kustomization, name, secretType string) *types.SecretArgs {
func findOrMakeSecretArgs(m *types.Kustomization, name, secretType string) *types.SecretArgs {
for i, v := range m.SecretGenerator {
if name == v.Name {
return &m.SecretGenerator[i]
}
}
// secret not found, create new one and add it to the kustomization file.
secret := &types.SecretArgs{GeneratorArgs: types.GeneratorArgs{Name: name}, Type: secretType}
secret := &types.SecretArgs{
GeneratorArgs: types.GeneratorArgs{Name: name},
Type: secretType}
m.SecretGenerator = append(m.SecretGenerator, *secret)
return &m.SecretGenerator[len(m.SecretGenerator)-1]
}
func mergeFlagsIntoSecretArgs(src *[]types.KVSource, flags flagsAndArgs) {
func mergeFlagsIntoGeneratorArgs(args *types.GeneratorArgs, flags flagsAndArgs) {
if len(flags.LiteralSources) > 0 {
*src = append(*src, types.KVSource{
Name: "literals",
Args: flags.LiteralSources,
})
args.LiteralSources = append(
args.LiteralSources, flags.LiteralSources...)
}
if len(flags.FileSources) > 0 {
*src = append(*src, types.KVSource{
Name: "files",
Args: flags.FileSources,
})
args.FileSources = append(
args.FileSources, flags.FileSources...)
}
if flags.EnvFileSource != "" {
*src = append(*src, types.KVSource{
Name: "envfiles",
Args: []string{flags.EnvFileSource},
})
args.EnvSources = append(
args.EnvSources, flags.EnvFileSource)
}
}

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 add
@@ -20,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")
}
}
@@ -41,7 +32,7 @@ func TestMakeSecretArgs(t *testing.T) {
if len(kustomization.SecretGenerator) != 0 {
t.Fatal("Initial kustomization should not have any secrets")
}
args := makeSecretArgs(kustomization, secretName, secretType)
args := findOrMakeSecretArgs(kustomization, secretName, secretType)
if args == nil {
t.Fatalf("args should always be non-nil")
@@ -55,7 +46,7 @@ func TestMakeSecretArgs(t *testing.T) {
t.Fatalf("Pointer address for newly inserted secret generator should be same")
}
args2 := makeSecretArgs(kustomization, secretName, secretType)
args2 := findOrMakeSecretArgs(kustomization, secretName, secretType)
if args2 != args {
t.Fatalf("should have returned an existing args with name: %v", secretName)
@@ -67,50 +58,52 @@ func TestMakeSecretArgs(t *testing.T) {
}
func TestMergeFlagsIntoSecretArgs_LiteralSources(t *testing.T) {
var kv []types.KVSource
mergeFlagsIntoSecretArgs(&kv, flagsAndArgs{LiteralSources: []string{"k1=v1"}})
if len(kv) != 1 {
t.Fatalf("Initial literal source should have been added")
k := &types.Kustomization{}
args := findOrMakeSecretArgs(k, "foo", "forbidden")
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{LiteralSources: []string{"k1=v1"}})
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{LiteralSources: []string{"k2=v2"}})
if k.SecretGenerator[0].LiteralSources[0] != "k1=v1" {
t.Fatalf("expected v1")
}
mergeFlagsIntoSecretArgs(&kv, flagsAndArgs{LiteralSources: []string{"k2=v2"}})
if len(kv) != 2 {
t.Fatalf("Second literal source should have been added")
if k.SecretGenerator[0].LiteralSources[1] != "k2=v2" {
t.Fatalf("expected v2")
}
}
func TestMergeFlagsIntoSecretArgs_FileSources(t *testing.T) {
var kv []types.KVSource
mergeFlagsIntoSecretArgs(&kv, flagsAndArgs{FileSources: []string{"file1"}})
if len(kv) != 1 {
t.Fatalf("Initial file source should have been added")
k := &types.Kustomization{}
args := findOrMakeSecretArgs(k, "foo", "forbidden")
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{FileSources: []string{"file1"}})
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{FileSources: []string{"file2"}})
if k.SecretGenerator[0].FileSources[0] != "file1" {
t.Fatalf("expected file1")
}
mergeFlagsIntoSecretArgs(&kv, flagsAndArgs{FileSources: []string{"file2"}})
if len(kv) != 2 {
t.Fatalf("Second file source should have been added")
if k.SecretGenerator[0].FileSources[1] != "file2" {
t.Fatalf("expected file2")
}
}
func TestMergeFlagsIntoSecretArgs_EnvSource(t *testing.T) {
envFileName := "env1"
envFileName2 := "env2"
var kv []types.KVSource
mergeFlagsIntoSecretArgs(&kv, flagsAndArgs{EnvFileSource: envFileName})
if len(kv) != 1 {
t.Fatalf("Initial env source should have been added")
k := &types.Kustomization{}
args := findOrMakeSecretArgs(k, "foo", "forbidden")
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{EnvFileSource: "env1"})
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{EnvFileSource: "env2"})
if k.SecretGenerator[0].EnvSources[0] != "env1" {
t.Fatalf("expected env1")
}
mergeFlagsIntoSecretArgs(&kv, flagsAndArgs{EnvFileSource: envFileName2})
if len(kv) != 2 {
t.Fatalf("Second env source should have been added")
if k.SecretGenerator[0].EnvSources[1] != "env2" {
t.Fatalf("expected env2")
}
}

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
}

52
pkg/hasher/hasher.go Normal file
View File

@@ -0,0 +1,52 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package hasher
import (
"crypto/sha256"
"encoding/json"
"fmt"
"sort"
)
// SortArrayAndComputeHash sorts a string array and
// returns a hash for it
func SortArrayAndComputeHash(s []string) (string, error) {
sort.Strings(s)
data, err := json.Marshal(s)
if err != nil {
return "", err
}
return Encode(Hash(string(data)))
}
// Copied from https://github.com/kubernetes/kubernetes
// /blob/master/pkg/kubectl/util/hash/hash.go
func Encode(hex string) (string, error) {
if len(hex) < 10 {
return "", fmt.Errorf(
"input length must be at least 10.")
}
enc := []rune(hex[:10])
for i := range enc {
switch enc[i] {
case '0':
enc[i] = 'g'
case '1':
enc[i] = 'h'
case '3':
enc[i] = 'k'
case 'a':
enc[i] = 'm'
case 'e':
enc[i] = 't'
}
}
return string(enc), nil
}
// Hash returns the hex form of the sha256 of the argument.
func Hash(data string) string {
return fmt.Sprintf("%x", sha256.Sum256([]byte(data)))
}

41
pkg/hasher/hasher_test.go Normal file
View File

@@ -0,0 +1,41 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package hasher_test
import (
"testing"
. "sigs.k8s.io/kustomize/pkg/hasher"
)
func TestSortArrayAndComputeHash(t *testing.T) {
array1 := []string{"a", "b", "c", "d"}
array2 := []string{"c", "b", "d", "a"}
h1, err := SortArrayAndComputeHash(array1)
if err != nil {
t.Errorf("unexpected error %v", err)
}
if h1 == "" {
t.Errorf("failed to hash %v", array1)
}
h2, err := SortArrayAndComputeHash(array2)
if err != nil {
t.Errorf("unexpected error %v", err)
}
if h2 == "" {
t.Errorf("failed to hash %v", array2)
}
if h1 != h2 {
t.Errorf("hash is not consistent with reordered list: %s %s", h1, h2)
}
}
func TestHash(t *testing.T) {
// hash the empty string to be sure that sha256 is being used
expect := "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
sum := Hash("")
if expect != sum {
t.Errorf("expected hash %q but got %q", expect, sum)
}
}

View File

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

@@ -11,7 +11,6 @@ import (
"sigs.k8s.io/kustomize/internal/loadertest"
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/k8sdeps/kv/plugin"
"sigs.k8s.io/kustomize/k8sdeps/transformer"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/pgmconfig"
@@ -33,12 +32,12 @@ type KustTestHarness struct {
func NewKustTestHarness(t *testing.T, path string) *KustTestHarness {
return NewKustTestHarnessWithPluginConfig(
t, path, plugin.DefaultPluginConfig())
t, path, plugins.DefaultPluginConfig())
}
func NewKustTestPluginHarness(t *testing.T, path string) *KustTestHarness {
return NewKustTestHarnessWithPluginConfig(
t, path, plugin.ActivePluginConfig())
t, path, plugins.ActivePluginConfig())
}
func NewKustTestHarnessWithPluginConfig(
@@ -51,8 +50,7 @@ func NewKustTestHarnessFull(
t *testing.T, path string,
lr loader.LoadRestrictorFunc, pc *types.PluginConfig) *KustTestHarness {
rf := resmap.NewFactory(resource.NewFactory(
kunstruct.NewKunstructuredFactoryWithGeneratorArgs(
&types.GeneratorMetaArgs{PluginConfig: pc})))
kunstruct.NewKunstructuredFactoryImpl()))
return &KustTestHarness{
t: t,
rf: rf,

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

@@ -24,7 +24,6 @@ import (
"strings"
"time"
"sigs.k8s.io/kustomize/k8sdeps/kv/plugin"
"sigs.k8s.io/kustomize/pkg/pgmconfig"
)
@@ -56,7 +55,7 @@ func DefaultSrcRoot() (string, error) {
}
nope = append(nope, root)
root = plugin.DefaultPluginConfig().DirectoryPath
root = DefaultPluginConfig().DirectoryPath
if FileExists(root) {
return root, nil
}

View File

@@ -1,19 +1,53 @@
/*
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 plugins
const PluginSymbol = "KustomizePlugin"
import (
"fmt"
"github.com/spf13/pflag"
"path/filepath"
"sigs.k8s.io/kustomize/pkg/pgmconfig"
"sigs.k8s.io/kustomize/pkg/types"
)
const (
PluginSymbol = "KustomizePlugin"
flagEnablePluginsName = "enable_alpha_plugins"
flagEnablePluginsHelp = `enable plugins, an alpha feature.
See https://github.com/kubernetes-sigs/kustomize/blob/master/docs/plugins.md
`
flagErrorFmt = `
unable to load plugin %s because plugins disabled
specify the flag
--%s
to %s`
)
func ActivePluginConfig() *types.PluginConfig {
pc := DefaultPluginConfig()
pc.Enabled = true
return pc
}
func DefaultPluginConfig() *types.PluginConfig {
return &types.PluginConfig{
Enabled: false,
DirectoryPath: filepath.Join(
pgmconfig.ConfigRoot(), pgmconfig.PluginRoot),
}
}
func NotEnabledErr(name string) error {
return fmt.Errorf(
flagErrorFmt,
name,
flagEnablePluginsName,
flagEnablePluginsHelp)
}
func AddEnablePluginsFlag(set *pflag.FlagSet, v *bool) {
set.BoolVar(
v, flagEnablePluginsName,
false, flagEnablePluginsHelp)
}

View File

@@ -22,7 +22,6 @@ import (
"sigs.k8s.io/kustomize/internal/loadertest"
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/k8sdeps/kv/plugin"
"sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/resource"
)
@@ -52,7 +51,7 @@ s/$BAR/bar/g
p := NewExecPlugin(
AbsolutePluginPath(
plugin.DefaultPluginConfig(),
DefaultPluginConfig(),
pluginConfig.Id()))
yaml, err := pluginConfig.AsYAML()

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 plugins
@@ -115,9 +102,8 @@ 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.GoEnabled {
return nil, errors.Errorf(
"plugins not enabled, but trying to load %s", res.Id())
if !l.pc.Enabled {
return nil, NotEnabledErr(res.Id().Gvk().Kind)
}
if p := NewExecPlugin(
l.absolutePluginPath(res.Id())); p.isAvailable() {

View File

@@ -8,8 +8,7 @@ import (
"sigs.k8s.io/kustomize/internal/loadertest"
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
kvplugin "sigs.k8s.io/kustomize/k8sdeps/kv/plugin"
"sigs.k8s.io/kustomize/pkg/plugins"
. "sigs.k8s.io/kustomize/pkg/plugins"
"sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/plugin"
@@ -54,7 +53,7 @@ func TestLoader(t *testing.T) {
rmF := resmap.NewFactory(resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl()))
l := plugins.NewLoader(kvplugin.ActivePluginConfig(), rmF)
l := NewLoader(ActivePluginConfig(), rmF)
if l == nil {
t.Fatal("expect non-nil loader")
}

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

@@ -17,10 +17,10 @@ limitations under the License.
package target_test
import (
"sigs.k8s.io/kustomize/pkg/plugins"
"strings"
"testing"
"sigs.k8s.io/kustomize/k8sdeps/kv/plugin"
"sigs.k8s.io/kustomize/pkg/kusttest"
"sigs.k8s.io/kustomize/pkg/loader"
)
@@ -225,7 +225,7 @@ spec:
func TestSharedPatchDisAllowed(t *testing.T) {
th := kusttest_test.NewKustTestHarnessFull(
t, "/app/overlay",
loader.RestrictionRootOnly, plugin.DefaultPluginConfig())
loader.RestrictionRootOnly, plugins.DefaultPluginConfig())
writeSmallBase(th)
th.WriteK("/app/overlay", `
commonLabels:
@@ -257,7 +257,7 @@ spec:
func TestSharedPatchAllowed(t *testing.T) {
th := kusttest_test.NewKustTestHarnessFull(
t, "/app/overlay",
loader.RestrictionNone, plugin.DefaultPluginConfig())
loader.RestrictionNone, plugins.DefaultPluginConfig())
writeSmallBase(th)
th.WriteK("/app/overlay", `
commonLabels:

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 target_test
import (
"testing"
"sigs.k8s.io/kustomize/pkg/kusttest"
)
const result = `
apiVersion: v1
data:
FRUIT: YXBwbGU=
MOUNTAIN: ZXZlcmVzdA==
OCEAN: cGFjaWZpYw==
VEGETABLE: Y2Fycm90
foo.env: Ck1PVU5UQUlOPWV2ZXJlc3QKT0NFQU49cGFjaWZpYwo=
passphrase: ZGF0IHBocmFzZQ==
kind: Secret
metadata:
name: bob-kf5c9fccbt
type: Opaque
`
func writeDataFiles(th *kusttest_test.KustTestHarness) {
th.WriteF("/app/foo.env", `
MOUNTAIN=everest
OCEAN=pacific
`)
th.WriteF("/app/phrase.dat", "dat phrase")
}
func TestBuiltinPlugins(t *testing.T) {
th := kusttest_test.NewKustTestHarness(t, "/app")
th.WriteK("/app", `
secretGenerator:
- name: bob
kvSources:
- pluginType: builtin
name: literals
args:
- FRUIT=apple
- VEGETABLE=carrot
- pluginType: builtin
name: files
args:
- foo.env
- passphrase=phrase.dat
- pluginType: builtin
name: envfiles
args:
- foo.env
`)
writeDataFiles(th)
m, err := th.MakeKustTarget().MakeCustomizedResMap()
if err != nil {
t.Fatalf("Err: %v", err)
}
th.AssertActualEqualsExpected(m, result)
}
func TestBuiltinIsTheDefault(t *testing.T) {
th := kusttest_test.NewKustTestHarness(t, "/app")
th.WriteK("/app", `
secretGenerator:
- name: bob
kvSources:
- name: literals
args:
- FRUIT=apple
- VEGETABLE=carrot
- name: files
args:
- foo.env
- passphrase=phrase.dat
- name: envfiles
args:
- foo.env
`)
writeDataFiles(th)
m, err := th.MakeKustTarget().MakeCustomizedResMap()
if err != nil {
t.Fatalf("Err: %v", err)
}
th.AssertActualEqualsExpected(m, result)
}

View File

@@ -61,7 +61,7 @@ kind: Secret
metadata:
labels:
app: release-name-minecraft
chart: minecraft-1.0.0
chart: minecraft-1.0.1
heritage: Tiller
release: release-name
name: LOOOOOOOONG-release-name-minecraft
@@ -72,7 +72,7 @@ kind: Service
metadata:
labels:
app: release-name-minecraft
chart: minecraft-1.0.0
chart: minecraft-1.0.1
heritage: Tiller
release: release-name
name: LOOOOOOOONG-release-name-minecraft
@@ -93,7 +93,7 @@ metadata:
volume.alpha.kubernetes.io/storage-class: default
labels:
app: release-name-minecraft
chart: minecraft-1.0.0
chart: minecraft-1.0.1
heritage: Tiller
release: release-name
name: LOOOOOOOONG-release-name-minecraft-datadir

View File

@@ -31,24 +31,26 @@ namePrefix: blah-
configMapGenerator:
- name: bob
literals:
- fruit=apple
- vegetable=broccoli
env: foo.env
- fruit=apple
- vegetable=broccoli
envs:
- foo.env
files:
- passphrase=phrase.dat
- forces.txt
- passphrase=phrase.dat
- forces.txt
- name: json
literals:
- 'v2=[{"path": "var/druid/segment-cache"}]'
- 'v2=[{"path": "var/druid/segment-cache"}]'
secretGenerator:
- name: bob
literals:
- fruit=apple
- vegetable=broccoli
env: foo.env
- fruit=apple
- vegetable=broccoli
envs:
- foo.env
files:
- passphrase=phrase.dat
- forces.txt
- passphrase=phrase.dat
- forces.txt
`)
th.WriteF("/app/foo.env", `
MOUNTAIN=everest
@@ -122,15 +124,15 @@ configMapGenerator:
- name: bob
behavior: create
literals:
- bean=pinto
- star=wolf-rayet
- bean=pinto
- star=wolf-rayet
literals:
- fruit=apple
- vegetable=broccoli
- fruit=apple
- vegetable=broccoli
files:
- forces.txt
- forces.txt
files:
- nobles=nobility.txt
- nobles=nobility.txt
`)
th.WriteF("/app/forces.txt", `
gravitational
@@ -177,7 +179,7 @@ configMapGenerator:
- name: com1
behavior: create
literals:
- from=base
- from=base
`)
th.WriteK("/app/base2", `
namePrefix: p2-
@@ -185,7 +187,7 @@ configMapGenerator:
- name: com2
behavior: create
literals:
- from=base
- from=base
`)
th.WriteK("/app/overlay/o1", `
bases:
@@ -194,7 +196,7 @@ configMapGenerator:
- name: com1
behavior: merge
literals:
- from=overlay
- from=overlay
`)
th.WriteK("/app/overlay/o2", `
bases:
@@ -203,7 +205,7 @@ configMapGenerator:
- name: com2
behavior: merge
literals:
- from=overlay
- from=overlay
`)
th.WriteK("/app/overlay", `
bases:
@@ -213,8 +215,8 @@ configMapGenerator:
- name: com1
behavior: merge
literals:
- foo=bar
- baz=qux
- foo=bar
- baz=qux
`)
m, err := th.MakeKustTarget().MakeCustomizedResMap()
if err != nil {

View File

@@ -178,14 +178,14 @@ resources:
- deployment.yaml
- service.yaml
configMapGenerator:
- name: configmap-in-base
literals:
- foo=bar
- name: configmap-in-base
literals:
- foo=bar
secretGenerator:
- name: secret-in-base
literals:
- username=admin
- password=somepw
- username=admin
- password=somepw
`)
th.WriteF("/app/deployment.yaml", `
apiVersion: apps/v1beta2
@@ -354,18 +354,18 @@ patchesStrategicMerge:
bases:
- ../app
configMapGenerator:
- name: configmap-in-overlay
literals:
- hello=world
- name: configmap-in-base
behavior: replace
literals:
- foo=override-bar
- name: configmap-in-overlay
literals:
- hello=world
- name: configmap-in-base
behavior: replace
literals:
- foo=override-bar
secretGenerator:
- name: secret-in-base
behavior: merge
literals:
- proxy=haproxy
- proxy=haproxy
`)
m, err := th.MakeKustTarget().MakeCustomizedResMap()
if err != nil {

View File

@@ -17,14 +17,50 @@ limitations under the License.
package target_test
import (
"path/filepath"
"strings"
"testing"
"sigs.k8s.io/kustomize/k8sdeps/kv/plugin"
"sigs.k8s.io/kustomize/pkg/kusttest"
)
func TestSecretGenerator(t *testing.T) {
th := kusttest_test.NewKustTestHarness(t, "/app")
th.WriteK("/app", `
secretGenerator:
- name: bob
literals:
- FRUIT=apple
- VEGETABLE=carrot
files:
- foo.env
- passphrase=phrase.dat
envs:
- foo.env
`)
th.WriteF("/app/foo.env", `
MOUNTAIN=everest
OCEAN=pacific
`)
th.WriteF("/app/phrase.dat", "dat phrase")
m, err := th.MakeKustTarget().MakeCustomizedResMap()
if err != nil {
t.Fatalf("Err: %v", err)
}
th.AssertActualEqualsExpected(m, `
apiVersion: v1
data:
FRUIT: YXBwbGU=
MOUNTAIN: ZXZlcmVzdA==
OCEAN: cGFjaWZpYw==
VEGETABLE: Y2Fycm90
foo.env: Ck1PVU5UQUlOPWV2ZXJlc3QKT0NFQU49cGFjaWZpYwo=
passphrase: ZGF0IHBocmFzZQ==
kind: Secret
metadata:
name: bob-kf5c9fccbt
type: Opaque
`)
}
func TestGeneratorOptionsWithBases(t *testing.T) {
th := kusttest_test.NewKustTestHarness(t, "/app/overlay")
th.WriteK("/app/base", `
@@ -77,47 +113,3 @@ metadata:
name: shouldNotHaveHash
`)
}
func TestGoPluginNotEnabled(t *testing.T) {
th := kusttest_test.NewKustTestHarness(t, "/app")
th.WriteK("/app", `
secretGenerator:
- name: attemptGoPlugin
kvSources:
- name: foo
pluginType: go
args:
- someArg
- someOtherArg
`)
_, err := th.MakeKustTarget().MakeCustomizedResMap()
if err == nil {
t.Fatalf("expected error")
}
if !strings.Contains(err.Error(), "enable go plugins by ") {
t.Fatalf("unexpected err: %v", err)
}
}
func TestGoPluginDoesNotExist(t *testing.T) {
th := kusttest_test.NewKustTestHarnessWithPluginConfig(
t, "/app", plugin.ActivePluginConfig())
th.WriteK("/app", `
secretGenerator:
- name: attemptGoPlugin
kvSources:
- name: foo
pluginType: go
args:
- someArg
- someOtherArg
`)
_, err := th.MakeKustTarget().MakeCustomizedResMap()
if err == nil {
t.Fatalf("expected error")
}
if !strings.Contains(err.Error(),
filepath.Join("kvSources", "foo.so")) {
t.Fatalf("unexpected err: %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

@@ -38,9 +38,9 @@ resources:
- deployment.yaml
- service.yaml
configMapGenerator:
- name: configmap-in-base
literals:
- foo=bar
- name: configmap-in-base
literals:
- foo=bar
`)
th.WriteF("/app/base/deployment.yaml", `
apiVersion: apps/v1beta2
@@ -93,9 +93,9 @@ patchesStrategicMerge:
bases:
- ../../base
configMapGenerator:
- name: configmap-in-overlay
literals:
- hello=world
- name: configmap-in-overlay
literals:
- hello=world
`)
}

View File

@@ -42,10 +42,10 @@ secretGenerator:
- name: the-non-default-namespace-secret
namespace: non-default
literals:
- password.txt=verySecret
- password.txt=verySecret
- name: the-secret
literals:
- password.txt=anotherSecret
- password.txt=anotherSecret
`)
m, err := th.MakeKustTarget().MakeCustomizedResMap()
if err != nil {

View File

@@ -10,7 +10,6 @@ import (
"testing"
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
kvplugin "sigs.k8s.io/kustomize/k8sdeps/kv/plugin"
"sigs.k8s.io/kustomize/k8sdeps/transformer"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/kusttest"
@@ -19,7 +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/types"
"sigs.k8s.io/kustomize/pkg/validators"
"sigs.k8s.io/kustomize/plugin"
)
@@ -58,17 +57,15 @@ 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)
}
rf := resmap.NewFactory(resource.NewFactory(
kunstruct.NewKunstructuredFactoryWithGeneratorArgs(
&types.GeneratorMetaArgs{
PluginConfig: kvplugin.ActivePluginConfig(),
})))
kunstruct.NewKunstructuredFactoryImpl()))
pl := plugins.NewLoader(kvplugin.ActivePluginConfig(), rf)
pl := plugins.NewLoader(plugins.ActivePluginConfig(), rf)
tg, err := target.NewKustTarget(ldr, rf, transformer.NewFactoryImpl(), pl)
if err != nil {
t.Fatalf("err %v", err)

View File

@@ -4,6 +4,7 @@
package target_test
import (
"strings"
"testing"
"sigs.k8s.io/kustomize/pkg/kusttest"
@@ -91,6 +92,29 @@ spec:
`)
}
func TestPluginsNotEnabled(t *testing.T) {
tc := plugin.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(
"someteam.example.com", "v1", "StringPrefixer")
th := kusttest_test.NewKustTestHarness(t, "/app")
th.WriteK("/app", `
transformers:
- stringPrefixer.yaml
`)
writeStringPrefixer(th, "/app/stringPrefixer.yaml")
_, err := th.MakeKustTarget().MakeCustomizedResMap()
if err == nil {
t.Fatalf("expected error")
}
if !strings.Contains(err.Error(), "unable to load plugin StringPrefixer") {
t.Fatalf("unexpected err: %v", err)
}
}
func TestSedTransformer(t *testing.T) {
tc := plugin.NewEnvForTest(t).Set()
defer tc.Reset()

View File

@@ -32,8 +32,8 @@ resources:
configMapGenerator:
- name: test-config-map
literals:
- foo=bar
- baz=qux
- foo=bar
- baz=qux
vars:
- name: CDB_PUBLIC_SVC
objref:

View File

@@ -111,7 +111,7 @@ commonLabels:
kind: StatefulSet
- path: spec/volumeClaimTemplates/metadata/labels
create: true
create: false
group: apps
kind: StatefulSet

View File

@@ -252,6 +252,12 @@ nameReference:
- path: spec/service/name
kind: APIService
group: apiregistration.k8s.io
- path: webhooks/clientConfig/service/name
kind: ValidatingWebhookConfiguration
group: admissionregistration.k8s.io
- path: webhooks/clientConfig/service/name
kind: MutatingWebhookConfiguration
group: admissionregistration.k8s.io
- kind: Role
group: rbac.authorization.k8s.io

View File

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

@@ -222,15 +222,6 @@ type GeneratorArgs struct {
// DataSources for the generator.
DataSources `json:",inline,omitempty" yaml:",inline,omitempty"`
// KVSources for the generator.
KVSources []KVSource `json:",inline,omitempty" yaml:",inline,omitempty"`
}
// GeneratorMetaArgs contains arguments common to generators
// that come from somewhere other than a kustomization file.
type GeneratorMetaArgs struct {
PluginConfig *PluginConfig
}
// PluginConfig holds plugin configuration.
@@ -241,9 +232,8 @@ type PluginConfig struct {
// further categorizing plugins.
DirectoryPath string
// GoEnabled is true if goplugins are enabled.
// See https://golang.org/pkg/plugin
GoEnabled bool
// Enabled is true if plugins are enabled.
Enabled bool
}
// ConfigMapArgs contains the metadata of how to generate a configmap.
@@ -252,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

@@ -24,6 +24,10 @@
# Example execution:
# ./plugin/someteam.example.com/v1/ChartInflator configFile.yaml
# TODO: allow specification of a specific chart VERSION
# so this test doesn't break every time minecraft is upgraded :P
# See https://github.com/helm/helm/issues/4008
set -e
# Yaml parsing is a ridiculous thing to do in bash,

View File

@@ -42,7 +42,7 @@ kind: Secret
metadata:
labels:
app: release-name-minecraft
chart: minecraft-1.0.0
chart: minecraft-1.0.1
heritage: Tiller
release: release-name
name: release-name-minecraft
@@ -53,7 +53,7 @@ kind: Service
metadata:
labels:
app: release-name-minecraft
chart: minecraft-1.0.0
chart: minecraft-1.0.1
heritage: Tiller
release: release-name
name: release-name-minecraft
@@ -74,7 +74,7 @@ metadata:
volume.alpha.kubernetes.io/storage-class: default
labels:
app: release-name-minecraft
chart: minecraft-1.0.0
chart: minecraft-1.0.1
heritage: Tiller
release: release-name
name: release-name-minecraft-datadir

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