Compare commits

..

35 Commits

Author SHA1 Message Date
Kubernetes Prow Robot
30b378a924 Merge pull request #1325 from keleustes/residequals-namereference
NameReference Transformer needs to account for namespace and cluster wide objects.
2019-07-17 13:20:13 -07:00
Kubernetes Prow Robot
3a843f1eca Merge pull request #1362 from Liujingfang1/doc
update the latest version in readme
2019-07-17 13:04:14 -07:00
Jerome Brette
9b40f8ab47 Implement code review comments to NameReferenceTransformer changes.
- Add comments where code with potentially misleading.
- Rename functions according to comments
2019-07-17 14:10:01 -05:00
jingfangliu
dc6dcd8150 update the latest version in readme 2019-07-17 12:05:12 -07:00
Kubernetes Prow Robot
3cb6c7f1f4 Merge pull request #1349 from yujunz/faq
Add FAQ about how to customize configuration
2019-07-17 11:26:11 -07:00
Kubernetes Prow Robot
7632839bc8 Merge pull request #1350 from yujunz/docs/plugins
Convert go plugin example to GPG based
2019-07-17 10:40:37 -07:00
Yujun Zhang
c3ea109b59 Update goPluginGuidedExample.md 2019-07-17 08:19:50 +08:00
Jerome Brette
579995dc8a Address simultaneous transformation of name and namespace
Namereference handler needs to address simulatenous change of
name and namespace in ClusterRoleBinding for instance.
2019-07-16 18:17:33 -05:00
Jerome Brette
b43bd5440d Update Issue 1264 Reproduction Test 2019-07-16 18:17:33 -05:00
Jerome Brette
c4d899f7f3 Improve NameReference Test cases
- Add more NameReference Namespace tests
- Address issue when mixing empty/no namespace and default namespace.
- Address ClusterRoleBinding subjects field pointing at multiple namespaces.
2019-07-16 18:17:33 -05:00
Jerome Brette
7998ee7036 Addresses slice case with notNamespaceable objects 2019-07-16 18:17:33 -05:00
Kubernetes Prow Robot
878960d7b1 Merge pull request #1355 from Liujingfang1/patch
enable extended patch transformer and add tests
2019-07-16 15:58:34 -07:00
jingfangliu
ed0cfc685b add test for extended patch with overlapping patches 2019-07-16 15:16:00 -07:00
Kubernetes Prow Robot
b0a7345123 Merge pull request #1359 from keleustes/imagetag
Address replacement of digest by ImageTransformer
2019-07-16 13:10:49 -07:00
Jerome Brette
580963ea76 Address replacement of digest by ImageTransformer
- See [Issue 1357](https://github.com/kubernetes-sigs/kustomize/issues/1357)
- Add more plugin tests.
2019-07-16 14:03:56 -05:00
Kubernetes Prow Robot
0707deae95 Merge pull request #1356 from keleustes/droppatch
Test tracking issue patchesStrategicMerge elements can be dropped
2019-07-16 10:50:18 -07:00
Yujun Zhang
fb44880b8c Add back GCP KMS example 2019-07-16 20:10:16 +08:00
Jerome Brette
e5ebca6604 Test tracking issue "patchesStrategicMerge elements can be dropped"
- Issue 1354
- $patch: delete is ignored or not depending of the include order
  in the kustomization.yaml
2019-07-15 19:02:52 -05:00
jingfangliu
f5fc9acb84 fix local test failures 2019-07-15 18:59:16 -05:00
jingfangliu
28d1bad3cb fix the ci failure 2019-07-15 18:58:52 -05:00
jingfangliu
6f74419628 fix local test failures 2019-07-15 16:34:13 -07:00
jingfangliu
8121467c1e fix the ci failure 2019-07-15 16:01:23 -07:00
jingfangliu
a85f297f31 enable extended patch transformer and add tests 2019-07-15 15:45:08 -07:00
Kubernetes Prow Robot
76a7816aeb Merge pull request #1348 from yujunz/nameref
Add storage class name ref
2019-07-15 13:17:25 -07:00
Kubernetes Prow Robot
7872405379 Merge pull request #1336 from richardmarshall/fix_test_flags
Remove go testing flags from kustomize help
2019-07-15 13:13:24 -07:00
Kubernetes Prow Robot
6c17a3409f Merge pull request #1346 from Liujingfang1/patchallkinds
add extended patch transformer
2019-07-15 11:45:24 -07:00
Yujun Zhang
f1dbab9dee Convert go plugin example to GPG based 2019-07-14 11:33:37 +08:00
Yujun Zhang
bfafbbf47f Add FAQ about how to customize configuration 2019-07-14 10:39:45 +08:00
Yujun Zhang
08d7c35da7 Add storage class name ref 2019-07-14 10:05:19 +08:00
Kubernetes Prow Robot
f12704f6c1 Merge pull request #1331 from Rjerk/fix-vp-doc
docs/versioningPolicy.md: fix expired urls
2019-07-12 14:41:05 -07:00
Tony Hsu
0edab60b30 Fix typo: kubectl v1.15 -> kubectl v1.14 (#1333)
* Fix typo: kubectl v1.15 -> kubectl v1.14

Match version number to the version in the link.

* Add both kubectl v1.14 and v1.15

* Add both kubectl v1.14 and v1.15
2019-07-12 14:38:10 -07:00
jingfangliu
3c05e2d664 add extended patch transformer 2019-07-12 14:34:08 -07:00
Richard Marshall
095333ffb1 Update references to NewEnvForTest 2019-07-10 20:43:50 -07:00
Richard Marshall
0d8d9e2f2b Move plugin EnvForTest manager into new package
Move the EnvForTest manager into an independent package that is not
imported by any non-test code. Previously this code was directly
embedded in the plugins package resulting in testing flags being exposed
in the main kustomize binary.
2019-07-10 17:16:05 -07:00
Liu Lan
120ba6b870 docs/versioningPolicy.md: fix expired urls
Signed-off-by: Liu Lan <liulan@umcloud.com>
2019-07-10 11:41:54 +08:00
52 changed files with 3230 additions and 222 deletions

View File

@@ -1,6 +1,6 @@
# kustomize # kustomize
_[v3.0.0](https://github.com/kubernetes-sigs/kustomize/releases/tag/v3.0.0) is the latest release._ _[v3.0.2](https://github.com/kubernetes-sigs/kustomize/releases/tag/v3.0.2) is the latest release._
`kustomize` lets you customize raw, template-free YAML `kustomize` lets you customize raw, template-free YAML
files for multiple purposes, leaving the original YAML files for multiple purposes, leaving the original YAML
@@ -24,7 +24,7 @@ these [instructions](docs/INSTALL.md).
Browse the [docs](docs) or jump right into the Browse the [docs](docs) or jump right into the
tested [examples](examples). tested [examples](examples).
kustomize [v2.0.3] is available in [kubectl v1.15][kubectl]. kustomize [v2.0.3] is available in [kubectl v1.14 and v1.15][kubectl].
## Usage ## Usage

View File

@@ -28,3 +28,31 @@ To disable this, use v3, and the `load_restrictor` flag:
``` ```
kustomize build --load_restrictor none $target kustomize build --load_restrictor none $target
``` ```
## Some field is not transformed by kustomize
Example: [#1319](https://github.com/kubernetes-sigs/kustomize/issues/1319), [#1322](https://github.com/kubernetes-sigs/kustomize/issues/1322), [#1347](https://github.com/kubernetes-sigs/kustomize/issues/1347) and etc.
The fields transformed by kustomize is configured explicitly in [defaultconfig](https://github.com/kubernetes-sigs/kustomize/tree/master/pkg/transformers/config/defaultconfig). The configuration itself can be customized by including `configurations` in `kustomization.yaml`, e.g.
```yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
configurations:
- kustomizeconfig.yaml
```
The configuration directive allows customization of the following transformers:
```yaml
commonAnnotations: []
commonLabels: []
nameprefix: []
namespace: []
varreference: []
namereference: []
images: []
replicas: []
```
To persist the changes to default configuration, submit a PR like [#1338](https://github.com/kubernetes-sigs/kustomize/pull/1338), [#1348](https://github.com/kubernetes-sigs/kustomize/pull/1348) and etc.

View File

@@ -5,12 +5,11 @@
[Go plugin caveats]: goPluginCaveats.md [Go plugin caveats]: goPluginCaveats.md
This is a (no reading allowed!) 60 second copy/paste guided This is a (no reading allowed!) 60 second copy/paste guided
example. example.
Full plugin docs [here](README.md). Full plugin docs [here](README.md).
Be sure to read the [Go plugin caveats]. Be sure to read the [Go plugin caveats].
This demo uses a Go plugin, `SopsEncodedSecrets`, This demo uses a Go plugin, `SopsEncodedSecrets`,
that lives in the [sopsencodedsecrets repository]. that lives in the [sopsencodedsecrets repository].
This is an inprocess [Go plugin], not an This is an inprocess [Go plugin], not an
@@ -22,14 +21,20 @@ current setup.
#### requirements #### requirements
* linux, git, curl, Go 1.12 * linux, git, curl, Go 1.12
* Google cloud (gcloud) install
* a Google account (will use Google kms - For encryption
volunteers needed to convert to a GPG example).
* gpg
Or
* Google cloud (gcloud) install
* a Google account with KMS permission
## Make a place to work ## Make a place to work
``` ```shell
# Keeping these separate to avoid cluttering the DEMO dir. # Keeping these separate to avoid cluttering the DEMO dir.
DEMO=$(mktemp -d) DEMO=$(mktemp -d)
tmpGoPath=$(mktemp -d) tmpGoPath=$(mktemp -d)
@@ -40,7 +45,7 @@ tmpGoPath=$(mktemp -d)
Need v3.0.0 for what follows, and you must _compile_ Need v3.0.0 for what follows, and you must _compile_
it (not download the binary from the release page): it (not download the binary from the release page):
``` ```shell
GOPATH=$tmpGoPath go install sigs.k8s.io/kustomize/v3/cmd/kustomize GOPATH=$tmpGoPath go install sigs.k8s.io/kustomize/v3/cmd/kustomize
``` ```
@@ -62,7 +67,7 @@ The kustomize program reads the config file
kustomization file), then locates the Go plugin's kustomization file), then locates the Go plugin's
object code at the following location: object code at the following location:
> ``` > ```shell
> $XGD_CONFIG_HOME/kustomize/plugin/$apiVersion/$lKind/$kind.so > $XGD_CONFIG_HOME/kustomize/plugin/$apiVersion/$lKind/$kind.so
> ``` > ```
@@ -82,7 +87,7 @@ left to plugins to find their own config.
This demo will house the plugin it uses at the This demo will house the plugin it uses at the
ephemeral directory ephemeral directory
``` ```shell
PLUGIN_ROOT=$DEMO/kustomize/plugin PLUGIN_ROOT=$DEMO/kustomize/plugin
``` ```
@@ -105,10 +110,10 @@ to a plugin.
This demo uses a plugin called _SopsEncodedSecrets_, This demo uses a plugin called _SopsEncodedSecrets_,
and it lives in the [SopsEncodedSecrets repository]. and it lives in the [SopsEncodedSecrets repository].
Somewhat arbitrarily, we'll chose to install Somewhat arbitrarily, we'll chose to install
this plugin with this plugin with
``` ```shell
apiVersion=mygenerators apiVersion=mygenerators
kind=SopsEncodedSecrets kind=SopsEncodedSecrets
``` ```
@@ -119,7 +124,7 @@ By convention, the ultimate home of the plugin
code and supplemental data, tests, documentation, code and supplemental data, tests, documentation,
etc. is the lowercase form of its kind. etc. is the lowercase form of its kind.
``` ```shell
lKind=$(echo $kind | awk '{print tolower($0)}') lKind=$(echo $kind | awk '{print tolower($0)}')
``` ```
@@ -129,7 +134,7 @@ In this case, the repo name matches the lowercase
kind already, so we just clone the repo and get kind already, so we just clone the repo and get
the proper directory name automatically: the proper directory name automatically:
``` ```shell
mkdir -p $PLUGIN_ROOT/${apiVersion} mkdir -p $PLUGIN_ROOT/${apiVersion}
cd $PLUGIN_ROOT/${apiVersion} cd $PLUGIN_ROOT/${apiVersion}
git clone git@github.com:monopole/sopsencodedsecrets.git git clone git@github.com:monopole/sopsencodedsecrets.git
@@ -137,7 +142,7 @@ git clone git@github.com:monopole/sopsencodedsecrets.git
Remember this directory: Remember this directory:
``` ```shell
MY_PLUGIN_DIR=$PLUGIN_ROOT/${apiVersion}/${lKind} MY_PLUGIN_DIR=$PLUGIN_ROOT/${apiVersion}/${lKind}
``` ```
@@ -146,14 +151,14 @@ MY_PLUGIN_DIR=$PLUGIN_ROOT/${apiVersion}/${lKind}
Plugins may come with their own tests. Plugins may come with their own tests.
This one does, and it hopefully passes: This one does, and it hopefully passes:
``` ```shell
cd $MY_PLUGIN_DIR cd $MY_PLUGIN_DIR
go test SopsEncodedSecrets_test.go go test SopsEncodedSecrets_test.go
``` ```
Build the object code for use by kustomize: Build the object code for use by kustomize:
``` ```shell
cd $MY_PLUGIN_DIR cd $MY_PLUGIN_DIR
GOPATH=$tmpGoPath go build -buildmode plugin -o ${kind}.so ${kind}.go GOPATH=$tmpGoPath go build -buildmode plugin -o ${kind}.so ${kind}.go
``` ```
@@ -171,7 +176,7 @@ On load failure
version of Go (_go1.12_) on the same `$GOOS` version of Go (_go1.12_) on the same `$GOOS`
(_linux_) and `$GOARCH` (_amd64_) used to build (_linux_) and `$GOARCH` (_amd64_) used to build
the kustomize being [used in this demo]. the kustomize being [used in this demo].
* change the plugin's dependencies in its `go.mod` * change the plugin's dependencies in its `go.mod`
to match the versions used by kustomize (check to match the versions used by kustomize (check
kustomize's `go.mod` used in its tagged commit). kustomize's `go.mod` used in its tagged commit).
@@ -188,11 +193,11 @@ reusable instead of bizarrely woven throughout the
code as a individual special cases. code as a individual special cases.
## Create a kustomization ## Create a kustomization
Make a kustomization directory to Make a kustomization directory to
hold all your config: hold all your config:
``` ```shell
MYAPP=$DEMO/myapp MYAPP=$DEMO/myapp
mkdir -p $MYAPP mkdir -p $MYAPP
``` ```
@@ -202,7 +207,7 @@ Make a config file for the SopsEncodedSecrets plugin.
Its `apiVersion` and `kind` allow the plugin to be Its `apiVersion` and `kind` allow the plugin to be
found: found:
``` ```shell
cat <<EOF >$MYAPP/secGenerator.yaml cat <<EOF >$MYAPP/secGenerator.yaml
apiVersion: ${apiVersion} apiVersion: ${apiVersion}
kind: ${kind} kind: ${kind}
@@ -223,7 +228,7 @@ This plugin expects to find more data in
Make a kustomization file referencing the plugin Make a kustomization file referencing the plugin
config: config:
``` ```shell
cat <<EOF >$MYAPP/kustomization.yaml cat <<EOF >$MYAPP/kustomization.yaml
commonLabels: commonLabels:
app: hello app: hello
@@ -232,31 +237,46 @@ generators:
EOF EOF
``` ```
Now for the hard part. Generate the real encrypted data. Now generate the real encrypted data.
### Assure you have an encryption tool installed
### Assure you have a Google Cloud sops key ring. We're going to use [sops](https://github.com/mozilla/sops) to encode a file. Choose either GPG or Google Cloud KMS as the secret provider to continue.
We're going to use [sops](https://github.com/mozilla/sops) to encode a file. #### GPG
Try this: Try this:
```shell
gpg --list-keys
``` ```
If it returns a list, presumably you've already created keys. If not, try import test keys from sops for dev.
```shell
curl https://raw.githubusercontent.com/mozilla/sops/master/pgp/sops_functional_tests_key.asc | gpg --import
SOPS_PGP_FP="1022470DE3F0BC54BC6AB62DE05550BC07FB1A0A"
```
#### Google Cloude KMS
Try this:
```shell
gcloud kms keys list --location global --keyring sops gcloud kms keys list --location global --keyring sops
``` ```
If it succeeds, presumably you've already If it succeeds, presumably you've already created keys and placed them in a keyring called sops. If not, do this:
created keys and placed them in a keyring called `sops`.
If not, do this:
``` ```shell
gcloud kms keyrings create sops --location global gcloud kms keyrings create sops --location global
gcloud kms keys create sops-key --location global \ gcloud kms keys create sops-key --location global \
--keyring sops --purpose encryption --keyring sops --purpose encryption
``` ```
Extract your keyLocation for use below: Extract your keyLocation for use below:
```
```shell
keyLocation=$(\ keyLocation=$(\
gcloud kms keys list --location global --keyring sops |\ gcloud kms keys list --location global --keyring sops |\
grep GOOGLE | cut -d " " -f1) grep GOOGLE | cut -d " " -f1)
@@ -265,14 +285,15 @@ echo $keyLocation
### Install `sops` ### Install `sops`
``` ```shell
GOPATH=$tmpGoPath go install go.mozilla.org/sops/cmd/sops GOPATH=$tmpGoPath go install go.mozilla.org/sops/cmd/sops
``` ```
### Create data encrypted with your Google Cloud key ### Create data encrypted with your private key
Create raw data to encrypt: Create raw data to encrypt:
```
```shell
cat <<EOF >$MYAPP/myClearData.yaml cat <<EOF >$MYAPP/myClearData.yaml
VEGETABLE: carrot VEGETABLE: carrot
ROCKET: saturn-v ROCKET: saturn-v
@@ -283,21 +304,31 @@ EOF
Encrypt the data into file the plugin wants to read: Encrypt the data into file the plugin wants to read:
With PGP
```shell
$tmpGoPath/bin/sops --encrypt \
--pgp $SOPS_PGP_FP \
$MYAPP/myClearData.yaml >$MYAPP/myEncryptedData.yaml
``` ```
Or GCP KMS
```shell
$tmpGoPath/bin/sops --encrypt \ $tmpGoPath/bin/sops --encrypt \
--gcp-kms $keyLocation \ --gcp-kms $keyLocation \
$MYAPP/myClearData.yaml >$MYAPP/myEncryptedData.yaml $MYAPP/myClearData.yaml >$MYAPP/myEncryptedData.yaml
``` ```
Review the files Review the files
```
```shell
tree $DEMO tree $DEMO
``` ```
This should look something like: This should look something like:
> ``` > ```shell
> /tmp/tmp.0kIE9VclPt > /tmp/tmp.0kIE9VclPt
> ├── kustomize > ├── kustomize
> │   └── plugin > │   └── plugin
@@ -319,7 +350,7 @@ This should look something like:
## Build your app, using the plugin: ## Build your app, using the plugin:
``` ```shell
XDG_CONFIG_HOME=$DEMO $tmpGoPath/bin/kustomize build --enable_alpha_plugins $MYAPP XDG_CONFIG_HOME=$DEMO $tmpGoPath/bin/kustomize build --enable_alpha_plugins $MYAPP
``` ```
@@ -328,10 +359,9 @@ encrypted data for the names `ROCKET` and `CAR`.
Above, if you had set Above, if you had set
> ``` > ```shell
> PLUGIN_ROOT=$HOME/.config/kustomize/plugin > PLUGIN_ROOT=$HOME/.config/kustomize/plugin
> ``` > ```
there would be no need to use `XDG_CONFIG_HOME` in the there would be no need to use `XDG_CONFIG_HOME` in the
_kustomize_ command above. _kustomize_ command above.

View File

@@ -232,11 +232,11 @@ moment forward.
[beta-level rules]: https://github.com/kubernetes/community/blob/master/contributors/devel/api_changes.md#alpha-beta-and-stable-versions [beta-level rules]: https://github.com/kubernetes/community/blob/master/contributors/devel/api_changes.md#alpha-beta-and-stable-versions
[changes]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api_changes.md [changes]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api_changes.md
[adapt]: https://github.com/kubernetes-sigs/kustomize/blob/master/pkg/types/kustomization.go#L166 [adapt]: https://github.com/kubernetes-sigs/kustomize/blob/master/pkg/types/kustomization.go#L166
[special]: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#resources [special]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#resources
[k8s API]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md [k8s API]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md
[conventions]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md [conventions]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md
[release process]: ../releasing/README.md [release process]: ../releasing/README.md
[kustomization]: glossary.md#kustomization [kustomization]: glossary.md#kustomization
[`kind`]: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#types-kinds [`kind`]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds
[`apiVersion`]: https://kubernetes.io/docs/concepts/overview/kubernetes-api/#api-versioning [`apiVersion`]: https://kubernetes.io/docs/concepts/overview/kubernetes-api/#api-versioning
[semantic versioning]: https://semver.org [semantic versioning]: https://semver.org

View File

@@ -69,6 +69,11 @@ func (fs *UnstructAdapter) GetGvk() gvk.Gvk {
} }
} }
// SetGvk set the Gvk of the object to the input Gvk
func (fs *UnstructAdapter) SetGvk(g gvk.Gvk) {
fs.SetGroupVersionKind(toSchemaGvk(g))
}
// Copy provides a copy behind an interface. // Copy provides a copy behind an interface.
func (fs *UnstructAdapter) Copy() ifc.Kunstructured { func (fs *UnstructAdapter) Copy() ifc.Kunstructured {
return &UnstructAdapter{*fs.DeepCopy()} return &UnstructAdapter{*fs.DeepCopy()}

View File

@@ -201,7 +201,7 @@ func NewCmdBuildPrune(
func writeIndividualFiles( func writeIndividualFiles(
fSys fs.FileSystem, folderPath string, m resmap.ResMap) error { fSys fs.FileSystem, folderPath string, m resmap.ResMap) error {
byNamespace := m.GroupedByNamespace() byNamespace := m.GroupedByCurrentNamespace()
for namespace, resList := range byNamespace { for namespace, resList := range byNamespace {
for _, res := range resList { for _, res := range resList {
fName := fileName(res) fName := fileName(res)

View File

@@ -54,6 +54,7 @@ type Kunstructured interface {
MarshalJSON() ([]byte, error) MarshalJSON() ([]byte, error)
UnmarshalJSON([]byte) error UnmarshalJSON([]byte) error
GetGvk() gvk.Gvk GetGvk() gvk.Gvk
SetGvk(gvk.Gvk)
GetKind() string GetKind() string
GetName() string GetName() string
SetName(string) SetName(string)

View File

@@ -9,6 +9,7 @@ import (
"sigs.k8s.io/kustomize/v3/internal/loadertest" "sigs.k8s.io/kustomize/v3/internal/loadertest"
"sigs.k8s.io/kustomize/v3/k8sdeps/kunstruct" "sigs.k8s.io/kustomize/v3/k8sdeps/kunstruct"
. "sigs.k8s.io/kustomize/v3/pkg/plugins" . "sigs.k8s.io/kustomize/v3/pkg/plugins"
plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
"sigs.k8s.io/kustomize/v3/pkg/resmap" "sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/resource" "sigs.k8s.io/kustomize/v3/pkg/resource"
) )
@@ -41,7 +42,7 @@ port: "12345"
) )
func TestLoader(t *testing.T) { func TestLoader(t *testing.T) {
tc := NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(

View File

@@ -1,7 +1,7 @@
// Copyright 2019 The Kubernetes Authors. // Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
package plugins package test
import ( import (
"io/ioutil" "io/ioutil"
@@ -12,13 +12,14 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/pgmconfig" "sigs.k8s.io/kustomize/v3/pkg/pgmconfig"
"sigs.k8s.io/kustomize/v3/pkg/plugins"
) )
// EnvForTest manages the plugin test environment. // EnvForTest manages the plugin test environment.
// It sets/resets XDG_CONFIG_HOME, makes/removes a temp objRoot. // It sets/resets XDG_CONFIG_HOME, makes/removes a temp objRoot.
type EnvForTest struct { type EnvForTest struct {
t *testing.T t *testing.T
compiler *Compiler compiler *plugins.Compiler
workDir string workDir string
oldXdg string oldXdg string
wasSet bool wasSet bool
@@ -61,7 +62,7 @@ func (x *EnvForTest) BuildExecPlugin(g, v, k string) {
} }
} }
func (x *EnvForTest) makeCompiler() *Compiler { func (x *EnvForTest) makeCompiler() *plugins.Compiler {
// The plugin loader wants to find object code under // The plugin loader wants to find object code under
// $XDG_CONFIG_HOME/kustomize/plugins // $XDG_CONFIG_HOME/kustomize/plugins
// and the compiler writes object code to // and the compiler writes object code to
@@ -73,11 +74,11 @@ func (x *EnvForTest) makeCompiler() *Compiler {
if err != nil { if err != nil {
x.t.Error(err) x.t.Error(err)
} }
srcRoot, err := DefaultSrcRoot() srcRoot, err := plugins.DefaultSrcRoot()
if err != nil { if err != nil {
x.t.Error(err) x.t.Error(err)
} }
return NewCompiler(srcRoot, objRoot) return plugins.NewCompiler(srcRoot, objRoot)
} }
func (x *EnvForTest) createWorkDir() { func (x *EnvForTest) createWorkDir() {

View File

@@ -112,13 +112,18 @@ type ResMap interface {
// match. // match.
GetById(resid.ResId) (*resource.Resource, error) GetById(resid.ResId) (*resource.Resource, error)
// GroupedByNamespace returns a map of namespace // GroupedByCurrentNamespace returns a map of namespace
// to a slice of *Resource in that namespace. // to a slice of *Resource in that namespace.
// Resources for whom IsNamespaceableKind is false are // Resources for whom IsNamespaceableKind is false are
// are not included at all (see NonNamespaceable). // are not included at all (see NonNamespaceable).
// Resources with an empty namespace are placed // Resources with an empty namespace are placed
// in the resid.DefaultNamespace entry. // in the resid.DefaultNamespace entry.
GroupedByNamespace() map[string][]*resource.Resource GroupedByCurrentNamespace() map[string][]*resource.Resource
// GroupByOrginalNamespace performs as GroupByNamespace
// but use the original namespace instead of the current
// one to perform the grouping.
GroupedByOriginalNamespace() map[string][]*resource.Resource
// NonNamespaceable returns a slice of resources that // NonNamespaceable returns a slice of resources that
// cannot be placed in a namespace, e.g. // cannot be placed in a namespace, e.g.
@@ -390,19 +395,19 @@ func demandOneMatch(
return nil, fmt.Errorf("no matches for %sId %s", s, id) return nil, fmt.Errorf("no matches for %sId %s", s, id)
} }
// GroupedByNamespace implements ResMap.GroupByNamespace // GroupedByCurrentNamespace implements ResMap.GroupByCurrentNamespace
func (m *resWrangler) GroupedByNamespace() map[string][]*resource.Resource { func (m *resWrangler) GroupedByCurrentNamespace() map[string][]*resource.Resource {
items := m.groupedByNamespace() items := m.groupedByCurrentNamespace()
delete(items, resid.TotallyNotANamespace) delete(items, resid.TotallyNotANamespace)
return items return items
} }
// NonNamespaceable implements ResMap.NonNamespaceable // NonNamespaceable implements ResMap.NonNamespaceable
func (m *resWrangler) NonNamespaceable() []*resource.Resource { func (m *resWrangler) NonNamespaceable() []*resource.Resource {
return m.groupedByNamespace()[resid.TotallyNotANamespace] return m.groupedByCurrentNamespace()[resid.TotallyNotANamespace]
} }
func (m *resWrangler) groupedByNamespace() map[string][]*resource.Resource { func (m *resWrangler) groupedByCurrentNamespace() map[string][]*resource.Resource {
byNamespace := make(map[string][]*resource.Resource) byNamespace := make(map[string][]*resource.Resource)
for _, res := range m.rList { for _, res := range m.rList {
namespace := res.CurId().EffectiveNamespace() namespace := res.CurId().EffectiveNamespace()
@@ -414,6 +419,25 @@ func (m *resWrangler) groupedByNamespace() map[string][]*resource.Resource {
return byNamespace return byNamespace
} }
// GroupedByNamespace implements ResMap.GroupByOrginalNamespace
func (m *resWrangler) GroupedByOriginalNamespace() map[string][]*resource.Resource {
items := m.groupedByOriginalNamespace()
delete(items, resid.TotallyNotANamespace)
return items
}
func (m *resWrangler) groupedByOriginalNamespace() map[string][]*resource.Resource {
byNamespace := make(map[string][]*resource.Resource)
for _, res := range m.rList {
namespace := res.OrgId().EffectiveNamespace()
if _, found := byNamespace[namespace]; !found {
byNamespace[namespace] = []*resource.Resource{}
}
byNamespace[namespace] = append(byNamespace[namespace], res)
}
return byNamespace
}
// AsYaml implements ResMap. // AsYaml implements ResMap.
func (m *resWrangler) AsYaml() ([]byte, error) { func (m *resWrangler) AsYaml() ([]byte, error) {
firstObj := true firstObj := true

View File

@@ -62,6 +62,14 @@ func (rm *rmBuilder) AddWithNs(ns string, m map[string]interface{}) *rmBuilder {
return rm return rm
} }
func (rm *rmBuilder) AddWithNsAndName(ns string, n string, m map[string]interface{}) *rmBuilder {
err := rm.m.Append(rm.rf.FromMapWithNamespaceAndName(ns, n, m))
if err != nil {
rm.t.Fatalf("test setup failure: %v", err)
}
return rm
}
func (rm *rmBuilder) ReplaceResource(m map[string]interface{}) *rmBuilder { func (rm *rmBuilder) ReplaceResource(m map[string]interface{}) *rmBuilder {
r := rm.rf.FromMap(m) r := rm.rf.FromMap(m)
_, err := rm.m.Replace(r) _, err := rm.m.Replace(r)

View File

@@ -43,6 +43,11 @@ func (rf *Factory) FromMapWithNamespace(n string, m map[string]interface{}) *Res
return rf.makeOne(rf.kf.FromMap(m), nil).setOriginalNs(n) return rf.makeOne(rf.kf.FromMap(m), nil).setOriginalNs(n)
} }
// FromMapWithNamespaceAndName returns a new instance with the given "original" namespace.
func (rf *Factory) FromMapWithNamespaceAndName(ns string, n string, m map[string]interface{}) *Resource {
return rf.makeOne(rf.kf.FromMap(m), nil).setOriginalNs(ns).setOriginalName(n)
}
// FromMapAndOption returns a new instance of Resource with given options. // FromMapAndOption returns a new instance of Resource with given options.
func (rf *Factory) FromMapAndOption( func (rf *Factory) FromMapAndOption(
m map[string]interface{}, args *types.GeneratorArgs, option *types.GeneratorOptions) *Resource { m map[string]interface{}, args *types.GeneratorArgs, option *types.GeneratorOptions) *Resource {

View File

@@ -127,7 +127,7 @@ func (r *Resource) GetOutermostNameSuffix() string {
} }
func (r *Resource) InSameFuzzyNamespace(o *Resource) bool { func (r *Resource) InSameFuzzyNamespace(o *Resource) bool {
return r.GetNamespace() == o.GetNamespace() && return r.CurId().IsNsEquals(o.CurId()) &&
r.GetOutermostNamePrefix() == o.GetOutermostNamePrefix() && r.GetOutermostNamePrefix() == o.GetOutermostNamePrefix() &&
r.GetOutermostNameSuffix() == o.GetOutermostNameSuffix() r.GetOutermostNameSuffix() == o.GetOutermostNameSuffix()
} }

View File

@@ -8,10 +8,10 @@
package target_test package target_test
import ( import (
"sigs.k8s.io/kustomize/v3/pkg/plugins"
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" kusttest_test "sigs.k8s.io/kustomize/v3/pkg/kusttest"
plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
// This is an example of using a helm chart as a base, // This is an example of using a helm chart as a base,
@@ -28,7 +28,7 @@ import (
// TODO: Download and inflate the chart, and check that // TODO: Download and inflate the chart, and check that
// in for the test. // in for the test.
func TestChartInflatorPlugin(t *testing.T) { func TestChartInflatorPlugin(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildExecPlugin( tc.BuildExecPlugin(

View File

@@ -9,8 +9,8 @@ import (
"strings" "strings"
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" kusttest_test "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
const patchAddProbe = ` const patchAddProbe = `
@@ -340,7 +340,7 @@ patchesStrategicMerge:
} }
func TestIssue1251_Plugins_ProdVsDev(t *testing.T) { func TestIssue1251_Plugins_ProdVsDev(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -380,7 +380,7 @@ transformers:
} }
func TestIssue1251_Plugins_Local(t *testing.T) { func TestIssue1251_Plugins_Local(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -430,7 +430,7 @@ jsonOp: '%s'
// Remote in the sense that they are bundled in a different kustomization. // Remote in the sense that they are bundled in a different kustomization.
func TestIssue1251_Plugins_Bundled(t *testing.T) { func TestIssue1251_Plugins_Bundled(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(

File diff suppressed because it is too large Load Diff

View File

@@ -64,6 +64,7 @@ func (kt *KustTarget) configureBuiltinTransformers(
// - patch SMP // - patch SMP
configurators := []transformerConfigurator{ configurators := []transformerConfigurator{
kt.configureBuiltinPatchStrategicMergeTransformer, kt.configureBuiltinPatchStrategicMergeTransformer,
kt.configureBuiltinPatchTransformer,
kt.configureBuiltinNamespaceTransformer, kt.configureBuiltinNamespaceTransformer,
kt.configureBuiltinNameTransformer, kt.configureBuiltinNameTransformer,
kt.configureBuiltinLabelTransformer, kt.configureBuiltinLabelTransformer,
@@ -170,7 +171,6 @@ func (kt *KustTarget) configureBuiltinPatchStrategicMergeTransformer(
tConfig *config.TransformerConfig) ( tConfig *config.TransformerConfig) (
result []transformers.Transformer, err error) { result []transformers.Transformer, err error) {
if len(kt.kustomization.PatchesStrategicMerge) == 0 { if len(kt.kustomization.PatchesStrategicMerge) == 0 {
result = append(result, transformers.NewNoOpTransformer())
return return
} }
var c struct { var c struct {
@@ -188,6 +188,31 @@ func (kt *KustTarget) configureBuiltinPatchStrategicMergeTransformer(
return return
} }
func (kt *KustTarget) configureBuiltinPatchTransformer(
tConfig *config.TransformerConfig) (
result []transformers.Transformer, err error) {
if len(kt.kustomization.Patches) == 0 {
return
}
var c struct {
Path string `json:"path,omitempty" yaml:"path,omitempty"`
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"`
}
for _, patch := range kt.kustomization.Patches {
c.Target = &patch.Target
c.Patch = patch.Patch
c.Path = patch.Path
p := builtin.NewPatchTransformerPlugin()
err = kt.configureBuiltinPlugin(p, c, "patch")
if err != nil {
return nil, err
}
result = append(result, p)
}
return
}
func (kt *KustTarget) configureBuiltinLabelTransformer( func (kt *KustTarget) configureBuiltinLabelTransformer(
tConfig *config.TransformerConfig) ( tConfig *config.TransformerConfig) (
result []transformers.Transformer, err error) { result []transformers.Transformer, err error) {

View File

@@ -61,6 +61,8 @@ spec:
volumeMounts: volumeMounts:
- name: nginx-persistent-storage - name: nginx-persistent-storage
mountPath: /tmp/ps mountPath: /tmp/ps
- name: sidecar
image: sidecar:latest
volumes: volumes:
- name: nginx-persistent-storage - name: nginx-persistent-storage
emptyDir: {} emptyDir: {}
@@ -138,8 +140,6 @@ spec:
env: env:
- name: ANOTHERENV - name: ANOTHERENV
value: FOO value: FOO
- name: sidecar
image: sidecar
volumes: volumes:
- name: nginx-persistent-storage - name: nginx-persistent-storage
`) `)
@@ -187,7 +187,7 @@ spec:
volumeMounts: volumeMounts:
- mountPath: /tmp/ps - mountPath: /tmp/ps
name: nginx-persistent-storage name: nginx-persistent-storage
- image: sidecar - image: sidecar:latest
name: sidecar name: sidecar
volumes: volumes:
- gcePersistentDisk: - gcePersistentDisk:
@@ -293,3 +293,258 @@ spec:
t.Fatalf("Unexpected err: %v", err) t.Fatalf("Unexpected err: %v", err)
} }
} }
// TestMultiplePatchesWithPatchDeleteIgnored demonstrates that if the
// patch containing the $patch:delete directive is second in the list,
// the behavior of kustomize is incorrect, and the sidecar container is
// not removed from the final output. No conflict, nor error is reported
// even so the patch is ignored. See issue #1354
func TestMultiplePatchesWithPatchDeleteIgnored(t *testing.T) {
th := kusttest_test.NewKustTestHarness(t, "/app/overlay/staging")
makeCommonFileForMultiplePatchTest(th)
th.WriteF("/app/overlay/staging/deployment-patch1.yaml", `
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- name: nginx
env:
- name: SOME_NAME
value: somevalue
`)
th.WriteF("/app/overlay/staging/deployment-patch2.yaml", `
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- $patch: delete
name: sidecar
`)
m, err := th.MakeKustTarget().MakeCustomizedResMap()
if err != nil {
t.Fatalf("Err: %v", err)
}
th.AssertActualEqualsExpected(m, `apiVersion: apps/v1beta2
kind: Deployment
metadata:
annotations:
note: This is a test annotation
labels:
app: mynginx
env: staging
org: example.com
team: foo
name: staging-team-foo-nginx
spec:
selector:
matchLabels:
app: mynginx
env: staging
org: example.com
team: foo
template:
metadata:
annotations:
note: This is a test annotation
labels:
app: mynginx
env: staging
org: example.com
team: foo
spec:
containers:
- env:
- name: SOME_NAME
value: somevalue
image: nginx
name: nginx
volumeMounts:
- mountPath: /tmp/ps
name: nginx-persistent-storage
- image: sidecar:latest
name: sidecar
volumes:
- emptyDir: {}
name: nginx-persistent-storage
- configMap:
name: staging-team-foo-configmap-in-base-g7k6gt2889
name: configmap-in-base
---
apiVersion: v1
kind: Service
metadata:
annotations:
note: This is a test annotation
labels:
app: mynginx
env: staging
org: example.com
team: foo
name: staging-team-foo-nginx
spec:
ports:
- port: 80
selector:
app: mynginx
env: staging
org: example.com
team: foo
---
apiVersion: v1
data:
foo: bar
kind: ConfigMap
metadata:
annotations:
note: This is a test annotation
labels:
app: mynginx
env: staging
org: example.com
team: foo
name: staging-team-foo-configmap-in-base-g7k6gt2889
---
apiVersion: v1
data:
hello: world
kind: ConfigMap
metadata:
labels:
env: staging
name: staging-configmap-in-overlay-k7cbc75tg8
`)
}
// TestMultiplePatchesWithPatchDeleteApplied demonstrates that if the
// patch containing the $patch:delete directive is first in the list,
// the behavior of kustomize is correct, and the sidecar container
// is removed from the final output. See issue #1354
func TestMultiplePatchesWithPatchDeleteApplied(t *testing.T) {
th := kusttest_test.NewKustTestHarness(t, "/app/overlay/staging")
makeCommonFileForMultiplePatchTest(th)
th.WriteF("/app/overlay/staging/deployment-patch1.yaml", `
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- $patch: delete
name: sidecar
`)
th.WriteF("/app/overlay/staging/deployment-patch2.yaml", `
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- name: nginx
env:
- name: SOME_NAME
value: somevalue
`)
m, err := th.MakeKustTarget().MakeCustomizedResMap()
if err != nil {
t.Fatalf("Err: %v", err)
}
th.AssertActualEqualsExpected(m, `apiVersion: apps/v1beta2
kind: Deployment
metadata:
annotations:
note: This is a test annotation
labels:
app: mynginx
env: staging
org: example.com
team: foo
name: staging-team-foo-nginx
spec:
selector:
matchLabels:
app: mynginx
env: staging
org: example.com
team: foo
template:
metadata:
annotations:
note: This is a test annotation
labels:
app: mynginx
env: staging
org: example.com
team: foo
spec:
containers:
- env:
- name: SOME_NAME
value: somevalue
image: nginx
name: nginx
volumeMounts:
- mountPath: /tmp/ps
name: nginx-persistent-storage
volumes:
- emptyDir: {}
name: nginx-persistent-storage
- configMap:
name: staging-team-foo-configmap-in-base-g7k6gt2889
name: configmap-in-base
---
apiVersion: v1
kind: Service
metadata:
annotations:
note: This is a test annotation
labels:
app: mynginx
env: staging
org: example.com
team: foo
name: staging-team-foo-nginx
spec:
ports:
- port: 80
selector:
app: mynginx
env: staging
org: example.com
team: foo
---
apiVersion: v1
data:
foo: bar
kind: ConfigMap
metadata:
annotations:
note: This is a test annotation
labels:
app: mynginx
env: staging
org: example.com
team: foo
name: staging-team-foo-configmap-in-base-g7k6gt2889
---
apiVersion: v1
data:
hello: world
kind: ConfigMap
metadata:
labels:
env: staging
name: staging-configmap-in-overlay-k7cbc75tg8
`)
}

View File

@@ -5,7 +5,6 @@ package target_test
import ( import (
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"strings"
"testing" "testing"
) )
@@ -51,19 +50,46 @@ resources:
- role.yaml - role.yaml
`) `)
_, err := th.MakeKustTarget().MakeCustomizedResMap() m, err := th.MakeKustTarget().MakeCustomizedResMap()
// TODO: Fix #1044 // This validates Fix #1444. This should not be an error anymore -
// This should not be an error -
// the secrets have the same name but are in different namespaces. // the secrets have the same name but are in different namespaces.
// The ClusterRole (by def) is not in a namespace, // The ClusterRole (by def) is not in a namespace,
// an in this case applies to *any* Secret resource // an in this case applies to *any* Secret resource
// named "dummy" // named "dummy"
if err == nil { if err != nil {
t.Fatalf("unexpected lack of error") t.Fatalf("Err: %v", err)
}
if !strings.Contains(
err.Error(),
"slice case - multiple matches for ~G_v1_Secret|default|dummy") {
t.Fatalf("unexpected error: %s", err)
} }
th.AssertActualEqualsExpected(m, `
apiVersion: v1
data:
dummy: ""
kind: Secret
metadata:
name: dummy
namespace: default
type: Opaque
---
apiVersion: v1
data:
dummy: ""
kind: Secret
metadata:
name: dummy
namespace: kube-system
type: Opaque
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: dummy
rules:
- apiGroups:
- ""
resourceNames:
- dummy
resources:
- secrets
verbs:
- get
`)
} }

View File

@@ -12,9 +12,10 @@ import (
"sigs.k8s.io/kustomize/v3/k8sdeps/kunstruct" "sigs.k8s.io/kustomize/v3/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/v3/k8sdeps/transformer" "sigs.k8s.io/kustomize/v3/k8sdeps/transformer"
"sigs.k8s.io/kustomize/v3/pkg/fs" "sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" kusttest_test "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/loader" "sigs.k8s.io/kustomize/v3/pkg/loader"
"sigs.k8s.io/kustomize/v3/pkg/plugins" "sigs.k8s.io/kustomize/v3/pkg/plugins"
plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
"sigs.k8s.io/kustomize/v3/pkg/resmap" "sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/resource" "sigs.k8s.io/kustomize/v3/pkg/resource"
"sigs.k8s.io/kustomize/v3/pkg/target" "sigs.k8s.io/kustomize/v3/pkg/target"
@@ -22,7 +23,7 @@ import (
) )
func TestPluginDir(t *testing.T) { func TestPluginDir(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildExecPlugin( tc.BuildExecPlugin(

View File

@@ -7,8 +7,8 @@ import (
"strings" "strings"
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" kusttest_test "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
func writeDeployment(th *kusttest_test.KustTestHarness, path string) { func writeDeployment(th *kusttest_test.KustTestHarness, path string) {
@@ -50,7 +50,7 @@ metadata:
} }
func TestOrderedTransformers(t *testing.T) { func TestOrderedTransformers(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -96,7 +96,7 @@ spec:
} }
func TestPluginsNotEnabled(t *testing.T) { func TestPluginsNotEnabled(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -119,7 +119,7 @@ transformers:
} }
func TestSedTransformer(t *testing.T) { func TestSedTransformer(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildExecPlugin( tc.BuildExecPlugin(
@@ -187,7 +187,7 @@ metadata:
} }
func TestTransformedTransformers(t *testing.T) { func TestTransformedTransformers(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(

View File

@@ -299,10 +299,10 @@ nameReference:
- kind: ServiceAccount - kind: ServiceAccount
version: v1 version: v1
fieldSpecs: fieldSpecs:
- path: subjects/name - path: subjects
kind: RoleBinding kind: RoleBinding
group: rbac.authorization.k8s.io group: rbac.authorization.k8s.io
- path: subjects/name - path: subjects
kind: ClusterRoleBinding kind: ClusterRoleBinding
group: rbac.authorization.k8s.io group: rbac.authorization.k8s.io
- path: spec/serviceAccountName - path: spec/serviceAccountName
@@ -343,6 +343,8 @@ nameReference:
fieldSpecs: fieldSpecs:
- path: spec/volumeName - path: spec/volumeName
kind: PersistentVolumeClaim kind: PersistentVolumeClaim
- path: rules/resourceNames
kind: ClusterRole
- kind: StorageClass - kind: StorageClass
version: v1 version: v1
@@ -352,5 +354,7 @@ nameReference:
kind: PersistentVolume kind: PersistentVolume
- path: spec/storageClassName - path: spec/storageClassName
kind: PersistentVolumeClaim kind: PersistentVolumeClaim
- path: spec/volumeClaimTemplates/spec/storageClassName
kind: StatefulSet
` `
) )

View File

@@ -106,6 +106,102 @@ func (o *nameReferenceTransformer) Transform(m resmap.ResMap) error {
return nil return nil
} }
// selectReferral picks the referral among a subset of candidates.
// It returns the current name and namespace of the selected candidate.
// Note that the content of the referricalCandidateSubset slice is most of the time
// identical to the referralCandidates resmap. Still in some cases, such
// as ClusterRoleBinding, the subset only contains the resources of a specific
// namespace.
func (o *nameReferenceTransformer) selectReferral(
oldName string,
referrer *resource.Resource,
target gvk.Gvk,
referralCandidates resmap.ResMap,
referralCandidateSubset []*resource.Resource) (interface{}, interface{}, error) {
for _, res := range referralCandidateSubset {
id := res.OrgId()
if id.IsSelected(&target) && res.GetOriginalName() == oldName {
matches := referralCandidates.GetMatchingResourcesByOriginalId(id.Equals)
// If there's more than one match, there's no way
// to know which one to pick, so emit error.
if len(matches) > 1 {
return nil, nil, fmt.Errorf(
"multiple matches for %s:\n %v",
id, getIds(matches))
}
// In the resource, note that it is referenced
// by the referrer.
res.AppendRefBy(referrer.CurId())
// Return transformed name of the object,
// complete with prefixes, hashes, etc.
return res.GetName(), res.GetNamespace(), nil
}
}
return oldName, nil, nil
}
// utility function to replace a simple string by the new name
func (o *nameReferenceTransformer) getSimpleNameField(
oldName string,
referrer *resource.Resource,
target gvk.Gvk,
referralCandidates resmap.ResMap,
referralCandidateSubset []*resource.Resource) (interface{}, error) {
newName, _, err := o.selectReferral(oldName, referrer, target,
referralCandidates, referralCandidateSubset)
return newName, err
}
// utility function to replace name field within a map[string]interface{}
// and leverage the namespace field.
func (o *nameReferenceTransformer) getNameAndNsStruct(
inMap map[string]interface{},
referrer *resource.Resource,
target gvk.Gvk,
referralCandidates resmap.ResMap) (interface{}, error) {
// Example:
if _, ok := inMap["name"]; !ok {
return nil, fmt.Errorf(
"%#v is expected to contain a name field", inMap)
}
oldName, ok := inMap["name"].(string)
if !ok {
return nil, fmt.Errorf(
"%#v is expected to contain a name field of type string", oldName)
}
subset := referralCandidates.Resources()
if namespacevalue, ok := inMap["namespace"]; ok {
namespace := namespacevalue.(string)
bynamespace := referralCandidates.GroupedByOriginalNamespace()
if _, ok := bynamespace[namespace]; !ok {
return inMap, nil
}
subset = bynamespace[namespace]
}
newname, newnamespace, err := o.selectReferral(oldName, referrer, target,
referralCandidates, subset)
if err != nil {
return nil, err
}
inMap["name"] = newname
if newnamespace != "" {
// We don't want value "" to replace value "default" since
// the empty string is handled as a wild card here not default namespace
// by kubernetes.
inMap["namespace"] = newnamespace
}
return inMap, nil
}
func (o *nameReferenceTransformer) getNewNameFunc( func (o *nameReferenceTransformer) getNewNameFunc(
referrer *resource.Resource, referrer *resource.Resource,
target gvk.Gvk, target gvk.Gvk,
@@ -114,52 +210,40 @@ func (o *nameReferenceTransformer) getNewNameFunc(
switch in.(type) { switch in.(type) {
case string: case string:
oldName, _ := in.(string) oldName, _ := in.(string)
for _, res := range referralCandidates.Resources() { return o.getSimpleNameField(oldName, referrer, target,
id := res.OrgId() referralCandidates, referralCandidates.Resources())
if id.IsSelected(&target) && res.GetOriginalName() == oldName {
matches := referralCandidates.GetMatchingResourcesByOriginalId(id.GvknEquals)
// If there's more than one match, there's no way
// to know which one to pick, so emit error.
if len(matches) > 1 {
return nil, fmt.Errorf(
"string case - multiple matches for %s:\n %v",
id, getIds(matches))
}
// In the resource, note that it is referenced
// by the referrer.
res.AppendRefBy(referrer.CurId())
// Return transformed name of the object,
// complete with prefixes, hashes, etc.
return res.GetName(), nil
}
}
return in, nil
case []interface{}: case []interface{}:
l, _ := in.([]interface{}) l, _ := in.([]interface{})
var names []string for idx, item := range l {
for _, item := range l { switch item.(type) {
name, ok := item.(string) case string:
if !ok { // Kind: Role/ClusterRole
// FieldSpec is rules.resourceNames
oldName, _ := item.(string)
newName, err := o.getSimpleNameField(oldName, referrer, target,
referralCandidates, referralCandidates.Resources())
if err != nil {
return nil, err
}
l[idx] = newName
case map[string]interface{}:
// Kind: RoleBinding/ClusterRoleBinding
// FieldSpec is subjects
// Note: The corresponding fieldSpec had been changed from
// from path: subjects/name to just path: subjects. This is
// what get mutatefield to request the mapping of the whole
// map containing namespace and name instead of just a simple
// string field containing the name
oldMap, _ := item.(map[string]interface{})
newMap, err := o.getNameAndNsStruct(oldMap, referrer, target,
referralCandidates)
if err != nil {
return nil, err
}
l[idx] = newMap
default:
return nil, fmt.Errorf( return nil, fmt.Errorf(
"%#v is expected to be %T", item, name) "%#v is expected to be either a []string or a []map[string]interface{}", in)
}
names = append(names, name)
}
for _, res := range referralCandidates.Resources() {
indexes := indexOf(res.GetOriginalName(), names)
id := res.OrgId()
if id.IsSelected(&target) && len(indexes) > 0 {
matches := referralCandidates.GetMatchingResourcesByOriginalId(id.GvknEquals)
if len(matches) > 1 {
return nil, fmt.Errorf(
"slice case - multiple matches for %s:\n %v",
id, getIds(matches))
}
for _, index := range indexes {
l[index] = res.GetName()
}
res.AppendRefBy(referrer.CurId())
return l, nil
} }
} }
return in, nil return in, nil

View File

@@ -8,6 +8,8 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/k8sdeps/kunstruct" "sigs.k8s.io/kustomize/v3/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/v3/pkg/gvk"
"sigs.k8s.io/kustomize/v3/pkg/resid"
"sigs.k8s.io/kustomize/v3/pkg/resmap" "sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/resmaptest" "sigs.k8s.io/kustomize/v3/pkg/resmaptest"
"sigs.k8s.io/kustomize/v3/pkg/resource" "sigs.k8s.io/kustomize/v3/pkg/resource"
@@ -466,6 +468,7 @@ func TestNameReferenceHappyRun(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if err = expected.ErrorIfNotEqualLists(m); err != nil { if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err) t.Fatalf("actual doesn't match expected: %v", err)
} }
@@ -497,7 +500,7 @@ func TestNameReferenceUnhappyRun(t *testing.T) {
}, },
}, },
}).ResMap(), }).ResMap(),
expectedErr: "is expected to be string"}, expectedErr: "is expected to be"},
{ {
resMap: resmaptest_test.NewRmBuilder(t, rf).Add( resMap: resmaptest_test.NewRmBuilder(t, rf).Add(
map[string]interface{}{ map[string]interface{}{
@@ -517,7 +520,7 @@ func TestNameReferenceUnhappyRun(t *testing.T) {
}, },
}, },
}).ResMap(), }).ResMap(),
expectedErr: "is expected to be either a string or a []interface{}"}, expectedErr: "is expected to be"},
} }
nrt := NewNameReferenceTransformer(defaultTransformerConfig.NameReference) nrt := NewNameReferenceTransformer(defaultTransformerConfig.NameReference)
@@ -590,3 +593,431 @@ func TestNameReferencePersistentVolumeHappyRun(t *testing.T) {
t.Fatalf("actual doesn't match expected: %v", err) t.Fatalf("actual doesn't match expected: %v", err)
} }
} }
// utility map to create a deployment object
// with (metadatanamespace, metadataname) as key
// and pointing to "refname" secret and configmap
func deploymentMap(metadatanamespace string, metadataname string,
configmapref string, secretref string) map[string]interface{} {
deployment := map[string]interface{}{
"group": "apps",
"apiVersion": "v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": metadataname,
},
"spec": map[string]interface{}{
"template": map[string]interface{}{
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
"env": []interface{}{
map[string]interface{}{
"name": "CM_FOO",
"valueFrom": map[string]interface{}{
"configMapKeyRef": map[string]interface{}{
"name": configmapref,
"key": "somekey",
},
},
},
map[string]interface{}{
"name": "SECRET_FOO",
"valueFrom": map[string]interface{}{
"secretKeyRef": map[string]interface{}{
"name": secretref,
"key": "somekey",
},
},
},
},
},
},
},
},
},
}
if metadatanamespace != "" {
metadata := deployment["metadata"].(map[string]interface{})
metadata["namespace"] = metadatanamespace
}
return deployment
}
const (
defaultNs = "default"
ns1 = "ns1"
ns2 = "ns2"
ns3 = "ns3"
orgname = "uniquename"
prefixedname = "prefix-uniquename"
suffixedname = "uniquename-suffix"
modifiedname = "modifiedname"
)
// TestNameReferenceNamespace creates serviceAccount and clusterRoleBinding
// object with the same original names (uniquename) in different namespaces
// and with different current Id.
func TestNameReferenceNamespace(t *testing.T) {
rf := resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl())
m := resmaptest_test.NewRmBuilder(t, rf).
// Add ConfigMap with the same org name in noNs, "ns1" and "ns2" namespaces
AddWithName(orgname, map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": modifiedname,
}}).
AddWithNsAndName(ns1, orgname, map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": prefixedname,
"namespace": ns1,
}}).
AddWithNsAndName(ns2, orgname, map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": suffixedname,
"namespace": ns2,
}}).
// Add Secret with the same org name in noNs, "ns1" and "ns2" namespaces
AddWithNsAndName(defaultNs, orgname, map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"name": modifiedname,
"namespace": defaultNs,
}}).
AddWithNsAndName(ns1, orgname, map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"name": prefixedname,
"namespace": ns1,
}}).
AddWithNsAndName(ns2, orgname, map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"name": suffixedname,
"namespace": ns2,
}}).
// Add Deployment with the same org name in noNs, "ns1" and "ns2" namespaces
AddWithNsAndName(defaultNs, orgname, deploymentMap(defaultNs, modifiedname, modifiedname, modifiedname)).
AddWithNsAndName(ns1, orgname, deploymentMap(ns1, prefixedname, orgname, orgname)).
AddWithNsAndName(ns2, orgname, deploymentMap(ns2, suffixedname, orgname, orgname)).ResMap()
expected := resmaptest_test.NewSeededRmBuilder(t, rf, m.ShallowCopy()).
ReplaceResource(deploymentMap(defaultNs, modifiedname, modifiedname, modifiedname)).
ReplaceResource(deploymentMap(ns1, prefixedname, prefixedname, prefixedname)).
ReplaceResource(deploymentMap(ns2, suffixedname, suffixedname, suffixedname)).ResMap()
nrt := NewNameReferenceTransformer(defaultTransformerConfig.NameReference)
err := nrt.Transform(m)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err)
}
}
// TestNameReferenceNamespace creates serviceAccount and clusterRoleBinding
// object with the same original names (uniquename) in different namespaces
// and with different current Id.
func TestNameReferenceClusterWide(t *testing.T) {
rf := resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl())
m := resmaptest_test.NewRmBuilder(t, rf).
// Add ServiceAccount with the same org name in noNs, "ns1" and "ns2" namespaces
AddWithName(orgname, map[string]interface{}{
"apiVersion": "v1",
"kind": "ServiceAccount",
"metadata": map[string]interface{}{
"name": modifiedname,
}}).
AddWithNsAndName(ns1, orgname, map[string]interface{}{
"apiVersion": "v1",
"kind": "ServiceAccount",
"metadata": map[string]interface{}{
"name": prefixedname,
"namespace": ns1,
}}).
AddWithNsAndName(ns2, orgname, map[string]interface{}{
"apiVersion": "v1",
"kind": "ServiceAccount",
"metadata": map[string]interface{}{
"name": suffixedname,
"namespace": ns2,
}}).
// Add a PersistentVolume to have a clusterwide resource
AddWithName(orgname, map[string]interface{}{
"apiVersion": "v1",
"kind": "PersistentVolume",
"metadata": map[string]interface{}{
"name": modifiedname,
}}).
AddWithName(orgname, map[string]interface{}{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRole",
"metadata": map[string]interface{}{
"name": modifiedname,
},
"rules": []interface{}{
map[string]interface{}{
"resources": []interface{}{
"persistentvolumes",
},
"resourceNames": []interface{}{
orgname,
},
},
}}).
AddWithName(orgname, map[string]interface{}{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRoleBinding",
"metadata": map[string]interface{}{
"name": modifiedname,
},
"roleRef": map[string]interface{}{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRole",
"name": orgname,
},
"subjects": []interface{}{
map[string]interface{}{
"kind": "ServiceAccount",
"name": orgname,
"namespace": defaultNs,
},
map[string]interface{}{
"kind": "ServiceAccount",
"name": orgname,
"namespace": ns1,
},
map[string]interface{}{
"kind": "ServiceAccount",
"name": orgname,
"namespace": ns2,
},
}}).ResMap()
expected := resmaptest_test.NewSeededRmBuilder(t, rf, m.ShallowCopy()).
ReplaceResource(
map[string]interface{}{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRole",
"metadata": map[string]interface{}{
"name": modifiedname,
},
// Behavior of the transformer is still imperfect
// It should use the (resources,apigroup,resourceNames) as
// combination to select the candidates.
"rules": []interface{}{
map[string]interface{}{
"resources": []interface{}{
"persistentvolumes",
},
"resourceNames": []interface{}{
modifiedname,
},
},
}}).
ReplaceResource(
map[string]interface{}{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRoleBinding",
"metadata": map[string]interface{}{
"name": modifiedname,
},
"roleRef": map[string]interface{}{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRole",
"name": modifiedname,
},
// The following tests required a change in
// getNameFunc implementation in order to leverage
// the namespace field.
"subjects": []interface{}{
map[string]interface{}{
"kind": "ServiceAccount",
"name": modifiedname,
"namespace": defaultNs,
},
map[string]interface{}{
"kind": "ServiceAccount",
"name": prefixedname,
"namespace": ns1,
},
map[string]interface{}{
"kind": "ServiceAccount",
"name": suffixedname,
"namespace": ns2,
},
},
}).ResMap()
clusterRoleId := resid.NewResId(
gvk.Gvk{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRole"}, modifiedname)
clusterRoleBindingId := resid.NewResId(
gvk.Gvk{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRoleBinding"}, modifiedname)
clusterRole, _ := expected.GetByCurrentId(clusterRoleId)
clusterRole.AppendRefBy(clusterRoleBindingId)
nrt := NewNameReferenceTransformer(defaultTransformerConfig.NameReference)
err := nrt.Transform(m)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err)
}
}
// TestNameReferenceNamespaceTransformation creates serviceAccount and clusterRoleBinding
// object with the same original names (uniquename) in different namespaces
// and with different current Id.
func TestNameReferenceNamespaceTransformation(t *testing.T) {
rf := resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl())
m := resmaptest_test.NewRmBuilder(t, rf).
// Add ServiceAccount with the same org name in "ns1" namespaces
AddWithNsAndName(ns1, orgname, map[string]interface{}{
"apiVersion": "v1",
"kind": "ServiceAccount",
"metadata": map[string]interface{}{
"name": prefixedname,
"namespace": ns1,
}}).
// Simulate NamespaceTransformer effect (ns3 transformed in ns2)
AddWithNsAndName(ns3, orgname, map[string]interface{}{
"apiVersion": "v1",
"kind": "ServiceAccount",
"metadata": map[string]interface{}{
"name": suffixedname,
"namespace": ns2,
}}).
AddWithName(orgname, map[string]interface{}{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRole",
"metadata": map[string]interface{}{
"name": modifiedname,
}}).
AddWithName(orgname, map[string]interface{}{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRoleBinding",
"metadata": map[string]interface{}{
"name": modifiedname,
},
"roleRef": map[string]interface{}{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRole",
"name": orgname,
},
"subjects": []interface{}{
map[string]interface{}{
"kind": "ServiceAccount",
"name": orgname,
"namespace": ns1,
},
map[string]interface{}{
"kind": "ServiceAccount",
"name": orgname,
"namespace": ns3,
},
}}).ResMap()
expected := resmaptest_test.NewSeededRmBuilder(t, rf, m.ShallowCopy()).
ReplaceResource(
map[string]interface{}{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRoleBinding",
"metadata": map[string]interface{}{
"name": modifiedname,
},
"roleRef": map[string]interface{}{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRole",
"name": modifiedname,
},
// The following tests required a change in
// getNameFunc implementation in order to leverage
// the namespace field.
"subjects": []interface{}{
map[string]interface{}{
"kind": "ServiceAccount",
"name": prefixedname,
"namespace": ns1,
},
map[string]interface{}{
"kind": "ServiceAccount",
"name": suffixedname,
"namespace": ns2,
},
},
}).ResMap()
clusterRoleId := resid.NewResId(
gvk.Gvk{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRole"}, modifiedname)
clusterRoleBindingId := resid.NewResId(
gvk.Gvk{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRoleBinding"}, modifiedname)
clusterRole, _ := expected.GetByCurrentId(clusterRoleId)
clusterRole.AppendRefBy(clusterRoleBindingId)
nrt := NewNameReferenceTransformer(defaultTransformerConfig.NameReference)
err := nrt.Transform(m)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err)
}
}
// TestNameReferenceNamespace creates configmap, secret, deployment
// It validates the change done is IsSameFuzzyNamespace which
// uses the IsNsEquals method instead of the simple == operator.
func TestNameReferenceCandidateSelection(t *testing.T) {
rf := resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl())
m := resmaptest_test.NewRmBuilder(t, rf).
AddWithName("cm1", map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "p1-cm1-hash",
}}).
AddWithNsAndName("default", "secret1", map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"name": "p1-secret1-hash",
"namespace": "default",
}}).
AddWithName("deploy1", deploymentMap("", "p1-deploy1", "cm1", "secret1")).
ResMap()
expected := resmaptest_test.NewSeededRmBuilder(t, rf, m.ShallowCopy()).
ReplaceResource(deploymentMap("", "p1-deploy1", "p1-cm1-hash", "p1-secret1-hash")).
ResMap()
nrt := NewNameReferenceTransformer(defaultTransformerConfig.NameReference)
err := nrt.Transform(m)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err)
}
}

