mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-14 18:40:55 +00:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b67179e951 | ||
|
|
47237aa7a2 | ||
|
|
5e6c06fb61 | ||
|
|
901455eb0b | ||
|
|
f8c80b7335 | ||
|
|
8db82d27e9 | ||
|
|
1eab47b63f | ||
|
|
c4656b71e5 | ||
|
|
711d3d3515 | ||
|
|
0488f570cb | ||
|
|
0e459ebac8 | ||
|
|
70719a8f65 | ||
|
|
773c1f2199 | ||
|
|
bf1c801a5e | ||
|
|
e1420b408c | ||
|
|
88a7471039 | ||
|
|
3c58cf0bf0 | ||
|
|
77eebb89fd | ||
|
|
d4d993a53c | ||
|
|
ef3b0672c5 | ||
|
|
0f30c09cbf | ||
|
|
6f670a8f38 | ||
|
|
8c93f7ba74 | ||
|
|
7d3735b19e | ||
|
|
f5f8e49fa3 | ||
|
|
1382d87d7f | ||
|
|
e65b45f969 | ||
|
|
d72b16235a | ||
|
|
3118ccfd05 | ||
|
|
fdba7df3c1 | ||
|
|
02d753027a | ||
|
|
1a43759ac3 | ||
|
|
7574f07be3 | ||
|
|
48717f3f30 | ||
|
|
74d3e92b55 | ||
|
|
f66024b1c1 | ||
|
|
bf4e09a400 | ||
|
|
d968c0b4b1 | ||
|
|
9837b5b429 | ||
|
|
1a03dcabde | ||
|
|
6fb11493ad | ||
|
|
1f063d6712 | ||
|
|
cebcd8a44d | ||
|
|
b15b20467c | ||
|
|
1d005d47b5 |
@@ -96,7 +96,7 @@ For example, to set the tag used on an image to match an
|
||||
environment variable, run
|
||||
|
||||
```
|
||||
kustomize edit set imagetag nginx:$MY_NGINX_VERSION
|
||||
kustomize edit set image nginx:$MY_NGINX_VERSION
|
||||
```
|
||||
|
||||
as part of some encapsulating work flow executed before
|
||||
|
||||
@@ -69,13 +69,16 @@ commonAnnotations:
|
||||
# markers ("---").
|
||||
resources:
|
||||
- some-service.yaml
|
||||
- ../some-dir/some-deployment.yaml
|
||||
- sub-dir/some-deployment.yaml
|
||||
|
||||
# Each entry in this list results in the creation of
|
||||
# one ConfigMap resource (it's a generator of n maps).
|
||||
# The example below creates two ConfigMaps. One with the
|
||||
# names and contents of the given files, the other with
|
||||
# key/value as data.
|
||||
# Each configMapGenerator item accepts a parameter of
|
||||
# behavior: [create|replace|merge]. This allows an overlay to modify or
|
||||
# replace an existing configMap from the parent.
|
||||
configMapGenerator:
|
||||
- name: myJavaServerProps
|
||||
files:
|
||||
@@ -215,7 +218,7 @@ crds:
|
||||
# Vars are used to capture text from one resource's field
|
||||
# and insert that text elsewhere.
|
||||
#
|
||||
# For example, suppose one specify the name of a k8s Service
|
||||
# For example, suppose someone specifies the name of a k8s Service
|
||||
# object in a container's command line, and the name of a
|
||||
# k8s Secret object in a container's environment variable,
|
||||
# so that the following would work:
|
||||
@@ -255,7 +258,7 @@ vars:
|
||||
# reference within that object. That's where the text is found.
|
||||
#
|
||||
# The field reference is optional; it defaults to `metadata.name`,
|
||||
# a normal default, since kustomize is used to generates or
|
||||
# a normal default, since kustomize is used to generate or
|
||||
# modify the names of resources.
|
||||
#
|
||||
# At time of writing, only string type fields are supported.
|
||||
|
||||
@@ -81,8 +81,8 @@ func (f *ConfigMapFactory) MakeConfigMap(
|
||||
}
|
||||
all = append(all, pairs...)
|
||||
|
||||
for _, kv := range all {
|
||||
err = addKvToConfigMap(cm, kv.Key, kv.Value)
|
||||
for _, p := range all {
|
||||
err = addKvToConfigMap(cm, p.Key, p.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -80,8 +80,8 @@ func (f *SecretFactory) MakeSecret(args *types.SecretArgs, options *types.Genera
|
||||
}
|
||||
all = append(all, pairs...)
|
||||
|
||||
for _, kv := range all {
|
||||
err = addKvToSecret(s, kv.Key, kv.Value)
|
||||
for _, p := range all {
|
||||
err = addKvToSecret(s, p.Key, p.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ func (kf *KunstructuredFactoryImpl) validate(u unstructured.Unstructured) error
|
||||
kind := u.GetKind()
|
||||
if kind == "" {
|
||||
return fmt.Errorf("missing kind in object %v", u)
|
||||
} else if kind == "List" {
|
||||
} else if strings.HasSuffix(kind, "List") {
|
||||
return nil
|
||||
}
|
||||
if u.GetName() == "" {
|
||||
|
||||
@@ -42,6 +42,15 @@ func TestSliceFromBytes(t *testing.T) {
|
||||
testConfigMap.Map(),
|
||||
},
|
||||
})
|
||||
testConfigMapList := factory.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMapList",
|
||||
"items": []interface{}{
|
||||
testConfigMap.Map(),
|
||||
testConfigMap.Map(),
|
||||
},
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -151,6 +160,24 @@ items:
|
||||
expectedOut: []ifc.Kunstructured{testList},
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "ConfigMapList",
|
||||
input: []byte(`
|
||||
apiVersion: v1
|
||||
kind: ConfigMapList
|
||||
items:
|
||||
- apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
- apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: winnie
|
||||
`),
|
||||
expectedOut: []ifc.Kunstructured{testConfigMapList},
|
||||
expectedErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -60,7 +60,7 @@ func (pt *patchTransformer) Transform(baseResourceMap resmap.ResMap) error {
|
||||
for _, patch := range patches {
|
||||
// Merge patches with base resource.
|
||||
id := patch.Id()
|
||||
matchedIds := baseResourceMap.FindByGVKN(id)
|
||||
matchedIds := baseResourceMap.GetMatchingIds(id.GvknEquals)
|
||||
if len(matchedIds) == 0 {
|
||||
return fmt.Errorf("failed to find an object with %s to apply the patch", id.GvknString())
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ func (o *Options) RunBuild(
|
||||
return err
|
||||
}
|
||||
defer ldr.Cleanup()
|
||||
kt, err := target.NewKustTarget(ldr, fSys, rf, ptf)
|
||||
kt, err := target.NewKustTarget(ldr, rf, ptf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package add
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
@@ -53,11 +54,54 @@ func (a *flagsAndArgs) Validate(args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExpandFileSource normalizes a string list, possibly
|
||||
// containing globs, into a validated, globless list.
|
||||
// For example, this list:
|
||||
// some/path
|
||||
// some/dir/a*
|
||||
// bfile=some/dir/b*
|
||||
// becomes:
|
||||
// some/path
|
||||
// some/dir/airplane
|
||||
// some/dir/ant
|
||||
// some/dir/apple
|
||||
// bfile=some/dir/banana
|
||||
// i.e. everything is converted to a key=value pair,
|
||||
// where the value is always a relative file path,
|
||||
// and the key, if missing, is the same as the value.
|
||||
// In the case where the key is explicitly declared,
|
||||
// the globbing, if present, must have exactly one match.
|
||||
func (a *flagsAndArgs) ExpandFileSource(fSys fs.FileSystem) error {
|
||||
result, err := globPatterns(fSys, a.FileSources)
|
||||
if err != nil {
|
||||
return err
|
||||
var results []string
|
||||
for _, pattern := range a.FileSources {
|
||||
var patterns []string
|
||||
key := ""
|
||||
// check if the pattern is in `--from-file=[key=]source` format
|
||||
// and if so split it to send only the file-pattern to glob function
|
||||
s := strings.Split(pattern, "=")
|
||||
if len(s) == 2 {
|
||||
patterns = append(patterns, s[1])
|
||||
key = s[0]
|
||||
} else {
|
||||
patterns = append(patterns, s[0])
|
||||
}
|
||||
result, err := globPatterns(fSys, patterns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if the format is `--from-file=[key=]source` accept only one result
|
||||
// and extend it with the `key=` prefix
|
||||
if key != "" {
|
||||
if len(result) != 1 {
|
||||
return fmt.Errorf(
|
||||
"'pattern '%s' catches files %v, should catch only one.", pattern, result)
|
||||
}
|
||||
fileSource := fmt.Sprintf("%s=%s", key, result[0])
|
||||
results = append(results, fileSource)
|
||||
} else {
|
||||
results = append(results, result...)
|
||||
}
|
||||
}
|
||||
a.FileSources = result
|
||||
a.FileSources = results
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ func TestExpandFileSource(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.Create("dir/fa1")
|
||||
fakeFS.Create("dir/fa2")
|
||||
fakeFS.Create("dir/reademe")
|
||||
fakeFS.Create("dir/readme")
|
||||
fa := flagsAndArgs{
|
||||
FileSources: []string{"dir/fa*"},
|
||||
}
|
||||
@@ -102,3 +102,37 @@ func TestExpandFileSource(t *testing.T) {
|
||||
t.Fatalf("FileSources is not correctly expanded: %v", fa.FileSources)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpandFileSourceWithKey(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.Create("dir/faaaaaaaaaabbbbbbbbbccccccccccccccccc")
|
||||
fakeFS.Create("dir/foobar")
|
||||
fakeFS.Create("dir/simplebar")
|
||||
fakeFS.Create("dir/readme")
|
||||
fa := flagsAndArgs{
|
||||
FileSources: []string{"foo-key=dir/fa*", "bar-key=dir/foobar", "dir/simplebar"},
|
||||
}
|
||||
fa.ExpandFileSource(fakeFS)
|
||||
expected := []string{
|
||||
"foo-key=dir/faaaaaaaaaabbbbbbbbbccccccccccccccccc",
|
||||
"bar-key=dir/foobar",
|
||||
"dir/simplebar",
|
||||
}
|
||||
if !reflect.DeepEqual(fa.FileSources, expected) {
|
||||
t.Fatalf("FileSources is not correctly expanded: %v", fa.FileSources)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpandFileSourceWithKeyAndError(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.Create("dir/fa1")
|
||||
fakeFS.Create("dir/fa2")
|
||||
fakeFS.Create("dir/readme")
|
||||
fa := flagsAndArgs{
|
||||
FileSources: []string{"foo-key=dir/fa*"},
|
||||
}
|
||||
err := fa.ExpandFileSource(fakeFS)
|
||||
if err == nil {
|
||||
t.Fatalf("FileSources should not be correctly expanded: %v", fa.FileSources)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,14 +19,15 @@ package misc
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
kustomizeVersion = "unknown"
|
||||
goos = "unknown"
|
||||
goarch = "unknown"
|
||||
goos = runtime.GOOS
|
||||
goarch = runtime.GOARCH
|
||||
gitCommit = "$Format:%H$" // sha1 from git, output of $(git rev-parse HEAD)
|
||||
|
||||
buildDate = "1970-01-01T00:00:00Z" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
// that was confirmed to point to an existing directory.
|
||||
type ConfirmedDir string
|
||||
|
||||
// Return a temporary dir, else error.
|
||||
// NewTmpConfirmedDir returns a temporary dir, else error.
|
||||
// The directory is cleaned, no symlinks, etc. so its
|
||||
// returned as a ConfirmedDir.
|
||||
func NewTmpConfirmedDir() (ConfirmedDir, error) {
|
||||
@@ -34,7 +34,15 @@ func NewTmpConfirmedDir() (ConfirmedDir, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return ConfirmedDir(n), nil
|
||||
|
||||
// In MacOs `ioutil.TempDir` creates a directory
|
||||
// with root in the `/var` folder, which is in turn a symlinked path
|
||||
// to `/private/var`.
|
||||
// Function `filepath.EvalSymlinks`is used to
|
||||
// resolve the real absolute path.
|
||||
deLinked, err := filepath.EvalSymlinks(n)
|
||||
return ConfirmedDir(deLinked), err
|
||||
|
||||
}
|
||||
|
||||
// HasPrefix returns true if the directory argument
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package fs
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -101,3 +102,18 @@ func TestHasPrefix_SlashFooBar(t *testing.T) {
|
||||
t.Fatalf("/foo/bar should have prefix /")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewTempConfirmDir(t *testing.T) {
|
||||
tmp, err := NewTmpConfirmedDir()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
delinked, err := filepath.EvalSymlinks(string(tmp))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if string(tmp) != delinked {
|
||||
t.Fatalf("unexpected path containing symlinks")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,11 @@ import (
|
||||
|
||||
func makeTestDir(t *testing.T) (FileSystem, string) {
|
||||
x := MakeRealFS()
|
||||
testDir, err := ioutil.TempDir("", "kustomize_testing_dir")
|
||||
td, err := ioutil.TempDir("", "kustomize_testing_dir")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %s", err)
|
||||
}
|
||||
testDir, err := filepath.EvalSymlinks(td)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %s", err)
|
||||
}
|
||||
|
||||
@@ -24,6 +24,13 @@ import (
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
// Used as a temporary non-empty occupant of the cloneDir
|
||||
// field, as something distinguishable from the empty string
|
||||
// in various outputs (especially tests). Not using an
|
||||
// actual directory name here, as that's a temporary directory
|
||||
// with a unique name that isn't created until clone time.
|
||||
const notCloned = fs.ConfirmedDir("/notCloned")
|
||||
|
||||
// RepoSpec specifies a git repository and a branch and path therein.
|
||||
type RepoSpec struct {
|
||||
// Raw, original spec, used to look for cycles.
|
||||
@@ -88,7 +95,7 @@ func NewRepoSpecFromUrl(n string) (*RepoSpec, error) {
|
||||
}
|
||||
return &RepoSpec{
|
||||
raw: n, host: host, orgRepo: orgRepo,
|
||||
path: path, ref: gitRef}, nil
|
||||
cloneDir: notCloned, path: path, ref: gitRef}, nil
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -122,46 +122,52 @@ func TestNewRepoSpecFromUrlErrors(t *testing.T) {
|
||||
|
||||
func TestNewRepoSpecFromUrl_CloneSpecs(t *testing.T) {
|
||||
testcases := []struct {
|
||||
input string
|
||||
repo string
|
||||
path string
|
||||
ref string
|
||||
input string
|
||||
cloneSpec string
|
||||
absPath string
|
||||
ref string
|
||||
}{
|
||||
{
|
||||
input: "https://git-codecommit.us-east-2.amazonaws.com/someorg/somerepo/somedir",
|
||||
repo: "https://git-codecommit.us-east-2.amazonaws.com/someorg/somerepo",
|
||||
path: "somedir",
|
||||
ref: "",
|
||||
input: "https://git-codecommit.us-east-2.amazonaws.com/someorg/somerepo/somedir",
|
||||
cloneSpec: "https://git-codecommit.us-east-2.amazonaws.com/someorg/somerepo",
|
||||
absPath: notCloned.Join("somedir"),
|
||||
ref: "",
|
||||
},
|
||||
{
|
||||
input: "https://git-codecommit.us-east-2.amazonaws.com/someorg/somerepo/somedir?ref=testbranch",
|
||||
repo: "https://git-codecommit.us-east-2.amazonaws.com/someorg/somerepo",
|
||||
path: "somedir",
|
||||
ref: "testbranch",
|
||||
input: "https://git-codecommit.us-east-2.amazonaws.com/someorg/somerepo/somedir?ref=testbranch",
|
||||
cloneSpec: "https://git-codecommit.us-east-2.amazonaws.com/someorg/somerepo",
|
||||
absPath: notCloned.Join("somedir"),
|
||||
ref: "testbranch",
|
||||
},
|
||||
{
|
||||
input: "https://fabrikops2.visualstudio.com/someorg/somerepo?ref=master",
|
||||
repo: "https://fabrikops2.visualstudio.com/someorg/somerepo",
|
||||
path: "",
|
||||
ref: "master",
|
||||
input: "https://fabrikops2.visualstudio.com/someorg/somerepo?ref=master",
|
||||
cloneSpec: "https://fabrikops2.visualstudio.com/someorg/somerepo",
|
||||
absPath: notCloned.String(),
|
||||
ref: "master",
|
||||
},
|
||||
{
|
||||
input: "http://github.com/someorg/somerepo/somedir",
|
||||
repo: "https://github.com/someorg/somerepo.git",
|
||||
path: "somedir",
|
||||
ref: "",
|
||||
input: "http://github.com/someorg/somerepo/somedir",
|
||||
cloneSpec: "https://github.com/someorg/somerepo.git",
|
||||
absPath: notCloned.Join("somedir"),
|
||||
ref: "",
|
||||
},
|
||||
{
|
||||
input: "git@github.com:someorg/somerepo/somedir",
|
||||
repo: "git@github.com:someorg/somerepo.git",
|
||||
path: "somedir",
|
||||
ref: "",
|
||||
input: "git@github.com:someorg/somerepo/somedir",
|
||||
cloneSpec: "git@github.com:someorg/somerepo.git",
|
||||
absPath: notCloned.Join("somedir"),
|
||||
ref: "",
|
||||
},
|
||||
{
|
||||
input: "git@gitlab2.sqtools.ru:10022/infra/kubernetes/thanos-base.git?ref=v0.1.0",
|
||||
repo: "git@gitlab2.sqtools.ru:10022/infra/kubernetes/thanos-base.git",
|
||||
path: "",
|
||||
ref: "v0.1.0",
|
||||
input: "git@gitlab2.sqtools.ru:10022/infra/kubernetes/thanos-base.git?ref=v0.1.0",
|
||||
cloneSpec: "git@gitlab2.sqtools.ru:10022/infra/kubernetes/thanos-base.git",
|
||||
absPath: notCloned.String(),
|
||||
ref: "v0.1.0",
|
||||
},
|
||||
{
|
||||
input: "git@bitbucket.org:company/project.git//path?ref=branch",
|
||||
cloneSpec: "git@bitbucket.org:company/project.git",
|
||||
absPath: notCloned.Join("path"),
|
||||
ref: "branch",
|
||||
},
|
||||
}
|
||||
for _, testcase := range testcases {
|
||||
@@ -169,13 +175,13 @@ func TestNewRepoSpecFromUrl_CloneSpecs(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if rs.CloneSpec() != testcase.repo {
|
||||
if rs.CloneSpec() != testcase.cloneSpec {
|
||||
t.Errorf("CloneSpec expected to be %v, but got %v on %s",
|
||||
testcase.repo, rs.CloneSpec(), testcase.input)
|
||||
testcase.cloneSpec, rs.CloneSpec(), testcase.input)
|
||||
}
|
||||
if rs.path != testcase.path {
|
||||
t.Errorf("path expected to be %v, but got %v on %s",
|
||||
testcase.path, rs.path, testcase.input)
|
||||
if rs.AbsPath() != testcase.absPath {
|
||||
t.Errorf("AbsPath expected to be %v, but got %v on %s",
|
||||
testcase.absPath, rs.AbsPath(), testcase.input)
|
||||
}
|
||||
if rs.ref != testcase.ref {
|
||||
t.Errorf("ref expected to be %v, but got %v on %s",
|
||||
|
||||
@@ -17,9 +17,12 @@ limitations under the License.
|
||||
package transformer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/evanphx/json-patch"
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||
"sigs.k8s.io/kustomize/pkg/resource"
|
||||
"sigs.k8s.io/kustomize/pkg/transformers"
|
||||
)
|
||||
|
||||
@@ -32,17 +35,18 @@ type patchJson6902JSONTransformer struct {
|
||||
var _ transformers.Transformer = &patchJson6902JSONTransformer{}
|
||||
|
||||
// newPatchJson6902JSONTransformer constructs a PatchJson6902 transformer.
|
||||
func newPatchJson6902JSONTransformer(t resid.ResId, p jsonpatch.Patch) (transformers.Transformer, error) {
|
||||
func newPatchJson6902JSONTransformer(
|
||||
id resid.ResId, p jsonpatch.Patch) (transformers.Transformer, error) {
|
||||
if len(p) == 0 {
|
||||
return transformers.NewNoOpTransformer(), nil
|
||||
}
|
||||
return &patchJson6902JSONTransformer{target: t, patch: p}, nil
|
||||
return &patchJson6902JSONTransformer{target: id, patch: p}, nil
|
||||
}
|
||||
|
||||
// Transform apply the json patches on top of the base resources.
|
||||
func (t *patchJson6902JSONTransformer) Transform(baseResourceMap resmap.ResMap) error {
|
||||
obj, err := findTargetObj(baseResourceMap, t.target)
|
||||
if obj == nil {
|
||||
func (t *patchJson6902JSONTransformer) Transform(m resmap.ResMap) error {
|
||||
obj, err := t.findTargetObj(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rawObj, err := obj.MarshalJSON()
|
||||
@@ -59,3 +63,30 @@ func (t *patchJson6902JSONTransformer) Transform(baseResourceMap resmap.ResMap)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *patchJson6902JSONTransformer) findTargetObj(
|
||||
m resmap.ResMap) (*resource.Resource, error) {
|
||||
var matched []resid.ResId
|
||||
// TODO(monopole): namespace bug in json patch?
|
||||
// Since introduction in PR #300
|
||||
// (see pkg/patch/transformer/util.go),
|
||||
// this code has treated an empty namespace like a wildcard
|
||||
// rather than like an additional restriction to match
|
||||
// only the empty namespace. No test coverage to confirm.
|
||||
// Not sure if desired, keeping it for now.
|
||||
if t.target.Namespace() != "" {
|
||||
matched = m.GetMatchingIds(t.target.NsGvknEquals)
|
||||
} else {
|
||||
matched = m.GetMatchingIds(t.target.GvknEquals)
|
||||
}
|
||||
if len(matched) == 0 {
|
||||
return nil, fmt.Errorf(
|
||||
"couldn't find target %v for json patch", t.target)
|
||||
}
|
||||
if len(matched) > 1 {
|
||||
return nil, fmt.Errorf(
|
||||
"found multiple targets %v matching %v for json patch",
|
||||
matched, t.target)
|
||||
}
|
||||
return m[matched[0]], nil
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package transformer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||
"sigs.k8s.io/kustomize/pkg/resource"
|
||||
)
|
||||
|
||||
func findTargetObj(m resmap.ResMap, targetId resid.ResId) (*resource.Resource, error) {
|
||||
matchedIds := m.FindByGVKN(targetId)
|
||||
if targetId.Namespace() != "" {
|
||||
var ids []resid.ResId
|
||||
for _, id := range matchedIds {
|
||||
if id.Namespace() == targetId.Namespace() {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
matchedIds = ids
|
||||
}
|
||||
|
||||
if len(matchedIds) == 0 {
|
||||
return nil, fmt.Errorf("couldn't find any object to apply the json patch %v", targetId)
|
||||
}
|
||||
if len(matchedIds) > 1 {
|
||||
return nil, fmt.Errorf("found multiple objects that the patch can apply %v", matchedIds)
|
||||
}
|
||||
return m[matchedIds[0]], nil
|
||||
}
|
||||
@@ -22,23 +22,30 @@ import (
|
||||
"sigs.k8s.io/kustomize/pkg/gvk"
|
||||
)
|
||||
|
||||
// ResId conflates GroupVersionKind with a textual name to uniquely identify a kubernetes resource (object).
|
||||
// ResId is an immutable identifier of a k8s resource object.
|
||||
type ResId struct {
|
||||
// Gvk of the resource.
|
||||
gvKind gvk.Gvk
|
||||
// original name of the resource before transformation.
|
||||
|
||||
// name of the resource before transformation.
|
||||
name string
|
||||
// namePrefix of the resource
|
||||
// an untransformed resource has no prefix, fully transformed resource has an arbitrary number of prefixes
|
||||
// concatenated together.
|
||||
|
||||
// namePrefix of the resource.
|
||||
// An untransformed resource has no prefix.
|
||||
// A fully transformed resource has an arbitrary
|
||||
// number of prefixes concatenated together.
|
||||
prefix string
|
||||
// nameSuffix of the resource
|
||||
// an untransformed resource has no suffix, fully transformed resource has an arbitrary number of suffixes
|
||||
// concatenated together.
|
||||
|
||||
// nameSuffix of the resource.
|
||||
// An untransformed resource has no suffix.
|
||||
// A fully transformed resource has an arbitrary
|
||||
// number of suffixes concatenated together.
|
||||
suffix string
|
||||
// namespace the resource belongs to
|
||||
// an untransformed resource has no namespace, fully transformed resource has the namespace from
|
||||
// the top most overlay
|
||||
|
||||
// Namespace the resource belongs to.
|
||||
// An untransformed resource has no namespace.
|
||||
// A fully transformed resource has the namespace
|
||||
// from the top most overlay.
|
||||
namespace string
|
||||
}
|
||||
|
||||
@@ -108,10 +115,16 @@ func (n ResId) GvknString() string {
|
||||
return n.gvKind.String() + separator + n.name
|
||||
}
|
||||
|
||||
// GvknEquals return if two ResId have the same Group/Version/Kind and name
|
||||
// The comparison excludes prefix and suffix
|
||||
// GvknEquals returns true if the other id matches
|
||||
// Group/Version/Kind/name.
|
||||
func (n ResId) GvknEquals(id ResId) bool {
|
||||
return n.gvKind.Equals(id.gvKind) && n.name == id.name
|
||||
return n.name == id.name && n.gvKind.Equals(id.gvKind)
|
||||
}
|
||||
|
||||
// NsGvknEquals returns true if the other id matches
|
||||
// namespace/Group/Version/Kind/name.
|
||||
func (n ResId) NsGvknEquals(id ResId) bool {
|
||||
return n.namespace == id.namespace && n.GvknEquals(id)
|
||||
}
|
||||
|
||||
// Gvk returns Group/Version/Kind of the resource.
|
||||
|
||||
@@ -10,31 +10,80 @@ var stringTests = []struct {
|
||||
x ResId
|
||||
s string
|
||||
}{
|
||||
{ResId{gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm", prefix: "p", suffix: "s", namespace: "ns"},
|
||||
"g_v_k|ns|p|nm|s"},
|
||||
{ResId{gvKind: gvk.Gvk{Version: "v", Kind: "k"},
|
||||
name: "nm", prefix: "p", suffix: "s", namespace: "ns"},
|
||||
"~G_v_k|ns|p|nm|s"},
|
||||
{ResId{gvKind: gvk.Gvk{Kind: "k"},
|
||||
name: "nm", prefix: "p", suffix: "s", namespace: "ns"},
|
||||
"~G_~V_k|ns|p|nm|s"},
|
||||
{ResId{gvKind: gvk.Gvk{},
|
||||
name: "nm", prefix: "p", suffix: "s", namespace: "ns"},
|
||||
"~G_~V_~K|ns|p|nm|s"},
|
||||
{ResId{gvKind: gvk.Gvk{},
|
||||
name: "nm", prefix: "p", suffix: "s"},
|
||||
"~G_~V_~K|~X|p|nm|s"},
|
||||
{ResId{gvKind: gvk.Gvk{},
|
||||
name: "nm", suffix: "s"},
|
||||
"~G_~V_~K|~X|~P|nm|s"},
|
||||
{ResId{gvKind: gvk.Gvk{},
|
||||
suffix: "s"},
|
||||
"~G_~V_~K|~X|~P|~N|s"},
|
||||
{ResId{gvKind: gvk.Gvk{}},
|
||||
"~G_~V_~K|~X|~P|~N|~S"},
|
||||
{ResId{},
|
||||
"~G_~V_~K|~X|~P|~N|~S"},
|
||||
{
|
||||
ResId{
|
||||
namespace: "ns",
|
||||
gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "p",
|
||||
suffix: "s",
|
||||
},
|
||||
"g_v_k|ns|p|nm|s",
|
||||
},
|
||||
{
|
||||
ResId{
|
||||
namespace: "ns",
|
||||
gvKind: gvk.Gvk{Version: "v", Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "p",
|
||||
suffix: "s",
|
||||
},
|
||||
"~G_v_k|ns|p|nm|s",
|
||||
},
|
||||
{
|
||||
ResId{
|
||||
namespace: "ns",
|
||||
gvKind: gvk.Gvk{Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "p",
|
||||
suffix: "s",
|
||||
},
|
||||
"~G_~V_k|ns|p|nm|s",
|
||||
},
|
||||
{
|
||||
ResId{
|
||||
namespace: "ns",
|
||||
gvKind: gvk.Gvk{},
|
||||
name: "nm",
|
||||
prefix: "p",
|
||||
suffix: "s",
|
||||
},
|
||||
"~G_~V_~K|ns|p|nm|s",
|
||||
},
|
||||
{
|
||||
ResId{
|
||||
gvKind: gvk.Gvk{},
|
||||
name: "nm",
|
||||
prefix: "p",
|
||||
suffix: "s",
|
||||
},
|
||||
"~G_~V_~K|~X|p|nm|s",
|
||||
},
|
||||
{
|
||||
ResId{
|
||||
gvKind: gvk.Gvk{},
|
||||
name: "nm",
|
||||
suffix: "s",
|
||||
},
|
||||
"~G_~V_~K|~X|~P|nm|s",
|
||||
},
|
||||
{
|
||||
ResId{
|
||||
gvKind: gvk.Gvk{},
|
||||
suffix: "s",
|
||||
},
|
||||
"~G_~V_~K|~X|~P|~N|s",
|
||||
},
|
||||
{
|
||||
ResId{
|
||||
gvKind: gvk.Gvk{},
|
||||
},
|
||||
"~G_~V_~K|~X|~P|~N|~S",
|
||||
},
|
||||
{
|
||||
ResId{},
|
||||
"~G_~V_~K|~X|~P|~N|~S",
|
||||
},
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
@@ -49,31 +98,80 @@ var gvknStringTests = []struct {
|
||||
x ResId
|
||||
s string
|
||||
}{
|
||||
{ResId{gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm", prefix: "p", suffix: "s", namespace: "ns"},
|
||||
"g_v_k|nm"},
|
||||
{ResId{gvKind: gvk.Gvk{Version: "v", Kind: "k"},
|
||||
name: "nm", prefix: "p", suffix: "s", namespace: "ns"},
|
||||
"~G_v_k|nm"},
|
||||
{ResId{gvKind: gvk.Gvk{Kind: "k"},
|
||||
name: "nm", prefix: "p", suffix: "s", namespace: "ns"},
|
||||
"~G_~V_k|nm"},
|
||||
{ResId{gvKind: gvk.Gvk{},
|
||||
name: "nm", prefix: "p", suffix: "s", namespace: "ns"},
|
||||
"~G_~V_~K|nm"},
|
||||
{ResId{gvKind: gvk.Gvk{},
|
||||
name: "nm", prefix: "p", suffix: "s"},
|
||||
"~G_~V_~K|nm"},
|
||||
{ResId{gvKind: gvk.Gvk{},
|
||||
name: "nm", suffix: "s"},
|
||||
"~G_~V_~K|nm"},
|
||||
{ResId{gvKind: gvk.Gvk{},
|
||||
suffix: "s"},
|
||||
"~G_~V_~K|"},
|
||||
{ResId{gvKind: gvk.Gvk{}},
|
||||
"~G_~V_~K|"},
|
||||
{ResId{},
|
||||
"~G_~V_~K|"},
|
||||
{
|
||||
ResId{
|
||||
namespace: "ns",
|
||||
gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "p",
|
||||
suffix: "s",
|
||||
},
|
||||
"g_v_k|nm",
|
||||
},
|
||||
{
|
||||
ResId{
|
||||
namespace: "ns",
|
||||
gvKind: gvk.Gvk{Version: "v", Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "p",
|
||||
suffix: "s",
|
||||
},
|
||||
"~G_v_k|nm",
|
||||
},
|
||||
{
|
||||
ResId{
|
||||
namespace: "ns",
|
||||
gvKind: gvk.Gvk{Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "p",
|
||||
suffix: "s",
|
||||
},
|
||||
"~G_~V_k|nm",
|
||||
},
|
||||
{
|
||||
ResId{
|
||||
namespace: "ns",
|
||||
gvKind: gvk.Gvk{},
|
||||
name: "nm",
|
||||
prefix: "p",
|
||||
suffix: "s",
|
||||
},
|
||||
"~G_~V_~K|nm",
|
||||
},
|
||||
{
|
||||
ResId{
|
||||
gvKind: gvk.Gvk{},
|
||||
name: "nm",
|
||||
prefix: "p",
|
||||
suffix: "s",
|
||||
},
|
||||
"~G_~V_~K|nm",
|
||||
},
|
||||
{
|
||||
ResId{
|
||||
gvKind: gvk.Gvk{},
|
||||
name: "nm",
|
||||
suffix: "s",
|
||||
},
|
||||
"~G_~V_~K|nm",
|
||||
},
|
||||
{
|
||||
ResId{
|
||||
gvKind: gvk.Gvk{},
|
||||
suffix: "s",
|
||||
},
|
||||
"~G_~V_~K|",
|
||||
},
|
||||
{
|
||||
ResId{
|
||||
gvKind: gvk.Gvk{},
|
||||
},
|
||||
"~G_~V_~K|",
|
||||
},
|
||||
{
|
||||
ResId{},
|
||||
"~G_~V_~K|",
|
||||
},
|
||||
}
|
||||
|
||||
func TestGvknString(t *testing.T) {
|
||||
@@ -85,47 +183,147 @@ func TestGvknString(t *testing.T) {
|
||||
}
|
||||
|
||||
var GvknEqualsTest = []struct {
|
||||
x1 ResId
|
||||
x2 ResId
|
||||
id1 ResId
|
||||
id2 ResId
|
||||
gVknResult bool
|
||||
nSgVknResult bool
|
||||
}{
|
||||
{ResId{gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm", prefix: "AA", suffix: "aa", namespace: "X"},
|
||||
ResId{gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm", prefix: "BB", suffix: "bb", namespace: "Z"}},
|
||||
{ResId{gvKind: gvk.Gvk{Version: "v", Kind: "k"},
|
||||
name: "nm", prefix: "AA", suffix: "aa", namespace: "X"},
|
||||
ResId{gvKind: gvk.Gvk{Version: "v", Kind: "k"},
|
||||
name: "nm", prefix: "BB", suffix: "bb", namespace: "Z"}},
|
||||
{ResId{gvKind: gvk.Gvk{Kind: "k"},
|
||||
name: "nm", prefix: "AA", suffix: "aa", namespace: "X"},
|
||||
ResId{gvKind: gvk.Gvk{Kind: "k"},
|
||||
name: "nm", prefix: "BB", suffix: "bb", namespace: "Z"}},
|
||||
{ResId{name: "nm", prefix: "AA", suffix: "aa", namespace: "X"},
|
||||
ResId{name: "nm", prefix: "BB", suffix: "bb", namespace: "Z"}},
|
||||
{
|
||||
id1: ResId{
|
||||
namespace: "X",
|
||||
gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "AA",
|
||||
suffix: "aa",
|
||||
},
|
||||
id2: ResId{
|
||||
namespace: "X",
|
||||
gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "BB",
|
||||
suffix: "bb",
|
||||
},
|
||||
gVknResult: true,
|
||||
nSgVknResult: true,
|
||||
},
|
||||
{
|
||||
id1: ResId{
|
||||
namespace: "X",
|
||||
gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "AA",
|
||||
suffix: "aa",
|
||||
},
|
||||
id2: ResId{
|
||||
namespace: "Z",
|
||||
gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "BB",
|
||||
suffix: "bb",
|
||||
},
|
||||
gVknResult: true,
|
||||
nSgVknResult: false,
|
||||
},
|
||||
{
|
||||
id1: ResId{
|
||||
namespace: "X",
|
||||
gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "AA",
|
||||
suffix: "aa",
|
||||
},
|
||||
id2: ResId{
|
||||
gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "BB",
|
||||
suffix: "bb",
|
||||
},
|
||||
gVknResult: true,
|
||||
nSgVknResult: false,
|
||||
},
|
||||
{
|
||||
id1: ResId{
|
||||
namespace: "X",
|
||||
gvKind: gvk.Gvk{Version: "v", Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "AA",
|
||||
suffix: "aa",
|
||||
},
|
||||
id2: ResId{
|
||||
namespace: "Z",
|
||||
gvKind: gvk.Gvk{Version: "v", Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "BB",
|
||||
suffix: "bb",
|
||||
},
|
||||
gVknResult: true,
|
||||
nSgVknResult: false,
|
||||
},
|
||||
{
|
||||
id1: ResId{
|
||||
namespace: "X",
|
||||
gvKind: gvk.Gvk{Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "AA",
|
||||
suffix: "aa",
|
||||
},
|
||||
id2: ResId{
|
||||
namespace: "Z",
|
||||
gvKind: gvk.Gvk{Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "BB",
|
||||
suffix: "bb",
|
||||
},
|
||||
gVknResult: true,
|
||||
nSgVknResult: false,
|
||||
},
|
||||
{
|
||||
id1: ResId{
|
||||
namespace: "X",
|
||||
name: "nm",
|
||||
prefix: "AA",
|
||||
suffix: "aa",
|
||||
},
|
||||
id2: ResId{
|
||||
namespace: "Z",
|
||||
name: "nm",
|
||||
prefix: "BB",
|
||||
suffix: "bb",
|
||||
},
|
||||
gVknResult: true,
|
||||
nSgVknResult: false,
|
||||
},
|
||||
}
|
||||
|
||||
func TestEquals(t *testing.T) {
|
||||
for _, hey := range GvknEqualsTest {
|
||||
if !hey.x1.GvknEquals(hey.x2) {
|
||||
t.Fatalf("%v should equal %v", hey.x1, hey.x2)
|
||||
for _, tst := range GvknEqualsTest {
|
||||
if tst.id1.GvknEquals(tst.id2) != tst.gVknResult {
|
||||
t.Fatalf("GvknEquals(\n%v,\n%v\n) should be %v",
|
||||
tst.id1, tst.id2, tst.gVknResult)
|
||||
}
|
||||
if tst.id1.NsGvknEquals(tst.id2) != tst.nSgVknResult {
|
||||
t.Fatalf("NsGvknEquals(\n%v,\n%v\n) should be %v",
|
||||
tst.id1, tst.id2, tst.nSgVknResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyWithNewPrefixSuffix(t *testing.T) {
|
||||
r1 := ResId{
|
||||
namespace: "X",
|
||||
gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "a",
|
||||
suffix: "b",
|
||||
namespace: "X"}
|
||||
}
|
||||
r2 := r1.CopyWithNewPrefixSuffix("p-", "-s")
|
||||
expected := ResId{
|
||||
namespace: "X",
|
||||
gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "p-a",
|
||||
suffix: "b-s",
|
||||
namespace: "X"}
|
||||
}
|
||||
if !r2.GvknEquals(expected) {
|
||||
t.Fatalf("%v should equal %v", r2, expected)
|
||||
}
|
||||
@@ -133,18 +331,20 @@ func TestCopyWithNewPrefixSuffix(t *testing.T) {
|
||||
|
||||
func TestCopyWithNewNamespace(t *testing.T) {
|
||||
r1 := ResId{
|
||||
namespace: "X",
|
||||
gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "a",
|
||||
suffix: "b",
|
||||
namespace: "X"}
|
||||
}
|
||||
r2 := r1.CopyWithNewNamespace("zzz")
|
||||
expected := ResId{
|
||||
namespace: "zzz",
|
||||
gvKind: gvk.Gvk{Group: "g", Version: "v", Kind: "k"},
|
||||
name: "nm",
|
||||
prefix: "a",
|
||||
suffix: "b",
|
||||
namespace: "zzz"}
|
||||
}
|
||||
if !r2.GvknEquals(expected) {
|
||||
t.Fatalf("%v should equal %v", r2, expected)
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ func (rmF *Factory) FromFiles(
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Load from path "+path+" failed")
|
||||
}
|
||||
res, err := rmF.newResMapFromBytes(content)
|
||||
res, err := rmF.NewResMapFromBytes(content)
|
||||
if err != nil {
|
||||
return nil, internal.Handler(err, path)
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func (rmF *Factory) FromFiles(
|
||||
}
|
||||
|
||||
// newResMapFromBytes decodes a list of objects in byte array format.
|
||||
func (rmF *Factory) newResMapFromBytes(b []byte) (ResMap, error) {
|
||||
func (rmF *Factory) NewResMapFromBytes(b []byte) (ResMap, error) {
|
||||
resources, err := rmF.resF.SliceFromBytes(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resmap
|
||||
package resmap_test
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"sigs.k8s.io/kustomize/pkg/internal/loadertest"
|
||||
"sigs.k8s.io/kustomize/pkg/loader"
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
. "sigs.k8s.io/kustomize/pkg/resmap"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
@@ -124,7 +125,7 @@ metadata:
|
||||
},
|
||||
}),
|
||||
}
|
||||
m, err := rmF.newResMapFromBytes(encoded)
|
||||
m, err := rmF.NewResMapFromBytes(encoded)
|
||||
fmt.Printf("%v\n", m)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
|
||||
@@ -32,20 +32,23 @@ import (
|
||||
// ResMap is a map from ResId to Resource.
|
||||
type ResMap map[resid.ResId]*resource.Resource
|
||||
|
||||
// FindByGVKN find the matched ResIds by Group/Version/Kind and Name
|
||||
func (m ResMap) FindByGVKN(inputId resid.ResId) []resid.ResId {
|
||||
type IdMatcher func(resid.ResId) bool
|
||||
|
||||
// GetMatchingIds returns a slice of ResId keys from the map
|
||||
// that all satisfy the given matcher function.
|
||||
func (m ResMap) GetMatchingIds(matches IdMatcher) []resid.ResId {
|
||||
var result []resid.ResId
|
||||
for id := range m {
|
||||
if id.GvknEquals(inputId) {
|
||||
if matches(id) {
|
||||
result = append(result, id)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// DemandOneMatchForId find the matched resource by Group/Version/Kind and Name
|
||||
func (m ResMap) DemandOneMatchForId(inputId resid.ResId) (*resource.Resource, bool) {
|
||||
result := m.FindByGVKN(inputId)
|
||||
// DemandOneGvknMatchForId find the matched resource by Group/Version/Kind and Name
|
||||
func (m ResMap) DemandOneGvknMatchForId(inputId resid.ResId) (*resource.Resource, bool) {
|
||||
result := m.GetMatchingIds(inputId.GvknEquals)
|
||||
if len(result) == 1 {
|
||||
return m[result[0]], true
|
||||
}
|
||||
@@ -177,7 +180,7 @@ func MergeWithOverride(maps ...ResMap) (ResMap, error) {
|
||||
continue
|
||||
}
|
||||
for id, r := range m {
|
||||
matchedId := result.FindByGVKN(id)
|
||||
matchedId := result.GetMatchingIds(id.GvknEquals)
|
||||
if len(matchedId) == 1 {
|
||||
id = matchedId[0]
|
||||
switch r.Behavior() {
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resmap
|
||||
package resmap_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
|
||||
"sigs.k8s.io/kustomize/pkg/gvk"
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
. "sigs.k8s.io/kustomize/pkg/resmap"
|
||||
"sigs.k8s.io/kustomize/pkg/resource"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
@@ -71,7 +72,7 @@ metadata:
|
||||
}
|
||||
}
|
||||
|
||||
func TestDemandOneMatchForId(t *testing.T) {
|
||||
func TestDemandOneGvknMatchForId(t *testing.T) {
|
||||
rm1 := ResMap{
|
||||
resid.NewResIdWithPrefixNamespace(cmap, "cm1", "prefix1", "ns1"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
@@ -91,19 +92,22 @@ func TestDemandOneMatchForId(t *testing.T) {
|
||||
}),
|
||||
}
|
||||
|
||||
_, ok := rm1.DemandOneMatchForId(resid.NewResIdWithPrefixNamespace(cmap, "cm2", "prefix1", "ns1"))
|
||||
_, ok := rm1.DemandOneGvknMatchForId(
|
||||
resid.NewResIdWithPrefixNamespace(cmap, "cm2", "prefix1", "ns1"))
|
||||
if !ok {
|
||||
t.Fatal("Expected single map entry but got none")
|
||||
}
|
||||
|
||||
// confirm that ns and prefix are not included in match
|
||||
_, ok = rm1.DemandOneMatchForId(resid.NewResIdWithPrefixNamespace(cmap, "cm2", "prefix", "ns"))
|
||||
_, ok = rm1.DemandOneGvknMatchForId(
|
||||
resid.NewResIdWithPrefixNamespace(cmap, "cm2", "prefix", "ns"))
|
||||
if !ok {
|
||||
t.Fatal("Expected single map entry but got none")
|
||||
}
|
||||
|
||||
// confirm that name is matched correctly
|
||||
result, ok := rm1.DemandOneMatchForId(resid.NewResIdWithPrefixNamespace(cmap, "cm3", "prefix1", "ns1"))
|
||||
result, ok := rm1.DemandOneGvknMatchForId(
|
||||
resid.NewResIdWithPrefixNamespace(cmap, "cm3", "prefix1", "ns1"))
|
||||
if ok {
|
||||
t.Fatalf("Expected no map entries but got %v", result)
|
||||
}
|
||||
@@ -111,7 +115,8 @@ func TestDemandOneMatchForId(t *testing.T) {
|
||||
cmap2 := gvk.Gvk{Version: "v2", Kind: "ConfigMap"}
|
||||
|
||||
// confirm that gvk is matched correctly
|
||||
result, ok = rm1.DemandOneMatchForId(resid.NewResIdWithPrefixNamespace(cmap2, "cm2", "prefix1", "ns1"))
|
||||
result, ok = rm1.DemandOneGvknMatchForId(
|
||||
resid.NewResIdWithPrefixNamespace(cmap2, "cm2", "prefix1", "ns1"))
|
||||
if ok {
|
||||
t.Fatalf("Expected no map entries but got %v", result)
|
||||
}
|
||||
@@ -291,8 +296,73 @@ func TestDeepCopy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorIfNotEqual(t *testing.T) {
|
||||
func TestGetMatchingIds(t *testing.T) {
|
||||
// These ids used as map keys.
|
||||
// They must be different to avoid overwriting
|
||||
// map entries during construction.
|
||||
ids := []resid.ResId{
|
||||
resid.NewResId(
|
||||
gvk.Gvk{Kind: "vegetable"},
|
||||
"bedlam"),
|
||||
resid.NewResId(
|
||||
gvk.Gvk{Group: "g1", Version: "v1", Kind: "vegetable"},
|
||||
"domino"),
|
||||
resid.NewResIdWithPrefixNamespace(
|
||||
gvk.Gvk{Kind: "vegetable"},
|
||||
"peter", "p", "happy"),
|
||||
resid.NewResIdWithPrefixNamespace(
|
||||
gvk.Gvk{Version: "v1", Kind: "fruit"},
|
||||
"shatterstar", "p", "happy"),
|
||||
}
|
||||
|
||||
m := ResMap{}
|
||||
for _, id := range ids {
|
||||
// Resources values don't matter in this test.
|
||||
m[id] = nil
|
||||
}
|
||||
if len(m) != len(ids) {
|
||||
t.Fatalf("unexpected map len %d presumably due to duplicate keys", len(m))
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
matcher IdMatcher
|
||||
count int
|
||||
}{
|
||||
{
|
||||
"match everything",
|
||||
func(resid.ResId) bool { return true },
|
||||
4,
|
||||
},
|
||||
{
|
||||
"match nothing",
|
||||
func(resid.ResId) bool { return false },
|
||||
0,
|
||||
},
|
||||
{
|
||||
"name is peter",
|
||||
func(x resid.ResId) bool { return x.Name() == "peter" },
|
||||
1,
|
||||
},
|
||||
{
|
||||
"happy vegetable",
|
||||
func(x resid.ResId) bool {
|
||||
return x.Namespace() == "happy" &&
|
||||
x.Gvk().Kind == "vegetable"
|
||||
},
|
||||
1,
|
||||
},
|
||||
}
|
||||
for _, tst := range tests {
|
||||
result := m.GetMatchingIds(tst.matcher)
|
||||
if len(result) != tst.count {
|
||||
t.Fatalf("test '%s'; actual: %d, expected: %d",
|
||||
tst.name, len(result), tst.count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorIfNotEqual(t *testing.T) {
|
||||
rm1 := ResMap{
|
||||
resid.NewResId(cmap, "cm1"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
@@ -436,7 +506,6 @@ func TestMergeWithoutOverride(t *testing.T) {
|
||||
}
|
||||
|
||||
func generateMergeFixtures(b types.GenerationBehavior) []ResMap {
|
||||
|
||||
input1 := ResMap{
|
||||
resid.NewResId(cmap, "cmap"): rf.FromMapAndOption(
|
||||
map[string]interface{}{
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
internal "sigs.k8s.io/kustomize/pkg/internal/error"
|
||||
@@ -94,10 +95,14 @@ func (rf *Factory) SliceFromBytes(in []byte) ([]*Resource, error) {
|
||||
for len(kunStructs) > 0 {
|
||||
u := kunStructs[0]
|
||||
kunStructs = kunStructs[1:]
|
||||
if u.GetKind() == "List" {
|
||||
if strings.HasSuffix(u.GetKind(), "List") {
|
||||
items := u.Map()["items"]
|
||||
itemsSlice, ok := items.([]interface{})
|
||||
if !ok {
|
||||
if items == nil {
|
||||
// an empty list
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("items in List is type %T, expected array", items)
|
||||
}
|
||||
for _, item := range itemsSlice {
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resource
|
||||
package resource_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/internal/loadertest"
|
||||
"sigs.k8s.io/kustomize/pkg/patch"
|
||||
. "sigs.k8s.io/kustomize/pkg/resource"
|
||||
)
|
||||
|
||||
func TestSliceFromPatches(t *testing.T) {
|
||||
@@ -67,7 +68,7 @@ items:
|
||||
patchList2 := patch.StrategicMerge("patch5.yaml")
|
||||
patch5 := `
|
||||
apiVersion: v1
|
||||
kind: List
|
||||
kind: DeploymentList
|
||||
items:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -86,6 +87,17 @@ items:
|
||||
name: deployment-b
|
||||
spec:
|
||||
<<: *hostAliases
|
||||
`
|
||||
patchList3 := patch.StrategicMerge("patch6.yaml")
|
||||
patch6 := `
|
||||
apiVersion: v1
|
||||
kind: List
|
||||
items:
|
||||
`
|
||||
patchList4 := patch.StrategicMerge("patch7.yaml")
|
||||
patch7 := `
|
||||
apiVersion: v1
|
||||
kind: List
|
||||
`
|
||||
testDeploymentSpec := map[string]interface{}{
|
||||
"template": map[string]interface{}{
|
||||
@@ -125,6 +137,8 @@ items:
|
||||
l.AddFile("/"+string(patchBad), []byte(patch3))
|
||||
l.AddFile("/"+string(patchList), []byte(patch4))
|
||||
l.AddFile("/"+string(patchList2), []byte(patch5))
|
||||
l.AddFile("/"+string(patchList3), []byte(patch6))
|
||||
l.AddFile("/"+string(patchList4), []byte(patch7))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -162,6 +176,18 @@ items:
|
||||
expectedOut: []*Resource{testDeploymentA, testDeploymentB},
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "listWithNoEntries",
|
||||
input: []patch.StrategicMerge{patchList3},
|
||||
expectedOut: []*Resource{},
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "listWithNo'items:'",
|
||||
input: []patch.StrategicMerge{patchList4},
|
||||
expectedOut: []*Resource{},
|
||||
expectedErr: false,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
rs, err := factory.SliceFromPatches(l, test.input)
|
||||
|
||||
@@ -38,7 +38,7 @@ func (r *Resource) String() string {
|
||||
if err != nil {
|
||||
return "<" + err.Error() + ">"
|
||||
}
|
||||
return strings.TrimSpace(string(bs))
|
||||
return strings.TrimSpace(string(bs)) + r.options.String()
|
||||
}
|
||||
|
||||
// DeepCopy returns a new copy of resource
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resource
|
||||
package resource_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
|
||||
"sigs.k8s.io/kustomize/pkg/gvk"
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
. "sigs.k8s.io/kustomize/pkg/resource"
|
||||
)
|
||||
|
||||
var factory = NewFactory(
|
||||
@@ -37,7 +38,9 @@ var testConfigMap = factory.FromMap(
|
||||
},
|
||||
})
|
||||
|
||||
const testConfigMapString = `{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie","namespace":"hundred-acre-wood"}}`
|
||||
const genArgOptions = "{nsfx:false,beh:unspecified}"
|
||||
|
||||
const configMapAsString = `{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie","namespace":"hundred-acre-wood"}}`
|
||||
|
||||
var testDeployment = factory.FromMap(
|
||||
map[string]interface{}{
|
||||
@@ -48,7 +51,7 @@ var testDeployment = factory.FromMap(
|
||||
},
|
||||
})
|
||||
|
||||
const testDeploymentString = `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"pooh"}}`
|
||||
const deploymentAsString = `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"pooh"}}`
|
||||
|
||||
func TestResourceString(t *testing.T) {
|
||||
tests := []struct {
|
||||
@@ -57,11 +60,11 @@ func TestResourceString(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
in: testConfigMap,
|
||||
s: testConfigMapString,
|
||||
s: configMapAsString + genArgOptions,
|
||||
},
|
||||
{
|
||||
in: testDeployment,
|
||||
s: testDeploymentString,
|
||||
s: deploymentAsString + genArgOptions,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package target
|
||||
package target_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package target
|
||||
package target_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
@@ -14,17 +14,152 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package target
|
||||
package target_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGenerator1(t *testing.T) {
|
||||
// Generate a Secret and a ConfigMap from the same data
|
||||
// to compare the result.
|
||||
func TestGeneratorBasics(t *testing.T) {
|
||||
th := NewKustTestHarness(t, "/app")
|
||||
th.writeK("/app", `
|
||||
namePrefix: blah-
|
||||
configMapGenerator:
|
||||
- name: bob
|
||||
literals:
|
||||
- fruit=apple
|
||||
- vegetable=broccoli
|
||||
env: foo.env
|
||||
files:
|
||||
- passphrase=phrase.dat
|
||||
- forces.txt
|
||||
secretGenerator:
|
||||
- name: bob
|
||||
literals:
|
||||
- fruit=apple
|
||||
- vegetable=broccoli
|
||||
env: foo.env
|
||||
files:
|
||||
- passphrase=phrase.dat
|
||||
- forces.txt
|
||||
`)
|
||||
th.writeF("/app/foo.env", `
|
||||
MOUNTAIN=everest
|
||||
OCEAN=pacific
|
||||
`)
|
||||
th.writeF("/app/phrase.dat", `
|
||||
Life is short.
|
||||
But the years are long.
|
||||
Not while the evil days come not.
|
||||
`)
|
||||
th.writeF("/app/forces.txt", `
|
||||
gravitational
|
||||
electromagnetic
|
||||
strong nuclear
|
||||
weak nuclear
|
||||
`)
|
||||
m, err := th.makeKustTarget().MakeCustomizedResMap()
|
||||
if err != nil {
|
||||
t.Fatalf("Err: %v", err)
|
||||
}
|
||||
th.assertActualEqualsExpected(m, `
|
||||
apiVersion: v1
|
||||
data:
|
||||
MOUNTAIN: everest
|
||||
OCEAN: pacific
|
||||
forces.txt: |2
|
||||
|
||||
gravitational
|
||||
electromagnetic
|
||||
strong nuclear
|
||||
weak nuclear
|
||||
fruit: apple
|
||||
passphrase: |2
|
||||
|
||||
Life is short.
|
||||
But the years are long.
|
||||
Not while the evil days come not.
|
||||
vegetable: broccoli
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: blah-bob-k772g5db55
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
MOUNTAIN: ZXZlcmVzdA==
|
||||
OCEAN: cGFjaWZpYw==
|
||||
forces.txt: CmdyYXZpdGF0aW9uYWwKZWxlY3Ryb21hZ25ldGljCnN0cm9uZyBudWNsZWFyCndlYWsgbnVjbGVhcgo=
|
||||
fruit: YXBwbGU=
|
||||
passphrase: CkxpZmUgaXMgc2hvcnQuCkJ1dCB0aGUgeWVhcnMgYXJlIGxvbmcuCk5vdCB3aGlsZSB0aGUgZXZpbCBkYXlzIGNvbWUgbm90Lgo=
|
||||
vegetable: YnJvY2NvbGk=
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: blah-bob-gmc2824f4b
|
||||
type: Opaque
|
||||
`)
|
||||
}
|
||||
|
||||
// TODO: These should be errors instead.
|
||||
func TestGeneratorRepeatsInKustomization(t *testing.T) {
|
||||
th := NewKustTestHarness(t, "/app")
|
||||
th.writeK("/app", `
|
||||
namePrefix: blah-
|
||||
configMapGenerator:
|
||||
- name: bob
|
||||
behavior: create
|
||||
literals:
|
||||
- bean=pinto
|
||||
- star=wolf-rayet
|
||||
literals:
|
||||
- fruit=apple
|
||||
- vegetable=broccoli
|
||||
files:
|
||||
- forces.txt
|
||||
files:
|
||||
- nobles=nobility.txt
|
||||
`)
|
||||
th.writeF("/app/forces.txt", `
|
||||
gravitational
|
||||
electromagnetic
|
||||
strong nuclear
|
||||
weak nuclear
|
||||
`)
|
||||
th.writeF("/app/nobility.txt", `
|
||||
helium
|
||||
neon
|
||||
argon
|
||||
krypton
|
||||
xenon
|
||||
radon
|
||||
`)
|
||||
m, err := th.makeKustTarget().MakeCustomizedResMap()
|
||||
if err != nil {
|
||||
t.Fatalf("Err: %v", err)
|
||||
}
|
||||
th.assertActualEqualsExpected(m, `
|
||||
apiVersion: v1
|
||||
data:
|
||||
fruit: apple
|
||||
nobles: |2
|
||||
|
||||
helium
|
||||
neon
|
||||
argon
|
||||
krypton
|
||||
xenon
|
||||
radon
|
||||
vegetable: broccoli
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: blah-bob-gfkcbk5ckf
|
||||
`)
|
||||
}
|
||||
|
||||
func TestGeneratorOverlays(t *testing.T) {
|
||||
th := NewKustTestHarness(t, "/app/overlay")
|
||||
th.writeK("/app/base1", `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namePrefix: p1-
|
||||
configMapGenerator:
|
||||
- name: com1
|
||||
@@ -33,8 +168,6 @@ configMapGenerator:
|
||||
- from=base
|
||||
`)
|
||||
th.writeK("/app/base2", `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namePrefix: p2-
|
||||
configMapGenerator:
|
||||
- name: com2
|
||||
@@ -43,8 +176,6 @@ configMapGenerator:
|
||||
- from=base
|
||||
`)
|
||||
th.writeK("/app/overlay/o1", `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
bases:
|
||||
- ../../base1
|
||||
configMapGenerator:
|
||||
@@ -54,8 +185,6 @@ configMapGenerator:
|
||||
- from=overlay
|
||||
`)
|
||||
th.writeK("/app/overlay/o2", `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
bases:
|
||||
- ../../base2
|
||||
configMapGenerator:
|
||||
@@ -65,8 +194,6 @@ configMapGenerator:
|
||||
- from=overlay
|
||||
`)
|
||||
th.writeK("/app/overlay", `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
bases:
|
||||
- o1
|
||||
- o2
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package target
|
||||
package target_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package target
|
||||
package target_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package target
|
||||
package target_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
@@ -11,7 +11,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package target
|
||||
package target_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc/transformer"
|
||||
interror "sigs.k8s.io/kustomize/pkg/internal/error"
|
||||
@@ -42,7 +41,6 @@ import (
|
||||
type KustTarget struct {
|
||||
kustomization *types.Kustomization
|
||||
ldr ifc.Loader
|
||||
fSys fs.FileSystem
|
||||
rFactory *resmap.Factory
|
||||
tFactory transformer.Factory
|
||||
}
|
||||
@@ -50,7 +48,6 @@ type KustTarget struct {
|
||||
// NewKustTarget returns a new instance of KustTarget primed with a Loader.
|
||||
func NewKustTarget(
|
||||
ldr ifc.Loader,
|
||||
fSys fs.FileSystem,
|
||||
rFactory *resmap.Factory,
|
||||
tFactory transformer.Factory) (*KustTarget, error) {
|
||||
content, err := loadKustFile(ldr)
|
||||
@@ -70,12 +67,23 @@ func NewKustTarget(
|
||||
return &KustTarget{
|
||||
kustomization: &k,
|
||||
ldr: ldr,
|
||||
fSys: fSys,
|
||||
rFactory: rFactory,
|
||||
tFactory: tFactory,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func quoted(l []string) []string {
|
||||
r := make([]string, len(l))
|
||||
for i, v := range l {
|
||||
r[i] = "'" + v + "'"
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func commaOr(q []string) string {
|
||||
return strings.Join(q[:len(q)-1], ", ") + " or " + q[len(q)-1]
|
||||
}
|
||||
|
||||
func loadKustFile(ldr ifc.Loader) ([]byte, error) {
|
||||
var content []byte
|
||||
match := 0
|
||||
@@ -88,8 +96,9 @@ func loadKustFile(ldr ifc.Loader) ([]byte, error) {
|
||||
}
|
||||
switch match {
|
||||
case 0:
|
||||
return nil, fmt.Errorf("No kustomization file found in %s. Kustomize supports the following kustomization files: %s",
|
||||
ldr.Root(), strings.Join(constants.KustomizationFileNames, ", "))
|
||||
return nil, fmt.Errorf(
|
||||
"unable to find one of %v in directory '%s'",
|
||||
commaOr(quoted(constants.KustomizationFileNames)), ldr.Root())
|
||||
case 1:
|
||||
return content, nil
|
||||
default:
|
||||
@@ -110,7 +119,7 @@ func unmarshal(y []byte, o interface{}) error {
|
||||
// MakeCustomizedResMap creates a ResMap per kustomization instructions.
|
||||
// The Resources in the returned ResMap are fully customized.
|
||||
func (kt *KustTarget) MakeCustomizedResMap() (resmap.ResMap, error) {
|
||||
ra, err := kt.accumulateTarget()
|
||||
ra, err := kt.AccumulateTarget()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -134,11 +143,11 @@ func (kt *KustTarget) shouldAddHashSuffixesToGeneratedResources() bool {
|
||||
!kt.kustomization.GeneratorOptions.DisableNameSuffixHash
|
||||
}
|
||||
|
||||
// accumulateTarget returns a new ResAccumulator,
|
||||
// AccumulateTarget returns a new ResAccumulator,
|
||||
// holding customized resources and the data/rules used
|
||||
// to do so. The name back references and vars are
|
||||
// not yet fixed.
|
||||
func (kt *KustTarget) accumulateTarget() (
|
||||
func (kt *KustTarget) AccumulateTarget() (
|
||||
ra *ResAccumulator, err error) {
|
||||
// TODO(monopole): Get rid of the KustomizationErrors accumulator.
|
||||
// It's not consistently used, and complicates tests.
|
||||
@@ -236,15 +245,15 @@ func (kt *KustTarget) accumulateBases() (
|
||||
continue
|
||||
}
|
||||
subKt, err := NewKustTarget(
|
||||
ldr, kt.fSys, kt.rFactory, kt.tFactory)
|
||||
ldr, kt.rFactory, kt.tFactory)
|
||||
if err != nil {
|
||||
errs.Append(errors.Wrap(err, "couldn't make target for "+path))
|
||||
ldr.Cleanup()
|
||||
continue
|
||||
}
|
||||
subRa, err := subKt.accumulateTarget()
|
||||
subRa, err := subKt.AccumulateTarget()
|
||||
if err != nil {
|
||||
errs.Append(errors.Wrap(err, "accumulateTarget"))
|
||||
errs.Append(errors.Wrap(err, "AccumulateTarget"))
|
||||
ldr.Cleanup()
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package target
|
||||
package target_test
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
@@ -25,14 +25,16 @@ import (
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/gvk"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
"sigs.k8s.io/kustomize/pkg/internal/loadertest"
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||
"sigs.k8s.io/kustomize/pkg/resource"
|
||||
. "sigs.k8s.io/kustomize/pkg/target"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
const (
|
||||
kustomizationContent1 = `
|
||||
kustomizationContent = `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namePrefix: foo-
|
||||
@@ -45,6 +47,8 @@ commonAnnotations:
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- namespace.yaml
|
||||
generatorOptions:
|
||||
disableNameSuffixHash: false
|
||||
configMapGenerator:
|
||||
- name: literalConfigMap
|
||||
literals:
|
||||
@@ -81,9 +85,9 @@ metadata:
|
||||
]`
|
||||
)
|
||||
|
||||
func TestResources1(t *testing.T) {
|
||||
func TestResources(t *testing.T) {
|
||||
th := NewKustTestHarness(t, "/whatever")
|
||||
th.writeK("/whatever/", kustomizationContent1)
|
||||
th.writeK("/whatever/", kustomizationContent)
|
||||
th.writeF("/whatever/deployment.yaml", deploymentContent)
|
||||
th.writeF("/whatever/namespace.yaml", namespaceContent)
|
||||
th.writeF("/whatever/jsonpatch.json", jsonpatchContent)
|
||||
@@ -144,7 +148,9 @@ func TestResources1(t *testing.T) {
|
||||
"DB_USERNAME": "admin",
|
||||
"DB_PASSWORD": "somepw",
|
||||
},
|
||||
}, &types.GeneratorArgs{}, nil),
|
||||
},
|
||||
&types.GeneratorArgs{},
|
||||
&types.GeneratorOptions{}),
|
||||
resid.NewResIdWithPrefixSuffixNamespace(
|
||||
gvk.Gvk{Version: "v1", Kind: "Secret"},
|
||||
"secret", "foo-", "-bar", "ns1"): th.fromMapAndOption(
|
||||
@@ -166,7 +172,9 @@ func TestResources1(t *testing.T) {
|
||||
"DB_USERNAME": base64.StdEncoding.EncodeToString([]byte("admin")),
|
||||
"DB_PASSWORD": base64.StdEncoding.EncodeToString([]byte("somepw")),
|
||||
},
|
||||
}, &types.GeneratorArgs{}, nil),
|
||||
},
|
||||
&types.GeneratorArgs{},
|
||||
&types.GeneratorOptions{}),
|
||||
resid.NewResIdWithPrefixSuffixNamespace(
|
||||
gvk.Gvk{Version: "v1", Kind: "Namespace"},
|
||||
"ns1", "foo-", "-bar", ""): th.fromMap(
|
||||
@@ -195,9 +203,20 @@ func TestResources1(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestKustomizationNotFound(t *testing.T) {
|
||||
_, err := NewKustTarget(loadertest.NewFakeLoader("/foo"), nil, nil)
|
||||
if err == nil {
|
||||
t.Fatalf("expected an error")
|
||||
}
|
||||
if err.Error() !=
|
||||
`unable to find one of 'kustomization.yaml', 'kustomization.yml' or 'Kustomization' in directory '/foo'` {
|
||||
t.Fatalf("unexpected error: %q", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceNotFound(t *testing.T) {
|
||||
th := NewKustTestHarness(t, "/whatever")
|
||||
th.writeK("/whatever", kustomizationContent1)
|
||||
th.writeK("/whatever", kustomizationContent)
|
||||
_, err := th.makeKustTarget().MakeCustomizedResMap()
|
||||
if err == nil {
|
||||
t.Fatalf("Didn't get the expected error for an unknown resource")
|
||||
@@ -218,13 +237,12 @@ func findSecret(m resmap.ResMap) *resource.Resource {
|
||||
|
||||
func TestDisableNameSuffixHash(t *testing.T) {
|
||||
th := NewKustTestHarness(t, "/whatever")
|
||||
th.writeK("/whatever/", kustomizationContent1)
|
||||
th.writeK("/whatever/", kustomizationContent)
|
||||
th.writeF("/whatever/deployment.yaml", deploymentContent)
|
||||
th.writeF("/whatever/namespace.yaml", namespaceContent)
|
||||
th.writeF("/whatever/jsonpatch.json", jsonpatchContent)
|
||||
|
||||
kt := th.makeKustTarget()
|
||||
m, err := kt.MakeCustomizedResMap()
|
||||
m, err := th.makeKustTarget().MakeCustomizedResMap()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected Resources error %v", err)
|
||||
}
|
||||
@@ -236,9 +254,11 @@ func TestDisableNameSuffixHash(t *testing.T) {
|
||||
t.Errorf("unexpected secret resource name: %s", secret.GetName())
|
||||
}
|
||||
|
||||
kt.kustomization.GeneratorOptions = &types.GeneratorOptions{
|
||||
DisableNameSuffixHash: true}
|
||||
m, err = kt.MakeCustomizedResMap()
|
||||
th.writeK("/whatever/",
|
||||
strings.Replace(kustomizationContent,
|
||||
"disableNameSuffixHash: false",
|
||||
"disableNameSuffixHash: true", -1))
|
||||
m, err = th.makeKustTarget().MakeCustomizedResMap()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected Resources error %v", err)
|
||||
}
|
||||
@@ -324,11 +344,11 @@ vars:
|
||||
name: heron
|
||||
apiVersion: v300
|
||||
`)
|
||||
ra, err := th.makeKustTarget().accumulateTarget()
|
||||
ra, err := th.makeKustTarget().AccumulateTarget()
|
||||
if err != nil {
|
||||
t.Fatalf("Err: %v", err)
|
||||
}
|
||||
vars := ra.varSet.Set()
|
||||
vars := ra.Vars()
|
||||
if len(vars) != 2 {
|
||||
t.Fatalf("unexpected size %d", len(vars))
|
||||
}
|
||||
@@ -374,11 +394,11 @@ vars:
|
||||
bases:
|
||||
- ../o1
|
||||
`)
|
||||
ra, err := th.makeKustTarget().accumulateTarget()
|
||||
ra, err := th.makeKustTarget().AccumulateTarget()
|
||||
if err != nil {
|
||||
t.Fatalf("Err: %v", err)
|
||||
}
|
||||
vars := ra.varSet.Set()
|
||||
vars := ra.Vars()
|
||||
if len(vars) != 4 {
|
||||
for i, v := range vars {
|
||||
fmt.Printf("%v: %v\n", i, v)
|
||||
@@ -427,7 +447,7 @@ vars:
|
||||
bases:
|
||||
- ../o1
|
||||
`)
|
||||
_, err := th.makeKustTarget().accumulateTarget()
|
||||
_, err := th.makeKustTarget().AccumulateTarget()
|
||||
if err == nil {
|
||||
t.Fatalf("expected var collision")
|
||||
}
|
||||
|
||||
@@ -14,24 +14,24 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package target
|
||||
package target_test
|
||||
|
||||
// A collection of utilities used in target tests.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sigs.k8s.io/kustomize/pkg/transformers/config/defaultconfig"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
|
||||
"sigs.k8s.io/kustomize/k8sdeps/transformer"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/internal/loadertest"
|
||||
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||
"sigs.k8s.io/kustomize/pkg/resource"
|
||||
. "sigs.k8s.io/kustomize/pkg/target"
|
||||
"sigs.k8s.io/kustomize/pkg/transformers/config/defaultconfig"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
@@ -50,17 +50,8 @@ func NewKustTestHarness(t *testing.T, path string) *KustTestHarness {
|
||||
}
|
||||
|
||||
func (th *KustTestHarness) makeKustTarget() *KustTarget {
|
||||
// Warning: the following filesystem - a fake - must be rooted at /.
|
||||
// This fs root is used as the working directory for the shell spawned by
|
||||
// the secretgenerator, and has nothing to do with the filesystem used
|
||||
// to load relative paths from the fake filesystem.
|
||||
// This trick only works for secret generator commands that don't actually
|
||||
// try to read the file system, because these tests don't write to the
|
||||
// real "/" directory. See use of exec package in the secretfactory.
|
||||
fakeFs := fs.MakeFakeFS()
|
||||
fakeFs.Mkdir("/")
|
||||
kt, err := NewKustTarget(
|
||||
th.ldr, fakeFs, th.rf, transformer.NewFactoryImpl())
|
||||
th.ldr, th.rf, transformer.NewFactoryImpl())
|
||||
if err != nil {
|
||||
th.t.Fatalf("Unexpected construction error %v", err)
|
||||
}
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package target
|
||||
package target_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package target
|
||||
package target_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package target
|
||||
package target_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
@@ -55,6 +55,11 @@ func (ra *ResAccumulator) ResMap() resmap.ResMap {
|
||||
return result
|
||||
}
|
||||
|
||||
// Vars returns a copy of underlying vars.
|
||||
func (ra *ResAccumulator) Vars() []types.Var {
|
||||
return ra.varSet.Set()
|
||||
}
|
||||
|
||||
func (ra *ResAccumulator) MergeResourcesWithErrorOnIdCollision(
|
||||
resources resmap.ResMap) (err error) {
|
||||
ra.resMap, err = resmap.MergeWithErrorOnIdCollision(
|
||||
@@ -98,7 +103,7 @@ func (ra *ResAccumulator) makeVarReplacementMap() (map[string]string, error) {
|
||||
result := map[string]string{}
|
||||
for _, v := range ra.varSet.Set() {
|
||||
id := resid.NewResId(v.ObjRef.GVK(), v.ObjRef.Name)
|
||||
if r, found := ra.resMap.DemandOneMatchForId(id); found {
|
||||
if r, found := ra.resMap.DemandOneGvknMatchForId(id); found {
|
||||
s, err := r.GetFieldValue(v.FieldRef.FieldPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("field path err for var: %+v", v)
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package target
|
||||
package target_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||
"sigs.k8s.io/kustomize/pkg/resource"
|
||||
. "sigs.k8s.io/kustomize/pkg/target"
|
||||
"sigs.k8s.io/kustomize/pkg/transformers/config"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package target
|
||||
package target_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package target
|
||||
package target_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -23,8 +23,6 @@ import (
|
||||
func TestVariableRef(t *testing.T) {
|
||||
th := NewKustTestHarness(t, "/app/overlay/staging")
|
||||
th.writeK("/app/base", `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namePrefix: base-
|
||||
resources:
|
||||
- cockroachdb-statefulset-secure.yaml
|
||||
@@ -323,8 +321,6 @@ spec:
|
||||
storage: 1Gi
|
||||
`)
|
||||
th.writeK("/app/overlay/staging", `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namePrefix: dev-
|
||||
bases:
|
||||
- ../../base
|
||||
@@ -583,8 +579,6 @@ spec:
|
||||
func TestVariableRefIngress(t *testing.T) {
|
||||
th := NewKustTestHarness(t, "/app/overlay")
|
||||
th.writeK("/app/base", `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- ingress.yaml
|
||||
@@ -657,8 +651,6 @@ spec:
|
||||
targetPort: http
|
||||
`)
|
||||
th.writeK("/app/overlay", `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
nameprefix: kustomized-
|
||||
bases:
|
||||
- ../base
|
||||
@@ -723,3 +715,137 @@ spec:
|
||||
- kustomized-nginx.example.com
|
||||
`)
|
||||
}
|
||||
|
||||
func TestVariableRefMounthPath(t *testing.T) {
|
||||
th := NewKustTestHarness(t, "/app/base")
|
||||
th.writeK("/app/base", `
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- namespace.yaml
|
||||
|
||||
vars:
|
||||
- name: NAMESPACE
|
||||
objref:
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
name: my-namespace
|
||||
|
||||
`)
|
||||
th.writeF("/app/base/deployment.yaml", `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: my-deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: app
|
||||
image: busybox
|
||||
volumeMounts:
|
||||
- name: my-volume
|
||||
mountPath: "/$(NAMESPACE)"
|
||||
env:
|
||||
- name: NAMESPACE
|
||||
value: $(NAMESPACE)
|
||||
volumes:
|
||||
- name: my-volume
|
||||
emptyDir: {}
|
||||
`)
|
||||
th.writeF("/app/base/namespace.yaml", `
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: my-namespace
|
||||
`)
|
||||
|
||||
m, err := th.makeKustTarget().MakeCustomizedResMap()
|
||||
if err != nil {
|
||||
t.Fatalf("Err: %v", err)
|
||||
}
|
||||
th.assertActualEqualsExpected(m, `
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: my-namespace
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: my-deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
- name: NAMESPACE
|
||||
value: my-namespace
|
||||
image: busybox
|
||||
name: app
|
||||
volumeMounts:
|
||||
- mountPath: /my-namespace
|
||||
name: my-volume
|
||||
volumes:
|
||||
- emptyDir: {}
|
||||
name: my-volume
|
||||
`)
|
||||
}
|
||||
|
||||
func TestVariableRefMaps(t *testing.T) {
|
||||
th := NewKustTestHarness(t, "/app/base")
|
||||
th.writeK("/app/base", `
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- namespace.yaml
|
||||
vars:
|
||||
- name: NAMESPACE
|
||||
objref:
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
name: my-namespace
|
||||
`)
|
||||
th.writeF("/app/base/deployment.yaml", `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: my-deployment
|
||||
labels:
|
||||
my-label: $(NAMESPACE)
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: app
|
||||
image: busybox
|
||||
`)
|
||||
th.writeF("/app/base/namespace.yaml", `
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: my-namespace
|
||||
`)
|
||||
|
||||
m, err := th.makeKustTarget().MakeCustomizedResMap()
|
||||
if err != nil {
|
||||
t.Fatalf("Err: %v", err)
|
||||
}
|
||||
th.assertActualEqualsExpected(m, `
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: my-namespace
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
my-label: my-namespace
|
||||
name: my-deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- image: busybox
|
||||
name: app
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -100,10 +100,63 @@ varReference:
|
||||
- path: spec/containers/env/value
|
||||
kind: Pod
|
||||
|
||||
- path: spec/initContainers/command
|
||||
kind: Pod
|
||||
|
||||
- path: spec/initContainers/args
|
||||
kind: Pod
|
||||
|
||||
- path: spec/initContainers/env/value
|
||||
kind: Pod
|
||||
|
||||
- path: spec/rules/host
|
||||
kind: Ingress
|
||||
|
||||
- path: spec/tls/hosts
|
||||
kind: Ingress
|
||||
|
||||
- path: spec/template/spec/containers/volumeMounts/mountPath
|
||||
kind: StatefulSet
|
||||
|
||||
- path: spec/template/spec/initContainers/volumeMounts/mountPath
|
||||
kind: StatefulSet
|
||||
|
||||
- path: spec/containers/volumeMounts/mountPath
|
||||
kind: Pod
|
||||
|
||||
- path: spec/initContainers/volumeMounts/mountPath
|
||||
kind: Pod
|
||||
|
||||
- path: spec/template/spec/containers/volumeMounts/mountPath
|
||||
kind: ReplicaSet
|
||||
|
||||
- path: spec/template/spec/initContainers/volumeMounts/mountPath
|
||||
kind: ReplicaSet
|
||||
|
||||
- path: spec/template/spec/containers/volumeMounts/mountPath
|
||||
kind: Job
|
||||
|
||||
- path: spec/template/spec/initContainers/volumeMounts/mountPath
|
||||
kind: Job
|
||||
|
||||
- path: spec/template/spec/containers/volumeMounts/mountPath
|
||||
kind: CronJob
|
||||
|
||||
- path: spec/template/spec/initContainers/volumeMounts/mountPath
|
||||
kind: CronJob
|
||||
|
||||
- path: spec/template/spec/containers/volumeMounts/mountPath
|
||||
kind: DaemonSet
|
||||
|
||||
- path: spec/template/spec/initContainers/volumeMounts/mountPath
|
||||
kind: DaemonSet
|
||||
|
||||
- path: spec/template/spec/containers/volumeMounts/mountPath
|
||||
kind: Deployment
|
||||
|
||||
- path: spec/template/spec/initContainers/volumeMounts/mountPath
|
||||
kind: Deployment
|
||||
|
||||
- path: metadata/labels
|
||||
`
|
||||
)
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package transformers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
@@ -75,7 +76,10 @@ func (pt *imageTransformer) findAndReplaceImage(obj map[string]interface{}) erro
|
||||
}
|
||||
|
||||
func (pt *imageTransformer) updateContainers(obj map[string]interface{}, path string) error {
|
||||
containers := obj[path].([]interface{})
|
||||
containers, ok := obj[path].([]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("containers path is not of type []interface{} but %T", obj[path])
|
||||
}
|
||||
for i := range containers {
|
||||
container := containers[i].(map[string]interface{})
|
||||
containerImage, found := container["image"]
|
||||
@@ -85,20 +89,21 @@ func (pt *imageTransformer) updateContainers(obj map[string]interface{}, path st
|
||||
|
||||
imageName := containerImage.(string)
|
||||
for _, img := range pt.images {
|
||||
if isImageMatched(imageName, img.Name) {
|
||||
name, tag := split(imageName)
|
||||
if img.NewName != "" {
|
||||
name = img.NewName
|
||||
}
|
||||
if img.NewTag != "" {
|
||||
tag = ":" + img.NewTag
|
||||
}
|
||||
if img.Digest != "" {
|
||||
tag = "@" + img.Digest
|
||||
}
|
||||
container["image"] = name + tag
|
||||
break
|
||||
if !isImageMatched(imageName, img.Name) {
|
||||
continue
|
||||
}
|
||||
name, tag := split(imageName)
|
||||
if img.NewName != "" {
|
||||
name = img.NewName
|
||||
}
|
||||
if img.NewTag != "" {
|
||||
tag = ":" + img.NewTag
|
||||
}
|
||||
if img.Digest != "" {
|
||||
tag = "@" + img.Digest
|
||||
}
|
||||
container["image"] = name + tag
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -89,7 +89,7 @@ func (o *nameReferenceTransformer) updateNameReference(
|
||||
s, _ := in.(string)
|
||||
for id, res := range m {
|
||||
if id.Gvk().IsSelected(&backRef) && id.Name() == s {
|
||||
matchedIds := m.FindByGVKN(id)
|
||||
matchedIds := m.GetMatchingIds(id.GvknEquals)
|
||||
// If there's more than one match, there's no way
|
||||
// to know which one to pick, so emit error.
|
||||
if len(matchedIds) > 1 {
|
||||
@@ -115,7 +115,7 @@ func (o *nameReferenceTransformer) updateNameReference(
|
||||
for id, res := range m {
|
||||
indexes := indexOf(id.Name(), names)
|
||||
if id.Gvk().IsSelected(&backRef) && len(indexes) > 0 {
|
||||
matchedIds := m.FindByGVKN(id)
|
||||
matchedIds := m.GetMatchingIds(id.GvknEquals)
|
||||
if len(matchedIds) > 1 {
|
||||
return nil, fmt.Errorf(
|
||||
"Multiple matches for name %s:\n %v", id, matchedIds)
|
||||
|
||||
@@ -39,6 +39,17 @@ func (rv *refvarTransformer) replaceVars(in interface{}) (interface{}, error) {
|
||||
xs = append(xs, expansion.Expand(a.(string), rv.mappingFunc))
|
||||
}
|
||||
return xs, nil
|
||||
case map[string]interface{}:
|
||||
inMap := in.(map[string]interface{})
|
||||
xs := make(map[string]interface{}, len(inMap))
|
||||
for k, v := range inMap {
|
||||
s, ok := v.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%#v is expected to be %T", v, s)
|
||||
}
|
||||
xs[k] = expansion.Expand(s, rv.mappingFunc)
|
||||
}
|
||||
return xs, nil
|
||||
case interface{}:
|
||||
s, ok := in.(string)
|
||||
if !ok {
|
||||
|
||||
91
pkg/transformers/refvars_test.go
Normal file
91
pkg/transformers/refvars_test.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package transformers
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||
"sigs.k8s.io/kustomize/pkg/transformers/config"
|
||||
)
|
||||
|
||||
func TestVarRef(t *testing.T) {
|
||||
type given struct {
|
||||
varMap map[string]string
|
||||
fs []config.FieldSpec
|
||||
res resmap.ResMap
|
||||
}
|
||||
type expected struct {
|
||||
res resmap.ResMap
|
||||
}
|
||||
testCases := []struct {
|
||||
description string
|
||||
given given
|
||||
expected expected
|
||||
}{
|
||||
{
|
||||
description: "var replacement in map[string]",
|
||||
given: given{
|
||||
varMap: map[string]string{
|
||||
"FOO": "BAR",
|
||||
},
|
||||
fs: []config.FieldSpec{
|
||||
{Gvk: cmap, Path: "data"},
|
||||
},
|
||||
res: resmap.ResMap{
|
||||
resid.NewResId(cmap, "cm1"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm1",
|
||||
},
|
||||
"data": map[string]interface{}{
|
||||
"item1": "$(FOO)",
|
||||
"item2": "bla",
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
expected: expected{
|
||||
res: resmap.ResMap{
|
||||
resid.NewResId(cmap, "cm1"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm1",
|
||||
},
|
||||
"data": map[string]interface{}{
|
||||
"item1": "BAR",
|
||||
"item2": "bla",
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
// arrange
|
||||
tr := NewRefVarTransformer(tc.given.varMap, tc.given.fs)
|
||||
|
||||
// act
|
||||
err := tr.Transform(tc.given.res)
|
||||
|
||||
// assert
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
a, e := tc.given.res, tc.expected.res
|
||||
if !reflect.DeepEqual(a, e) {
|
||||
err = e.ErrorIfNotEqual(a)
|
||||
t.Fatalf("actual doesn't match expected: \nACTUAL:\n%v\nEXPECTED:\n%v\nERR: %v", a, e, err)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,11 @@ limitations under the License.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GenArgs contains both generator args and options
|
||||
type GenArgs struct {
|
||||
args *GeneratorArgs
|
||||
@@ -30,6 +35,18 @@ func NewGenArgs(args *GeneratorArgs, opts *GeneratorOptions) *GenArgs {
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GenArgs) String() string {
|
||||
if g == nil {
|
||||
return "{nilGenArgs}"
|
||||
}
|
||||
return "{" +
|
||||
strings.Join([]string{
|
||||
"nsfx:" + strconv.FormatBool(g.NeedsHashSuffix()),
|
||||
"beh:" + g.Behavior().String()},
|
||||
",") +
|
||||
"}"
|
||||
}
|
||||
|
||||
// NeedHashSuffix returns true if the hash suffix is needed.
|
||||
// It is needed when the two conditions are both met
|
||||
// 1) GenArgs is not nil
|
||||
|
||||
47
pkg/types/genargs_test.go
Normal file
47
pkg/types/genargs_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
func TestGenArgs_String(t *testing.T) {
|
||||
tests := []struct {
|
||||
ga *GenArgs
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
ga: nil,
|
||||
expected: "{nilGenArgs}",
|
||||
},
|
||||
{
|
||||
ga: &GenArgs{},
|
||||
expected: "{nsfx:false,beh:unspecified}",
|
||||
},
|
||||
{
|
||||
ga: NewGenArgs(
|
||||
&GeneratorArgs{Behavior: "merge"},
|
||||
&GeneratorOptions{DisableNameSuffixHash: false}),
|
||||
expected: "{nsfx:true,beh:merge}",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if test.ga.String() != test.expected {
|
||||
t.Fatalf("Expected '%s', got '%s'", test.expected, test.ga.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,9 +75,11 @@ type VarSet struct {
|
||||
set []Var
|
||||
}
|
||||
|
||||
// Set returns the var set.
|
||||
// Set returns a copy of the var set.
|
||||
func (vs *VarSet) Set() []Var {
|
||||
return vs.set
|
||||
s := make([]Var, len(vs.set))
|
||||
copy(s, vs.set)
|
||||
return s
|
||||
}
|
||||
|
||||
// MergeSet absorbs other vars with error on name collision.
|
||||
|
||||
Reference in New Issue
Block a user