View File

@@ -200,7 +200,6 @@ func (k *Kustomization) EnforceFields() []string {
// new field names. // new field names.
func FixKustomizationPreUnmarshalling(data []byte) []byte { func FixKustomizationPreUnmarshalling(data []byte) []byte {
deprecateFieldsMap := map[string]string{ deprecateFieldsMap := map[string]string{
"patches:": "patchesStrategicMerge:",
"imageTags:": "images:", "imageTags:": "images:",
} }
for oldname, newname := range deprecateFieldsMap { for oldname, newname := range deprecateFieldsMap {

View File

@@ -149,7 +149,7 @@ func (p *ImageTagTransformerPlugin) findContainers(obj map[string]interface{}) e
func isImageMatched(s, t string) bool { func isImageMatched(s, t string) bool {
// Tag values are limited to [a-zA-Z0-9_.-]. // Tag values are limited to [a-zA-Z0-9_.-].
pattern, _ := regexp.Compile("^" + t + "(:[a-zA-Z0-9_.-]*)?$") pattern, _ := regexp.Compile("^" + t + "(@sha256)?(:[a-zA-Z0-9_.-]*)?$")
return pattern.MatchString(s) return pattern.MatchString(s)
} }
@@ -175,7 +175,7 @@ func split(imageName string) (name string, tag string) {
} }
i := ic i := ic
if ic < 0 { if ia > 0 {
i = ia i = ia
} }

View File

@@ -0,0 +1,147 @@
// Code generated by pluginator on PatchTransformer; DO NOT EDIT.
package builtin
import (
"fmt"
"github.com/evanphx/json-patch"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/resource"
"sigs.k8s.io/kustomize/v3/pkg/types"
"sigs.k8s.io/yaml"
)
type PatchTransformerPlugin struct {
ldr ifc.Loader
rf *resmap.Factory
loadedPatch *resource.Resource
decodedPatch jsonpatch.Patch
Path string `json:"path,omitempty" yaml:"path,omitempty"`
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
Target *types.Selector `json:"target,omitempty", yaml:"target,omitempty"`
}
//noinspection GoUnusedGlobalVariable
func NewPatchTransformerPlugin() *PatchTransformerPlugin {
return &PatchTransformerPlugin{}
}
func (p *PatchTransformerPlugin) Config(
ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) {
p.ldr = ldr
p.rf = rf
err = yaml.Unmarshal(c, p)
if err != nil {
return err
}
if p.Patch == "" && p.Path == "" {
err = fmt.Errorf(
"must specify one of patch and path in\n%s", string(c))
return
}
if p.Patch != "" && p.Path != "" {
err = fmt.Errorf(
"patch and path can't be set at the same time\n%s", string(c))
return
}
var in []byte
if p.Path != "" {
in, err = ldr.Load(p.Path)
if err != nil {
return
}
}
if p.Patch != "" {
in = []byte(p.Patch)
}
patchSM, errSM := p.rf.RF().FromBytes(in)
patchJson, errJson := jsonPatchFromBytes(in)
if errSM != nil && errJson != nil {
err = fmt.Errorf(
"unable to get either a Strategic Merge Patch or JSON patch 6902 from %s", p.Patch)
return
}
if errSM == nil && errJson != nil {
p.loadedPatch = patchSM
}
if errJson == nil && errSM != nil {
p.decodedPatch = patchJson
}
if patchSM != nil && patchJson != nil {
err = fmt.Errorf(
"a patch can't be both a Strategic Merge Patch and JSON patch 6902 %s", p.Patch)
}
return nil
}
func (p *PatchTransformerPlugin) Transform(m resmap.ResMap) error {
if p.loadedPatch != nil && p.Target == nil {
target, err := m.GetById(p.loadedPatch.OrgId())
if err != nil {
return err
}
err = target.Patch(p.loadedPatch.Kunstructured)
if err != nil {
return err
}
}
if p.Target == nil {
return fmt.Errorf("must specify a target for patch %s", p.Patch)
}
resources, err := m.Select(*p.Target)
if err != nil {
return err
}
for _, resource := range resources {
if p.decodedPatch != nil {
rawObj, err := resource.MarshalJSON()
if err != nil {
return err
}
modifiedObj, err := p.decodedPatch.Apply(rawObj)
if err != nil {
return errors.Wrapf(
err, "failed to apply json patch '%s'", p.Patch)
}
err = resource.UnmarshalJSON(modifiedObj)
if err != nil {
return err
}
}
if p.loadedPatch != nil {
patchCopy := p.loadedPatch.DeepCopy()
patchCopy.SetName(resource.GetName())
patchCopy.SetNamespace(resource.GetNamespace())
patchCopy.SetGvk(resource.GetGvk())
err = resource.Patch(patchCopy.Kunstructured)
if err != nil {
return err
}
}
}
return nil
}
// jsonPatchFromBytes loads a Json 6902 patch from
// a bytes input
func jsonPatchFromBytes(
in []byte) (jsonpatch.Patch, error) {
ops := string(in)
if ops == "" {
return nil, fmt.Errorf("empty json patch operations")
}
if ops[0] != '[' {
jsonOps, err := yaml.YAMLToJSON(in)
if err != nil {
return nil, err
}
ops = string(jsonOps)
}
return jsonpatch.DecodePatch([]byte(ops))
}

View File

@@ -7,11 +7,11 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
func TestAnnotationsTransformer(t *testing.T) { func TestAnnotationsTransformer(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(

View File

@@ -7,11 +7,11 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
func TestConfigMapGenerator(t *testing.T) { func TestConfigMapGenerator(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(

View File

@@ -7,11 +7,11 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
func TestHashTransformer(t *testing.T) { func TestHashTransformer(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(

View File

@@ -150,7 +150,7 @@ func (p *plugin) findContainers(obj map[string]interface{}) error {
func isImageMatched(s, t string) bool { func isImageMatched(s, t string) bool {
// Tag values are limited to [a-zA-Z0-9_.-]. // Tag values are limited to [a-zA-Z0-9_.-].
pattern, _ := regexp.Compile("^" + t + "(:[a-zA-Z0-9_.-]*)?$") pattern, _ := regexp.Compile("^" + t + "(@sha256)?(:[a-zA-Z0-9_.-]*)?$")
return pattern.MatchString(s) return pattern.MatchString(s)
} }
@@ -176,7 +176,7 @@ func split(imageName string) (name string, tag string) {
} }
i := ic i := ic
if ic < 0 { if ia > 0 {
i = ia i = ia
} }

View File

@@ -7,11 +7,11 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
func TestImageTagTransformer(t *testing.T) { func TestImageTagTransformerNewTag(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -36,18 +36,22 @@ metadata:
spec: spec:
template: template:
spec: spec:
initContainers:
- name: nginx2
image: my-nginx:1.8.0
- name: init-alpine
image: alpine:1.8.0
containers: containers:
- name: ngnix - image: nginx:1.7.9
image: nginx:1.7.9 name: nginx-tagged
- name: repliaced-with-digest - image: nginx:latest
image: foobar:1 name: nginx-latest
- name: postgresdb - image: foobar:1
image: postgres:1.8.0 name: replaced-with-digest
- image: postgres:1.8.0
name: postgresdb
initContainers:
- image: nginx
name: nginx-notag
- image: nginx@sha256:111111111111111111
name: nginx-sha256
- image: alpine:1.8.0
name: init-alpine
`) `)
th.AssertActualEqualsExpected(rm, ` th.AssertActualEqualsExpected(rm, `
@@ -61,14 +65,307 @@ spec:
spec: spec:
containers: containers:
- image: nginx:v2 - image: nginx:v2
name: ngnix name: nginx-tagged
- image: nginx:v2
name: nginx-latest
- image: foobar:1 - image: foobar:1
name: repliaced-with-digest name: replaced-with-digest
- image: postgres:1.8.0 - image: postgres:1.8.0
name: postgresdb name: postgresdb
initContainers: initContainers:
- image: my-nginx:1.8.0 - image: nginx:v2
name: nginx2 name: nginx-notag
- image: nginx:v2
name: nginx-sha256
- image: alpine:1.8.0
name: init-alpine
`)
}
func TestImageTagTransformerNewImage(t *testing.T) {
tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(
"builtin", "", "ImageTagTransformer")
th := kusttest_test.NewKustTestPluginHarness(t, "/app")
rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: ImageTagTransformer
metadata:
name: notImportantHere
imageTag:
name: nginx
newName: busybox
`, `
group: apps
apiVersion: v1
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: nginx:latest
name: nginx-latest
- image: foobar:1
name: replaced-with-digest
- image: postgres:1.8.0
name: postgresdb
initContainers:
- image: nginx
name: nginx-notag
- image: nginx@sha256:111111111111111111
name: nginx-sha256
- image: alpine:1.8.0
name: init-alpine
`)
th.AssertActualEqualsExpected(rm, `
apiVersion: v1
group: apps
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: busybox:1.7.9
name: nginx-tagged
- image: busybox:latest
name: nginx-latest
- image: foobar:1
name: replaced-with-digest
- image: postgres:1.8.0
name: postgresdb
initContainers:
- image: busybox
name: nginx-notag
- image: busybox@sha256:111111111111111111
name: nginx-sha256
- image: alpine:1.8.0
name: init-alpine
`)
}
func TestImageTagTransformerNewImageAndTag(t *testing.T) {
tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(
"builtin", "", "ImageTagTransformer")
th := kusttest_test.NewKustTestPluginHarness(t, "/app")
rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: ImageTagTransformer
metadata:
name: notImportantHere
imageTag:
name: nginx
newName: busybox
newTag: v2
`, `
group: apps
apiVersion: v1
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: nginx:latest
name: nginx-latest
- image: foobar:1
name: replaced-with-digest
- image: postgres:1.8.0
name: postgresdb
initContainers:
- image: nginx
name: nginx-notag
- image: nginx@sha256:111111111111111111
name: nginx-sha256
- image: alpine:1.8.0
name: init-alpine
`)
th.AssertActualEqualsExpected(rm, `
apiVersion: v1
group: apps
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: busybox:v2
name: nginx-tagged
- image: busybox:v2
name: nginx-latest
- image: foobar:1
name: replaced-with-digest
- image: postgres:1.8.0
name: postgresdb
initContainers:
- image: busybox:v2
name: nginx-notag
- image: busybox:v2
name: nginx-sha256
- image: alpine:1.8.0
name: init-alpine
`)
}
func TestImageTagTransformerNewDigest(t *testing.T) {
tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(
"builtin", "", "ImageTagTransformer")
th := kusttest_test.NewKustTestPluginHarness(t, "/app")
rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: ImageTagTransformer
metadata:
name: notImportantHere
imageTag:
name: nginx
Digest: sha256:222222222222222222
`, `
group: apps
apiVersion: v1
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: nginx:latest
name: nginx-latest
- image: foobar:1
name: replaced-with-digest
- image: postgres:1.8.0
name: postgresdb
initContainers:
- image: nginx
name: nginx-notag
- image: nginx@sha256:111111111111111111
name: nginx-sha256
- image: alpine:1.8.0
name: init-alpine
`)
th.AssertActualEqualsExpected(rm, `
apiVersion: v1
group: apps
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: nginx@sha256:222222222222222222
name: nginx-tagged
- image: nginx@sha256:222222222222222222
name: nginx-latest
- image: foobar:1
name: replaced-with-digest
- image: postgres:1.8.0
name: postgresdb
initContainers:
- image: nginx@sha256:222222222222222222
name: nginx-notag
- image: nginx@sha256:222222222222222222
name: nginx-sha256
- image: alpine:1.8.0
name: init-alpine
`)
}
func TestImageTagTransformerNewImageAndDigest(t *testing.T) {
tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(
"builtin", "", "ImageTagTransformer")
th := kusttest_test.NewKustTestPluginHarness(t, "/app")
rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: ImageTagTransformer
metadata:
name: notImportantHere
imageTag:
name: nginx
newName: busybox
Digest: sha256:222222222222222222
`, `
group: apps
apiVersion: v1
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: nginx:latest
name: nginx-latest
- image: foobar:1
name: replaced-with-digest
- image: postgres:1.8.0
name: postgresdb
initContainers:
- image: nginx
name: nginx-notag
- image: nginx@sha256:111111111111111111
name: nginx-sha256
- image: alpine:1.8.0
name: init-alpine
`)
th.AssertActualEqualsExpected(rm, `
apiVersion: v1
group: apps
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: busybox@sha256:222222222222222222
name: nginx-tagged
- image: busybox@sha256:222222222222222222
name: nginx-latest
- image: foobar:1
name: replaced-with-digest
- image: postgres:1.8.0
name: postgresdb
initContainers:
- image: busybox@sha256:222222222222222222
name: nginx-notag
- image: busybox@sha256:222222222222222222
name: nginx-sha256
- image: alpine:1.8.0 - image: alpine:1.8.0
name: init-alpine name: init-alpine
`) `)

View File

@@ -6,8 +6,8 @@ package main_test
import ( import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" kusttest_test "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
const ( const (
@@ -59,7 +59,7 @@ metadata:
) )
func TestInventoryTransformerCollect(t *testing.T) { func TestInventoryTransformerCollect(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -80,7 +80,7 @@ policy: GarbageCollect
} }
func TestInventoryTransformerIgnore(t *testing.T) { func TestInventoryTransformerIgnore(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -101,7 +101,7 @@ policy: GarbageIgnore
} }
func TestInventoryTransformerDefaultPolicy(t *testing.T) { func TestInventoryTransformerDefaultPolicy(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(

View File

@@ -7,11 +7,11 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
func TestLabelTransformer(t *testing.T) { func TestLabelTransformer(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(

View File

@@ -7,11 +7,11 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
func TestLegacyOrderTransformer(t *testing.T) { func TestLegacyOrderTransformer(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(

View File

@@ -7,11 +7,11 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
func TestNamespaceTransformer1(t *testing.T) { func TestNamespaceTransformer1(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -174,7 +174,7 @@ metadata:
} }
func TestNamespaceTransformerClusterLevelKinds(t *testing.T) { func TestNamespaceTransformerClusterLevelKinds(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(

View File

@@ -8,7 +8,7 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
const target = ` const target = `
@@ -29,7 +29,7 @@ spec:
` `
func TestPatchJson6902TransformerMissingFile(t *testing.T) { func TestPatchJson6902TransformerMissingFile(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -58,7 +58,7 @@ path: jsonpatch.json
} }
func TestBadPatchJson6902Transformer(t *testing.T) { func TestBadPatchJson6902Transformer(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -87,7 +87,7 @@ jsonOp: 'thisIsNotAPatch'
} }
func TestBothEmptyJson6902Transformer(t *testing.T) { func TestBothEmptyJson6902Transformer(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -115,7 +115,7 @@ target:
} }
func TestBothSpecifiedJson6902Transformer(t *testing.T) { func TestBothSpecifiedJson6902Transformer(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -151,7 +151,7 @@ jsonOp: '[{"op": "add", "path": "/spec/template/spec/dnsPolicy", "value": "Clust
} }
func TestPatchJson6902TransformerFromJsonFile(t *testing.T) { func TestPatchJson6902TransformerFromJsonFile(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -201,7 +201,7 @@ spec:
} }
func TestPatchJson6902TransformerFromYamlFile(t *testing.T) { func TestPatchJson6902TransformerFromYamlFile(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -251,7 +251,7 @@ spec:
} }
func TestPatchJson6902TransformerWithInline(t *testing.T) { func TestPatchJson6902TransformerWithInline(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(

View File

@@ -8,7 +8,7 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
const ( const (
@@ -58,7 +58,7 @@ spec:
) )
func TestPatchStrategicMergeTransformerMissingFile(t *testing.T) { func TestPatchStrategicMergeTransformerMissingFile(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -83,7 +83,7 @@ paths:
} }
func TestBadPatchStrategicMergeTransformer(t *testing.T) { func TestBadPatchStrategicMergeTransformer(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -108,7 +108,7 @@ patches: 'thisIsNotAPatch'
} }
func TestBothEmptyPatchStrategicMergeTransformer(t *testing.T) { func TestBothEmptyPatchStrategicMergeTransformer(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -131,7 +131,7 @@ metadata:
} }
func TestPatchStrategicMergeTransformerFromFiles(t *testing.T) { func TestPatchStrategicMergeTransformerFromFiles(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -181,7 +181,7 @@ spec:
} }
func TestPatchStrategicMergeTransformerWithInline(t *testing.T) { func TestPatchStrategicMergeTransformerWithInline(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -216,7 +216,7 @@ spec:
} }
func TestPatchStrategicMergeTransformerMultiplePatches(t *testing.T) { func TestPatchStrategicMergeTransformerMultiplePatches(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -293,7 +293,7 @@ spec:
} }
func TestStrategicMergeTransformerMultiplePatchesWithConflicts(t *testing.T) { func TestStrategicMergeTransformerMultiplePatchesWithConflicts(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -354,7 +354,7 @@ paths:
} }
func TestStrategicMergeTransformerWrongNamespace(t *testing.T) { func TestStrategicMergeTransformerWrongNamespace(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -397,7 +397,7 @@ paths:
} }
func TestStrategicMergeTransformerNoSchema(t *testing.T) { func TestStrategicMergeTransformerNoSchema(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -437,7 +437,7 @@ spec:
} }
func TestStrategicMergeTransformerNoSchemaMultiPatches(t *testing.T) { func TestStrategicMergeTransformerNoSchemaMultiPatches(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(
@@ -493,7 +493,7 @@ spec:
} }
func TestStrategicMergeTransformerNoSchemaMultiPatchesWithConflict(t *testing.T) { func TestStrategicMergeTransformerNoSchemaMultiPatchesWithConflict(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(

View File

@@ -0,0 +1,148 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//go:generate go run sigs.k8s.io/kustomize/v3/cmd/pluginator
package main
import (
"fmt"
"github.com/evanphx/json-patch"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/resource"
"sigs.k8s.io/kustomize/v3/pkg/types"
"sigs.k8s.io/yaml"
)
type plugin struct {
ldr ifc.Loader
rf *resmap.Factory
loadedPatch *resource.Resource
decodedPatch jsonpatch.Patch
Path string `json:"path,omitempty" yaml:"path,omitempty"`
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
Target *types.Selector `json:"target,omitempty", yaml:"target,omitempty"`
}
//noinspection GoUnusedGlobalVariable
var KustomizePlugin plugin
func (p *plugin) Config(
ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) {
p.ldr = ldr
p.rf = rf
err = yaml.Unmarshal(c, p)
if err != nil {
return err
}
if p.Patch == "" && p.Path == "" {
err = fmt.Errorf(
"must specify one of patch and path in\n%s", string(c))
return
}
if p.Patch != "" && p.Path != "" {
err = fmt.Errorf(
"patch and path can't be set at the same time\n%s", string(c))
return
}
var in []byte
if p.Path != "" {
in, err = ldr.Load(p.Path)
if err != nil {
return
}
}
if p.Patch != "" {
in = []byte(p.Patch)
}
patchSM, errSM := p.rf.RF().FromBytes(in)
patchJson, errJson := jsonPatchFromBytes(in)
if errSM != nil && errJson != nil {
err = fmt.Errorf(
"unable to get either a Strategic Merge Patch or JSON patch 6902 from %s", p.Patch)
return
}
if errSM == nil && errJson != nil {
p.loadedPatch = patchSM
}
if errJson == nil && errSM != nil {
p.decodedPatch = patchJson
}
if patchSM != nil && patchJson != nil {
err = fmt.Errorf(
"a patch can't be both a Strategic Merge Patch and JSON patch 6902 %s", p.Patch)
}
return nil
}
func (p *plugin) Transform(m resmap.ResMap) error {
if p.loadedPatch != nil && p.Target == nil {
target, err := m.GetById(p.loadedPatch.OrgId())
if err != nil {
return err
}
err = target.Patch(p.loadedPatch.Kunstructured)
if err != nil {
return err
}
}
if p.Target == nil {
return fmt.Errorf("must specify a target for patch %s", p.Patch)
}
resources, err := m.Select(*p.Target)
if err != nil {
return err
}
for _, resource := range resources {
if p.decodedPatch != nil {
rawObj, err := resource.MarshalJSON()
if err != nil {
return err
}
modifiedObj, err := p.decodedPatch.Apply(rawObj)
if err != nil {
return errors.Wrapf(
err, "failed to apply json patch '%s'", p.Patch)
}
err = resource.UnmarshalJSON(modifiedObj)
if err != nil {
return err
}
}
if p.loadedPatch != nil {
patchCopy := p.loadedPatch.DeepCopy()
patchCopy.SetName(resource.GetName())
patchCopy.SetNamespace(resource.GetNamespace())
patchCopy.SetGvk(resource.GetGvk())
err = resource.Patch(patchCopy.Kunstructured)
if err != nil {
return err
}
}
}
return nil
}
// jsonPatchFromBytes loads a Json 6902 patch from
// a bytes input
func jsonPatchFromBytes(
in []byte) (jsonpatch.Patch, error) {
ops := string(in)
if ops == "" {
return nil, fmt.Errorf("empty json patch operations")
}
if ops[0] != '[' {
jsonOps, err := yaml.YAMLToJSON(in)
if err != nil {
return nil, err
}
ops = string(jsonOps)
}
return jsonpatch.DecodePatch([]byte(ops))
}

View File

@@ -0,0 +1,341 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package main_test
import (
"strings"
"testing"
kusttest_test "sigs.k8s.io/kustomize/v3/pkg/kusttest"
plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
)
const (
target = `
apiVersion: apps/v1
metadata:
name: myDeploy
labels:
old-label: old-value
kind: Deployment
spec:
replica: 2
template:
metadata:
labels:
old-label: old-value
spec:
containers:
- name: nginx
image: nginx
---
apiVersion: apps/v1
metadata:
name: yourDeploy
labels:
new-label: new-value
kind: Deployment
spec:
replica: 1
template:
metadata:
labels:
new-label: new-value
spec:
containers:
- name: nginx
image: nginx:1.7.9
---
apiVersion: apps/v1
metadata:
name: myDeploy
label:
old-label: old-value
kind: MyKind
spec:
template:
metadata:
labels:
old-label: old-value
spec:
containers:
- name: nginx
image: nginx
`
)
func TestPatchTransformerMissingFile(t *testing.T) {
tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(
"builtin", "", "PatchTransformer")
th := kusttest_test.NewKustTestPluginHarness(t, "/app")
_, err := th.RunTransformer(`
apiVersion: builtin
kind: PatchTransformer
metadata:
name: notImportantHere
path: patch.yaml
`, target)
if err == nil {
t.Fatalf("expected error")
}
if !strings.Contains(err.Error(),
"cannot read file \"/app/patch.yaml\"") {
t.Fatalf("unexpected err: %v", err)
}
}
func TestPatchTransformerBadPatch(t *testing.T) {
tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(
"builtin", "", "PatchTransformer")
th := kusttest_test.NewKustTestPluginHarness(t, "/app")
_, err := th.RunTransformer(`
apiVersion: builtin
kind: PatchTransformer
metadata:
name: notImportantHere
patch: "thisIsNotAPatch"
`, target)
if err == nil {
t.Fatalf("expected error")
}
if !strings.Contains(err.Error(),
"unable to get either a Strategic Merge Patch or JSON patch 6902 from") {
t.Fatalf("unexpected err: %v", err)
}
}
func TestPatchTransformerMissingSelector(t *testing.T) {
tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(
"builtin", "", "PatchTransformer")
th := kusttest_test.NewKustTestPluginHarness(t, "/app")
_, err := th.RunTransformer(`
apiVersion: builtin
kind: PatchTransformer
metadata:
name: notImportantHere
patch: '[{"op": "add", "path": "/spec/template/spec/dnsPolicy", "value": "ClusterFirst"}]'
`, target)
if err == nil {
t.Fatalf("expected error")
}
if !strings.Contains(err.Error(),
"must specify a target for patch") {
t.Fatalf("unexpected err: %v", err)
}
}
func TestPatchTransformerBothEmptyPathAndPatch(t *testing.T) {
tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(
"builtin", "", "PatchTransformer")
th := kusttest_test.NewKustTestPluginHarness(t, "/app")
_, err := th.RunTransformer(`
apiVersion: builtin
kind: PatchTransformer
metadata:
name: notImportantHere
`, target)
if err == nil {
t.Fatalf("expected error")
}
if !strings.Contains(err.Error(), "must specify one of patch and path in") {
t.Fatalf("unexpected err: %v", err)
}
}
func TestPatchTransformerBothNonEmptyPathAndPatch(t *testing.T) {
tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(
"builtin", "", "PatchTransformer")
th := kusttest_test.NewKustTestPluginHarness(t, "/app")
_, err := th.RunTransformer(`
apiVersion: builtin
kind: PatchTransformer
metadata:
name: notImportantHere
Path: patch.yaml
Patch: "something"
`, target)
if err == nil {
t.Fatalf("expected error")
}
if !strings.Contains(err.Error(), "patch and path can't be set at the same time") {
t.Fatalf("unexpected err: %v", err)
}
}
func TestPatchTransformerFromFiles(t *testing.T) {
tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(
"builtin", "", "PatchTransformer")
th := kusttest_test.NewKustTestPluginHarness(t, "/app")
th.WriteF("/app/patch.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeploy
spec:
replica: 3
`)
rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: PatchTransformer
metadata:
name: notImportantHere
path: patch.yaml
target:
name: .*Deploy
`, target)
th.AssertActualEqualsExpected(rm, `
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
old-label: old-value
name: myDeploy
spec:
replica: 3
template:
metadata:
labels:
old-label: old-value
spec:
containers:
- image: nginx
name: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
new-label: new-value
name: yourDeploy
spec:
replica: 3
template:
metadata:
labels:
new-label: new-value
spec:
containers:
- image: nginx:1.7.9
name: nginx
---
apiVersion: apps/v1
kind: MyKind
metadata:
label:
old-label: old-value
name: myDeploy
spec:
replica: 3
template:
metadata:
labels:
old-label: old-value
spec:
containers:
- image: nginx
name: nginx
`)
}
func TestPatchTransformerWithInline(t *testing.T) {
tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(
"builtin", "", "PatchTransformer")
th := kusttest_test.NewKustTestPluginHarness(t, "/app")
rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: PatchTransformer
metadata:
name: notImportantHere
patch: '[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value": "nginx:latest"}]'
target:
name: .*Deploy
kind: Deployment
`, target)
th.AssertActualEqualsExpected(rm, `
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
old-label: old-value
name: myDeploy
spec:
replica: 2
template:
metadata:
labels:
old-label: old-value
spec:
containers:
- image: nginx:latest
name: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
new-label: new-value
name: yourDeploy
spec:
replica: 1
template:
metadata:
labels:
new-label: new-value
spec:
containers:
- image: nginx:latest
name: nginx
---
apiVersion: apps/v1
kind: MyKind
metadata:
label:
old-label: old-value
name: myDeploy
spec:
template:
metadata:
labels:
old-label: old-value
spec:
containers:
- image: nginx
name: nginx
`)
}

View File

@@ -7,11 +7,11 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
func TestPrefixSuffixTransformer(t *testing.T) { func TestPrefixSuffixTransformer(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(

View File

@@ -7,11 +7,11 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
func TestReplicaCountTransformer(t *testing.T) { func TestReplicaCountTransformer(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(

View File

@@ -7,11 +7,11 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
func TestSecretGenerator(t *testing.T) { func TestSecretGenerator(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(

View File

@@ -7,11 +7,11 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
func TestBashedConfigMapPlugin(t *testing.T) { func TestBashedConfigMapPlugin(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildExecPlugin( tc.BuildExecPlugin(

View File

@@ -11,7 +11,7 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
// This test requires having the helm binary on the PATH. // This test requires having the helm binary on the PATH.
@@ -19,7 +19,7 @@ import (
// TODO: Download and inflate the chart, and check that // TODO: Download and inflate the chart, and check that
// in for the test. // in for the test.
func TestChartInflator(t *testing.T) { func TestChartInflator(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildExecPlugin( tc.BuildExecPlugin(

View File

@@ -7,11 +7,11 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
func TestDatePrefixerPlugin(t *testing.T) { func TestDatePrefixerPlugin(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(

View File

@@ -8,7 +8,7 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
func shouldContain(t *testing.T, s []byte, x string) { func shouldContain(t *testing.T, s []byte, x string) {
@@ -18,7 +18,7 @@ func shouldContain(t *testing.T, s []byte, x string) {
} }
func TestPrintWorkDirPlugin(t *testing.T) { func TestPrintWorkDirPlugin(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildExecPlugin( tc.BuildExecPlugin(

View File

@@ -7,11 +7,11 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
func TestSecretsFromDatabasePlugin(t *testing.T) { func TestSecretsFromDatabasePlugin(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(

View File

@@ -7,11 +7,11 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
func TestSedTransformer(t *testing.T) { func TestSedTransformer(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildExecPlugin("someteam.example.com", "v1", "SedTransformer") tc.BuildExecPlugin("someteam.example.com", "v1", "SedTransformer")

View File

@@ -7,11 +7,11 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
func TestSomeServiceGeneratorPlugin(t *testing.T) { func TestSomeServiceGeneratorPlugin(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(

View File

@@ -7,11 +7,11 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
func TestStringPrefixerPlugin(t *testing.T) { func TestStringPrefixerPlugin(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildGoPlugin( tc.BuildGoPlugin(

View File

@@ -10,11 +10,11 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/v3/pkg/kusttest" "sigs.k8s.io/kustomize/v3/pkg/kusttest"
"sigs.k8s.io/kustomize/v3/pkg/plugins" plugins_test "sigs.k8s.io/kustomize/v3/pkg/plugins/test"
) )
func TestValidatorHappy(t *testing.T) { func TestValidatorHappy(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildExecPlugin("someteam.example.com", "v1", "Validator") tc.BuildExecPlugin("someteam.example.com", "v1", "Validator")
@@ -49,7 +49,7 @@ metadata:
} }
func TestValidatorUnHappy(t *testing.T) { func TestValidatorUnHappy(t *testing.T) {
tc := plugins.NewEnvForTest(t).Set() tc := plugins_test.NewEnvForTest(t).Set()
defer tc.Reset() defer tc.Reset()
tc.BuildExecPlugin("someteam.example.com", "v1", "Validator") tc.BuildExecPlugin("someteam.example.com", "v1", "Validator")